Навчальний посібник: Як тренувати мовну модель RoBERTa для іспанської мови
SpanBERTa: Як ми з нуля навчили мовну модель RoBERTa для іспанської мови
Вперше опубліковано стажером з машинного навчання Skim AI Крісом Траном.
Вступ¶
Методи самонавчання за допомогою моделей-трансформерів досягли найсучасніших результатів у виконанні більшості завдань НЛП. Однак, оскільки їхнє навчання вимагає значних обчислювальних витрат, більшість доступних на сьогоднішній день моделей-трансформерів доступні лише для англійської мови. Тому, щоб покращити ефективність виконання завдань НЛП в наших проектах іспанською мовою, моя команда в Скіммі ШІ вирішив тренувати Роберта мовну модель для іспанської мови з нуля і назвали її SpanBERTa.
SpanBERTa має такий самий розмір, як і RoBERTa-base. Ми використовували схему навчання RoBERTa, щоб навчити модель на 18 ГБ дискового простору ОСКАРіспанського корпусу за 8 днів на 4 графічних процесорах Tesla P100.
У цьому блозі ми розглянемо наскрізний процес навчання BERT-подібної мовної моделі з нуля, використовуючи трансформатори
і токенізатори
бібліотеки Hugging Face. Існує також блокнот Google Colab для безпосереднього запуску кодів у цій статті. Ви також можете модифікувати блокнот відповідним чином, щоб навчити BERT-подібну модель для інших мов або налаштувати її на вашому власному наборі даних.
Перш ніж продовжити, я хочу висловити величезну подяку команді Hugging Face за те, що вони зробили найсучасніші моделі НЛП доступними для кожного.
Налаштування¶
1. Встановлення залежностей¶
У [0]:
%pture !pip uninstall -y tensorflow !pip install transformers==2.8.0
2. Дані¶
Ми попередньо навчили SpanBERTa ОСКАРіспанського корпусу. Повний розмір набору даних становить 150 ГБ, і ми використали частину з 18 ГБ для тренування.
У цьому прикладі, для простоти, ми використаємо набір даних іспанських субтитрів до фільмів з OpenSubtitles. Цей набір даних має розмір 5.4 ГБ, і ми будемо тренуватися на підмножині розміром ~300 МБ.
У [0]:
імпортувати os # Завантажити та розархівувати набір даних субтитрів до фільмів if not os.path.exists('data/dataset.txt'): !wget "https://object.pouta.csc.fi/OPUS-OpenSubtitles/v2016/mono/es.txt.gz" -O dataset.txt.gz !gzip -d dataset.txt.gz !mkdir дані !mv dataset.txt дані
--2020-04-06 15:53:04-- https://object.pouta.csc.fi/OPUS-OpenSubtitles/v2016/mono/es.txt.gz Вирішення проблеми object.pouta.csc.fi (object.pouta.csc.fi)... 86.50.254.18, 86.50.254.19 Підключення до object.pouta.csc.fi (object.pouta.csc.fi)|86.50.254.18|:443... встановлено. HTTP-запит відправлено, чекаємо на відповідь... 200 OK Довжина: 1859673728 (1.7G) [application/gzip] Збереження до: 'dataset.txt.gz' dataset.txt.gz 100%[===================>] 1.73G 17.0MB/s за 1m 46s 2020-04-06 15:54:51 (16.8 МБ/с) - 'dataset.txt.gz' збережено [1859673728/1859673728]
У [0]:
# Загальна кількість рядків та деякі випадкові рядки !wc -l data/dataset.txt !shuf -n 5 data/dataset.txt
179287150 data/dataset.txt Sabes, pensé que tenías más pelotas que para enfrentarme a través de mi hermano. Supe todos los encantamientos en todos las lenguas de los Elfos hombres y Orcos. Anteriormente en Blue Bloods: Я хочу, щоб ви пообіцяли, що не матимете жодних справ з Деніелом Стаффордом. Fue comiquísimo.
У [0]:
# Отримати підмножину перших 1 000 000 рядків для навчання TRAIN_SIZE = 1000000 #@param {type: "integer"} !(head -n $TRAIN_SIZE data/dataset.txt) > data/train.txt
У [0]:
# Отримати підмножину наступних 10 000 рядків для перевірки VAL_SIZE = 10000 #@param {type: "integer"} !(sed -n {TRAIN_SIZE + 1},{TRAIN_SIZE + VAL_SIZE}p data/dataset.txt) > data/dev.txt
3. Навчити токенізаторів¶
Оригінальна реалізація BERT використовує токенізатор WordPiece зі словником на 32 тисячі підслівників. Цей метод, однак, може вводити "невідомі" лексеми при обробці рідкісних слів.
У цій реалізації ми використовуємо байтовий токенізатор BPE зі словником 50 265 одиниць підслівників (так само, як і в RoBERTa-base). Використання байтового рівня BPE дозволяє вивчити словник підслівників невеликого розміру, який може кодувати будь-які вхідні дані без отримання "невідомих" токенів.
Тому що ByteLevelBPETokenizer
створює 2 файли ["vocab.json", "merges.txt"].
в той час як BertWordPieceTokenizer
створює лише 1 файл vocab.txt
це призведе до помилки, якщо ми використаємо BertWordPieceTokenizer
для завантаження виходів токенізатора BPE.
У [0]:
%%time from tokenizers import ByteLevelBPETokenizer path = "data/train.txt" # Ініціалізуємо токенізатор tokenizer = ByteLevelBPETokenizer() # Налаштуйте навчання tokenizer.train(files=шлях, vocab_size=50265, min_frequency=2, special_tokens=["", "", "", "", ""]) # Збереження файлів на диск !mkdir -p "models/roberta" tokenizer.save("models/roberta")
Час роботи процесора: користувач 1хв 37с, sys: 1.02 с, всього: 1хв 38с Час роботи на стіні: 1хв 38с
Супершвидко! Поїздка на 10 мільйонах ліній займає лише 2 хвилини.
Модель мови навчання з нуля¶
1. Архітектура моделі¶
RoBERTa має точно таку ж архітектуру, як і BERT. Єдина відмінність полягає в наступному:
- RoBERTa використовує токенізатор BPE байтового рівня з більшим словником підслівників (50k проти 32k).
- RoBERTa реалізує динамічне маскування слів і відкидає завдання передбачення наступного речення.
- Гіперпараметри тренування RoBERTa.
Інші конфігурації архітектури можна знайти в документації (Роберта, БЕРТ).
У [0]:
import json config = {{ "архітектури "architectures": [ "RobertaForMaskedLM" ], "attention_probs_dropout_prob": 0.1, "hidden_act": "gelu", "hidden_dropout_prob": 0.1, "hidden_size": 768, "initializer_range": 0.02, "intermediate_size": 3072, "layer_norm_eps": 1e-05, "max_position_embeddings": 514, "model_type": "roberta", "num_attention_heads": 12, "num_hidden_layers": 12, "type_vocab_size": 1, "vocab_size": 50265 } з open("models/roberta/config.json", 'w') як fp: json.dump(config, fp) tokenizer_config = {"max_len": 512} with open("models/roberta/tokenizer_config.json", 'w') as fp: json.dump(tokenizer_config, fp)
2. Гіперпараметри тренування¶
Гіперпараметр | BERT-база | RoBERTa-base |
---|---|---|
Довжина послідовності | 128, 512 | 512 |
Розмір партії | 256 | 8K |
Пікова швидкість навчання | 1e-4 | 6e-4 |
Макс Степс | 1M | 500K |
Етапи розминки | 10K | 24K |
Зниження ваги | 0.01 | 0.01 |
Adam $epsilon$ | 1e-6 | 1e-6 |
Adam $beta_1$ | 0.9 | 0.9 |
Adam $beta_2$ | 0.999 | 0.98 |
Градієнтне відсікання | 0.0 | 0.0 |
Зауважте, що розмір партії при навчанні RoBERTa становить 8000. Таким чином, хоча RoBERTa-base була навчена на 500 тис. кроків, обчислювальні витрати на її навчання в 16 разів перевищують витрати на навчання BERT-base. У розділі Папір RoBERTaпоказано, що навчання з великими партіями покращує складність задачі моделювання замаскованої мови, а також точність виконання кінцевого завдання. Більший розмір партії можна отримати, налаштувавши крок_накопичення_градієнта
.
Через обчислювальні обмеження ми скористалися схемою навчання BERT-base і навчили нашу модель SpanBERTa на 4 графічних процесорах Tesla P100 за 8 днів, виконавши 200 тис. кроків за 8 днів.
3. Почніть тренування¶
Ми навчимо нашу модель з нуля, використовуючи run_language_modeling.py
скрипт, наданий Hugging Face, який буде попередньо обробляти, токенізувати корпус і навчати модель на Масковане мовне моделювання завдання. Скрипт оптимізовано для навчання на одному великому корпусі. Тому, якщо ваш набір даних великий і ви хочете розбити його для послідовного навчання, вам потрібно буде модифікувати скрипт, або бути готовим отримати монструозну машину з великим обсягом пам'яті.
У [0]:
# Оновлення 22 квітня 2020 року: Hugging Face оновив скрипт run_language_modeling.py. # Будь ласка, використовуйте цю версію до оновлення. !wget -c https://raw.githubusercontent.com/chriskhanhtran/spanish-bert/master/run_language_modeling.py
--2020-04-24 02:28:21-- https://raw.githubusercontent.com/chriskhanhtran/spanish-bert/master/run_language_modeling.py Вирішення проблеми raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ... Підключення до raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... підключено. HTTP-запит відправлено, чекаємо на відповідь... 200 OK Довжина: 34328 (34K) [text/plain] Збереження до: 'run_language_modeling.py' run_language_modeling 100%[===================>] 33.52K --.-KB/s за 0.003s 2020-04-24 02:28:21 (10.1 МБ/с) - 'run_language_modeling.py' збережено [34328/34328].
Важливі аргументи
--line_by_line
Чи обробляти окремі рядки тексту в наборі даних як окремі послідовності. Якщо кожен рядок у вашому наборі даних довгий і містить ~512 токенів або більше, вам слід використовувати цей параметр. Якщо кожен рядок короткий, стандартна попередня обробка тексту об'єднає всі рядки, токенізує їх і розділить токенізовані результати на блоки по 512 токенів. Ви також можете розбити ваші набори даних на невеликі фрагменти і обробити їх окремо. Обробка 3 ГБ тексту займе ~50 хвилин за замовчуваннямTextDataset
клас.--should_continue
Чи продовжувати з останньої контрольної точки в каталозі output_dir.--назва_моделі_або_шлях
Контрольна точка моделі для ініціалізації ваг. Залиште значення None, якщо ви хочете навчити модель з нуля.--mlm
Тренуйтеся з маскованим мовним моделюванням втрат замість мовного моделювання.--config_name, --tokenizer_name
Необов'язковий попередньо навчений конфіг та ім'я або шлях до токенізатора, якщо вони не збігаються з ім'ям_моделі_або_шляхом. Якщо обидва параметри не вказано, ініціалізуйте новий конфіг.--per_gpu_train_batch_size
Розмір партії на GPU/CPU для навчання. Виберіть найбільшу кількість, яку ви можете вмістити на своїх графічних процесорах. Ви побачите помилку, якщо розмір партії занадто великий.--gradient_accumulation_steps
Кількість кроків оновлення, яку потрібно накопичити перед виконанням проходу назад/оновлення. Ви можете використовувати цей трюк для збільшення розміру пакета. Наприклад, якщоper_gpu_train_batch_size = 16
іgradient_accumulation_steps = 4
то загальна кількість поїздів буде дорівнювати 64.--overwrite_output_dir
Перезапишіть вміст вихідного каталогу.--no_cuda, --fp16, --fp16_opt_level
Аргументи на користь навчання на GPU/CPU.- Іншими аргументами є шляхи моделі та гіперпараметри навчання.
Наполегливо рекомендується вказати тип моделі (наприклад, "roberta", "bert", "gpt2" і т.д.) у шляху до моделі, оскільки скрипт використовує АвтоМоделі
щоб вгадати конфігурацію моделі, використовуючи шаблонний пошук за вказаним шляхом.
У [0]:
# Шляхи моделі MODEL_TYPE = "roberta" #@param ["roberta", "bert"]. MODEL_DIR = "models/roberta" #@param {type: "string"} OUTPUT_DIR = "models/roberta/output" #@param {type: "string"} TRAIN_PATH = "data/train.txt" #@param {type: "string"} EVAL_PATH = "data/dev.txt" #@param {type: "string"}
У цьому прикладі ми будемо тренуватися лише 25 кроків на графічному процесорі Tesla P4, наданому Colab.
У [0]:
!nvidia-smi
Пн Квітень 6 15:59:35 2020 +-----------------------------------------------------------------------------+ | NVIDIA-SMI 440.64.00 Версія драйвера: 418.67 Версія CUDA: 10.1 | |Версія драйвера: 418.67 |Версія CUDA: 10.1 |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage| GPU-Util Compute M. | |===============================+======================+======================| | 0 Tesla P4 вимкнено | 00000000:00:04.0 вимкнено | 0 | N/A 31C P8 7W / 75W | 0MiB / 7611MiB | 0% За замовчуванням +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Процеси: Пам'ять графічного процесора | GPU PID Тип GPU Назва процесу Використання |=============================================================================| | Процеси не знайдено. +-----------------------------------------------------------------------------+
У [0]:
# Командний рядок cmd = """python run_language_modeling.py --output_dir {output_dir} --model_type {тип_моделі} --mlm --config_name {ім'я_конфігурації} --tokenizer_name {ім'я_токенайзера} {line_by_line} {should_continue} {назва_моделі_або_шлях} --train_data_file {шлях_до_потоку} --eval_data_file {eval_path} --do_train {do_eval} {evaluate_during_training} --overwrite_output_dir --block_size 512 --max_step 25 --warmup_steps 10 --learning_rate 5e-5 --per_gpu_train_batch_size 4 --gradient_accumulation_steps 4 --weight_decay 0.01 --adam_epsilon 1e-6 --max_grad_norm 100.0 --save_total_limit 10 --save_steps 10 --logging_steps 2 --seed 42 """
У [0]:
# Аргументи для навчання з нуля. Я вимикаю evaluate_during_training, # line_by_line, should_continue і model_name_or_path. train_params = { "output_dir": OUTPUT_DIR, "тип_моделі": MODEL_TYPE, "ім'я_конфігурації": MODEL_DIR, "tokenizer_name": MODEL_DIR, "train_path": TRAIN_PATH, "eval_path": EVAL_PATH, "do_eval": "--do_eval", "evaluate_during_training": "", "line_by_line": "", "should_continue": "", "назва_моделі_або_шлях": "", }
Якщо ви тренуєтеся на віртуальній машині, ви можете встановити tensorboard для моніторингу процесу навчання. Ось наша Тензорна дошка за навчання SpanBERTa.
pip install tensorboard==2.1.0 tensorboard dev upload --logdir виконується
Після 200 тис. кроків втрати досягли 1.8, а розгубленість - 5.2.
А тепер почнемо тренування!
У [ ]:
!{cmd.format(**train_params)}
04/06/2020 15:59:55 - INFO - __main__ - Створення функцій з файлу набору даних за адресою data
04/06/2020 16:04:43 - INFO - __main__ - Збереження функцій у кешований файл data/roberta_cached_lm_510_train.txt
04/06/2020 16:04:46 - INFO - __main__ - ***** Тренування з бігу *****
04/06/2020 16:04:46 - INFO - __main__ - Num examples = 165994
04/06/2020 16:04:46 - INFO - __main__ - Кількість епох = 1
04/06/2020 16:04:46 - INFO - __main__ - Миттєвий розмір пакету на GPU = 4
04/06/2020 16:04:46 - INFO - __main__ - Загальний розмір партії поїздів (з паралельним, розподіленим та накопиченням) = 16
04/06/2020 16:04:46 - INFO - __main__ - Кроки градієнтного накопичення = 4
04/06/2020 16:04:46 - INFO - __main__ - Всього кроків оптимізації = 25
Епоха: 0% 0/1 [00:00<?, ?it/s]
Ітерація: 0% 0/41499 [00:00<?, ?it/s]
Ітерація: 0% 1/41499 [00:01<13:18:02, 1.15s/it]
Ітерація: 0% 2/41499 [00:01<11:26:47, 1.01it/s]
Ітерація: 0% 3/41499 [00:02<10:10:30, 1.13it/s]
Ітерація: 0% 4/41499 [00:03<9:38:10, 1.20it/s]
Ітерація: 0% 5/41499 [00:03<8:52:44, 1.30it/s]
Ітерація: 0% 6/41499 [00:04<8:22:47, 1.38it/s]
Ітерація: 0% 7/41499 [00:04<8:00:55, 1.44it/s]
Ітерація: 0% 8/41499 [00:05<8:03:40, 1.43it/s]
Ітерація: 0% 9/41499 [00:06<7:46:57, 1.48it/s]
Ітерація: 0% 10/41499 [00:06<7:35:35, 1.52it/s]
Ітерація: 0% 11/41499 [00:07<7:28:29, 1.54it/s]
Ітерація: 0% 12/41499 [00:08<7:41:41, 1.50it/s]
Ітерація: 0% 13/41499 [00:08<7:34:28, 1.52it/s]
Ітерація: 0% 14/41499 [00:09<7:28:46, 1.54it/s]
Ітерація: 0% 15/41499 [00:10<7:23:29, 1.56it/s]
Ітерація: 0% 16/41499 [00:10<7:38:06, 1.51іт/с]
Ітерація: 0% 17/41499 [00:11<7:29:13, 1.54it/s]
Ітерація: 0% 18/41499 [00:12<7:24:04, 1.56іт/с]
Ітерація: 0% 19/41499 [00:12<7:21:59, 1.56it/s]
Ітерація: 0% 20/41499 [00:13<7:38:06, 1.51іт/с]
04/06/2020 16:06:23 - INFO - __main__ - ***** Оцінка виконання *****
04/06/2020 16:06:23 - INFO - __main__ - Кількість прикладів = 156
04/06/2020 16:06:23 - INFO - __main__ - Розмір пакету = 4
Оцінюємо: 100% 39/39 [00:08<00:00, 4.41it/s]
04/06/2020 16:06:32 - INFO - __main__ - ***** Результати перевірки *****
04/06/2020 16:06:32 - INFO - __main__ - perplexity = tensor(6077.6812)
4. Прогнозування замаскованих слів¶
Після тренування мовної моделі ви можете завантажити її та поділитися нею зі спільнотою. Ми завантажили нашу модель SpanBERTa на сервер Hugging Face. Перш ніж оцінювати модель на наступних завданнях, давайте подивимося, як вона навчилася заповнювати замасковані слова з урахуванням контексту.
У [0]:
%pture %%час з трансформаторів імпортуємо конвеєр fill_mask = pipeline( "fill-mask", model="chriskhanhtran/spanberta", tokenizer="chriskhanhtran/spanberta" )
Я вибираю речення зі статті Вікіпедії про COVID-19.
Оригінальне речення звучить так: "Часто мийте руки водою та ополіскувачем,"що означає"Часто мийте руки з милом.“
Замасковане слово - це "jabón" (мило) і топ-5 прогнозів такі мило, сіль, пар, лимон і оцет. Цікаво, що модель якимось чином засвоює, що ми повинні мити руки речами, які можуть вбивати бактерії або містять кислоту.
У [0]:
fill_mask("Lavarse frecuentemente las manos con agua y .")
Out[0]:
[{'score': 0.6469631195068359, 'sequence': 'Lavarse frecuentemente las manos con agua y jabón.', 'token': 18493}, {'score': 0.06074320897459984, 'послідовність': 'Lavarse frecuentemente las manos con agua y sal.', 'token': 619}, {'score': 0.029787985607981682, 'послідовність': 'Lavarse frecuentemente las manos con agua y vapor.', 'token': 11079}, {'score': 0.026410052552819252, 'послідовність': 'Lavarse frecuentemente las manos con agua y limón.', 'token': 12788}, {'score': 0.017029203474521637, 'послідовність': 'Lavarse frecuentemente las manos con agua y vinagre.', 'token': 18424}]
Висновок¶
Ми розглянули, як навчити BERT-модель іспанської мови з нуля, і побачили, що модель вивчила властивості мови, намагаючись передбачити замасковані слова з урахуванням контексту. Ви також можете скористатися цією статтею, щоб налаштувати попередньо навчену BERT-подібну модель на своєму наборі даних.
Далі ми застосуємо попередньо навчені моделі до наступних задач, включаючи класифікацію послідовностей, NER, POS-тегування та NLI, а також порівняємо продуктивність моделі з деякими не-BERT моделями.
Слідкуйте за нашими наступними публікаціями!