處理:讓LLM讀取掃描件的關(guān)鍵一步)
前陣子幫同事處理一份掃描版項(xiàng)目立項(xiàng)書PDF接近一百頁頁面上是清晰可讀的漢字但鼠標(biāo)劃線就是選不中復(fù)制出來全是空白。同事想直接丟給大語言模型LLM做摘要結(jié)果模型告訴我“無法從圖片中提取文字”。那一刻我意識到OCR并不是一個(gè)可有可無的預(yù)處理步驟而是決定LLM任務(wù)能否成立的第一道關(guān)卡。這個(gè)場景并不算少見。越來越多的知識庫、合同、檔案、行業(yè)報(bào)告仍以掃描件或圖片格式存在。它們不是秘密也不是不存在只是沒法被直接復(fù)制、檢索、喂給模型。沒有OCR這些文檔等于“看得見摸不著”有了OCR它們才能進(jìn)入LLM的上下文成為可計(jì)算、可提問、可生成的內(nèi)容資產(chǎn)。我后來把整套處理邏輯整理成了一個(gè)簡單的流水線并給它取了個(gè)名字叫“OCR It”從無法復(fù)制的文檔中提取文本供你的LLM使用。這篇博客想講的不只是怎么安裝一個(gè)OCR工具而是為什么這條流水線的關(guān)鍵點(diǎn)不在識別字符而在如何提供一份干凈的、結(jié)構(gòu)完整的、LLM真正消費(fèi)得了的文本。換句話說OCR不是為了存檔而是為了把文檔從“視覺對象”翻譯成“語言模型的輸入”。1. 為什么“無法復(fù)制的文檔”正在成為LLM工作流的隱形瓶頸1.1 從復(fù)制到理解文檔里其實(shí)有“兩層文本”一個(gè)很常見的誤解是只要是PDF里面的文字就應(yīng)該能復(fù)制。實(shí)際上PDF里的文字存在兩種完全不同的形態(tài)。第一種是“文本型PDF”。文件里本身就包含了字符編碼和字體信息就像Word導(dǎo)出的PDF一樣鼠標(biāo)可以選詞CtrlC可以復(fù)制。這種情況下解析PDF是最簡單的事很多Python庫都能直接抽取文本根本不需要OCR。第二種是“圖像型PDF”。每一頁其實(shí)是一張圖片可能是掃描儀生成的也可能是打印后重新拍照的。它看起來有文字但底層只有像素沒有任何字符信息。你想要復(fù)制復(fù)制出來的當(dāng)然是空白或亂碼。LLM能理解什么它能理解的是token是文本序列。哪怕現(xiàn)在出現(xiàn)了多模態(tài)大模型能直接讀圖片多數(shù)商業(yè)化工作流里仍然會(huì)把文本抽取作為前置步驟。因?yàn)槲谋靖p、更便宜、更容易緩存和處理。所以“無法復(fù)制的文檔”對LLM來說幾乎等于不存在的文檔。OCR就是把這層阻斷打開的鑰匙。1.2 掃描件、圖片PDF和拍照件處理難度完全不同很多人覺得OCR就是“拿個(gè)工具掃一下”。但實(shí)際接觸后會(huì)發(fā)現(xiàn)不同來源的圖片難度完全不是一個(gè)量級。我一般會(huì)把需要處理的文檔分成三類文檔類型特征典型處理難度常見策略掃描型PDF掃描儀生成文字較正分辨率較高相對容易直接頁面轉(zhuǎn)圖片后OCR圖片型PDF由JPG、PNG等圖片直接合成PDF中等必要時(shí)先增強(qiáng)圖像再OCR手機(jī)拍照件有透視、陰影、反光、傾斜困難需要先矯正、裁剪、增強(qiáng)如果面對的是一批質(zhì)量參差不齊的拍照件直接調(diào)用OCR接口往往會(huì)出現(xiàn)很多錯(cuò)誤。這時(shí)候不要急著調(diào)整識別參數(shù)而要先處理圖像質(zhì)量。這個(gè)原則在后面的工程化部分會(huì)反復(fù)出現(xiàn)。1.3 LLM對輸入文本的要求不是“能讀”而是“好用”O(jiān)CR工具輸出的原始文本很多時(shí)候并不是LLM真正想吃的格式。比如段落被硬拆成一行一行頁眉頁腳混在正文里多欄新聞稿被讀成左右穿插表格單元格順序混亂識別結(jié)果里夾雜大量空格和換行。如果直接把這種文本丟給LLM模型可能也能回答但回答質(zhì)量會(huì)明顯下降。因?yàn)長LM非常依賴上下文連貫性一個(gè)被切碎的文本會(huì)讓模型抓不住主謂賓關(guān)系甚至把不同欄目的內(nèi)容混在一起理解。所以我建議把OCR之后的文本處理當(dāng)成一個(gè)獨(dú)立環(huán)節(jié)。OCR負(fù)責(zé)“識別”文本處理負(fù)責(zé)“組織”。只有兩者都做好了LLM才能拿到結(jié)構(gòu)完整、邏輯清晰的輸入。這也是“OCR It”理念里最容易被忽略的部分。2. 一條從文檔到LLM的OCR提取流水線2.1 先判斷文檔類型再選OCR策略拿到一個(gè)文檔后不要直接寫代碼先花30秒做判斷。如果文檔是PDF優(yōu)先檢查它是否有文本層。最簡單的方法是用PDF閱讀器嘗試選擇文字或者用pdftotext命令直接抽取pdftotext sample.pdf sample.txt如果抽取出來的文件是空的或者只有少量頁眉頁腳那么這個(gè)PDF多半是圖像型需要進(jìn)入OCR流程。如果原始材料是圖片比如PNG、JPG、微信截圖就跳過PDF解析直接做圖像預(yù)處理。具體來說要檢查分辨率夠不夠文字區(qū)域是否小于200像素高有沒有明顯傾斜有沒有陰影、反光、水印干擾頁面是否是多欄排版。2.2 最小可用的本地OCR流程在常見的開源方案里Tesseract仍然是很多人的入門選擇。雖然它面對復(fù)雜中文文檔時(shí)不一定是最優(yōu)解但勝在免費(fèi)、跨平臺、容易上手。下面是一個(gè)管道示意Python環(huán)境下比較常見import pdf2image import pytesseract from PIL import Image # PDF頁面轉(zhuǎn)圖像 images pdf2image.convert_from_path(scan.pdf, dpi200) # 逐頁識別 for i, img in enumerate(images): text pytesseract.image_to_string(img, langchi_sim) print(f--- Page {i1} ---) print(text)這段代碼是示意結(jié)構(gòu)實(shí)際運(yùn)行前需要安裝Tesseract引擎和中文語言包同時(shí)確認(rèn)路徑配置正確。在macOS上可以用Homebrew安裝在Ubuntu上可以用apt install tesseract-ocr tesseract-ocr-chi-simWindows上則需要下載安裝包。如果你更愿意用PaddleOCR也可以獲得更好的中文版面效果但依賴安裝會(huì)更重一些。初期不必糾結(jié)選哪個(gè)先用最小流程跑通一頁再根據(jù)結(jié)果決定是否換引擎。2.3 讓OCR結(jié)果可以直接交給LLM識別完文本只是完成了一半。我們要把輸出整理成適合LLM閱讀的結(jié)構(gòu)。一種實(shí)用的做法是給每一頁加上頁碼標(biāo)記并把連續(xù)的行合并成段落。比如# Page 1 項(xiàng)目立項(xiàng)書 本項(xiàng)目的目標(biāo)是構(gòu)建一套基于OCR的文檔處理流水線 用于將掃描件中的文本提取并交給大語言模型使用。 # Page 2 第一章 背景與意義 ...這樣的結(jié)構(gòu)至少解決了三個(gè)問題頁碼保留方便追溯段落合并避免上下文斷裂Markdown層級讓LLM更容易識別結(jié)構(gòu)。如果直接使用云端或本地API還可以把這段整理后的文本作為system或user消息的一部分傳入然后再提具體要求比如“根據(jù)以上內(nèi)容生成摘要”。經(jīng)過整理的OCR文本模型的理解效果通常比直接喂原始OCR輸出好很多。3. 關(guān)鍵選擇本地OCR、云端OCR還是離線模型3.1 三種方案的對比在實(shí)際項(xiàng)目中OCR的選型通常不是技術(shù)潔癖問題而是成本、隱私和精度的權(quán)衡。方案優(yōu)點(diǎn)缺點(diǎn)適合場景本地開源OCRTesseract、PaddleOCR免費(fèi)、離線、可控、隱私好中文準(zhǔn)確率依賴模型和預(yù)處理個(gè)人使用、敏感文檔、小批量云端OCR服務(wù)各類商業(yè)API準(zhǔn)確率高、接口簡單、支持復(fù)雜版面有費(fèi)用、有并發(fā)限制、數(shù)據(jù)離開本機(jī)大批量、通用文檔、團(tuán)隊(duì)工具多模態(tài)LLM直接讀圖可理解版面、語義更強(qiáng)成本高、速度慢、不穩(wěn)定復(fù)雜表格、少樣本、混合場景選擇哪種方案取決于你的“無法復(fù)制的文檔”到底是什么形態(tài)以及你打算把它用在什么任務(wù)上。3.2 為什么我會(huì)先選本地OCR做小批量驗(yàn)證我個(gè)人的做法是當(dāng)項(xiàng)目剛起步手里只有幾十頁樣例時(shí)先不要開通任何付費(fèi)API也不要急著訓(xùn)練模型。先用本地OCR跑通全流程。這樣做有幾個(gè)實(shí)際好處沒有網(wǎng)絡(luò)請求排查問題更快輸出可以隨時(shí)打印出來檢查不用被接口文檔干擾如果本地OCR效果已經(jīng)足夠好就可以省下后續(xù)云服務(wù)費(fèi)用更關(guān)鍵的是你可以先建立一套“預(yù)處理→識別→清洗→喂模型”的流程框架后續(xù)哪怕?lián)Q成云端API也只是替換其中一環(huán)。本地OCR的缺點(diǎn)是準(zhǔn)確率可能不如大廠的商業(yè)服務(wù)。但很多時(shí)候先跑通流程比咬住那1%到2%的準(zhǔn)確率更重要。流程穩(wěn)定后再升級單點(diǎn)效果是更現(xiàn)實(shí)的路徑。3.3 批量場景下的成本、限流和隱私邊界一旦從單頁驗(yàn)證進(jìn)入批量處理問題就會(huì)從“效果”轉(zhuǎn)向“工程”。使用云端OCR時(shí)需要確認(rèn)接口的并發(fā)限制單條請求大小限制每月免費(fèi)額度響應(yīng)超時(shí)數(shù)據(jù)存儲策略。不能把所有頁面一次性循環(huán)調(diào)用否則很容易觸發(fā)限流。通常要加一個(gè)簡單的重試機(jī)制比如遇到429或者5xx時(shí)等待幾秒再重試。隱私邊界也要提前判斷。合同、病歷、身份證、內(nèi)部報(bào)告這類敏感內(nèi)容不適合未經(jīng)脫敏就上傳到云端。這種情況下本地OCR幾乎是唯一合規(guī)選擇。即使是本地模型也要注意模型運(yùn)行環(huán)境的安全而不是只看識別準(zhǔn)確率。4. 提高可復(fù)用性的幾個(gè)工程化細(xì)節(jié)4.1 預(yù)處理決定OCR上限的不是模型而是圖像質(zhì)量很多人把OCR效果差歸罪于模型不夠強(qiáng)但實(shí)際觀察下來大部分問題都出在圖像上。一張照片如果傾斜、曝光不均勻、文字模糊無論用多好的OCR引擎效果都會(huì)受限。常見且有效的操作包括轉(zhuǎn)灰度去掉顏色干擾清理背景降低陰影和斑點(diǎn)影響二值化讓文字和背景對比明顯傾斜矯正讓文字行保持水平提高分辨率確保小字能被識別。這些操作不一定每次都需要但遇到拍照件時(shí)值得先做一輪實(shí)驗(yàn)。你可以保存處理前后兩張圖把OCR結(jié)果分別跑一遍直觀看到差異。4.2 版面分析多欄、表格、頁眉頁腳要先處理中文文檔里最常見的版面問題是多欄。論文、報(bào)紙、甚至一些排版復(fù)雜的報(bào)告都是左右兩欄。直接把整頁圖丟給OCR兩個(gè)欄目的文字可能會(huì)被交錯(cuò)讀取。Tesseract里可以通過--psm參數(shù)設(shè)置頁面分割模式比如--psm 3自動(dòng)頁面分割但可能處理不好復(fù)雜版面--psm 4按單列文本處理--psm 6假設(shè)統(tǒng)一文本塊。PaddleOCR則帶有版面分析模型可以檢測標(biāo)題、段落、表格、圖片等區(qū)域。如果你的文檔類型比較固定比如只有合同或只有論文可以針對性地調(diào)整版面參數(shù)。在實(shí)際操作中我建議先用“自動(dòng)版面檢測”生成一個(gè)結(jié)構(gòu)預(yù)覽確認(rèn)欄目邊界和段落順序再?zèng)Q定是否拆分區(qū)域單獨(dú)識別。這一步看起來很笨但能省下后續(xù)大量清洗時(shí)間。4.3 輸出清洗把“文本塊”拼成“段落”O(jiān)CR輸出通常會(huì)有大量換行因?yàn)橐媸前次谋究蜉敵龅摹H绻愕哪繕?biāo)是讓LLM理解整段邏輯就必須把斷開的行重新拼起來。一種簡單的啟發(fā)式方法是如果當(dāng)前行以句號、問號、感嘆號結(jié)尾說明是段落結(jié)束保留換行如果當(dāng)前行末尾沒有任何標(biāo)點(diǎn)且下一行不是標(biāo)題就把兩行合并過濾掉頁眉、頁腳、頁碼比如只出現(xiàn)一次的內(nèi)容或純數(shù)字行??梢允褂谜齽t表達(dá)式處理也可以用規(guī)則腳本。更高級的做法是訓(xùn)練一個(gè)小模型判斷段落邊界但大多數(shù)項(xiàng)目沒有必要。清洗之后一定要輸出一份“清洗后文本”給人工抽檢不要只看前幾頁就認(rèn)為全部沒問題。4.4 把OCR封裝成可復(fù)用的函數(shù)為了讓整個(gè)流程可復(fù)用我建議把OCR從“腳本”升級成“函數(shù)”。def extract_text(file_path, page_start1, page_endNone): 從文檔中提取文本返回整理后的Markdown字符串。 這里只展示流程骨架具體參數(shù)需根據(jù)你的環(huán)境調(diào)整。 text_blocks [] images load_images(file_path, page_start, page_end) for page_no, img in enumerate(images, startpage_start): try: raw run_ocr(img) cleaned clean_ocr_output(raw) text_blocks.append(f# Page {page_no}\n\n{cleaned}) except Exception as e: text_blocks.append(f# Page {page_no}\n\n[OCR_ERROR] {e}) return \n.join(text_blocks)這樣做的好處是調(diào)用方不需要關(guān)心OCR細(xì)節(jié)異常能被捕獲并記錄后續(xù)換成云端API時(shí)只需要替換run_ocr內(nèi)部實(shí)現(xiàn)頁面級錯(cuò)誤不會(huì)中斷整個(gè)批次。這也是“OCR It”從一次性腳本走向工程化工具的關(guān)鍵一步。5. 實(shí)際落地時(shí)最常遇到的四個(gè)問題5.1 識別結(jié)果出現(xiàn)大量錯(cuò)字或亂碼排查順序不要亂先看原圖是否清晰。如果文字邊緣模糊先做圖像增強(qiáng)再確認(rèn)語言包和編碼。中文文檔要用中文語言包輸出編碼建議指定UTF-8檢查字體。如果文檔里是特殊字體可能不匹配訓(xùn)練數(shù)據(jù)最后再看OCR引擎配置。比如Tesseract的--oem、--psm是否適合當(dāng)前版面。如果錯(cuò)字集中在某些字符上例如“0”和“O”混淆“一”和“-”混淆可以通過后處理規(guī)則修復(fù)。但要注意不要濫用替換規(guī)則否則可能把正確字符改錯(cuò)。5.2 多欄文檔被當(dāng)成一欄讀取句子錯(cuò)亂這是報(bào)紙、論文、PPT講義里最容易出現(xiàn)的問題。第一步切換不同PSM模式觀察哪一檔接近正確欄序。第二步使用版面分析模型把左欄、右欄分別切出來再按“左上到右上”的順序拼接。第三步如果文檔結(jié)構(gòu)固定可以在圖像預(yù)處理階段按欄切割為后續(xù)LLM輸入保留清晰順序。有一個(gè)經(jīng)驗(yàn)可以分享不要指望一個(gè)通用設(shè)置解決所有文檔。先固定文檔類型再調(diào)整版面參數(shù)是更高效的路徑。5.3 表格和公式變成亂行OCR面對表格時(shí)通常表現(xiàn)較差。因?yàn)楸砀窭锏膯卧裎恢眯畔⒑苤匾兾谋拘蛄袝?huì)丟失這種二維關(guān)系。如果表格數(shù)量少可以不用OCR轉(zhuǎn)而使用專門的表格解析工具或者手動(dòng)錄入關(guān)鍵字段。如果表格很多可以考慮把表格區(qū)域單獨(dú)截取出來交給支持表格識別的服務(wù)或多模態(tài)模型。要理解的是OCR并不能“理解”表格它只是把文字按位置輸出。真正的表格結(jié)構(gòu)化需要額外的定位和行列推理。公式就更特殊了普通的OCR不擅長處理數(shù)學(xué)符號。如果文檔核心內(nèi)容是數(shù)學(xué)公式你需要考慮公式識別方案比如把公式區(qū)域?qū)С鰹長aTeX。5.4 長文檔超過LLM上下文限制現(xiàn)在主流LLM的上下文窗口在不斷增大但掃描件動(dòng)輒幾十上百頁依然可能超出限制。常用的策略是“分層摘要”按頁提取OCR文本對每頁文本做一次獨(dú)立摘要再把所有頁的摘要拼接成最終輸入如果需要細(xì)粒度答案可以在最終回答時(shí)附帶“引用頁碼”。這種方法不會(huì)丟失太多信息也能讓長文檔被“塞進(jìn)”上下文。另一個(gè)辦法是使用向量數(shù)據(jù)庫把每頁文本切塊并嵌入讓LLM先檢索相關(guān)頁面再回答。但這就是更完整的RAG工程了需要額外搭建。6. 從單次嘗試到長期使用的判斷框架6.1 一個(gè)三次迭代的落地方法先跑通、再優(yōu)化、最后工程化面對OCRLLM項(xiàng)目我的建議永遠(yuǎn)是分三步走。第一步跑通。拿3到5頁文檔寫一個(gè)最簡單的腳本用默認(rèn)配置識別把結(jié)果打印出來。目標(biāo)是確認(rèn)流程沒有斷能輸出文本即可。第二步優(yōu)化。根據(jù)第一步輸出的問題調(diào)整圖像預(yù)處理、版面參數(shù)和文本清洗規(guī)則。每次只改一個(gè)變量重新評估。最多迭代幾次就能找到適合自己的配置組合。第三步工程化。把腳本封裝成函數(shù)通過命令行或API輸入文件路徑輸出整理后的Markdown記錄日志并處理異常。如果文檔量很大再加入任務(wù)隊(duì)列、并發(fā)控制和失敗重試。這三步不要跳步。很多人一上來就想做整個(gè)RAG系統(tǒng)結(jié)果數(shù)據(jù)源還沒清洗干凈后面全部白費(fèi)。6.2 適用邊界與不適合場景“OCR It”并不是萬能的。它適合解決的是“可讀但不可復(fù)制”的文檔比如清晰掃描件、截圖、高質(zhì)量拍照件。它不適合以下幾個(gè)場景低分辨率的監(jiān)控截圖、模糊的傳真件手寫文字密集的文檔大量復(fù)雜公式的數(shù)學(xué)論文需要高精度證據(jù)鏈比如法院卷宗、病歷原件。在這些場景里OCR可以作為輔助檢索工具但絕不能作為唯一事實(shí)來源。要讓LLM基于OCR結(jié)果做嚴(yán)肅判斷必須保留人工復(fù)核環(huán)節(jié)。6.3 真正的判斷OCR是為LLM準(zhǔn)備“干凈的上下文”回到開頭的場景。那份一百頁的掃描版立項(xiàng)書如果不經(jīng)過OCRLLM一點(diǎn)忙都幫不上。但經(jīng)過OCR后能不能直接得到一份完美摘要也不一定。識別過程中的分段錯(cuò)誤、表格丟失、版面混亂都會(huì)影響最終結(jié)果。所以我更愿意把OCR看作“文檔進(jìn)入LLM之前的數(shù)據(jù)治理層”。它真正要解決的問題不只是把字符取出來而是讓取出來的文本變成可追溯、可切分、可理解的結(jié)構(gòu)化上下文。你越早意識到這一點(diǎn)就越不會(huì)迷信某個(gè)OCR工具而是會(huì)花精力去搭建一條穩(wěn)定、可維護(hù)、能應(yīng)對真實(shí)臟數(shù)據(jù)的流水線。如果你手里也有一批“看得見、復(fù)制不了”的文檔別急著丟給一個(gè)在線工具。先選定3頁樣例跑通本地OCR整理成一頁干凈的Markdown再丟給LLM試一次。這一步走通之后后面才談得上批量、自動(dòng)化和工程化。