使用PyTorch微调VGG16做图像分类时反向传播报错的解决方法
解决PyTorch预训练VGG16训练时的RuntimeError问题
问题场景
使用PyTorch做图像分类,训练简单线性模型的函数可正常运行,但加载预训练VGG16并修改最后一层、冻结其余参数后,训练抛出错误:
RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn
用户的VGG16修改代码:
model_vgg = models.vgg16(weights='DEFAULT') model_vgg.classifier[6] = nn.Linear(4096, 2) for param in model_vgg.parameters(): param.requires_grad = False model_vgg.classifier[-1].requires_grad = True train(model_vgg, train_loader, 2)
原训练函数核心逻辑:
criterion = nn.CrossEntropyLoss() def train(model, dataloader, epoch): model.to(device) optimizer = torch.optim.Adam(model.parameters(), lr=1e-3) running_loss, running_acc = 0., 0. for i in range(1, epoch + 1): model.train() for inputs, targets in dataloader: inputs, targets = inputs.to(device), targets.to(device) outputs = model(inputs) loss = criterion(outputs, targets) optimizer.zero_grad() loss.backward() optimizer.step() preds = torch.argmax(outputs, 1) running_loss += loss.item() running_acc += torch.sum(preds == targets).item() # 原print语句存在语法错误,需修正 print(f"[TRAIN epoch {i}] Loss: {running_loss/len(data_train):.2f} Acc: {100 * running_acc/len(data_train):.2f}%")
问题原因
训练函数里初始化优化器时,传入了model.parameters(),但此时模型绝大多数参数的requires_grad被设为False,导致优化器中没有可训练的参数。反向传播时,损失无法找到需要更新的梯度节点,从而触发错误。
解决方案
1. 仅传入可训练参数给优化器
修改训练函数中的优化器初始化代码,只传入最后一层(model.classifier[-1])的参数:
# 替换原optimizer = torch.optim.Adam(model.parameters(), lr=1e-3) optimizer = torch.optim.Adam(model.classifier[-1].parameters(), lr=1e-3)
这样优化器只会跟踪需要训练的最后一层参数,反向传播时就能正常计算梯度。
2. 验证参数可训练状态(可选)
用以下代码确认参数的requires_grad状态,确保只有最后一层参数可训练:
for name, param in model_vgg.named_parameters(): print(f"{name}: {param.requires_grad}")
输出中应仅classifier.6.weight和classifier.6.bias的requires_grad为True。
3. 修正训练函数语法错误
原训练函数存在两处语法问题:
accuracy_history = [](data_train):.2f}%")是无效代码,需删除或正确初始化print语句未闭合,需补充完整格式化字符串
修正后的训练函数片段:
def train(model, dataloader, epoch, data_train): model.to(device) optimizer = torch.optim.Adam(model.classifier[-1].parameters(), lr=1e-3) running_loss, running_acc = 0., 0. loss_history = [] accuracy_history = [] for i in range(1, epoch + 1): model.train() running_loss = 0. # 每个epoch重置损失和准确率 running_acc = 0. for inputs, targets in dataloader: inputs, targets = inputs.to(device), targets.to(device) outputs = model(inputs) loss = criterion(outputs, targets) optimizer.zero_grad() loss.backward() optimizer.step() preds = torch.argmax(outputs, 1) running_loss += loss.item() * inputs.size(0) # 按样本数加权 running_acc += torch.sum(preds == targets).item() epoch_loss = running_loss / len(data_train) epoch_acc = 100 * running_acc / len(data_train) loss_history.append(epoch_loss) accuracy_history.append(epoch_acc) print(f"[TRAIN epoch {i}] Loss: {epoch_loss:.2f} Acc: {epoch_acc:.2f}%")
内容的提问来源于stack exchange,提问作者ChairmanShinTan
相关产品推荐
相关产品推荐

