HuggingFace模型封装调用多encoder时出现cpu/cuda张量设备不匹配错误
问题根因
你的报错核心原因是普通Python列表存储的PyTorch子模块不会被父模型自动识别和迁移设备:
- 单encoder场景下你大概率是直接将encoder作为父模型的类属性(比如
self.encoder = XXXEncoder()),调用model.to('cuda')时PyTorch会自动识别子模块,完成参数迁移 - 多encoder场景下你把所有encoder存到了普通Python列表
self.prompt_encoders中,PyTorch的nn.Module机制不会遍历普通列表中的子元素做设备迁移,因此其余encoder的参数都留在了CPU上,forward调用时就会触发CPU/CUDA设备不匹配的报错 - 你现有代码中调用
encoder(xxx).to(device)是在推理完成后才迁移结果,推理过程中encoder参数和输入张量的设备已经不匹配,因此这行代码的转移动作无法解决报错
可行解决方案
最优方案:将存储容器替换为nn.ModuleList
这是PyTorch官方推荐的多子模块存储方案,修改后父模型调用to()方法时会自动遍历nn.ModuleList中的所有子模块完成设备迁移,操作步骤如下:
- 找到你父模型中初始化
self.prompt_encoders的代码,将普通列表替换为nn.ModuleList:
import torch.nn as nn # 原来的写法(错误) # self.prompt_encoders = [] # 替换为 self.prompt_encoders = nn.ModuleList() # 后续添加encoder的逻辑保持不变,还是用append self.prompt_encoders.append(your_encoder_instance)
- 正常调用父模型的设备迁移方法即可,所有子encoder会自动同步设备:
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") model = YourHuggingFaceWrapperModel().to(device)
临时验证方案(不推荐用于训练,性能损失大)
如果不想修改初始化逻辑,可在forward循环中临时迁移encoder参数到输入所在设备,修改你的报错代码段即可:
for encoder in self.prompt_encoders: wlog.info("********** offset: %s, length: %s", encoder.id_offset, encoder.length) prompt_token_fn = encoder.get_prompt_token_fn() encoder_masks = prompt_token_fn(input_ids) wlog.info("Encoder masks: %s", encoder_masks) if encoder_masks.any(): prompt_input_ids = input_ids[encoder_masks] wlog.info("Prompt Input ids: %s", prompt_input_ids) # 新增:将encoder临时迁移到输入张量所在设备 encoder = encoder.to(input_ids.device) prompt_embeds = encoder(prompt_input_ids, prompt_ids).to(device=inputs_embeds.device)
内容的提问来源于stack exchange,提问作者Ahmad
相关产品推荐
相关产品推荐

