继续做早鸟,首先这一期的任务提纲:
- TF-IDF原理。
- 文本矩阵化,使用词袋模型,以TF-IDF特征值为权重。(可以使用Python中TfidfTransformer库)
- 互信息的原理。
- 使用第二步生成的特征矩阵,利用互信息进行特征筛选
TF-IDF原理
感觉在word2vec 特别是现在的contextual word embedding之后,利用tf-idf直接向量化文本几乎已经弃用了,但是tf-idf作为权重,用来进行句子或者篇章中词向量的加权,也依然在被使用。
第一次接触tf-idf是很早以前,读吴军先生的《数学之类》[5]的时候,当时就被一系列文本向量化的表示和操作所吸引。回到主题,tf-idf实际上是两个指标,即tf Term Frequency,词频,idf Inverse Document Frequency 逆文本频率 。
TF-IDF的主要思想是:如果某个词在一篇文章中出现的频率高,并且在其他文章中很少出现,则认为此词具有很好的类别区分能力,适合用来分类。
形式化的表达为:
t
f
i
,
j
=
n
i
,
j
∑
k
n
k
,
j
tf_{i,j} = \frac{n_{i,j}}{\sum_k n_{k,j}}
tfi,j=∑knk,jni,j,
i
d
f
i
=
∣
D
∣
∣
j
:
t
i
∈
D
j
∣
idf_i = \frac{|D|}{|{j:t_i \in D_j}|}
idfi=∣j:ti∈Dj∣∣D∣,
t
f
tf
tf-
i
d
f
i
=
t
f
i
,
j
∗
i
d
f
i
idf _i= tf_{i,j} * idf_i
idfi=tfi,j∗idfi。其中i 表示词
i
i
i ,
j
j
j 表示所属文章
D
j
D_j
Dj,
∣
x
∣
|x|
∣x∣ 表示集合
x
x
x中元素的个数。
以TF-IDF特征值为权重的文本矩阵化
直接上代码
# -*- coding: utf-8 -*-
# @Time : 2019/4/12 11:43
# @Author : Lei Wang
# @Site :
# @File : tfidf.py
# @Software: PyCharm
import pkuseg
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
text = []
with open(r'..\cnews\cnews.train.txt', 'r', encoding = 'utf-8') as fsource:
text_line= fsource.readline()
seg = pkuseg.pkuseg()
seg_list = seg.cut(text_line)
text.extend(seg_list)
with open(r'..\stoplist_baidu.txt', 'r', encoding = 'utf-8') as fstop:
content = fstop.read()
stop_words = content.split('\n')
count_vectorizer = CountVectorizer(stop_words=stop_words)
tfidf_transformer = TfidfTransformer()
tfidf = tfidf_transformer.fit_transform(count_vectorizer.fit_transform(text))
print (tfidf)
print('-------------给个小一点的例子--------------')
corpus = [ 'This is the first document.',
'This is the second second document.',
'And the third one.',
'Is this the first document?',
]
s_count_vectorizer = CountVectorizer()
s_tfidf_transformer = TfidfTransformer()
s_tfidf = s_tfidf_transformer.fit_transform(s_count_vectorizer.fit_transform(corpus))
print (s_tfidf)
结果截图:
互信息的原理
谈起互信息这个话题,还是依着《数学之美》[5]思路,先从信息和熵的定义开始说。信息是用来消除不确定性的。换句话说,知道的信息越多,不确定性就越低。对于一个变量 X X X,如果我们知道,我们知道它的分布 P ( X ) P(X) P(X), 则可以定义它的信息熵为: H ( X ) = − ∑ x ∈ X P ( x ) l o g P ( x ) H(X) = - \sum_{x \in X} P(x) logP(x) H(X)=−∑x∈XP(x)logP(x)。 这里继续给出条件熵的概念,类比于条件概率分布, H ( X ∣ Y ) = − ∑ x ∈ X , y ∈ Y P ( x , y ) l o g P ( x ∣ y ) H(X|Y)=-\sum_{x \in X,y \in Y} P(x,y)logP(x|y) H(X∣Y)=−∑x∈X,y∈YP(x,y)logP(x∣y)那么问题来了,互信息呢? 互信息其实是用来度量两个事件的相关性,其定义如下:
I ( X ; Y ) = − ∑ x ∈ X , y ∈ Y P ( x , y ) P ( x ) P ( y ) = H ( X ) − H ( X ∣ Y ) I(X;Y) = -\sum_{x \in X,y \in Y} \frac{P(x,y)}{P(x)P(y)} = H(X) - H(X|Y) I(X;Y)=−∑x∈X,y∈YP(x)P(y)P(x,y)=H(X)−H(X∣Y)
关于互信息,其实我当年做过一个很有意思的推导,就是想推导多元的互信息熵。我当时只是推导了三元,基本结论是
I
(
X
;
Y
;
Z
)
=
I
(
X
;
Y
)
−
I
(
X
;
Y
∣
Z
)
=
I
(
X
;
Y
)
−
∑
l
o
g
P
(
x
y
z
)
P
(
z
)
P
(
x
z
)
P
(
y
z
)
I(X;Y;Z) = I(X;Y) - I(X;Y|Z) = I(X;Y) - \sum log\frac{P(xyz)P(z)}{P(xz)P(yz)}
I(X;Y;Z)=I(X;Y)−I(X;Y∣Z)=I(X;Y)−∑logP(xz)P(yz)P(xyz)P(z)
正确性有待各位有心的观众检验,多元互信息的表达我参考文献[6]。
关于互信息,数学之美上还有一个关于tf-idf的信息学解释,有兴趣可以去看书的108-109页的相关内容,写的非常清晰易懂,这里就不再赘述了。
既然写到这里,那就顺便写一下深度学习中最常用的相对熵和交叉熵。相对熵,也就是KL散度的定义,他是用来衡量两个取值为正的函数f(X)和g(X)的相似性,即
K
L
(
f
(
X
)
∣
∣
g
(
X
)
)
=
∑
x
∈
X
f
(
x
)
l
o
g
f
(
x
)
g
(
x
)
KL(f(X)||g(X))=\sum_{x \in X} f(x) log\frac{f(x)}{g(x)}
KL(f(X)∣∣g(X))=∑x∈Xf(x)logg(x)f(x), 而交叉熵则是
H
(
f
(
x
)
,
g
(
x
)
)
=
−
∑
x
∈
X
f
(
x
)
l
o
g
g
(
x
)
=
H
(
f
(
x
)
)
+
K
L
(
f
(
x
)
∣
∣
g
(
x
)
)
H(f(x),g(x))=-\sum_{x \in X} f(x) logg(x) = H(f(x)) +KL(f(x)||g(x))
H(f(x),g(x))=−∑x∈Xf(x)logg(x)=H(f(x))+KL(f(x)∣∣g(x))。当然这两个熵也都有对应的连续变量形式的表达。可以看出:
- 两个完全相同的函数,交叉熵为0
- 交叉熵越大,两个函数差异越大;反之亦然
- 对于概率密度或者概率分布函数,如果取值均大于0,交叉熵可以度量两个随机分布的差异性
利用互信息进行特征筛选
特征选择指的是删除了原始特征里和结果预测关系不大的特征,而不像降维会去做特征的计算组合构成了新的特征。 这里参考了文献[7]
常用的方法有:过滤法,包裹法和嵌入法
1. 过滤型
方法:评价单个特征和结果之间的相关程度,排序留下Top相关的部分。
评价方式:Pearson相关系数、互信息
缺点:没有考虑到特征之间的关联作用,可能把有用的关联特征踢掉。因此工业界使用的比较少
python包:SelectKBest指定过滤个数、SelectPercentile指定过滤的百分比
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
iris = load_iris()
X, y = iris.data, iris.target
X.shape
(150, 4)
X_new = SelectKBest(chi2, k = 2).fit_transform(X, y)
X_new.shape
(150, 2)
2.包裹型
方法:把特征选择看做一个特征子集搜索问题,筛选各种特征子集,用模型评估效果(递归特征删除算法,RFE)。
应用在LR上过程:用全量特征跑一个模型;删掉5~10%的弱特征,观察准确率/AUC的变化;逐步进行,直至准确率/AUC出现大的下滑停止。
python:RFE
from sklearn.feature_selection import RFE
from sklearn.linear_model import LinearRegression
from sklearn.datasets import load_boston
boston = load_boston()
X = boston["data"]
Y = boston["target"]
names = boston["feature_names"]
lr = LinearRegression()
rfe = RFE(lr, n_features_to_select = 1)
rfe.fit(X, Y)
print("Features sorted by their rank:")
print(sorted(zip(map(lambda x: round(x, 4), rfe.ranking_),names)))
Features sorted by their rank:
[(1, ‘NOX’), (2, ‘RM’), (3, ‘CHAS’), (4, ‘PTRATIO’), (5, ‘DIS’), (6, ‘LSTAT’), (7, ‘RAD’), (8, ‘CRIM’), (9, ‘INDUS’), (10, ‘ZN’), (11, ‘TAX’), (12, ‘B’), (13, ‘AGE’)]
3.嵌入型
方法:根据模型来分析特征重要性,最常见的方式为正则化方式来做特征选择
举例:举个例子,最早在电商用LR做CRT预估,在3-5亿维系数的特征上用L1正则化的LR模型。剩余2-3千万的feature,意味着其他的feature的重要程度不够。
python:feature_selection.SelectFromModel选出权重不为0的特征
from sklearn.svm import LinearSVC
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectFromModel
iris = load_iris()
X, y = iris.data, iris.target
X.shape
(150, 4)
lsvc = LinearSVC(C = 0.01, penalty = "l1", dual = False).fit(X, y)
model = SelectFromModel(lsvc, prefit = True)
X_new = model.transform(X)
X_new.shape
(150, 3)
回到主题,如何利用互信息进行特征筛选, 还是利用sklearn的库,因为这个需要标签,所以用了IMDB,代码如下
# -*- coding: utf-8 -*-
# @Time : 2019/4/13 12:11
# @Author : Lei Wang
# @Site :
# @File : feature_selection.py
# @Software: PyCharm
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import mutual_info_classif
#选择K个最好的特征,返回选择特征后的数据
#arr = SelectKBest(mutual_info_classif, k=2).fit_transform(tfidf, target[:99])
from nltk.corpus import stopwords
import collections
import pandas as pd
import numpy as np
import os
import codecs
pos_list=[]
with open('../Sentiment_IMDB/aclImdb/train/pos_all.txt','r',encoding='utf8')as f:
line=f.readlines()
pos_list.extend(line)
neg_list=[]
with open('../Sentiment_IMDB/aclImdb/train/neg_all.txt','r',encoding='utf8')as f:
line=f.readlines()
neg_list.extend(line)
#创建标签
label=[1 for i in range(12500)]
label.extend([0 for i in range(12499)])
#评论内容整合
content=pos_list.extend(neg_list)
content=pos_list
stop_words=set(stopwords.words('english'))
count_vectorizer = CountVectorizer(stop_words=stop_words)
tfidf_transformer = TfidfTransformer()
tfidf = tfidf_transformer.fit_transform(count_vectorizer.fit_transform(content))
print (tfidf.shape)
arr = SelectKBest(mutual_info_classif, k=2).fit_transform(tfidf[:24999], label)
print(arr)
参考文献
- 文本挖掘预处理之TF-IDF:文本挖掘预处理之TF-IDF - 刘建平Pinard - 博客园 (https://www.cnblogs.com/pinard/p/6693230.html)
- 使用不同的方法计算TF-IDF值:使用不同的方法计算TF-IDF值 - 简书(https://www.jianshu.com/p/f3b92124cd2b)
- sklearn-点互信息和互信息:sklearn:点互信息和互信息 - 专注计算机体系结构 - CSDN博客 (https://blog.csdn.net/u013710265/article/details/72848755)
- 如何进行特征选择(理论篇)机器学习你会遇到的“坑”:如何进行特征选择(理论篇)机器学习你会遇到的“坑” (https://baijiahao.baidu.com/s?id=1604074325918456186&wfr=spider&for=pc)
- 吴军. 数学之美[M]. 人民邮电出版社, 2012
- Multivariate mutual information, wiki,(https://en.wikipedia.org/wiki/Multivariate_mutual_information)
- bd-liuming ,CSDN(https://blog.csdn.net/fisherming/article/details/79925574 )