以 word2vec 中的CBOW模型为例
CBOW模型原理通过训练上游任务达到训练embedding的效果。
我们可以通过 代码示例, 和 数据流的 shape 的变化把握该模型究竟如何工作的。
==上游任务== 通过句子中的上下文 预测中心词。
首先对数据进行预处理;
1 | #Create windows |
以上代码,对句子进行滑动采样。滑动窗size:2*window_size+1 ,然后将采样结果存入 dict 中, 中心词为预测目标,在 dict 中对应target 这个key;左右长度为window_size 的上下文拼接,在dict中对应context这个key。采样结果定长,不足长度用MASK_TOKEN 补齐。数据预处理结束。
模型主要模块
我们要feed进神经网络的输入是 dict 中的context, 用batch_size 记为minibatch的大小。输入x_in的shape为:(batch_size, 2*window_size), 我们将这样一个矩阵输入 embedding 层。embedding 层初始化如下:
1 | self.embedding = nn.Embedding(num_embeddings=vocabulary_size, |
这里用 pytorch 中封装好的 torch.nn.Embedding函数来实现。(也可以造轮子,自己实现该模块)
embedding 层的两个重要参数 字典的长度(len(vocab)),记作 vocab_size,和你想要训练的embedding 的size,记作 embed_size。
神经网络在NLP中常见的pipeline
预处理完的数据,不能直接丢进神经网络模型,需要对其数值化。这部分的处理是套路化的几部。在大部分NLP任务几乎都有
首先 根据原始数据 构建 vocab,该模块的核心是构建一张 word-to-index 的dict。完成 word, index的互查
vectorizer 模块,这部分的核心功能是 完成对 原始数据的 数值化,核心函数 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15def vectorize(self, context, vector_length=-1):
"""
:param context(str): the string of words separated by a space
:param vector_length(int): an argument for forcing the length of index vectors
:return:
"""
indices = [self.cbow_vocab.lookup_token(token) for token in context.split(' ')]
if vector_length < 0:
vector_length = len(indices)
out_vector = np.zeros(vector_length, dtype=np.int64)
out_vector[:len(indices)] = indices
out_vector[len(indices):] = self.cbow_vocab.mask_index
return out_vectordataset 模块,对 pytorch 中 torch.utils.data.DataSet 的继承。对一个封装好的对象的继承,可以很好的完成数据到 torch.tensor的转换,需要人工做的主要部分是在其_getitem_()函数中实现对原始数据的vectorize.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16def __getitem__(self, index):
"""the primary entry point method for PyTorch datasets
Args:
index (int): the index to the data point
Returns:
a dictionary holding the data point's features (x_data) and label (y_target)
"""
row = self._target_df.iloc[index]
context_vector = \
self._vectorizer.vectorize(row.context, self._max_seq_length)
target_index = self._vectorizer.cbow_vocab.lookup_token(row.target)
return {'x_data': context_vector,
'y_target': target_index}
经过上述数值化处理后的结果可以feed进神经网络模型。这时x_in的size 为(batch_size, 2*window_size), 值为 token对应的index。
然后将x_in feed进embedding层后,生成的结果 shape:(batch_size, 2*window_size, embed_size), 为了训练 需要 去除 dim=1的维度,使用sum函数在dim=1的维度上求和。然后feed进一个线性层。
线性层参数如下:
1 | self.fc1 = nn.Linear(in_features=embedding_size, |
得到结果 y_out ; shape 为 (batch_size, vocab_size), 然后拿来后 y_target, shape (batch_size),求loss, 误差反向传播,更新优化器,这样 embedding层和线性层都得到训练。
==当然对于这个任务来说,我们关心的是训练好的embedding参数。==