
本文详解如何在 PyTorch 中正确加载 Facebook 的 DeiT(如 deit_tiny_patch16_224)预训练模型,精准冻结主干参数并仅解冻/替换分类头,避免 RuntimeError: element 0 of tensors does not require grad 报错。
本文详解如何在 pytorch 中正确加载 facebook 的 deit(如 `deit_tiny_patch16_224`)预训练模型,精准冻结主干参数并仅解冻/替换分类头,避免 `runtimeerror: element 0 of tensors does not require grad` 报错。
在使用 DeiT 等 Vision Transformer 进行迁移学习时,一个常见却易被忽略的陷阱是:模型分类头的属性名并非通用统一。例如,torchvision 官方 ViT 实现中分类层名为 heads,而 Facebook Research 的 deit_tiny_patch16_224(通过 torch.hub 加载)实际使用的是 head(单数),而非 heads(复数)。若错误地修改 pretrained_vit.heads = ...,新层将不会被模型前向传播路径所调用,导致后续反向传播时所有参数 requires_grad=False 且无 grad_fn,从而触发 RuntimeError。
✅ 正确做法如下:
import torch
import torch.nn as nn
import torchvision.transforms as T
# 1. 加载 DeiT Tiny 模型(注意:需确保 torch >= 1.12, torchvision >= 0.13)
model = torch.hub.load('facebookresearch/deit:main', 'deit_tiny_patch16_224', pretrained=True).to(device)
# 2. 全局冻结所有参数(推荐使用 requires_grad_(),更安全、简洁)
model.requires_grad_(False)
# 3. 替换分类头 —— 关键:属性名为 'head',非 'heads'
num_classes = len(class_names) # 你的数据集类别数
model.head = nn.Linear(in_features=192, out_features=num_classes).to(device)
# 4. 仅解冻新分类头(确保其参与梯度更新)
model.head.requires_grad_(True)
# 验证:检查 head 层是否可训练
print(f"Head layer trainable: {model.head.weight.requires_grad}") # 应输出 True
# 5. 构建优化器(仅传入 head 参数,或显式过滤)
optimizer = torch.optim.Adam(model.head.parameters(), lr=1e-3)
# 或更通用写法:
# optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=1e-3)
criterion = nn.CrossEntropyLoss()⚠️ 注意事项与最佳实践:
- 命名一致性检查:始终通过 print(model) 或 list(model.named_children()) 查看模型结构,确认分类层确切名称(DeiT 是 head,ViT-B_16 是 heads,其他实现可能为 classifier 或 fc)。
- 避免手动遍历参数赋值:model.requires_grad_(False) 比循环设置 parameter.requires_grad = False 更鲁棒,它递归作用于所有子模块,且能正确处理嵌套 nn.Sequential 或自定义模块。
-
梯度计算验证:训练前可用小批量数据做一次前向+反向测试:
x = torch.randn(2, 3, 224, 224).to(device) y = model(x) loss = criterion(y, torch.randint(0, num_classes, (2,)).to(device)) loss.backward() # 若此处不报错,说明梯度流正常
-
性能提示:DeiT Tiny 的 head 输入特征维度为 192(对应 embed_dim=192),请勿硬编码——可通过 model.head.in_features 动态获取,提升代码健壮性:
model.head = nn.Linear(model.head.in_features, num_classes).to(device)
总结:迁移学习成功的关键在于「精准定位可训练模块」。对于 DeiT 系列,牢记 head 是唯一需替换与解冻的顶层分类层;善用 requires_grad_() 统一控制参数状态,并通过结构打印与梯度验证双重确认,即可稳定实现高效微调。


















