李宏毅机器学习2021/2022 HW5学习记录

用s2s来完成机器翻译的工作。
这个代码要安装的库fairseq由于年代久远,有点兼容性问题,所以实际并没有跑代码,记录一下学习过程。
不涉及具体的代码,整理了一下encoder和decoder框架,结合了自注意力机制

基础代码

基础代码采用的是RNN的网络架构,它采用了双向RNN网络,结合了自注意力机制。

RNN

参考链接

基本网络结构

RNN的基本网络结构如下:
在这里插入图片描述
双向的RNN结构如下:
在这里插入图片描述

代码

Pytorch中有写好的RNN的代码,设置好参数就能直接调用:
RNN公式
在这里插入图片描述

需要设置的参数及含义如下:

  • input_size – input x 的特征数,RNN接收一个形状为(batch, input_size)的输入张量x
  • hidden_size – 隐藏状态h的特征数量
  • num_layers – 循环层的数量. Default: 1
  • nonlinearity – 非线性激活函数的选择, ‘tanh’ or ‘relu’. Default: ‘tanh’
  • bias – 是否使用偏置项。如果为False,RNN层将不使用偏置权重b_ih和b_hh 。Default: True
  • batch_first –是否将输入和输出张量设置为(batch, seq, feature)的形式,而不是传统的(seq, batch, feature)形式。.Default: False
  • dropout – Default: 0
  • bidirectional – 是否使用双向RNN。Default: False

输入相关:

  • input:输入序列的张量,其形状为(batch, seq, feature)或(seq, batch, feature)。其中,batch表示批次大小,seq表示时间步数,feature表示输入特征的数量。当使用可变长度序列时,输入可以是一个打包的变长序列。
  • h_0:初始隐藏状态的张量,其形状为(Dnum_layers, batch, hidden_size)或(Dnum_layers, seq, batch, hidden_size)。其中,D表示双向RNN的方向数(如果是双向RNN),num_layers表示循环层的数量,hidden_size表示隐藏状态的特征数量。初始隐藏状态用于指定RNN在开始处理输入序列之前的初始状态值,默认为全零张量。

输出相关:

  • output:RNN模型的输出张量,形状为(batch, seq, Dhidden_size)或(seq, batch, Dhidden_size)或(seq, batch, D*hidden_size)。其中,batch表示批次大小,seq表示时间步数,D表示双向RNN的方向数(如果是双向RNN),hidden_size表示隐藏状态的特征数量。输出包含了RNN最后一层的所有时间步的隐藏状态h_t。
  • h_n:最终的隐藏状态张量,形状为(Dnum_layers, batch, hidden_size)或(Dnum_layers, seq, batch, hidden_size)。其中,D表示双向RNN的方向数(如果是双向RNN),num_layers表示循环层的数量,hidden_size表示隐藏状态的特征数量。h_n包含了每个输入元素的最终隐藏状态。
  • weight_ih_l[k]:第k层的输入到隐藏状态的权重,形状为(hidden_size, input_size)。当k=0时,input_size表示输入特征的数量,否则(input_size, num_directions * hidden_size)。这些权重是可学习的参数。
  • weight_hh_l[k]:第k层的隐藏状态到隐藏状态的权重,形状为(hidden_size, hidden_size)。这些权重是可学习的参数。
  • bias_ih_l[k]:第k层的输入到隐藏状态的偏置项,形状为(hidden_size)。这些偏置项是可学习的参数。
  • bias_hh_l[k]:第k层的隐藏状态到隐藏状态的偏置项,形状为(hidden_size)。这些偏置项是可学习的参数。

应用实例如下:

#########定义模型和输入#########
# (input_size, hidden_size, num_layers)
rnn = nn.RNN(10, 20, 1)
# (seq_len, batch_size, input_size)
input = torch.randn(5, 3, 10)
# (num_layers, batch_size, hidden_size)
h0 = torch.randn(1, 3, 20)
# 获取输出
output, hn = rnn(input, h0)

Encoder-Decoder框架

Seq2Seq模型的基本思想非常简单一一使用一个循环神经网络读取输入句子,将整个句子的信息压缩到一个固定维度(注意是固定维度,下文的注意力集中机制将在此做文章)的编码中;再使用另一个循环神经网络读取这个编码,将其“解压”为目标语言的一个句子。这两个循环神经网络分别称为编码器(Encoder)和解码器(Decoder)
在这里插入图片描述
Decoder:根据x的中间语义表示和已经生成的y1~yi-1 来生成 i 时刻的 yi。解码器部分的结构与一般的语言模型几乎完全相同:输入为单词的词向量,输出为softmax层产生的单词概率,损失函数为log perplexity。事实上,解码器可以理解为一个以输入编码为前提的语言模型。语言模型中使用的一些技巧,如共享softmax层和词向量的参数,均可以直接应用到 Seq2Seq模型的解码器中。

Encoder:对输入序列
进行编码,通过非线性变换转化为中间语义表示
。编码器部分网络结构则更为简单。它与解码器一样拥有词向量层和循环神经网络,但是由于在编码阶段并未输出最终结果,因此不需要softmax层。

用GPT给的代码理解一下。

Encoder 类

class Encoder(nn.Module):
    def __init__(self, input_size, embed_size, hidden_size, num_layers, dropout):
        super().__init__()
        self.embedding = nn.Embedding(input_size, embed_size)
        self.rnn = nn.GRU(embed_size, hidden_size, num_layers, dropout=dropout, batch_first=True)
        self.dropout = nn.Dropout(dropout)

    def forward(self, src):
        embedded = self.dropout(self.embedding(src))
        outputs, hidden = self.rnn(embedded)
        return outputs, hidden

torch.nn.GRU
参数:

  • input_dim 表示输入的特征维度
  • hidden_dim 表示输出的特征维度,如果没有特殊变化,相当于out
  • num_layers 表示网络的层数
  • nonlinearity 表示选用的非线性**函数,默认是 ‘tanh’
  • bias 表示是否使用偏置,默认使用
  • batch_first 表示输入数据的形式,默认是 False,[即(序列长度seq,批大小batch,特征维度feature)];若True则(batch,seq,feature)
  • dropout 缺省值为0,表示不使用dropout层;若为1,则除最后一层外,其它层的输出都会加dropout层
  • bidirectional 表示是否使用双向的 rnn,默认是 False
    输出:
    out:[seq_len,batch_size,output_dim],所有输出
    ht:[num_layers * num_directions, batch_size, hidden_size],num_directions=1,单向,取值2时为双向,num_layers为层数,最后一个输出

Attention

class Attention(nn.Module):
    def __init__(self, hidden_size, num_heads, dropout):
        super().__init__()
        self.attention = nn.MultiheadAttention(hidden_size, num_heads, dropout=dropout)

    def forward(self, query, key, value, mask):
        attn_output, _ = self.attention(query, key, value, attn_mask=mask)
        return attn_output

Decoder 类

class Decoder(nn.Module):
    def __init__(self, output_size, embed_size, hidden_size, num_layers, dropout, num_heads):
        super().__init__()
        # 嵌入层将目标语言的单词索引转换为向量
        self.embedding = nn.Embedding(output_size, embed_size)
        # RNN处理拼接的嵌入向量和注意力上下文向量
        self.rnn = nn.GRU(hidden_size + embed_size, hidden_size, num_layers, dropout=dropout, batch_first=True)
        # 注意力层计算当前解码器隐藏状态和所有编码器输出之间的注意力
        self.attention = Attention(hidden_size, num_heads, dropout)
        # 线性层将RNN的输出映射到输出词汇表的大小
        self.fc_out = nn.Linear(hidden_size, output_size)
        # dropout用于正则化
        self.dropout = nn.Dropout(dropout)

    def forward(self, trg, hidden, encoder_outputs, src_mask):
        # trg:当前的目标词索引
        # hidden:解码器的上一个隐藏状态
        # encoder_outputs:编码器的所有输出
        # src_mask:源序列的掩码,用于注意力计算中忽略<pad> token

        # trg增加一个维度,以符合RNN的输入要求
        trg = trg.unsqueeze(1)
        # 对目标词进行嵌入
        embedded = self.dropout(self.embedding(trg))
        # 计算注意力
        attn_output = self.attention(embedded, encoder_outputs, encoder_outputs, src_mask)
        # 将注意力输出和嵌入向量拼接作为RNN的输入
        rnn_input = torch.cat((embedded, attn_output), dim=2)
        # 通过RNN
        output, hidden = self.rnn(rnn_input, hidden)
        # 预测下一个词
        prediction = self.fc_out(output.squeeze(1))
        # 返回预测和新的隐藏状态
        return prediction, hidden

Seq2Seq 类

class Seq2Seq(nn.Module):
    def __init__(self, encoder, decoder, src_pad_idx, device):
        super().__init__()
        # 初始化编码器和解码器
        self.encoder = encoder
        self.decoder = decoder
        # 源序列中的<pad> token的索引
        self.src_pad_idx = src_pad_idx
        # 设备(CPU或GPU)
        self.device = device

    def create_mask(self, src):
        # 创建掩码以忽略<pad> token
        mask = (src != self.src_pad_idx).permute(1, 0)
        return mask

    def forward(self, src, trg):
        # src:源序列
        # trg:目标序列

        # 对源序列进行编码
        encoder_outputs, hidden = self.encoder(src)
        # 为源序列创建掩码
        src_mask = self.create_mask(src)

        trg_len = trg.shape[1]
        batch_size = trg.shape[0]
        trg_vocab_size = self.decoder.embedding.num_embeddings
        # 初始化一个存储预测结果的tensor
        outputs = torch.zeros(batch_size, trg_len, trg_vocab_size).to(self.device)

        # 解码器的初始输入是目标序列的第一个词
        input = trg[:, 0]
        for t in range(1, trg_len):
            # 对每个时间步进行解码
            output, hidden = self.decoder(input, hidden, encoder_outputs, src_mask)
            # 存储预测结果
            outputs[:, t] = output
            # 获取概率最高的词作为下一个输入
            top1 = output.argmax(1)
            input = top1

        # 返回所有时间步的预测结果
        return outputs

写到这里还是云里雾里,打算先放一放

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值