MacBook M3, 16 гигабайт, никакого облака. Свежая Gemma 4 берёт с картинки график и отдаёт CSV. Первые три кейса — идеально. На четвёртом модель начала врать. И врать аккуратнее, чем говорила правду.
Вводная
Вышла Gemma 4 12B Unified — мультимодальная модель, которая читает не только текст, но и картинки. В квантованном виде она помещается на обычный ноутбук, и мне стало любопытно, что это даёт на практике, а не в бенчмарках.
Просто запустить «hello world» неинтересно. Задача была двойная: собрать на этой модели маленький рабочий инструмент — и заодно честно проверить, где у локального зрения предел. Научился сам — расскажи, как оно на самом деле.
Инструмент выбрал такой, чтобы локальность была оправдана, а не «потому что могу»: вытаскивать данные из картинок с графиками и таблицами в CSV. Это то, что нельзя слить в облако, и то, что сразу грузит vision по полной — OCR, чтение осей, разбор структуры.
Дальше по порядку: что за модель и влезает ли в 16 ГБ, поднимается ли на Mac, на какие грабли я наступил, как устроен инструмент — и карта из семи кейсов, где видно, чему верить, а чему нет.
Зачем локально, если облако читает лучше
Облачные API распознают картинки точнее и быстрее. Но есть данные, которые нельзя выгружать наружу: внутренние дашборды, отчёты под NDA, в общем, визуализация, которую надо оцифровать, не светя в стороннем логе. Тут локальная модель — единственный вариант. Приватность, офлайн, нулевая стоимость инференса. Вопрос один: насколько ей можно верить. Об этом и текст.
Что берём и влезает ли это в 16 ГБ
Герой — Gemma 4 12B Unified. Мультимодальная, encoder‑free: проецирует патчи картинки напрямую, без отдельного визуального энкодера. Контекст до 256K, режим рассуждений гасится одним флагом.
В full precision это ~24 ГБ, в 16 не влезает. Беру квантованную:
gemma-4-12b-it-UD-Q4_K_XL.gguf— 6.86 ГБ;mmproj-F16.gguf— это «глаза», без него модель текстовая — 167 МБ.
В рантайме mmproj добавляет ~360 МБ. Помещается с запасом, если не выкручивать контекст. Я ставил --ctx-size 8192 — для одной картинки за глаза, а 256K сожрали бы всю память.
Сетап: где я споткнулся
Ставится через Homebrew и llama.cpp. Грабли я собрал на свежести модели — выношу сразу, чтобы ты не повторял.
Качаем модель. Имена файлов идут позиционно, не через --include — иначе CLI скачает только последний:
hf download unsloth/gemma-4-12b-it-GGUF \ gemma-4-12b-it-UD-Q4_K_XL.gguf mmproj-F16.gguf \ --local-dir ~/models/gemma-4-12b
Первая засада. Стабильный llama.cpp из Homebrew (билд 9430) при старте с mmproj падал:
clip_init: ... unknown projector type: gemma4uv
gemma4uv — новый визуальный проектор encoder‑free 12B. Поддержку влили в llama.cpp за пару дней до моих экспериментов, и стабильная сборка её ещё не знала. Сборка из исходников через brew install --HEAD упала на компиляции — устаревшие Command Line Tools, «Tier 2 configuration». Сработал готовый официальный бинарник с GitHub Releases, новее билда 9496. Скачал, распаковал, запустил:
~/llama-bin/llama-b9528/llama-server \ --model ~/models/gemma-4-12b/gemma-4-12b-it-UD-Q4_K_XL.gguf \ --mmproj ~/models/gemma-4-12b/mmproj-F16.gguf \ --ctx-size 8192 --jinja --reasoning off \ --temp 0.1 --top-p 0.95 --top-k 64 --port 8080
Строка в логе, ради которой всё затевалось:
loaded multimodal model, '.../mmproj-F16.gguf'
Зрение поднялось. Урок: на свежих моделях версия раннера решает больше, чем железо. Проверяй номер билда, прежде чем грешить на свой ноут.
Инструмент: 200 строк и один важный промпт
Сам chartscan.py — без внешних зависимостей, только стандартная библиотека. Логика простая: кодируем картинку в base64, шлём в llama-server по OpenAI‑совместимому эндпоинту, парсим строгий JSON, пишем CSV. Весь интеллект — в промпте и схеме:
Возвращай ТОЛЬКО валидный JSON. Схема:{ "kind": "chart" | "table", "chart_type": "bar"|"line"|"pie"|"scatter"|"area"|"other"|null, "title": ..., "columns": [...], "rows": [[...]], "n_labels_seen": целое, // сколько ЯВНЫХ подписей насчитал "value_source": "labeled"|"estimated"|"mixed", "notes": ...}
Четыре решения, которые стоит перенять:
temperature=0.1, а не рекомендованная Google1.0. Структурированное извлечение хочет детерминизма, а не разнообразия. Единица — под диалог.value_source— модель сама говорит, взяла числа из подписей или прикинула по осям. Звучит как готовое решение проблемы доверия. Спойлер: не работает, вернусь к этому ниже.n_labels_seen— модель сперва считает подписи, потом заполняет строки, а скрипт сверяет одно с другим. Разошлось — красный флаг.Graceful‑fail. На большой таблице вывод обрывается по лимиту токенов посреди JSON. Скрипт не падает, а дозакрывает скобки и отдаёт все целиком прочитанные строки с пометкой
ВЫВОД ОБРЕЗАН. Недописанную хвостовую строку отбрасывает, а не угадывает.
Честный стресс‑тест: карта попаданий и провалов
Дальше самое интересное. Я взял семь картинок — от тривиальных до намеренно злых — и сверил вывод с оригиналом руками.
Где попадает идеально
Простая таблица (описание полей, 5×4): все ячейки точь‑в-точь, включая пустые (вернула null) и длинный текст с переносами. Сто процентов.

Круговая диаграмма с подписями: все шесть значений (15.2 / 18.2 / 12.1 / 9.1 / 24.2 / 21.2) совпали до десятой. Флаг labeled честный.

Зона комфорта: чистый растр плюс явные числовые подписи. Здесь модели можно верить.

Где честно предупреждает
Stacked bar без подписей. Значения прикинуты по оси, попадание в пределах 1–2 пунктов от реальных границ сегментов, каждая колонка сходится к 100%. И модель сама поставила estimated и объяснила почему. Единственная придирка: выдала 29.5, 20.5 — ложная точность. По сетке с шагом 20 полпроцента не разрешить физически, а десятичные она дорисовала.

Линейный график с 16 плотными подписями — отдельная драма. С первого захода модель потеряла часть точек, переврала значения и выдумала годы 2027–2043, которых на оси нет, достроив её «по плюс четыре года». И при этом нагло пометила всё как labeled. Я ужесточил промпт: запретил достраивать ось (нет подписи — ставь null), потребовал считать подписи, привязал labeled к тому, что и X, и Y взяты из видимого текста. После правки:
n_labels_seen: 16, строк 16 — счёт сходится, точки не теряются;выдуманные годы исчезли, неподписанные X встали в
null;флаг сменился на честный
mixed.
Структурную дыру я закрыл. Но 4 значения из 16 всё равно прочитаны неверно (513→517, 1018→1010, пара 1319/1802 переставлена местами) — всё в загромождённой середине, где подписи налезают на линию. Это уже предел самой модели, промптом не лечится. Зато теперь она об этом честно сигналит.


Где врёт молча — и это страшнее всего
Мыльный скриншот простой зарплатной таблицы. Первая строка прочитана идеально. Вторая, по Петрову, выдумана целиком: 800/1000/800/1000/1200/1400 из оригинала превратились в 1200/1300/1100/1200/1400/1500. Третья — одна ошибка. Подзаголовки аванс/зарплата переврала в важн/план.

И вот тут самое неприятное: рядом стоял флаг labeled: доверяй и счёт сходится. Самоотчёт модели соврал. Кросс‑чек по n_labels_seen считает точки графика, для таблиц он бесполезен — рассинхрон строк не возникает, даже когда половина чисел галлюцинация. Урок: нельзя доверять модели судить о собственной надёжности. Сигнализация должна быть внешней по отношению к ней.
Гигантская вложенная таблица — 30 строк на 18 столбцов, объединённые ячейки, двухуровневая шапка 2019/2020 → квартал → План/Факт. Сначала она вообще не доехала: на ~10 ток/с генерация 4096 токенов тянется минут семь, и клиент отваливался по таймауту раньше, чем сервер договаривал. Поднял таймаут — дождался. Вывод оборвался по лимиту токенов, но graceful‑fail спас 29 строк.


А вот качество спасённого — финал всей истории. Модель прочитала первую ячейку‑две в каждой строке верно, а дальше перестала читать и насыпала гладкую арифметическую прогрессию круглыми тысячами — 10000, 11000, 12000, 13000, — хотя в оригинале рваные 4302, 657, 9892. Объединённые ячейки развалились: товары Nestea уехали под BonAqua. Колонки местами сдвинулись. Появились несуществующие позиции — «братиш», «черника». Последняя строка продублировала предыдущую слово в слово.
Попробуй на глаз отличить это от настоящих данных. В том и подвох: фабрикация выглядит чище правды. Ровные тысячи, аккуратная структура — а это почти весь синтетик.
Главный вывод
Наивная гипотеза «локальная модель честна о своей точности» — неверна. Модель врёт и про числа, и про собственный флаг доверия. Точная формулировка такая:
Локальный VLM полезен только внутри узкой проверяемой зоны — чистые таблицы и простые графики с подписями. За её пределами он не падает, а правдоподобно врёт. И чем аккуратнее выглядит результат, тем больше повод насторожиться: честный OCR рваных реальных данных выглядит грязнее, чем выдуманная гладкая прогрессия.
Отсюда и роль инженера. Она не в том, чтобы выжать последние проценты точности — та упирается в саму модель. Она в том, чтобы натянуть проволочки‑сигнализации: строгая схема, флаг происхождения значений, кросс-чек подписей, graceful‑fail с явной пометкой обрыва. Они не делают модель умнее. Они говорят, когда ей не верить.
Когда брать локально, а когда не мучиться
Бери локально, если: разовая оцифровка чувствительных таблиц и простых графиков с подписями, нужен офлайн, нулевая стоимость, приватность. С обязательной ручной сверкой на сомнительных кейсах.
Не мучайся, если: плотные многосерийные графики, мыльные сканы, огромные вложенные таблицы. Тут либо облачный API, либо человек с глазами.
Вот как выглядит локальный инференс на M3 16 ГБ:

Простой график отдаётся за полминуты, гигантская таблица — за минуты. Практическим потолком на больших задачах оказывается wall‑clock, а не точность.
Локальная 12B на ноуте — это про приватность и контроль, а не про идеальную цифру. Если держать это в голове и не верить гладкому выводу на слово — инструмент рабочий.
Ссылки
GGUF‑кванты, которые качал я: https://huggingface.co/unsloth/gemma-4-12b‑it‑GGUF
Официальная карточка Gemma 4 12B (instruction‑tuned): https://huggingface.co/google/gemma-4-12B‑it
Релизы llama.cpp (бери билд ≥ 9496 ради
gemma4uv): https://github.com/ggml‑org/llama.cpp/releasesКод
chartscan.py: [https://gist.github.com/inforobotvit/0c90319f61ca20899011265c40c3f60b]
Будешь повторять — проверь номер билда llama.cpp, на свежих моделях это решает.
Если нужен гайд по установке и работе с этой моделью на твоём маке, напиши комментарий — подготовлю и пришлю.
Пишу про практики работы с AI в Telegram‑канале «Я и мой друг робот» — про мульти‑агентные системы, визуализацию данных и реальные автоматизации: https://t.me/mewithrobot
Комментарии (10)

sergeym69
06.06.2026 16:12Первая таблица конвертируется в JSON с ошибками
Вторая слишком сложная для такой маленькой сетки, но Qwen3.6-35B полностью конвертирует таблицу в JSONСкрытый текст
[ { "Категория": "Вода и чай", "Наименование": "BonAqua", "Вид": "без газа", "2019_Q1_План": 9355, "2019_Q1_Факт": 5467, "2019_Q2_План": 4302, "2019_Q2_Факт": 657, "2019_Q3_План": 9892, "2019_Q3_Факт": 6663, "2019_Q4_План": 4802, "2019_Q4_Факт": 9405, "2020_Q1_План": 12199, "2020_Q1_Факт": 4885, "2020_Q2_План": 4767, "2020_Q2_Факт": 341, "2020_Q3_План": 6864, "2020_Q3_Факт": null }, { "Категория": "Вода и чай", "Наименование": "BonAqua", "Вид": "с газом", "2019_Q1_План": 5410, "2019_Q1_Факт": 7326, "2019_Q2_План": 3652, "2019_Q2_Факт": 2824, "2019_Q3_План": 7730, "2019_Q3_Факт": 5125, "2019_Q4_План": 8480, "2019_Q4_Факт": 6168, "2020_Q1_План": 7901, "2020_Q1_Факт": 8026, "2020_Q2_План": 2729, "2020_Q2_Факт": 3703, "2020_Q3_План": 11324, "2020_Q3_Факт": null }, { "Категория": "Вода и чай", "Наименование": "Nestea", "Вид": "зеленый", "2019_Q1_План": 6727, "2019_Q1_Факт": 3557, "2019_Q2_План": 9419, "2019_Q2_Факт": 7154, "2019_Q3_План": 4865, "2019_Q3_Факт": 8354, "2019_Q4_План": 9894, "2019_Q4_Факт": 3630, "2020_Q1_План": 6625, "2020_Q1_Факт": 4867, "2020_Q2_План": 6490, "2020_Q2_Факт": 7137, "2020_Q3_План": 5054, "2020_Q3_Факт": null }, { "Категория": "Вода и чай", "Наименование": "Nestea", "Вид": "черный", "2019_Q1_План": 4227, "2019_Q1_Факт": 6391, "2019_Q2_План": 9905, "2019_Q2_Факт": 3727, "2019_Q3_План": 6048, "2019_Q3_Факт": 7094, "2019_Q4_План": 1802, "2019_Q4_Факт": 8515, "2020_Q1_План": 3215, "2020_Q1_Факт": 7310, "2020_Q2_План": 6900, "2020_Q2_Факт": 2430, "2020_Q3_План": 8053, "2020_Q3_Факт": null }, { "Категория": "Газировка", "Наименование": "Coca-Cola", "Вид": "именная", "2019_Q1_План": 24472, "2019_Q1_Факт": 34804, "2019_Q2_План": 33929, "2019_Q2_Факт": 89941, "2019_Q3_План": 30493, "2019_Q3_Факт": 76823, "2019_Q4_План": 52624, "2019_Q4_Факт": 24603, "2020_Q1_План": 14063, "2020_Q1_Факт": 25760, "2020_Q2_План": 46034, "2020_Q2_Факт": 116866, "2020_Q3_План": 20991, "2020_Q3_Факт": null }, { "Категория": "Газировка", "Наименование": "Coca-Cola", "Вид": "обычная", "2019_Q1_План": 79264, "2019_Q1_Факт": 97234, "2019_Q2_План": 16219, "2019_Q2_Факт": 84329, "2019_Q3_План": 73550, "2019_Q3_Факт": 18992, "2019_Q4_План": 62100, "2019_Q4_Факт": 38215, "2020_Q1_План": 116563, "2020_Q1_Факт": 70574, "2020_Q2_План": 9439, "2020_Q2_Факт": 97530, "2020_Q3_План": 73474, "2020_Q3_Факт": null }, { "Категория": "Газировка", "Наименование": "Coca-Cola", "Вид": "ЧМ по футболу", "2019_Q1_План": 62305, "2019_Q1_Факт": 26480, "2019_Q2_План": 43372, "2019_Q2_Факт": 8192, "2019_Q3_План": 35991, "2019_Q3_Факт": 79566, "2019_Q4_План": 86678, "2019_Q4_Факт": 95586, "2020_Q1_План": 72974, "2020_Q1_Факт": 20648, "2020_Q2_План": 25719, "2020_Q2_Факт": 7475, "2020_Q3_План": 39551, "2020_Q3_Факт": null }, { "Категория": "Газировка", "Наименование": "Fanta", "Вид": "апельсин", "2019_Q1_План": 46885, "2019_Q1_Факт": 52670, "2019_Q2_План": 75007, "2019_Q2_Факт": 32674, "2019_Q3_План": 54392, "2019_Q3_Факт": 98827, "2019_Q4_План": 55508, "2019_Q4_Факт": 59261, "2020_Q1_План": 57747, "2020_Q1_Факт": 46117, "2020_Q2_План": 66955, "2020_Q2_Факт": 46385, "2020_Q3_План": 33583, "2020_Q3_Факт": null }, { "Категория": "Газировка", "Наименование": "Fanta", "Вид": "лимон", "2019_Q1_План": 95360, "2019_Q1_Факт": 38744, "2019_Q2_План": 72305, "2019_Q2_Факт": 24229, "2019_Q3_План": 79387, "2019_Q3_Факт": 74870, "2019_Q4_План": 24303, "2019_Q4_Факт": 12105, "2020_Q1_План": 130435, "2020_Q1_Факт": 27881, "2020_Q2_План": 59939, "2020_Q2_Факт": 19461, "2020_Q3_План": 115060, "2020_Q3_Факт": null }, { "Категория": "Газировка", "Наименование": "Fanta", "Вид": "мандарин", "2019_Q1_План": 9141, "2019_Q1_Факт": 30452, "2019_Q2_План": 48009, "2019_Q2_Факт": 94625, "2019_Q3_План": 25671, "2019_Q3_Факт": 13603, "2019_Q4_План": 25338, "2019_Q4_Факт": 7290, "2020_Q1_План": 6684, "2020_Q1_Факт": 33183, "2020_Q2_План": 66143, "2020_Q2_Факт": 94274, "2020_Q3_План": 26302, "2020_Q3_Факт": null }, { "Категория": "Газировка", "Наименование": "Schweppes", "Вид": "биттер лимон", "2019_Q1_План": 3776, "2019_Q1_Факт": 1342, "2019_Q2_План": 9254, "2019_Q2_Факт": 8066, "2019_Q3_План": 867, "2019_Q3_Факт": 4126, "2019_Q4_План": 1239, "2019_Q4_Факт": 3606, "2020_Q1_План": 2200, "2020_Q1_Факт": 1825, "2020_Q2_План": 6801, "2020_Q2_Факт": 5779, "2020_Q3_План": 519, "2020_Q3_Факт": null }, { "Категория": "Газировка", "Наименование": "Schweppes", "Вид": "стандарт", "2019_Q1_План": 2256, "2019_Q1_Факт": 8429, "2019_Q2_План": 5424, "2019_Q2_Факт": 2277, "2019_Q3_План": 5075, "2019_Q3_Факт": 5980, "2019_Q4_План": 7244, "2019_Q4_Факт": 5890, "2020_Q1_План": 3026, "2020_Q1_Факт": 9576, "2020_Q2_План": 5706, "2020_Q2_Факт": 2439, "2020_Q3_План": 5394, "2020_Q3_Факт": null }, { "Категория": "Газировка", "Наименование": "Sprite", "Вид": "лайм", "2019_Q1_План": 78713, "2019_Q1_Факт": 6360, "2019_Q2_План": 16428, "2019_Q2_Факт": 50940, "2019_Q3_План": 22006, "2019_Q3_Факт": 78749, "2019_Q4_План": 17451, "2019_Q4_Факт": 18304, "2020_Q1_План": 81283, "2020_Q1_Факт": 6605, "2020_Q2_План": 18353, "2020_Q2_Факт": 34838, "2020_Q3_План": 27320, "2020_Q3_Факт": null }, { "Категория": "Газировка", "Наименование": "Sprite", "Вид": "лимон", "2019_Q1_План": 33611, "2019_Q1_Факт": 27844, "2019_Q2_План": 43430, "2019_Q2_Факт": 45570, "2019_Q3_План": 61214, "2019_Q3_Факт": 41838, "2019_Q4_План": 36043, "2019_Q4_Факт": 88945, "2020_Q1_План": 36173, "2020_Q1_Факт": 22024, "2020_Q2_План": 30249, "2020_Q2_Факт": 48453, "2020_Q3_План": 84578, "2020_Q3_Факт": null }, { "Категория": "Газировка", "Наименование": "Sprite", "Вид": "стандарт", "2019_Q1_План": 92443, "2019_Q1_Факт": 89095, "2019_Q2_План": 96447, "2019_Q2_Факт": 26217, "2019_Q3_План": 50179, "2019_Q3_Факт": 32218, "2019_Q4_План": 43077, "2019_Q4_Факт": 83323, "2020_Q1_План": 72492, "2020_Q1_Факт": 86079, "2020_Q2_План": 130907, "2020_Q2_Факт": 21078, "2020_Q3_План": 66070, "2020_Q3_Факт": null }, { "Категория": "Газировка", "Наименование": "Фруктайм", "Вид": "Буратино", "2019_Q1_План": 2704, "2019_Q1_Факт": 8745, "2019_Q2_План": 9496, "2019_Q2_Факт": 5003, "2019_Q3_План": 2073, "2019_Q3_Факт": 9457, "2019_Q4_План": 3396, "2019_Q4_Факт": 7687, "2020_Q1_План": 3589, "2020_Q1_Факт": 10748, "2020_Q2_План": 9717, "2020_Q2_Факт": 7308, "2020_Q3_План": 1615, "2020_Q3_Факт": null }, { "Категория": "Газировка", "Наименование": "Фруктайм", "Вид": "Дюшес", "2019_Q1_План": 3360, "2019_Q1_Факт": 83, "2019_Q2_План": 562, "2019_Q2_Факт": 1554, "2019_Q3_План": 8167, "2019_Q3_Факт": 8936, "2019_Q4_План": 6684, "2019_Q4_Факт": 7655, "2020_Q1_План": 3074, "2020_Q1_Факт": 50, "2020_Q2_План": 414, "2020_Q2_Факт": 1088, "2020_Q3_План": 11204, "2020_Q3_Факт": null }, { "Категория": "Газировка", "Наименование": "Фруктайм", "Вид": "Крем-сода", "2019_Q1_План": 8970, "2019_Q1_Факт": 1717, "2019_Q2_План": 5275, "2019_Q2_Факт": 3572, "2019_Q3_План": 8641, "2019_Q3_Факт": 2930, "2019_Q4_План": 3453, "2019_Q4_Факт": 6080, "2020_Q1_План": 5918, "2020_Q1_Факт": 1384, "2020_Q2_План": 4152, "2020_Q2_Факт": 3351, "2020_Q3_План": 5961, "2020_Q3_Факт": null }, { "Категория": "Газировка", "Наименование": "Фруктайм", "Вид": "Лимонад", "2019_Q1_План": 2486, "2019_Q1_Факт": 9784, "2019_Q2_План": 221, "2019_Q2_Факт": 6297, "2019_Q3_План": 2776, "2019_Q3_Факт": 8595, "2019_Q4_План": 7247, "2019_Q4_Факт": 7344, "2020_Q1_План": 1724, "2020_Q1_Факт": 9848, "2020_Q2_План": 112, "2020_Q2_Факт": 6818, "2020_Q3_План": 2716, "2020_Q3_Факт": null }, { "Категория": "", "Наименование": "Rich", "Вид": "апельсин", "2019_Q1_План": 76983, "2019_Q1_Факт": 27320, "2019_Q2_План": 56234, "2019_Q2_Факт": 73466, "2019_Q3_План": 78602, "2019_Q3_Факт": 86454, "2019_Q4_План": 11865, "2019_Q4_Факт": 32475, "2020_Q1_План": 80787, "2020_Q1_Факт": 28672, "2020_Q2_План": 71101, "2020_Q2_Факт": 87086, "2020_Q3_План": 73078, "2020_Q3_Факт": null }, { "Категория": "", "Наименование": "Rich", "Вид": "клубника", "2019_Q1_План": 48860, "2019_Q1_Факт": 58807, "2019_Q2_План": 70417, "2019_Q2_Факт": 54989, "2019_Q3_План": 11813, "2019_Q3_Факт": 3466, "2019_Q4_План": 60875, "2019_Q4_Факт": 35298, "2020_Q1_План": 43791, "2020_Q1_Факт": 40133, "2020_Q2_План": 61568, "2020_Q2_Факт": 37829, "2020_Q3_План": 16006, "2020_Q3_Факт": null }, { "Категория": "", "Наименование": "Rich", "Вид": "малина", "2019_Q1_План": 40832, "2019_Q1_Факт": 31573, "2019_Q2_План": 56074, "2019_Q2_Факт": 36154, "2019_Q3_План": 3914, "2019_Q3_Факт": 70377, "2019_Q4_План": 80505, "2019_Q4_Факт": 65326, "2020_Q1_План": 32138, "2020_Q1_Факт": 45467, "2020_Q2_План": 81703, "2020_Q2_Факт": 41867, "2020_Q3_План": 2440, "2020_Q3_Факт": null }, { "Категория": "", "Наименование": "Rich", "Вид": "мультифрукт", "2019_Q1_План": 34002, "2019_Q1_Факт": 52308, "2019_Q2_План": 43135, "2019_Q2_Факт": 77166, "2019_Q3_План": 24288, "2019_Q3_Факт": 29031, "2019_Q4_План": 95217, "2019_Q4_Факт": 99170, "2020_Q1_План": 20712, "2020_Q1_Факт": 32508, "2020_Q2_План": 43241, "2020_Q2_Факт": 71286, "2020_Q3_План": 32250, "2020_Q3_Факт": null }, { "Категория": "", "Наименование": "Rich", "Вид": "томат", "2019_Q1_План": 48535, "2019_Q1_Факт": 10514, "2019_Q2_План": 13096, "2019_Q2_Факт": 83541, "2019_Q3_План": 6382, "2019_Q3_Факт": 29087, "2019_Q4_План": 6035, "2019_Q4_Факт": 83753, "2020_Q1_План": 34964, "2020_Q1_Факт": 11887, "2020_Q2_План": 19110, "2020_Q2_Факт": 119702, "2020_Q3_План": 7491, "2020_Q3_Факт": null }, { "Категория": "", "Наименование": "Rich", "Вид": "яблоко", "2019_Q1_План": 93662, "2019_Q1_Факт": 28506, "2019_Q2_План": 49014, "2019_Q2_Факт": 56111, "2019_Q3_План": 97707, "2019_Q3_Факт": 78015, "2019_Q4_План": 40550, "2019_Q4_Факт": 86313, "2020_Q1_План": 128829, "2020_Q1_Факт": 20714, "2020_Q2_План": 57752, "2020_Q2_Факт": 39079, "2020_Q3_План": 71073, "2020_Q3_Факт": null }, { "Категория": "", "Наименование": "Да!", "Вид": "апельсин", "2019_Q1_План": 17670, "2019_Q1_Факт": 11410, "2019_Q2_План": 85507, "2019_Q2_Факт": 41017, "2019_Q3_План": 9832, "2019_Q3_Факт": 91099, "2019_Q4_План": 2910, "2019_Q4_Факт": 44398, "2020_Q1_План": 14966, "2020_Q1_Факт": 15500, "2020_Q2_План": 89580, "2020_Q2_Факт": 26666, "2020_Q3_План": 12934, "2020_Q3_Факт": null }, { "Категория": "", "Наименование": "Да!", "Вид": "клубника", "2019_Q1_План": 57916, "2019_Q1_Факт": 97166, "2019_Q2_План": 58757, "2019_Q2_Факт": 79716, "2019_Q3_План": 68397, "2019_Q3_Факт": 41434, "2019_Q4_План": 23829, "2019_Q4_Факт": 15870, "2020_Q1_План": 37668, "2020_Q1_Факт": 123361, "2020_Q2_План": 70274, "2020_Q2_Факт": 43597, "2020_Q3_План": 77791, "2020_Q3_Факт": null }, { "Категория": "", "Наименование": "Да!", "Вид": "мультифрукт", "2019_Q1_План": 90816, "2019_Q1_Факт": 6525, "2019_Q2_План": 56508, "2019_Q2_Факт": 58973, "2019_Q3_План": 63094, "2019_Q3_Факт": 9928, "2019_Q4_План": 10582, "2019_Q4_Факт": 28902, "2020_Q1_План": 78316, "2020_Q1_Факт": 8168, "2020_Q2_План": 63207, "2020_Q2_Факт": 63012, "2020_Q3_План": 43826, "2020_Q3_Факт": null } ]
VitTurov Автор
06.06.2026 16:12Да, большая модель справляется, но 35B это не локально-на-16-ГБ, а такое ограничение к сожалению есть.

Alex_23_2
06.06.2026 16:12Для gemma-4 по умолчанию дается мало токенов для головы, для llama.cpp нужны доп параметры
image-min-tokens = 560 image-max-tokens = 2240 batch-size = 4096 ubatch-size = 4096тогда полностью конвертируются обе таблицы без ошибок, но контекст конечно надо больше, а не 8000
Это для всех Gemma4

VitTurov Автор
06.06.2026 16:12О, вот это по-настоящему ценно, спасибо.
Похоже, ткнули прямо в корень: по умолчанию llama.cpp выдаёт Gemma мало токенов на картинку – она видит её в низком разрешении, плотные ячейки не читаются, и дальше модель дорисовывает правдоподобное. То есть «врёт» она не от вредности, а оттого, что я ей зрение задушил дефолтным конфигом. Это заметно меняет вывод статьи.Перепрогоню обе таблицы с
--image-max-tokens 2240и поднятымиbatch/ubatch(там ведь non-causal attention – ubatch должен вмещать все токены картинки, иначе падает с ассертом). Заодно проверю, влезет ли это вообще в мои 16 ГБ – и так впритык. Результат отпишу. Спасибо, что поймали до того, как я наломал дров дальше.
badsynt
"Тьмы низких истин нам дороже Вас возвышающий обман"
Немного подправленная цитата - лозунг современных "универсальных" LLM.
Поэтому использовать "это" именно для OCR простеньких таблиц просто глупо. GLM-OCR имеющая неквантованный (F16) GGUF размером 2GB, делает это лучше и быстрее. Но тоже, кстати, способна врать...
PS: А уже Gemm'у можно попросить причесать ту галиматью (иногда), которую выводит GLM в необходимый формат ( markdown или csv)
VitTurov Автор
Вот за что я люблю Хабр, вот за такие комментарии, которые реально расширяют горизонт!
Справедливо, спасибо за наводку – GLM-OCR не щупал, возьму на тест. Для чистого OCR таблиц заточенная 0.9B действительно правильнее, чем 12B-универсал, тут не спорю.
Я писал статью не для того, чтобы сказать: «Gemma – лучший OCR» (она им и не является). Скорее некая карта, где у общего локального VLM ломается зрение и как это ловить. И вы сами назвали ключевое – «тоже способна врать». Вот это и есть суть. Приёмы из статьи (флаг происхождения значений, сверка числа подписей, спасение обрезанного вывода) не зависят от модели – обернут GLM-OCR ровно так же. Ценность не в выборе модели, а в недоверии к её выводу.
Спасибо за пайплайн (GLM извлекает → Gemma причёсывает в csv/markdown) – здравое разделение ролей: специалист читает, универсал форматирует. Возможно, соберу и протестирую на тех же таблицах – честное сравнение.
А «возвышающий обман» – это буквально подзаголовок статьи, так что цитата в точку. Снимаю шляпу!
GidraVydra
А зачем вообще для OCR "простеньких таблиц" нейросеть?
VitTurov Автор
Справедливо, для чистых таблиц классический OCR/парсер дешевле. ценность была в разборе Gemma: даже на таких синтетических задачах, но вопрос честный.