暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

利用词袋/词集模型和tf-idf算法,提取文本特征

私人物语 2020-04-14
1299

背景知识

文本特征提取有两个非常重要的模型:

词集模型:Set Of Words,单词构成的集合,集合自然每个元素都只有一个,也即词集中的每个单词都只有一个;

词袋模型:Bag Of Words,如果一个单词在文档中出现不止一次,并统计其出现的次数(频数);

两者本质区别,在于词袋模型在词集模型基础上,增加了词频维度。词集关注有无,词袋关注频率。

建立词袋模型分三步:分词(tokenizing),统计修订词特征值(counting)与标准化(normalizing)。

词袋模型首先会进行分词,在分词之后,通过统计每个词在文本中出现的次数,我们就可以得到该文本基于词的特征,如果将各个文本样本的这些词与对应的词频放在一起,就是我们常说的向量化(word2vec)。向量化完毕后一般也会使用TF-IDF进行特征的权重修正,再将特征进行标准化。再进行一些其他的特征工程后,就可以将数据带入机器学习算法进行分类聚类了。

当然,词袋模型有很大的局限性,因为它仅仅考虑了词频,没有考虑上下文的关系,因此会丢失一部分文本的语义。但是大多数时候,如果我们的目的是分类聚类,则词袋模型表现得很好。


词集词袋模型实现

dataset = [
'This is the first document',
'This is the second second document',
'And the third one',
'Is this the first document'
]


# 生成词汇表, 去重
vocabSet = set()
for sentence in dataset:
vocabSet |= set(sentence.split(' '))
vocabList = list(vocabSet)
print('vocabList: \n', vocabList)


# 词集模型
SOW = []
for doc in dataset:
vec = [0]*len(vocabList)
for i, word in enumerate(vocabList):
if word in doc.split(' '):
vec[i] = 1
SOW.append(vec)


print('sow vector:\n', SOW)


# 词袋模型
BOW = []
for doc in dataset:
vec = [0]*len(vocabList)
for word in doc.split(' '):
vec[vocabList.index(word)] += 1
BOW.append(vec)


print('bow vector:\n', BOW)

打印结果:

vocabList:
 ['this''And''document''is''second''third''one''This''first''Is''the']
sow vector:
[[1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1], [1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0], [1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0], [1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1]]
bow vector:
[[1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1], [1, 0, 1, 1, 2, 0, 0, 0, 0, 1, 0], [1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0], [1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1]]

这就完成了文本的词集化和词袋化,每个句子都转化成长度为11维特征向量。

但这种实现方式很简陋,没有排除停用词,分词规则简单,缺乏编码错误处理机制,故工程上并不会这么做。这里只是为了演示词袋化过程,读者需要理解这一点。


词袋模型实现二

机器学习工具包scikit-learn
提供的CountVectorizer
类可用于完成文本的词频统计与向量化。CountVectorize
类初始化比较重要的几个参数为:

decode_error
,处理解码失败的方式,分为strict
ignore
replace
三种方式。

strip_accents
,在预处理步骤中移除重音的方式。默认为None
,可设为ascii
unicode
,将使用ascii
unicode
编码在预处理步骤去除raw document
中的重音符号。
max_features
,词袋特征个数的最大值。
stop_words
,设置停用词,设为english
将使用内置的英语停用词,设为一个list
可自定义停用词,设为None
不使用停用词,设为None
max_df∈[0.7, 1.0)
将自动根据当前的语料库建立停用词表。
max_df
,可以设置为范围在[0.0 1.0]
float
,也可以设置为没有范围限制的int
,默认为1.0
。这个参数的作用是作为一个阈值,当构造语料库的关键词集的时候,如果某个词的document frequence
大于max_df
,这个词不会被当作关键词。如果这个参数是float
,则表示词出现的次数与语料库文档数的百分比,如果是int
,则表示词出现的次数。如果参数中已经给定了vocabulary
,则这个参数无效。
min_df
,类似于max_df
,参数min_df=n
表示词必须要在至少n
个文档中出现过,否则就不考虑。
binary
,默认为False
,当与TF-IDF结合使用时需要设置为True

#!/usr/local/bin/python3


from sklearn.feature_extraction.text import CountVectorizer


# 实例化分词对象
vec = CountVectorizer(min_df=1)


# 语料列表
corpus = [
'This is the first document.',
'This is the second second document.',
'And the third one.',
'Is this the first document?'
]


# 文本转词频矩阵, 矩阵元素a[i][j] 表示j词在第i个文本下的词频
x = vec.fit_transform(corpus)
print(x)


# 提取全部特征词
fnames = vec.get_feature_names()
print(fnames)


# 向量化
arr = x.toarray()
print(arr)


# 词汇表
vocabulary = vec.vocabulary_
print(vocabulary)

打印结果:

# 依次是第i个句子中第j个特征词出现的次数
  (0, 8)    1  # 第1个句子中第8个特征词this出现的次数为1
(0, 3) 1
(0, 6) 1
(0, 2) 1
(0, 1) 1
(1, 8) 1
(1, 3) 1
(1, 6) 1
(1, 1) 1
(1, 5) 2
(2, 6) 1
(2, 0) 1
(2, 7) 1
(2, 4) 1
(3, 8) 1
(3, 3) 1
(3, 6) 1
(3, 2) 1
(3, 1) 1


# 全部特征词
['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this']


# 每个文本的词向量特征, 是一个稀疏矩阵
[[0 1 1 1 0 0 1 0 1]
[0 1 0 1 0 2 1 0 1]
[1 0 0 0 1 0 1 1 0]
[0 1 1 1 0 0 1 0 1]]

# 词汇表
{'this': 8, 'is': 3, 'the': 6, 'first': 2, 'document': 1, 'second': 5, 'and': 0, 'third': 7, 'one': 4}

利用现有词汇表对其他文本进行特征提取

vocabulary = vec.vocabulary_
vec_2 = CountVectorizer(min_df=1, vocabulary=vocabulary)


corpus2 =[
'I come to China to travel',
'This is a car polupar in China',
'I love tea and Apple ',
'The work is to write some papers in science'
]


arr2 = vec_2.fit_transform(corpus2).toarray()
print(arr2)


fnames2 = vec_2.get_feature_names()
print(fnames2)

打印结果:

# 词向量
[[0 0 0 0 0 0 0 0 0]
[0 0 0 1 0 0 0 0 1]
[1 0 0 0 0 0 0 0 0]
[0 0 0 1 0 0 1 0 0]]
# 特征词
['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this']

可见特征词列表和前面一样,没有变化。


TF-IDF概念

TF-IDF(Term Frequency–Inverse Document Frequency),即“词频-逆文本频率”,是一种用于资讯检索与文本挖掘的常用加权技术。它由两部分组成,TF和IDF。

TF-IDF是一种统计方法,用以评估一个字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。TF-IDF加权的各种形式常被搜索引擎应用,作为文件与用户查询之间相关程度的度量或评级。

TF-IDF的主要思想是:如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。TF-IDF实际上是:TF * IDF。

词频(Term Frequency,TF):指的是某一个给定的词语在该文件中出现的频率。

逆向文件频率(Inverse Document Frequency,IDF):是一个词语普遍重要性的度量。某一特定词语的IDF,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取对数得到。公式表示为:IDF(x) = log ( N/N(x) )
。由于极端情况下有N(x)=0
的情况,故而需要做平滑处理,最常见的平滑公式为IDF(x) = log ( (N+1)/(N(x)+1) ) + 1

于是就有 TF-IDF(x) = TF(x) * IDF(x)
,某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF
。因此,TF-IDF倾向于过滤掉常见的词语,保留重要的词语。

TF-IDF处理

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import TfidfVectorizer




# 实例化分词对象
vec = CountVectorizer(min_df=1)


# 语料列表
corpus = [
'This is the first document.',
'This is the second second document.',
'And the third one.',
'Is this the first document?'
]


# 文本转词频矩阵, 矩阵元素a[i][j] 表示j词在第i个文本下的词频
x = vec.fit_transform(corpus)
print('文本转词矩阵处理: \n', x)


# TF-IDF
tt = TfidfTransformer()
tfidf = tt.fit_transform(x)
print('tfidf转换器处理:\n', tfidf)


# TF-IDF
tv = TfidfVectorizer()
tfidf_2 = tv.fit_transform(corpus)
print('tfidf矢量器处理:\n', tfidf_2)

如代码所示,第一种方法是在用 CountVectorizer
 类向量化之后再调用 TfidfTransformer
 类进行预处理;第二种方法是直接用 TfidfVectorizer
 完成向量化与 TF-IDF 预处理。打印结果如下:

文本转词矩阵处理: 
(0, 8) 1
(0, 3) 1
(0, 6) 1
(0, 2) 1
(0, 1) 1
(1, 8) 1
(1, 3) 1
(1, 6) 1
(1, 1) 1
(1, 5) 2
(2, 6) 1
(2, 0) 1
(2, 7) 1
(2, 4) 1
(3, 8) 1
(3, 3) 1
(3, 6) 1
(3, 2) 1
(3, 1) 1
tfidf转换器处理:
(0, 8) 0.4387767428592343
(0, 6) 0.35872873824808993
(0, 3) 0.4387767428592343
(0, 2) 0.5419765697264572
(0, 1) 0.4387767428592343
(1, 8) 0.2723014675233404
(1, 6) 0.22262429232510395
(1, 5) 0.8532257361452786
(1, 3) 0.2723014675233404
(1, 1) 0.2723014675233404
(2, 7) 0.5528053199908667
(2, 6) 0.2884767487500274
(2, 4) 0.5528053199908667
(2, 0) 0.5528053199908667
(3, 8) 0.4387767428592343
(3, 6) 0.35872873824808993
(3, 3) 0.4387767428592343
(3, 2) 0.5419765697264572
(3, 1) 0.4387767428592343
tfidf矢量器处理:
(0, 1) 0.4387767428592343
(0, 2) 0.5419765697264572
(0, 6) 0.35872873824808993
(0, 3) 0.4387767428592343
(0, 8) 0.4387767428592343
(1, 5) 0.8532257361452786
(1, 1) 0.2723014675233404
(1, 6) 0.22262429232510395
(1, 3) 0.2723014675233404
(1, 8) 0.2723014675233404
(2, 4) 0.5528053199908667
(2, 7) 0.5528053199908667
(2, 0) 0.5528053199908667
(2, 6) 0.2884767487500274
(3, 1) 0.4387767428592343
(3, 2) 0.5419765697264572
(3, 6) 0.35872873824808993
(3, 3) 0.4387767428592343
(3, 8) 0.4387767428592343

两种方法得到的各个词的tfidf
值一致。


结语

词集模型和词袋模型,在文本特征提取上有重要意义。tf-idf算法在文本词频统计后做一些修订优化。但也因其局限性导致词汇量可能变得非常大。词汇量过大又将导致需要非常大的矢量来编码文档,从而对内存产生很大的要求,同时拖慢算法的速度。期待下一篇,word2vector工具带来的改进。

文章转载自私人物语,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论