图解transformer和相应代码及参数设定

       图解transformer会更加直观一些,比代码直观,其中的参数也能够更加直观的认识,加深印象,所以是一种比较好的方式来理解。如果说transformer是设计摩天大楼(LLM就是摩天大楼,transformer是摩天大楼中的主体部分)的解决方案,那么这个图就是设计摩天大楼的内部核心图纸,值得一看。

      当然,这个图略显简单,其关联众多要素和其逻辑、数据流转方式是背后隐性较为重要的东西。当然,其很神奇,神奇的根源来自其对元素与元素关系的简洁、直接、有效的计算,最后以注意力权重的形式体现,整体以注意力输出来体现,这很奇妙,奇妙的方式计算人类关注事物、联想和建立联系的实质,挖开了人类智能的一个缺口,宝藏被大量释放。

1.模型图解

      下面这个图蕴含着丰富的信息,值得好好解读一下,其中每个模块和层及名称都是对应模型设定的一部分。这个图画得很精确,简洁,也很经典,信息量也大。后面的参数和代码实现,都和图中内容一一对应,其中的数据流描述也很精确。可以说,这个图可以看成是打开transformer的车钥匙

       看图说话,从大到小的表达,左边是 的内部结构,右边是的内部结构。

 2.模型代码:

       玩转代码是操作transformer模型的“驾驶技术”,把握方向盘,开油门,踩刹车,换挡等等,都是通过代码中的设置来操作的。

encoder = TransformerEncoder(
    len(src_vocab), key_size, query_size, value_size, num_hiddens,
    norm_shape, ffn_num_input, ffn_num_hiddens, num_heads,
    num_layers, dropout)

decoder = TransformerDecoder(
    len(tgt_vocab), key_size, query_size, value_size, num_hiddens,
    norm_shape, ffn_num_input, ffn_num_hiddens, num_heads,
    num_layers, dropout)

net = d2l.EncoderDecoder(encoder, decoder)

TransformerEncoder与 这部分相对应,

其中blk指block(块),与这部分相应,N\times的意思是N个模块叠加,与下面的代码对应:

for i, blk in enumerate(self.blks):
            X = blk(X, valid_lens)
            self.attention_weights[i] = blk.attention.attention.attention_weights
        return X

 模型中这个部分,pos_encoding与对应,

self.embedding与对应,

X与图中对应,

对应的代码如下,:

X = self.pos_encoding(self.embedding(X) * math.sqrt(self.num_hiddens))

 依此类推,代码中的变量与图中的部分都是一一对应关系。

TransformerEncoder的全部代码如下

class TransformerEncoder(d2l.Encoder):
    def __init__(self, vocab_size, key_size, query_size, value_size,
                 num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens,
                 num_heads, num_layers, dropout, use_bias=False, **kwargs):
        super(TransformerEncoder, self).__init__(**kwargs)
        self.num_hiddens = num_hiddens
        self.embedding = nn.Embedding(vocab_size, num_hiddens)
        self.pos_encoding = d2l.PositionalEncoding(num_hiddens, dropout)
        self.blks = nn.Sequential()
        for i in range(num_layers):
            self.blks.add_module("block"+str(i),
                EncoderBlock(key_size, query_size, value_size, num_hiddens,
                             norm_shape, ffn_num_input, ffn_num_hiddens,
                             num_heads, dropout, use_bias))

    def forward(self, X, valid_lens, *args):
# 因为位置编码值在-1和1之间,
# 因此嵌⼊值乘以嵌⼊维度的平⽅根进⾏缩放,
# 然后再与位置编码相加。
        X = self.pos_encoding(self.embedding(X) * math.sqrt(self.num_hiddens))
        self.attention_weights = [None] * len(self.blks)
        for i, blk in enumerate(self.blks):
            X = blk(X, valid_lens)
            self.attention_weights[i] = blk.attention.attention.attention_weights
        return X

 TransformerDecoder与这部分相应

       代码中的DecoderBlock与图中这个部分对应,DecoderBlock      在 TransformerDecoder代码中被“浓缩成”blk,整个代码的执行X, state = blk(X, state),其中state有三个部分:enc_outputs(encoder的输出), enc_valid_lens(encoder的有效长度) = state[0], state[1],state[2]有两种状态下,代表的内容不同,其中训练模式下key_values = X,state[2]没有实质性内容;预测模式下,key_values = torch.cat((state[2][self.i], X), axis=1),state[2][self.i] = key_values,state[2]是前面所有时刻的信息(代表上下文)。

DecoderBlock的代码:

class DecoderBlock(nn.Module):
"""解码器中第i个块"""
    def __init__(self, key_size, query_size, value_size, num_hiddens,
                 norm_shape, ffn_num_input, ffn_num_hiddens, num_heads,
                 dropout, i, **kwargs):
        super(DecoderBlock, self).__init__(**kwargs)
        self.i = i
        self.attention1 = d2l.MultiHeadAttention(
        key_size, query_size, value_size, num_hiddens, num_heads, dropout)
        self.addnorm1 = AddNorm(norm_shape, dropout)
        self.attention2 = d2l.MultiHeadAttention(
        key_size, query_size, value_size, num_hiddens, num_heads, dropout)
        self.addnorm2 = AddNorm(norm_shape, dropout)
        self.ffn = PositionWiseFFN(ffn_num_input, ffn_num_hiddens,num_hiddens)
        self.addnorm3 = AddNorm(norm_shape, dropout)
    def forward(self, X, state):
        enc_outputs, enc_valid_lens = state[0], state[1]
# 训练阶段,输出序列的所有词元都在同⼀时间处理,
# 因此state[2][self.i]初始化为None。
# 预测阶段,输出序列是通过词元⼀个接着⼀个解码的,
# 因此state[2][self.i]包含着直到当前时间步第i个块解码的输出表⽰
        if state[2][self.i] is None:
            key_values = X
        else:
            key_values = torch.cat((state[2][self.i], X), axis=1)
        state[2][self.i] = key_values
        if self.training:
            batch_size, num_steps, _ = X.shape
# dec_valid_lens的开头:(batch_size,num_steps),
# 其中每⼀⾏是[1,2,...,num_steps]
            dec_valid_lens = torch.arange(
                1, num_steps + 1, device=X.device).repeat(batch_size, 1)
        else:
            dec_valid_lens = None
# ⾃注意⼒
        X2 = self.attention1(X, key_values, key_values, dec_valid_lens)
        Y = self.addnorm1(X, X2)
# 编码器-解码器注意⼒。
# enc_outputs的开头:(batch_size,num_steps,num_hiddens)
        Y2 = self.attention2(Y, enc_outputs, enc_outputs, enc_valid_lens)
        Z = self.addnorm2(Y, Y2)
        return self.addnorm3(Z, self.ffn(Z)), state

        同样的,代码中的 pos_encoding与对应,代码中的self.embedding与图中相对应。

TransformerDecoder具体代码如下,各部分的详细对应情况

class TransformerDecoder(d2l.AttentionDecoder):
    def __init__(self, vocab_size, key_size, query_size, value_size,
                 num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens,
                num_heads, num_layers, dropout, **kwargs):
        super(TransformerDecoder, self).__init__(**kwargs)
        self.num_hiddens = num_hiddens
        self.num_layers = num_layers
        self.embedding = nn.Embedding(vocab_size, num_hiddens)
        self.pos_encoding = d2l.PositionalEncoding(num_hiddens, dropout)
        self.blks = nn.Sequential()
        for i in range(num_layers):
            self.blks.add_module("block" + str(i),
                                 DecoderBlock(key_size, query_size, value_size, num_hiddens,
                                              norm_shape, ffn_num_input, ffn_num_hiddens,
                                              num_heads, dropout, i))
        self.dense = nn.Linear(num_hiddens, vocab_size)

    def init_state(self, enc_outputs, enc_valid_lens, *args):
        return [enc_outputs, enc_valid_lens, [None] * self.num_layers]

    def forward(self, X, state):
        X = self.pos_encoding(self.embedding(X) * math.sqrt(self.num_hiddens))

        self._attention_weights = [[None] * len(self.blks) for _ in range(2)]
        for i, blk in enumerate(self.blks):
            X, state = blk(X, state)
        # 解码器⾃注意⼒权重
        self._attention_weights[0][
            i] = blk.attention1.attention.attention_weights
        # “编码器-解码器”⾃注意⼒权重
        self._attention_weights[1][
            i] = blk.attention2.attention.attention_weights
        return self.dense(X), state

    def attention_weights(self):
        return self._attention_weights

3.参数设置与模型图解对应

        参数设置大概相当于汽车的各个部件的参数和操控方式,有发动机、传动装置、底盘、前大灯、空调、变速箱等等。 汽车也是个模型,transformer也是个模型,模型内部的构造如此,参数大体相应。

        代码中有相关的一些参数设置。参数设置也与内部的元素有关,比如key_size, query_size, value_size与其中的形状设定有关,num_hiddens与的设定有关,ffn_num_input, ffn_num_hiddens与设定有关,norm_shape与的设定有关。num_layers就是中的N\times,和中的N\times

4.模型图解与数据流运转

Transformer的数据流运转很重要,图中有相对清晰的体现,不过细节需要通过代码的执行过程来熟悉和了解,只有把数据流运转弄懂弄通,你才能真正掌握并熟练使用transformer。

Transformers是一种用于自然语言处理和其他相关领域的深度学习模型。它是通过编码器-解码器结构实现的。编码器将输入序列转换为一系列隐藏状态,而解码器则根据编码器的输出和之前的上下文生成输出序列。 关于图解transformer,引用中提到的图显示了Transformer的位置编码方法,其中将两个信号交织在一起。这个图可以帮助我们更好地理解位置编码的实现方式。 此外,引用中还展示了一个包含两个堆叠编码器和解码器的Transformer结构的图示。这个图可以帮助我们了解多层Transformer的组织结构。 最后,引用中提到的训练模型的直觉可以帮助我们更好地理解Transformer的工作原理。这个直觉的图示可能显示了一些与训练有关的信息,可以帮助我们更好地理解整个前向传递过程。 综上所述,通过引用中提到的图示,我们可以更好地理解Transformer模型的一些关键概念和操作。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [【Transformer图解 Transformer](https://blog.csdn.net/sikh_0529/article/details/128968765)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值