Пайплайн авторазмітки даних з LLM та Snorkel

Проектуємо та впроваджуємо системи штучного інтелекту: від прототипу до production-ready рішення. Наша команда поєднує експертизу в машинному навчанні, дата-інжинірингу та MLOps, щоб AI працював не в лабораторії, а в реальному бізнесі.
Показано 1 з 1Усі 1564 послуг
Пайплайн авторазмітки даних з LLM та Snorkel
Середній
~1-2 тижні
Часті запитання

Напрямки AI-розробки

Етапи розробки AI-рішення

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1358
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1251
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    957
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1188
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    646
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    929

Автоматичне розмічування даних за допомогою LLM та Snorkel

Команди витрачають багато днів на ручне розмічування датасетів для NLP або Computer Vision. Вузьке місце — не архітектура моделі, а якісні розмічені дані. Авторозмічувальні пайплайни скорочують ручну працю на 60–80%, зберігаючи точність вище порогу для навчання. Ми впроваджуємо кастомні пайплайни з LLM, Snorkel та ансамблевими стратегіями — під ключ, з гарантією якості. Snorkel — фреймворк для программатичної розмітки даних (Wikipedia)

Як обрати стратегію авторазмітки?

Кожен пайплайн починається з аналізу розподілу даних, схеми міток та вимог до точності. Обираємо оптимальну стратегію: LLM-розмітка для нюансних задач, слабка розмітка (Snorkel) для великих обсягів, або гібридний ансамбль моделей. Наші інженери реалізували 30+ проєктів авторазмітки — від тональності текстів до детекції об'єктів на зображеннях. Оцінимо ваш датасет і запропонуємо рішення за 2-3 дні.

Чому ансамбль моделей дає кращу точність?

Комбінація правил Snorkel і нейромереж підвищує recall без втрати precision. Ensemble-підхід дає на 15–20% точніше, ніж будь-яка окрема модель, без значної втрати швидкості. Коли weak model і LLM розходяться (ensemble_disagree), такі приклади автоматично надсилаються людині. Це catch-перевірка ловить 100% неоднозначних випадків.

Технічна реалізація авторазмітки

Розмітка через LLM та zero-shot

from anthropic import Anthropic
import numpy as np
import pandas as pd
from dataclasses import dataclass
from typing import Optional

@dataclass
class AutoLabelResult:
    text: str
    predicted_label: str
    confidence: float
    auto_accepted: bool
    method: str  # 'weak_model', 'llm', 'rules', 'ensemble'

class AutoLabelingPipeline:
    def __init__(self, task_type: str, confidence_threshold: float = 0.85):
        self.task_type = task_type
        self.threshold = confidence_threshold
        self.llm = Anthropic()
        self.stats = {'auto_accepted': 0, 'sent_to_review': 0}

    def label_batch(self, texts: list[str],
                    label_schema: list[str],
                    method: str = 'ensemble') -> list[AutoLabelResult]:
        """Авторазмітка батчу текстів"""
        if method == 'llm':
            return self._llm_labeling(texts, label_schema)
        elif method == 'weak_model':
            return self._weak_model_labeling(texts, label_schema)
        elif method == 'ensemble':
            return self._ensemble_labeling(texts, label_schema)
        else:
            raise ValueError(f"Unknown method: {method}")

    def _llm_labeling(self, texts: list[str],
                      label_schema: list[str]) -> list[AutoLabelResult]:
        """LLM-розмітка з оцінкою впевненості"""
        results = []
        batch_size = 10

        for i in range(0, len(texts), batch_size):
            batch = texts[i:i + batch_size]
            texts_formatted = "\n".join([f"{j+1}. {t[:300]}" for j, t in enumerate(batch)])
            labels_str = ", ".join(label_schema)

            response = self.llm.messages.create(
                model="claude-3-5-sonnet-20241022",
                max_tokens=400,
                messages=[{
                    "role": "user",
                    "content": f"""Classify each text. Labels: {labels_str}

Texts:
{texts_formatted}

Return JSON array: [{""label"": ""..."", ""confidence"": 0.0-1.0}]
confidence = how certain you are (0.9+ for obvious cases, 0.5-0.7 for ambiguous)."""
                }]
            )

            try:
                import json
                preds = json.loads(response.content[0].text)
                for text, pred in zip(batch, preds):
                    confidence = pred.get('confidence', 0.5)
                    results.append(AutoLabelResult(
                        text=text,
                        predicted_label=pred['label'],
                        confidence=confidence,
                        auto_accepted=confidence >= self.threshold,
                        method='llm'
                    ))
            except Exception:
                # Fallback: відправити на ручну розмітку
                for text in batch:
                    results.append(AutoLabelResult(
                        text=text,
                        predicted_label='unknown',
                        confidence=0.0,
                        auto_accepted=False,
                        method='llm_failed'
                    ))

        return results

    def _weak_model_labeling(self, texts: list[str],
                              label_schema: list[str]) -> list[AutoLabelResult]:
        """Швидка розмітка через zero-shot модель"""
        from transformers import pipeline

        classifier = pipeline(
            "zero-shot-classification",
            model="facebook/bart-large-mnli",
            device=0
        )

        results = []
        predictions = classifier(texts, candidate_labels=label_schema, batch_size=32)

        for text, pred in zip(texts, predictions):
            confidence = pred['scores'][0]
            # Штраф за близькі scores (невизначеність між лейблами)
            if len(pred['scores']) > 1 and pred['scores'][1] > 0.3:
                confidence *= 0.9

            results.append(AutoLabelResult(
                text=text,
                predicted_label=pred['labels'][0],
                confidence=confidence,
                auto_accepted=confidence >= self.threshold,
                method='weak_model'
            ))

        return results

    def _ensemble_labeling(self, texts: list[str],
                            label_schema: list[str]) -> list[AutoLabelResult]:
        """Комбінація: швидка модель + LLM для невизначених випадків"""
        # Крок 1: Швидка розмітка
        weak_results = self._weak_model_labeling(texts, label_schema)

        # Крок 2: LLM для невизначених
        uncertain_indices = [
            i for i, r in enumerate(weak_results)
            if not r.auto_accepted and r.confidence > 0.5  # Не зовсім провал
        ]
        uncertain_texts = [texts[i] for i in uncertain_indices]

        if uncertain_texts:
            llm_results = self._llm_labeling(uncertain_texts, label_schema)
            for idx, llm_result in zip(uncertain_indices, llm_results):
                # Якщо моделі згодні — підвищуємо впевненість
                if llm_result.predicted_label == weak_results[idx].predicted_label:
                    combined_confidence = (weak_results[idx].confidence + llm_result.confidence) / 2 + 0.1
                    weak_results[idx].confidence = min(combined_confidence, 1.0)
                    weak_results[idx].auto_accepted = combined_confidence >= self.threshold
                    weak_results[idx].method = 'ensemble_agree'
                else:
                    # Розбіжність — відправити людині
                    weak_results[idx].auto_accepted = False
                    weak_results[idx].method = 'ensemble_disagree'

        return weak_results

Слабка розмітка з Snorkel

from snorkel.labeling import labeling_function, PandasLFApplier
from snorkel.labeling.model import LabelModel
import re

# Константи міток
NEGATIVE, ABSTAIN, POSITIVE = -1, -2, 0

@labeling_function()
def lf_contains_positive_words(x):
    positive_words = ['excellent', 'great', 'amazing', 'love', 'perfect', 'отлично', 'супер', 'замечательно']
    return POSITIVE if any(w in x.text.lower() for w in positive_words) else ABSTAIN

@labeling_function()
def lf_contains_negative_words(x):
    negative_words = ['terrible', 'awful', 'worst', 'hate', 'horrible', 'ужасно', 'плохо', 'отстой']
    return NEGATIVE if any(w in x.text.lower() for w in negative_words) else ABSTAIN

@labeling_function()
def lf_rating_pattern(x):
    match = re.search(r'(\d)[/из]\s*5', x.text)
    if match:
        rating = int(match.group(1))
        if rating >= 4:
            return POSITIVE
        elif rating <= 2:
            return NEGATIVE
    return ABSTAIN

@labeling_function()
def lf_exclamation_positive(x):
    if x.text.count('!') >= 2 and len(x.text) < 100:
        return POSITIVE
    return ABSTAIN

def train_label_model(df: pd.DataFrame) -> pd.Series:
    """Snorkel: об'єднання слабких labeling functions"""
    lfs = [lf_contains_positive_words, lf_contains_negative_words,
           lf_rating_pattern, lf_exclamation_positive]

    applier = PandasLFApplier(lfs=lfs)
    L_train = applier.apply(df=df)

    # Навчання generative model
    label_model = LabelModel(cardinality=2, verbose=True)
    label_model.fit(L_train=L_train, n_epochs=500, lr=0.001)

    return label_model.predict(L=L_train)

Моніторинг якості та налаштування порогів

Як контролювати точність авторазмітки?

Для верифікації даних ми використовуємо золоті приклади (до 5% датасету), які дозволяють постійно моніторити точність авторазмітки та своєчасно коригувати пороги або правила. Моніторинг через золоті приклади — стандартна практика, що знижує ризик накопичення помилок.

class AutoLabelQualityMonitor:
    """Контроль якості через золоті приклади"""

    def __init__(self, gold_samples: list[dict]):
        """gold_samples: [{text, true_label}]"""
        self.gold = gold_samples

    def evaluate_accuracy(self, pipeline: AutoLabelingPipeline) -> dict:
        """Точність авторазмітки на золотих прикладах"""
        texts = [g['text'] for g in self.gold]
        true_labels = [g['true_label'] for g in self.gold]
        label_schema = list(set(true_labels))

        results = pipeline.label_batch(texts, label_schema, method='ensemble')

        correct = sum(
            1 for r, true in zip(results, true_labels)
            if r.predicted_label == true
        )
        auto_accepted_correct = sum(
            1 for r, true in zip(results, true_labels)
            if r.auto_accepted and r.predicted_label == true
        )
        auto_accepted_total = sum(1 for r in results if r.auto_accepted)

        return {
            'overall_accuracy': correct / len(results),
            'auto_accepted_accuracy': (
                auto_accepted_correct / auto_accepted_total
                if auto_accepted_total > 0 else 0
            ),
            'auto_acceptance_rate': auto_accepted_total / len(results),
            'review_queue_size': len(results) - auto_accepted_total
        }

Порівняння методів авторазмітки

Метод Швидкість Точність Коли використовувати
Snorkel (правила) висока (100k записів/хв) 70-85% (з ручним налаштуванням) Великі обсяги, прості патерни
Zero-shot (BART) середня (1k зап./хв) 80-90% Немає розмічених даних, є мітки класів
LLM (Claude/GPT-4) низька (30 зап./хв) 92-98% Складні нюансні задачі, висока точність
Ensemble (Snorkel + LLM) середня 95-97% Баланс швидкості та точності в продакшні

Економія ресурсів та вибір порогу confidence

Поріг confidence Auto-accept rate Точність автоприйнятих Ручна робота
0.95 35% 98.5% 65% завдань
0.90 52% 97.2% 48% завдань
0.85 68% 95.8% 32% завдань
0.80 78% 93.1% 22% завдань
0.70 89% 88.4% 11% завдань

Оптимальний поріг для більшості задач класифікації — 0.85–0.90. Скорочення ручної роботи на 65–70% при точності автоприйнятих прикладів 95–97%. Економія бюджету на розмітку до 80% за рахунок автоматизації. Окупність впровадження — менше двох тижнів.

Вибір порогу confidence залежить від ціни помилки. Якщо хибна класифікація критична (медична діагностика) — ставте 0.95, жертвуючи швидкістю. Для масових задач (тональність відгуків) — 0.85 дає кращий баланс. Ми допомагаємо підібрати поріг експериментально за 1-2 дні на ваших даних — гарантуємо, що точність авторазмітки буде не нижче обумовленої.

Процес впровадження та типові помилки

Покрокове налаштування пайплайну

  1. Аналіз датасету: оцінюємо розподіл міток, обсяг, наявність шуму.
  2. Вибір моделей: LLM (Claude 3.5) для складних, zero-shot для простих.
  3. Створення правил Snorkel: від 10 до 50+ labeling functions.
  4. Інтеграція обчислень: код об'єднує слабкі мітки в єдиний датасет.
  5. Запуск пілоту: розмічаємо 1000 прикладів, звіряємо з золотими.
  6. Коригування порогу: підбираємо confidence threshold за ROC-кривою.
  7. Виробничий прогін: full pipeline з моніторингом.

Що найчастіше йде не так?

  • Сліпа довіра порогу без урахування складності класів: для рідкісного класу точність може бути нижчою.
  • Використання лише однієї моделі: ансамбль завжди надійніший.
  • Відсутність золотих прикладів: без них ви не дізнаєтесь якість.
  • Занадто низький поріг заради економії: призводить до накопичення помилок.

Результати та економічна ефективність

Приклад з практики

Для клієнта з датасетом у 50 000 відгуків (задача тональності) ми впровадили ensemble-пайплайн з порогом 0.85. Результат: 95% точність на авторозмічених прикладах, ручна робота скоротилася з 40 до 12 людино-днів — прискорення в 3,3 рази. Окупність впровадження — менше двох тижнів.

Зв'яжіться з нами для оцінки вашого датасету — ми підберемо оптимальну стратегію авторазмітки. Оцінимо проєкт безкоштовно за 2-3 дні. Отримайте консультацію щодо впровадження пайплайну та дізнайтеся, як автоматизувати розмітку ваших даних.

Чому дата-інжиніринг визначає успіх ML-моделі

Минулого року до нас звернулася компанія, яка витратила $50 000 на навчання NLP-моделі, але отримала лише 60% точності на продакшені. Причина — data leakage через випадковий split часових даних. Перед тим як навчати модель, потрібно зрозуміти структуру даних: чи є дублі, як часто змінюється схема, наскільки репрезентативна вибірка. Дата-інжиніринг для ML — це не просто ETL, а побудова відтворюваної інфраструктури, яка робить навчання надійним, а перенавчання — передбачуваним. За досвідом нашої команди (понад 8 років у дата-інжинірингу, 30+ проектів у ML) кожна друга проблема в продакшені пов’язана не з архітектурою моделі, а з якістю даних. Замовте аудит ваших даних — оцінимо поточний пайплайн безкоштовно.

Як ETL-пайплайни для ML відрізняються від BI

ETL для аналітики та ETL для ML — різні завдання. В аналітиці важлива агрегація, у ML — індивідуальні записи з історією. В аналітиці train/val/test split не потрібен, у ML — критичний. В аналітиці skew даних заважає інтерпретації, у ML — безпосередньо впливає на якість моделі.

Інструменти. Apache Spark для великих обсягів (10GB+): PySpark з DataFrames, оптимізації через partitioning та caching. dbt для трансформацій поверх DWH (Snowflake, BigQuery, Redshift) — декларативно, версіонується, тестується. Pandas + Polars для обсягів до кількох GB — Polars у 5–10x швидше за Pandas на типових трансформаціях.

Temporal splits. Для ML важливо, що split за часом, а не випадковий. Якщо дані часові (транзакції, події користувачів), випадковий split дає data leakage: модель бачить «майбутні» дані при навчанні. Правило: train на періоді T1–T2, validation на T2–T3 (з gap для запобігання leakage), test на T3–T4. Неправильний split може коштувати 10–15% якості моделі на валідації. Temporal split best practices (scikit-learn docs)

Інкрементальні пайплайни. Модель перенавчається щотижня на нових даних. Потрібен пайплайн, який інкрементально додає нові записи до навчальної вибірки, не перевантажуючи все з нуля. Delta Lake або Apache Iceberg — формати з ACID-транзакціями, Change Data Capture, time travel.

Як уникнути training-serving skew за допомогою Feature Store

Feature Store вирішує проблему розсинхронізації між навчанням та інференсом. Найпідступніша помилка в ML-інфраструктурі — training-serving skew: ознака обчислюється по-різному в навчанні та в продакшені. Модель вчиться на «правильних» даних, а інференс отримує інші.

Feast (open source) — офлайн store на Parquet/Delta в S3 для навчання, онлайн store на Redis для low-latency інференсу (<10ms). Feature definitions як Python-код:

from feast import FeatureView, Field
from feast.types import Float32, Int64

user_features = FeatureView(
    name="user_features",
    entities=["user_id"],
    schema=[
        Field(name="purchase_count_7d", dtype=Int64),
        Field(name="avg_session_duration", dtype=Float32),
    ],
    ttl=timedelta(days=7),
    source=user_features_source,
)

Один definition використовується всюди — немає розбіжностей.

Потокові ознаки. Коли ознака має оновлюватися в реальному часі (кількість транзакцій за останні 10 хвилин), потрібна потокова обробка. Apache Kafka + Apache Flink або Kafka Streams для обчислення ознак у реальному часі → запис в онлайн store. Складніше, дорожче, потрібно лише коли staleness ознак критична для якості.

Розмітка даних: як не витратити бюджет даремно

Розмітка — найтрудомісткіша та недооцінювана частина ML-проекту. Погано розмічені дані не виправить жодна архітектура.

Label Studio — open source, підтримує розмітку зображень (bounding box, polygon, segmentation), тексту (NER, класифікація), аудіо, відео. Піднімається за 10 хвилин через Docker. Для невеликих команд — перший вибір.

Оцінка якості розмітки. Inter-annotator agreement — наскільки згодні розмітники між собою. Cohen's Kappa > 0.8 — добре, 0.6–0.8 — прийнятно, < 0.6 — завдання неоднозначне або інструкція погана. Перетин розміток (10–20% прикладів розмічають два незалежних анотатори) — обов'язкова практика.

Active learning. Не розмічати випадкові приклади, а вибирати ті, на яких модель найбільш невпевнена (low confidence, high uncertainty). Дозволяє досягти тієї ж якості при 50–70% обсягу розмітки. Modals, Prodigy, Label Studio підтримують active learning workflows. На одному з проектів для NLP ми скоротили бюджет на розмітку в 2,5 рази завдяки active learning — економія склала $15 000 на 100 000 розмічених прикладів.

Синтетичні дані. Коли реальних даних мало або отримати їх дорого. Для CV: рендеринг у Blender/Unity з реалістичними текстурами (domain randomization). Для NLP: parafrase через LLM, backtranslation. Ризик: модель навчається на distribution синтетичних даних, а не реальних — потрібна обережність і перевірка на реальному holdout.

Якість даних: валідація та моніторинг

Great Expectations — de facto стандарт для data validation у ML-пайплайнах. Expectations — це декларативні твердження про дані: «колонка age містить значення від 0 до 120», «колонка user_id не містить null», «розподіл amount не відхиляється більш ніж на 20% від baseline». Запускається в пайплайні, при провалі — блокує проходження.

Pandera — Pythonic alternative для pandas/polars DataFrames. Schema-based validation з type hints:

import pandera as pa

schema = pa.DataFrameSchema({
    "user_id": pa.Column(int, nullable=False),
    "score": pa.Column(float, pa.Check.between(0, 1)),
    "label": pa.Column(str, pa.Check.isin(["positive", "negative", "neutral"])),
})

Data freshness. Модель очікує дані за останні N днів. ETL впав, дані не оновилися — модель використовує застарілі ознаки. Моніторинг свіжості даних: timestamp останнього запису в кожній таблиці, алерт при затримці > порога.

Дедуплікація. Дублікати в навчальній вибірці завищують метрики (одні й ті самі приклади в train і val) і спотворюють ваги моделі. MinHash LSH для наближеної дедуплікації великих датасетів. Для точної — хеш за нормалізованим контентом.

Інструмент Область застосування Коли вибирати
Great Expectations Універсальна, таблиці, пайплайни Великі команди, багато метаданих
Pandera pandas/polars DataFrames Python-centric проекти, type hints
Deequ Apache Spark, великі дані Якщо пайплайн вже на Spark

Сховища та формати

Формат Найкраще для Особливості
Parquet Батчеве навчання, аналітика Columnar, ефективне стиснення
Delta Lake Інкрементальні апдейти, ACID Time travel, schema evolution
Apache Iceberg Enterprise, multi-engine Найкращий catalog, hidden partitioning
HDF5 Числові масиви (CV датасети) Ієрархічна структура
TFDS / datasets Стандартизовані ML датасети Hugging Face datasets — зручний для NLP

Для більшості ML-проектів на старті: Parquet в S3 + DVC для версіонування. Delta Lake або Iceberg — коли з'являється потреба в інкрементальних оновленнях або time travel.

Типові помилки при побудові пайплайнів

  • Пропуск перевірки свіжості даних. Якщо ETL падає вночі, а модель запускається вранці — вона отримує дані 24-годинної давності. Рішення: алерт при затримці > 30 хвилин.
  • Відсутність версіонування даних. Не можна відтворити експеримент, бо дані змінилися. DVC або Delta Lake time travel виправляють це.
  • Забувають про schema evolution. Нове поле з’являється, а пайплайн падає. Автоматичне виявлення змін схеми через Great Expectations.

Active learning дозволяє скоротити бюджет на розмітку до 50–70%. На одному проекті це склало економію $15 000 на 100 000 розмічених прикладів. Закажіть консультацію — розрахуємо потенційну економію для вашого кейсу.

Що входить у проект з дата-інжинірингу для ML

Ми надаємо повний цикл:

  • Аудит існуючих даних та пайплайнів (1 тиждень).
  • Проектування архітектури: вибір інструментів, форматів, способів розмітки.
  • Реалізація ETL/ELT пайплайну з валідацією та моніторингом.
  • Документація коду та процесів (model card, data card).
  • Навчання вашої команди роботі з пайплайном.
  • SLA на супровід та підтримку.

Терміни: від 2 до 6 тижнів залежно від обсягу даних і складності інтеграцій.

Як ми будуємо пайплайн: покроково

  1. Аудит існуючих даних. Профілювання: ydata-profiling (колишній pandas-profiling) генерує HTML-репорт зі статистиками, дистрибуціями, кореляціями, missing values за хвилини.
  2. Проектування пайплайну. Визначаємо джерела даних, частоту оновлення, вимоги до latency ознак, обсяги.
  3. Реалізація та тестування. Unit-тести на трансформації, integration-тести на пайплайн, data validation через Great Expectations.
  4. Деплой та моніторинг. Алерти на freshness, quality checks, аномалії в обсягах даних.

Чому варто довірити це нам

Ми займаємося дата-інжинірингом та ML з понад 8-річним досвідом. За цей час реалізували понад 40 проектів — від побудови пайплайнів для NLP-моделей до розмітки датасетів для комп’ютерного зору. Гарантуємо відтворюваність пайплайнів та повну прозорість процесів. У кожному проекті використовуємо інструменти з відкритим кодом, щоб ви не були прив’язані до вендора.

Зв’яжіться з нами для безкоштовного аудиту ваших даних — оцінимо поточний пайплайн і запропонуємо roadmap. Замовте побудову ML-пайплайну під ключ.