Hugging Face Wav2Vec2分类训练触发requires_grad类RuntimeError求助
报错根因定位
你遇到的RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn完全是训练流程的逻辑写错导致的,不存在需要翻PyTorch底层的复杂问题,所有报错点全部能对着你贴的代码逐行找到:
直接触发报错的核心错误
- 训练流程完全跳过了模型前向传播
你算损失用的张量是经过手动softmax处理的原始输入Input,全程没有写output = model(Input)这步,整个计算过程根本不经过模型的任何网络层,自然没有关联计算图的grad_fn,PyTorch检测到你要求对一个和可训练参数毫无关系的张量求导,直接抛出这个报错。 - 新加的分类层根本没开梯度
你之前先遍历所有模型参数把requires_grad设成了False,等后面才给model.classifier赋值自定义的网络层。这时候这些新层的参数默认继承之前的冻结状态,就算你补全前向传播逻辑,这些层的参数依然不会计算梯度,还是会报一模一样的错。
就算修好上面的问题,后面跑起来也会崩的连带bug
- 模型选错了:你用的
Wav2Vec2ForCTC是给语音转录设计的CTC结构,根本不是做分类任务用的,结构上就不匹配,应该换成Wav2Vec2ForSequenceClassification。 - 任务维度和输出头对不上:你明明白白要做5分类,最后分类层输出的是2类维度;手动转one-hot的时候只处理了0、1类的标签,剩下2-4类的标签连对应逻辑都没有,算损失的时候维度直接不匹配。
- 输入形状完全不符合Wav2Vec的要求:Wav2Vec需要的输入形状是
[batch_size, 音频序列长度]的1D张量,你把输入额外加了个维度变成类似CNN的输入形状,还对原始输入单独做softmax,直接把原始信号的分布改得面目全非,模型根本没法正常推理。 - 训练循环的变量名写错了:循环遍历的时候用的是
row变量,到打印损失的分支突然用了根本没定义的idx,跑到那行直接报NameError。
可直接替换的修复代码
按顺序调整完所有逻辑就能正常跑通训练:
- 替换适配分类任务的模型,把配置对齐到5分类要求
from transformers import AutoConfig, Wav2Vec2ForSequenceClassification num_labels = 5 model_name = "Zaid/wav2vec2-large-xlsr-53-arabic-egyptian" # 配置直接指定为单标签分类任务,关闭CTC相关逻辑 model_config = AutoConfig.from_pretrained( model_name, num_labels=num_labels, problem_type="single_label_classification" ) # 加载分类专用的Wav2Vec结构,跳过原转录头的维度不匹配告警 model = Wav2Vec2ForSequenceClassification.from_pretrained( model_name, config=model_config, ignore_mismatched_sizes=True ) - 冻结预训练主干,单独放开新分类层的梯度
# 冻结Wav2Vec的特征编码器主干 model.freeze_feature_encoder() # 替换自定义分类头,把输出维度对齐到5分类 model.classifier = nn.Sequential( nn.Linear(768, 256), nn.Dropout(0.25), nn.ReLU(), nn.Linear(256, 64), nn.Dropout(0.25), nn.ReLU(), nn.Linear(64, num_labels), nn.Dropout(0.25), nn.Softmax(dim=1) ) # 必须单独给新分类层开梯度,不然还是没法反向传播 for param in model.classifier.parameters(): param.requires_grad = True # 损失函数换成分类更稳定的CrossEntropyLoss,优化器只传入需要训练的参数 criterion = nn.CrossEntropyLoss().to(device) optimizer = AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr=2e-5, eps=1e-6) - 修正训练循环的逻辑错误
print_every = 300 total_loss = 0 all_losses = [] model.train() model.to(device) for epoch in range(2): print("Epoch number: ", epoch) for row in range(len(trn_ivectors)): # 把硬编码的16918换成动态读取的样本数 # 修正输入形状,去掉多余的通道维度,匹配Wav2Vec的输入要求 Input = torch.tensor(trn_ivectors[row]).float().unsqueeze(0).to(device) label = torch.tensor(trn_labels[row]).long().to(device) optimizer.zero_grad() # 必须把输入传给模型,拿到带计算图的输出张量 output = model(Input) logits = output.logits # 直接计算损失,不需要手动写one-hot转换逻辑 loss = criterion(logits, label.unsqueeze(0)) total_loss += loss.item() loss.backward() optimizer.step() # 把之前写错的未定义idx换成遍历用的row if row % print_every == 0 and row > 0: average_loss = total_loss / print_every print("{}/{}. Average loss: {}".format(row, len(trn_ivectors), average_loss)) all_losses.append(average_loss) total_loss = 0 torch.save(model.state_dict(), "model_after_train.pt")
内容的提问来源于stack exchange,提问作者Omar
相关产品推荐
相关产品推荐

