{"version":2,"relationshipURLBase":"https://codingai-lec03.pages.dev/course/notebook-blocks/143820f109ad427a91de6c9db85091b7.html","relationshipResolution":"Resolve relative relationship URLs against relationshipURLBase; complete records and absolute URLs are in blocks.","title":"从共享特征到两个输出头 · 实现代码","url":"https://codingai-lec03.pages.dev/course/notebook-blocks/143820f109ad427a91de6c9db85091b7.html","markdown":"https://codingai-lec03.pages.dev/course/notebook-blocks/143820f109ad427a91de6c9db85091b7.md","pages":[{"id":"reading-opening","title":"从共享特征到两个输出头 · 实现代码","unit":"从共享特征到两个输出头 · 实现代码","unitId":"notebook-blocks/143820f109ad427a91de6c9db85091b7","chapter":"course","number":1}],"aliases":[{"oldId":"b-df7a1cf64eb973cb4ca167ab","targetId":"b-df7a1cf64eb973cb4ca167ab","page":"reading-opening","operation":"move","beforeSHA256":"68ab73f0c1cbd872efb5dd565e57f07f1b4a2b56c5ea0ce2c6ed91ff60ff5a3f","afterSHA256":"68ab73f0c1cbd872efb5dd565e57f07f1b4a2b56c5ea0ce2c6ed91ff60ff5a3f","reason":"Same source body in authored semantic page"},{"oldId":"b-650c442e25baaa684ce29fde","targetId":"b-650c442e25baaa684ce29fde","page":"reading-opening","operation":"move","beforeSHA256":"137a0d10dfe56717d3a1522c441607a56c43245252ab45c379768b582327eaec","afterSHA256":"137a0d10dfe56717d3a1522c441607a56c43245252ab45c379768b582327eaec","reason":"Same source body in authored semantic page"},{"oldId":"b-e101fa697261b08b476c6653","targetId":"b-e101fa697261b08b476c6653","page":"reading-opening","operation":"move","beforeSHA256":"42cba31deccba2a350b9b23bd0ca1dcaa4c4c5cb9d35c773e1fcb89124788ccd","afterSHA256":"42cba31deccba2a350b9b23bd0ca1dcaa4c4c5cb9d35c773e1fcb89124788ccd","reason":"Same source body in authored semantic page"},{"oldId":"b-d4077d578219c8b72ca64703","targetId":"b-d4077d578219c8b72ca64703","page":"reading-opening","operation":"move","beforeSHA256":"b1c765d7bf903321abd36e75bae49048d61faec2aa3fd11922b328c5970003b1","afterSHA256":"b1c765d7bf903321abd36e75bae49048d61faec2aa3fd11922b328c5970003b1","reason":"Same source body in authored semantic page"},{"oldId":"b-f6e5eee50e96698f28512377","targetId":"b-f6e5eee50e96698f28512377","page":"reading-opening","operation":"move","beforeSHA256":"13aa6f19d1012ce26455856541869391d75585395369a37bff83c083c0b497a7","afterSHA256":"13aa6f19d1012ce26455856541869391d75585395369a37bff83c083c0b497a7","reason":"Same source body in authored semantic page"}],"blocks":[{"id":"b-df7a1cf64eb973cb4ca167ab","type":"p","scope":"document","url":"https://codingai-lec03.pages.dev/course/notebook-blocks/143820f109ad427a91de6c9db85091b7.html#b-df7a1cf64eb973cb4ca167ab","content":"[LAB 10 · 从 RGB 图像训练小型检测器](/course/notebooks/a14237bfa9dd413d959ab4e458e11f42.html)","sha256":"68ab73f0c1cbd872efb5dd565e57f07f1b4a2b56c5ea0ce2c6ed91ff60ff5a3f","chapter":{"id":"course","title":"从共享特征到两个输出头 · 实现代码"},"unit":{"id":"notebook-blocks/143820f109ad427a91de6c9db85091b7","title":"从共享特征到两个输出头 · 实现代码"},"page":{"id":"reading-opening","title":"从共享特征到两个输出头 · 实现代码","number":1},"sectionTitle":"从共享特征到两个输出头 · 实现代码","currentQuestion":"从共享特征到两个输出头 · 实现代码","sourceSlides":[],"knowledgeBlock":null,"formulas":[],"previousBlock":null,"nextBlock":"b-650c442e25baaa684ce29fde","prerequisites":[],"relatedBlocks":[]},{"id":"b-650c442e25baaa684ce29fde","type":"h1","scope":"document","url":"https://codingai-lec03.pages.dev/course/notebook-blocks/143820f109ad427a91de6c9db85091b7.html#b-650c442e25baaa684ce29fde","content":"# 从共享特征到两个输出头 · 实现代码","sha256":"137a0d10dfe56717d3a1522c441607a56c43245252ab45c379768b582327eaec","chapter":{"id":"course","title":"从共享特征到两个输出头 · 实现代码"},"unit":{"id":"notebook-blocks/143820f109ad427a91de6c9db85091b7","title":"从共享特征到两个输出头 · 实现代码"},"page":{"id":"reading-opening","title":"从共享特征到两个输出头 · 实现代码","number":1},"sectionTitle":"从共享特征到两个输出头 · 实现代码","currentQuestion":"从共享特征到两个输出头 · 实现代码","sourceSlides":[],"knowledgeBlock":null,"formulas":[],"previousBlock":"b-df7a1cf64eb973cb4ca167ab","nextBlock":"b-e101fa697261b08b476c6653","prerequisites":[{"id":"b-df7a1cf64eb973cb4ca167ab","url":"#b-df7a1cf64eb973cb4ca167ab","role":"problem-context"}],"relatedBlocks":[]},{"id":"b-e101fa697261b08b476c6653","type":"p","scope":"document","url":"https://codingai-lec03.pages.dev/course/notebook-blocks/143820f109ad427a91de6c9db85091b7.html#b-e101fa697261b08b476c6653","content":"主线阅读与优先实验","sha256":"42cba31deccba2a350b9b23bd0ca1dcaa4c4c5cb9d35c773e1fcb89124788ccd","chapter":{"id":"course","title":"从共享特征到两个输出头 · 实现代码"},"unit":{"id":"notebook-blocks/143820f109ad427a91de6c9db85091b7","title":"从共享特征到两个输出头 · 实现代码"},"page":{"id":"reading-opening","title":"从共享特征到两个输出头 · 实现代码","number":1},"sectionTitle":"从共享特征到两个输出头 · 实现代码","currentQuestion":"从共享特征到两个输出头 · 实现代码","sourceSlides":[],"knowledgeBlock":null,"formulas":[],"previousBlock":"b-650c442e25baaa684ce29fde","nextBlock":"b-d4077d578219c8b72ca64703","prerequisites":[{"id":"b-650c442e25baaa684ce29fde","url":"#b-650c442e25baaa684ce29fde","role":"section-title"},{"id":"b-df7a1cf64eb973cb4ca167ab","url":"#b-df7a1cf64eb973cb4ca167ab","role":"problem-context"}],"relatedBlocks":[]},{"id":"b-d4077d578219c8b72ca64703","type":"p","scope":"document","url":"https://codingai-lec03.pages.dev/course/notebook-blocks/143820f109ad427a91de6c9db85091b7.html#b-d4077d578219c8b72ca64703","content":"RGB图像、单特征层锚框检测器、分类/位置损失、NMS、独立检测评价","sha256":"b1c765d7bf903321abd36e75bae49048d61faec2aa3fd11922b328c5970003b1","chapter":{"id":"course","title":"从共享特征到两个输出头 · 实现代码"},"unit":{"id":"notebook-blocks/143820f109ad427a91de6c9db85091b7","title":"从共享特征到两个输出头 · 实现代码"},"page":{"id":"reading-opening","title":"从共享特征到两个输出头 · 实现代码","number":1},"sectionTitle":"从共享特征到两个输出头 · 实现代码","currentQuestion":"从共享特征到两个输出头 · 实现代码","sourceSlides":[],"knowledgeBlock":null,"formulas":[],"previousBlock":"b-e101fa697261b08b476c6653","nextBlock":"b-f6e5eee50e96698f28512377","prerequisites":[{"id":"b-650c442e25baaa684ce29fde","url":"#b-650c442e25baaa684ce29fde","role":"section-title"},{"id":"b-df7a1cf64eb973cb4ca167ab","url":"#b-df7a1cf64eb973cb4ca167ab","role":"problem-context"}],"relatedBlocks":[]},{"id":"b-f6e5eee50e96698f28512377","type":"pre","scope":"document","url":"https://codingai-lec03.pages.dev/course/notebook-blocks/143820f109ad427a91de6c9db85091b7.html#b-f6e5eee50e96698f28512377","content":"```\n%%writefile -a lab10_detector.py\ndef report_predictions(pred,targets):\n    empties=[i for i,t in enumerate(targets) if not len(t['boxes'])]\n    multi=[i for i,t in enumerate(targets) if len(t['boxes'])>=2]\n    return {'AP50':average_precision(pred,targets,.5),'AP75':average_precision(pred,targets,.75),\n            'operating_point':average_precision(pred,targets,.5,.4),\n            'empty_images':len(empties),'empty_false_positive_images_score04':sum(int((pred[i]['scores']>=.4).any()) for i in empties),\n            'multi_object_images':len(multi),'multi_object_AP50':average_precision([pred[i] for i in multi],[targets[i] for i in multi],.5)}\n\ndef draw_predictions(images,targets,predictions,path,title,indices=None):\n    if indices is None:indices=[0,1,2,3,4,5]\n    fig,axes=plt.subplots(2,3,figsize=(11,7))\n    for ax,i in zip(axes.flat,indices):\n        ax.imshow(images[i].permute(1,2,0));ax.axis('off')\n        for b,c in zip(targets[i]['boxes'],targets[i]['labels']):\n            x,y,x2,y2=b.numpy()*48;ax.add_patch(Rectangle((x,y),x2-x,y2-y,fill=False,color='lime',lw=1.8))\n        count=0\n        for b,s,c in zip(predictions[i]['boxes'],predictions[i]['scores'],predictions[i]['labels']):\n            if s<.4:continue\n            x,y,x2,y2=b.numpy()*48;ax.add_patch(Rectangle((x,y),x2-x,y2-y,fill=False,color='yellow',ls='--',lw=1.1))\n            ax.text(x,y,f'{int(c)}:{s:.2f}',color='yellow',fontsize=7);count+=1\n        ax.set_title(f\"sample {i}: GT={len(targets[i]['boxes'])}, pred={count}\")\n    fig.suptitle(title+' | green=GT; yellow=prediction score>=0.4');fig.tight_layout();fig.savefig(path,dpi=150);plt.close(fig)\n\ndef run(out='lab10-results',epochs=28):\n    started=datetime.now(timezone.utc).isoformat();start=time.time();out=Path(out);out.mkdir(parents=True,exist_ok=True);seed_all(7)\n    train,ttrain,_=make_scenes(768,1101); val,tval,_=make_scenes(96,3101);test,ttest,_=make_scenes(192,5901)\n    small,tsmall,_=make_scenes(192,5901,scale=.5);anchors=anchors_grid()\n    matched=[match_targets(anchors,t) for t in ttrain];classes=torch.stack([m[0] for m in matched]);targets=torch.stack([m[1] for m in matched])\n    model=TinyAnchorDetector();random_pred=predict(model,test,anchors,score=.01)\n    baseline=report_predictions(random_pred,ttest);opt=torch.optim.Adam(model.parameters(),lr=.002);history=[]\n    for epoch in range(epochs):\n        model.train(); losses=[]; order=torch.randperm(len(train))\n        for ids in order.split(32):\n            logits,offset=model(train[ids]);loss,cls,loc=detection_loss(logits,offset,classes[ids],targets[ids])\n            opt.zero_grad();loss.backward();opt.step();losses.append([float(loss.detach()),float(cls.detach()),float(loc.detach())])\n        item={'epoch':epoch+1,'loss':float(np.mean(losses,axis=0)[0]),'classification_loss':float(np.mean(losses,axis=0)[1]),'box_loss':float(np.mean(losses,axis=0)[2])};history.append(item)\n        if (epoch+1)%7==0 or epoch==0:print(item,flush=True)\n    pred=predict(model,test,anchors);small_pred=predict(model,small,anchors);val_pred=predict(model,val,anchors)\n    loose_pred=predict(model,test,anchors,nms_iou=.65)\n    results={'provenance':provenance(__file__,started),'config':{'seed':7,'train_count':768,'validation_count':96,'heldout_count':192,\n             'split_seeds':[1101,3101,5901],'image_shape':[3,48,48],'num_anchors':len(anchors),'epochs':epochs,'batch_size':32,\n             'optimizer':'Adam lr=.002','matching':'positive IoU>=.45 plus distinct forced GT anchors; ignore [.25,.45); background<.25',\n             'loss':'hard-negative CE (3:1,min12) + 3*positive smoothL1','box_parameterization':'dx/dw,dy/dh,log width ratio,log height ratio',\n             'AP':'per-class global score sort; maximum-IoU GT, one match per GT; precision-envelope all-points integral',\n             'AP_candidate_score_floor':.01,'random_baseline_candidate_score_floor':.01,'NMS_IoU':.35,'display_score':.4,\n             'model_parameters':sum(p.numel() for p in model.parameters()),'data_identity':'generated images; color/shape correlated; distinct original sample seed splits; no external data'},\n             'target_counts':{'positive':int((classes>0).sum()),'ignore':int((classes==-1).sum()),'background':int((classes==0).sum())},\n             'random_initialization':baseline,'validation':report_predictions(val_pred,tval),'heldout':report_predictions(pred,ttest),\n             'condition_half_object_scale':report_predictions(small_pred,tsmall),'condition_NMS_065':report_predictions(loose_pred,ttest),\n             'training_history':history,'seconds':time.time()-start}\n    # Select actual worst examples by mean best GT overlap, plus an empty image.\n    failures=[]\n    for i,t in enumerate(ttest):\n        p=pred[i];b=p['boxes'][p['scores']>=.4]\n        quality=float(iou_matrix(t['boxes'],b).max(1).values.mean()) if len(t['boxes']) and len(b) else 0\n        if len(t['boxes']):failures.append((quality,i))\n    failures.sort();indices=[i for _,i in failures[:5]]+[next(i for i,t in enumerate(ttest) if not len(t['boxes']))]\n    results['failure_examples']=[{'sample_index':i,'mean_best_box_IoU_unconditioned_on_class':q} for q,i in failures[:5]]\n    torch.save({'state_dict':model.state_dict(),'anchors':anchors,'config':results['config']},out/'detector.pt')\n    np.savez_compressed(out/'heldout-inputs-and-targets.npz',images=test.numpy(),half_scale_images=small.numpy(),\n                        target_boxes=np.array([t['boxes'].numpy() for t in ttest],dtype=object),target_labels=np.array([t['labels'].numpy() for t in ttest],dtype=object))\n    serialize=lambda ps:[{k:v.tolist() for k,v in p.items()} for p in ps]\n    save_json(out/'predictions.json',{'heldout':serialize(pred),'half_scale':serialize(small_pred),'targets':serialize([{k:v for k,v in t.items() if k!='sample_id'} for t in ttest]),'half_scale_targets':serialize([{k:v for k,v in t.items() if k!='sample_id'} for t in tsmall])})\n    draw_predictions(test,ttest,pred,out/'heldout-predictions.png','Heldout image-to-box predictions')\n    draw_predictions(test,ttest,pred,out/'failure-predictions.png','Lowest geometric overlap examples',indices)\n    draw_predictions(small,tsmall,small_pred,out/'half-scale-predictions.png','Same original scenes, object size x0.5')\n    fig,ax=plt.subplots(figsize=(7,4));ax.plot([h['loss'] for h in history],label='total');ax.plot([h['classification_loss'] for h in history],label='classification');ax.plot([3*h['box_loss'] for h in history],label='3 x box');ax.set(xlabel='epoch (zero based)',ylabel='training loss');ax.legend();fig.tight_layout();fig.savefig(out/'training-curve.png',dpi=150);plt.close(fig)\n    save_json(out/'run.json',results)\n    print(json.dumps({'heldout_mAP50':results['heldout']['AP50']['mAP'],'half_scale_mAP50':results['condition_half_object_scale']['AP50']['mAP'],'seconds':results['seconds']},indent=2),flush=True)\n    return results\n\nif __name__=='__main__':\n    p=argparse.ArgumentParser();p.add_argument('--out',default='lab10-results');p.add_argument('--epochs',type=int,default=28);args=p.parse_args();run(args.out,args.epochs)\n\n```","sha256":"13aa6f19d1012ce26455856541869391d75585395369a37bff83c083c0b497a7","chapter":{"id":"course","title":"从共享特征到两个输出头 · 实现代码"},"unit":{"id":"notebook-blocks/143820f109ad427a91de6c9db85091b7","title":"从共享特征到两个输出头 · 实现代码"},"page":{"id":"reading-opening","title":"从共享特征到两个输出头 · 实现代码","number":1},"sectionTitle":"从共享特征到两个输出头 · 实现代码","currentQuestion":"从共享特征到两个输出头 · 实现代码","sourceSlides":[],"knowledgeBlock":null,"formulas":[],"previousBlock":"b-d4077d578219c8b72ca64703","nextBlock":null,"prerequisites":[{"id":"b-650c442e25baaa684ce29fde","url":"#b-650c442e25baaa684ce29fde","role":"section-title"},{"id":"b-df7a1cf64eb973cb4ca167ab","url":"#b-df7a1cf64eb973cb4ca167ab","role":"problem-context"}],"relatedBlocks":[]}]}
