词表征与词向量
词表征(Word Representation)
文本数据经过预处理后,需要转化成数值特征,便于后续处理 one-hot 表征 (localist representation): - 例如给定词典,包含\(10^7\)个单词,则每个单词表示为 \(10^7\times 1\) 的 one-hot 向量;“我们”表示为 \([0,0,...,0,0,1,0,0,0...0,0,0]_{(10^7,1)}\),1 的索引为“我们”在词典中的位置 - 句子或文档向量则可表示为 \(10^7\times 1\) 的向量,词典中每个单词在句子或文档中是否出现;Bolean 向量。 - 也可以表示为,词典中每个单词在句子或文档中出现的次数;Count-based 句子向量
1 | # 单词级 one-hot 编码 |
({'The': 1,
'cat': 2,
'sat': 3,
'on': 4,
'the': 5,
'mat.': 6,
'dog': 7,
'ate': 8,
'my': 9,
'homework.': 10},
array([[[0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]],
[[0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]]]))
1 | # 字符级 one-hot 编码 |
({'0': 1,
'1': 2,
'2': 3,
'3': 4,
'4': 5,
'5': 6,
'6': 7,
'7': 8,
'8': 9,
'9': 10,
'a': 11,
'b': 12,
'c': 13,
'd': 14,
'e': 15,
'f': 16,
'g': 17,
'h': 18,
'i': 19,
'j': 20,
'k': 21,
'l': 22,
'm': 23,
'n': 24,
'o': 25,
'p': 26,
'q': 27,
'r': 28,
's': 29,
't': 30,
'u': 31,
'v': 32,
'w': 33,
'x': 34,
'y': 35,
'z': 36,
'A': 37,
'B': 38,
'C': 39,
'D': 40,
'E': 41,
'F': 42,
'G': 43,
'H': 44,
'I': 45,
'J': 46,
'K': 47,
'L': 48,
'M': 49,
'N': 50,
'O': 51,
'P': 52,
'Q': 53,
'R': 54,
'S': 55,
'T': 56,
'U': 57,
'V': 58,
'W': 59,
'X': 60,
'Y': 61,
'Z': 62,
'!': 63,
'"': 64,
'#': 65,
'$': 66,
'%': 67,
'&': 68,
"'": 69,
'(': 70,
')': 71,
'*': 72,
'+': 73,
',': 74,
'-': 75,
'.': 76,
'/': 77,
':': 78,
';': 79,
'<': 80,
'=': 81,
'>': 82,
'?': 83,
'@': 84,
'[': 85,
'\\': 86,
']': 87,
'^': 88,
'_': 89,
'`': 90,
'{': 91,
'|': 92,
'}': 93,
'~': 94,
' ': 95,
'\t': 96,
'\n': 97,
'\r': 98,
'\x0b': 99,
'\x0c': 100},
array([[[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
...,
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.]],
[[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
...,
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.]]]))
1 | # tf 实现 one-hot 编码,句子的 Bolean 向量 |
({'the': 1,
'cat': 2,
'sat': 3,
'on': 4,
'mat': 5,
'dog': 6,
'ate': 7,
'my': 8,
'homework': 9},
array([[0., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0.],
[0., 1., 0., 0., 0., 0., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0.]]))
使用散列技巧的单词级 one-hot 编码 - 当词汇表非常大大时,散列技巧降低向量长度 - 可能出现散列冲突
1 | samples = ['The cat sat on the mat.', 'The dog ate my homework.'] |
\(tf-idf\) 表征
\[tfidf(w)=tf(d,w)\times idf(w)\]
- \(tf(d,w)\) - 文档 d 中单词 w 的词频;
- \(idf(w)=log\frac{N}{N_{w}}\),\(N\) - 语料库的文档总数,\(N_{w}\) - 词语 w 出现在多少个文档中
1 | from sklearn.feature_extraction.text import TfidfVectorizer |
matrix([[0. , 0.46979139, 0.58028582, 0.38408524, 0. ,
0. , 0.38408524, 0. , 0.38408524],
[0. , 0.6876236 , 0. , 0.28108867, 0. ,
0.53864762, 0.28108867, 0. , 0.28108867],
[0.51184851, 0. , 0. , 0.26710379, 0.51184851,
0. , 0.26710379, 0.51184851, 0.26710379],
[0. , 0.46979139, 0.58028582, 0.38408524, 0. ,
0. , 0.38408524, 0. , 0.38408524]])
1 | print(vectorizer.get_feature_names()) |
['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this']
localist representation
的缺点: 1. 稀疏性表征,向量长度为词典内单词总数 2. 无法表征单词的相似度,任意两个单词向量的点积为 0 3. 表达能力弱,如无法表达语义;同样 8 维向量,one-hot只能表示 8 个单词,而词向量每个元素取值连续,因此能表示无穷个单词
词向量
分布语义(distributional Semantic),特定单词的含义由频繁出现在其前后的词决定 \(\Longrightarrow\) 分布式表征,通过单词的上下文(context)表征单词。
不同的单词,其上下文越相近,该单词的含义越相似。例如单词 w,其上下文由其前 n 个单词及其后 n 个单词的集合组成,n 表示窗口尺寸(fixed window)。如下所示,单词 "banking" 的上下文,窗口尺寸 \(n=5\) : \[ \begin{align*} ...goverment\ debt\ problems\ turning\ into\ &\boxed{banking}\ crisises\ as\ happened\ in\ 2009 ...\\ ...saying\ that\ Europe\ needs\ unified\ &\boxed{banking}\ regulation\ to\ replace\ the\ hodgepodge...\\ ...Indian\ has\ just\ given\ its\ &\boxed{banking}\ system\ a\ shot\ in\ the\ arm... \end{align*} \]
词向量,也称为 word embedding 或 word representation,将文本映射到特定维度的向量空间(vector space)中:
- 更致密的向量,
- 点积表示不同词间的相似性,表达语义
- capacity,表达能力强,词向量每个元素取值连续,因此能表示无穷个单词
- 泛化能力强,即在测试集上也有很好的效果,可用于迁移学习进行后续操作
训练词向量原理:
- 大量语料库,从中得出固定的词汇表,每个单词通过一个向量表示,向量维度 50、200或300
- 按语料库中文本的顺序,遍历每一个位置,以该位置单词作为中心词 c,选定窗口尺寸 n,窗口内上下文单词为 o
- 利用词向量的相似性,计算给定 c 时,o 的概率,或相反;c 的词向量就可以用来计算 o 中的单词
- 不断调整词向量,最大化概率
Skip-Gram Model
- 将文本按顺序,将固定窗口尺寸 n=2 内的单词,转变成 中心词-上下文词 组成的词对;中心词将作为输入,上下文词将作为训练标签。词汇表,10000个单词
- 构建如下图神经网络,输入为中心词 ‘ants’ 的 one-hot 向量;隐藏层矩阵本质为词向量查找表 (\(10000\times 300\)),通过 one-hot 向量查找到 ’ants‘ 的词向量 (\(300,\));再经过输出层矩阵 (\(300\times 10000\))的处理,得到 (\(10000,\)) 的向量,为词汇表中每个单词的概率分布。
- 例如,输入词对,(’ants‘,’car‘),训练目标:使得 ‘ants' 的上下文词 ’car‘ 的概率最大,而其它非上下文词的概率最小。最终得到隐藏层的权重矩阵即为所要得到的词向量,输出层矩阵则丢弃。
- 输出层有 (\(300\times 10000\)) 个参数,且 softmax 函数还需要计算 \(\sum_i^{10000}{e^i}\) ,模型最终的计算量会非常大
CBOW模型
- 与 skip-gram 相反,以 上下文词 作为输入,来训练模型,以最大化中心词的概率
重采样
在语料中频繁出现的一些单词,对中心词并没有给出多大信息,如 (fox
, the
) 词对中的 the
。Word2Vec 实现了 重采样机制,以解决该问题
给定单词 \(w_i\) ,其在语料库(\(10^6\)个单词)中的词频为 \(z(w_i)\)
sample
参数决定出现多少重采样,值越小,单词越不可能被选择,sample=0.001重采样单词 \(w_i\) 的概率为:
\[P(w_i)=(\sqrt{\frac{z(w_i)}{0.001}}+1)\cdot \frac{0.001}{z(w_i)}\]
- \(P(w_i)=1.0\),当单词词频 \(z(w_i)<=0.0026\) 时,该单词 100% 会被选择
- \(P(w_i)=0.5\),当单词词频 \(z(w_i)<=0.00746\) 时,该单词有 50% 可能性会被选择
- \(P(w_i)=0.033\),当单词词频 \(z(w_i)<=1.0\) 时,该单词 3.3% 会被选择
- 即整个语料库由该单词组成
重采样后,频繁出现的单词在采样时被选择的概率降低,而稀有词在采样时不会被忽略
加速训练
由于输出层的\(softmax\)函数会输出整个词汇表中所有单词的的概率,计算量非常大,两种方法加快训练速度: - Hierarchical Softmax
- Negative sampling
Hierarchical Softmax
- 基于层级\(softmax\)的模型,\(softmax\)的输出不再是词汇表所有单词的概率分布,而是一颗
Huffman
树,其中叶子节点共N
个,对应于N
个单词,非叶子节点N-1
个
霍夫曼树:
- 霍夫曼树(
Huffman Tree
),一种满二叉树,其带权路径长最小,也称为最优二叉树- 带权路径长,指的是所有叶子节点的权值与路径长的乘积之和;
- 若叶子节点的权值都是已知的,使权值越大的叶子节点路径越小,则整棵树的带权路径长最小。
- 构造霍夫曼树的目的是为了完成霍夫曼编码,变长、极少多余编码方案,使得频率高的字符对应的二进制位较短,频率低的字符对应的二进制位较长。相对于等长编码,将文件中每个字符转换为固定个数的二进制位
霍夫曼树的构造,给定包含权重值为 \((w_1,w_2,...w_n)\) 的节点列表: - 将节点列表按权重进行升序排列,并构建一颗空树 - 遍历排序后的节点,依次将其添加到树中,添加规则如下: - 若树为空,则作为根节点 - 若权重大于根节点权重,则该节点作为根节点右兄弟,生成一个新的根节点,权重为两者之和 - 若权重不大于根节点权重,则该节点作为根节点左兄弟,生成一个新的根节点,权重为两者之和 - 约定霍夫曼树左子树编码为0,右子树编码为1,左子树的权重小于右子树的权重; - 权重越高的节点编码值越短,权重越低的编码值越长;保证了越常用的单词拥有越短的编码。
词汇表构建的霍夫曼树
- 首先根据语料库,构建词典\(V\),然后以每个单词的词频为权重构建霍夫曼树,
- 出现越频繁的单词在树中深度越浅,越少见的词在越深;每个单词有其对应的由‘01’组成的编码
- 如下图所示:
- 树中叶子节点即为每个单词,内部节点为模型参数
- 在训练词对 ”深度:学习“ 时,目标为 “学习”,其路径为
643
,编码为101
;- 因此输入 ”深度“ 的词向量,经节点 6 处理,需要输出值 1;经节点 4 处理需要输出值 0;经过节点 3 处理,需要输出值 1
- 输入词向量与 目标单词 的霍夫曼树路径中每个节点参数求 点积,然后
sigmoid
激活 处理得到的结果,要与每个编码值相同 - 训练完成后,每个单词对应的词向量即为目标数据,霍夫曼树的节点参数被丢弃
- 训练完成后,再次输入单词 ”深度“ 的词向量,经过节点 6 处理得到概率 0.55;
- 因为训练时,输入为 “深度”,目标单词会有很多,概率 0.55,说明 “深度”的目标单词中 55% 出现在节点 6 的右边,45% 出现在左边;
- “深度:的” 词对占所有 “深度”词对的 0.45*0.75
Hierarchical Softmax
:
CBOW 模型中,给定上下文词 o,最大化中心词 c 的条件概率 \(P(c|o)\)
- \(X_o\):上下文词向量平均后的向量,作为树的输入
- \(p\) :表示从根节点出发到中心词 c 所代表的叶节点的路径
- \(l\):表示路径包含的节点的个数
- \(p_1, p_2, ...p_l\):表示路径 \(p\) 中的节点
- \(d_2 ,d_3, ...d_l \in{\{0,1\}}\):表示路径 \(p\) 中每个节点的编码,0 或 1,根节点无编码
- \(\theta_1, \theta_2,...\theta_{l-1}\):表示路径 \(p\) 中非叶节点对应的参数向量
中心词的条件概率,可以看作从根节点到中心词对应的叶子节点的路径的概率,该路径是唯一的;在树中选择左分支还是右分支,即可看作一个二分类问题,选择每个节点的概率: \[ \begin{align}P(d_j|X_o,\theta_{j-1})&=\begin{cases}\sigma(X_o^T\theta_{j-1}), &d_j=0 \\1-\sigma(X_o^T\theta_{j-1}), &d_j=1\end{cases}\\&=\left[\sigma(X_o^T\theta_{j-1})\right]^{1-d_j}\cdot \left[1-\sigma(X_o^T\theta_{j-1})\right]^{d_j}\end{align} \] 中性词的概率,即为到中心词节点的路径中所有节点的概率的连乘: \[ P(c|o)=\prod_{j=2}^{l}P(d_j|X_o,\theta_{j-1}) \]
最大化模型的目标函数: \[ \begin{align}L&=\sum_{c\in V}{logP(c|o)}\\&=\sum_{c\in V}\sum_{j=2}^{l}\Big\{(1-d_j)\cdot log\left[\sigma(X_o^T\theta_{j-1})\right]+d_j\cdot log\left[1-\sigma(X_o^T\theta_{j-1})\right]\Big\}\\&=\sum_{c\in V}\sum_{j=2}^{l}L(o,j)\end{align} \] 最大化目标函数中的每一项,两个参数,节点参数 \(\theta_{j-1}\),输入的向量 \(X_o\),设定学习速率 \(\eta\)。梯度下降法更新这两个参数: \[ \begin{align}\theta_{j-1}&:\theta_{j-1}+\eta\cdot\frac{\partial L(o,j)}{\partial\theta_{j-1}}\\X_o&:X_o+\eta\cdot\sum_{j=2}^{l}\frac{\partial L(o,j)}{\partial X_o}\end{align} \] \(X_o\) 为多个上下文词向量的平均,word2vec 直接将更新量应用到对应的那几个上下文词向量上
Negative Sampling
输出时,不再是计算整个词汇表的概率分布;而是从词汇表中选择一定量的负样本,再加上目标单词,计算这些单词的概率分布,训练时也只会更新这些单词对应的权重参数 - 如(fox,quick)
词对,输出层矩阵\(300\times 10000\)处理输入中心词fox
词向量时,只从输出层矩阵中选择上下文词quick
对应的那列权重向量(positive word)
,以及随机选择的另外 5 个单词对应的权重向量,来进行更新,此时只需要更新 (\(300\times 6\)) 个参数
如何选择负样本(Negative Samples): - 可以通过词频均匀采样:\(P(w_i)=\frac{f(w_i)}{\sum_{j=0}^{n}(f(w_j))}\) - 效果最好的是 3/4 指数采样:\(P(w_i)=\frac{f(w_i)^{3/4}}{\sum_{j=0}^{n}(f(w_j)^{3/4})}\) - \(f(w_i)\) 为单词 \(w_i\) 在语料库中出现的概率
底层 C 语言实现了包含 100M 元素的数组,称为 unigram table。向表中填充单词对应的索引,该索引出现的次数由 \(P(w_i)\cdot 表的尺寸\) 决定。选择负样本时,只要生成 \(0-100M\) 内的 5 个随机数
参考文章:
1.http://mccormickml.com/2016/04/19/word2vec-tutorial-the-skip-gram-model/ 2.http://mccormickml.com/2017/01/11/word2vec-tutorial-part-2-negative-sampling/ 3.https://ruder.io/word-embeddings-softmax/