今天分析一下前向传播的整个过程。
在训练之前,先解决昨天的一个疑问,我们输入的句子经过预处理后,变成了 32 ∗ 32 32 * 32 32∗32的id矩阵,而导入的模型里面的隐藏单元是768,也就是说输入的维度应该是768,那么这 32 ∗ 32 32 * 32 32∗32的矩阵是如何变成768维的呢?
矩阵里存放的每个id值都对应着一个字,而这些字应该都有对应的embedding,而word embedding的shape是 18018 ∗ 768 18018 * 768 18018∗768,词汇表里正好有18018行,可以确定一个字的embedding是768维的。
那么思路就有了,一句话里有32个字,那么把这句话转成对应的embedding,一句话维度应该就是 32 ∗ 768 32 * 768 32∗768。32句话就是 32 ∗ 32 ∗ 768 32 * 32 *768 32∗32∗768这样一个矩阵,进入隐藏层。
那么在代码中就是如何实现的呢?
在ErnieGramEmbeddings类中的forward函数里只需要一句话就可实现
input_embedings = self.word_embeddings(input_ids)
word_embeddings是nn.Embedding的实例对象
self.word_embeddings = nn.Embedding(
vocab_size, emb_size, padding_idx=pad_token_id)
在Embedding的forward函数里,又返回了一个embedding函数
def forward(self, x):
return F.embedding(
x,
weight=self.weight,
padding_idx=self._padding_idx,
sparse=self._sparse,
name=self._name)
进入到F.embedding函数里,终于找到了最底层的函数
传入Weight和x,它的作用应该就是在word embedding中根据id(行号)找到对应的一行
最后返回的input_embedings的结果如下
可以看到,经过一些列操作,输入的ID值变成了对应的embedding向量
在前向计算的过程中,我们需要三个embedding:input、position、token type
最后返回的embeddings是三者相加,然后进行归一化、dropout防止过拟合。
返回的embeddings的数据如下:
返回的embeddings最后给到了embedding_output,这个值,进入了encoder层,继续在网络中继续训练,
然后所有句子的输出都保存到了sequence_output之中,但我们只需要序列中第一个标记(’ [CLS] ')的输出,于是将sequence_output进入到pooler层,对CLS位置进行线性全连接,将它作为整个sequence的输出。
由此,前向传播的整个过程计算结束了,写了这么多,在实际代码中只需要一行即可完成
probs = model(input_ids=input_ids, token_type_ids=token_type_ids)
接下来,就是评估损失、梯度下降、进行优化了,在此之前,我先补一补NLP的一些概念。