BiDAF添加Self-Attention

发布于 2020-05-15  1.48k 次阅读


项目地址:BiDAF-pytorch-with-Self-Attention

参考论文:R-NET(Microsoft Research Asia)

简单的PyTorch练习项目,为现成的BiDAF添加Self-Attention。

这篇博客全当是萌新学习pytorch的笔记就好。

需要修改的文件只有model/model.py。毕竟是练手项目,所以似乎并无什么坑点,只要照着论文一步步实现就好了吧

核心代码:

def self_attention_layer(v):
    """
    :param v: (batch, c_len ,hidden_size * 2)
    :return: x: (batch, c_len ,hidden_size * 2)
    """

    c_len = v.size(1)

    # (batch, c_len)
    wv = self.att_weight_w(v).squeeze()

    # (batch, c_len)
    w2v = self.att_weight_w2(v).squeeze()

    x = []

    for i in range(c_len):
        # (batch, c_len) ->  (batch, 1)
        w2vi = w2v.index_select(1, torch.LongTensor([i]).to(self.device))
        # (batch, 1) -> (batch, c_len)
        w2vi = w2vi.expand(-1, c_len)
        # (batch, c_len) + (batch, c_len) -> (batch, c_len)
        wv3 = wv + w2vi
        # (batch, c_len)
        wv3 = self.att_fix * wv3
        # (batch, c_len)
        wv3 = torch.exp(torch.tanh(wv3))
        # (batch, 1)
        su = torch.sum(wv3, 1).unsqueeze(1)
        # (batch, 1) -> (batch, c_len)
        su = su.expand(-1, c_len)
        # (batch, c_len)
        wv3 = torch.div(wv3, su)
        # (batch, c_len, 1)
        wv3 = wv3.unsqueeze(2)
        # (batch, c_len, 1) -> (batch, 1, c_len)
        wv3 = wv3.permute(0, 2, 1)
        # (batch, 1, c_len) * (batch, c_len ,hidden_size * 2) -> (batch, hidden_size * 2)
        xi = torch.bmm(wv3, v).squeeze()
        #  (1...c_len, batch, hidden_size * 2)
        x.append(xi)

    # (batch, hidden_size * 2, c_len)
    x = torch.stack(x, 2).to(self.device)
    # (c_len, batch, hidden_size * 2) -> (batch, c_len, hidden_size * 2)
    x = x.permute(0, 2, 1)
    return x

主要意义大概就是学习torch的几个函数吧:

torch.size(dim)

显而易见,获取张量dim维的大小。

torch.index_select(dim, index) 

将指定张量按照dim维度进行切片,index为引下标的一维张量。

注意临时张量千万别忘记to(device),否则会报错。

expand(dim, new_size)

将张量的dim维由1扩增到new_size,不消耗内存空间。

torch.unsqueeze(dim)

在张量的dim维插入大小为1的维度。

torch.div(x, y)

执行张量除法,对大小相同的张量为逐位除。

torch.sum(x, dim)

对张量x的dim维求和,求和后dim维消失。

torch.permute(...)

对张量各维度进行重排,参数为之前的维度顺序。(若参数为(a, b, c)),则将之前的第(a, b, c)维转化为新的第(0, 1, 2)维。

torch.bmm(x, y)

进行批矩乘。输入x, y为大小为(batch, a, b), (batch, b, c)的向量,则输出大小为(batch, a, c)的向量,(1, 2)维运算同常规矩乘。

torch.stack(x, dim)

将存储张量列表、元组转化为新的张量,dim为新增的维度。

还是别忘了to(device)。

torch.exp()
torch.tanh()

标准的算术操作,分别作用于张量的每个位置。

效果并不怎么好的样子,默认参数下连原版baseline都不如。(话说我直接训练原版模型也达不到人家GitHub上说明的成绩(非酋实锤),添加Self-Attention后模型的表现比我训练的原版模型稍好一些)

大概是我写丑了吧。似乎要达到论文中的水平还需要一些奇怪的姿势呢,比如玄学调参、调结构(真·炼丹)。

GTX970用来测试+Debug还是够用的,真正训练直接交给学校的1080和1080ti。

显卡:果然和炼丹相比,还是炼铜更适合我。

所以说,现在N卡炼丹炼铜两不误。A卡呢?在ROCm完全可用之前,开始爪巴比较适合她。真·Game Processing Unit。

(回学校后Debian部署ROCm教程预定)


Faster than LIGHT