Разметка данных нейросетью: датасет на 50 000 строк дешёвой моделью

Как разметить датасет дешёвой моделью: инструкция, JSON-ответ, скрипт на Python, проверка выборки и сколько стоят 50 000 строк.

6 мин · · автоматизация · экономия

Разметить вручную несколько десятков тысяч строк — это недели работы или подряд на асессоров. Разметка данных нейросетью сводится к скрипту: дешёвая модель читает каждую строку по вашей инструкции и возвращает метку в JSON, а человек проверяет случайную выборку. Ниже — промпт, код на Python, способ проверить качество и расчёт стоимости 50 000 строк на трёх моделях.

Когда разметку можно отдать модели

Хорошо подходят задачи с фиксированным набором ответов:

  • классификация по списку меток: тема обращения, тональность отзыва, категория товара;
  • флаги «да/нет»: есть ли в тексте контакт, спам ли это, упоминается ли конкурент;
  • извлечение коротких полей: город, номер заказа, сумма.

Плохо подходят задачи, где сами люди спорят о правильном ответе, и задачи, где для метки нужны знания, которых нет в тексте строки.

Простое правило: если вы можете написать инструкцию, по которой два разных человека разметят одинаково, эту инструкцию можно отдать модели. Если не можете — модель тоже будет угадывать.

Инструкция: промпт, который даёт стабильный ответ

Инструкция одна на весь датасет и уходит в системном сообщении. В ней должно быть:

  1. Список меток с определением каждой — одно-два предложения.
  2. Правила для пограничных случаев: что делать, если в обращении две темы.
  3. Три–пять примеров «текст → метка».
  4. Отдельная метка для непонятного (unclear), чтобы модель не угадывала.
  5. Жёсткий формат ответа.
bash
Ты размечаешь обращения клиентов интернет-магазина.
Выбери ровно одну метку:
- billing — оплата, возврат денег, чеки, списания;
- delivery — сроки, курьер, пункт выдачи, трек-номер;
- quality — брак, не тот товар, некомплект;
- other — всё остальное, что понятно по тексту;
- unclear — текст пустой, бессмысленный или тему нельзя определить.
Если тем несколько, выбирай ту, с которой клиент начинает.

Примеры:
"Деньги списали дважды" -> billing
"Курьер не приехал, трек не обновляется" -> delivery

Ответь только JSON без пояснений: {"label": "<метка>"}

Почему JSON, а не просто слово: ответ разбирается одной строкой кода, лишний текст сразу виден как ошибка, а при необходимости в объект легко добавить второе поле — например, извлечённый номер заказа.

Скрипт разметки на Python

Скрипт читает data.csv с колонками id и text, пишет результат построчно в labels.jsonl и при перезапуске пропускает уже размеченные строки. Нужен Python 3.9+ и пакет openai.

bash
import csv
import json
import os

from openai import OpenAI

client = OpenAI(base_url="https://api.zukko.pro/v1", api_key=os.environ["ZUKKO_API_KEY"])
MODEL = "deepseek-v4.1-flash"
LABELS = {"billing", "delivery", "quality", "other", "unclear"}
with open("instruction.txt", encoding="utf-8") as f:
    INSTRUCTION = f.read()


def label_row(text: str) -> str:

Что здесь важно:

  • temperature=0 уменьшает разброс ответов между повторами.
  • Заголовок X-Zukko-Cache: true включает кэш ответов Zukko: одинаковый запрос (та же модель, то же тело) второй раз отдаётся бесплатно. Дубликаты строк в датасете и повторный прогон после падения не стоят денег. Подробности — в документации по лимитам и кэшу.
  • Метки __parse_error__ и __bad_label__ не теряются: такие строки уходят на ручную проверку или повторный прогон с исправленной инструкцией.

Один поток на 50 000 строк работает долго, поэтому на практике запросы распараллеливают. Как это делать без упора в ограничения провайдера, разобрано в статье о массовой обработке запросов.

Проверка выборки человеком

Без проверки вы не знаете, сколько в разметке ошибок. Проверка состоит из двух шагов.

Пилот до основного прогона

Разметьте вручную 200–300 строк — это ваш эталон. Прогоните по ним две-три модели с одной инструкцией и посчитайте долю совпадений с эталоном для каждой. Посмотрите, на каких метках модели ошибаются, и поправьте инструкцию: обычно ошибки собираются вокруг одного-двух пограничных случаев.

Пилот стоит копейки даже на более дорогой модели. Для 300 строк по 550 входных и 40 выходных токенов на claude-sonnet-5 (0.2 / 1.013 $ за 1M): 300 × 550 = 165 000 входных токенов × 0.2 / 1 000 000 = $0.033, плюс 300 × 40 = 12 000 выходных × 1.013 / 1 000 000 ≈ $0.012. Итого около $0.045.

Выборка после прогона

Возьмите случайные строки из готовой разметки и проверьте их руками. Сколько строк нужно, считается по стандартной формуле для доли: n = z² × p × (1 − p) / e². Для 95% уверенности (z = 1.96), худшего случая p = 0.5 и погрешности e = 5 процентных пунктов: 1.96² × 0.25 / 0.05² ≈ 384. Около 400 случайных строк дают долю ошибок с точностью ±5 п.п. Строки с меткой unclear и ошибками разбора проверяйте все, они не входят в случайную выборку.

Сколько стоит разметка 50 000 строк

Допущения: инструкция — 400 токенов, текст строки — в среднем 150 токенов, ответ {"label": "delivery"} — до 40 токенов с учётом служебных.

  • Вход: 50 000 × (400 + 150) = 27 500 000 токенов = 27.5M.
  • Выход: 50 000 × 40 = 2 000 000 токенов = 2M.
  • Формула: стоимость = 27.5 × цена входа + 2 × цена выхода (цены в $ за 1M).
МодельВход / выход, $ за 1MВходВыходИтогоИндекс силы
deepseek-v4.1-flash0.013 / 0.03827.5 × 0.013 = $0.3582 × 0.038 = $0.076≈ $0.4339
gemini-3.8-flash0.025 / 0.08827.5 × 0.025 = $0.6882 × 0.088 = $0.176≈ $0.8641
glm-5.3-flash0.04 / 0.0427.5 × 0.04 = $1.102 × 0.04 = $0.08$1.1842

Для сравнения, та же разметка на claude-sonnet-5: 27.5 × 0.2 + 2 × 1.013 = 5.5 + 2.026 ≈ $7.53.

Две оговорки. Первая: сколько токенов занимает ваш текст, зависит от токенизатора модели. Прогоните 100 строк, посмотрите resp.usage.prompt_tokens и resp.usage.completion_tokens и подставьте свои средние в формулу; посчитать токены до запуска можно и через count_tokens. Вторая — кэш промптов: у DeepSeek и GLM чтение кэша стоит столько же, сколько вход, поэтому повторяющаяся инструкция дешевле не становится. У Gemini 3.8 Flash чтение кэша дешевле входа (0.013 против 0.025), но попадёт ли инструкция в кэш и сообщит ли поставщик о чтении в ответе, зависит от провайдера — фактическое разбиение видно в истории расходов. Записи кэша у этих моделей отдельно нет.

Индекс силы в таблице — сводный балл Artificial Analysis по общим бенчмаркам, а не по вашей разметке. Выбирайте не самую дешёвую модель, а самую дешёвую из тех, что прошли ваш пилот с нужной долей совпадений. Если ни одна дешёвая не дотягивает, трудные строки можно отдавать сильной модели — схема разобрана в статье о каскаде моделей. Актуальный список и цены — GET https://api.zukko.pro/v1/models или страница «Модели» в кабинете.

Для разметки удобно завести отдельный ключ с дневным лимитом: если скрипт зациклится, он остановится на потолке, а не на нуле баланса. Ключ создаётся в разделе API-ключи кабинета.

Частые вопросы

Можно ли попросить модель указать свою уверенность?

Можно добавить поле, но самооценка модели не проверена на вашей задаче. Опирайтесь на долю совпадений в пилоте и в случайной выборке, а не на число, которое модель написала сама.

Что делать, если модель отвечает не JSON?

Скрипт выше снимает обёртку из тройных обратных кавычек и помечает остальное как __parse_error__. Если таких строк больше пары процентов, ужесточите формулировку формата в инструкции и добавьте пример ответа.

Сколько строк проверять руками?

Около 400 случайных строк дают оценку доли ошибок с точностью ±5 процентных пунктов при 95% уверенности. Плюс все строки с unclear и ошибками разбора.

Как не платить дважды при перезапуске?

Скрипт пропускает уже записанные id, а заголовок X-Zukko-Cache: true отдаёт одинаковые запросы из кэша бесплатно. Кэш хранит успешные ответы до 7 дней.

zukko

Платите в 25×
меньше

за Claude Code & ChatGPT

Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.