You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyTorch迁移学习遇RuntimeError:张量无梯度属性问题排查

问题描述

在PyTorch中进行ResNet18迁移学习时,将主干网络所有参数的requires_grad设为False,仅启用全连接层的梯度计算,但执行loss.backward()时触发以下错误:

RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn

用户怀疑问题与GPU访问有关,相关代码及报错堆栈如下:

代码片段

import torchvision.models as models
model_ft = models.resnet18(weights='IMAGENET1K_V1')
for param in model_ft.parameters():
    param.requires_grad = False

for param in model_ft.fc.parameters():
    param.requires_grad = True

num_ftrs = model_ft.fc.in_features
fc_layer = nn.Linear(in_features=num_ftrs, out_features=1)
model_ft.fc = fc_layer
    

model_ft = model_ft.to(device)
criterion = nn.MSELoss()
optimizer_ft = optim.Adam(model_ft.parameters(), lr=0.001)
exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)
model_ft = simpleTrainingLoop(model_ft,
                       train_data_loader,
                       criterion,
                       optimizer_ft,
                       exp_lr_scheduler)

def simpleTrainingLoop(model,
                dataloader_train,
                criterion, 
                optimizer, 
                scheduler, 
                num_epoch = 1):
        since = time.time()
        for e in range(num_epoch):
                running_loss = 0
                for inputs, labels in dataloader_train:
                        inputs = inputs.to(device)
                        labels = labels.to(device)
                        
                        optimizer.zero_grad()
                        outputs = model(inputs)
                        _,preds = torch.max(outputs,1)
                        loss = criterion(preds,labels)
                        # if we can know which layer has the problem will be better 
                        loss.backward()
                        optimizer.step()
                        running_loss += loss.item() * inputs.size(0)
                scheduler.step()
                train_loss = running_loss/train_size
                print(f' training loss: {train_loss}')
        time_elapsed = time.time() - since
        print(f'training complete in {time_elapsed // 60:.0f}m {time_elapsed % 60:.0f}s')

报错堆栈

c:\Users\LangZheZR\Desktop\projects\eye_on_ground_challenge\test_CNN_image_tabular_concat.ipynb Cell 11 line 2
     21 optimizer_ft = optim.Adam(model_ft.parameters(), lr=0.001)
     22 exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)
---> 23 model_ft = simpleTrainingLoop(model_ft,
     24                        train_data_loader,
     25                        criterion,
     26                        optimizer_ft,
     27                        exp_lr_scheduler)

c:\Users\LangZheZR\Desktop\projects\eye_on_ground_challenge\test_CNN_image_tabular_concat.ipynb Cell 11 line 2
     18 loss = criterion(preds,labels)
     19 # if we can know which layer has the problem will be better 
---> 20 loss.backward()
     21 optimizer.step()
     22 running_loss += loss.item() * inputs.size(0)

File c:\Python311\Lib\site-packages\torch\_tensor.py:492, in Tensor.backward(self, gradient, retain_graph, create_graph, inputs)
    482 if has_torch_function_unary(self):
    483     return handle_torch_function(
    484         Tensor.backward,
    485         (self,),
   (...)
    490         inputs=inputs,
...
    257     allow_unreachable=True,
    258     accumulate_grad=True,
    259 )

RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn

问题原因

该错误与GPU访问无关,核心问题出在训练循环的逻辑上:

  1. torch.max()的错误使用:torch.max(outputs,1)返回的preds是argmax运算的结果,属于不可逆操作,生成的张量没有梯度信息(无grad_fn)。用这个无梯度张量计算损失后,反向传播时找不到梯度回传的路径,直接触发报错。
  2. 冗余的参数设置:先将原全连接层的requires_grad设为True,之后直接替换了整个全连接层,新层默认requires_grad=True,之前的设置完全多余,但不影响功能。
  3. 优化器参数冗余:传入了所有模型参数,但主干网络参数requires_grad=False会被PyTorch自动忽略,虽不报错但效率较低。

修复方案

1. 核心修复:移除torch.max()的错误使用

直接用模型输出outputs计算损失,不要用argmax后的结果。如果是回归任务(使用MSELoss),需注意维度匹配:

# 替换训练循环中的对应行
outputs = model(inputs)
# 去掉torch.max,直接用outputs计算损失,squeeze消除多余维度
loss = criterion(outputs.squeeze(), labels)

2. 规范模型初始化流程(可选)

先冻结主干网络,再替换全连接层,避免冗余操作:

model_ft = models.resnet18(weights='IMAGENET1K_V1')
# 先冻结所有主干参数
for param in model_ft.parameters():
    param.requires_grad = False
# 替换全连接层,新层默认requires_grad=True
num_ftrs = model_ft.fc.in_features
model_ft.fc = nn.Linear(num_ftrs, 1)

3. 优化器只传入需要更新的参数(可选)

仅将全连接层参数传给优化器,提升训练效率:

optimizer_ft = optim.Adam(model_ft.fc.parameters(), lr=0.001)

修复后的完整代码

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import models
from torch.optim import lr_scheduler
import time

# 初始化模型并修改全连接层
model_ft = models.resnet18(weights='IMAGENET1K_V1')
# 冻结主干网络所有参数
for param in model_ft.parameters():
    param.requires_grad = False

# 替换全连接层(新层默认requires_grad=True)
num_ftrs = model_ft.fc.in_features
model_ft.fc = nn.Linear(num_ftrs, 1)

model_ft = model_ft.to(device)
criterion = nn.MSELoss()
# 仅传入全连接层参数给优化器
optimizer_ft = optim.Adam(model_ft.fc.parameters(), lr=0.001)
exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)

def simpleTrainingLoop(model,
                dataloader_train,
                criterion, 
                optimizer, 
                scheduler, 
                num_epoch = 1):
        since = time.time()
        # 获取训练集总大小
        train_size = len(dataloader_train.dataset)
        for e in range(num_epoch):
                running_loss = 0
                # 显式设置模型为训练模式
                model.train()
                for inputs, labels in dataloader_train:
                        inputs = inputs.to(device)
                        labels = labels.to(device)
                        
                        optimizer.zero_grad()
                        outputs = model(inputs)
                        # 直接用模型输出计算损失,匹配维度
                        loss = criterion(outputs.squeeze(), labels)
                        loss.backward()
                        optimizer.step()
                        running_loss += loss.item() * inputs.size(0)
                scheduler.step()
                train_loss = running_loss/train_size
                print(f'训练损失: {train_loss}')
        time_elapsed = time.time() - since
        print(f'训练完成,耗时 {time_elapsed // 60:.0f}m {time_elapsed % 60:.0f}s')
        return model

model_ft = simpleTrainingLoop(model_ft,
                       train_data_loader,
                       criterion,
                       optimizer_ft,
                       exp_lr_scheduler)

额外说明
  • 如果是分类任务,需将全连接层输出维度改为对应类别数,损失函数换成nn.CrossEntropyLoss(),此时无需对outputs做squeeze,CrossEntropyLoss可直接接受模型输出和标签。
  • 训练时显式设置model.train()更稳妥,确保模型处于训练模式(比如启用Dropout、BatchNorm的训练行为)。

内容的提问来源于stack exchange,提问作者Xiaofan Lu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 03:33:18