Hoá đơn, giấy tờ, biên bản scan hay ảnh chụp bằng điện thoại vẫn là nguồn dữ liệu lớn nhất mà doanh nghiệp chưa đưa được vào hệ thống. Công nghệ OCR (nhận dạng ký tự quang học) biến ảnh chứa chữ thành văn bản máy đọc được, nhưng với tiếng Việt, nơi một dấu thanh đổi nghĩa cả từ, "chạy được" khác xa "dùng được". Bài này tự dựng một bộ ba tài liệu giả lập (hoá đơn, thông báo, bảng giá), làm xấu chúng ở hai mức, rồi đo lỗi ký tự (CER) và lỗi từ (WER) của Tesseract và PaddleOCR. Sau đó đo từng bước tiền xử lý ảnh bằng OpenCV, từng chế độ chia trang --psm, và hậu xử lý để khôi phục dấu và kiểm tra chéo số tiền.
Bài dùng cách đo ở Đánh giá mô hình phân loại: precision, recall, ROC và dữ liệu lệch và nối tiếp bài Speech-to-text tiếng Việt với Whisper, nơi cũng dùng WER. Môi trường: Apple M4, 16 GB, Python 3.12, Tesseract 5.x (engine LSTM), PaddleOCR 2.8.0, OpenCV 4.10, ONNX Runtime 1.18.1. Mọi con số là output thật của máy này; mã nằm trong code/s7-30.
Bài toán OCR tiếng Việt khó ở đâu
Ba điểm khiến bài toán OCR tiếng Việt khó hơn OCR tiếng Anh. Thứ nhất, hệ chữ viết có 134 chữ cái mang dấu hoặc mũ (đếm cả chữ hoa lẫn chữ thường, như trong diacritics.py bên dưới), nhiều dấu chồng lên nhau như "ậ", "ể". Thứ hai, dấu nằm sát dòng trên và dòng dưới nên ảnh độ phân giải thấp làm dấu dính vào nét chữ. Thứ ba, nhiều mô hình huấn luyện sẵn được xây cho tập ký tự rộng nhưng không phủ đủ dấu tiếng Việt; phần đo bên dưới cho thấy chuyện này xảy ra thật với một bộ mô hình phổ biến.
Một hệ thống OCR hoàn chỉnh gồm ba khâu: phát hiện vùng chữ, nhận dạng chuỗi ký tự trong từng vùng, rồi hậu xử lý. Tesseract (engine LSTM từ bản 4) nhận dạng theo dòng, tự phân tích bố cục trang. PaddleOCR tách rõ hai mô hình: mô hình phát hiện và mô hình nhận dạng. Bài báo PP-OCRv3 (Li và cộng sự, 2022) mô tả bản cập nhật của hệ OCR siêu nhẹ này: phát hiện dùng LK-PAN, RSE-FPN, chưng cất DML; nhận dạng thay CRNN bằng SVTR_LCNet. Tác giả báo hmean cao hơn khoảng 5% so với PP-OCRv2 với tốc độ tương đương, nhưng bài báo không nói gì về đa ngôn ngữ; phần tiếng Việt nằm ở bộ mô hình đa ngôn ngữ riêng, xem mục sau.
Dữ liệu thử OCR: ba tài liệu, ba mức chất lượng
Để có đáp án chuẩn mà không đụng dữ liệu cá nhân, make_docs.py vẽ ba tài liệu tiếng Việt bằng Pillow (phông Arial): hoá đơn bán hàng với số tiền và thuế 8%, thông báo bảo trì, bảng giá dịch vụ. Tên cửa hàng, mã đơn, số tiền đều là bịa. Mỗi tài liệu đưa vào OCR có ba bản. clean là ảnh sạch 1500 px rộng. noisy mô phỏng ảnh chụp: nền đổ bóng, nhiễu Gauss, xoay 1,8 độ, làm mờ nhẹ, thu nhỏ còn 0,55 lần. harsh làm nặng hơn: bóng nặng, nhiễu mạnh, xoay 3 độ, mờ nhiều, thu còn 0,34 lần (ảnh chỉ rộng khoảng 510 px).
def degrade(img, harsh=False):
a = np.asarray(img, dtype=np.float32)
shade = 1.0 - (0.45 if harsh else 0.28) * (xx / w) - (0.25 if harsh else 0.12) * (yy / h)
a = a * shade + np.random.normal(0, 22 if harsh else 9, a.shape)
im = Image.fromarray(np.clip(a, 0, 255).astype(np.uint8))
im = im.rotate(3.0 if harsh else 1.8, resample=Image.BICUBIC, expand=True, fillcolor=200)
im = im.filter(ImageFilter.GaussianBlur(1.8 if harsh else 1.1))
k = 0.34 if harsh else 0.55
return im.resize((int(im.width * k), int(im.height * k)), Image.BILINEAR)Cần nói rõ giới hạn của phép đo OCR này: một phông chữ sạch, chữ in, chỉ ba tài liệu. Kết quả cho thấy xu hướng và cách đo, không phải độ chính xác bạn sẽ gặp trên chứng từ thật có dấu mộc, bảng kẻ, chữ viết tay. Với dự án thật, hãy gom vài chục ảnh thật (đã được phép) và gõ tay đáp án. Chỉ số: CER là khoảng cách chỉnh sửa theo ký tự chia số ký tự đáp án, WER tương tự theo từ, tính bằng thư viện jiwer sau khi chuẩn hoá Unicode NFC (nếu quên bước này, "ế" dựng sẵn và "e" cộng dấu rời bị coi là khác nhau).
Chạy OCR bằng Tesseract và PaddleOCR cho tiếng Việt
Tesseract dùng gói ngôn ngữ vie. Kho tessdata_fast chứa mô hình số nguyên 8 bit, README mô tả là thoả hiệp tốc độ/độ chính xác; kho tessdata_best là mô hình LSTM chính xác nhất, cả hai chỉ chạy với engine LSTM và dùng giấy phép Apache-2.0. Gói cài sẵn của Homebrew chỉ có tiếng Anh nên tôi trỏ TESSDATA_PREFIX tới thư mục chứa vie.traineddata của từng kho (ocr_lib.py):
def tesseract(path, tessdata="tessdata_fast", psm=6, lang="vie"):
env = dict(os.environ, TESSDATA_PREFIX=os.path.join(HERE, tessdata))
with tempfile.TemporaryDirectory() as d:
out = os.path.join(d, "o")
subprocess.run(["tesseract", path, out, "-l", lang, "--psm", str(psm)],
env=env, check=True, capture_output=True)
return nfc(open(out + ".txt", encoding="utf-8").read())Với PaddleOCR 2.8, tham số lang="vi" được ánh xạ sang nhóm latin: mô hình phát hiện en và mô hình nhận dạng latin của PP-OCRv3, tài liệu đa ngôn ngữ của PaddleOCR ghi "Vietnamese" là vi và PP-OCRv3 cho bản đa ngôn ngữ từ 5/2022. Có một trục trặc thật khi chạy OCR bằng PaddlePaddle: trên máy này, PaddlePaddle 2.6.x (bản wheel cho macOS arm64) treo ở bước phát hiện (predict_det) cả ở lần chạy thứ hai, tôi kill sau 50 giây. Thay vì bỏ cuộc, convert_to_onnx.sh đổi đúng hai mô hình mà PaddleOCR đã tải sang ONNX bằng paddle2onnx, rồi chạy bằng ONNX Runtime qua rapidocr-onnxruntime, dùng chính từ điển latin_dict.txt của gói. Cùng trọng số, khác runtime; tiền/hậu xử lý của RapidOCR có thể lệch đôi chút so với PaddleOCR() gốc, nên coi đây là "PP-OCRv3 latin chạy qua ONNX".
from rapidocr_onnxruntime import RapidOCR
engine = RapidOCR(det_model_path="onnx/det.onnx", rec_model_path="onnx/rec_latin.onnx",
rec_keys_path="dict/latin_dict.txt", use_cls=False)
res, _ = engine("docs/hoa_don_noisy.png", use_cls=False) # [(hộp, chuỗi, điểm tin cậy), ...]Kết quả OCR: CER, WER và thời gian
Bảng dưới là output của ocr_compare.py, trung bình trên ba tài liệu. "Tiền xử lý" là chuỗi OpenCV mô tả ở mục sau.
CER trung bình (%) trên 3 tài liệu; ngoặc là WER (%)
engine ảnh clean noisy harsh giây/ảnh
tesseract fast gốc 0.08 ( 0.4) 0.38 ( 1.9) 39.63 ( 62.2) 0.15
tesseract fast tiền xử lý 0.31 ( 1.5) 0.41 ( 2.0) 20.20 ( 53.3) 0.17
tesseract best gốc 0.89 ( 4.7) 0.65 ( 3.5) 36.51 ( 59.1) 0.30
tesseract best tiền xử lý 0.67 ( 4.0) 0.90 ( 4.4) 20.18 ( 58.2) 0.36
PP-OCRv3 latin gốc 16.71 ( 69.8) 21.82 ( 85.5) 24.70 ( 90.0) 1.08
PP-OCRv3 latin tiền xử lý 17.30 ( 71.2) 17.25 ( 72.1) 36.22 ( 90.8) 0.59Đọc kỹ thì có bốn điều không hiển nhiên:
- Tesseract gần như hoàn hảo trên chữ in sạch hoặc nhiễu vừa (CER dưới 1%) với tiếng Việt, kể cả mô hình
fast. Đừng kết luận "engine OCR này tốt hơn engine OCR kia" hay "Tesseract tốt hơn PaddleOCR": đây là chữ Arial in, đúng sở trường của một engine học trên phông chữ in. bestkhông tốt hơnfasttrên bộ dữ liệu này (CER 0,89% so với 0,08% trên ảnh sạch, chậm gấp đôi). Chênh vài phần mười phần trăm trên ba tài liệu nằm trong sai số; điều chắc chắn chỉ là trả thêm thời gian không mua được gì ở đây.- Khi ảnh xấu thật (harsh), mọi engine đều hỏng: Tesseract CER 36–40% trên ảnh gốc, tiền xử lý đưa xuống khoảng 20%, vẫn không dùng được cho tự động hoá OCR.
- PP-OCRv3 latin có CER 17% ngay cả trên ảnh sạch. Con số này là lỗi hệ thống chứ không phải nhiễu, và lý do nằm ở từ điển ký tự.
Thời gian là trung bình mỗi ảnh trên CPU, đo trong lúc máy còn chạy việc khác, nên chỉ nên đọc thứ tự cỡ phần mười giây: PP-OCRv3 chậm hơn Tesseract khoảng bốn đến bảy lần trên CPU. Cả tiến trình ocr_compare.py (44 giây) có đỉnh RAM 1.615.724.544 byte, khoảng 1,6 GB theo /usr/bin/time -l.
Vì sao OCR PaddleOCR "vi" mất dấu: kiểm tra bộ ký tự
Đầu ra thô của PP-OCRv3 trên hoá đơn "noisy" có dạng "Khäch hang: Tran Van Binh", "Töng tien hang". Có chữ cái, nhưng dấu thanh biến thành ký tự lạ hoặc mất. Nghi vấn: từ điển nhận dạng không chứa chữ cái tiếng Việt. diacritics.py kiểm tra bằng cách sinh đủ 134 chữ cái có dấu và đối chiếu với latin_dict.txt:
chữ cái tiếng Việt có dấu: 134 | có trong từ điển latin: 32 | thiếu: 102
thiếu (một phần): ÃÙĂăĨĩŨũƠơƯưẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻ
CER trung bình (%) trên 3 tài liệu, ảnh tiền xử lý: có dấu | bỏ dấu cả hai phía
engine mức có dấu bỏ dấu
tesseract fast clean 0.31 0.00
tesseract fast noisy 0.41 0.07
tesseract fast harsh 20.20 17.90
PP-OCRv3 latin clean 17.30 2.69
PP-OCRv3 latin noisy 17.25 3.45
PP-OCRv3 latin harsh 36.22 25.69Từ điển 186 ký tự này phủ được "à á â ã è é ê ì í ò ó ô õ ù ú ý Đ đ" (kế thừa từ tiếng Pháp, Bồ Đào Nha, Tây Ban Nha) nhưng thiếu hoàn toàn "ă ơ ư", mọi chữ có dấu hỏi, dấu nặng, và các tổ hợp như "ế", "ậ". Mô hình không thể xuất ký tự không có trong từ điển, nên nó chọn ký tự gần nhất. Khi bỏ dấu cả đáp án lẫn kết quả, CER của PP-OCRv3 trên ảnh sạch tụt từ 17,30% xuống 2,69%: phần lớn lỗi là mất dấu, còn nhận dạng nét chữ thì ổn. Đây là phát hiện từ đo đạc trên bản PaddleOCR 2.8 và mô hình PP-OCRv3 trong bài; tôi không có căn cứ để nói gì về các bản mô hình phát hành sau thời điểm này.
Bài học tổng quát cho mọi dự án OCR đa ngôn ngữ: trước khi đo độ chính xác, hãy kiểm tra tập ký tự của mô hình có phủ ngôn ngữ của bạn không. Bước này mất vài giây và tránh cả tuần tinh chỉnh vô vọng. Nếu cần PaddleOCR cho tiếng Việt, hướng đúng là huấn luyện lại phần nhận dạng với từ điển đủ dấu; phần này cần GPU và dữ liệu, chưa chạy trong bài.
Tiền xử lý ảnh trước khi OCR bằng OpenCV: bóc từng bước
Với OCR, chất lượng ảnh quyết định kết quả. Tài liệu Improving the quality of the output của Tesseract khuyên ảnh từ 300 DPI trở lên, xử lý nhiễu và nền không đều (Tesseract tự nhị phân hoá bằng Otsu nên kém khi nền tối không đồng đều), chỉnh nghiêng, và thêm viền nhỏ quanh chữ. Hàm preprocess làm đủ các bước: phóng 2 lần, san phẳng nền, khử nhiễu, nhị phân Otsu, chỉnh nghiêng, thêm viền 20 px.
img = cv2.resize(img, None, fx=2.0, fy=2.0, interpolation=cv2.INTER_CUBIC)
bg = cv2.medianBlur(cv2.morphologyEx(img, cv2.MORPH_CLOSE, np.ones((25, 25), np.uint8)), 21)
img = cv2.divide(img, bg, scale=255) # chia cho nền ước lượng: san phẳng đổ bóng
img = cv2.fastNlMeansDenoising(img, None, 12, 7, 21)
_, bw = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)Nhưng "làm đủ" chưa chắc tốt nhất. ablate.py cộng dồn từng bước và đo CER của Tesseract fast:
CER (%) Tesseract fast, --psm 6, cộng dồn từng bước
bước noisy harsh
ảnh gốc 0.38 39.63
+ phóng 2x 0.45 64.67
+ san phẳng nền 0.31 13.05
+ khử nhiễu 0.56 9.72
+ chỉnh nghiêng, không nhị phân 0.16 14.76
+ nhị phân Otsu (đủ bộ) 0.41 20.20Ba quan sát. Phóng to riêng lẻ có thể làm hỏng: harsh từ 39,63% lên 64,67% vì phóng cả bóng và nhiễu. San phẳng nền là bước đáng giá nhất (64,67% xuống 13,05%). Nhị phân Otsu làm tệ đi (14,76% lên 20,20% khi thêm nó vào bộ đã chỉnh nghiêng) vì Tesseract đã tự nhị phân hoá bên trong và Otsu chạy trên nét chữ mảnh đã mờ dễ làm đứt nét. Với ba tài liệu, chênh vài điểm phần trăm là nhiễu đo; hãy tin vào các bước nhảy lớn (phóng làm hỏng, san phẳng nền cứu) hơn là thứ tự bước kế tiếp. Nguyên tắc đo từng bước trên dữ liệu của bạn quan trọng hơn công thức cụ thể.
Chế độ chia trang --psm: tham số OCR quan trọng hơn bạn nghĩ
Theo trang man của Tesseract, --psm 3 là phân tích trang tự động (mặc định), 4 là một cột chữ, 6 là một khối chữ đồng nhất, 11 là chữ rải rác. Đo trên ảnh đã tiền xử lý:
psm clean noisy harsh
3 tự động 0.24 0.41 69.11
4 cột 0.24 0.41 74.49
6 khối 0.31 0.41 20.20
11 rời 0.24 0.41 42.94Trên ảnh sạch hoặc nhiễu vừa, chọn chế độ nào cũng như nhau. Trên ảnh xấu, bộ phân tích bố cục tự động tưởng nhiễu là cột chữ, kết quả CER 69%; chỉ định --psm 6 cho tài liệu một khối đưa về 20%. Lỗi này im lặng: không có cảnh báo nào, chỉ có văn bản sai. Với hoá đơn và biểu mẫu có cấu trúc cố định, hãy cắt vùng cần đọc (ví dụ ô tổng tiền) rồi dùng --psm 7 (một dòng) thay vì đọc cả trang.
Hậu xử lý: khôi phục dấu bằng từ điển
Hậu xử lý là nơi hệ thống OCR tiếng Việt thắng hay thua, và là phần ít người đo. Ý tưởng đơn giản: tiếng Việt là ngôn ngữ đơn lập, mỗi âm tiết là một đơn vị rời, nên có thể dựng từ điển âm tiết và sửa từng âm tiết sai dấu. postprocess.py dựng từ điển từ danh sách từ trong tệp vie.traineddata (trích bằng combine_tessdata và dawg2wordlist trong extract_wordlist.sh, 7.181 từ) cộng văn bản tiếng Việt của một bài trong series, được 4.678 âm tiết. Hàm restore_token có hai chế độ:
def restore_token(tok, aggressive=False):
low = unicodedata.normalize("NFC", tok.lower())
if low in cnt and not aggressive: cand = low # đã là âm tiết hợp lệ: giữ nguyên
else:
opts = by_plain.get(L.strip_marks(low)) # các âm tiết cùng chữ khi bỏ dấu
if not opts: return tok
cand = opts.most_common(1)[0][0] # chọn âm tiết có tần suất cao nhất
...Chế độ "giữ từ đúng" chỉ sửa âm tiết chưa có trong từ điển. Chế độ "chọn theo tần suất" bỏ dấu rồi luôn chọn biến thể thường gặp nhất. Kết quả (từ điển không có thuật ngữ riêng của tài liệu, CER trung bình, ảnh tiền xử lý):
engine mức thô giữ từ đúng chọn theo tần suất
tesseract fast clean 0.31 0.30 6.53
tesseract fast noisy 0.41 0.40 6.60
tesseract fast harsh 20.20 20.66 23.86
PP-OCRv3 latin clean 17.30 13.01 9.83
PP-OCRv3 latin noisy 17.25 14.21 9.91
PP-OCRv3 latin harsh 36.22 36.09 32.39Hậu xử lý OCR chỉ đáng dùng với engine mất dấu. Với PP-OCRv3, chế độ tần suất giảm CER từ 17,30% xuống 9,83% trên ảnh sạch. Với Tesseract đã đọc đúng dấu, cùng chế độ đó phá văn bản, CER tăng từ 0,31% lên 6,53%, vì nó đổi các từ đúng thành từ phổ biến hơn. Ví dụ thật từ postprocess_output.txt với PP-OCRv3 trên hoá đơn "noisy":
Khäch hang: Tran Vän Binh
=> Khách hàng: Trận Vẫn Bình
Töng thanh toán: 4.525.200d (Bón triêu nám tram hai muoi lam nghin hai trám dng)
=> Tổng thành toán: 4.525.200đ (Bốn triệu nằm trăm hai mười làm nghìn hai trăm dng)Thấy rõ ranh giới: "Tran" đúng là "Trần" nhưng từ điển chọn "Trận" vì tần suất; "Vän" thành "Vẫn" thay cho "Văn"; "thanh toán" thành "thành toán". Bỏ dấu là phép biến đổi mất thông tin, nên đoán lại cần ngữ cảnh (mô hình ngôn ngữ, hoặc từ điển theo trường nhập liệu như danh sách tên phố, danh mục sản phẩm). Một lưu ý trung thực về thí nghiệm: thêm danh sách thuật ngữ miền (viết từ chính từ vựng của ba tài liệu thử) chỉ cải thiện thêm dưới 1 điểm CER, và vì danh sách đó rò từ vựng của bộ thử nên cột đó lạc quan; cột "không thuật ngữ miền" ở trên mới công bằng.
Kiểm tra chéo số tiền: hậu xử lý OCR rẻ nhất, hiệu quả nhất
Với OCR hoá đơn, lỗi đắt nhất không phải một dấu thanh mà là một chữ số. Các ràng buộc nghiệp vụ cho phép phát hiện chúng không cần học máy: tổng các dòng hàng phải bằng tổng tiền hàng, thuế 8% phải đúng, tổng thanh toán là tổng cộng với thuế. check_invoice trích số bằng biểu thức chính quy rồi đối chiếu:
lines = re.findall(r"(\d+)\s*[x×]\s*([\d.]+)\s*đ", text)
sub = sum(int(q) * money(p) for q, p in lines)
if sub != t1: out.append(f"tổng dòng hàng {sub:,} != tổng tiền hàng {t1:,}")
if round(t1 * 0.08) != t2: out.append(f"thuế 8% của {t1:,} phải là {round(t1*0.08):,}, OCR đọc {t2:,}")
if t1 + t2 != t3: out.append(f"{t1:,} + {t2:,} != tổng thanh toán {t3:,}")clean ['khớp']
noisy ['khớp']
harsh ['không đọc được đủ 3 dòng tổng']
--- Thử sai một chữ số để xem cảnh báo
['thuế 8% của 4,190,000 phải là 335,200, OCR đọc 385,200', '4,190,000 + 385,200 != tổng thanh toán 4,525,200']Hai bản đầu khớp; bản harsh bị chặn vì không đọc đủ ba dòng tổng, đúng cách hệ thống nên làm: chuyển cho người duyệt thay vì tự tin ghi số sai. Khi cố ý đổi "335.200" thành "385.200", hai ràng buộc cùng báo lỗi. Tuy vậy, kiểm tra tổng không bắt được lỗi bù trừ hay lỗi ở trường không có ràng buộc (tên khách, địa chỉ); chúng vẫn cần ngưỡng tin cậy và người duyệt mẫu.
Khi nào không nên dùng OCR thuần, và lưu ý pháp lý
- Chữ viết tay, ảnh chụp nghiêng nặng, chữ mờ: OCR bằng Tesseract và PP-OCRv3 trong bài đều thất bại ở mức harsh; cần mô hình chuyên biệt và dữ liệu huấn luyện riêng, bài này chưa đo.
- Bảng biểu và biểu mẫu phức tạp: chỉ có chữ là chưa đủ, cần hiểu cấu trúc; nếu mẫu cố định, cắt vùng theo toạ độ đáng tin hơn.
- Giấy tờ tuỳ thân: ảnh đưa vào OCR chứa dữ liệu cá nhân, phải có căn cứ hợp pháp theo Nghị định 13/2023/NĐ-CP về bảo vệ dữ liệu cá nhân, lưu ảnh gốc có thời hạn và kiểm soát truy cập. Chạy OCR local giữ dữ liệu trong hạ tầng của bạn nhưng không thay nghĩa vụ này.
Lỗi thường gặp
- Quên chuẩn hoá Unicode trước khi so sánh: "ế" dựng sẵn khác "e" cộng dấu rời, làm CER sai lệch.
- Không kiểm tra tập ký tự của mô hình OCR với ngôn ngữ đích, rồi tìm lỗi ở tiền xử lý ảnh.
- Tiền xử lý một chiều: áp mọi bước cho mọi ảnh. Phóng to riêng lẻ và Otsu đều có lúc làm tệ hơn; luôn đo từng bước.
- Để
--psmmặc định cho tài liệu có cấu trúc rõ. - Hậu xử lý mạnh tay cho engine đã đúng dấu: sửa nhiều hơn làm hỏng nhiều hơn.
- Đo OCR trên dữ liệu sạch của chính mình rồi kỳ vọng như ảnh khách chụp.
Checklist triển khai OCR tiếng Việt
- Gom vài chục ảnh thật đã được phép để đánh giá OCR, gõ tay đáp án, đo CER và WER sau khi chuẩn hoá NFC.
- Kiểm tra tập ký tự của mô hình OCR có đủ 134 chữ cái có dấu không.
- Đo từng bước tiền xử lý; san phẳng nền trước, thêm bước khác chỉ khi có số liệu.
- Chọn
--psmtheo loại tài liệu; cắt vùng cố định cho biểu mẫu. - Thêm ràng buộc nghiệp vụ (tổng tiền, thuế, định dạng mã đơn) và đường chuyển cho người duyệt.
- Giới hạn RAM và số luồng của tiến trình OCR; lưu ảnh có thời hạn và kiểm soát truy cập.
Mã nằm trong code/s7-30 cùng các tệp *_output.txt, requirements.txt (đã ghi phiên bản). Mô hình ONNX khoảng 11 MB, các gói vie.traineddata lấy từ hai kho tessdata. Tiến trình nặng nhất dùng khoảng 1,6 GB RAM.
Bài tiếp theo
LLM hoạt động thế nào: token, ngữ cảnh, sampling và giới hạn. Sau khi OCR và Whisper biến ảnh và giọng nói thành văn bản, bài sau mở hộp đen của mô hình ngôn ngữ lớn: văn bản được chia token ra sao, cửa sổ ngữ cảnh giới hạn điều gì, và vì sao cùng một câu hỏi có thể cho câu trả lời khác nhau. Về phân loại văn bản tiếng Việt sau khi OCR xong, xem thêm Phân loại văn bản tiếng Việt: tách từ, TF-IDF và mô hình tuyến tính.



