背景说明:
最近B站网课《这是B站目前讲的最好的【Transformer实战】教程!带你从零详细解读Trans…》(2.7模型基本测试运行-第三步_哔哩哔哩_bilibili)中跟着学和操作,中间和最后都出现了奇怪的维度相关的报错,还有一个函数返回的问题。经过分析,在此对相关问题进行讨论。
A. 维度的问题
- 问题说明:
当把MultiHeadedAttention多头注意力类中forward函数中,有这样一句话:
图(1)
如果这样写,那么按照网课中所有测试语句写到的:
图(2)
将出现如下报错:
图(3)
而如果将语句(1)改为
图(4)
0改成1,那么上述问题将不再出现,可是最终copy任务中却会报错:
图(5)
而按照语句1书写时,(5)中所示问题却不会出现。
不论如何,以上出现的两种报错一定是出在了数据与其对应掩码张量维度不agreeable导致的。
- 问题分析与检查:
首先,有必要说明一点:某数据(或者说是某张量),其对应的掩码张量,必须有如下维度规则——
图(6)
先从(5)报错检查,发现tensor a (13) 出现位置在
图(6)
(6)中data_generator意思是生成[1, V)的随机整数,生成每条10个,每批13条,总共20批的整数张量数据。
阅读run_epoch函数发现,他会自适应为每批数据添加mask掩码张量。
图(7)
由此再找向迭代器,发现data_gen返回批数据调用了Batch函数
图(8)
该函数会自动添加掩码张量,新扩充倒数第二维向量。
图(9)
可见以上函数中,自动生成的掩码张量src_mask,比传入的源数据src在倒数第二维上多扩充了一维。即:
图(10)
溯源(7)中的model,层层回溯,最终发现无一不指向MultiHeadedAttention多头注意力机制类。而在该类中,本身就如先前(1)与(4)提到,对mask做过一次维度扩充。所以mask总共做过两次维度扩充操作。
图(11)
至于(只经过数字化的)源数据,则经过了:1. 词嵌入的维度扩展(所谓词嵌入意思就是,不严格的说,比如,把原来的一条数据中的一个数值,再扩张成一条向量);2. 多头注意力机制中的分割(最终得到计算中使用的K,Q,V,我们的掩码张量也是直接作用在这仨上的。)总共也经过了两次维度变化。具体如下所示
图(12)
现在比较一下两者结果:
图(13)
可见,对于copy任务自动生成的掩码张量中,采用unsqueeze(1)生成将符合我们预先声明的规则,而采用unsqueeze(0)生成将在第1、2维度出错。现在我们就解决了copy任务中的报错。
那么,网课中的报错又是为什么?
网课中的每一个测试部分都会经过输入测试数据,而mask掩码张量无一例外采用mask = Variable(torch.zeros(8, 4, 4))产生。务必注意,因为是中间测试点而非最终整体测试,此处mask仅仅经过了多头注意力的维度扩充,而并没有经过采用Batch批数据函数的data_gen生成数据,故也没有被自动扩充倒数第一维(见前述图(9))。
图(14)
故而我们得知网课中掩码张量的实际维度的变化以及与源数据的对应比较应该如下:
图(15)
由此,不难发现网课的中间测试节点测试中,对于mask = Variable(torch.zeros(8, 4, 4))的掩码张量设置,unsqueeze(0)才是正确的,不会报错。至此,问题解决。
- 函数返回问题
在import的pyitcast.transformer_utils包中,SimpleLossCompute函数在高版本中(比如我的)会报错:
图(16)
经过查询后得知,将包内的该函数返回语句从return loss.data[0] * norm改成:loss.item() * norm即可,如下图所示:
图(17)
- 写在最后
这个课程其他应该就没有什么问题了,至少第二章没问题了。第三章如果有问题,并且我解决了的话日后继续回来更新。其实这个课程本身我觉得还是讲的挺不错的,就是这几个小问题还是让我搞了半天。最后推荐一个其他博主的记录链接(虽然没有记录完)。
Transformer 代码详细解析_迷路爸爸180的博客-优快云博客
还有,想该博主一样转发一下哈佛的transformer实现原始版本。
The Annotated Transformer (harvard.edu)
上一个是老版的pytorch实现。网站也给出了新版pytorch实现链接,我也附在此处。