报错

AttributeError: module 'd2l.torch' has no attribute 'EncoderBlock'

原因我的d2l版本:1.0.3。使用的d2l库版本与教程或代码示例的版本不一致;现在的D2L库版本或环境中没有EncoderBlock这个类.

解决方法:使用自定义EncoderBlock类。

class EncoderBlock(nn.Module):
    """Transformer编码器块"""
    def __init__(self, key_size, query_size, value_size, num_hiddens,
                 norm_shape, ffn_num_input, ffn_num_hiddens, num_heads,
                 dropout, use_bias=False, **kwargs):
        super(EncoderBlock, self).__init__(**kwargs)
        self.attention = d2l.MultiHeadAttention(
            key_size, query_size, value_size, num_hiddens, num_heads, dropout, use_bias)
        self.addnorm1 = AddNorm(norm_shape, dropout)
        self.ffn = PositionWiseFFN(ffn_num_input, ffn_num_hiddens, num_hiddens)
        self.addnorm2 = AddNorm(norm_shape, dropout)

    def forward(self, X, valid_lens):
        # 多头注意力 + 残差连接 + 层归一化
        Y = self.addnorm1(X, self.attention(X, X, X, valid_lens))
        # 前馈网络 + 残差连接 + 层归一化
        return self.addnorm2(Y, self.ffn(Y))

同时需要额外定义上述encoder类中需要的MultiHeadAttention、AddNorm、PositionWiseFFN类

import torch
import torch.nn as nn
import torch.nn.functional as F
import math

class MultiHeadAttention(nn.Module):
    """多头注意力机制"""
    def __init__(self, key_size, query_size, value_size, num_hiddens, num_heads, dropout, bias=False, **kwargs):
        super(MultiHeadAttention, self).__init__(**kwargs)
        self.num_heads = num_heads
        self.num_hiddens = num_hiddens
        self.head_dim = num_hiddens // num_heads
        
        assert self.head_dim * num_heads == num_hiddens, "num_hiddens必须能被num_heads整除"
        
        self.w_q = nn.Linear(query_size, num_hiddens, bias=bias)
        self.w_k = nn.Linear(key_size, num_hiddens, bias=bias)
        self.w_v = nn.Linear(value_size, num_hiddens, bias=bias)
        self.w_o = nn.Linear(num_hiddens, num_hiddens, bias=bias)
        
        self.dropout = nn.Dropout(dropout)

    def forward(self, queries, keys, values, valid_lens=None):
        batch_size, seq_len = queries.shape[0], queries.shape[1]
        
        # 线性变换并重塑为多头
        Q = self.w_q(queries).view(batch_size, seq_len, self.num_heads, self.head_dim).transpose(1, 2)
        K = self.w_k(keys).view(batch_size, seq_len, self.num_heads, self.head_dim).transpose(1, 2)
        V = self.w_v(values).view(batch_size, seq_len, self.num_heads, self.head_dim).transpose(1, 2)
        
        # 计算注意力分数
        scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.head_dim)
        
        # 应用有效长度掩码
        if valid_lens is not None:
            mask = torch.arange(seq_len, device=queries.device)[None, :] < valid_lens[:, None]
            mask = mask.unsqueeze(1).unsqueeze(1)
            scores = scores.masked_fill(~mask, -1e9)
        
        # 计算注意力权重
        attention_weights = F.softmax(scores, dim=-1)
        attention_weights = self.dropout(attention_weights)
        
        # 应用注意力权重到values上
        output = torch.matmul(attention_weights, V)
        
        # 重塑并线性变换
        output = output.transpose(1, 2).contiguous().view(batch_size, seq_len, self.num_hiddens)
        return self.w_o(output)

class PositionWiseFFN(nn.Module):
    """基于位置的前馈网络"""
    def __init__(self, ffn_num_input, ffn_num_hiddens, ffn_num_outputs, **kwargs):
        super(PositionWiseFFN, self).__init__(**kwargs)
        self.dense1 = nn.Linear(ffn_num_input, ffn_num_hiddens)
        self.relu = nn.ReLU()
        self.dense2 = nn.Linear(ffn_num_hiddens, ffn_num_outputs)

    def forward(self, X):
        return self.dense2(self.relu(self.dense1(X)))

class AddNorm(nn.Module):
    """残差连接后进行层规范化"""
    def __init__(self, normalized_shape, dropout, **kwargs):
        super(AddNorm, self).__init__(**kwargs)
        self.dropout = nn.Dropout(dropout)
        self.ln = nn.LayerNorm(normalized_shape)

    def forward(self, X, Y):
        return self.ln(self.dropout(Y) + X)

class EncoderBlock(nn.Module):
    """Transformer编码器块 - 替换 d2l.EncoderBlock"""
    def __init__(self, key_size, query_size, value_size, num_hiddens,
                 norm_shape, ffn_num_input, ffn_num_hiddens, num_heads,
                 dropout, use_bias=False, **kwargs):
        super(EncoderBlock, self).__init__(**kwargs)
        self.attention = MultiHeadAttention(
            key_size, query_size, value_size, num_hiddens, num_heads, dropout, use_bias)
        self.addnorm1 = AddNorm(norm_shape, dropout)
        self.ffn = PositionWiseFFN(ffn_num_input, ffn_num_hiddens, num_hiddens)
        self.addnorm2 = AddNorm(norm_shape, dropout)

    def forward(self, X, valid_lens):
        # 多头注意力 + 残差连接 + 层归一化
        attn_output = self.attention(X, X, X, valid_lens)
        Y = self.addnorm1(X, attn_output)
        # 前馈网络 + 残差连接 + 层归一化
        ffn_output = self.ffn(Y)
        return self.addnorm2(Y, ffn_output)

更多推荐