【ML】记录深蓝Transformer作业笔记 第五节,有待更新

写在前面

本文是记录在深蓝学习transformer中的一些思考和作业完成的过程,

记录重点代码片段,和解读,不会po出全部老师的代码

以下是我看课件前的一些疑问,在学习过程中有了解答

  • seq基本框架是什么:
    类似于打电话:声音-电信号-声音,seq是:编码器输入-上下文向量-解码器输出。不同是:声音到电信号有人或自然写定的规律/规则,seq是神经网络学习转化的方式

  • 为什么需要这种框架:

  • sos eos作用:
    提示句子的开始和结束

  • 解码器还需要输入?
    teacher forcing 提高学习速度,原则上只靠上下文向量来学习也可以,但是一错错一串:还是会先预测,预测错了但我使用真实的值。

重点代码

  1. 建立语料库
sentences = [
    ['咖哥 喜欢 小冰 <pad> <pad>', '<sos> KaGe likes XiaoBing <pad>', 'KaGe likes XiaoBing <pad> <eos>'],
    ['我 爱 学习 人工智能 <pad>', '<sos> I love studying AI', 'I love studying AI <eos>'],
    ['深度 学习 改变 世界 <pad>', '<sos> Deep learning YYDS <pad>', 'Deep learning YYDS <pad> <eos>'],
    ['自然 语言 处理 很 强大', '<sos> NLP is so powerful', 'NLP is so powerful <eos>'],
    ['神经网络 非常 复杂 <pad> <pad>', '<sos> Neural Net are complex', 'Neural Net are complex <eos>']]`
for s in sentences:
    word_list_cn.extend(s[0].split())
    word_list_en.extend(s[1].split())
    word_list_en.extend(s[2].split())
word_list_cn = list(set(word_list_cn))
word_list_en = list(set(word_list_en))

分割原始句子,然后set去重复
将每一个句子分成三个句子,并且生成序号和词 在makedata函数中

word2idx_cn={w: i for i, w in enumerate(word_list_en)}

enumerate 返回元组,而w:i直接传建字典
2. 生成训练数据
我理解第一和第二步就是在生成torch网络直接能用的数据,和torch的dataloader,dataset一个意思。

random_sentence = random.choice(sentences)
encoder_input = np.array([[word2idx_cn[n] for n in random_sentence[0].split()]])
decoder_input = np.array([[word2idx_en[n] for n in random_sentence[1].split()]])
# 将目标句子中的单词转换为对应的索引
target = np.array([[word2idx_en[n] for n in random_sentence[2].split()]])

如果 random_sentence 是 [‘咖哥 喜欢 小冰 ’, ‘ KaGe likes XiaoBing ’, ‘KaGe likes XiaoBing ’],且对应的 word2idx_cn 字典如下所示:{‘咖哥’: 0, ‘喜欢’: 1, ‘小冰’: 2, ‘’: 3},
那么 encoder_input 将是 array([[0, 1, 2, 3, 3]]),表示输入句子中每个中文单词的索引。
在这里插入图片描述解码器的数据输入和真实输出是错开一位的,如之前在疑问中所讲

  1. 定义encoder decoder 和模型类
class Encoder(nn.Module):
    def __init__(self, input_size, hidden_size):
        super(Encoder, self).__init__()       
        self.hidden_size = hidden_size # 设置隐藏层大小       
        self.embedding = nn.Embedding(input_size, hidden_size) # 创建词嵌入层       
        self.rnn = nn.RNN(hidden_size, hidden_size, batch_first=True) # 创建RNN层
    # 定义前向传播函数
    def forward(self, inputs, hidden):
        embedded = self.embedding(inputs) # 将输入转换为嵌入向量       
        output, hidden = self.rnn(embedded, hidden) # 将嵌入向量输入RNN层并获取输出
        return output, hidden

# 定义解码器类,继承自nn.Module
class Decoder(nn.Module):
    def __init__(self, hidden_size, output_size):
        super(Decoder, self).__init__()       
        self.hidden_size = hidden_size # 设置隐藏层大小       
        self.embedding = nn.Embedding(output_size, hidden_size) # 创建词嵌入层
        self.rnn = nn.RNN(hidden_size, hidden_size, batch_first=True)  # 创建RNN层       
        self.out = nn.Linear(hidden_size, output_size) # 创建线性输出层
    # 定义前向传播函数
    def forward(self, inputs, hidden):       
        embedded = self.embedding(inputs) # 将输入转换为嵌入向量       
        output, hidden = self.rnn(embedded, hidden) # 将嵌入向量输入RNN层并获取输出       
        output = self.out(output) # 使用线性层生成最终输出
        return output, hidden
  • 嵌入层用于将词汇表中的单词映射到连续的向量空间,以便神经网络能够处理。input_size是词汇表的大小,hidden_size是嵌入向量的维度。

  • batch_first=True表示在输入数据中批次维度是第一个维度。

  • 将输入的inputs通过嵌入层embedding,将每个单词转换成嵌入向量。将嵌入向量输入到RNN中,并得到RNN的输出output和新的隐藏状态hidden

  • 嵌入层的工作原理是将每个输入的整数索引转换为对应的嵌入向量,这是通过查找嵌入层的权重矩阵中的行来实现的。每个单词都有一个唯一的整数索引,这个索引将用于查找对应的嵌入向量。嵌入层的参数在训练过程中会被学习,以便它们可以捕获输入数据中的语义信息。这使得模型可以更好地理解单词之间的关系,从而有助于提高模型的性能。

  • 解码器多一层线性层,匹配输出大小

  • decoder的input形参接受decoder_input,hidden为context也就是encoder的hidden输出

class Seq2Seq(nn.Module):
    def __init__(self, encoder, decoder):
        super(Seq2Seq, self).__init__()
        # 初始化编码器和解码器
        self.encoder = encoder
        self.decoder = decoder
    # 定义前向传播函数
    def forward(self, source, hidden, target):
        # 将输入序列通过编码器并获取输出和隐藏状态
        encoder_output, encoder_hidden = self.encoder(source, hidden)
        # 将编码器的隐藏状态传递给解码器作为初始隐藏状态
        decoder_hidden = encoder_hidden
        # 将目标序列通过解码器并获取输出
        decoder_output, _ = self.decoder(target, decoder_hidden)
        return decoder_output
  • encoder_hidden就是上下文向量context
  • 疑问??? encoder的output干吗去了为神马不用output来做context
    在这里插入图片描述
  1. 定义训练函数略

        hidden = torch.zeros(1, encoder_input.size(0), n_hidden) # 初始化隐藏状态      

这里初始化隐藏状态,要给一个状态是网络结构要求的
6. 测试函数

decoder_input = np.array([word2idx_en['<sos>']] + [word_dict['<eos>']] * (len(encoder_input[0]) - 1))
decoder_input = torch.LongTensor(decoder_input).unsqueeze(0)

为decoder_input添加了一个额外的维度,以符合模型的输入要求。

为decoder_input添加了一个额外的维度,以符合模型的输入要求。原来的维度不就是这样吗?,我之前在yawrate的demo中改用rnn,链接:demo,之后有空更新一下rnn结构的yawrate估计,用单例模式测试是也出过同样的问题,求教了gpt以下是gpt给我的解答:

在这段代码中,为了理解为什么在decoder_input = torch.LongTensor(decoder_input).unsqueeze(0)中添加了一个维度,需要了解PyTorch的张量形状和序列到序列(seq2seq)模型的工作方式。

在seq2seq模型中,输入和输出序列通常是多个时间步的序列,每个时间步包含一个单词或符号。因此,序列的形状通常是(batch_size,
sequence_length),其中batch_size表示批处理中的样本数量,sequence_length表示时间步的数量。

在这段代码中,encoder_input和decoder_input都是单个句子的索引序列,因此它们的形状是(1,
sequence_length),其中1表示批处理中只有一个样本。这是因为在测试函数中,您处理的是单个输入句子,而不是一批句子。

PyTorch中的RNN层(以及其他神经网络层)期望输入的形状为(batch_size, sequence_length,
input_size),其中batch_size通常表示在一个时间步中处理多个序列,但在这种情况下,我们只有一个序列。因此,为了使这个单个序列符合RNN层的输入要求,需要将其形状从(1,
sequence_length)更改为(1, sequence_length, input_size)。

unsqueeze(0)方法的作用是在索引0的位置添加一个新维度,这将在最外层添加一个维度,从而将形状更改为(1,
sequence_length)到(1, 1, sequence_length)。这是为了使输入张量符合RNN层期望的形状。

所以,这一行代码的目的是确保decoder_input的形状与RNN层的要求相匹配,即添加了一个额外的维度来表示批处理中的单个样本。这是因为RNN层的输入通常是三维张量,表示为(batch_size,
sequence_length, input_size),即使在这里batch_size为1。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值