戰(zhàn):基于LiteRT.js的收據(jù)掃描器與WebGPU推理優(yōu)化)
瀏覽器里跑OCR這件事我從Tesseract.js時(shí)代就開始折騰中間踩過的坑能寫滿一個(gè)筆記本。早期方案要么模型體積大到離譜要么識(shí)別一張小票要等十幾秒用戶體驗(yàn)基本等于勸退。直到LiteRT.js這套運(yùn)行時(shí)方案成熟起來配合WebAssembly和WebGPU兩條推理后端才真正讓打開網(wǎng)頁就能掃收據(jù)這件事變得可用。這篇內(nèi)容圍繞基于瀏覽器的收據(jù)掃描器LiteRT.js展開把模型選型、推理后端切換、圖像預(yù)處理、結(jié)構(gòu)化解析這幾塊拆開講透適合正在做前端OCR、想把手頭識(shí)別任務(wù)搬到瀏覽器端、或者單純對(duì)WebGPU推理感興趣的朋友參考。不管你是剛接觸OCR的新手還是已經(jīng)用過Paddle OCR、Tesseract的老手這里面的實(shí)操細(xì)節(jié)和避坑經(jīng)驗(yàn)應(yīng)該都能用得上。1. 為什么收據(jù)掃描這件事值得單獨(dú)做一套瀏覽器方案1.1 收據(jù)識(shí)別的場景特殊性決定了它不能照搬通用OCR很多人第一反應(yīng)是OCR不是早就有了嗎調(diào)個(gè)接口不就完了。但收據(jù)這個(gè)場景非常特殊通用OCR方案直接套上去識(shí)別率會(huì)讓你懷疑人生。收據(jù)通常是熱敏紙打印字跡偏淡、對(duì)比度低而且紙張經(jīng)常有折痕、卷曲、油漬拍攝時(shí)又多半是隨手一拍角度傾斜、光照不均、背景雜亂。更麻煩的是收據(jù)的版式高度非標(biāo)準(zhǔn)化——同樣是超市小票不同商家的字段順序、分隔線樣式、金額對(duì)齊方式完全不同。通用OCR模型大多在規(guī)整文檔上訓(xùn)練遇到這種臟數(shù)據(jù)就露怯。而收據(jù)識(shí)別真正要的不是把每個(gè)字都認(rèn)出來而是抽取結(jié)構(gòu)化字段商戶名、日期、總金額、稅、明細(xì)條目。這意味著整條鏈路要分成檢測(cè)、識(shí)別、后處理三段每段都有針對(duì)性的優(yōu)化空間。把這件事放到瀏覽器里做還要額外考慮模型體積、內(nèi)存占用、推理速度復(fù)雜度直接翻倍。1.2 瀏覽器端推理到底解決了什么真實(shí)痛點(diǎn)先說清楚為什么非要在瀏覽器里跑而不是丟給服務(wù)端。最直接的理由是隱私。收據(jù)上往往帶著消費(fèi)記錄、卡號(hào)后四位、甚至部分個(gè)人信息用戶對(duì)把照片上傳到某個(gè)服務(wù)器這件事天然警惕。本地推理意味著圖片從頭到尾不離開設(shè)備這對(duì)做記賬類、報(bào)銷類工具的產(chǎn)品來說是硬需求。其次是成本和延遲。服務(wù)端OCR要么按調(diào)用量付費(fèi)要么自己維護(hù)GPU集群量一大成本就壓不住。瀏覽器端推理把算力攤到用戶設(shè)備上服務(wù)端只負(fù)責(zé)可選的同步和存儲(chǔ)邊際成本幾乎為零。延遲上省掉了圖片上傳和結(jié)果回傳的往返本地推理在WebGPU加持下能做到幾百毫秒出結(jié)果體驗(yàn)比等網(wǎng)絡(luò)請(qǐng)求順暢得多。還有離線可用這一條地鐵里、飛機(jī)上照樣能掃這是服務(wù)端方案給不了的。1.3 LiteRT.js在這個(gè)鏈路里扮演的角色LiteRT.js本質(zhì)上是把訓(xùn)練好的模型轉(zhuǎn)換成能在瀏覽器里高效執(zhí)行的格式并提供統(tǒng)一的推理接口。它屏蔽了底層到底是走WebAssembly還是WebGPU的差異讓上層代碼用同一套API調(diào)用。你可以把它理解成一個(gè)模型執(zhí)行引擎模型文件通常是量化后的tflite或類似格式加載進(jìn)來輸入張量喂進(jìn)去輸出張量拿出來中間的內(nèi)存管理、算子調(diào)度、后端選擇都由它處理。它和Tesseract.js的區(qū)別在于定位。Tesseract.js是把一整套OCR流程含檢測(cè)和識(shí)別打包開箱即用但定制性差LiteRT.js更像底層運(yùn)行時(shí)你可以自由組合檢測(cè)模型和識(shí)別模型針對(duì)收據(jù)場景做深度定制。代價(jià)是你得自己搭流程但換來的是識(shí)別率和性能的可控性。對(duì)于收據(jù)這種非標(biāo)場景這個(gè)取舍是值得的。2. 檢測(cè)與識(shí)別雙模型的分工設(shè)計(jì)2.1 文本檢測(cè)模型負(fù)責(zé)把收據(jù)切成行收據(jù)識(shí)別的第一步是找到文字在哪里。這一步用文本檢測(cè)模型完成輸出是一堆文本框通常是旋轉(zhuǎn)矩形或多邊形。收據(jù)場景下檢測(cè)有幾個(gè)要點(diǎn)一是要能處理傾斜文本因?yàn)榕臄z角度很難保證水平二是對(duì)小字要敏感收據(jù)底部的明細(xì)行字號(hào)可能只有幾個(gè)像素高三是要能區(qū)分真正的文字區(qū)域和分隔線、logo、條形碼這些干擾元素。我實(shí)測(cè)下來基于DBNet思路的輕量檢測(cè)模型在收據(jù)上表現(xiàn)比較均衡。輸入分辨率建議控制在960×960以內(nèi)再大推理時(shí)間會(huì)明顯上升而收據(jù)文字本身不需要那么高的分辨率。檢測(cè)閾值二值化閾值要調(diào)低一點(diǎn)大概0.2到0.3之間因?yàn)闊崦艏堊舟E淡閾值太高會(huì)漏檢。這個(gè)參數(shù)沒有萬能值最好準(zhǔn)備幾張典型收據(jù)做驗(yàn)證集手動(dòng)掃一遍閾值找最優(yōu)點(diǎn)。2.2 識(shí)別模型把每個(gè)文本框轉(zhuǎn)成字符串檢測(cè)出來的每個(gè)文本框裁剪出來送進(jìn)識(shí)別模型。識(shí)別模型通常是CRNN或者基于Transformer的序列識(shí)別結(jié)構(gòu)輸出字符序列。收據(jù)識(shí)別的字符集要特別設(shè)計(jì)數(shù)字、小數(shù)點(diǎn)、貨幣符號(hào)、常見漢字、英文字母再加上一些收據(jù)上高頻出現(xiàn)的符號(hào)如星號(hào)、斜杠、冒號(hào)。字符集越小模型越容易收斂推理也越快。這里有個(gè)容易忽略的點(diǎn)收據(jù)上的數(shù)字識(shí)別準(zhǔn)確率直接決定金額字段對(duì)不對(duì)而數(shù)字恰恰是最容易混淆的0和O、1和l、5和S、8和B。我的做法是在訓(xùn)練數(shù)據(jù)里刻意增加數(shù)字樣本的權(quán)重并且在識(shí)別后處理階段加一層數(shù)字糾錯(cuò)規(guī)則——比如金額字段里如果出現(xiàn)字母O大概率是數(shù)字0。這種規(guī)則簡單但有效能救回不少邊緣case。2.3 兩個(gè)模型如何串成完整流水線整條流水線的順序是圖像預(yù)處理 → 檢測(cè) → 文本框排序 → 逐框識(shí)別 → 結(jié)構(gòu)化解析。排序這一步很關(guān)鍵但常被忽視。檢測(cè)模型輸出的文本框是無序的而收據(jù)是有閱讀順序的從上到下從左到右。如果不排序識(shí)別結(jié)果就是一堆亂序字符串后處理根本沒法做。排序算法我推薦用先按y坐標(biāo)聚類成行再在行內(nèi)按x坐標(biāo)排序的策略。具體做法是把所有文本框按中心點(diǎn)y坐標(biāo)排序然后遍歷如果當(dāng)前框和上一個(gè)框的y坐標(biāo)差值小于某個(gè)閾值比如框高的一半就認(rèn)為是同一行。這樣能處理輕微傾斜的情況。對(duì)于傾斜嚴(yán)重的收據(jù)最好先做一次透視校正把收據(jù)擺正再檢測(cè)排序會(huì)簡單很多。// 文本框按行聚類再排序的簡化實(shí)現(xiàn) function sortTextBoxes(boxes) { // 按中心點(diǎn)y排序 const sorted [...boxes].sort((a, b) a.cy - b.cy); const lines []; let currentLine [sorted[0]]; for (let i 1; i sorted.length; i) { const box sorted[i]; const lastBox currentLine[currentLine.length - 1]; const threshold Math.max(box.height, lastBox.height) * 0.5; if (Math.abs(box.cy - lastBox.cy) threshold) { currentLine.push(box); } else { lines.push(currentLine); currentLine [box]; } } lines.push(currentLine); // 每行內(nèi)按x排序 return lines.flatMap(line line.sort((a, b) a.cx - b.cx) ); }3. WebAssembly與WebGPU兩條推理后端的取舍3.1 兩種后端的能力邊界在哪里WebAssembly后端是保底方案幾乎所有現(xiàn)代瀏覽器都支持兼容性沒得說。它跑在CPU上通過SIMD指令和多線程需要SharedArrayBuffer支持能獲得不錯(cuò)的性能。缺點(diǎn)是CPU推理終究有上限模型稍大一點(diǎn)延遲就上去了而且會(huì)占用主線程資源處理大圖時(shí)頁面可能卡頓。WebGPU后端是性能方案直接把計(jì)算丟給GPU。對(duì)于卷積密集的檢測(cè)和識(shí)別模型加速比通常能到3到10倍具體取決于模型結(jié)構(gòu)和GPU性能。但WebGPU的瀏覽器支持還在鋪開階段而且不同廠商的GPU驅(qū)動(dòng)質(zhì)量參差不齊偶爾會(huì)遇到算子不支持或者結(jié)果異常的情況。我的策略是運(yùn)行時(shí)探測(cè)優(yōu)先嘗試WebGPU初始化失敗或者推理結(jié)果異常時(shí)自動(dòng)降級(jí)到WebAssembly保證功能始終可用。3.2 后端切換的探測(cè)與降級(jí)邏輯探測(cè)邏輯不能只看navigator.gpu是否存在那個(gè)只能說明API可用不代表能跑通你的模型。穩(wěn)妥的做法是初始化時(shí)用一個(gè)極小的測(cè)試張量跑一次推理驗(yàn)證輸出是否在合理范圍內(nèi)。如果這一步失敗或者超時(shí)就切到WebAssembly。async function selectBackend() { if (!navigator.gpu) return wasm; try { const adapter await navigator.gpu.requestAdapter(); if (!adapter) return wasm; // 用微型模型做一次冒煙測(cè)試 const ok await smokeTest(webgpu); return ok ? webgpu : wasm; } catch (e) { console.warn(WebGPU初始化失敗降級(jí)到WASM, e); return wasm; } }注意冒煙測(cè)試一定要設(shè)超時(shí)。有些設(shè)備上WebGPU初始化會(huì)卡住不返回沒有超時(shí)保護(hù)的話整個(gè)應(yīng)用就掛起了。我一般設(shè)3秒超時(shí)超時(shí)直接判定為不可用。3.3 實(shí)測(cè)性能對(duì)比與內(nèi)存占用在一臺(tái)中端筆記本集成顯卡上我用同一張1200×1600的收據(jù)照片做了對(duì)比測(cè)試。WebAssembly單線程下整條流水線約2.8秒開啟多線程后降到1.4秒左右。WebGPU下約450毫秒提升非常明顯。內(nèi)存占用方面WebAssembly峰值約180MBWebGPU約220MBGPU方案略高但可接受。后端配置檢測(cè)耗時(shí)識(shí)別耗時(shí)總耗時(shí)峰值內(nèi)存WASM單線程1.6s1.2s2.8s180MBWASM多線程0.8s0.6s1.4s195MBWebGPU0.25s0.2s0.45s220MB需要說明的是WebGPU的首次推理會(huì)包含著色器編譯開銷可能比后續(xù)推理慢好幾倍。所以第一次掃描感覺慢是正常的第二次開始就快了??梢栽趹?yīng)用啟動(dòng)時(shí)用一張占位圖預(yù)熱一次把編譯開銷提前消化掉。4. 圖像預(yù)處理決定識(shí)別率上限的關(guān)鍵環(huán)節(jié)4.1 透視校正讓傾斜收據(jù)站直用戶拍收據(jù)很少能拍得方方正正透視畸變會(huì)讓文字行變成斜線直接影響檢測(cè)和排序。校正的思路是找到收據(jù)的四個(gè)角點(diǎn)然后做透視變換把它映射成矩形。角點(diǎn)檢測(cè)可以用邊緣檢測(cè)加輪廓擬合也可以讓用戶手動(dòng)點(diǎn)四個(gè)角體驗(yàn)差但準(zhǔn)確。自動(dòng)方案我推薦用輪廓面積篩選找出圖像中最大的四邊形輪廓大概率就是收據(jù)邊界。變換之后收據(jù)就擺正了后續(xù)檢測(cè)和排序都省事。這一步的收益非常大我實(shí)測(cè)在傾斜30度的收據(jù)上校正前后識(shí)別準(zhǔn)確率能差20個(gè)百分點(diǎn)以上。校正的代價(jià)是一次矩陣運(yùn)算開銷可以忽略。4.2 灰度化、二值化與對(duì)比度增強(qiáng)的組合拳熱敏紙收據(jù)的通病是對(duì)比度低字和背景灰度接近。直接送進(jìn)模型檢測(cè)模型可能連框都找不準(zhǔn)。預(yù)處理要做幾件事先灰度化降維再做自適應(yīng)直方圖均衡CLAHE提升局部對(duì)比度最后根據(jù)情況決定是否二值化。二值化要謹(jǐn)慎。全局閾值二值化在光照不均的收據(jù)上會(huì)災(zāi)難性失敗——亮的地方全白暗的地方全黑。如果一定要二值化用自適應(yīng)閾值局部閾值每個(gè)像素根據(jù)鄰域計(jì)算閾值。但我更傾向于不做硬二值化而是把增強(qiáng)后的灰度圖直接送模型讓模型自己去學(xué)特征?,F(xiàn)代檢測(cè)識(shí)別模型對(duì)灰度輸入的魯棒性已經(jīng)足夠好硬二值化反而會(huì)丟失信息。4.3 分辨率與縮放的權(quán)衡輸入分辨率是個(gè)需要權(quán)衡的參數(shù)。分辨率太低小字糊成一團(tuán)識(shí)別不了分辨率太高推理時(shí)間線性增長內(nèi)存也吃不消。收據(jù)的特點(diǎn)是長條形寬高比可能到1:3甚至更極端。如果直接縮放到正方形要么寬度被壓扁要么高度被拉伸都會(huì)變形。我的做法是保持寬高比縮放讓長邊不超過一個(gè)上限比如1280短邊按比例算。如果短邊縮得太小導(dǎo)致小字不可讀就分段處理——把長收據(jù)切成上下幾段分別識(shí)別最后拼接結(jié)果。切分點(diǎn)選在空白行處避免把一行文字切斷。這個(gè)策略在處理超長超市小票時(shí)特別有用。5. 從識(shí)別文本到結(jié)構(gòu)化字段的解析實(shí)戰(zhàn)5.1 收據(jù)字段的抽取規(guī)則設(shè)計(jì)識(shí)別出來的是一行行文本要變成商戶名、日期、總金額這樣的結(jié)構(gòu)化數(shù)據(jù)得靠解析規(guī)則。收據(jù)的字段有幾個(gè)強(qiáng)特征可以利用總金額通常出現(xiàn)在合計(jì)總計(jì)應(yīng)付這類關(guān)鍵詞附近且是整張收據(jù)里數(shù)值最大的金額之一日期有固定的格式模式年月日、斜杠、橫杠分隔商戶名一般在收據(jù)頂部字號(hào)較大。我的解析策略是關(guān)鍵詞定位 正則匹配 數(shù)值校驗(yàn)三層。先用關(guān)鍵詞找到候選行再用正則從候選行里摳出具體值最后做合理性校驗(yàn)比如日期不能是未來、金額不能為負(fù)、明細(xì)項(xiàng)之和應(yīng)該接近總金額。校驗(yàn)這層能過濾掉大量誤識(shí)別。5.2 金額與日期的正則匹配細(xì)節(jié)金額的正則要兼容多種寫法12.34、12,34、¥12.34、12.34元、-12.34退款。我用的模式大致是/[-¥]?\s*(\d{1,3}(?:[,]\d{3})*(?:[.。]\d{2})?)/注意小數(shù)點(diǎn)可能被識(shí)別成中文句號(hào)千分位可能是中文逗號(hào)這些都要覆蓋。日期更麻煩格式五花八門2024-01-15、2024/01/15、2024年1月15日、15/01/2024日月順序還可能顛倒。我的做法是準(zhǔn)備一組正則模式逐個(gè)嘗試匹配成功后再用日期庫校驗(yàn)合法性。對(duì)于日月順序歧義的情況比如03/04/2024優(yōu)先按本地習(xí)慣解析同時(shí)保留原始字符串供用戶確認(rèn)。const amountPattern /[-¥]?\s*(\d{1,3}(?:[,]\d{3})*(?:[.。]\d{1,2})?)/g; const datePatterns [ /(\d{4})[-/年.](\d{1,2})[-/月.](\d{1,2})/, /(\d{1,2})[-/](\d{1,2})[-/](\d{4})/, ]; function normalizeAmount(str) { return parseFloat( str.replace(/[,]/g, ) .replace(/[。]/g, .) .replace(/[¥\s]/g, ) ); }5.3 明細(xì)條目的對(duì)齊與金額校驗(yàn)收據(jù)的明細(xì)行通常是商品名 ... 金額的格式中間用空格或點(diǎn)號(hào)填充。識(shí)別后這些填充符可能變成各種奇怪字符需要?dú)w一化。我的做法是把連續(xù)的非字母數(shù)字字符除了小數(shù)點(diǎn)都當(dāng)作分隔符然后取最后一個(gè)數(shù)值作為該行金額前面的作為商品名。校驗(yàn)環(huán)節(jié)是提升可信度的關(guān)鍵。把所有明細(xì)金額加起來和識(shí)別出的總金額比對(duì)如果差值在合理范圍比如幾分錢以內(nèi)考慮四舍五入就認(rèn)為解析可信如果差得離譜說明某處識(shí)別錯(cuò)了標(biāo)記出來讓用戶復(fù)核。這個(gè)機(jī)制能顯著降低錯(cuò)誤數(shù)據(jù)進(jìn)入賬本的概率。6. 工程化落地中的性能與體驗(yàn)優(yōu)化6.1 模型加載與緩存策略模型文件動(dòng)輒幾MB到幾十MB每次打開頁面都重新下載體驗(yàn)很差。用Cache API或者IndexedDB把模型緩存到本地第二次打開直接讀緩存。緩存要帶版本號(hào)模型更新時(shí)能正確失效。加載過程要有進(jìn)度反饋用戶看到進(jìn)度條在動(dòng)才不會(huì)以為卡死。WebAssembly的編譯也有開銷可以用WebAssembly.compileStreaming邊下載邊編譯比先下載完再編譯快不少。如果模型支持分片還可以做懶加載——檢測(cè)模型先加載識(shí)別模型在檢測(cè)出文本框后再加載讓首屏更快。6.2 大圖處理與內(nèi)存控制手機(jī)拍的照片動(dòng)輒4000×3000直接送進(jìn)模型內(nèi)存直接爆。預(yù)處理階段就要把圖縮到合理尺寸并且及時(shí)釋放中間張量。LiteRT.js的張量對(duì)象用完要顯式釋放不然內(nèi)存會(huì)持續(xù)增長直到頁面崩潰。我踩過這個(gè)坑連續(xù)掃十幾張圖后頁面就卡死了排查半天才發(fā)現(xiàn)是張量沒釋放。處理大圖的另一個(gè)技巧是用OffscreenCanvas在Worker里做預(yù)處理避免阻塞主線程。整個(gè)OCR流程其實(shí)都適合放進(jìn)Worker主線程只負(fù)責(zé)UI和結(jié)果展示這樣即使推理耗時(shí)較長頁面也不會(huì)失去響應(yīng)。6.3 用戶交互中的容錯(cuò)設(shè)計(jì)再好的模型也會(huì)有識(shí)別錯(cuò)的時(shí)候交互設(shè)計(jì)要承認(rèn)這一點(diǎn)。我的做法是識(shí)別結(jié)果全部可編輯用戶能直接改。對(duì)于低置信度的字段模型輸出的概率低于閾值用顏色標(biāo)出來提示用戶重點(diǎn)核對(duì)。金額字段做二次確認(rèn)因?yàn)榻痤~錯(cuò)了后果最嚴(yán)重。還要處理識(shí)別失敗的情況。如果檢測(cè)不到任何文本框或者解析不出關(guān)鍵字段不要直接報(bào)錯(cuò)而是引導(dǎo)用戶重拍——提示可能是光線太暗、角度太斜、或者收據(jù)沒放平。給出具體的改進(jìn)建議比一句識(shí)別失敗有用得多。7. 踩過的坑與實(shí)測(cè)經(jīng)驗(yàn)匯總7.1 WebGPU結(jié)果異常但無報(bào)錯(cuò)的隱蔽問題最坑的一次是WebGPU在某些設(shè)備上推理不報(bào)錯(cuò)但輸出全是NaN或者亂碼。這種問題最難查因?yàn)榱鞒炭雌饋硗耆!:髞戆l(fā)現(xiàn)是某些GPU驅(qū)動(dòng)對(duì)特定算子實(shí)現(xiàn)有bug。解決辦法就是前面說的冒煙測(cè)試——初始化時(shí)跑一次已知輸入的推理驗(yàn)證輸出是否符合預(yù)期不符合就降級(jí)。這個(gè)檢查一定要做否則線上會(huì)出現(xiàn)一批用戶識(shí)別結(jié)果全錯(cuò)但沒有任何錯(cuò)誤日志的情況。7.2 中文標(biāo)點(diǎn)與全角字符的歸一化中文環(huán)境下識(shí)別結(jié)果里混入全角字符是常態(tài)全角數(shù)字、全角小數(shù)點(diǎn)、全角逗號(hào)。這些字符直接參與數(shù)值解析會(huì)失敗。必須在解析前做一次歸一化把全角轉(zhuǎn)半角。這個(gè)轉(zhuǎn)換要覆蓋數(shù)字、字母、標(biāo)點(diǎn)用Unicode碼點(diǎn)范圍判斷即可。我一開始漏了全角小數(shù)點(diǎn)導(dǎo)致一批金額解析失敗排查了好久。7.3 多線程WASM的兼容性陷阱WebAssembly多線程依賴SharedArrayBuffer而SharedArrayBuffer需要特定的響應(yīng)頭COOP和COEP才能啟用。如果你的部署環(huán)境沒法設(shè)置這些頭多線程就用不了只能退回單線程。這個(gè)限制在開發(fā)環(huán)境容易忽略因?yàn)楸镜亻_發(fā)服務(wù)器可能默認(rèn)就帶了這些頭一上線就發(fā)現(xiàn)多線程失效。部署前一定要確認(rèn)響應(yīng)頭配置。7.4 模型量化帶來的精度損失為了減小體積和加速模型通常會(huì)做量化比如從float32量化到int8。量化能帶來2到4倍的速度提升和體積縮減但精度會(huì)有損失。收據(jù)識(shí)別對(duì)數(shù)字精度敏感量化后可能出現(xiàn)原本能認(rèn)對(duì)的數(shù)字認(rèn)錯(cuò)了。我的經(jīng)驗(yàn)是檢測(cè)模型可以大膽量化識(shí)別模型要謹(jǐn)慎最好在量化后做一輪驗(yàn)證集測(cè)試確認(rèn)精度損失在可接受范圍內(nèi)再用。如果損失太大考慮用混合量化對(duì)精度敏感的層保持高精度。8. 后續(xù)可以繼續(xù)深挖的方向這套方案跑通之后還有不少可以優(yōu)化的空間。比如引入版面分析模型自動(dòng)識(shí)別收據(jù)的表格結(jié)構(gòu)對(duì)明細(xì)條目的解析會(huì)更準(zhǔn)。再比如做端上的增量學(xué)習(xí)把用戶手動(dòng)修正的結(jié)果收集起來定期微調(diào)模型讓識(shí)別率隨使用逐步提升。還有多語言支持不同地區(qū)的收據(jù)格式差異很大字符集和解析規(guī)則都要適配。性能上模型蒸餾是個(gè)方向用大模型教小模型在保持精度的同時(shí)進(jìn)一步壓縮體積。WebGPU的算子優(yōu)化也值得跟進(jìn)隨著瀏覽器實(shí)現(xiàn)成熟能解鎖的加速空間還很大。我個(gè)人比較看好的是把整條流水線做成可插拔的模塊檢測(cè)、識(shí)別、解析各自獨(dú)立方便針對(duì)不同場景替換組件——畢竟收據(jù)只是文檔識(shí)別的一個(gè)子集同一套框架稍微調(diào)整就能用到發(fā)票、名片、表單上。