:基于深度卷積神經(jīng)網(wǎng)絡(luò)與CRNN的端到端方案)
簡介面向深度學(xué)習(xí)、機(jī)器學(xué)習(xí)、計算機(jī)視覺與模式識別領(lǐng)域的研究者和學(xué)生這份PDF專業(yè)文獻(xiàn)聚焦自然場景中街景門牌號識別難題。針對背景復(fù)雜、字符風(fēng)格多樣、人工特征難以適用的問題作者基于AlexNet改進(jìn)網(wǎng)絡(luò)結(jié)構(gòu)加深網(wǎng)絡(luò)深度并增加卷積核數(shù)量在激活函數(shù)后引入批歸一化BN于全連接層采用低比例Dropout策略同時將圖像灰度化以弱化背景和光照干擾。在SVHN街景門牌號數(shù)據(jù)集上該網(wǎng)絡(luò)訓(xùn)練約13小時識別率達(dá)94.58%在精度與訓(xùn)練成本之間實現(xiàn)了良好平衡。資源為單篇PDF共1個文件壓縮包僅1.78MB包含論文全文、網(wǎng)絡(luò)結(jié)構(gòu)圖、實驗對比數(shù)據(jù)及參考文獻(xiàn)可完整還原從數(shù)據(jù)預(yù)處理、網(wǎng)絡(luò)設(shè)計到實驗驗證的整個研究過程。文中還梳理了CNN、深度學(xué)習(xí)、AlexNet、BN、Dropout、OCR、LeNet-5及GPU加速等關(guān)鍵知識點適合作為課程設(shè)計、科研入門或算法復(fù)現(xiàn)的參考資料。目前已有480人學(xué)習(xí)下載是相關(guān)領(lǐng)域?qū)W習(xí)者值得參考的專業(yè)資源。1. 街景門牌號識別為什么深度學(xué)習(xí)比傳統(tǒng)OCR更靠譜街景門牌號識別說白了就是讓算法看懂街景照片里那串門牌數(shù)字。過去用傳統(tǒng)OCR在干凈掃描件上還能跑一遇到傾斜、模糊、反光、遮擋的實拍門牌就翻車——閾值分割對光照敏感模板匹配扛不住形變字符切分在粘連數(shù)字面前直接啞火?;谏疃染矸e神經(jīng)網(wǎng)絡(luò)的街景門牌號識別方法把特征提取、序列建模和字符映射端到端塞進(jìn)同一個網(wǎng)絡(luò)在SVHN這類基準(zhǔn)集上通常能到95%以上的準(zhǔn)確率關(guān)鍵是它對真實街景的退化魯棒性要好得多。這篇筆記不講論文復(fù)現(xiàn)的虛話直接從任務(wù)拆解、模型搭建、訓(xùn)練調(diào)參到落地加速把每一步的參數(shù)和坑都擺出來適合正在做車牌識別、門牌識別、快遞單號識別這類實拍數(shù)字識別項目的工程師和學(xué)生。2. 門牌號識別任務(wù)拆解從SVHN到真實街景的差異2.1 先搞清SVHN數(shù)據(jù)集與真實街景的邊界SVHNStreet View House Number是從谷歌街景里裁剪出來的門牌號圖片每張圖含有1到5位數(shù)字。它的標(biāo)注是每個數(shù)字的邊界框和類別標(biāo)簽所以很多人把它當(dāng)多目標(biāo)檢測或分類任務(wù)來做。但要注意SVHN里的圖片已經(jīng)經(jīng)過預(yù)處理門牌號大致在畫面中心背景干擾相對可控光照分布也相對均勻。真實街景完全不同——門牌可能出現(xiàn)在畫面角落被樹枝或車燈遮擋字體可能是藝術(shù)字墻面反光、陰影、夜間照明都會讓數(shù)字紋理徹底改變。所以嚴(yán)格來說門牌號識別在工程上分兩條路線一條是檢測識別級聯(lián)先用目標(biāo)檢測把門牌區(qū)域框出來再對區(qū)域做數(shù)字識別另一條是端到端序列識別直接輸入整張街景圖輸出數(shù)字序列。后者對數(shù)據(jù)量和模型容量的要求更高但省去了檢測框的誤差傳遞。我一般建議如果你手里的街景圖是全景掃描件門牌占比小且背景復(fù)雜優(yōu)先用檢測識別如果門牌已經(jīng)裁切好或者門牌在畫面中占據(jù)主要區(qū)域直接上端到端序列識別更省事。下面的代碼是常見的數(shù)據(jù)預(yù)處理從SVHN的mat格式標(biāo)注中提取數(shù)字序列和bounding box并生成訓(xùn)練用的圖片列表。SVHN官方提供digitStruct.mat里面存了每個樣本的bbox和label用Python讀出來轉(zhuǎn)成自己的格式。import h5py import numpy as np from pathlib import Path # 讀取SVHN的digitStruct.mat # 該文件結(jié)構(gòu)每個樣本有name, bbox包含height,left,top,width,label def load_svhn_labels(mat_path): with h5py.File(mat_path, r) as f: names f[digitStruct][name] bboxes f[digitStruct][bbox] records [] for i in range(len(names)): # 逐樣本解析處理單個數(shù)字和多個數(shù)字的存儲差異 name .join(chr(c) for c in f[names[i]][()].flatten()) bbox_group f[bboxes[i]][()].flatten() bbox_list [] for item in bbox_group: if item.ndim 0: # 單個數(shù)字時是標(biāo)量 item np.array([item]) for j in item: bbox_list.append({k: f[j][k][()].reshape(-1)[0] for k in [height,left,top,width,label]}) labels [int(b[label]) % 10 for b in bbox_list] # 10代表0 records.append({name: name, labels: labels}) return records records load_svhn_labels(digitStruct.mat) print(f樣本總數(shù): {len(records)}, 示例標(biāo)簽: {records[0]})這段代碼的邏輯是h5py讀取SVHN官方mat文件names和bboxes都是對象引用需要逐級解除引用。特別注意單個數(shù)字和多個數(shù)字的存儲結(jié)構(gòu)不同ndim 0時得手動包一層數(shù)組。label里用10表示數(shù)字0取??梢赃€原成0-9。參數(shù)說明mat_path是digitStruct.mat的路徑返回的records列表里每個元素包含原始文件名和數(shù)字序列后續(xù)按name去圖片目錄里找對應(yīng)png即可。2.2 門牌號識別是序列標(biāo)注問題不是簡單分類對一張包含 502 三個數(shù)字的門牌圖如果只做單標(biāo)簽分類你得預(yù)定義所有可能的組合這顯然不現(xiàn)實。正確做法是把它當(dāng)序列標(biāo)注輸入圖像輸出一個不定長的數(shù)字序列。常用的建模方式有兩種CTCConnectionist Temporal Classification和Attention。CTC不需要逐字符對齊標(biāo)注只需要最終的序列標(biāo)簽所以訓(xùn)練省事Attention需要在訓(xùn)練時讓模型逐步生成字符解碼更靈活但容易漂移。工程上我傾向CTC尤其數(shù)字序列長度不超過5位時CTC的穩(wěn)定性和速度都更優(yōu)。既然要做序列識別圖像輸入就要有序列的形態(tài)。常見做法是把圖片縮放成固定高度比如32寬度按比例縮放但不超過某個上限比如256然后沿著寬度方向把特征圖切成一列一列的時間步。深度卷積神經(jīng)網(wǎng)絡(luò)在這里的作用是提取每列的特征向量再用循環(huán)網(wǎng)絡(luò)BiLSTM建模列與列之間的上下文關(guān)系最后接入CTC損失。這個過程可以用一個公式概括CNN提取視覺特征 - 特征圖按列展開成序列 - BiLSTM建模列間依賴 - CTC解碼出數(shù)字串。在動手寫模型之前先要把訓(xùn)練數(shù)據(jù)整理成統(tǒng)一格式。下面是一個輕量的數(shù)據(jù)加載器把圖片高度固定到32寬度padding到固定值同時生成對應(yīng)的標(biāo)簽序列和序列長度。import torch from torch.utils.data import Dataset from torchvision import transforms from PIL import Image class HouseNumberDataset(Dataset): def __init__(self, records, img_dir, fixed_height32, max_width128): self.records records self.img_dir img_dir self.fixed_height fixed_height self.max_width max_width self.tf transforms.Compose([ transforms.Resize((fixed_height, max_width)), # 高度固定寬度強(qiáng)制縮放 transforms.ToTensor(), transforms.Normalize(mean[0.5, 0.5, 0.5], std[0.5, 0.5, 0.5]) ]) self.char_map {str(i): i1 for i in range(10)} # 0-9映射到1-100位留給CTC blank def __len__(self): return len(self.records) def __getitem__(self, idx): rec self.records[idx] img Image.open(f{self.img_dir}/{rec[name]}).convert(RGB) img self.tf(img) labels [self.char_map[str(d)] for d in rec[labels]] label_len len(labels) return { img: img, labels: torch.tensor(labels, dtypetorch.long), label_len: label_len }這里有一個容易被忽略的參數(shù)Resize((fixed_height, max_width))直接把所有圖壓成 32x128。這樣做的好處是張量形狀統(tǒng)一方便batch訓(xùn)練壞處是長寬比失真尤其把細(xì)長門牌橫向拉寬后數(shù)字會變形。更穩(wěn)的做法是保持高度32寬度按原圖比例等比縮放不夠max_width的部分用0填充右邊加padding這個坑我們放到第4章詳細(xì)說。char_map里用0作為CTC的blank符號所以數(shù)字1-9映射到1-9數(shù)字0映射到10這樣模型輸出通道數(shù)設(shè)為1110個數(shù)字1個blank。3. 搭建深度卷積神經(jīng)網(wǎng)絡(luò)模型選型與訓(xùn)練配置3.1 從LeNet到CRNN門牌號識別的主流模型骨架深度卷積神經(jīng)網(wǎng)絡(luò)在門牌號識別上的進(jìn)化路線很清晰最早是LeNet這種淺層CNN直接輸出數(shù)字類別只適合單數(shù)字后來SVHN比賽帶火了多數(shù)字識別大家開始用多個CNN分支或滑動窗口再后來序列識別思路普及CNNRNNCTC的組合成為標(biāo)配也就是常說的CRNN結(jié)構(gòu)。對街景門牌號來說CRNN比純CNN多了一個關(guān)鍵能力它能建模數(shù)字之間的順序關(guān)系。比如 12 和 21純CNN把整張圖當(dāng)成一個整體特征容易混淆CRNN通過時間序列把左右位置的自然順序編碼進(jìn)特征里準(zhǔn)確率明顯提升。卷積層怎么選我在實際項目里用過ResNet18、VGG16和MobileNetV3做特征提取結(jié)論是參數(shù)規(guī)模不是第一位的感受野和特征圖寬度才是。門牌數(shù)字筆畫細(xì)卷積核不能太大3x3足夠網(wǎng)絡(luò)深度至少要有4個下采樣把32高圖片縮到2x2或1x1的feature map否則序列長度不夠。推薦一個實用的backbone配置4個卷積階段每階段兩個3x3卷積加一個2x2 maxpool通道數(shù)從64翻倍到512。這樣輸入32x128的圖最終特征圖是4x32高4寬32序列長度就是32。循環(huán)層用BiLSTM還是GRU門牌號序列短用雙向LSTM一層或兩層都夠。隱藏單元數(shù)一般取256或512。如果怕過擬合可以加dropout。關(guān)鍵參數(shù)是batch_firstTrue這樣tensor形狀是(batch, seq_len, feature_dim)方便和CNN輸出對齊。CTC損失函數(shù)在PyTorch里是torch.nn.CTCLoss需要傳入三個東西模型的logits、目標(biāo)標(biāo)簽序列、每個樣本的序列長度和目標(biāo)長度。3.2 最小可復(fù)現(xiàn)訓(xùn)練腳本數(shù)據(jù)加載、損失函數(shù)與優(yōu)化器下面這段代碼是一個完整的CRNN訓(xùn)練循環(huán)PyTorch版注釋里標(biāo)了每個參數(shù)的推薦值和調(diào)整方向。它可以直接跑通SVHN數(shù)據(jù)集的子集但你要把它當(dāng)成模板根據(jù)自己的數(shù)據(jù)規(guī)模改batch size和迭代輪數(shù)。import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader class CRNN(nn.Module): CNN BiLSTM CTC 的門牌號識別網(wǎng)絡(luò) def __init__(self, num_classes11, hidden_size256): super().__init__() # 卷積特征提取輸入3x32x128輸出512x4x32 self.cnn nn.Sequential( nn.Conv2d(3, 64, 3, padding1), nn.BatchNorm2d(64), nn.ReLU(inplaceTrue), nn.Conv2d(64, 64, 3, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(2, 2), # 16x64 nn.Conv2d(64, 128, 3, padding1), nn.BatchNorm2d(128), nn.ReLU(inplaceTrue), nn.Conv2d(128, 128, 3, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(2, 2), # 8x32 nn.Conv2d(128, 256, 3, padding1), nn.BatchNorm2d(256), nn.ReLU(inplaceTrue), nn.Conv2d(256, 256, 3, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(2, (2, 1)), # 池化時不縮寬度保持序列長度 4x32 ) self.lstm nn.LSTM(256*4, hidden_size, bidirectionalTrue, num_layers2, batch_firstTrue, dropout0.3) self.fc nn.Linear(hidden_size*2, num_classes) def forward(self, x): x self.cnn(x) # [B, 256, 4, 32] b, c, h, w x.size() x x.view(b, c*h, w) # 合并高度和通道變成 [B, 1024, 32] x x.permute(0, 2, 1) # [B, 32, 1024] 序列長度32 x, _ self.lstm(x) # [B, 32, 512] x self.fc(x) # [B, 32, 11] return x # logits后面接CTCLoss # 訓(xùn)練參數(shù)設(shè)置 batch_size 32 epochs 20 learning_rate 1e-3 device torch.device(cuda if torch.cuda.is_available() else cpu) model CRNN(num_classes11, hidden_size256).to(device) optimizer optim.Adam(model.parameters(), lrlearning_rate, weight_decay1e-5) scheduler optim.lr_scheduler.CosineAnnealingLR(optimizer, T_maxepochs) criterion nn.CTCLoss(blank0, zero_infinityTrue) # 假設(shè) dataset 是上一節(jié)的 HouseNumberDataset dataloader DataLoader(dataset, batch_sizebatch_size, shuffleTrue, num_workers4, collate_fncollate_fn) for epoch in range(epochs): model.train() total_loss 0 for batch in dataloader: imgs, targets, target_lengths batch[img].to(device), batch[labels], batch[label_len] logits model(imgs) # [B, T, C] log_probs logits.log_softmax(2).permute(1, 0, 2) # CTC需要[T, B, C] input_lengths torch.full((batch_size,), logits.size(1), dtypetorch.long) loss criterion(log_probs, targets, input_lengths, target_lengths) optimizer.zero_grad() loss.backward() nn.utils.clip_grad_norm_(model.parameters(), 5.0) # 梯度裁剪防止LSTM梯度爆炸 optimizer.step() total_loss loss.item() scheduler.step() print(fEpoch {epoch1}/{epochs}, Loss: {total_loss/len(dataloader):.4f})這段代碼的邏輯拆解如下CRNN前向時CNN輸出特征圖是[B, C, H, W]其中H4W32。為了把特征圖轉(zhuǎn)成序列把C和H合并成一個維度得到[B, 1024, 32]再permute成[B, 32, 1024]這樣32個時間步每步1024維特征。BiLSTM雙向輸出是512維hidden_size256雙向翻倍最后接線性層映射到11類。幾個參數(shù)值得單獨說。nn.MaxPool2d(2, (2, 1))是故意讓高度縮減一半、寬度不變因為高度從8縮到4寬度保持32這樣序列長度仍是32。如果你把寬度也縮了比如變成16序列長度就太短數(shù)字密集時會丟信息。CTCLoss的blank0必須和char_map里為blank預(yù)留的位置一致否則解碼會錯位。zero_infinityTrue的作用是當(dāng)loss出現(xiàn)inf比如某樣本目標(biāo)長度大于輸入長度時置零避免訓(xùn)練崩潰但這種情況應(yīng)該靠數(shù)據(jù)檢查杜絕。collate_fn沒有寫全實際需要把不同長度的標(biāo)簽序列pad到同一長度并記錄真實長度。常見做法是用torch.nn.utils.rnn.pad_sequence加上左邊padding并在標(biāo)簽前插入一個值比如-1供CTCLoss忽略。這里不再展開第4章會提到標(biāo)簽對齊的坑。學(xué)習(xí)率策略建議用warmup余弦退火。上面的腳本里直接用余弦退火但前幾個epoch可能因為學(xué)習(xí)率過大導(dǎo)致振蕩。一個更穩(wěn)的做法是前3個epoch用線性warmup從1e-4升到1e-3之后再按余弦衰減。優(yōu)化器選Adam沒問題但weight_decay別太大1e-5足夠太大會把卷積核的權(quán)重壓得過小。batch size在顯存允許范圍內(nèi)越大越好但BatchNorm的參數(shù)會受影響建議用32到64之間。4. 訓(xùn)練與調(diào)參避坑5條真實踩坑記錄4.1 現(xiàn)象驗證集loss不降精度卡在80%上不去最常見的原因是學(xué)習(xí)率設(shè)置不當(dāng)。我見過有人用默認(rèn)的1e-3跑SVHN一開始loss下降很快到了第5個epoch就開始震蕩驗證集準(zhǔn)確率一直在80%左右徘徊。查下來發(fā)現(xiàn)是學(xué)習(xí)率太大模型在最優(yōu)解附近來回跳躍。另一個原因是數(shù)據(jù)歸一化不一致訓(xùn)練時用ImageNet的mean/std但推理時忘了轉(zhuǎn)成相同預(yù)處理。解決方法是先跑兩三個epoch觀察loss曲線。如果loss在初始值附近波動劇烈把學(xué)習(xí)率降到1e-4或1e-5。如果loss下降極慢說明學(xué)習(xí)率太小適當(dāng)上調(diào)。更系統(tǒng)的方式是使用學(xué)習(xí)率預(yù)熱前三個epoch用1/10的目標(biāo)學(xué)習(xí)率然后線性漲到目標(biāo)值之后自然衰減。另外固定一套預(yù)處理pipeline訓(xùn)練和推理都使用完全相同的Resize和Normalize參數(shù)不要混用。4.2 現(xiàn)象模型永遠(yuǎn)輸出空序列或只輸出重復(fù)數(shù)字CTC訓(xùn)練時很容易出現(xiàn)模型傾向輸出blank字符導(dǎo)致預(yù)測結(jié)果全是空的。尤其當(dāng)訓(xùn)練集中很多圖片只有一個數(shù)字而blank通道的概率遠(yuǎn)大于數(shù)字通道時模型學(xué)到的捷徑就是全輸出blank。另一個表現(xiàn)是輸出一串相同的數(shù)字比如111而不是12。原因是BiLSTM的時間步之間相關(guān)性過強(qiáng)或者梯度在時間維度上傳播不均勻。我當(dāng)時的處理辦法有三步第一步檢查標(biāo)簽字符映射確認(rèn)blank索引是0且數(shù)字標(biāo)簽從1開始第二步在損失函數(shù)上動刀CTCLoss的blank參數(shù)不能錯同時可以用一個簡單的正則項懲罰blank概率的平均值過大第三步調(diào)整模型結(jié)構(gòu)把BiLSTM的隱藏單元數(shù)從512降到256增加dropout強(qiáng)迫模型不過度依賴時序記憶。還有一個經(jīng)驗如果訓(xùn)練數(shù)據(jù)里不同長度的樣本分布不均盡量按長度分batch避免一個batch里全是單數(shù)字樣本。4.3 現(xiàn)象訓(xùn)練時隨機(jī)裁剪效果好但推理時結(jié)果變差我踩過最深的坑是數(shù)據(jù)增強(qiáng)和推理預(yù)處理不一致。訓(xùn)練時我用了RandomResizedCrop把門牌號隨機(jī)縮放裁剪使得模型對尺度變化更魯棒但推理時我用的是直接Resize((32, 128))把整張圖壓扁。結(jié)果訓(xùn)練指標(biāo)93%推理時面對同樣來源的圖片只有85%差了一大截。原因很簡單模型學(xué)到的特征對長寬比和絕對位置敏感推理時破壞了這種分布。解決方法是讓推理和訓(xùn)練的尺寸策略統(tǒng)一。要么訓(xùn)練時也強(qiáng)制所有圖片Resize((32, 128))放棄尺度增強(qiáng)要么推理時保持高度32寬度按比例縮放后padding到128。我推薦后者寫一個inference_transform先用Resize只傳高度再手動計算寬度并pad這樣既保持了寬高比也兼容了batch輸入。另外對門牌這種小字符水平翻轉(zhuǎn)增強(qiáng)千萬別用數(shù)字翻轉(zhuǎn)后語義全變。4.4 現(xiàn)象整圖輸入模型小門牌根本認(rèn)不出來如果輸入圖片是整條街景門牌號只占幾十個像素上面那種整圖識別模型基本會翻車。因為卷積網(wǎng)絡(luò)下采樣后小目標(biāo)特征幾乎丟失。有人試過把輸入分辨率從32x128調(diào)成64x256精度提升有限顯存卻翻倍。正確的做法是先用目標(biāo)檢測定位門牌區(qū)域再對裁剪區(qū)域做識別。檢測器可以用YOLO或Faster R-CNN門牌類別的anchor長寬比偏扁需要調(diào)整anchor尺寸。我做過一個方案用YOLOv8檢測門牌置信度閾值設(shè)0.3NMS IoU閾值設(shè)0.5把檢測框擴(kuò)10%再送CRNN識別。這樣整圖識別準(zhǔn)確率從70%漲到91%。代價是兩階段延遲大約多15ms但收益明顯。如果你不想引入檢測器可以嘗試在CNN中使用特征金字塔FPN讓淺層高分辨率特征圖也能接觸到最后分類但實現(xiàn)復(fù)雜度會上升不一定劃算。4.5 現(xiàn)象SVHN上表現(xiàn)很好換真實街景數(shù)據(jù)就崩這是所有公開數(shù)據(jù)集訓(xùn)練的宿命SVHN雖然來自街景但已經(jīng)過裁剪和篩選背景多樣性遠(yuǎn)小于真實場景。我的真實測試集里有夜間長曝光、玻璃反光、綠色藤蔓遮擋、傾斜角度接近45°的門牌模型直接崩潰。這說明模型學(xué)到的特征是“SVHN風(fēng)格”的不是通用的數(shù)字紋理。后悔藥有三丸。第一丸是收集至少500張真實街景圖片用半自動標(biāo)注檢測模型預(yù)標(biāo)記人工修正加入訓(xùn)練集做finetune。第二丸是合成數(shù)據(jù)增強(qiáng)找一批不同風(fēng)格的字體隨機(jī)旋轉(zhuǎn)、加模擬陰影、貼到真實街景背景上生成大量偽樣本。第三丸是使用灰度圖或邊緣圖作為額外輸入通道減少對顏色的依賴。我最后靠75%真實25%合成數(shù)據(jù)把真實街景的精度從62%拉到了84%。別指望模型自己泛化數(shù)據(jù)上必須下功夫。5. 從單張圖片到街景視頻流推斷加速與后處理技巧門牌號識別在實際產(chǎn)品里往往要處理連續(xù)視頻流而不是單張靜態(tài)圖。我的經(jīng)驗是先做batch推斷再把同一門牌在多幀中的識別結(jié)果做時序融合。CTC解碼時常用貪婪搜索每幀輸出一個數(shù)字序列但街道上車輛角度變化單幀可能截到殘缺門牌。一種輕量做法是滑動窗口收集最近5幀的識別結(jié)果對每個位置的數(shù)字做投票票數(shù)最高且超過3票才輸出否則等待下一幀。這個技巧能把誤檢率降低一半代價只是延遲5幀。模型加速方面CRNN的瓶頸通常在BiLSTM因為LSTM是串行計算??梢园阉鎿Q成BiGRU或直接用因果卷積TCN推理速度能快一截。如果精度損失可接受可以把模型導(dǎo)出成ONNX用TensorRT或OpenVINO做FP16量化。我在Jetson Nano上把CRNN從TensorFlow轉(zhuǎn)到TensorRT后單幀推斷從45ms降到18ms顯存占用也降了一半。注意量化時對BatchNorm層的處理最好是先折疊BN再量化不然誤差會放大。驗證模型是否真正落地我有個習(xí)慣不看單張圖準(zhǔn)確率而是錄一段真實街景視頻跑一個端到端demo統(tǒng)計“門牌號連續(xù)5幀識別一致”的比例。這個比例比單幀精度更能反映產(chǎn)品體驗。另外別忘了做置信度校準(zhǔn)——CTC輸出的概率往往過于自信稍微加一個溫度系數(shù)temperature scaling會讓閾值調(diào)節(jié)更靠譜。最后說個教訓(xùn)別急著優(yōu)化模型結(jié)構(gòu)先把數(shù)據(jù)管線搞干凈。我見過太多人花一個月調(diào)網(wǎng)絡(luò)最后發(fā)現(xiàn)是訓(xùn)練和推理的預(yù)處理不一致白白浪費時間。希望這篇筆記能幫你少走這幾步冤枉路。本文還有配套的精品資源點擊獲取