语言用数字表达
距离:1.点与点之间的距离,长度越短越相似。
并不在乎距离的长短就是强度的大小时,采用2.两个之间的夹角。
同一种类别的东西,总是聚集在某一块地方,距离就会很近。
某些词经常出现,比如“在”“你”“吗”这些,被称为“中性词”,越有区分力的词可能越远离中心地带。点与点的距离可以告诉我们词的频率属性。
在训练时,不需要人工打标签,只需要在原始语料上做训练,取一小段文本,取出这些词的向量表示。比如原文本“这是一段训练”,取出除了一以外的向量,然后整合成整体向量,去预测中间的“一”。然后再挪动选择的窗口,像上面一样,去预测下一个中间的文字。这样计算机就能搞清前后文的关系。
可以用前后文预测中间词,也可以用中间词预测前后文。
2.2 Continuous Bag of Words (CBOW) 2.2.1 什么是CBOW在深度学习中,将词语向量化。
CBOW是什么个东西呢?用一句话概述:挑一个要预测的词,来学习这个词前后文中词语和预测词的关系。
举个例子吧,有这样一句话。
我爱莫烦Python,莫烦Python通俗易懂。
模型在做的事情如图中所示,将这句话拆成输入和输出,用前后文的词向量来预测句中的某个词。
那么这个模型的输入输出可以是:
# 1
# 输入:[我,爱] + [烦,Python]
# 输出:莫
# 2
# 输入:[爱,莫] + [Python, ,]
# 输出:烦
# 3
# 输入:[莫,烦] + [,,莫]
# 输出:Python
# 4
# 输入:[烦,Python] + [莫,烦]
# 输出:,
通过在大数据量的短语或文章中学习这样的词语关系,这个模型就能理解要预测的词
和前后文
的关系。而图中彩色的词向量就是这种训练过程的一个副产品。
词向量的几种典型应用:
- 把这些对词语理解的向量通过特定方法组合起来,就可以有对某句话的理解了;
- 可以在向量空间中找寻同义词,因为同义词表达的意思相近,往往在空间中距离也非常近;
- 词语的距离换算。
词语距离计算这个比较有意思,比如可以拿词语做加减法。公猫 - 母猫
就约等于 男人 - 女人
。 如果我哪天想知道 莫烦Python
的友商有哪些,我可以做一下这样的计算
友商 = 莫烦Python - (腾讯 - 阿里)
为了做一个有区分力的词向量,我做了一些假数据,想让计算机学会这些假词向量的正确向量空间。
将训练的句子人工分为两派(数字派,字母派),虽然都是文本,但是期望模型能自动区分出在空间上,数字和字母是有差别的。因为数字总是和数字一同出现, 而字母总是和字母一同出现。并且我还在字母中安排了一个数字卧底,这个卧底的任务就是把字母那边的情报向数字通风报信。 所以期望的样子就是数字 9
不但靠近数字,而且也靠近字母。
corpus = [
# numbers
"5 2 4 8 6 2 3 6 4",
"4 8 5 6 9 5 5 6",
"1 1 5 2 3 3 8",
"3 6 9 6 8 7 4 6 3",
"8 9 9 6 1 4 3 4",
"1 0 2 0 2 1 3 3 3 3 3",
"9 3 3 0 1 4 7 8",
"9 9 8 5 6 7 1 2 3 0 1 0",
# alphabets, expecting that 9 is close to letters
"a t g q e h 9 u f",
"e q y u o i p s",
"q o 9 p l k j o k k o p",
"h g y i u t t a e q",
"i k d q r e 9 e a d",
"o p d g 9 s a f g a",
"i u y g h k l a s w",
"o l u y a o g f s",
"o p i u y g d a s j d l",
"u k i l o 9 l j s",
"y g i s h k j l f r f",
"i o h n 9 9 d 9 f a 9",
]
这就是最终的实验结果。可以看到内奸9已经被暴露啦~
我们就开始写模型吧,这个模型还相对比较简单。
下面就是CBOW中的词向量组件了,最为核心的就是 self.embeddings
,词向量就存在于这里里面。之后我们会将它可视化
from tensorflow import keras
import tensorflow as tf
class CBOW(keras.Model):
def __init__(self, v_dim, emb_dim):
super().__init__()
self.embeddings = keras.layers.Embedding(
input_dim=v_dim, output_dim=emb_dim, # [n_vocab, emb_dim]
embeddings_initializer=keras.initializers.RandomNormal(0., 0.1),
)
...
接下来就是模型的预测是如何进行的了,我们用class
的call
功能定义模型的前向预测部分。说白了,其实也就是把预测时的embedding词向量给拿出来, 然后求一个词向量平均,这样输出就够了。在用这个平均的向量预测一下目标值。
class CBOW(keras.Model):
...
def call(self, x, training=None, mask=None):
# x.shape = [n, skip_window*2]
o = self.embeddings(x) # [n, skip_window*2, emb_dim]
o = tf.reduce_mean(o, axis=1) # [n, emb_dim]
return o
在求loss的时候我们稍微做一些手脚,这样可以在训练拥有庞大词汇的模型上有好处。使用nce_loss
能够大大加速softmax求loss的方式,它不关心所有词汇loss, 而是抽样选取几个词汇用来传递loss,因为如果考虑所有词汇,那么当词汇量大的时候,会很慢。
class CBOW(keras.Model):
def __init__(self, v_dim, emb_dim):
...
# noise-contrastive estimation
self.nce_w = self.add_weight(
name="nce_w", shape=[v_dim, emb_dim],
initializer=keras.initializers.TruncatedNormal(0., 0.1)) # [n_vocab, emb_dim]
self.nce_b = self.add_weight(
name="nce_b", shape=(v_dim,),
initializer=keras.initializers.Constant(0.1)) # [n_vocab, ]
self.opt = keras.optimizers.Adam(0.01)
# negative sampling: take one positive label and num_sampled negative labels to compute the loss
# in order to reduce the computation of full softmax
def loss(self, x, y, training=None):
embedded = self.call(x, training)
return tf.reduce_mean(
tf.nn.nce_loss(
weights=self.nce_w, biases=self.nce_b, labels=tf.expand_dims(y, axis=1),
inputs=embedded, num_sampled=5, num_classes=self.v_dim))
def step(self, x, y):
with tf.GradientTape() as tape:
loss = self.loss(x, y, True)
grads = tape.gradient(loss, self.trainable_variables)
self.opt.apply_gradients(zip(grads, self.trainable_variables))
return loss.numpy()
这就完成了整个模型的搭建啦。我加上我写好的处理数据组件,就能直接开始训练啦。
from utils import process_w2v_data
def train(model, data):
for t in range(2500):
bx, by = data.sample(8)
loss = model.step(bx, by)
if t % 200 == 0:
print("step: {} | loss: {}".format(t, loss))
if __name__ == "__main__":
d = process_w2v_data(corpus, skip_window=2, method="cbow")
m = CBOW(d.num_word, 2)
train(m, d)
值得注意的是,在数据处理方面,我没在代码中特别强调,但是可以看出,process_w2v_data()
这个功能需要确定我们的skip_window
这个参数, 这个参数的作用就是,在我们要预测的词周围,我们要选取多少个词作为他的输入。如果skip_window=1
则意味着我们选取这个词前后各1个词作为网络的输入, 如果skip_window=2
则意味着我们选取这个词前后各2个词,以此类推。具体代码可以看写在 utils.py 当中的代码。
最后如果将学到的embedding
向量结果进行可视化,就有了我们之前在文章前面看到的那个样子,观看一下字母和数字的距离,我们也能知道CBOW学会了这些词之间的内在联系。
我们已经能成功的训练出这些词向量了,除了可视化展示他们,我们还能怎么使用这些向量呢?在这里我举一个例子。
句子是由词语组成的,那么有一种理解句子的方式,就是将这个句子中所有词语的词向量都加起来,然后就变成了句子的理解。 不过这种空间上的向量相加,从直观上理解,就不是特别成立,因为它加出来以后,还是在这个词汇空间中的某个点, 你说它是句向量吧,好像也不行,说它是一个词的理解吧,好像也不对。
所以更常用的方式是将这些训练好的词向量当做预训练模型,然后放入另一个神经网络(比如RNN)当成输入,使用另一个神经网络加工后,训练句向量。
2.3 Skip-Gram欢迎分享,转载请注明来源:内存溢出
评论列表(0条)