{"version":2,"relationshipURLBase":"https://codingai-lec03.pages.dev/course/notebook-blocks/b7b5c924dcd44b9e9286614edc3fa329.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/b7b5c924dcd44b9e9286614edc3fa329.html","markdown":"https://codingai-lec03.pages.dev/course/notebook-blocks/b7b5c924dcd44b9e9286614edc3fa329.md","pages":[{"id":"reading-opening","title":"读源码时跟踪三种集合 · 实现代码","unit":"读源码时跟踪三种集合 · 实现代码","unitId":"notebook-blocks/b7b5c924dcd44b9e9286614edc3fa329","chapter":"course","number":1}],"aliases":[{"oldId":"b-6fc436ee2f255f0dbdfb7f50","targetId":"b-6fc436ee2f255f0dbdfb7f50","page":"reading-opening","operation":"move","beforeSHA256":"68ab73f0c1cbd872efb5dd565e57f07f1b4a2b56c5ea0ce2c6ed91ff60ff5a3f","afterSHA256":"68ab73f0c1cbd872efb5dd565e57f07f1b4a2b56c5ea0ce2c6ed91ff60ff5a3f","reason":"Same source body in authored semantic page"},{"oldId":"b-ec25e4b3e4190b49588951fb","targetId":"b-ec25e4b3e4190b49588951fb","page":"reading-opening","operation":"move","beforeSHA256":"cfa8f9c2dbf41e39bf22b62709f40f428ea128e5d31352ff6596a04810c64e60","afterSHA256":"cfa8f9c2dbf41e39bf22b62709f40f428ea128e5d31352ff6596a04810c64e60","reason":"Same source body in authored semantic page"},{"oldId":"b-c84f28db168ca9a4cc32931c","targetId":"b-c84f28db168ca9a4cc32931c","page":"reading-opening","operation":"move","beforeSHA256":"42cba31deccba2a350b9b23bd0ca1dcaa4c4c5cb9d35c773e1fcb89124788ccd","afterSHA256":"42cba31deccba2a350b9b23bd0ca1dcaa4c4c5cb9d35c773e1fcb89124788ccd","reason":"Same source body in authored semantic page"},{"oldId":"b-5181d936e8bc62fed21018b9","targetId":"b-5181d936e8bc62fed21018b9","page":"reading-opening","operation":"move","beforeSHA256":"b1c765d7bf903321abd36e75bae49048d61faec2aa3fd11922b328c5970003b1","afterSHA256":"b1c765d7bf903321abd36e75bae49048d61faec2aa3fd11922b328c5970003b1","reason":"Same source body in authored semantic page"},{"oldId":"b-5eb96d9a7d01663e8ff00998","targetId":"b-5eb96d9a7d01663e8ff00998","page":"reading-opening","operation":"move","beforeSHA256":"037694098c36fa9e1f66ed918865df1f003d2a1bc7c2e6e11e61f3d1326a9c8c","afterSHA256":"037694098c36fa9e1f66ed918865df1f003d2a1bc7c2e6e11e61f3d1326a9c8c","reason":"Same source body in authored semantic page"}],"blocks":[{"id":"b-6fc436ee2f255f0dbdfb7f50","type":"p","scope":"document","url":"https://codingai-lec03.pages.dev/course/notebook-blocks/b7b5c924dcd44b9e9286614edc3fa329.html#b-6fc436ee2f255f0dbdfb7f50","content":"[LAB 10 · 从 RGB 图像训练小型检测器](/course/notebooks/a14237bfa9dd413d959ab4e458e11f42.html)","sha256":"68ab73f0c1cbd872efb5dd565e57f07f1b4a2b56c5ea0ce2c6ed91ff60ff5a3f","chapter":{"id":"course","title":"读源码时跟踪三种集合 · 实现代码"},"unit":{"id":"notebook-blocks/b7b5c924dcd44b9e9286614edc3fa329","title":"读源码时跟踪三种集合 · 实现代码"},"page":{"id":"reading-opening","title":"读源码时跟踪三种集合 · 实现代码","number":1},"sectionTitle":"读源码时跟踪三种集合 · 实现代码","currentQuestion":"读源码时跟踪三种集合 · 实现代码","sourceSlides":[],"knowledgeBlock":null,"formulas":[],"previousBlock":null,"nextBlock":"b-ec25e4b3e4190b49588951fb","prerequisites":[],"relatedBlocks":[]},{"id":"b-ec25e4b3e4190b49588951fb","type":"h1","scope":"document","url":"https://codingai-lec03.pages.dev/course/notebook-blocks/b7b5c924dcd44b9e9286614edc3fa329.html#b-ec25e4b3e4190b49588951fb","content":"# 读源码时跟踪三种集合 · 实现代码","sha256":"cfa8f9c2dbf41e39bf22b62709f40f428ea128e5d31352ff6596a04810c64e60","chapter":{"id":"course","title":"读源码时跟踪三种集合 · 实现代码"},"unit":{"id":"notebook-blocks/b7b5c924dcd44b9e9286614edc3fa329","title":"读源码时跟踪三种集合 · 实现代码"},"page":{"id":"reading-opening","title":"读源码时跟踪三种集合 · 实现代码","number":1},"sectionTitle":"读源码时跟踪三种集合 · 实现代码","currentQuestion":"读源码时跟踪三种集合 · 实现代码","sourceSlides":[],"knowledgeBlock":null,"formulas":[],"previousBlock":"b-6fc436ee2f255f0dbdfb7f50","nextBlock":"b-c84f28db168ca9a4cc32931c","prerequisites":[{"id":"b-6fc436ee2f255f0dbdfb7f50","url":"#b-6fc436ee2f255f0dbdfb7f50","role":"problem-context"}],"relatedBlocks":[]},{"id":"b-c84f28db168ca9a4cc32931c","type":"p","scope":"document","url":"https://codingai-lec03.pages.dev/course/notebook-blocks/b7b5c924dcd44b9e9286614edc3fa329.html#b-c84f28db168ca9a4cc32931c","content":"主线阅读与优先实验","sha256":"42cba31deccba2a350b9b23bd0ca1dcaa4c4c5cb9d35c773e1fcb89124788ccd","chapter":{"id":"course","title":"读源码时跟踪三种集合 · 实现代码"},"unit":{"id":"notebook-blocks/b7b5c924dcd44b9e9286614edc3fa329","title":"读源码时跟踪三种集合 · 实现代码"},"page":{"id":"reading-opening","title":"读源码时跟踪三种集合 · 实现代码","number":1},"sectionTitle":"读源码时跟踪三种集合 · 实现代码","currentQuestion":"读源码时跟踪三种集合 · 实现代码","sourceSlides":[],"knowledgeBlock":null,"formulas":[],"previousBlock":"b-ec25e4b3e4190b49588951fb","nextBlock":"b-5181d936e8bc62fed21018b9","prerequisites":[{"id":"b-ec25e4b3e4190b49588951fb","url":"#b-ec25e4b3e4190b49588951fb","role":"section-title"},{"id":"b-6fc436ee2f255f0dbdfb7f50","url":"#b-6fc436ee2f255f0dbdfb7f50","role":"problem-context"}],"relatedBlocks":[]},{"id":"b-5181d936e8bc62fed21018b9","type":"p","scope":"document","url":"https://codingai-lec03.pages.dev/course/notebook-blocks/b7b5c924dcd44b9e9286614edc3fa329.html#b-5181d936e8bc62fed21018b9","content":"RGB图像、单特征层锚框检测器、分类/位置损失、NMS、独立检测评价","sha256":"b1c765d7bf903321abd36e75bae49048d61faec2aa3fd11922b328c5970003b1","chapter":{"id":"course","title":"读源码时跟踪三种集合 · 实现代码"},"unit":{"id":"notebook-blocks/b7b5c924dcd44b9e9286614edc3fa329","title":"读源码时跟踪三种集合 · 实现代码"},"page":{"id":"reading-opening","title":"读源码时跟踪三种集合 · 实现代码","number":1},"sectionTitle":"读源码时跟踪三种集合 · 实现代码","currentQuestion":"读源码时跟踪三种集合 · 实现代码","sourceSlides":[],"knowledgeBlock":null,"formulas":[],"previousBlock":"b-c84f28db168ca9a4cc32931c","nextBlock":"b-5eb96d9a7d01663e8ff00998","prerequisites":[{"id":"b-ec25e4b3e4190b49588951fb","url":"#b-ec25e4b3e4190b49588951fb","role":"section-title"},{"id":"b-6fc436ee2f255f0dbdfb7f50","url":"#b-6fc436ee2f255f0dbdfb7f50","role":"problem-context"}],"relatedBlocks":[]},{"id":"b-5eb96d9a7d01663e8ff00998","type":"pre","scope":"document","url":"https://codingai-lec03.pages.dev/course/notebook-blocks/b7b5c924dcd44b9e9286614edc3fa329.html#b-5eb96d9a7d01663e8ff00998","content":"```\n%%writefile lab10_detector.py\n\"\"\"LAB10: train and evaluate an image-to-box anchor detector, CPU, no downloads.\nRun: python lab10_detector.py --out lab10-results --epochs 28\nDependencies: torch numpy matplotlib. This script is self-contained.\n\"\"\"\n\"\"\"Shared transparent geometry and image construction for LAB10/11; no downloads.\"\"\"\nimport json, hashlib, platform, sys, time\nfrom pathlib import Path\nfrom datetime import datetime, timezone\nimport numpy as np\nimport torch\nfrom torch import nn\nfrom torch.nn import functional as F\nimport matplotlib\nmatplotlib.use('Agg')\nimport matplotlib.pyplot as plt\nfrom matplotlib.patches import Rectangle\n\ndef seed_all(seed=7):\n    np.random.seed(seed); torch.manual_seed(seed); torch.set_num_threads(2)\n\ndef iou_matrix(a,b):\n    if len(a)==0 or len(b)==0: return torch.zeros((len(a),len(b)))\n    lo=torch.maximum(a[:,None,:2],b[None,:,:2]); hi=torch.minimum(a[:,None,2:],b[None,:,2:])\n    inter=(hi-lo).clamp(min=0).prod(-1)\n    area_a=(a[:,2:]-a[:,:2]).clamp(min=0).prod(-1)\n    area_b=(b[:,2:]-b[:,:2]).clamp(min=0).prod(-1)\n    return inter/(area_a[:,None]+area_b[None,:]-inter).clamp(min=1e-8)\n\ndef make_scenes(n,seed,size=48,scale=1.0):\n    \"\"\"Independent seed defines original sample, scale changes only object size.\n    Boxes xyxy normalized; red rectangle=1, blue ellipse=2; zero to three objects.\n    Images are rasterized from latent scene, labels are never a network input.\n    \"\"\"\n    rng=np.random.default_rng(seed); xs=[]; ys=[]; masks=[]\n    yy,xx=np.mgrid[:size,:size]\n    for index in range(n):\n        image=rng.uniform(.035,.095,(3,size,size)).astype('float32')\n        num=int(rng.choice([0,1,2,3],p=[.18,.30,.36,.16]))\n        boxes=[]; labels=[]; mask=np.zeros((size,size),np.int64)\n        original=[]\n        for obj in range(num):\n            for attempt in range(60):\n                w,h=rng.integers(9,19,size=2); x=rng.integers(2,size-w-2); y=rng.integers(2,size-h-2)\n                box=np.array([x,y,x+w,y+h],np.float32)/size\n                if not original or iou_matrix(torch.tensor(np.array(original)),torch.tensor(box[None])).max()<.08: break\n            original.append(box); label=int(rng.integers(1,3)); color=rng.uniform(.7,.98)\n            cx,cy=(box[0]+box[2])/2,(box[1]+box[3])/2\n            sw,sh=(box[2]-box[0])*scale,(box[3]-box[1])*scale\n            x1,y1,x2,y2=np.round(np.array([cx-sw/2,cy-sh/2,cx+sw/2,cy+sh/2])*size).astype(int)\n            x2=max(x2,x1+2); y2=max(y2,y1+2)\n            region=(xx>=x1)&(xx<x2)&(yy>=y1)&(yy<y2)\n            if label==2: region &= ((xx+.5-(x1+x2)/2)/((x2-x1)/2))**2+((yy+.5-(y1+y2)/2)/((y2-y1)/2))**2<=1\n            image[:,region]*=.2\n            image[0 if label==1 else 2,region]=color\n            image[1,region]=.15+color*.12\n            mask[region]=label\n            boxes.append([x1/size,y1/size,x2/size,y2/size]); labels.append(label)\n        xs.append(image); masks.append(mask)\n        ys.append({'boxes':torch.tensor(boxes,dtype=torch.float32).reshape(-1,4),'labels':torch.tensor(labels,dtype=torch.long),'sample_id':f'{seed}:{index}'})\n    return torch.from_numpy(np.stack(xs)),ys,torch.from_numpy(np.stack(masks))\n\ndef nms(boxes,scores,threshold=.35):\n    order=torch.argsort(scores,descending=True,stable=True); keep=[]\n    while len(order):\n        i=order[0]; keep.append(i.item()); order=order[1:]\n        if len(order): order=order[iou_matrix(boxes[i:i+1],boxes[order])[0]<=threshold]\n    return torch.tensor(keep,dtype=torch.long)\n\ndef average_precision(predictions,targets,iou_threshold=.5,score_threshold=0):\n    \"\"\"Per-class global score order, max-IoU GT then unused check; all-points AP.\"\"\"\n    result={}; all_tp=all_fp=all_gt=0\n    for cls in [1,2]:\n        gt={i:t['boxes'][t['labels']==cls] for i,t in enumerate(targets)}\n        used={i:set() for i in gt}; total=sum(len(v) for v in gt.values())\n        rows=[]\n        for i,p in enumerate(predictions):\n            for j in range(len(p['scores'])):\n                if int(p['labels'][j])==cls and float(p['scores'][j])>=score_threshold:\n                    rows.append((float(p['scores'][j]),i,j,p['boxes'][j]))\n        rows.sort(key=lambda r:(-r[0],r[1],r[2])); tp=[]; fp=[]\n        for score,i,j,b in rows:\n            ok=False\n            if len(gt[i]):\n                overlaps=iou_matrix(b[None],gt[i])[0]; best=int(overlaps.argmax())\n                ok=float(overlaps[best])>=iou_threshold and best not in used[i]\n                if ok:used[i].add(best)\n            tp.append(int(ok));fp.append(int(not ok))\n        tp=np.cumsum(tp);fp=np.cumsum(fp)\n        recall=tp/max(total,1); precision=tp/np.maximum(tp+fp,1)\n        mr=np.r_[0,recall,1];mp=np.r_[0,precision,0]\n        mp=np.maximum.accumulate(mp[::-1])[::-1];loc=np.where(mr[1:]!=mr[:-1])[0]\n        ap=float(((mr[loc+1]-mr[loc])*mp[loc+1]).sum()) if total else None\n        result[str(cls)]={'AP':ap,'GT':total,'TP':int(tp[-1]) if len(tp) else 0,'FP':int(fp[-1]) if len(fp) else 0}\n        all_tp+=result[str(cls)]['TP'];all_fp+=result[str(cls)]['FP'];all_gt+=total\n    return {'per_class':result,'mAP':float(np.mean([r['AP'] for r in result.values() if r['AP'] is not None])),\n            'precision':all_tp/max(all_tp+all_fp,1),'recall':all_tp/max(all_gt,1),'iou_threshold':iou_threshold,'score_threshold':score_threshold}\n\ndef provenance(path,started):\n    p=Path(path)\n    return {'utc_started':started,'utc_completed':datetime.now(timezone.utc).isoformat(),'execution':'CPU, complete script',\n            'python':sys.version,'torch':torch.__version__,'numpy':np.__version__,'platform':platform.platform(),'threads':torch.get_num_threads(),\n            'code_sha256':hashlib.sha256(p.read_bytes()).hexdigest(),\n            'device':'CPU', 'source_file':p.name}\n\ndef save_json(path,data):Path(path).write_text(json.dumps(data,indent=2,ensure_ascii=False))\n\ndef segmentation_metrics(pred,target):\n    valid=target!=255; t=target[valid];p=pred[valid]\n    cm=torch.bincount(t*3+p,minlength=9).reshape(3,3).double()\n    intersection=cm.diag();union=cm.sum(0)+cm.sum(1)-intersection\n    iou=intersection/union.clamp(min=1)\n    return {'IoU_background':float(iou[0]),'IoU_red_rectangle':float(iou[1]),'IoU_blue_ellipse':float(iou[2]),\n            'mIoU_3_classes':float(iou[union>0].mean()),'pixel_accuracy':float(intersection.sum()/cm.sum()),\n            'valid_pixels':int(valid.sum()),'ignored_pixels':int((~valid).sum()),'confusion_matrix_true_rows':cm.int().tolist()}\n\nimport argparse\n\n\n```","sha256":"037694098c36fa9e1f66ed918865df1f003d2a1bc7c2e6e11e61f3d1326a9c8c","chapter":{"id":"course","title":"读源码时跟踪三种集合 · 实现代码"},"unit":{"id":"notebook-blocks/b7b5c924dcd44b9e9286614edc3fa329","title":"读源码时跟踪三种集合 · 实现代码"},"page":{"id":"reading-opening","title":"读源码时跟踪三种集合 · 实现代码","number":1},"sectionTitle":"读源码时跟踪三种集合 · 实现代码","currentQuestion":"读源码时跟踪三种集合 · 实现代码","sourceSlides":[],"knowledgeBlock":null,"formulas":[],"previousBlock":"b-5181d936e8bc62fed21018b9","nextBlock":null,"prerequisites":[{"id":"b-ec25e4b3e4190b49588951fb","url":"#b-ec25e4b3e4190b49588951fb","role":"section-title"},{"id":"b-6fc436ee2f255f0dbdfb7f50","url":"#b-6fc436ee2f255f0dbdfb7f50","role":"problem-context"}],"relatedBlocks":[]}]}
