目录
为什么要用特征工程
特征提取(Feature Extraction)
1.目的
2.对应的工具
3.三种方法
4.对应的sklearn的API
(1)字典特征提取
(2)文本特征提取
总结
特征是从数据中抽取出来的对结果有预测有用的信息,可以是文本或者数据。
Feature Engineer。特征工程就是把原始数据转换成特征的过程。特征工程对数据进行处理,使得特征在机器学习算法上发挥更好的作用。
从数学的角度,就是人工的去设计输入变量x。
过程包括:特征选择(Feature Selection)、特征提取(Feature Extraction)、特征构建(Feature Construction)。
目的:筛选出更好的特征,获取更好的训练数据。
要使得预测模型性能达到最佳,不仅要选择最好的算法,更要尽可能从原始数据中获取更多的信息。而特征工程就是获取更多的训练数据。
数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限。
将任意数据(如文本或图像)转换成机器学习的数学特征
sklearn:特征工程
pandas:数据清洗 数据处理
字典特征提取
文本特征提取
图像特征提取(目前主要应用于深度学习)
sklearn.feature_extraction
该包下有对应的上述方法中的字典特征提取、文本特征提取等的API
1.目的
将字典中属于类别的非数值的特征转换成one-hot编码。也就是对字典数据进行特征值化。
one-hot编码:一位有效编码。其方法就是使用n位状态来进行编码,每个状态都有独立的位置,并且在任意的时候,只有一位有效。这样转换成数值的特征不会有数值大小的比较。
也就是为每个类别生成一个布尔列,这些列中只有一列可以为该样本取值为1.
2.对应的sklean的API
sklearn.feature_extraction.DictVectorizer(dtype=, separator=’=’, sparse=True, sort=True)
参数介绍
参数 | 含义 |
dtype | 特征值的类型,默认的为float |
separator | 默认为‘=’。当构造one-hot编码的特征值时要使用的分割字符串。分割传入字典数据的键与值的字符串,也就是最后生成的特征矩阵的列名的键与值的分割符。 |
sparse | 默认为true:产生一个稀疏矩阵;false:不产生稀疏矩阵,返回的是二维数组的one-hot编码方式 |
sort | 默认为true:拟合要对feature_names(包含所有特征名称)和vocabulary_(特征名称和特征列索引映射字典)进行排序 |
DictVectorizer为一个转换器父类。调用该API这个是实例化一个转换器类,里面有一些迭代器,用来返回该转换器的一些信息。主要API有以下几个:
(1)DictVectorizer.fit_transform(x)
将输入的字典x转换成矩阵,返回的是稀疏矩阵sparse矩阵
(2)DictVectorizer.inverse_transform(x)
将矩阵还原成特征字典列表。传入的x必须是DictVectorizer经过transform或者fit_transform产生的x
(3)DictVectorizer.get_feature_names()
返回类别的名称
DictVectorizer.transform(X)
将x转换为numpy.ndarray或者Scipy.sparse
3.实例
通过一个实例看下一个样本集合是怎么进行提取特征值的
# 导入DictVectorizer
from sklearn.feature_extraction import DictVectorizer
#字典特征提取
def dict():
#定义数据集
data = [{'city':'上海','temperature':100},
{'city':'北京','temperature':60},
{'city':'深圳','temperature':30}]
#1.实例化一个转换器类
transfor = DictVectorizer()
#2.调用fit_transform,生成矩阵
data_new = transfor.fit_transform(data)
print(transfor.get_feature_names())
print(data_new)
return None
从上面的代码中可以看出:
(1)首先要从sklearn中导入DictVectorizer
(2)定义了一个数据集
(3)实例化一个DictVectorizer转换器的实例
(4)调用fit_transform,生成矩阵
我们看下最后输出的内容:
['city=上海', 'city=北京', 'city=深圳', 'temperature']
(0, 0) 1.0
(0, 3) 100.0
(1, 1) 1.0
(1, 3) 60.0
(2, 2) 1.0
(2, 3) 30.0
默认的返回的是sparse矩阵,并不是我们之前说的字典特征就是将特征的类别转换成one-hot编码的形式。
这个sparse矩阵即稀疏矩阵,特点就是把非0值按位置表示出来。为什么会有这种方式呢?
字典特征提取的目的就是将不同类别的特征转换成one-hot编码的方式,但是如果我们有1000甚至更多的类别,那么在转换成one-hot编码的时候,就会有很多很多的0存在,这样就会浪费空间,而这个稀疏矩阵只需要把非0的位置表示出来,那么其他的位置就是0,从而节省内存,提供加载效率。
那么对于上述的返回值的表示的含义如下:
那么怎么得到one-hot编码的矩阵呢?这个就是在实例化DictVectorizer的一个参数sparse,如果我们把该值设置为false,输出结果
transfor = DictVectorizer(sparse=False)
把每一个样本转换成了一个向量 的二维数组
['city=上海', 'city=北京', 'city=深圳', 'temperature']
[[ 1. 0. 0. 100.]
[ 0. 1. 0. 60.]
[ 0. 0. 1. 30.]]
从这个矩阵和对比上面输出的内容,可以看出这个60的确就是在第2行第3列的位置。我们将刚才的每个样本中的两个特征值转换四个特征值来表示,也就是第一个特征值为1,其他为0的就是上海,第2个特征值为1,其他为0的就是北京等等。这样就把字符串的字典特征转换成了one-hot编码的形式。
4.应用场景
(1)数据集中有很多的类别特征的时候
首先要将数据集的特征转换成字典类型,然后使用DictVectorizer进行转换
(2)本身拿到的数据就是字典类型
一篇文章进行分类的话,使用什么作为特征更容易分类呢?通常采用单词作为特征值更容易分类。当然句子、短语、字母都可以作为特征。
通常取文章中出现的单词作为特征值。对应的sklearn的API为
sklearn.feature_extraction.text.CountVectorizer(input='content', encoding='utf-8',
decode_error='strict', strip_accents=None, lowercase=True,
preprocessor=None, tokenizer=None, analyzer='word',
stop_words=None, token_pattern=r"(?u)\b\w\w+\b",
ngram_range=(1, 1), max_df=1.0, min_df=1,
max_features=None, vocabulary=None, binary=False,
dtype=np.float64, norm='l2', use_idf=True, smooth_idf=True,
sublinear_tf=False)
统计是单词出现的频率,返回的是词频矩阵
对应的参数含义
参数 | 含义 |
input | 一般使用默认即可,也可以设置为filename/file |
encoding | 编码方式 |
decode_error | 默认为strict,遇到不能解码的字符串则抛出UnicodeDecodeError ignore:忽略错误 replace |
strip_accents | 默认为None ascil或unicode:在预处理的去除raw document的重音符号 |
analyzer | 一般使用默认word,可设置为string,也可以设置为callable |
preprocessor | 设置为None或callable |
token | 设置为None或callable |
ngram_range | 词组切分的长度范围 |
stop_words | 设置停用词, |
lowercase | 将所有字符变为小写 |
token_pattern | 过滤规则 |
max_df | 设置范围为[0.0,1.0],作用是阀值,当某个词的document frequence>max_df,则不被作为关键词。如果设定了vacabulary,则该参数无效 |
min_df | 当某个词的document frequence |
max_features | 默认为None,对关键词的tem_frequency进行降序排序,只取前max_features作为关键词频 |
vocabulary | 默认为None,自动从数据集中构建关键词集 |
binary | 默认为false,一个关键词出现次数为n词,如果为true,非0的n将全部只为1 |
dtype | 对得到的词频矩阵,设置这个矩阵的数组类型 |
调用该API这个是实例化一个转换器类,里面有一些迭代器,用来返回该转换器的一些信息。主要API有以下几个:
(1)CountVectorizer.fit_transform(x)
x为数组或者sparse矩阵,返回值为sparse矩阵
(2)CountVectorizer.inverse_transform(x)
将transfor的sparse矩阵还原。x必须为transform或者fit_transform产生的x
(3)CountVectorizer.get_feature_names()
返回单词列表
#导入相应的包
from sklearn.feature_extraction.text import CountVectorizer
def count():
#文本特征抽取
data=["life is short , i like like python","lift is too long , i dislike "," i am a IT "]
#实例化一个转换器类
transfor = CountVectorizer()
#调用fit_transform()
data_new = transfor.fit_transform(data)
print(transfor.get_feature_names())
print(data_new)
return None
跟字典特征的提取的过程一样,导入相应的包、实例化转换器、调用迭代器输出矩阵,我们看下输出的内容:
['am', 'dislike', 'is', 'it', 'life', 'lift', 'like', 'long', 'python', 'short', 'too']
(0, 4) 1
(0, 2) 1
(0, 9) 1
(0, 6) 2
(0, 8) 1
(1, 2) 1
(1, 5) 1
(1, 10) 1
(1, 7) 1
(1, 1) 1
(2, 0) 1
(2, 3) 1
同字典特征提取一样,返回的是稀疏矩阵,即非0值的位置。
我们发现CountVectorizer并没有一个sparse的参数,但是可以调用sparse矩阵的toarray()转换成对应的样本数量的n维数组的形式。
print(data_new.toarray())
输出的结果如下
['am', 'dislike', 'is', 'it', 'life', 'lift', 'like', 'long', 'python', 'short', 'too']
[[0 0 1 0 1 0 2 0 1 1 0]
[0 1 1 0 0 1 0 1 0 0 1]
[1 0 0 1 0 0 0 0 0 0 0]]
代表的含义就是上面的特征词在每个样本中出现的次数 。
在看下在实例化转换器 CountVectorizer的stop_words参数的含义,指的是停用词,即这些词不会出现在特征词中。网上也有好多停用词表供使用。
#实例化一个转换器类
transfor = CountVectorizer(stop_words=['is','too','am'])
从运行结果可以看到,‘is’,‘too’,‘am’已经不在特征词中。
['dislike', 'it', 'life', 'lift', 'like', 'long', 'python', 'short']
(0, 2) 1
(0, 7) 1
(0, 4) 2
(0, 6) 1
(1, 3) 1
(1, 5) 1
(1, 0) 1
(2, 1) 1
[[0 0 1 0 2 0 1 1]
[1 0 0 1 0 1 0 0]
[0 1 0 0 0 0 0 0]]
我们刚才的例子中提到的是英文文章的特征提取,那么如果我们把数据集换成中文呢?结果是不是也是一样呢?我们在刚才的例子中换下数据集
def count_ch():
#文本特征抽取
data = ["今天的天气很晴朗", "心情也跟着好起来", "学习机器学习真有意思"]
#实例化一个转换器类
transfor = CountVectorizer()
#调用fit_transform()
data_new = transfor.fit_transform(data)
print(transfor.get_feature_names())
print(data_new)
print("转换成n维数组:")
print(data_new.toarray())
return None
我们看下输出的结果如下:
['今天的天气很晴朗', '学习机器学习真有意思', '心情也跟着好起来']
(0, 0) 1
(1, 2) 1
(2, 1) 1
转换成n维数组:
[[1 0 0]
[0 0 1]
[0 1 0]]
我们可以看到每个样本都作为了特征词,这个主要由于英文自动有空格进行分割单词,但是中文没有分割,所以每个短语都成了单独的一个特征词,那么中文的特征提取怎么做呢?
当然第一种比较笨的方法,就是通过自动给文本中的内容按照词组进行加空格进行分组,但是这种方式显然无法利用实际应用中。那么就有了另外一种方式通过结巴分词的方式来将原文本进行分词。当然还有其他的一些分词方式,如盘古分词、Yaha分词、清华YHULAC等
结巴分词就是将文本中的词语进行分开。有三种模式:
(1)精确模式:试图将句子最精确的切开,适合文本分析
(2)全模式:把句子中所有的可以成词的词语都扫描出来
(3)搜索引擎模式:在精确模式的基础上,对长词在进行切分,适合用于搜索引擎分词
默认的为精确模式。我们看下同样一个文本在不同的模式下的输出的区别
#导入相应的包
import jieba
def cut_word_test(text):
#传入一个词组然后进行自动分词
#用空格来连接结巴分词之后的列表
result = " ".join(list(jieba.cut(text, cut_all=True)))
print("<全模式>结巴分词的结果为:")
print(result)
result = " ".join(list(jieba.cut(text, cut_all=False)))
print("<精确模式>结巴分词的结果为:")
print(result)
result = " ".join(list(jieba.cut(text)))
print("<默认>结巴分词的结果为:")
print(result)
result = " ".join(list(jieba.cut_for_search(text)))
print("<搜索引擎模式>结巴分词的结果为:")
print(result)
return result
输出的结果如下:
<全模式>结巴分词的结果为:
我 在 南京 南京理工 理工 理工大 理工大学 工大 大学 读研 研究 研究生
<精确模式>结巴分词的结果为:
我 在 南京 理工大学 读 研究生
<默认>结巴分词的结果为:
我 在 南京 理工大学 读 研究生
<搜索引擎模式>结巴分词的结果为:
我 在 南京 理工 工大 大学 理工大 理工大学 读 研究 研究生
还是通过一个实例看下中文的特征提取,区别于英文的特征提取,在进行调用转换器进行转换成矩阵之前,首先要对数据集进行分词。
def cut_word(text):
#传入一个词组然后进行自动分词
print("中文输入的内容为:" + text)
#用空格来连接结巴分词之后的列表
result = " ".join(list(jieba.cut(text)))
print("默认的结巴分词的结果为:")
print(result)
return result
def count_ch1():
#中文特征提取,自动分词
data = ["今天的天气很晴朗", "心情也跟着好起来", "学习机器学习真有意思"]
#进行分词
data_cut=[]
for text in data:
data_cut.append(cut_word(text))
#实例化一个转换器类
transfor = CountVectorizer()
#调用fit_transform()
data_new = transfor.fit_transform(data_cut)
print(transfor.get_feature_names())
print(data_new)
print("转换成n维数组:")
print(data_new.toarray())
从上面的代码中可以看出我们在使用CountVectorizer进行转换的数据集,是通过结巴分词进行分词的数据集,其他的就和英文特征提取的步骤一样:实例化转换器,调用转换器的迭代器进行转换,最后输出的结果如下:
['今天', '天气', '学习', '心情', '晴朗', '有意思', '机器', '起来', '跟着']
(0, 0) 1
(0, 1) 1
(0, 4) 1
(1, 3) 1
(1, 8) 1
(1, 7) 1
(2, 2) 2
(2, 6) 1
(2, 5) 1
转换成n维数组:
[[1 1 0 0 1 0 0 0 0]
[0 0 0 1 0 0 0 1 1]
[0 0 2 0 0 1 1 0 0]]
同样我们可以在实例化CountVectorizer的时候,通过stop_words来过滤某些词组。
上面介绍的是统计所有特征词出现的词频,那么怎么去衡量一个特征词的重要程度呢?另外对一个文章进行分类的时候,怎么去确定这个文章的关键词呢?
所谓的关键词就是在某一个类别的文章中出现的次数很多,在其他文章中出现的次数很少或几乎不出现。
上面统计了两篇文章中对应的词组出现的一个比例关系,我们发现第一篇文章中出现了大量的“车”、“共享 ”,我们就猜测可能是在讲共享单车的一篇互联网科技文章,而另外一篇出现了大量的“经济”、“银行”、“证券”,那就可能是一篇财经类的文章,并且两篇词组在一篇文章中的基本上不会出现在另外一篇文章中,那我们就可以通过这些词组来区分是哪类文章了。
其中sklearn中有专门的API来实现挑选这种关键词:文本特征提取TfidfVectorizer
用来表示在某个词或者短语在多篇文章中出现的次数的情况,可以看出哪个词语更为重要。
TF-IDF的主要思想:如果某个词语或者短语在一篇文章中出现的概率高,并且在其他文章中很少出现,那么则认为该词语或者短语具有很好的类别区分能力,适合用来分类。
作用:主要用来衡量一个字词对于一个文章集或者语料库中的一份文章的重要程度。
(1)TF:词频Term Frequency,指定某一个给定的词语在该文章中出现的频率。
(2)IDF:逆向文档频率Inverse Document Frequent,指的是一个词语普遍重要性的度量。某一个词语的IDF,
(3)TF-IDF:就是TF和IDF的乘积
我们通过一个实例来看下上面三个概念。
假设我们有一个10000篇文件的语料库,其中1000篇文章中都含有“房贷”,100篇文章中含有“彩妆”
现在有A文章,出现了10个“房贷”,B文章出现了10个“彩妆”,A和B的文章总的词组量都是100个,那么对于“房贷”和“彩妆”的TF-IDF是多少呢?
文章A:TF= 10/100 = 0.1
文章B:TF=1 0/100 = 0.1
我们但从TF中看不出这两个词的重要性
文章A:IDF = lg(10000/1000) = 1
文章B:IDF = lg(10000/100) = 2
那么A和B的TF-IDF为
文章A:TF-IDF = 0.1x1 = 0.1
文章B:TF-IDF = 0.1x2 = 0.2
那么可以很明显的看出文章B的“彩妆”的重要程度用数值进行表示出来。
(4)sklearn的TF-IDF的API -TfidfVectorizer
sklearn.feature_extraction.text.TfidfVectorizer(input='content', encoding='utf-8',
decode_error='strict', strip_accents=None, lowercase=True,
preprocessor=None, tokenizer=None, analyzer='word',
stop_words=None, token_pattern=r"(?u)\b\w\w+\b",
ngram_range=(1, 1), max_df=1.0, min_df=1,
max_features=None, vocabulary=None, binary=False,
dtype=np.float64, norm='l2', use_idf=True, smooth_idf=True,
sublinear_tf=False)
参数含义可以参见CountVectorizer,不在多余介绍。其里面的迭代器和CountVectorizer也是类似的,也不再多余介绍。通过一个实例看下这个API的使用
from sklearn.feature_extraction.text import CountVectorizer,TfidfVectorizer
def tfidf():
#使用TF-idf进行文本特征提取
data = ["今天的天气很晴朗", "心情也跟着好起来", "学习机器学习真有意思"]
#进行分词
data_cut=[]
for text in data:
data_cut.append(cut_word(text))
#实例化一个转换器类
transfor = TfidfVectorizer()
#调用fit_transform()
data_new = transfor.fit_transform(data_cut)
print(transfor.get_feature_names())
print(data_new)
print("转换成n维数组:")
print(data_new.toarray())
return None
看下输出的结果
默认的结巴分词的结果为:
学习 机器 学习 真 有意思
['今天', '天气', '学习', '心情', '晴朗', '有意思', '机器', '起来', '跟着']
(0, 4) 0.5773502691896257
(0, 1) 0.5773502691896257
(0, 0) 0.5773502691896257
(1, 7) 0.5773502691896257
(1, 8) 0.5773502691896257
(1, 3) 0.5773502691896257
(2, 5) 0.40824829046386296
(2, 6) 0.40824829046386296
(2, 2) 0.8164965809277259
转换成n维数组:
[[0.57735027 0.57735027 0. 0. 0.57735027 0.
0. 0. 0. ]
[0. 0. 0. 0.57735027 0. 0.
0. 0.57735027 0.57735027]
[0. 0. 0.81649658 0. 0. 0.40824829
0.40824829 0. 0. ]]
我们可以看到不同的词组的TF-IDF的大小不一样,有的值比较大,有的值会比较小,值比较大的就体现这个词的重要性,具有分类意义。
所谓的特征提取就是将原始数据进行特征化,将不是数值的数据进行转换成数值。
字典特征抽取DictVectorizer就是将类别转换成one-hot编码;
文本特征提取CountVectorizer用来统计词组的个数,统计的是词频;
TfidfVectorizer用来统计词组的重要性程度,对文章分类的前期数据处理。