[LAB 14 · 图像问答:视觉条件与问题先验](/course/notebooks/11d9f74871544a77b42024a9fe06b81a.html) # 3. 模型、损失与公平比较范围 · 实现代码 [独立阅读](/course/notebook-blocks/7f07825ae8a84326b6a7a768b26c7caf.html) 按问题继续 RGB+问题输入、question-only对照、同图换问与同问换图 展开完整实现代码 ``` class VQA(nn.Module): def __init__(self,visual): super().__init__();self.visual=visual self.question=nn.Embedding(len(QUESTIONS),12) if visual: self.cnn=nn.Sequential(nn.Conv2d(3,12,3,padding=1),nn.ReLU(),nn.MaxPool2d(2),nn.Conv2d(12,24,3,padding=1),nn.ReLU(),nn.MaxPool2d(2),nn.Flatten(),nn.Linear(24*8*10,64),nn.ReLU()) self.answer=nn.Sequential(nn.Linear(64+12 if visual else 12,64),nn.ReLU(),nn.Linear(64,len(ANSWERS))) def forward(self,pixels,question_id): q=self.question(question_id) features=torch.cat([self.cnn(pixels),q],-1) if self.visual else q return self.answer(features) full=VQA(True);question_only=VQA(False) curve=[] for label,net in [('image+question',full),('question-only',question_only)]: optimizer=torch.optim.Adam(net.parameters(),lr=.002) for epoch in range(28): order=torch.randperm(Ytr.numel());net.train();loss_sum=0. for pair_ids in order.split(72): image_ids=pair_ids//3;qids=pair_ids%3 logits=net(Xtr[image_ids],qids);target=Ytr[image_ids,qids] loss=F.cross_entropy(logits,target) optimizer.zero_grad();loss.backward();optimizer.step();loss_sum+=float(loss.detach())*len(pair_ids) if epoch in [0,5,13,27]:print(label,'epoch',epoch+1,'CE',round(loss_sum/Ytr.numel(),4)) curve.append({'model':label,'epoch':epoch+1,'train_ce':loss_sum/Ytr.numel()}) torch.save(net.state_dict(),OUT/('lab14-'+('visual' if net.visual else 'question-only')+'-weights.pt')) ```