Почему все существующие OCR ломаются о ваши каракули
Традиционный OCR - это как человек с идеальным зрением, который никогда не видел письменности. Он отлично справляется с печатным текстом, но ваши рукописные заметки для него - это набор случайных линий. Tesseract, EasyOCR и прочие библиотеки задумывались в эпоху, когда рукописный ввод был экзотикой. В 2026 году это уже недопустимо.
Проблема в контексте. Человек, читая "прЕкт", понимает, что это "проект", потому что знает язык. Старый OCR видит только пиксели. Мультимодальные LLM вроде Qwen3.5-4B - это первый реальный шанс заставить машину понимать, что она видит, а не просто сопоставлять шаблоны.
Забудьте про универсальные решения. Распознавание рукописного текста - это всегда кастомизация. Ваш почерк уникален, и модель нужно учить на ваших данных. К счастью, Qwen3.5-4B достаточно мала, чтобы работать локально, и достаточно умна, чтобы адаптироваться.
Qwen3.5-4B и llama.cpp: странный, но эффективный дуэт
Почему именно эта связка? Потому что она работает там, где другие пасуют. Llama.cpp на 10 марта 2026 года - это не просто раннер для моделей, это полноценная платформа для инференса с поддержкой OpenCode Interpreter. Да, тот самый, который позволяет моделям выполнять код. Для обработки изображений это критически важно.
Qwen3.5-4B-Instruct с поддержкой зрения (V) - это компактная модель, которая умеет "смотреть" на картинки. В теории. На практике, чтобы заставить её читать рукопись, нужно пройти через семь кругов ада с квантованием, флагами компиляции и prompt engineering. Но результат того стоит: точность под 90% на сложных почерках против 40-50% у традиционных методов.
1 Готовим окружение: не повторяйте этих ошибок
Первая и главная ошибка - пытаться собрать llama.cpp "как в том туториале 2024 года". На 2026 год репозиторий изменился до неузнаваемости. Вам нужна поддержка OpenCode, иначе модель будет просто описывать картинку, а не выполнять инструкции по её обработке.
# Клонируем репозиторий с подмодулями
git clone --recurse-submodules https://github.com/ggerganov/llama.cpp
cd llama.cpp
# Собираем с поддержкой всех нужных фич на 2026 год
make LLAMA_CUDA=1 LLAMA_OPENBLAS=1 LLAMA_BUILD_SERVER=1 LLAMA_OPENCODE=1 -j$(nproc)
Обратите внимание на флаг LLAMA_OPENCODE=1. Без него вы получите модель, которая умеет смотреть, но не умеет думать над тем, что видит. Это самая частая причина провала.
Если у вас Intel Arc GPU, вам пригодится мой гайд по сборке llama.cpp с SYCL. Для ARM-систем вроде Orange Pi логика аналогичная, но со своими особенностями.
2 Находим и качаем правильную модель
Не всякий Qwen3.5-4B подойдет. Вам нужна конкретно Qwen2.5-VL-4B-Instruct или её более новая итерация, если она появилась. "VL" значит Vision-Language. Базовая текстовая модель здесь бесполезна.
Идем на Hugging Face. Но не качаем первое попавшееся. Ищем конвертированную в GGUF версию с квантованием Q4_K_M или Q5_K_M. Почему? Потому что более агрессивные квантования (Q2, Q3) съедают те самые тонкие признаки, которые отличают "и" от "н". А полная версия (F16) будет жрать память как не в себя.
На 10.03.2026 актуальная ссылка для загрузки - TheBloke/Qwen2.5-VL-4B-Instruct-GGUF. TheBloke - это гарантия качества конвертации. Качайте файл qwen2.5-vl-4b-instruct.Q5_K_M.gguf.
3 Запускаем и настраиваем: магия в деталях
Запустить модель - это полдела. Заставить её именно распознавать текст, а не сочинять хокку про вашу записку - это искусство. Вот рабочий шаблон команды:
./llama-cli -m ./models/qwen2.5-vl-4b-instruct.Q5_K_M.gguf \
--mmproj ./models/qwen2.5-vl-4b-instruct.mmproj \
--image "путь/к/вашему/изображению.jpg" \
-p "[INST] Extract all handwritten text from the image. Return only the text, without descriptions or comments. [/INST]" \
-ngl 99 -c 4096 -n 512 --temp 0.1
Разберем по косточкам:
--mmproj: Это файл проекции для визуальной части модели. Без него llama.cpp не поймет, что делать с картинкой. Он должен лежать рядом с моделью GGUF.-p: Промпт. Здесь важно быть жестким и конкретным. "Extract all handwritten text" - прямая команда. "Return only the text" - убираем болтовню модели. Если промпт слабый, модель начнет философствовать.-ngl 99: Заливаем все слои в GPU VRAM. Для 4B модели на Q5_K_M нужно около 4-5 ГБ VRAM.-c 4096: Контекст. Для одной картинки хватит, но если вы планируете обрабатывать длинные документы, смотрите гайд по выбору модели под большие контексты.--temp 0.1: Низкая температура для детерминированности. Нам нужен точный текст, не креатив.
Почему это работает, когда другие методы пасуют?
Ключ - в архитектуре. Qwen2.5-VL тренировалась на огромных наборах данных, где изображения и текст были связаны. Она не просто находит контуры букв, она строит семантические связи. Видит кляксу? Понимает, что это, вероятно, точка или запятая, а не дефект изображения. Видит зачеркивание? Игнорирует его или интерпретирует как правку.
Llama.cpp же выступает как невероятно эффективный раннер. Он превращает сложную модель в инструмент, который можно запустить на потребительской видеокарте или даже CPU с приемлемой скоростью (3-5 токенов в секунду на CPU, 20-30 на средней GPU).
Склейка страниц в документ: когда одной картинки мало
Одна страница - это разминка. Реальная задача - это конспект из 50 листов или старый дневник. Тут нужно автоматизировать процесс. Пишем простой bash-скрипт, который:
- Конвертирует PDF в набор изображений (например, с помощью
pdftoppm). - Запускает llama-cli для каждого изображения, сохраняя вывод.
- Объединяет результаты, сохраняя порядок страниц.
Для сложной постобработки (например, структуризации конспекта в markdown) можно подключить более мощную текстовую модель, как в гайде по склейке PDF.
#!/bin/bash
for img in pages/*.jpg; do
echo "Processing $img..."
./llama-cli -m ./model.gguf --mmproj ./model.mmproj --image "$img" \
-p "[INST] Extract text. Return text only. [/INST]" \
-ngl 99 -c 4096 -n 1024 --temp 0.1 > "output/$(basename "$img".txt)"
sleep 2 # Даем GPU остыть, чтобы избежать троттлинга
done
Где всё ломается: частые ошибки и их лечение
| Симптом | Причина | Лечение |
|---|---|---|
| Модель возвращает описание картинки ("a handwritten note on paper"), а не текст. | Слабый или неверный промпт. Модель выполняет инструкцию "опиши изображение". | Усильте промпт: "[INST] Your task is OCR. Extract ONLY the handwritten words. Do not describe the image. [/INST]". |
| Вывод обрывается на полуслове. | Слишком маленький параметр -n (максимальное количество генерируемых токенов). |
Увеличьте -n 1024 или больше. Считайте токены: 1 токен ≈ 0.75 слова на английском. |
| Скорость обработки падает с каждой новой картинкой. | Утечка памяти или перегрев GPU. Контекст не очищается. | Добавьте паузу между запросами (sleep). Используйте флаг --prompt-cache для статических частей промпта. |
| Ошибка "failed to load model" или "missing mmproj". | Файл проекции (.mmproj) не скачан или не соответствует модели. |
Скачайте .mmproj файл из того же репозитория на Hugging Face, что и модель GGUF. Имена должны совпадать. |
А что дальше? Fine-tuning под ваш почерк
Базовая модель хороша, но идеал - это когда она учится на ваших конкретных записях. Fine-tuning 4B-модели в 2026 году - это не фантастика, а практика. Инструменты вроде Unsloth позволяют сделать это на одной видеокарте с 8-12 ГБ VRAM за несколько часов.
Смысл простой: вы готовите датасет из пар "изображение вашего почерка - правильный транскрипт". 100-200 таких пар уже дадут заметный прирост качества. Модель научится различать ваши специфические закорючки.
Вопросы, которые вы боялись задать
Модель ошибается в цифрах и датах. Это можно исправить?
Да, и это частая проблема. Модель видит "2025" и может выдать "2023" или "2025 год". Решение - постобработка с помощью простых правил или даже второй, маленькой модели, которая специализируется на проверке фактов (цифры, имена, даты). Или добавьте в промпт уточнение: "Pay special attention to numbers and dates. Transcribe them exactly as seen."
Как обрабатывать цветные рукописные заметки (маркер, разноцветные ручки)?
По умолчанию модель получает изображение в оттенках серого. Если цвет важен, нужно конвертировать изображение в RGB и убедиться, что llama.cpp передает его правильно. В промпте можно указать: "The text is written in multiple colors. Extract all text regardless of color."
Llama.cpp тормозит на CPU. Есть ли альтернативы?
Есть. Можно использовать OpenCode Interpreter через Python-библиотеки, но это потребует больше памяти и менее эффективно. Или собрать llama.cpp с поддержкой GPU (CUDA, Metal, Vulkan). На CPU помогает квантование до Q4_K_S и ограничение потоков (флаг -t).
Можно ли интегрировать это в автоматический пайплайн?
Абсолютно. Llama.cpp имеет встроенный сервер (./llama-server), который предоставляет API, похожий на OpenAI. Вы можете отправлять изображения по HTTP, получать текст и встраивать это в свои приложения. Главное - следить за очередью запросов, чтобы не перегрузить память.
Итог? Вы больше не зависите от облачных сервисов, качество которых непредсказуемо, а политика конфиденциальности сомнительна. У вас на столе (или в дата-центре) работает личный дешифратор ваших мыслей, который со временем будет только умнеть. И да, теперь вы можете оцифровать те коробки старых дневников, которые годами пылились на антресолях. Только приготовьтесь к неожиданным открытиям.