Для данного семинара Вам потребуется ноутбук task5_text_transformer.ipynb.
Чтобы запустить ноутбук с семинара на своем компьютере:
1) Cклонируйте репозиторий курса:
git clone https://github.com/Samsung-IT-Academy/stepik-dl-nlp.git
2) В терминале выполните команду:
pip install -r requirements.txt
3) Запустите ноутбук:
ipython notebook
Чтобы запустить ноутбук на Google Colab:
1) Скачайте ноутбук (вкладка Github, затем прописываете адрес репозитория):
2) Запустите ноутбук.
3) Не забудьте выполнить команду git clone из первой (закомментированной) ячейки, чтобы выкачать на colab библиотеку dlnlputils
Ноутбуки также работают и на Kaggle (следуйте комментариям в ячейках ноутбука).
Ссылка на репозиторий со всеми материалами курса и инструкцией по запуску: https://github.com/Samsung-IT-Academy/stepik-dl-nlp
Видео проигрыватель загружается.Воспроизвести видеоВоспроизвестиБез звукаТекущее время 0:00/Продолжительность 10:25Загрузка: 0.00%00:00Тип потока ОНЛАЙНSeek to live, currently behind liveОНЛАЙНОставшееся время -10:25 1xСкорость воспроизведения2x1.75x1.5x1.25x1x, выбрано0.75x0.5xГлавыГлавыОписанияОтключить описания, выбраноСубтитрынастройки субтитров, откроется диалог настройки субтитровСубтитры выкл., выбраноЗвуковая дорожкаPicture-in-PictureПолноэкранный режимThis is a modal window.Начало диалоговго окна. Кнопка Escape закроет или отменит окноТекстColorБелыйЧерныйКрасныйЗеленыйСинийЖелтыйПурпурныйГолубойTransparencyПрозрачностьПолупрозрачныйФонColorЧерныйБелыйКрасныйЗеленыйСинийЖелтыйПурпурныйГолубойTransparencyПрозрачностьПолупрозрачныйПрозрачныйОкноColorЧерныйБелыйКрасныйЗеленыйСинийЖелтыйПурпурныйГолубойTransparencyПрозрачныйПолупрозрачныйПрозрачностьРазмер шрифта50%75%100%125%150%175%200%300%400%Стиль края текстаНичегоПоднятыйПониженныйОдинаковыйТеньШрифтПропорциональный без засечекМоноширинный без засечекПропорциональный с засечкамиМоноширинный с засечкамиСлучайныйПисьменныйМалые прописныеСбросить сбросить все найстройки по умолчаниюГотовоЗакрыть модальное окноКонец диалогового окна.
Всем привет! Сегодня у нас насыщенный семинар. Мы попробуем охватить темы моделирования языка, механизмов внимания и рассмотрим модную архитектуру "трансформер". Проверять работоспособность методов сегодня мы будем с помощью известного произведения Льва Николаевича Толстого "Война и мир". Итак, загрузим обучающую выборку. В нашем случае, обучающая выборка — это просто большой текстовый файл без всякой разметки. При загрузке текста мы читаем весь его в память, а потом нарезаем на кусочки размером в 200 символов. Таким образом мы получим набор небольших фрагментов текста, но, при этом, длина каждого фрагмента будет больше, чем длина отдельного предложения. Современные языковые модели работают с более длинными последовательностями, поэтому общепринятая схема — это "не выполнять разбиение текста на отдельные предложения перед подачей их в языковую модель". Всего у нас получилось около 8 тысяч фрагментов. На экране вы видите один такой фрагмент. Он содержит фрагменты двух предложений. Как обычно, разобьём все наши данные на обучающую и тестовую выборку в соотношении 70% в обучающую выборку, 30% в тестовую. Современные языковые модели работают, как правило, не с целыми токенами. Они работают с фрагментами слов, или, так называемыми "sub-word units" (по сути, это N-граммы символов). Поэтому для токенизации мы используем не классический токенизатор с помощью регулярных выражений или каких-то других правил (например, как те которые мы использовали в предыдущих семинарах). Здесь мы используем алгоритм "byte pair encoding". Напомню, в двух словах, как он работает, в чём его основной принцип. Допустим, у нас есть последовательность символов: "ABCABE", например. Тогда, сначала, этот алгоритм будет искать наиболее частотную биграмму (в данном случае это "AB"), и он заменит её в тексте на какой-то новый символ, который в тексте ранее не встречался. Например, мы получим "XCXE". Такая же операция поиска наиболее частотной биграммы и замены её на новый символ будет повторяться в цикле. Например, на следующем шаге мы заменим биграмму "XC" на какой-нибудь символ "Y" и получим новую последовательность, и так далее. Таким образом мы последовательно сжимаем текст и, в процессе сжатия текста, запоминаем те замены, которые мы сделали. Например, мы можем запомнить, что "AB" было заменено на "X", а "XC" заменено было на "Y". Такой алгоритм позволяет получить нечто среднее между алгоритмами, работающими на уровне отдельных токенов, и на уровне отдельных символов. Когда мы работаем с отдельными символами, у нас алфавит маленький, то есть решать задачу классификации нам попроще, но длина последовательностей растёт и поэтому нам нужно строить модели, которые умеют запоминать далёкие зависимости. Это достаточно сложно. Наоборот, если мы работаем с отдельными токенами, то последовательности у нас гораздо короче. Но, с другой стороны, словарь у нас разрастается очень быстро (нам нужно уметь предсказывать каждый отдельный токен, а токенов очень много — гораздо больше, чем символов). И поэтому, с одной стороны, нам нужно помнить более короткие зависимости, но, зато, задача классификации становится сложной, потому что классов очень много. Алгоритмы, такие как byte pair encoding, позволяют найти золотую середину между этими двумя крайностями. В этом семинаре мы будем использовать реализацию byte pair encoding из библиотеки "YouTokenToMe". Эта библиотека была разработана ребятами из "ВКонтакте"[1] и, на сегодняшний день, является самой быстрой реализацией byte pair encoding. Давайте посмотрим, как работать с этой библиотекой. Основной модуль библиотеки реализован не на python, и поэтому наиболее удобный и быстрый способ скармливания данных в эту библиотеку — это через текстовый файл. Поэтому мы, сначала, сохраняем все наши тексты в этот файлик, а затем вызываем функцию обучения. И функция обучения читает данные из текстового файла и складывает обученную модель (то есть, словарь замен) в другой файлик, который мы указали. Самый важный параметр здесь — это размер словаря. Он и позволяет нам выбрать, что мы хотим — более длинные последовательности, но меньший словарь, или более короткие последовательности, но более крупный словарь, и, чем больше наш словарь, тем больше будет редких классов — это будет создавать нам некоторые сложности при обучении. Когда модель обучена, мы создаём экземпляр класса "bpe" и передаём туда путь к файлу с обученной моделью. Давайте посмотрим на словарь, который наша модель выучила. Мы сказали алгоритму: "Пожалуйста, выдели нам тысячу наиболее характерных N-грамм через byte pair encoding". Вот какие N-граммы нашлись. Во-первых, словарь содержит несколько служебных токенов — это токен "padding", то есть токен, предназначенный для выравнивания длин последовательностей, чтобы их подавать в нейросеть; это токен "unknown" — это когда алгоритм встретил в тексте какую-то N-грамму, которую не видел при обучении; и два токена "beginning of sequence" и "end of sequence". Далее идёт набор юниграмм, то есть, по сути — это все уникальные символы, которые встретились в обучающей выборке. А вот дальше уже идут более сложные конструкции, причём здесь идут вперемешку как биграммы и так и более длинные последовательности. Мы можем видеть здесь как фрагменты слов (какие-то устойчивые подслова, то есть основы слова) можем видеть имена людей без окончания (как, например, "Андрей", "Ростов", и так далее). Также здесь есть и явно слишком специфические последовательности (например, " Пьер,"). Скорее всего, это сигнал к тому, что можно сделать словарь поменьше для нашей модели. Но это не так очевидно, нужно смотреть на метрики на отложенной выборке, чтобы выбрать правильный размер словаря. Токенизатор из библиотеки "YouTokenToMe" принимает на вход не отдельный текст, а сразу список текстов (список строк) и на выходе возвращает список списков, каждый вложенный список содержит числа — это номера токенов (номера N-грамм) в словаре. В принципе, как обычно. Давайте посмотрим — а какой длины последовательности после токенизации у нас получились. Напомню что, когда мы загружали датасет, мы нарезали исходный текст на кусочки длиной 200 символов. В результате токенизации, большая часть фрагментов получила длину от 60 до 140, примерно. Причём наиболее распространённая длина последовательности — мода — около 80, то есть получилось сжать среднюю длину текста чуть более, чем в два раза. Таким образом, с точки зрения длины последовательности, задача уже проще, чем моделирование языка на уровне отдельных символов. Если мы построим гистограмму частот встречаемости токенов, то мы найдём старое доброе распределение Ципфа: у нас очень мало частотных N-грамм (то есть тех N-грамм, которые встретились больше 2000 раз — их, наверное, меньше 20 суммарно), и основное количество N-грамм встретилось порядка нескольких сотен раз. Надо сказать, что очень редких N-грамм (то есть, вот этот — самый левый столбик) — их небольшое количество (около 100, всего лишь), то есть большая часть словаря у нас не является редкими классами. Это хорошая новость — всегда проще решать задачу классификации, когда классы сбалансированы. Когда мы обучали наш токенизатор, мы использовали только обучающую подвыборку всех данных, то есть только 70% текстов. Логично, что в остальных 30% могут встретиться токены, которые не встречались в обучающей выборке. Но мы используем здесь BPE, и поэтому в тестовой выборке, на самом деле, не оказалось токенов, которые бы мы не увидели в том или ином виде в обучающей выборке. То есть — да, может быть, какие-то длинные N-граммы мы там не нашли, но зато мы смогли эти длинные N-граммы разбить на более мелкие, и, всё равно, все символы у нас так или иначе нашлись в словаре. Таким образом, когда у нас в новом тексте встречаются только неизвестные слова, то BPE просто деградирует до character-level, то есть наша модель просто становится моделью на уровне отдельных символов. Это, конечно, посложнее, но она не перестаёт работать.
[1] https://github.com/VKCOM/YouTokenToMe
К сожалению, у нас пока нет статистики ответов на данный вопрос,
но мы работаем над этим.