当前位置: 首页 > news >正文

特征工程 --- 特征提取

特征工程

一、特征工程逻辑思维导图

Untitled diagram _ Mermaid Chart-2025-08-01-102556

二、特征工程的概念

1.定义:

特征工程(Feature Engineering)是指在机器学习或深度学习项目中,为了更好地提高模型性能,通过构造、转换、选择特征的技术与过程。

2.目的:

目的说明
提高模型性能更有意义的特征 → 更好的模型拟合与预测能力
降低维度与计算复杂度去除冗余、无关特征,加快模型训练
提升泛化能力减少过拟合,提升对新数据的表现
增强可解释性构造更具现实意义的特征,方便模型解释

3.特征工程包括哪些步骤:

  • 特征提取
  • 特征选择
  • 特征处理
  • 特征降维

4. 举个简单例子

假设你要预测一个人的购车行为,原始数据有这些字段:

姓名年龄年收入(元)婚姻状态城市是否购车
张三25120000未婚北京
李四45300000已婚上海
王五35200000离异广州
赵六28180000未婚深圳

5.特征工程做了什么?

① 特征提取(Feature Extraction)
  • 通常直接使用结构化数据的字段
  • 或从原始字段中提取额外信息,如:
原始字段新提取特征
城市城市级别(如一线城市)
年龄年龄段(<30、30-50、>50)

② 特征处理(Feature Processing)
操作字段方法/说明
数值缩放年收入标准化(Z-score)或归一化(Min-Max)
分箱年龄分为年龄段(如 <30, 30-50, >50)
编码婚姻状态/城市独热编码 One-Hot 或 Label Encoding
缺失值处理若有缺失使用中位数/众数/插值填充

编码示例(婚姻状态)

婚姻状态婚姻_未婚婚姻_已婚婚姻_离异
未婚100
已婚010
离异001

③ 特征构造(Feature Construction)
构造目标构造方法
家庭稳定指数婚姻状态 × 年龄
收入等级年收入分箱(如高/中/低)
城市影响力评分自定义字典,如北京=3,广州=2

④ 特征选择(Feature Selection)
  • 目的:去除无用或冗余特征(如城市过多分散,且购车率差异小)
  • 可采用:
    • 相关系数法
    • 卡方检验
    • L1 正则化
    • 树模型的重要性评分(Feature Importance)

⑤ 特征降维(可选)

如果有维度灾难风险,比如城市编码后有 100 维,可以用:

方法适用场景
PCA连续变量、数值特征降维
LDA有监督、分类场景
AutoEncoder深度特征压缩

🔄 转换后数据示意
年龄年收入婚姻_未婚婚姻_已婚婚姻_离异城市_北京城市_上海城市_广州城市_深圳收入等级_高是否购车
250.2110010000
451.0001001001
350.6200100101
280.5410000010

6. 特征工程与建模的关系

原始数据
数据清洗
特征工程
模型训练
评估
  • 没有好的特征工程,再强的模型也很难表现好
  • 特征工程 ≈ 模型效果的地基

三、特征工程API

  • 实例化转换器对象,转换器类有很多,都是Transformer的子类, 常用的子类有:

    DictVectorizer  	字典特征提取
    CountVectorizer 	文本特征提取
    TfidfVectorizer 	TF-IDF文本特征词的重要程度特征提取 
    MinMaxScaler 		归一化
    StandardScaler 		标准化
    VarianceThreshold 	底方差过滤降维
    PCA  				主成分分析降维
    
  • 转换器对象调用fit_transform()进行转换, 其中fit用于计算数据,transform进行最终转换fit_transform()可以使用fit()和transform()代替

data_new = transfer.fit_transform(data)
可写成
transfer.fit(data)
data_new = transfer.transform(data)

在后面我们会讲解为什么会设计成这样。

四、DictVectorizer字典列表特征提取

from sklearn.feature_extraction import DictVectorizer

1. 什么是字典列表特征提取?

举个例子:你有一个这样的原始数据:

data = [{'age': 28, 'income': 50000, 'married': 'yes', 'city': 'Beijing'},{'age': 35, 'income': 80000, 'married': 'no',  'city': 'Shanghai'},{'age': 40, 'income': 90000, 'married': 'yes', 'city': 'Beijing'}
]
  • 每条数据是一个字典,整个数据是“字典的列表”。
  • DictVectorizer 会自动把分类变量做成 One-Hot,数值型保持原值。

2.代码演示(含稀疏矩阵和稠密矩阵)

from sklearn.feature_extraction import DictVectorizerdata = [{'age': 28, 'income': 50000, 'married': 'yes', 'city': 'Beijing'},{'age': 35, 'income': 80000, 'married': 'no',  'city': 'Shanghai'},{'age': 40, 'income': 90000, 'married': 'yes', 'city': 'Beijing'}
]# 1. 创建向量器
vec = DictVectorizer(sparse=True)  # 默认为 True,返回稀疏矩阵# 2. 进行转换
X_sparse = vec.fit_transform(data)# 3. 查看稀疏结果(CSR格式)
print(X_sparse)# 4. 转成稠密矩阵
X_dense = X_sparse.toarray()# 5. 获取特征名
print(vec.get_feature_names_out())
print(X_dense)

3.返回值的不同(稀疏 VS 稠密)

参数作用返回类型优点
sparse=True返回稀疏矩阵(默认)scipy.sparse 三元组表形式(CSR)节省内存
sparse=False返回稠密 numpy.ndarray直接是 array便于查看与调试

4. 稀疏矩阵转换成稠密矩阵

X_dense = X_sparse.toarray()

5. 示例输出

稀疏矩阵:(行索引,列索引) 数据

<Compressed Sparse Row sparse matrix of dtype 'float64'with 12 stored elements and shape (3, 6)>Coords	Values(0, 0)	28.0(0, 1)	1.0(0, 3)	50000.0(0, 5)	1.0(1, 0)	35.0(1, 2)	1.0(1, 3)	80000.0(1, 4)	1.0(2, 0)	40.0(2, 1)	1.0(2, 3)	90000.0(2, 5)	1.0
  • 内存中:是 CSR 格式Compressed Sparse Row),存储的是:

    • .data: 非零元素值列表
    • .indices: 非零元素所在的列索引
    • .indptr: 每行非零元素的起始位置索引
  • 打印时显示出来的 (row, col) \t value 只是为了可读性 —— 这叫做:

    “伪三元组表输出”(Human-readable triplet-like format)

它让我们看到非零值在矩阵中的具体位置和数值,但这不是它真实的底层结构。

特征名(vec.get_feature_names_out()):

['age' 'city=Beijing' 'city=Shanghai' 'income' 'married=no' 'married=yes']

稠密矩阵:

[[2.8e+01 1.0e+00 0.0e+00 5.0e+04 0.0e+00 1.0e+00][3.5e+01 0.0e+00 1.0e+00 8.0e+04 1.0e+00 0.0e+00][4.0e+01 1.0e+00 0.0e+00 9.0e+04 0.0e+00 1.0e+00]]

6. 特点总结

优点缺点
自动处理分类变量(One-Hot)特征维度可能膨胀,尤其是高基类分类变量
数值变量保持原样不支持嵌套结构(如 list、dict 中嵌 dict)
输出与 OneHotEncoder + ColumnTransformer 类似只适用于结构化数据(非嵌套 JSON)

7.使用场景建议

  • 小规模分类变量编码:如城市、性别等,用 DictVectorizer 非常便捷。
  • 大规模/高基类分类变量:建议使用 OneHotEncoder(handle_unknown='ignore') 结合 ColumnTransformer 管理更灵活。

五、CountVectorizer 文本特征提取

from sklearn.feature_extraction.text import CountVectorizer

1.基本原理

CountVectorizer 是最基础的文本向量化工具,它将文本中的词语转换为词频(Bag-of-Words,简称 BOW)特征矩阵。

功能:将一组文本(corpus)转换成词频矩阵(bag-of-words)

  • 每一行代表一条文本
  • 每一列代表一个词(特征)
  • 每个单元格表示该词在该文本中出现的次数

默认行为是:

  • 按英文空格和标点分词
  • 自动转小写
  • 只能处理英文,处理中文会把整句话当成一个词

2.案例解释

①简洁版

from sklearn.feature_extraction.text import CountVectorizer
import jiebacorpus = ["我爱北京天安门","天安门上太阳升","The sun rises in the east","I love Beijing Tiananmen"
]def mixed_tokenizer(text):if any( '\u4e00' <= chinese <='\u9fff' for chinese in text):return " ".join(jieba.cut(text))else:return text.lower()counter = CountVectorizer()
data2 = [mixed_tokenizer(word) for word in corpus]
data = counter.fit_transform(data2)
print(data.toarray())
print(counter.get_feature_names_out())

输出结果如下:

[[0 0 0 0 0 0 0 0 1 1 0][0 0 0 0 0 0 0 0 0 1 1][0 1 1 0 1 1 2 0 0 0 0][1 0 0 1 0 0 0 1 0 0 0]]
['beijing' 'east' 'in' 'love' 'rises' 'sun' 'the' 'tiananmen' '北京' '天安门''太阳升']

DataFrame构建词汇表

import jieba
from sklearn.feature_extraction.text import CountVectorizer
import pandas as pd# 自定义中文 + 英文分词函数
def mixed_tokenizer(text):if any('\u4e00' <= ch <= '\u9fff' for ch in text):  # 中文检测return list(jieba.cut(text))else:return text.lower().split()  # 英文按空格并小写处理# 用 CountVectorizer 进行词频统计
vectorizer = CountVectorizer(tokenizer=mixed_tokenizer,token_pattern=None)X = vectorizer.fit_transform(corpus)# 输出特征名(词汇表)
print("词汇表:")
print(vectorizer.get_feature_names_out())# 转成 DataFrame 展示词频矩阵
df = pd.DataFrame(X.toarray(), columns=vectorizer.get_feature_names_out())
print("\n词频矩阵:")
df.head()

输出如下所示:

词汇表:

['beijing' 'east' 'i' 'in' 'love' 'rises' 'sun' 'the' 'tiananmen' '上' '北京''天安门' '太阳升' '我' '爱']

词频矩阵:

beijingeastiinloverisessunthetiananmen北京天安门太阳升
0000000000011011
1000000000101100
2010101120000000
3101010001000000

3.参数解释

参数名作用示例
lowercase是否转小写(默认True)'NLP''nlp'
stop_words停用词(如 ‘the’, ‘is’)stop_words='english'
max_features限制输出特征数量max_features=1000
ngram_range支持 n-gram 模型(1,2) → 1-gram + 2-gram
token_pattern正则表达式控制分词方式默认只保留词长 ≥2 的单词
binary是否将词频转为二值(出现为1)binary=True

六、TfidfVectorizer TF-IDF文本特征词的重要程度特征提取

from sklearn.feature_extraction.text import TfidfVectorizer

TfidfVectorizer 是 sklearn 中用于将文本数据转换为基于 TF-IDF(词频 - 逆文档频率) 权重的特征向量的工具,核心作用是量化词语在文本中的重要程度,是文本分类、聚类等任务中常用的特征提取方法。

1. TF-IDF 核心原理

TF-IDF 由两部分组成:词频(TF)逆文档频率(IDF),最终通过两者的乘积衡量词语的重要性。

image-20250801194848790

  1. 词频(Term Frequency, TF) 表示一个词在单篇文档中出现的频率,反映该词在当前文档中的重要性。

    • 计算公式(sklearn 默认):直接使用词语在文档中出现的原始次数(与 CountVectorizer 的结果一致)。
    • 示例:若 “教育” 在文档 1 中出现 2 次,则该文档中 “教育” 的 TF 为 2。
  2. 逆文档频率(Inverse Document Frequency, IDF) 反映一个词在整个文档集合中的稀有程度:若一个词在多数文档中都出现(如 “的”“是”),则其重要性低;若仅在少数文档中出现,则重要性高。

    • sklearn 中的计算公式(带平滑处理):

      IDF(t)=log⁡(总文档数+1包含词t的文档数+1)+1 \text{IDF}(t) = \log\left(\frac{\text{总文档数} + 1}{\text{包含词}t\text{的文档数} + 1}\right) + 1 IDF(t)=log(包含词t的文档数+1总文档数+1)+1

      • 平滑目的:避免分母为 0(当某词未出现在任何文档中时)。
    • 示例:若 “民办” 在 3 篇文档中出现 2 次,总文档数为 3,则其 IDF 为 log⁡(3+12+1)+1≈0.2877+1=1.2877\log\left(\frac{3+1}{2+1}\right) + 1 \approx 0.2877 + 1 = 1.2877log(2+13+1)+10.2877+1=1.2877

  3. TF-IDF 最终值 词语的 TF-IDF 权重 = 该词的 TF × 该词的 IDF。

    • 含义:同时考虑词语在当前文档中的频率和在整体文档中的稀有性,权重越高则该词对当前文档的 “代表性” 越强。
  4. L2 归一化(sklearn 默认) 为了避免长文档的向量权重普遍高于短文档,TfidfVectorizer 会对每个文档的 TF-IDF 向量进行 L2 归一化:

归一化后的值=xix12+x22+...+xn2 \text{归一化后的值} = \frac{x_i}{\sqrt{x_1^2 + x_2^2 + ... + x_n^2}} 归一化后的值=x12+x22+...+xn2xi

其中 $x_i $是向量中的每个元素(即某词的 TF-IDF 权重)。

2. ✅ 各部分 “+1” 的意义详解:
部分数学目的原因解释
分子 N+1平衡缩放如果没有这个,极端情况下 IDF 可能会小于 1(造成结果偏小),这个 +1 有助于提高稀有词的重要性。
分母 +1防止除零避免某个词没有出现在任何文档中(即 df=0),否则会导致除以 0。
整体 +1平移,避免 0让所有 IDF 都大于 0,防止乘上 IDF 后造成词权重为 0,特别是在只有一个文档时。

在 sklearn 的 TfidfVectorizer 中,输出的 TF-IDF 向量默认经过了 L2 正则化,目的如下:

  • 保证每个文档的特征向量是单位长度,避免长文本向量绝对值过大
  • 更好地支持 余弦相似度计算(常用于文本匹配)
  • 提高模型对文本长度不一致的鲁棒性
3.案例演示:

①使用官方API:

from sklearn.feature_extraction.text import TfidfVectorizer
import jiebacorpus = ["我爱北京天安门","天安门上太阳升","The sun rises in the east","I love Beijing Tiananmen"
]def mixed_tokenizer(text):if any( '\u4e00' <= chinese <='\u9fff' for chinese in text):return " ".join(jieba.cut(text))else:return text.lower()transfer = TfidfVectorizer()
data = [mixed_tokenizer(word) for word in corpus]
data = transfer.fit_transform(data)
print(data.toarray())
print(transfer.get_feature_names_out())

结果输出:

[[0.         0.         0.         0.         0.         0.0.         0.         0.78528828 0.6191303  0.        ][0.         0.         0.         0.         0.         0.0.         0.         0.         0.6191303  0.78528828][0.         0.35355339 0.35355339 0.         0.35355339 0.353553390.70710678 0.         0.         0.         0.        ][0.57735027 0.         0.         0.57735027 0.         0.0.         0.57735027 0.         0.         0.        ]]
['beijing' 'east' 'in' 'love' 'rises' 'sun' 'the' 'tiananmen' '北京' '天安门''太阳升']

②利用公式自定义函数:

from sklearn.feature_extraction.text import CountVectorizer
import numpy as np
from sklearn.preprocessing import normalizedef myTfidVectorizer(data):counter = CountVectorizer()TF = counter.fit_transform(data).toarray()  # 词频矩阵(TF)IDF = np.log((len(TF) + 1) / ((TF != 0).sum(axis=0) + 1)) + 1  # IDF 计算TF_IDF = TF * IDF  # 每个位置乘以对应的 IDFTF_IDF = normalize(TF_IDF, norm="l2")  # L2归一化return TF_IDF,counter.get_feature_names_out()def default_tokenizer(text):if any("\u4e00" <= ch <= "\u9fff" for ch in text):  # 判断是否含有中文字符return " ".join(jieba.cut(text))else:return text.lower()import jiebacorpus = ["我爱北京天安门","天安门上太阳升","The sun rises in the east","I love Beijing Tiananmen"
]data = [default_tokenizer(word) for word in corpus]
transfer,feature = myTfidVectorizer(data)
print(transfer,feature)

结果输出:

[[0.         0.         0.         0.         0.         0.0.         0.         0.78528828 0.6191303  0.        ][0.         0.         0.         0.         0.         0.0.         0.         0.         0.6191303  0.78528828][0.         0.35355339 0.35355339 0.         0.35355339 0.353553390.70710678 0.         0.         0.         0.        ][0.57735027 0.         0.         0.57735027 0.         0.0.         0.57735027 0.         0.         0.        ]] ['beijing' 'east' 'in' 'love' 'rises' 'sun' 'the' 'tiananmen' '北京' '天安门''太阳升']
http://www.lryc.cn/news/607090.html

相关文章:

  • (一)LoRA微调BERT:为何在单分类任务中表现优异,而在多分类任务中效果不佳?
  • 【C++】类和对象 上
  • 逻辑回归算法中的一些问题
  • Leetcode——53. 最大子数组和
  • elementui中rules的validator 用法
  • 在线教程丨全球首个 MoE 视频生成模型!阿里 Wan2.2 开源,消费级显卡也能跑出电影级 AI 视频
  • Windows11 WSL安装Ubntu22.04,交叉编译C语言应用程序
  • 网站建设服务器从入门到上手
  • 《n8n基础教学》第一节:如何使用编辑器UI界面
  • 如何优雅删除Docker镜像和容器(保姆级别)
  • 服务器地域选择指南:深度分析北京/上海/广州节点对网站速度的影响
  • FreeSWITCH与Java交互实战:从EslEvent解析到Spring Boot生态整合的全指南
  • 分布式弹幕系统设计
  • Git 误删分支怎么恢复
  • ABP VNext + Dapr Workflows:轻量级分布式工作流
  • stl的MATLAB读取与显示
  • Blender 4.5 安装指南:快速配置中文版,适用于Win/mac/Linux系统
  • 【Mysql】字段隐式转换对where条件和join关联条件的影响
  • 安全专家发现利用多层跳转技术窃取Microsoft 365登录凭证的新型钓鱼攻击
  • 基于Pipeline架构的光存储读取程序 Qt版本
  • 九、Maven入门学习记录
  • 学习游戏制作记录(各种水晶能力以及多晶体)8.1
  • k8s之NDS解析到Ingress服务暴露
  • Wisdom SSH开启高效管理服务器的大门
  • Git之远程仓库
  • 【全网首个公开VMware vCenter 靶场环境】 Vulntarget-o 正式上线
  • Linux(15)——进程间通信
  • VMware 下 Ubuntu 操作系统下载与安装指南
  • 用 TensorFlow 1.x 快速找出两幅图的差异 —— 完整实战与逐行解析 -Python程序图片找不同
  • Ubuntu-Server-24.04-LTS版本操作系统如何关闭自动更新,并移除不必要的内核