本文主要参考Coursera上 国立高等经济大学的自然语言处理课程 和 吴恩达老师的的机器学习课程。
one-hot
one-hot 可以说是机器学习领域最常见的数据表示形式了。
实例
假设我们的语料库如下:
```
荣荣爱玩游戏
垃圾该分类了
我爱自然语言处理
```
经过 tokenization 化之后得到如下的 `vocabulary` 字典
```
{
"荣荣": 0,
"爱": 1,
"玩游戏": 2,
"垃圾": 3,
"分类": 4,
"我": 5,
"自然语言处理": 6
}
```
其中,`了` 和 `该` 已被当做 停止词 去掉。
将上述的词转化为 one-hot 形式如下:
```
"我": [0, 0, 0, 0, 1, 0]
"爱": [0, 1, 0, 0, 0, 0]
"自然语言处理": [0, 0, 0, 0, 0, 1]
```
向量的长度为词汇量的大小。
简单的流程
那么自然语言处理领域该怎么用 one-hot 呢?
流程如下所示:
1. 创建两个个字典变量 `vocabulary`, `reverse_vocabulary`;
2. 将文本tokenization化(参考我第一篇文章自然语言处理之文本预处理(一))),得到token;
3. 得到以下形式的词汇表 `vocabulary[token] = n` 和 `reverse_vocabulary[n] = token`,其中 `n` 为该词出现的顺序;
4. 最终得到 `vocabulary` 字典为 `word -> index` 的映射;`reverse_vocabulary` 为 `index -> word` 的映射;
5. 利用这两个字典,将数据转换成 one-hot 的形式
面临的问题:
* 数据量太大,一台服务器保存不下这个字典,怎么办?
* 想多台服务器并行计算怎么同步(CAP理论)?
此处仅提出了这两个问题,不进行深入讨论。
BoW(Bag of Words)
我们已经可以使用 one-hot 形式来表示词了,那么 __该怎么表示一个句子__ 呢?
还是以 上述内容为例,对于 `我爱自然语言处理` 这句话 tokenization 化之后得到了三个 one-hot 形式表示的词;将这三个词的向量进行垂直方向堆叠起来就可以当做这句话的向量表示。
如下:
```
text: 我爱自然语言处理
vector: [0, 1, 0, 0, 0, 0, 1, 1]
```
那么,如果有的词不在 `vocabulary` 字典中呢?(可以采取直接跳过的方式来处理)
```
text: 我喜欢自然语言处理
vector: [0, 0, 0, 0, 0, 0, 1, 1]
```
该模型有两个主要问题如下:
1. 正如这个模型的名字一样,该模型无法表示 __词在句子中出现的顺序__;
2. 所有出现过的词全都为 `1`。
n-gram
为了解决问题一(仅完善了一点,没有实际上解决),提出了 `n-gram` 的方法。
该方法将 连续的 `n` 个 token 组合在一起,继续拿上述例子:
```
荣荣爱玩游戏
垃圾该分类了
我爱自然语言处理
```
此时,tokeization 化之后结果为:
```
{
"荣荣": 0,
"荣荣爱": 1,
"爱": 2,
"爱玩游戏": 3,
"玩游戏": 4,
"垃圾": 5,
"分类": 6,
"我": 7,
"我爱": 8,
"爱自然语言处理": 9,
"自然语言处理": 10
}
```
此时,BoW 表示如下:
```
text: 我爱自然语言处理
vector: [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1]
```
可以看到当 `n` 增大的时候,得到的稀疏矩阵将会指数级增长,所以一般情况下,`n` 只取 `2-3`
当然,__对于 `n-gram` 形式表示的 token 中会有好多 token 经常出现,也有好多只出现过几次,这些都可以去掉来减少向量的维度__。
tf-idf
那么怎么解决问题二呢?
tf-idf 是一个很好的解决办法,tf-idf此处就不多加讲解了,不了解的人可以参考[阮一峰老师的文章](http://www.ruanyifeng.com/blog/2013/03/tf-idf.html)
如何将 tf-idf 应用到 BoW 中来呢?
其实只需要将向量中原本为 `1` 的值,替换为 tf-idf 的值即可。
```
text: 我爱自然语言处理
vector: [0, 0, 0, 0, 0, 0, 0, 0, 0.43, 0, 0.43]
```
上述 `vector` 中的值不是真实的 tf-idf 值。
python tf-idf example
```python
from sklearn.feature_extraction.text import TfidfVectorizer
import pandas as pd
texts = [
"good movie",
"not a good movie",
"did not like",
"i like it",
"good one"
]
tfidf = TfidfVectorizer(min_df=2, max_df=0.5, ngram_range=(1, 2))
features = tfidf.fit_transform(texts)
f = pd.DataFrame(features.todense(), columns=tfidf.get_feature_names())
print(f)
'''
good movie like movie not
0 0.707107 0.000000 0.707107 0.000000
1 0.577350 0.000000 0.577350 0.577350
2 0.000000 0.707107 0.000000 0.707107
3 0.000000 1.000000 0.000000 0.000000
4 0.000000 0.000000 0.000000 0.000000
'''
```
其中:
* min_df, max_df: 移除低频和高频(停止词)出现的 grams
如果参数的值为 float(0.0 - 1.0)则代表比值; 如果是 int 类型, 则代表出现的次数
* ngram_range: 上文提到的 n-gram, 此处保留 1 个和 2 个
总结
本文讲解了自然语言处理(机器学习)中常用的 BoW 模型及其其他形式,读者可以自行使用两种方式来分别实验后选择合适的模型。
下一篇文章将会继续介绍 深度学习中常用的文本表示模型
* word2vec
参考链接
http://www.ruanyifeng.com/blog/2013/03/tf-idf.html