(本文所使用的 Python 库和版本号: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2, NLTK 3.3)
词袋模型 (Bag Of Words, BOW) 和词向量 (Word Embedding, 也叫词嵌套等) 是自然语言处理和文本分析的两个最常用的模型.
词袋模型将一段文本看成一系列单词的集合, 由于单词很多, 故而这段文本就相当于一个袋子, 里面装着一系列单词. 故而计算机的 NLP 分析就是对这个袋子进行分析, 但是计算机不认识文本, 只认识数字, 那么我们需要一种机制将袋子里的文本转换成数字, 这种机制可以是一种 Dict 映射(key 为数字, value 为文本等), 或数组(索引为数字, 值为文本), 或者还可以用 HashCode 来计算文本的数字表示, 而 NLP 建模就是使用这些数字来建模. 词袋在学习之后, 就可以通过构建文档中所有单词的直方图来对每篇文档进行建模.
词向量模型是将单个单词映射到一个高维空间(维度可以到几千几万甚至几十万), 这个高维空间就用数组, 或者成为向量来表示, 故而建立一种单词 - 向量的映射关系, 所以成为词向量模型. 但是这种模型能表示的仅仅是单个单词, 对于有多个单词组成的一句话, 那么就需要做进一步处理, 比如一个单词就是一个向量,N个单词组成的一句话就是N个一维向量了, 故而可以用N个一维向量组成的矩阵来表示一句话, 只不过不同长度的句子, 该矩阵的行数不一样罢了.
下面我们仅仅学习用 NLP 创建词袋模型, 创建过程主要是提取文本的特征, 构建特征向量. 有两种方法可以构建特征向量, 分别是 CountVectorizer 和 TfidfVectorizer.
1. 用 CountVectorizer 提取文本特征
sklearn 模块中的 CountVectorizer 方法可以直接提取文本特征, 这个函数只考虑词汇在文本中出现的频率, 这个函数有一个参数: stop_words, 表示是否取出停用词, 所谓的停用词是指为了节省空间和提高效率而自动过滤到的词语, 比如 the, is, at, which 等, 对于不同的, 默认的 stop_words 不去除停用词.
- # 数据集暂时用简. 奥斯丁的《爱玛》中的文本
- dataset=nltk.corpus.gutenberg.words('austen-emma.txt')
- # print(len(dataset)) # 192427 代表读入正常
- chunks=split(" ".join(dataset[:10000]), 2000) # 将前面的 10000 个单词分成五个词袋, 每个袋子装 2000 个单词
- # 构建一个文档 - 词矩阵, 该矩阵记录了文档中每个单词出现的频次
- # 用 sk-learn 的 CountVectorizer 函数来实现这种构建过程
- from sklearn.feature_extraction.text import CountVectorizer
- vectorizer = CountVectorizer(min_df=4, max_df=.99)
- # fit_transform 函数需要输入一维数组, 且数组元素是用空格连起来的文本
- chunks=[" ".join(chunk) for chunk in chunks] # 故而需要转换一下
- doc_term_matrix = vectorizer.fit_transform(chunks)
- feature_names=vectorizer.get_feature_names() # 获取
- print(len(feature_names))
- print(doc_term_matrix.shape)
- # print(doc_term_matrix.T.toarray())
上面将简. 奥斯丁的《爱玛》中的前面 10000 个单词分成了五个词袋, 每个词袋包含 2000 个单词, 然后用 CountVectorizer 建立文本特征向量, 通过 fit_transform 后就在 CountVectorizer 对象内部建立了这种文档 - 词矩阵, 通过 print 可以看出结果.
为了更加明确的看出里面的文档 - 词矩阵, 可以用下面的代码将其打印出来:
- # 打印看看 doc_term_matrix 这个文档 - 词矩阵里面的内容
- print('Document Term Matrix------>>>>')
- bag_names=['Bag_'+str(i) for i in range(5)] # 5 个词袋
- formatted_row='{:>12}'*(1+len(bag_names)) # 每一行第一列是单词, 后面是每个词袋中的频率
- print(formatted_row.format('Word', *bag_names))
- for Word, freq in zip(feature_names,doc_term_matrix.T.toarray()): # 需要装置矩阵
- # 此处的 freq 是 csr_matrix 数据结构
- output = [str(x) for x in freq.data]
- print(formatted_row.format(Word,*output))
----------------------- 输 --------- 出 --------------------
- Document Term Matrix------>>>> Word Bag_0 Bag_1 Bag_2 Bag_3 Bag_4 about 3 4 0 1 1 among 1 1 1 1 0 because 1 1 0 1 1 believe 0 1 1 1 3 believed 0 1 1 1 2 best 1 2 1 1 0 better 0 3 1 1 2 beyond 1 0 1 2 3
- ...
----------------------- 完 --------------------------------
以上是部分结果, 可以看出 about 在 Bag_0 中出现了 3 次, 在 Bag_1 中出现了 4 次, 以此类推.
如果对这种矩阵的出现次数有疑惑, 可以看我的我的 GitHub 中代码 https://github.com/RayDean/MachineLearning , 里面有更详细的解释.
值得注意的是, CountVectorizer 也可以用于中文特征的提取, 但是需要对自定义的 split 函数进行修改, 原来的函数用空格作为分隔符, 可以很好的将英文分词, 但对中文无效, 故而中文的情况需要将 split 中的分词方式改成 jieba 分词.
2. 用 TfidfVectorizer 提取文本特征
TfidfVectorizer 的主要特点是: 除了考量某词汇在文本出现的频率, 还关注包含这个词汇的所有文本的数量, 这个方法能够削减高频没有意义的词汇带来的影响, 挖掘更有意义的特征. 一般的, 当文本条目越多, 这个方法的效果越显著.
在代码上, 用 TfidfVectorizer 和上面的 CountVectorizer 几乎一样, 只是将类名称替换一下即可.
- # 数据集暂时用简. 奥斯丁的《爱玛》中的文本
- dataset=nltk.corpus.gutenberg.words('austen-emma.txt')
- # print(len(dataset)) # 192427 代表读入正常
- chunks=split(" ".join(dataset[:10000]), 2000) # 将前面的 10000 个单词分成五个词袋, 每个袋子装 2000 个单词
- from sklearn.feature_extraction.text import TfidfVectorizer
- vectorizer = TfidfVectorizer()
- # fit_transform 函数需要输入一维数组, 且数组元素是用空格连起来的文本
- chunks=[" ".join(chunk) for chunk in chunks] # 故而需要转换一下
- doc_term_matrix = vectorizer.fit_transform(chunks)
- feature_names=vectorizer.get_feature_names() # 获取
- print(len(feature_names))
- print(doc_term_matrix.shape)
打印出来的结果并不是某个单词在词袋中出现的频率, 而是 tf-idf 权重, 这个权重有个计算公式, tf-idf=tf*idf, 也就是说 tf 与 idf 分别是两个不同的东西. 其中 tf 为谋个训练文本中, 某个词的出现次数, 即词频(Term Frequency);idf 为逆文档频率(Inverse Document Frequency), 对于词频的权重调整系数.
######################## 小 ********** 结 ###############################
1, 用于词袋模型中提取文本特征主要有两种方法: CountVectorizer 和 TfidfVectorizer, 其中 CountVectorizer 构建的文档 - 词矩阵里面是单词在某个词袋中出现的频率, 而 TfidfVectorizer 构建的矩阵中是单词的 tf-idf 权重.
2, 一般情况下, 文档的文本都比较长, 故而使用 TfidfVectorizer 更好一些, 推荐首选这个方法.
#################################################################
注: 本部分代码已经全部上传到 (我的 GitHub https://github.com/RayDean/MachineLearning ) 上, 欢迎下载.
参考资料:
1, Python 机器学习经典实例, Prateek Joshi 著, 陶俊杰, 陈小莉译
来源: https://juejin.im/post/5bbdb0b5e51d450e782659b7