檢測(cè):從切圖到結(jié)果合并的完整實(shí)踐指南)
做目標(biāo)檢測(cè)的人遲早會(huì)遇到這么一類(lèi)項(xiàng)目圖特別大目標(biāo)特別小。你拿著一張四千乘三千的航拍圖里面要找的是十幾個(gè)像素的車(chē)輛或屋頂直接丟進(jìn)檢測(cè)器要么顯存不夠跑不起來(lái)要么跑起來(lái)了但漏檢漏到你懷疑人生。這個(gè)時(shí)候大多數(shù)人第一反應(yīng)都一樣把圖拆開(kāi)拆成四份、九份甚至更多每塊單獨(dú)訓(xùn)練、單獨(dú)檢測(cè)最后再把檢測(cè)結(jié)果拼回原圖坐標(biāo)。這個(gè)思路看起來(lái)很樸素但真正落地的時(shí)候問(wèn)題比想象中多得多子圖切多大合適要不要重疊標(biāo)注文件怎么跟著轉(zhuǎn)換檢測(cè)結(jié)果怎么映射回大圖兩個(gè)子圖同時(shí)檢測(cè)到同一個(gè)目標(biāo)時(shí)要不要合并這些細(xì)節(jié)沒(méi)有處理好切塊不僅不會(huì)帶來(lái)提升反而會(huì)讓檢測(cè)結(jié)果亂成一團(tuán)。這篇內(nèi)容我就把“大圖拆塊 小目標(biāo)檢測(cè)”這條路線(xiàn)從頭到尾講清楚包括方案設(shè)計(jì)、切圖腳本、標(biāo)注轉(zhuǎn)換、推理合并、常見(jiàn)坑位以及什么時(shí)候該換用更進(jìn)階的做法。這些都是我實(shí)際跑過(guò)項(xiàng)目之后總結(jié)出來(lái)的不繞彎子直接說(shuō)重點(diǎn)。1. 先弄明白為什么大圖里的小目標(biāo)總是漏檢1.1 小目標(biāo)到底有多“小”“小目標(biāo)”在學(xué)術(shù)圈有個(gè)常用參考口徑COCO數(shù)據(jù)集里把像素面積小于32×32的目標(biāo)歸為小目標(biāo)。但實(shí)際項(xiàng)目里這個(gè)定義遠(yuǎn)遠(yuǎn)不夠用。大圖場(chǎng)景下的目標(biāo)往往不是32×32而是更極端的情況一張5000×4000的遙感影像目標(biāo)可能只有20×20像素甚至更小。按照“面積占整張圖比例”來(lái)算這類(lèi)目標(biāo)大概只占全圖面積的十萬(wàn)分之一。落到檢測(cè)任務(wù)里小目標(biāo)的核心難點(diǎn)有三層第一是像素信息少能用來(lái)區(qū)分的特征本身就非常有限第二是經(jīng)過(guò)網(wǎng)絡(luò)下采樣之后特征進(jìn)一步丟失到了高層特征圖上可能只剩一兩個(gè)像素點(diǎn)第三是正負(fù)樣本比例懸殊一張大圖里背景區(qū)域占絕大多數(shù)模型很容易被背景帶偏。1.2 整圖縮放對(duì)小目標(biāo)很不友好很多人第一個(gè)想法是大圖沒(méi)法直接訓(xùn)練那就先縮放到模型輸入尺寸比如640×640。這個(gè)操作的副作用非常直接。假設(shè)原圖是4000×3000模型輸入是640×480那么縮放比例大約是0.16。原圖里一個(gè)20×20的目標(biāo)縮放后變成大約3×3像素。3×3像素的物體任何一個(gè)檢測(cè)器都很難區(qū)分它到底是目標(biāo)還是噪點(diǎn)。更不用說(shuō)網(wǎng)絡(luò)內(nèi)部還有多層stride為2的下采樣經(jīng)過(guò)幾次池化或卷積步長(zhǎng)之后這個(gè)小目標(biāo)幾乎完全從特征圖里消失。這就像你把一張城市地圖縮到手機(jī)屏幕大小想找到某條街道上的一個(gè)報(bào)亭根本不可能。但如果你把地圖裁成四塊分別放大顯示報(bào)亭的細(xì)節(jié)就能看清楚。切塊的本質(zhì)就是“局部放大”人眼這種視覺(jué)機(jī)制放在檢測(cè)器上同樣成立。1.3 切塊到底解決了什么切塊不是玄學(xué)它本質(zhì)上解決了三個(gè)問(wèn)題。第一保留了目標(biāo)的原始像素分辨率。目標(biāo)在大圖里是20×20切成子圖后依然是20×20沒(méi)有因?yàn)榻y(tǒng)一縮放而變小。第二提高了目標(biāo)在輸入圖像中的相對(duì)占比。同樣一個(gè)20×20的目標(biāo)放在4000×3000的大圖里占比極小但放在1000×750的子圖里就清晰多了。第三把大圖對(duì)顯存和算力的壓力拆散。單張4K圖可能根本塞不進(jìn)顯卡但切出來(lái)的小塊可以輕松進(jìn)入模型前向計(jì)算。所以切塊方案的核心價(jià)值在于通過(guò)空間劃分把“大圖小目標(biāo)”問(wèn)題轉(zhuǎn)化成“小圖相對(duì)大目標(biāo)”問(wèn)題。這個(gè)思路本身不難難的是轉(zhuǎn)化過(guò)程中那些數(shù)據(jù)加工和結(jié)果合并的細(xì)節(jié)。2. 切塊方案的整體設(shè)計(jì)先想清楚再動(dòng)手2.1 切四塊還是切N塊標(biāo)題里說(shuō)的是“拆成四個(gè)”這也是最自然的直覺(jué)2×2網(wǎng)格簡(jiǎn)單干脆。但真上線(xiàn)前必須想清楚一個(gè)問(wèn)題切四塊之后子圖尺寸是不是仍然遠(yuǎn)超模型輸入尺寸。舉個(gè)例子一塊4000×3000的圖切成2×2四份每份是2000×1500。如果你用的是640×480輸入的檢測(cè)器子圖依然要縮放到640×480這輪縮放比例是0.32。相比整圖縮放的0.16目標(biāo)像素從3.2×3.2變成6.4×6.4確實(shí)有提升但對(duì)很多項(xiàng)目來(lái)說(shuō)6.4像素依然不夠。這種情況下切成3×3每塊約1333×1000或4×4每塊約1000×750反而更合理目標(biāo)在縮放后的尺寸能達(dá)到9.6甚至12.8像素明顯更利于檢測(cè)。所以我的判斷標(biāo)準(zhǔn)很簡(jiǎn)單先統(tǒng)計(jì)目標(biāo)在原圖中的典型尺寸再倒推子圖尺寸。假設(shè)模型輸入長(zhǎng)邊是S你希望小目標(biāo)在經(jīng)過(guò)切塊和縮放后至少有8到16像素那么子圖長(zhǎng)邊可以粗略按“S × 目標(biāo)尺寸 / 期望像素”來(lái)估算。實(shí)際項(xiàng)目里不需要算得那么精確但“根據(jù)目標(biāo)大小決定切塊數(shù)量”這個(gè)思路一定要有而不是無(wú)腦四等分。2.2 子圖尺寸和模型輸入怎么對(duì)齊子圖尺寸不要拍腦袋定至少要滿(mǎn)足兩個(gè)約束。第一個(gè)約束是模型有最佳輸入范圍。大多數(shù)檢測(cè)器在某個(gè)分辨率區(qū)間內(nèi)效果穩(wěn)定比如YOLO系列常用640Faster RCNN常用800×800或1333×800。子圖長(zhǎng)邊建議控制在模型輸入尺寸的1到2倍之間。太大了縮放損失依然嚴(yán)重太小了又失去了切塊的意義。第二個(gè)約束是子圖的長(zhǎng)寬比不要和模型輸入差太多。比如把一張超長(zhǎng)全景圖切成1×4的長(zhǎng)條每個(gè)長(zhǎng)條寬度可能只有高度的四分之一resize進(jìn)正方形輸入時(shí)會(huì)發(fā)生嚴(yán)重形變或者為了保持比例而大量填充黑邊這兩種情況都會(huì)損傷檢測(cè)精度。對(duì)于這類(lèi)圖應(yīng)該按長(zhǎng)寬比靈活調(diào)整網(wǎng)格而不是機(jī)械地切成四份。另外工程上的經(jīng)驗(yàn)是如果原圖尺寸只是模型輸入的兩倍以?xún)?nèi)而且目標(biāo)不算太小可以先試不切塊直接縮放訓(xùn)練很多時(shí)候也沒(méi)問(wèn)題。切塊不是越多越好而是“剛好讓目標(biāo)在輸入圖上達(dá)到可檢測(cè)尺寸”最好同時(shí)還要盡量保留目標(biāo)周?chē)纳舷挛男畔ⅰW訄D太碎目標(biāo)周邊環(huán)境信息丟失反而會(huì)增加誤檢。2.3 重疊率是很容易被忽略的參數(shù)完全沒(méi)有重疊的切塊會(huì)帶來(lái)一個(gè)非常具體的問(wèn)題目標(biāo)正好落在切分邊界上。比如一個(gè)寬40像素的目標(biāo)橫跨兩個(gè)子圖左半邊在塊A右半邊在塊B兩塊里都只有一個(gè)殘缺的目標(biāo)模型很難正確識(shí)別。更極端的情況是目標(biāo)的位置恰好落在四條邊界交匯點(diǎn)四個(gè)子圖各占一小塊哪邊都認(rèn)不出來(lái)。解決辦法就是切塊時(shí)留重疊區(qū)域。重疊率通常取子圖尺寸的10%到25%同時(shí)考慮目標(biāo)最大尺寸重疊寬度至少覆蓋目標(biāo)最大邊的一半以上。我的經(jīng)驗(yàn)是普通場(chǎng)景下重疊64到128像素比較穩(wěn)妥目標(biāo)較大的場(chǎng)景重疊區(qū)域要更大。比如目標(biāo)最大尺寸是100像素那重疊至少留50到100像素否則一個(gè)目標(biāo)很可能同時(shí)出現(xiàn)在兩個(gè)子圖里或者在所有子圖里都是殘缺的。訓(xùn)練階段和推理階段的重疊處理方式稍有不同。訓(xùn)練時(shí)建議一個(gè)目標(biāo)的標(biāo)簽只分配給目標(biāo)中心點(diǎn)所在的子圖避免同一個(gè)目標(biāo)在多個(gè)子圖里被重復(fù)學(xué)習(xí)推理時(shí)則不同所有子圖都正常檢測(cè)重疊區(qū)域里的同一個(gè)目標(biāo)可能被多個(gè)子圖同時(shí)檢出來(lái)這時(shí)候靠后處理NMS去合并。3. 完整實(shí)操?gòu)那袌D到合并檢測(cè)結(jié)果3.1 一次講清切圖腳本我這里按最常見(jiàn)的YOLO格式來(lái)演示切圖流程因?yàn)槟壳肮こ汤颵OLO系列的普及率最高。假設(shè)每張?jiān)瓐D對(duì)應(yīng)一個(gè)同名的txt標(biāo)簽文件每行是“類(lèi)別 cx cy w h”坐標(biāo)都是相對(duì)于原圖寬高的歸一化值。切圖腳本的核心邏輯如下確定網(wǎng)格行列數(shù)、重疊像素然后遍歷每個(gè)網(wǎng)格位置用OpenCV切出子圖同時(shí)把落在該子圖內(nèi)的目標(biāo)標(biāo)簽一并轉(zhuǎn)換寫(xiě)入新文件。import cv2 import os import numpy as np def slice_image(img_path, label_path, out_img_dir, out_label_dir, grid_rows2, grid_cols2, overlap64): img cv2.imread(img_path) H, W img.shape[:2] # 計(jì)算步長(zhǎng)最后一行/列做邊界對(duì)齊 step_h max(1, (H - overlap) // grid_rows) step_w max(1, (W - overlap) // grid_cols) # 讀取標(biāo)簽 boxes [] with open(label_path, r) as f: for line in f.readlines(): parts line.strip().split() cls int(parts[0]) cx, cy, w, h map(float, parts[1:]) # 轉(zhuǎn)成絕對(duì)像素坐標(biāo) x1 (cx - w / 2) * W y1 (cy - h / 2) * H x2 (cx w / 2) * W y2 (cy h / 2) * H boxes.append([cls, x1, y1, x2, y2]) for r in range(grid_rows): for c in range(grid_cols): y1 int(r * step_h) x1 int(c * step_w) y2 min(y1 step_h overlap, H) x2 min(x1 step_w overlap, W) # 邊界塊向左上對(duì)齊保證大小基本一致 y1 max(0, y2 - (step_h overlap)) x1 max(0, x2 - (step_w overlap)) sub_img img[y1:y2, x1:x2] sub_h, sub_w sub_img.shape[:2] # 保存子圖 base_name f{os.path.splitext(os.path.basename(img_path))[0]}_{r}_{c} cv2.imwrite(os.path.join(out_img_dir, base_name .jpg), sub_img) # 過(guò)濾落在當(dāng)前子圖內(nèi)的目標(biāo) new_lines [] for cls, bx1, by1, bx2, by2 in boxes: # 中心點(diǎn)是否在該子圖內(nèi) bcx (bx1 bx2) / 2 bcy (by1 by2) / 2 if x1 bcx x2 and y1 bcy y2: # 裁剪到子圖范圍內(nèi) nx1 (bx1 - x1) / sub_w ny1 (by1 - y1) / sub_h nx2 (bx2 - x1) / sub_w ny2 (by2 - y1) / sub_h nx1 min(max(nx1, 0), 1) ny1 min(max(ny1, 0), 1) nx2 min(max(nx2, 0), 1) ny2 min(max(ny2, 0), 1) ncx (nx1 nx2) / 2 ncy (ny1 ny2) / 2 nw nx2 - nx1 nh ny2 - ny1 new_lines.append(f{cls} {ncx:.6f} {ncy:.6f} {nw:.6f} {nh:.6f}) with open(os.path.join(out_label_dir, base_name .txt), w) as f: f.write(\n.join(new_lines))上面代碼里最關(guān)鍵的一步是“目標(biāo)中心點(diǎn)在該子圖內(nèi)”這個(gè)條件。中心點(diǎn)落在哪個(gè)塊就把目標(biāo)完整分配給那個(gè)塊。這樣能避免同一個(gè)目標(biāo)被多個(gè)子圖重復(fù)學(xué)習(xí)也能保證至少有一個(gè)子圖包含完整目標(biāo)。當(dāng)然如果你的場(chǎng)景里目標(biāo)特別密集每個(gè)目標(biāo)都可能跨多個(gè)塊也可以按“與子圖的交并比大于閾值”來(lái)分配但那種情況下標(biāo)簽信息會(huì)出現(xiàn)冗余需要更小心的后處理。3.2 標(biāo)注轉(zhuǎn)換千萬(wàn)別算錯(cuò)標(biāo)注轉(zhuǎn)換是切塊里最容易出錯(cuò)的一步坐標(biāo)算錯(cuò)會(huì)導(dǎo)致檢測(cè)框偏移、尺寸異常甚至模型訓(xùn)練直接崩掉。出錯(cuò)的原因幾乎都是“歸一化坐標(biāo)”和“絕對(duì)像素坐標(biāo)”混用。轉(zhuǎn)換邏輯其實(shí)很清晰。第一步把原圖標(biāo)簽里的歸一化坐標(biāo)還原成原圖上的絕對(duì)像素坐標(biāo)第二步判斷目標(biāo)中心是否落在當(dāng)前子圖內(nèi)第三步把絕對(duì)像素坐標(biāo)減去子圖的左上角偏移量變成子圖內(nèi)的絕對(duì)坐標(biāo)第四步再除以子圖的寬高轉(zhuǎn)成子圖內(nèi)的歸一化坐標(biāo)。舉個(gè)例子原圖寬4000一個(gè)目標(biāo)的中心cx是0.5那么原圖上絕對(duì)中心x是2000。如果子圖左上角x偏移是1000子圖寬是2000那么該目標(biāo)在子圖內(nèi)的歸一化cx就是(2000-1000)/2000 0.5。這個(gè)數(shù)看起來(lái)好像沒(méi)變但如果你忘了減偏移量直接拿2000除以2000就會(huì)得到1.0檢測(cè)框被硬生生推算到子圖最右邊位置就錯(cuò)了。切圖完成之后我強(qiáng)烈建議做一次可視化校驗(yàn)。用腳本把轉(zhuǎn)換后的標(biāo)簽畫(huà)到子圖上人工抽查幾十張看看框和目標(biāo)是否對(duì)齊。這一步花不了幾分鐘但能避免你在訓(xùn)練了好幾天之后才發(fā)現(xiàn)數(shù)據(jù)有問(wèn)題。我見(jiàn)過(guò)太多項(xiàng)目模型效果起不來(lái)最后查了半天原因就是標(biāo)簽坐標(biāo)映射錯(cuò)了一個(gè)偏移量。3.3 訓(xùn)練階段的配置調(diào)整切塊完成后訓(xùn)練數(shù)據(jù)集的大小會(huì)變成原來(lái)的N倍切N塊就近似N倍。這種情況下有幾個(gè)參數(shù)需要調(diào)整。首先是迭代輪次。數(shù)據(jù)量翻了幾倍如果還保持原來(lái)的epoch數(shù)訓(xùn)練步數(shù)會(huì)大增。一般可以適當(dāng)降低epoch或者用步數(shù)來(lái)控制訓(xùn)練長(zhǎng)度。其次是學(xué)習(xí)率數(shù)據(jù)量變大后一個(gè)epoch內(nèi)的step數(shù)量也變多了學(xué)習(xí)率調(diào)度策略要跟著調(diào)整尤其是余弦退火的周期。更重要的是切塊后的子圖可能有很多完全沒(méi)有目標(biāo)。這些空塊需要處理策略。如果全部保留負(fù)樣本比例會(huì)嚴(yán)重失衡模型容易偏向“什么都檢測(cè)不到”。我的做法是過(guò)濾掉大部分空塊但保留一小部分比如總量的10%到20%作為難負(fù)例尤其在推理階段會(huì)檢測(cè)全圖的場(chǎng)景下保留一定空塊能讓模型學(xué)會(huì)抑制背景區(qū)域的誤檢。還有一個(gè)很容易踩的坑是數(shù)據(jù)增強(qiáng)的適配。常規(guī)的隨機(jī)裁剪增強(qiáng)在切塊場(chǎng)景下要小心因?yàn)樽訄D本身已經(jīng)很小再隨機(jī)裁剪可能把唯一的目標(biāo)裁掉。我建議優(yōu)先使用翻轉(zhuǎn)、旋轉(zhuǎn)、色彩擾動(dòng)、馬賽克增強(qiáng)裁剪類(lèi)增強(qiáng)要克制或者使用帶目標(biāo)約束的裁剪。3.4 推理階段坐標(biāo)映射與NMS合并推理時(shí)先對(duì)測(cè)試大圖做和訓(xùn)練時(shí)完全相同的切塊處理相同網(wǎng)格數(shù)、相同重疊率、相同子圖尺寸。然后對(duì)每塊子圖單獨(dú)做檢測(cè)拿到檢測(cè)結(jié)果后把所有框映射回原圖坐標(biāo)。假設(shè)模型在子圖上輸出的歸一化框?yàn)?cx, cy, w, h)子圖左上角在原圖中的坐標(biāo)是(off_x, off_y)子圖寬高是(sub_w, sub_h)原圖寬高是(orig_w, orig_h)那么映射公式如下def map_box_to_original(cx, cy, w, h, off_x, off_y, sub_w, sub_h, orig_w, orig_h): abs_cx cx * sub_w off_x abs_cy cy * sub_h off_y abs_w w * sub_w abs_h h * sub_h orig_cx abs_cx / orig_w orig_cy abs_cy / orig_h orig_w_ratio abs_w / orig_w orig_h_ratio abs_h / orig_h return orig_cx, orig_cy, orig_w_ratio, orig_h_ratio映射完之后所有子圖的框都到了同一張?jiān)瓐D坐標(biāo)系里這時(shí)候需要做全局NMS。因?yàn)橹丿B區(qū)域的存在同一個(gè)目標(biāo)可能被相鄰兩個(gè)子圖各檢測(cè)一次產(chǎn)生兩個(gè)高度重疊的框。NMS的作用就是保留置信度更高的框抑制重復(fù)框。NMS要注意兩點(diǎn)一是按類(lèi)別做不同類(lèi)別的框不要互相抑制二是建議加一個(gè)比較溫和的IOU閾值比如0.5到0.6。如果場(chǎng)景里目標(biāo)本身密集閾值可以放寬到0.4避免把相鄰的不同目標(biāo)誤合并。實(shí)際項(xiàng)目里還有一種特殊情況目標(biāo)在切分邊界上中心點(diǎn)所在的子圖檢測(cè)出了完整框但隔壁子圖由于看到了殘缺目標(biāo)也可能輸出一個(gè)位置偏移的半截框。這兩個(gè)框的IOU不一定很高普通NMS可能合并不掉。遇到這種情況可以先檢查重疊率是否足夠如果問(wèn)題頻繁出現(xiàn)可以考慮放寬合并條件比如對(duì)同一類(lèi)別且中心距離小于某個(gè)像素閾值的框做合并處理。4. 常見(jiàn)問(wèn)題與避坑實(shí)錄4.1 檢測(cè)框位置偏移、尺寸不對(duì)這類(lèi)問(wèn)題十有八九出在標(biāo)注轉(zhuǎn)換或推理映射環(huán)節(jié)。最常見(jiàn)的錯(cuò)誤包括減偏移量時(shí)減錯(cuò)方向、歸一化時(shí)除以了原圖尺寸而不是子圖尺寸、推理映射時(shí)忘了加上子圖偏移。排查方法很簡(jiǎn)單寫(xiě)一個(gè)可視化腳本把檢測(cè)結(jié)果畫(huà)在原圖上對(duì)著問(wèn)題圖看。如果所有框都偏移到右上方向那基本是偏移量計(jì)算錯(cuò)誤如果框的寬高整體偏大或偏小那多半是縮放系數(shù)弄混了。我自己的習(xí)慣是每次改完切圖或推理腳本先跑一張圖把“子圖檢測(cè)框”和“原圖映射框”同時(shí)畫(huà)出來(lái)核對(duì)一遍確認(rèn)無(wú)誤再批量跑。4.2 目標(biāo)被切到邊界漏檢或重復(fù)如果訓(xùn)練后模型對(duì)跨邊界目標(biāo)的檢測(cè)效果明顯偏差先檢查切圖重疊率。沒(méi)有重疊或重疊太少跨邊界目標(biāo)就會(huì)變成“兩邊都只剩一半”的殘缺樣本。增大重疊率讓任何目標(biāo)至少完整出現(xiàn)在一個(gè)子圖里是最直接的解決辦法。重復(fù)檢測(cè)的處理主要靠NMS但如果發(fā)現(xiàn)同一目標(biāo)穩(wěn)定地被輸出兩個(gè)不完全重疊的框說(shuō)明目標(biāo)經(jīng)常橫跨多個(gè)子圖并且多個(gè)子圖都檢測(cè)到了。這時(shí)可以回到標(biāo)簽分配邏輯檢查訓(xùn)練時(shí)是否也應(yīng)該采用“每個(gè)目標(biāo)只分配給中心點(diǎn)所在塊”的策略。另外對(duì)邊界框的置信度做位置補(bǔ)償也是一種思路給距離子圖中心越近的框越高的置信度權(quán)重但這會(huì)引入額外超參數(shù)一般不作為首選。4.3 空塊太多導(dǎo)致訓(xùn)練失衡大圖場(chǎng)景通常目標(biāo)稀疏切塊后一半以上的子圖可能都是空塊。把空塊全部喂進(jìn)訓(xùn)練集會(huì)導(dǎo)致模型訓(xùn)練時(shí)大部分時(shí)間都在看背景整體收斂變慢甚至可能出現(xiàn)“全部輸出零檢測(cè)框”的退化結(jié)果。我的建議是先統(tǒng)計(jì)塊內(nèi)目標(biāo)數(shù)量保留所有含目標(biāo)的正樣本塊對(duì)于空塊隨機(jī)保留一部分?jǐn)?shù)量控制在正樣本塊數(shù)量的三分之一以?xún)?nèi)。如果任務(wù)本身誤檢率要求很高可以適當(dāng)多留如果更看重召回率可以少留甚至不留。這個(gè)比例需要根據(jù)驗(yàn)證集的實(shí)際表現(xiàn)微調(diào)。4.4 訓(xùn)練時(shí)間、顯存與I/O成本切塊后數(shù)據(jù)量膨脹訓(xùn)練I/O會(huì)成為新的瓶頸。幾千張?jiān)瓐D切成幾萬(wàn)個(gè)子圖如果每次都從磁盤(pán)隨機(jī)讀小文件訓(xùn)練速度會(huì)慢得讓人崩潰??尚械姆桨赣袃蓚€(gè)。第一個(gè)是離線(xiàn)切圖把切好的子圖直接保存到本地再配合數(shù)據(jù)加載器做緩存或預(yù)取簡(jiǎn)單直接缺點(diǎn)是占用磁盤(pán)空間。第二個(gè)是在線(xiàn)切圖在數(shù)據(jù)加載階段動(dòng)態(tài)從原圖切塊優(yōu)點(diǎn)是靈活可以隨機(jī)調(diào)整切塊偏移來(lái)增加數(shù)據(jù)多樣性缺點(diǎn)是每次訓(xùn)練都可能重復(fù)計(jì)算需要做好緩存。顯存方面切塊后的子圖尺寸變小單張進(jìn)入顯存基本沒(méi)問(wèn)題瓶頸反而轉(zhuǎn)移到了數(shù)據(jù)讀取和增強(qiáng)的CPU占用上建議加大num_workers或者用內(nèi)存映射方式讀取大圖。5. 什么時(shí)候切塊不夠用進(jìn)階擴(kuò)展思路5.1 推理時(shí)切片也是一條路訓(xùn)練時(shí)切塊是最穩(wěn)的方案但不是唯一的選擇。還有一種做法是訓(xùn)練時(shí)整圖訓(xùn)練推理時(shí)再把大圖切片分別檢測(cè)業(yè)內(nèi)叫SAHI切片輔助推理已經(jīng)有現(xiàn)成的開(kāi)源庫(kù)可以快速實(shí)驗(yàn)。訓(xùn)練時(shí)切片的好處是讓模型在訓(xùn)練階段就適應(yīng)小圖視角推理時(shí)切片的好處是訓(xùn)練數(shù)據(jù)可以保持原始上下文模型看到的目標(biāo)更完整。實(shí)際操作中前者通常更穩(wěn)因?yàn)橛?xùn)練和推理的數(shù)據(jù)分布一致不容易出現(xiàn)尺度不一致導(dǎo)致的性能下降。如果你暫時(shí)不想改動(dòng)訓(xùn)練流程直接拿SAHI在測(cè)試階段做切片推理也是一個(gè)低成本驗(yàn)證方案。5.2 切塊、多尺度與高分辨率特征層互補(bǔ)切塊本質(zhì)上是數(shù)據(jù)層面的尺度放大它和多尺度訓(xùn)練、特征金字塔這些策略并不沖突可以組合使用。多尺度訓(xùn)練能增加模型對(duì)目標(biāo)尺寸變化的魯棒性但不會(huì)解決“目標(biāo)在輸入圖上只有3個(gè)像素”的根本問(wèn)題。切塊先解決了這個(gè)底層問(wèn)題多尺度再在子圖基礎(chǔ)上去增強(qiáng)多樣性?xún)烧吲浜闲Ч谩A硗夂芏鄼z測(cè)器的特征金字塔默認(rèn)從P3層開(kāi)始小目標(biāo)特征到了P3已經(jīng)損失不少可以考慮使用帶P2層的檢測(cè)頭讓高分辨率特征圖直接參與到小目標(biāo)預(yù)測(cè)。這類(lèi)改進(jìn)在切塊方案之后通常還能帶來(lái)幾個(gè)點(diǎn)的提升。5.3 超分重建與切塊結(jié)合如果目標(biāo)實(shí)在太小切塊加放大之后仍然不夠識(shí)別可以考慮對(duì)局部區(qū)域做超分辨率重建先放大再檢測(cè)。比如先通過(guò)一個(gè)快速篩選器找到可能包含目標(biāo)的子圖區(qū)域?qū)@些區(qū)域做超分再進(jìn)入精細(xì)檢測(cè)器。這個(gè)方案的代價(jià)是推理鏈路變長(zhǎng)、耗時(shí)增加適合離線(xiàn)處理或?qū)φ倩芈室髽O高但對(duì)實(shí)時(shí)性要求不高的場(chǎng)景。我嘗試過(guò)在部分遙感項(xiàng)目里用這個(gè)組合小目標(biāo)的召回率確實(shí)有提升但速度會(huì)慢好幾倍是否值得需要根據(jù)業(yè)務(wù)具體權(quán)衡。5.4 部署階段的性能注意點(diǎn)切塊方案上線(xiàn)部署時(shí)計(jì)算量會(huì)變成原來(lái)的N倍這是一個(gè)經(jīng)常被忽略的成本。比如4張子圖推理次數(shù)就是原來(lái)的4倍如果再疊加多尺度或TTA計(jì)算量可能膨脹到十幾倍。部署時(shí)可以做兩件事優(yōu)化第一重疊區(qū)域的卷積特征可以緩存復(fù)用但通常實(shí)現(xiàn)復(fù)雜收益不一定明顯第二多個(gè)子圖可以放到同一個(gè)batch里并行推理比逐張推理更高效。另外前后處理的邏輯要封裝成獨(dú)立模塊輸入一張大圖輸出完整坐標(biāo)的檢測(cè)結(jié)果對(duì)上層應(yīng)用屏蔽切塊細(xì)節(jié)這樣后續(xù)切換算法或調(diào)整切塊參數(shù)時(shí)不會(huì)影響業(yè)務(wù)流程。最后幾句實(shí)在話(huà)切塊訓(xùn)練不是銀彈但在大圖小目標(biāo)場(chǎng)景里它確實(shí)是性?xún)r(jià)比最高、最容易落地的一步。我做過(guò)的一個(gè)遙感影像檢測(cè)項(xiàng)目目標(biāo)像素只有二三十像素原圖四千多像素寬最初直接縮放訓(xùn)練mAP慘不忍睹后來(lái)改成切塊加重疊什么網(wǎng)絡(luò)結(jié)構(gòu)都沒(méi)動(dòng)mAP直接漲了十幾個(gè)點(diǎn)。這個(gè)提升幾乎全部來(lái)自數(shù)據(jù)預(yù)處理層面的尺度還原。如果你正準(zhǔn)備上手這類(lèi)項(xiàng)目我的建議是動(dòng)手前先花半天統(tǒng)計(jì)目標(biāo)尺寸和分布想清楚切多少塊、子圖多大、重疊多少切完圖做好可視化校驗(yàn)確認(rèn)標(biāo)簽沒(méi)有出錯(cuò)訓(xùn)練和推理的切塊參數(shù)保持一致后處理NMS別偷懶。這幾件事做扎實(shí)了大圖小目標(biāo)的檢測(cè)基本就成功了一大半。