在自然语言处理中,第一步需要面对的就是词向量特征的提取。语言的特征提取在sklearn模块中有相当完善的方法和模块,而针对中文其实也可以同过分词软件做分词然后再按照英文文本的思路开展特征提取,机器学习。
词袋(bags of words)
- 用一个固定的整数id来标记在所有文档中出现的所有单词,形成字典
- 对于#i 个文档,计算每个单词w在文档中出现的次数n,并将n存放在X[i, id] 中。
构建词袋的过程中会遇到的问题
一般feature的数量都能达到100,000,这是如果有10,000个样本的话就需要相当大的内存。但实际上,样本的特征向量X[i] 有很多特征都是零(称之为稀疏矩阵)。
这时候需要使用scipy.sparse来存储这样的数据结构。luckily,scipy和sklearn都是同一家项目底下的,所以毫无疑问地互相兼容了。
用sklearn来对文本的每个单词做标记
在构建词袋的过程中,构建字典是很重要的一步。
将文本数值化在sklearn中用到的是CountVectorizer
导入方式如下
from sklearn.feature_extraction.text import CountVectorizer
1. 英文文本
毕竟是歪果仁发明的,当然sklearn对英语的文本就可以直接做标记了。
只需要三步走就可以直接构建词袋字典了
# 建立模型类
count_vect = CountVectorizer()
# 根据训练数据fit模型,搞定!
X_train_counts = count_vect.fit_transform(twenty_train.data)
2. 中文文本
针对中文文本,我们其实也不难做,只需要多加一步【分词】。在中文分词领域,结巴是个不错的选择。
安装好 jieba 分词后,直接调用
import jieba
# 训练样本如下
train_text = ['纽约市长白思豪表示','初步迹象显示这是蓄意实施的行为','也无明确证据显示新泽西和纽约曼哈顿爆炸事件有关联'];
train_data = list();
for each in train_text:
train_data.append(' '.join(jieba.cut(each)));
出来的效果如下
In [19]:train_data
Out[19]:
['纽约市 长白 思豪 表示',
'初步 迹象 显示 这是 蓄意 实施 的 行为',
'也 无 明确 证据 显示 新泽西 和 纽约 曼哈顿 爆炸事件 有 关联']
以分词后的dataset作为输入就可以构建用来标记词语的字典
from sklearn.feature_extraction.text import CountVectorizer
count_vect = CountVectorizer()
X_train_counts = count_vect.fit_transform(train_data)
count_vect的模型训练完毕之后可以通过 id 查找单词
……
# 这个比较麻烦,但原理很简单,count_vect.vocabulary_本质就是字典。
详见 这里
或者反过来,通过 单词 查找 id
In [22]:count_vect.vocabulary_.get(u'长白')
Out[22]: 17
tf-idf
根据词语的出现次数来构建词袋会出现一个问题:长的文章词语出现的次数会比短的文章要多,而实际上两篇文章可能谈论的都是同一个主题。
于是乎,我们用tf(term frequencies)——单词出现次数除以文章总单词数——这样的方法来代替出现次数来构建词袋字典。
除此之外,还有一个问题就是一个词如果在很多文章中都有出现,那么它对于区分文章的类别效果就微乎其微了。也就是说它对于我们识别文章所提供的信息就非常地少了。
于是乎就有了——tf-idf(Term Frequency times Inverse Document Frequency)——每个词再加上权重来构建词标记。
tf - idf算法在python中的模块就是 TfidfTransformer
from sklearn.feature_extraction.text import TfidfTransformer
(谜之声:真心佩服sklearn啊,机器学习常用的模块基本都应用尽有)