特征提取

2019-08-29 11:12:35 浏览数 (1)

首先必须知道什么是特征工程

什么是特征工程

特征工程是通过对原始数据的处理和加工,将原始数据属性通过处理转换为数据特征的过程,属性是数据本身具有的维度,特征是数据中所呈现出来的某一种重要的特性,通常是通过属性的计算,组合或转换得到的。比如主成分分析就是将大量的数据属性转换为少数几个特征的过程。某种程度而言,好的数据以及特征往往是一个性能优秀模型的基础

那么如何提取好的特征将是本文主要内容

我们将简要介绍一些常用的特征提取方法:

  • 字典加载特征:DictVectorizer
  • 文本特征提取:词频向量(CountVectorizer)
  • TF-IDF向量(TfidfVectorizer,TfidfTransformer) 特征哈希向量(HashingVectorizer)
  • 图像特征提取: 提取像素矩阵提取边缘和兴趣点

字典加载特征:DictVectorizer

python中的字典存储特征是一种常用的做法,其优点是容易理解。但是sklearn的输入特征必须是 numpyscipy数组。可以用DictVectorizer从字典中加载特征转换成numpy数组,并且对分类特征 会采用独热编码(one-hot)

字典特征提取器:
  • 将字典数据结构抽和向量化
  • 类别类型特征借助原型特征名称采用0 1 二值方式进行向量化
  • 数值类型特征保持不变
代码语言:javascript复制
from sklearn.feature_extraction import DictVectorizer

# 定义一个字典列表 用来表示多个数据样本
measurements = [
    {"city": "Dubai", "temperature": 33.0},
    {"city": "London", "temperature": 12.0},
    {"city": "San Fransisco", "temperature": 18.0},
]

# 初始化字典特征抽取器
vec = DictVectorizer()
data = vec.fit_transform(measurements).toarray()
# 查看提取后的特征值
print(data)
'''
[[ 1.  0.  0. 33.]
 [ 0.  1.  0. 12.]
 [ 0.  0.  1. 18.]]
'''
# 查看提取后特征的含义
print(vec.get_feature_names())
'''
['city=Dubai', 'city=London', 'city=San Fransisco', 'temperature']
'''

上面代码讲解: DictVectorizer将python的字典列表,转化成容易给sklearn处理的数据,所以第一条的 {"city": "Dubai", "temperature": 33.0} 变成 [ 1. 0. 0. 33.] ,同时可以看到提取后的特征的含义,二值方式进行向量化,1代表是,0代表不是,说明city:是Dubai

文本特征提取:词频向量(CountVectorizer)

词库模型(Bag-of-words model)是文字模型化最常用方法,它为每个单词设值一个特征值。依据 是用类似单词的文章意思也差不多。

CountVectorizer 类会将文档全部转换成小写。然后把句子分割成词块(token)或有意义的字母序 列,并统计它们出现的次数。词块大多是单词,但是他们也可能是一些短语,字母长度小于2的词 块(如 I, a)被略去。 可以用stop_words选项排除一些常用但没有太多意义的助词(如is,are,in)。

代码语言:javascript复制
from sklearn.feature_extraction.text import CountVectorizer 
corpus = [  'UNC played Duke in basketball', 
          'Duke lost the basketball game,game over', 
          'I ate a sandwich' ]  
vectorizer = CountVectorizer(stop_words = 'english') # 设置英语的常用停用词 
print (vectorizer.fit_transform(corpus).todense())  
print (vectorizer.vocabulary_) 
OUIT:
[[0 1 1 0 0 1 0 1]
 [0 1 1 2 1 0 0 0]
 [1 0 0 0 0 0 1 0]]
{'unc': 7, 'played': 5, 'duke': 2, 'basketball': 1, 'lost': 4, 'game': 3, 'ate': 0, 'sandwich': 6}

上面代码讲解:

在列表定义了三个字符串,输出了只有数字列表 ,而生成的字典的vules值是index下标 [0 1 1 0 0 1 0 1] 第二个单词 basketball index 为 1 出现1次,第三个单词 duke 出现1次,第六个单词 played 出现1次 ,第八个单词 unc 出现1次, 缺少的 in 因为是2个字母,所以被忽视。

对于中文又是如何处理的呢?

中文文本进行词频特征提取,可以先用jieba进行分词

代码语言:javascript复制
import jieba
seg_list = jieba.cut("大家好,我叫毛利") 
"/".join(seg_list)
OUT:
'大家/好/,/我/叫/毛利'

将句子分成一个一个分词

代码语言:javascript复制
import jieba 
from sklearn.feature_extraction.text import CountVectorizer 
corpus = [  '大家好,我叫毛利',  '毛利喜欢写博客',  '毛利说:“以后要多多写博客' ]  
cutcorpus = ["/".join(jieba.cut(x)) for x in corpus] 
vectorizer = CountVectorizer(stop_words = ['好','我'])  
counts = vectorizer.fit_transform(cutcorpus).todense() 
print (counts)  
print (vectorizer.vocabulary_) #查看映射字典    

OUT:
['大家/好/,/我/叫/毛利', '毛利/喜欢/写/博客', '毛利/说/:/“/以后/要/多多/写/博客']
[[0 0 0 0 1 1]
 [0 1 1 0 0 1]
 [1 1 0 1 0 1]]
{'大家': 4, '毛利': 5, '喜欢': 2, '博客': 1, '以后': 0, '多多': 3}

跟上面逻辑一样

那么如何知道这三句话相似程度呢?

用词频向量的欧式距离(L2范数)来衡量两个文档之间的距离(距离越小越相似)

公式说明: 因为用了CountVectorizer 将文本转化为数字,那么就用数学的方法,如果d比较小,那么两段文本的比较相近

代码语言:javascript复制
from sklearn.feature_extraction.text import CountVectorizer  #导入计数记录器
from sklearn.metrics.pairwise import  euclidean_distances #导入欧式距离
vectorizer = CountVectorizer() 
for x,y in [[0,1],[0,2],[1,2]]:  
    dist = euclidean_distances(counts[x],counts[y])  
    print('文档{}与文档{}的距离{}'.format(x,y,dist))  

OUT:
文档0与文档1的距离[[1.73205081]]
文档0与文档2的距离[[2.]]
文档1与文档2的距离[[1.73205081]]

可见['大家/好/,/我/叫/毛利', '毛利/喜欢/写/博客', '毛利/说/:/“/以后/要/多多/写/博客'] 第一句话和第二句话,第二句话和第三句话比较接近

Tf–idf权重向量

TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降

单词频率对文档意思有重要作用,但是在对比长度不同的文档时,长度较长的文档的单词频率将明 显倾向于更大。因此将单词频率正则化为权重是个好主意。

此外,如果一些词在需要分析的文集中的所有文档中都出现,那么可以认为这些词是文集中的常用 词,对区分文集中的文档帮助不大。因此,可以把单词在文集中出现的频率考虑进来作为修正。

一脸懵逼吧,其实就是有时候处理一篇文档很长,另一篇又非常短,如何处理?就是将单词出现频率化为占总文档的百分比,但是如果一些词都出现毫无区别价值,又占了比例,就要去除。Tf-idf即是考虑到这两方面因素设计的一个优化的词频权重指标。在搜索和数据挖掘中经常使用。

上公式

代码语言:javascript复制
Tf —— Term frequncy,词频
idf —— inverse document frequency,逆向文件频率
n_d —— 文集中文档总数
df —— 含有该单词的文档数量

公式理解: 如果df足够大,那么接近n_d,idf也就是逼近于0,那么Tfidf,也就很小了

Tfidf再正则化为权重

这里的xTfidf

代码语言:javascript复制
from sklearn.feature_extraction.text import TfidfTransformer 
transformer = TfidfTransformer(smooth_idf=False) 
counts = [[1, 0, 1],        
          [2, 3, 0],      
          [3, 0, 1],    
          [4, 0, 1],     
          [3, 2, 0],        
          [3, 0, 3]]
tfidf = transformer.fit_transform(counts)                        

tfidf.toarray() #输出成array

# 利用上面公式算出
OUT:
array([[0.57973867, 0.        , 0.81480247],
       [0.30276088, 0.95306655, 0.        ],
       [0.90554997, 0.        , 0.42423963],
       [0.94345574, 0.        , 0.33149853],
       [0.58149261, 0.81355169, 0.        ],
       [0.57973867, 0.        , 0.81480247]])

TfidfTransformer可以把普通的词频向量转换成Tf-idf权重向量。

TfidfVectorizer则将 CountVectorizerTfidfTransformer的功能集成在了一起。

代码语言:javascript复制
from sklearn.feature_extraction.text import TfidfVectorizer 
vectorizer = TfidfVectorizer()
vectorizer.fit_transform(cutcorpus).toarray()
vectorizer.vocabulary_
OUT:
'大家': 4, '毛利': 5, '喜欢': 2, '博客': 1, '以后': 0, '多多': 3}

完整代码

代码语言:javascript复制
import jieba 
from sklearn.feature_extraction.text import CountVectorizer 
corpus = [  '大家好,我叫毛利',  '毛利喜欢写博客',  '毛利说:“以后要多多写博客' ]  
cutcorpus = ["/".join(jieba.cut(x)) for x in corpus] 
vectorizer = CountVectorizer(stop_words = ['好','我'])  
counts = vectorizer.fit_transform(cutcorpus).todense() 
print (counts)  
print (vectorizer.vocabulary_) #查看映射字典  
from sklearn.feature_extraction.text import CountVectorizer 
from sklearn.metrics.pairwise import euclidean_distances 
vectorizer = CountVectorizer() 
for x,y in [[0,1],[0,2],[1,2]]:  
    dist = euclidean_distances(counts[x],counts[y])  
    print('文档{}与文档{}的距离{}'.format(x,y,dist))  

from sklearn.feature_extraction.text import TfidfVectorizer 
vectorizer = TfidfVectorizer()
vectorizer.fit_transform(cutcorpus).toarray()
vectorizer.vocabulary_

OUT:

代码语言:javascript复制
[0 0 0 0 1 1]
 [0 1 1 0 0 1]
 [1 1 0 1 0 1]]
{'大家': 4, '毛利': 5, '喜欢': 2, '博客': 1, '以后': 0, '多多': 3}
文档0与文档1的距离[[1.73205081]]
文档0与文档2的距离[[2.]]
文档1与文档2的距离[[1.73205081]]
{'大家': 4, '毛利': 5, '喜欢': 2, '博客': 1, '以后': 0, '多多': 3}

0 人点赞