PaddleOCR z ustawieniem lang="pl" nie ma polskiego modelu: używa wielojęzycznego modelu łacińskiego, który w wersji 2.x zamienia ó na ö, ć na é i ł na t. Pomaga dopiero przejście na PaddleOCR 3.2 z modelem PP-OCRv5. Na jednym dokumencie testowym udział liter z diakrytykami wzrósł po tej zmianie z 0,75% do 5,37%, a wszystkie znane błędy zniknęły. Opisujemy, jak zdiagnozowaliśmy problem w projekcie klienta, jak zmierzyć jakość polskiego OCR bez ręcznego przepisywania stron, co daje aktualizacja, na czym się wywraca i jak poprawić znaki, których nowy model dalej nie łapie.
Dlaczego PaddleOCR źle czyta polskie znaki?
Zgłoszenie od klienta brzmiało tak: „nasze OCR-y słabo odczytują polskie znaki, chyba lecą przez PaddleOCR”. System przetwarzał zeskanowane dokumenty firmowe i na produkcji odczytywał je właśnie przez PaddleOCR. Intuicja była trafna, ale przyczyna siedziała głębiej niż w ustawieniach.
Usługa OCR korzystała z PaddleOCR 2.8.1 i PaddlePaddle 2.6.2 z parametrami lang="pl" i ocr_version="PP-OCRv4". Wyglądało to na nowy model. W kontenerze faktycznie działał jednak model rozpoznawania latin_PP-OCRv3_rec z 2022 roku i detektor en_PP-OCRv3. PP-OCRv4 nie ma modelu łacińskiego, a biblioteka bez ostrzeżenia wraca do wersji 3. Polski nie ma własnego modelu w żadnej z tych wersji, więc trafia do modelu łacińskiego, wspólnego dla wielu języków.
Jakie błędy w polskich znakach robi model łaciński?
Błędy są przewidywalne, bo model zna znaki z innych języków łacińskich i wybiera najbliższy kształt:
| Powinno być | Model łaciński PP-OCRv3 | Rodzaj błędu |
|---|---|---|
| przepisów | przepisöw | ó odczytane jako niemieckie ö |
| działać | dziataé | ł jako t, ć jako é |
| zrobić | zrobié | ć jako é |
| głowa | glowa | brak ł |
| zgłoszenia | zgtoszenia | ł jako t |
Najczęstsze wzorce to ó zamienione na ö, ć na é, „ła” na „ta” oraz brak ą, ę i ł. Dla wyszukiwarki i modelu językowego, który później czyta ten tekst, „przepisöw” i „przepisów” to dwa różne słowa. Dokument formalnie jest w systemie, ale wyszukiwanie po polskiej frazie go nie znajduje.
Jak zmierzyć, czy OCR gubi diakrytyki?
Nie trzeba ręcznie przepisywać stron, żeby porównać silniki. Wystarczy policzyć, jaki procent liter w odczytanym tekście stanowią litery z polskimi znakami diakrytycznymi. W zwykłej polszczyźnie to około 7–9% liter. Wynik wyraźnie niższy oznacza, że OCR zamienia polskie znaki na ich łacińskie odpowiedniki.
W bazie deweloperskiej projektu tekst z PaddleOCR miał średnio 2,43% liter z diakrytykami. Tekst z dots.ocr, modelu wizyjno-językowego, który działał w tym samym systemie lokalnie, miał 5,23%. Nie jest to odsetek błędów, tylko udział polskich liter w tekście, ale przy podobnych dokumentach różnica ponad dwukrotna pokazuje, który silnik gubi znaki.
Drugi szybki test to wyszukanie liter, których w polskim nie ma: ö, ü, é, ä. Każde takie trafienie w polskim dokumencie to prawie na pewno błąd odczytu.
Czy da się poprawić PaddleOCR samą konfiguracją?
W linii PaddleOCR 2.x nie. Lepszego modelu łacińskiego dla tej wersji po prostu nie ma, więc żaden parametr nie zmieni wyniku. Lepszy model, latin_PP-OCRv5_mobile_rec, pojawił się dopiero w PaddleOCR 3.2 z PaddlePaddle 3.x.
Najpierw próbowaliśmy uruchomić PP-OCRv5 lokalnie i to się nie udało. PaddlePaddle 3.0 i 3.1 kończyły się błędem segmentacji w Dockerze na Macu (linux/arm64), w trzech konfiguracjach: z detektorem server, z detektorem mobile i z wyłączonymi flagami. Produkcja działała na x86_64, gdzie ta wersja jest stabilna, więc test jakości przenieśliśmy na x86.
Czy PP-OCRv5 lepiej czyta polskie znaki?
Test zrobiliśmy na serwerze produkcyjnym, ale z boku aplikacji: w osobnym, tymczasowym kontenerze z limitem 4 rdzeni i 8 GB pamięci, który po teście został usunięty. Użytkownicy w tym czasie pracowali w systemie, a obciążenie serwera pozostało niskie. Ten sam dokument przeszedł przez obecny model i przez PP-OCRv5 (latin_PP-OCRv5_mobile_rec z detektorem PP-OCRv5_mobile_det, PaddleOCR 3.2, PaddlePaddle 3.0).
| PP-OCRv3 latin (stary model) | PP-OCRv5 latin |
|---|---|
| Spokojna glowa w sprawie przepisöw | Spokojna głowa w sprawie przepisów |
| Zgubllem dowód osco sty, co mam zrobié? | Zgubiłem dowód osobisty, co mam zrobić? |
| Chcesz zaczaé od zgtoszenia utraty? | Chcesz zacząć od zgłoszenia utraty? |
| jak to moze dziataé | jak to może działać |
Udział liter z diakrytykami w tym dokumencie wzrósł z 0,75% do 5,37%. Wszystkie błędy, które znaliśmy ze starego modelu, zniknęły. Zostały pojedyncze: „Zatwierdż” zamiast „Zatwierdź”, „ręeznie” zamiast „ręcznie” i „Al” zamiast „AI” w logo. To jeden dokument, a nie pełna ewaluacja, ale kierunek był na tyle wyraźny, że aktualizację uznaliśmy za uzasadnioną.
Na co uważać przy aktualizacji do PaddleOCR 3?
Test ujawnił kilka pułapek, na które trafisz przy migracji:
- PaddlePaddle 3.x wymaga w obrazie biblioteki systemowej
libgomp1. - Plik wejściowy musi mieć rozszerzenie
.pdfalbo.png. - Bardzo duże bitmapy trzeba zmniejszyć przed OCR. Dokument testowy miał prawie 16 tys. pikseli wysokości, w planie wdrożenia przyjęliśmy limit 4000 pikseli dłuższego boku.
- PaddleOCR 3.1.0 ma błędną zależność od paddlex, która kończy się błędem
TypeError. Używaj wersji 3.2 lub nowszej. - API wyników się zmienia: zamiast
ocr.ocr()jestocr.predict(), a tekst, pewność i ramki przychodzą w kluczachrec_texts,rec_scoresirec_polys.
Ponieważ deweloperzy pracowali na Macach, zaplanowaliśmy Dockerfile zależny od architektury: na x86_64 nowe wersje, na arm64 stare, żeby lokalne środowisko dalej działało. Odpowiedź HTTP usługi OCR zostaje taka sama, więc reszta systemu nie musi wiedzieć o zmianie modelu.
Jak poprawić polskie znaki po OCR?
Nawet PP-OCRv5 zostawia pojedyncze błędy, a dokumenty przetworzone wcześniej mają tekst ze starego modelu. Na to zaprojektowaliśmy korektę słownikową, która działa na gotowym tekście:
- Znaki, których w polskim nie ma, zamieniamy według mapy pomyłek modelu łacińskiego (ö i ü na ó, é na ć albo ę, ä na a), ale tylko wtedy, gdy wynik jest słowem ze słownika polskiego.
- Słowa bez polskich znaków, których nie ma w słowniku, próbujemy uzupełnić o diakrytyki. Zmianę akceptujemy wyłącznie przy jednym jednoznacznym trafieniu. Para „moze” i „może” zostaje nietknięta, jeśli obie formy mają sens.
- Słownik to hunspell pl_PL albo lista słów SJP.
- Korekta ma flagę domyślnie wyłączoną, a dla starych dokumentów najpierw działa w trybie próbnym, który tylko raportuje, ile słów by zmieniła. Każda zmiana trafia do historii wersji dokumentu, więc da się ją cofnąć. Dokumentów poprawionych ręcznie nie dotyka.
Prostszy pomysł, czyli poprawianie diakrytyków modelem językowym, był w tym systemie już wcześniej. Na części ścieżek został wyłączony, bo gubił około 35% treści. Słownik jest mniej sprytny, ale nie usuwa zdań.
Gdzie jeszcze może uciekać zepsuty tekst z OCR?
W tym samym projekcie znaleźliśmy drugi problem, niezależny od modelu. W konfiguracji, w której tekst czytał dots.ocr, PaddleOCR działał równolegle tylko po to, żeby dostarczyć ramki słów do podglądu. Funkcja składająca tekst strony wybierała jednak słowa z PaddleOCR zamiast tekstu z dots.ocr, jeśli strona miała ramki. Pełny tekst dokumentu był poprawny, a tekst poszczególnych stron, który trafiał do indeksu wyszukiwarki, do RAG i do podglądu, miał zepsute polskie znaki.
Ręczna korekta jednego słowa w podglądzie odbudowywała cały tekst dokumentu ze słów PaddleOCR, więc poprawka jednego wyrazu psuła resztę. Naprawa to jedna linijka: brać tekst strony, gdy jest niepusty, a słowa zostawić do zaznaczania ramek. Jeśli Twój system łączy dwa silniki OCR, sprawdź, z którego z nich naprawdę pochodzi tekst w wyszukiwarce.
Jak wdrożyć nowy model OCR bez przerwy w pracy?
Plan wdrożenia zakładał, że użytkownicy nie zauważą zmiany:
- Zbudować nowy obraz usługi OCR na serwerze i uruchomić go obok starego, z osobnym limitem zasobów.
- Puścić około 20 dokumentów produkcyjnych przez obie usługi, z wynikami zapisanymi poza bazą, i porównać udział diakrytyków, liczbę słów i czas na stronę.
- Po akceptacji przełączyć adres usługi OCR w jednej zmiennej środowiskowej i zrestartować API oraz workera, co trwa sekundy.
- Ponownie przetworzyć dokumenty zgłoszone przez użytkowników, z pominięciem cache OCR.
- Wycofanie to cofnięcie tej jednej zmiennej. Stary kontener zostaje przez tydzień, potem znika.
Aktualizację modelu szacowaliśmy na 2–3 godziny pracy, korektę słownikową na 3–4 godziny, a wdrożenie z testem A/B na pół dnia. To niewiele w porównaniu z kosztem dokumentów, których nie da się znaleźć po polsku. Jeśli chcesz, żebyśmy przeszli tę samą drogę na Twoich skanach, zacznij od oferty OCR dokumentów.
Masz prototyp OCR, który działa na próbkach, a na produkcji gubi znaki albo treść? Zdiagnozujemy, który silnik odpowiada za tekst, zmierzymy jakość i wdrożymy poprawkę obok działającego systemu. Zobacz, jak wdrażamy prototypy AI →
