【python关于d2l版本不匹配】【李沐《动手学深度学习》BERT代码复现】缺少模块的问题解决办法
·
报错
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)
更多推荐
所有评论(0)