Tăng tốc OCR hàng loạt cùng paraOCR

Từ một nhu cầu đơn giản, mình đã tạo ra một công cụ OCR đa luồng, có cả giao diện web, và nhận về một cú "vỡ mộng" đầy bất ngờ.

Mọi chuyện bắt đầu rất đơn giản: mình có vài ngàn file PDF và ảnh cần nhận dạng ký tự (OCR).

Nhưng đó lại là các tài liệu tiếng Việt. Chi tiết này rất quan trọng, vì dù PaddleOCR chạy song song rất “ngon”, nó lại nhận dạng tiếng Việt không tốt bằng. Thế là mình chỉ còn hai lựa chọn: EasyOCR hoặc Tesseract.

Tesseract thì giống như một lão làng đáng tin cậy – lúc nào cũng hoàn thành nhiệm vụ nhưng khá chậm chạp. Trong khi đó, EasyOCR có hỗ trợ GPU.

Và trong đầu mình loé lên một ý nghĩ:

“Chắc là mình có thể ép EasyOCR chạy nhanh hơn nữa nếu cho nó xử lý song song trên GPU.”

Cái suy nghĩ “chắc là” đó đã chiếm trọn cuối tuần của mình, khai sinh ra một công cụ tên là paraOCR, và dạy cho mình một bài học nhớ đời về hiệu năng.

Giai đoạn 1: Cú hích tốc độ đầu tiên

Thí nghiệm đầu tiên của mình cực kỳ đơn giản: cho nhiều “công nhân” EasyOCR chạy song song.

Chỉ riêng việc này thôi đã giúp tăng tốc được khoảng 30%.

Nhưng 30% thì chưa đã. Mình nhận ra chiến lược của mình sai ở chỗ – mình đang đưa cho GPU xử lý cả file lớn, thay vì chia nhỏ chúng ra thành từng trang. Điều này có nghĩa là trong lúc CPU đang ì ạch render từng trang của một file, thì GPU lại phải ngồi chơi xơi nước.

Thế là mình chia lại toàn bộ quy trình:

  • Team CPU: Một nhóm công nhân chỉ chuyên một việc: đọc file PDF và render các trang ra thành hình ảnh.
  • Team GPU: Một nhóm công nhân khác ngồi đợi. Cứ khi nào team CPU chuẩn bị xong một lô ảnh là team GPU sẽ lao vào xử lý ngay lập tức.

Với cách này, team CPU có thể chuẩn bị lô hàng số 2 trong khi team GPU đang xử lý lô hàng số 1. Tốc độ? Tăng gần 60% so với ban đầu. Quá tuyệt phải không!

Giai đoạn 2: “Hay là mình thử các ‘bộ não’ OCR khác?”

Khi kiến trúc đã ổn, một ý nghĩ “trời ơi đất hỡi” khác lại xuất hiện:

“Sẽ thế nào nếu mình có thể thay đổi các bộ máy OCR (engine) mà không cần viết lại code?”

Điều này có nghĩa là mình phải tái cấu trúc lại toàn bộ, tạo ra một cái khung sườn để có thể dễ dàng lắp ráp EasyOCR, PaddleOCR, hay Tesseract vào.

Nghe thì vui đấy. Nhưng làm thì toát mồ hôi.

Sau nhiều giờ vật lộn với các loại tham số của từng backend, cuối cùng mình cũng làm được. Giờ đây, paraOCR đã có thể tổ chức một cuộc so găng tốc độ trực tiếp giữa các engine trên cùng một dàn máy.

Giai đoạn 3: Từ dòng lệnh khô khan đến giao diện Web thân thiện

Lúc này, paraOCR (tên mình bắt đầu gọi nó) vẫn là một công cụ chỉ chạy bằng dòng lệnh. Mình thì có thể dùng terminal, nhưng mình biết rằng nhiều người sẽ “chạy mất dép” nếu phải gõ lệnh.

Thế là mình xây dựng một giao diện Web đơn giản bằng Gradio:

  • Người dùng có thể tải lên file PDF hoặc cả một file ZIP chứa đầy ảnh.
  • Chọn bộ máy OCR mình thích.
  • Bấm “Run” và ngồi xem thanh tiến trình chạy, với log cập nhật trực tiếp.

Tất nhiên, việc này lại đẻ ra một vấn đề kỹ thuật khác. Hệ thống ghi log – vốn được thiết kế cho console – phải được xây dựng lại từ đầu để giao diện web và các “công nhân” có thể “nói chuyện” với nhau mà không bị xung đột. Lại thêm vài giờ nữa của mình bay màu…

Giai đoạn 4: Cú sốc ở Google Colab – Khi nhanh hóa chậm

Mình muốn ai cũng có thể dùng paraOCR, kể cả những người không có máy tính mạnh. Vì vậy, mình đã tạo một file Google Colab để mọi người có thể chạy nó trên cloud.

Và đây là lúc câu chuyện đi vào hồi vỡ mộng nhất.

Trên con máy RTX 3060 ở nhà, paraOCR chạy nhanh vun vút so với script EasyOCR thông thường. Nhưng trên Google Colab thì sao?

Đôi khi… script chạy tuần tự thông thường lại THẮNG.

Tại sao lại có chuyện ngược đời như vậy?

Bởi vì phần cứng của Google Colab đã đảo lộn mọi tính toán về hiệu năng:

  1. Ổ cứng siêu chậm: Mỗi lần một “công nhân GPU” phải tải mô hình OCR lên hoặc đọc một file ảnh, nó chậm như rùa.
  2. CPU chỉ có 2 nhân: Việc tạo ra quá nhiều “công nhân CPU” chỉ khiến chúng dẫm chân lên nhau (hiện tượng context-switching).
  3. Một GPU, nhiều “sếp”: Mỗi một công nhân GPU phải tự mình “học việc” bằng cách tải toàn bộ mô hình EasyOCR vào bộ nhớ GPU. Trên Colab, quá trình này có thể mất 25-30 giây cho mỗi công nhân. Trong khi script thường chỉ mất 3 giây để làm việc này một lần duy nhất.
  4. Chi phí quản lý > Lợi ích: Thiết kế của paraOCR là để che đi độ trễ của ổ cứng bằng cách cho CPU và GPU làm việc song song. Nhưng nếu cả hai đều chậm, thì chẳng có gì để che cả. Lúc này, chi phí để “quản lý” một đội quân hùng hậu lại lớn hơn lợi ích mà chúng mang lại.

Sau nhiều lần đo đạc “đau thương”, mình đã tìm ra cấu hình “đỡ tệ nhất” cho Colab:

  • workers = 2 (Mỗi nhân CPU gánh một công nhân)
  • gpu-workers = 2 (Đủ để giữ cho GPU bận rộn mà không tốn quá nhiều thời gian khởi động)

Bài học rút ra: Song song không phải lúc nào cũng tốt

Mình không hề có ý định xây dựng cả một framework OCR đa năng có cả WebUI và chạy được trên cloud. Mình chỉ muốn EasyOCR chạy nhanh hơn một chút thôi.

Nhưng trên hành trình đó, mình đã học được một điều quý giá hơn nhiều:

Nhiều “công nhân” hơn không phải lúc nào cũng tốt hơn. Hiểu được tại sao lại là điều quan trọng không kém gì việc tăng tốc.


Dùng thử paraOCR (Không cần máy khủng!)

Phần hay nhất là bạn không cần một chiếc máy tính có GPU mạnh để trải nghiệm paraOCR. Bạn có thể chạy nó ngay trên trình duyệt với Google Colab.

Cách dễ nhất để bắt đầu:

  1. Mở link này bằng Google Colab (Open in Colab): https://github.com/phuocnguyen90/paraOCR/blob/main/paraOCR_WebUI.ipynb
  2. Ở trên menu, chọn Runtime -> Change runtime type và đảm bảo bạn đã chọn T4 GPU.
  3. Bấm nút “Play” (▶️) ở từng ô code, từ trên xuống dưới.
  4. Sau khi chạy ô cuối cùng, một giao diện web sẽ hiện ra ngay bên dưới. Giờ bạn chỉ cần tải file PDF hoặc file ZIP chứa ảnh lên và bấm “Run”!

Đối với dân chuyên:

Nếu bạn muốn cài đặt trên máy cá nhân, hãy ghé thăm repo GitHub của dự án để xem hướng dẫn chi tiết.

Một vài lời thú tội…

Thứ nhất, đây là một tool nhà trồng được mình làm cho vui vào cuối tuần, nên em nó vẫn còn khá nhiều bug.
Thứ hai, xin lỗi về cái tít hơi “câu view”. paraOCR không phải lúc nào cũng nhanh gấp đôi. Qua các bài test, mức tăng tốc khoảng 60-70% là phổ biến và thực tế hơn. Tuy nhiên, hiệu năng còn tùy vào phần cứng, biết đâu máy bạn lại chạy nhanh hơn thì sao!

Mọi báo lỗi và đóng góp trên GitHub đều được chào đón nhiệt liệt!


Hãy thử ngay!

Nếu bạn cũng từng “vỡ mộng” vì những lầm tưởng về hiệu năng, mình hy vọng câu chuyện này sẽ hữu ích.

Leave a Reply

Your email address will not be published. Required fields are marked *