Энкодеры (BERT) и рабочий цикл Hugging Face
Как устроен и обучается BERT — маскирование 80/10/10, токены [CLS] и [SEP], отказ от NSP в RoBERTa — и как дообучить энкодер под классификацию через Hub, AutoModel, datasets и Trainer. Когда маленький энкодер выгоднее LLM.
Зачем это нужно
Заголовок раздела «Зачем это нужно»В уроке 4.5.1 вы узнали, что из одного блока трансформера выросли три семейства, и победил decoder-only. Но энкодеры никуда не делись. Каждый раз, когда поисковик находит документ по смыслу, почтовый фильтр отсеивает спам, а RAG-система (ступень 8) подбирает фрагменты для ответа, почти наверняка работает encoder-onlyencoder-only (только энкодер)EN encoder-only architectureДвунаправленное внимание, обучение через маскированное языковое моделирование (BERT, RoBERTa). Сегодня — модели эмбеддингов, реранкеры, классификаторы.-модель семейства BERTBERTEN BERT (Bidirectional Encoder Representations from Transformers)Энкодерный трансформер Google (2018) с двунаправленной маской, предобученный на MLM. Основа классификаторов, NER, эмбеддеров и реранкеров (BGE, multilingual-e5 — его потомки).. Она в сотни раз меньше LLM, отвечает за миллисекунды и легко дообучается под конкретную задачу.
Второй повод для урока практический. Почти всё, что вы будете делать дальше на своих видеокартах — LoRA для Qwen
(ступень 6), квантование (ступень 7), эмбеддинги для RAG (ступень 8), — начинается с одних и тех же строк:
AutoTokenizer.from_pretrained, load_dataset, Trainer. Удобнее всего освоить их на маленьком энкодере, где
обучение занимает минуты, а ошибки видны сразу.
Вход BERT: [CLS], [SEP] и сегменты
Заголовок раздела «Вход BERT: [CLS], [SEP] и сегменты»BERT (Google, 2018) — это стопка блоков трансформера с двунаправленной маской: каждый токен видит весь текст.
В базовой версии 12 блоков, ширина 768, 12 голов и около 110 млн параметров; в большой — 24 блока, ширина 1024 и
около 340 млн. Обучали её на английской Википедии и корпусе книг BooksCorpus, словарь WordPieceWordPieceEN WordPieceПодсловный токенизатор BERT, родственник BPE. Продолжение слова помечается «##»: playing → play ##ing. Словарь BERT — около 30 тысяч подслов. из 30 тысяч
подслов. WordPiece похож на BPE из урока 4.2.2, а продолжение слова помечается
двумя решётками: «playing» → play ##ing.
Вход всегда собирается по одному шаблону:
[CLS] мама мыла раму [SEP] рама была чистая [SEP] 0 0 0 0 0 1 1 1 1 ← номер сегмента- токен [CLS]токен [CLS]EN [CLS] tokenСпецтокен в начале входа BERT. Через внимание собирает информацию со всего текста; его итоговый вектор используют для классификации и как эмбеддинг текста (BGE). Фрагменты разделяет и закрывает токен [SEP]. стоит первым. Своего смысла у него нет, но за 12 блоков внимания он собирает информацию со всего входа, и его итоговый вектор служит представлением текста целиком. Поверх него ставят классификатор.
- [SEP] (separator) разделяет фрагменты и закрывает вход. Многие задачи устроены как пара текстов: вопрос и абзац, два предложения для сравнения, запрос и документ для реранкера (ступень 8).
- Сегментные эмбеддинги — третья таблица рядом с таблицей токенов и позиций: вектор «я из фрагмента A» или
«я из фрагмента B» прибавляется к эмбеддингу каждого токена. В Hugging Face их номера называются
token_type_ids.
Итак, эмбеддинг на входе — сумма трёх строк из трёх таблиц: токен + позиция + сегмент. Дальше — обычные блоки из урока 4.5.1, только без причинной маски.
Маскированное языковое моделирование и 80/10/10
Заголовок раздела «Маскированное языковое моделирование и 80/10/10»GPT учится предсказывать следующий токен. Для BERT такая цель бессмысленна: при двунаправленной маске токен видит самого себя, и угадать его тривиально. Поэтому BERT учат маскированному языковому моделированиюмаскированное языковое моделирование (MLM)EN masked language modeling (MLM)Цель обучения BERT — закрыть около 15% токенов и угадать их по контексту с обеих сторон. Сигнал дают только замаскированные позиции. (MLM): часть токенов портят, а модель восстанавливает исходные по контексту с обеих сторон. Как в школьном упражнении «вставьте пропущенное слово».
Рецепт из статьи:
- Среди обычных токенов (не [CLS], не [SEP], не [PAD]) случайно выбираем 15% — это цели.
- Из выбранных 80% заменяем на
[MASK], 10% — на случайный токен словаря, 10% оставляем как есть. - Loss — cross-entropy только по выбранным позициям. Остальным позициям ставят метку −100, и функция потерь их пропускает (так договорились PyTorch и Hugging Face).
Пример. В тексте 200 обычных токенов. Целей будет около : около 24 станут [MASK], 3 —
случайными словами, 3 останутся прежними. Loss усредняется по этим 30 позициям, остальные 170 сигнала не дают.
Зачем такие сложности, почему не 100% [MASK]? На дообучении и в работе токена [MASK] во входе не бывает.
Если бы цель всегда пряталась под [MASK], модели было бы достаточно хорошо понимать позиции с [MASK], а для обычных
токенов строить представления «как получится». С заменами модель не знает, какой из видимых токенов испорчен, и
вынуждена держать осмысленный вектор для каждого. В приложении к статье авторы сравнили несколько пропорций, и
80/10/10 оказалась удачным компромиссом.
Цена. Из каждых 100 токенов сигнал дают только 15. Decoder-only получает сигнал со всех ста — один из доводов в его пользу из урока 4.5.1.
NSP и почему от неё отказались
Заголовок раздела «NSP и почему от неё отказались»Вторая цель BERT — предсказание следующего предложенияпредсказание следующего предложения (NSP)EN next sentence prediction (NSP)Вторая цель предобучения BERT — угадать, идёт ли фрагмент B в тексте сразу после A. RoBERTa (2019) показала, что она не нужна, и от неё отказались. (NSP). В половине примеров фрагмент B действительно шёл в тексте после A, в половине — взят из случайного документа. Классификатор поверх [CLS] угадывает, какой случай. Идея была научить модель связям между предложениями.
В 2019 году Facebook AI выпустил RoBERTa: тот же BERT, но обученный «как следует». Авторы убрали NSP и подавали непрерывные куски текста до 512 токенов, маскировали динамически (новый набор масок при каждом проходе по данным, а не один раз при подготовке), взяли большие батчи и примерно в десять раз больше текста (160 ГБ против 16 ГБ). Модель заметно обошла BERT, а отказ от NSP качества не ухудшил. С тех пор NSP почти не используют: от неё отказались и RoBERTa, и XLM-R, на которой построена multilingual-e5.
Дообучение: головы для задач
Заголовок раздела «Дообучение: головы для задач»Предобученный энкодер выдаёт по вектору на каждый токен. Чтобы решить задачу, сверху ставят маленькую голову и дообучают всё вместе (дообучение, тонкая настройка, файнтюнингдообучение, тонкая настройка, файнтюнингEN fine-tuning):
flowchart TB T["[CLS] отличный товар [SEP]"] --> E["Энкодер BERT (предобучен)"] E --> C["вектор [CLS]"] --> H1["Linear(768 → 3)<br/>негатив / нейтр. / позитив"] E --> TOK["вектор каждого токена"] --> H2["Linear(768 → число тегов)<br/>B-PER, I-PER, O, …"]
- Классификация текста (тональность, тема, спам). Голова классификацииголова классификацииEN classification headНебольшой слой (обычно линейный) поверх предобученного энкодера, который превращает вектор [CLS] в логиты классов. Инициализируется случайно и обучается при дообучении. —
линейный слой поверх вектора [CLS] с числом выходов, равным числу классов. В Hugging Face это
AutoModelForSequenceClassification. - Разметка токенов, например распознавание именованных сущностейраспознавание именованных сущностей (NER)EN named entity recognition (NER)Разметка каждого токена тегом: имя, организация, место или «не сущность». Схема BIO: B-PER — начало имени, I-PER — продолжение, O — вне сущности. (NER): найти в тексте имена, организации, места.
Линейный слой применяют к вектору каждого токена и предсказывают тег:
B-PER(начало имени),I-PER(продолжение),O(не сущность). «Анна Каренина приехала в Москву» →B-PER I-PER O O B-LOC. Тонкость: WordPiece может разбить «Каренина» на три подслова, а тег один. Обычно тег ставят первому подслову, остальным — −100. В Hugging Face этоAutoModelForTokenClassification.
Голова инициализируется случайно, энкодер — предобученными весами. Учат всё вместе с маленьким learning rate (обычно – ) 2–4 эпохи: большой шаг быстро «сотрёт» то, что энкодер выучил на претрейне.
Энкодер или LLM?
Заголовок раздела «Энкодер или LLM?»Тональность отзыва можно определить двумя способами: дообучить маленький энкодер на размеченных примерах или спросить LLM без примеров (zero-shotzero-shot (без примеров)EN zero-shotРешение задачи без обучающих примеров и без дообучения — только по описанию задачи в промпте. С несколькими примерами в промпте — few-shot.). Сравним по существу:
| Дообученный энкодер (rubert-tiny2, ruBERT) | LLM без дообучения (Qwen3-8B через Ollama) | |
|---|---|---|
| Данные | нужны размеченные примеры, обычно от тысяч | не нужны, достаточно описать задачу словами |
| Качество на своей узкой задаче | обычно выше: модель выучила именно вашу разметку | хорошее «из коробки», но разметку понимает по-своему |
| Размер и железо | десятки–сотни млн параметров, работает на CPU | миллиарды параметров, нужна видеокарта |
| Задержка на один текст | миллисекунды | от сотен миллисекунд до секунд |
| Смена задачи | новая разметка и дообучение | переписать промпт |
Практическое правило: прототип — на LLM, продакшн с миллионами однотипных запросов — на энкодере. А разметку для энкодера нередко делает та же LLM: это дистилляция, о ней — в уроке 6.4.4.
Hugging Face: из чего состоит рабочий цикл
Заголовок раздела «Hugging Face: из чего состоит рабочий цикл»Hugging Face — компания и одновременно экосистема. Нам нужны четыре части.
1. Hub (huggingface.co) — хранилище моделей и датасетов, своего рода GitHub для весов. У каждой модели есть
карточка моделикарточка модели (model card)EN model cardДокумент к модели: назначение, где применять нельзя, данные, метрики (в том числе по группам), ограничения (Mitchell и др., 2019).: для чего обучена, на каких данных, как вызывать, какие ограничения и лицензия. Лицензию
читайте до того, как строить на модели продукт: Apache-2.0 и MIT разрешают почти всё, CC-BY-NC запрещает коммерческое
использование, у Llama — собственная лицензия с условиями. Модель называют по схеме организация/имя:
cointegrated/rubert-tiny2, BAAI/bge-m3, Qwen/Qwen3-8B.
2. transformers: AutoTokenizer и AutoModel. В репозитории модели лежит config.json с названием архитектуры.
Auto-классы transformersAuto-классы transformersEN Auto classes (AutoTokenizer, AutoModel)Классы библиотеки transformers, которые читают config.json модели и сами создают нужную архитектуру: AutoTokenizer, AutoModel, AutoModelForSequenceClassification, AutoModelForCausalLM и др. читают его и сами создают нужный класс:
from transformers import AutoTokenizer, AutoModel, AutoModelForSequenceClassification
tok = AutoTokenizer.from_pretrained("cointegrated/rubert-tiny2") # скачает словарь и настройки токенизатораenc = tok("Отличный товар, быстрая доставка!")print(enc.keys()) # input_ids, token_type_ids, attention_maskprint(tok.convert_ids_to_tokens(enc["input_ids"])) # [CLS] … [SEP] — шаблон добавлен сам
encoder = AutoModel.from_pretrained("cointegrated/rubert-tiny2") # только энкодер: скрытые состоянияclf = AutoModelForSequenceClassification.from_pretrained( "cointegrated/rubert-tiny2", num_labels=3) # энкодер + новая голова; transformers предупредит, # что веса classifier инициализированы случайно — так и надоПервый вызов скачивает файлы в кэш (~/.cache/huggingface), следующие берут их с диска. attention_mask —
единицы на настоящих токенах и нули на [PAD]: при батчевой обработке короткие тексты дополняются, и внимание не должно
смотреть на заполнитель.
3. datasets. load_dataset("организация/имя") скачивает датасет и хранит его на диске в формате Arrow: даже
миллионы строк не занимают оперативную память. Главные операции:
ds.map(f, batched=True)— применить функцию (обычно токенизацию) ко всем строкам пачками; результат кэшируется, второй запуск мгновенный;ds.filter(...),ds.train_test_split(test_size=0.1, seed=0)— отбор и разбиение;load_dataset(..., streaming=True)— не скачивать целиком, а читать по мере надобности. Так работают с корпусами в сотни гигабайт для претрейна (урок 5.1.2).
4. Trainer. Готовый цикл обучения: батчи, AdamW, разогрев learning rate, клиппинг, оценка на валидации,
сохранение чекпоинтов, смешанная точность. Всё это вы напишете руками в уроке 4.6.3,
а пока достаточно знать, что Trainer делает это за вас. Настраивается он через TrainingArguments, а метрики считает ваша функция compute_metrics.
Для метрик есть библиотека evaluate, но evaluate.load("accuracy") скачивает код метрики с Hub, поэтому простую
точность мы посчитаем сами.
Практика
Заголовок раздела «Практика»В браузере: маскирование 80/10/10
Заголовок раздела «В браузере: маскирование 80/10/10»Реализуйте сборку входа BERT для пары фрагментов, маскирование MLM и loss только по маскированным позициям. Тесты проверяют шаблон [CLS]/[SEP], что спецтокены не трогаются, частоты 15% и 80/10/10 на большом батче и что логиты в позициях с меткой −100 не влияют на loss.
import numpy as np
# Игрушечный словарь в стиле BERT: первые пять номеров — спецтокены, дальше обычные слова.
SPECIAL = ["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"]
PAD_ID, UNK_ID, CLS_ID, SEP_ID, MASK_ID = range(5)
WORDS = "мама мыла раму рама была чистая кот сидел на окне а пёс спал у двери".split()
VOCAB = SPECIAL + WORDS
STOI = {w: i for i, w in enumerate(VOCAB)}
IGNORE = -100 # метка «не считать loss в этой позиции» — как в PyTorch и Hugging Face
def build_pair(words_a, words_b, max_len):
"""Собирает вход BERT для пары фрагментов: [CLS] A [SEP] B [SEP], затем [PAD] до max_len.
Возвращает три списка длины max_len:
input_ids — номера токенов (STOI; неизвестное слово → UNK_ID);
token_type_ids — 0 для [CLS], A и первого [SEP]; 1 для B и второго [SEP]; 0 для [PAD];
special_mask — 1 там, где стоит спецтокен ([CLS], [SEP], [PAD]), иначе 0.
Если пара не помещается в max_len, бросьте ValueError.
"""
# TODO: ваш код
...
def mlm_mask(input_ids, special_mask, rng, p=0.15):
"""Маскирование BERT 80/10/10. input_ids и special_mask — numpy-массивы одной формы (любой).
1) Каждая НЕспециальная позиция выбирается с вероятностью p: rng.random(shape) < p.
2) labels = исходный номер в выбранных позициях и IGNORE (−100) во всех остальных.
3) Среди выбранных позиций: 80% заменяем на MASK_ID, 10% — на случайное ОБЫЧНОЕ слово
(равномерно из номеров len(SPECIAL) … len(VOCAB) − 1), 10% оставляем как есть.
Возвращает пару (inputs, labels) — НОВЫЕ массивы; исходный input_ids не меняйте.
rng — np.random.Generator; используйте только его.
"""
# TODO: ваш код
...
def mlm_loss(logits, labels):
"""Средняя cross-entropy только по позициям, где labels != IGNORE.
logits — массив (..., V), labels — массив (...). Считайте устойчиво (вычитайте максимум перед exp).
Если ни одной позиции для loss нет, верните 0.0.
"""
# TODO: ваш код
...
# ---------- Кнопка «Запустить» ----------
def demo():
ids, types, special = build_pair("мама мыла раму".split(), "рама была чистая".split(), max_len=12)
print("токены: ", [VOCAB[i] for i in ids])
print("сегменты:", types)
rng = np.random.default_rng(0)
batch = np.array([ids] * 4)
inputs, labels = mlm_mask(batch, np.array([special] * 4), rng, p=0.3)
for row_in, row_lab in zip(inputs, labels):
print(" ".join(VOCAB[i] for i in row_in), "| цели:", [VOCAB[l] for l in row_lab if l != IGNORE])
logits = np.zeros(inputs.shape + (len(VOCAB),)) # «модель», которая ничего не знает
print("loss необученной модели:", round(mlm_loss(logits, labels), 3), " ln V =", round(float(np.log(len(VOCAB))), 3))
try:
demo()
except Exception as e: # пока функции не дописаны
print("Допишите функции:", type(e).__name__, e)
То же самое в Hugging Face делает DataCollatorForLanguageModeling(tokenizer, mlm_probability=0.15): у него есть
параметры mask_replace_prob=0.8 и random_replace_prob=0.1, а флаг whole_word_mask=True маскирует слова целиком
(см. «Китайский угол»). Поскольку коллатор маскирует батч при каждой выдаче, маскирование получается динамическим,
как в RoBERTa.
На вашей машине: дообучаем русский энкодер на отзывах
Заголовок раздела «На вашей машине: дообучаем русский энкодер на отзывах»Возьмём маленький русский BERT cointegrated/rubert-tiny2 и датасет отзывов о товарах
ai-forever/ru-reviews-classification из бенчмарка MTEB: три класса тональности, 45 000 обучающих отзывов
по 15 000 на класс, в среднем около 130 символов. Поставьте библиотеки (pip install transformers datasets accelerate requests;
про версии — урок 4.6.5) и запустите:
"""finetune_rubert.py — дообучение rubert-tiny2 на тональность отзывов. python finetune_rubert.py"""import numpy as npfrom datasets import load_datasetfrom transformers import (AutoModelForSequenceClassification, AutoTokenizer, DataCollatorWithPadding, Trainer, TrainingArguments)
MODEL = "cointegrated/rubert-tiny2"ds = load_dataset("ai-forever/ru-reviews-classification")print(ds) # какие части и колонки есть: text, labelfor lab in range(3): # посмотрите глазами, какой номер какому классу print(lab, ds["train"].filter(lambda r: r["label"] == lab)[0]["text"][:100])
split = ds["train"].train_test_split(test_size=0.1, seed=0) # 10% train → валидация для выбора эпохиtrain_ds, val_ds, test_ds = split["train"], split["test"], ds["test"]
tok = AutoTokenizer.from_pretrained(MODEL)
def tokenize(batch): return tok(batch["text"], truncation=True, max_length=256) # длинные отзывы обрезаем
train_ds, val_ds, test_ds = (d.map(tokenize, batched=True) for d in (train_ds, val_ds, test_ds))
id2label = {0: "негатив", 1: "нейтрально", 2: "позитив"} # проверьте порядок по выводу выше!model = AutoModelForSequenceClassification.from_pretrained( MODEL, num_labels=3, id2label=id2label, label2id={v: k for k, v in id2label.items()})
def compute_metrics(eval_pred): logits, labels = eval_pred return {"accuracy": float((np.argmax(logits, axis=-1) == labels).mean())}
args = TrainingArguments( output_dir="rubert-reviews", per_device_train_batch_size=64, per_device_eval_batch_size=256, learning_rate=5e-5, num_train_epochs=3, warmup_steps=0.1, # дробное число = доля шагов на разогрев lr (подробно — урок 4.6.3) weight_decay=0.01, eval_strategy="epoch", # оценка на val после каждой эпохи save_strategy="epoch", save_total_limit=1, load_best_model_at_end=True, # в конце вернуть веса лучшей эпохи по val metric_for_best_model="accuracy", bf16=True, # смешанная точность на RTX 4080; на CPU уберите logging_steps=100,)trainer = Trainer(model=model, args=args, train_dataset=train_ds, eval_dataset=val_ds, processing_class=tok, # нужен для DataCollatorWithPadding и сохранения data_collator=DataCollatorWithPadding(tok), # дополняет батч до самого длинного текста в нём compute_metrics=compute_metrics)trainer.train()print(trainer.evaluate(test_ds)) # test смотрим один раз, в концеtrainer.save_model("rubert-reviews/best")Что увидите. Каждые 100 шагов Trainer печатает loss, grad_norm и learning_rate, после каждой эпохи —
eval_loss и eval_accuracy на валидации, в конце — словарь метрик на test. Мы этот скрипт на видеокарте не запускали,
поэтому дадим ориентиры, а не точные числа. Случайное угадывание даёт 33%. Нейтральный класс у отзывов размыт
(«нормально, но доставка долгая»), поэтому ждите точность заметно ниже 100%; разумный результат для маленькой модели —
порядка 70–80%. Время на одной RTX 4080 — несколько минут на эпоху (оценка), на CPU — десятки минут.
Готовую модель удобно вызывать через pipeline:
from transformers import pipeline
clf = pipeline("text-classification", model="rubert-reviews/best")print(clf(["Пришло быстро, всё работает", "Сломался через неделю", "Обычный чайник, ничего особенного"]))# [{'label': 'позитив', 'score': …}, {'label': 'негатив', 'score': …}, …] — метки взяты из id2labelБез id2label метки назывались бы LABEL_0, LABEL_1, LABEL_2.
Сравнение с LLM без примеров
Заголовок раздела «Сравнение с LLM без примеров»Теперь спросим Qwen3-8B, ничего не дообучая. Ollama — программа, которая скачивает и запускает LLM одной командой; подробно о ней и её родственниках — в уроке 4.6.5 и в уроке 7.2.2. Установите и скачайте модель (около 5 ГБ):
curl -fsSL https://ollama.com/install.sh | shollama pull qwen3:8b"""zero_shot_qwen.py — та же тональность силами LLM без дообучения. python zero_shot_qwen.py"""import re
import requestsfrom datasets import load_dataset
NAMES = ["негатив", "нейтрально", "позитив"] # тот же порядок, что в id2labeltest = load_dataset("ai-forever/ru-reviews-classification", split="test").shuffle(seed=0).select(range(300))
def ask(text): prompt = ("Определи тональность отзыва о товаре. Ответь одним словом: негатив, нейтрально или позитив.\n\n" f"Отзыв: {text}\n/no_think") # /no_think — выключить рассуждения у Qwen3 r = requests.post("http://localhost:11434/v1/chat/completions", json={ "model": "qwen3:8b", "temperature": 0, "messages": [{"role": "user", "content": prompt}]}) answer = r.json()["choices"][0]["message"]["content"] answer = re.sub(r"<think>.*?</think>", "", answer, flags=re.S).strip().lower() return next((i for i, name in enumerate(NAMES) if name[:5] in answer), -1) # −1: модель ответила не по формату
preds = [ask(row["text"]) for row in test]acc = sum(p == row["label"] for p, row in zip(preds, test)) / len(test)print(f"zero-shot Qwen3-8B: точность {acc:.1%}, ответов не по формату: {preds.count(-1)}")Сравните с eval_accuracy энкодера на тех же 300 отзывах (в скрипте дообучения: trainer.evaluate(test_ds.shuffle(seed=0).select(range(300)))).
Обратите внимание на три вещи. Качество: LLM неплохо отличает позитив от негатива, но «нейтральный» класс понимает
не так, как разметчики датасета. Скорость: засеките время — энкодер обрабатывает тысячи отзывов в секунду, LLM —
единицы. Формат: часть ответов LLM может не уложиться в одно слово, их приходится разбирать. Попробуйте добавить
в промпт пять размеченных примеров (few-shot, урок 8.1.2) и посмотрите, сократится ли разрыв.
Русские и китайские энкодеры
Заголовок раздела «Русские и китайские энкодеры»| Модель | Что это | Где пригодится |
|---|---|---|
DeepPavlov/rubert-base-cased | BERT-base, дообученный на русской Википедии и новостях (МФТИ) | классификация, NER на русском |
ai-forever/ruBert-base, ruBert-large | русские BERT от Сбера (организация ai-forever на Hub) | классификация, NER на русском |
cointegrated/rubert-tiny2 | маленький русский BERT, быстрый даже на CPU | прототипы, продакшн на CPU |
intfloat/multilingual-e5-base | многоязычный энкодер на базе XLM-R, обучен для эмбеддингов | поиск и RAG (урок 8.2.1) |
BAAI/bge-m3 | многоязычный энкодер-эмбеддер Пекинской академии ИИ (BAAI) | RAG на русском и китайском (урок 8.2.1) |
Проверь себя
Заголовок раздела «Проверь себя»- BERT — двунаправленный энкодер. Вход: [CLS] A [SEP] B [SEP], эмбеддинг = токен + позиция + сегмент.
- MLM: выбираем 15% обычных токенов, из них 80% → [MASK], 10% → случайный токен, 10% без изменений; loss только по выбранным (метка −100 у остальных). NSP оказалась лишней: RoBERTa без неё, но с динамическим маскированием и большим объёмом данных обошла BERT.
- Дообучение: голова поверх [CLS] для классификации, поверх каждого токена — для NER; маленький learning rate, 2–4 эпохи.
- Рабочий цикл Hugging Face: Hub и карточка модели (читайте лицензию) →
AutoTokenizer/AutoModel...→load_datasetиmap→TrainerсTrainingArgumentsиcompute_metrics→pipeline. - Узкая стабильная задача с разметкой и большим потоком запросов — энкодер; без разметки и для быстрых прототипов — LLM.
Дальше: модуль 4.6 — собираем GPT своими руками, от микро-GPT на чистом Python до модели на PyTorch — Микро-GPT на чистом Python.