Подготовка высококачественного обучающего набора данных
Соберите, очистите и отформатируйте данные для следования инструкциям в форматах Alpaca и ShareGPT, удалите дубликаты и разделите данные на обучающую и проверочную выборки.
«Подготовка высококачественного обучающего набора данных» — бесплатный урок AI Engineering Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Engineering Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Engineering Academy содержит 4 уроков всего.
Данные — самый важный фактор дообучения
При дообучении качество обучающих данных важнее любых гиперпараметров, выбора архитектуры или метода обучения. 100 высококачественных примеров превосходят 10 000 посредственных. Что заложено в данных, то и получим на выходе: дообученная модель добросовестно воспроизведёт все присутствующие в них закономерности, включая ошибки, предубеждения и несогласованности формата. Инвестиции в качество данных — самое эффективное действие в любом проекте дообучения.
Форматы следования инструкциям
В большинстве случаев дообучение для задач следования инструкциям использует разговорный формат сообщений с ролями системы, пользователя и ассистента. API дообучения OpenAI использует файлы JSONL, где каждая строка содержит полный пример разговора. Также широко применяются формат Alpaca (инструкция/вход/выход) и формат ShareGPT (список разговоров). Выберите формат, соответствующий платформе дообучения, которую планируете использовать.
import json
# OpenAI fine-tuning format (JSONL)
# Each line is one training example
openai_example = {
'messages': [
{'role': 'system', 'content': 'You are a JSON extraction agent.'},
{'role': 'user', 'content': 'Extract: "John Smith, age 32, from Seattle, joined 2023-01-15"'},
{'role': 'assistant', 'content': '{"name": "John Smith", "age": 32, "city": "Seattle", "join_date": "2023-01-15"}'}
]
}
# Alpaca format
alpaca_example = {
'instruction': 'Extract structured data from the following text.',
'input': 'John Smith, age 32, from Seattle, joined 2023-01-15',
'output': '{"name": "John Smith", "age": 32, "city": "Seattle", "join_date": "2023-01-15"}'
}
# Write as JSONL
with open('train.jsonl', 'w') as f:
f.write(json.dumps(openai_example) + '\n')
# Add more examples here...Сбор обучающих данных: три стратегии
Существует три основные стратегии создания набора данных для дообучения. Создание людьми: эксперты вручную пишут идеальные примеры — самое высокое качество, но это медленно и дорого. Создание с помощью LLM: мощная модель (GPT-4o) генерирует примеры, которые затем проверяют люди — значительно быстрее и дешевле, а при проверке качество получается хорошим. Извлечение из журналов: из существующих рабочих журналов извлекаются пары входных и выходных данных, после чего отбираются высококачественные примеры с помощью сигналов качества, таких как оценки пользователей или оценивание по принципу «LLM в роли судьи».
from openai import OpenAI
client = OpenAI()
def generate_training_example_with_gpt4o(task_description: str, example_input: str) -> dict:
'''Use GPT-4o to generate a training example for a smaller model.'''
prompt = f'''You are creating a training example for fine-tuning a smaller model.
Task: {task_description}
Given this input:
{example_input}
Write the ideal assistant response that demonstrates the correct behavior for this task.
Be specific, accurate, and follow the expected format precisely.'''
response = client.chat.completions.create(
model='gpt-4o', # teacher model
messages=[{'role': 'user', 'content': prompt}]
)
return {
'messages': [
{'role': 'system', 'content': task_description},
{'role': 'user', 'content': example_input},
{'role': 'assistant', 'content': response.choices[0].message.content}
]
}Фильтрация и проверка качества
Каждый обучающий пример должен пройти проверку качества до включения в набор. Проверьте, что ответ имеет правильный формат, точен (для фактических задач), не содержит галлюцинаций или вредоносного содержимого, пара «инструкция — ответ» логична, а пример представляет рабочий сценарий использования. Для проверки формата используйте автоматическую валидацию, для оценки содержания — LLM в роли судьи, а для выборки примеров — проверку человеком.
import json
def validate_training_example(example: dict, schema_validator=None) -> dict:
issues = []
# Format check
if 'messages' not in example:
issues.append('Missing messages field')
return {'valid': False, 'issues': issues}
messages = example['messages']
if not any(m['role'] == 'assistant' for m in messages):
issues.append('No assistant message found')
# Check assistant message quality
assistant_content = next((m['content'] for m in messages if m['role'] == 'assistant'), '')
if len(assistant_content) < 5:
issues.append('Assistant response too short')
# Schema validation for JSON output tasks
if schema_validator:
try:
parsed = json.loads(assistant_content)
schema_validator(parsed) # raises if invalid
except json.JSONDecodeError:
issues.append('Assistant response is not valid JSON')
except Exception as e:
issues.append(f'Schema validation failed: {str(e)}')
return {'valid': len(issues) == 0, 'issues': issues}
# Run validation on all examples before training
examples = load_training_examples('raw_dataset.jsonl')
valid_examples = [e for e in examples if validate_training_example(e)['valid']]
print(f'Valid examples: {len(valid_examples)}/{len(examples)}')Удаление дубликатов данных
Дубликаты и почти дубликаты в обучающих данных вредны. Они заставляют модель переобучаться на конкретных примерах, расходуя ресурсы обучения, которые могли бы пойти на освоение разнообразных закономерностей. Выполните удаление дубликатов до окончательной подготовки набора данных. Точное удаление дубликатов использует хеширование для поиска идентичных примеров. Удаление почти дубликатов использует MinHash или сходство векторных представлений для поиска примеров, различающихся лишь несущественными деталями.
import hashlib
from datasketch import MinHash, MinHashLSH
def exact_deduplicate(examples: list[dict]) -> list[dict]:
seen_hashes = set()
unique = []
for ex in examples:
# Hash the user and assistant messages
content = str(ex['messages'])
h = hashlib.md5(content.encode()).hexdigest()
if h not in seen_hashes:
seen_hashes.add(h)
unique.append(ex)
print(f'Exact dedup: {len(examples)} -> {len(unique)} ({len(examples)-len(unique)} removed)')
return unique
def near_deduplicate_by_input(examples: list[dict], similarity_threshold=0.85) -> list[dict]:
# Build index of input texts
inputs = [next((m['content'] for m in ex['messages'] if m['role'] == 'user'), '') for ex in examples]
lsh = MinHashLSH(threshold=similarity_threshold, num_perm=128)
unique_indices = set()
for i, text in enumerate(inputs):
m = MinHash(num_perm=128)
for word in text.lower().split():
m.update(word.encode('utf-8'))
if not lsh.query(m): # no similar items found
lsh.insert(str(i), m)
unique_indices.add(i)
return [examples[i] for i in sorted(unique_indices)]Разделение на обучающую и проверочную выборки
Разделите набор данных на обучающую и проверочную выборки до начала любого запуска дообучения. Проверочная выборка используется для контроля переобучения во время обучения: если ошибка на проверочной выборке растёт, а ошибка на обучающей снижается, модель переобучается. Типичное разделение — 90% на обучение и 10% на проверку. Убедитесь, что разделение выполнено случайным образом и стратифицировано, если в наборе есть значимые категории (например, оба набора содержат одинаковое представительство всех типов намерений).
import random
import json
def split_dataset(examples: list[dict], val_fraction=0.1, seed=42) -> tuple[list, list]:
random.seed(seed) # reproducible split
shuffled = examples.copy()
random.shuffle(shuffled)
n_val = max(1, int(len(shuffled) * val_fraction))
val_set = shuffled[:n_val]
train_set = shuffled[n_val:]
print(f'Train: {len(train_set)} examples, Validation: {len(val_set)} examples')
return train_set, val_set
def save_jsonl(examples: list[dict], path: str):
with open(path, 'w') as f:
for ex in examples:
f.write(json.dumps(ex) + '\n')
# Split and save
examples = load_training_examples('clean_dataset.jsonl')
train, val = split_dataset(examples, val_fraction=0.1)
save_jsonl(train, 'train.jsonl')
save_jsonl(val, 'validation.jsonl')
print(f'Saved train.jsonl ({len(train)}) and validation.jsonl ({len(val)})')Балансировка набора данных
Несбалансированные наборы данных заставляют дообученные модели чрезмерно специализироваться на распространённых случаях и ошибаться на редких, но важных. Если в наборе 900 примеров категории A и 100 примеров категории B, модель может научиться всегда предсказывать A. Сбалансируйте набор данных: увеличьте долю меньшинства (дублируйте редкие примеры), уменьшите долю большинства или сгенерируйте синтетические примеры для недостаточно представленных случаев с помощью GPT-4o.
from collections import Counter
import random
def analyze_distribution(examples: list[dict], category_extractor) -> dict:
categories = [category_extractor(ex) for ex in examples]
counts = Counter(categories)
print('Category distribution:')
for cat, count in counts.most_common():
print(f' {cat}: {count} ({100*count/len(examples):.1f}%)')
return counts
def oversample_minority(examples: list[dict], category_extractor, target_count: int) -> list[dict]:
by_category = {}
for ex in examples:
cat = category_extractor(ex)
by_category.setdefault(cat, []).append(ex)
balanced = []
for cat, cat_examples in by_category.items():
if len(cat_examples) < target_count:
# Oversample with replacement
oversampled = random.choices(cat_examples, k=target_count)
balanced.extend(oversampled)
else:
# Undersample to target_count
balanced.extend(random.sample(cat_examples, target_count))
random.shuffle(balanced)
return balancedОчистка и нормализация данных
Обучающие данные часто содержат несогласованности, ухудшающие дообучение: разный регистр букв в полях вывода, пробелы в конце строк, непоследовательное использование кавычек, разные форматы чисел или различающиеся соглашения об именовании ключей JSON. Нормализуйте эти данные до начала обучения. Дообученная модель выучит точное форматирование, присутствующее в данных: если данные несогласованны, модель воспроизведёт эту несогласованность.
import json
import re
def normalize_json_output_example(example: dict) -> dict:
'''Normalize JSON output in assistant messages for consistency.'''
messages = example.get('messages', [])
normalized = []
for msg in messages:
if msg['role'] == 'assistant':
content = msg['content'].strip()
# Try to parse and re-serialize JSON for consistent formatting
try:
parsed = json.loads(content)
# Normalize: sort keys, consistent spacing
content = json.dumps(parsed, ensure_ascii=False, sort_keys=True)
except json.JSONDecodeError:
pass # Not JSON output - leave as is
normalized.append({'role': 'assistant', 'content': content})
else:
normalized.append(msg)
return {'messages': normalized}
def normalize_dataset(examples: list[dict]) -> list[dict]:
return [normalize_json_output_example(ex) for ex in examples]Измерение показателей качества набора данных
До передачи данных на дообучение вычислите показатели качества всего набора. Проверьте среднее и максимальное число токенов на пример (длинные примеры дороже обучать, и их содержимое может быть усечено), охват словаря (охватывает ли набор всё разнообразие рабочих входных данных?) и показатель согласованности (дают ли похожие входные данные похожие ответы?). У большинства поставщиков дообучения есть конечная точка проверки данных, которая обнаруживает ошибки формата до того, как с Вас возьмут плату за неудачный запуск обучения.
import tiktoken
def analyze_dataset_quality(examples: list[dict], model='gpt-4o-mini') -> dict:
encoder = tiktoken.encoding_for_model(model)
token_counts = []
for ex in examples:
total_tokens = sum(
len(encoder.encode(m['content']))
for m in ex['messages']
)
token_counts.append(total_tokens)
report = {
'total_examples': len(examples),
'avg_tokens_per_example': sum(token_counts) / len(token_counts),
'max_tokens': max(token_counts),
'min_tokens': min(token_counts),
'examples_over_4k_tokens': sum(1 for t in token_counts if t > 4096),
'estimated_training_tokens': sum(token_counts),
'estimated_cost': sum(token_counts) / 1_000_000 * 8.0 # ~$8/1M tokens for gpt-4o-mini
}
for key, value in report.items():
print(f'{key}: {value}')
return reportИтеративное улучшение набора данных
Подготовка набора данных выполняется итеративно. Дообучите небольшую модель на исходном наборе, оцените её на отложенных примерах, выявите типы ошибок и свяжите их с пробелами или проблемами качества набора данных. Затем исправьте данные и повторите обучение. Этот цикл улучшения данных на основе ошибок является стандартной практикой производственного дообучения и гораздо эффективнее попытки собрать идеальный набор данных за один проход.
Согласованность системного промпта во всех примерах
Если после тонкой настройки Ваша модель всегда будет использовать один и тот же системный запрос в рабочей среде, включайте этот точный системный запрос в каждый пример для обучения. Если Вы хотите, чтобы модель работала без системного запроса, обучайте её без него. Несоответствие условий обучения и вывода — одна из главных причин, по которым тонкая настройка не оправдывает ожиданий. Модель обучается поведению, обусловленному точной структурой запроса, которую она видит во время обучения.
Быстрая проверка
Проверьте, насколько хорошо Вы поняли подготовку набора данных для обучения с тонкой настройкой из этого урока.
Итоги урока
В этом уроке Вы узнали, что при тонкой настройке качество данных важнее их количества: 100 отличных примеров эффективнее 10 000 посредственных; удаление дубликатов, проверка, балансировка и нормализация — четыре ключевых этапа очистки данных перед любым запуском обучения; а разделение на обучающую и проверочную выборки необходимо для выявления переобучения во время обучения, прежде чем оно ухудшит работу в реальных условиях. Далее мы выполним тонкую настройку LoRA с помощью HuggingFace PEFT.
Изучай Python с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 30
- Уроки
- 120
Часто задаваемые вопросы
Урок «Подготовка высококачественного обучающего набора данных» бесплатный?
Да — полный текст урока «Подготовка высококачественного обучающего набора данных» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Engineering Academy, подпишись на CoddyKit PRO. Курс AI Engineering Academy содержит 4 уроков всего.
Чему я научусь в уроке «Подготовка высококачественного обучающего набора данных»?
Соберите, очистите и отформатируйте данные для следования инструкциям в форматах Alpaca и ShareGPT, удалите дубликаты и разделите данные на обучающую и проверочную выборки. Ты практикуешь AI Engineering Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать AI Engineering Academy?
Предыдущий опыт не требуется. AI Engineering Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Подготовка высококачественного обучающего набора данных»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке AI Engineering Academy?
Да. Каждый урок AI Engineering Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Когда дообучение эффективнее составления запросов
- Подготовка высококачественного обучающего набора данных
- Дообучение LoRA с Hugging Face PEFT
- Оценка и развёртывание дообученной модели