Эту статью, вероятно, лучше назвать «Деконструкция fast.ai» или «Fast.ai из первых принципов», и она будет сосредоточена на построении модели для генерации текста с помощью PyTorch. Прошу прощения за заголовок, который выглядит довольно привлекательно.

Я предполагаю, что читатели будут знакомы с fast.ai и, вероятно, прошли онлайн-курс глубокого обучения. Я сделал первую итерацию курса, когда он использовал Керас. Вторая итерация, выпущенная для публики только на прошлой неделе, переключилась на использование PyTorch в качестве фреймворка. Я прошел эту версию курса лично в виде сертификата по глубокому обучению Университета Сан-Франциско (уроки записываются и скомпилированы в MOOC).

Первый курс, Керас, был потрясающим - интуитивно понятным, практичным и вдохновляющим. Версия 2, PyTorch, была более передовой и содержала большую часть того же теоретического материала, что и раньше, за исключением того, что она пыталась абстрагироваться от некоторых болевых точек и гибкости PyTorch с помощью собственного fast.ai библиотека. Для меня он лишил PyTorch заметности и непосредственности и вместо этого предоставил множество волшебных функций.

Это соответствует духу курса, который посвящен быстрому созданию высокопроизводительных приложений искусственного интеллекта, но меня не устраивает. Библиотека fast.ai лишена элегантности Keras; это плохо задокументировано; код слишком старается быть умным, а не читабельным; и кроме того: PyTorch действительно не нуждается в уровне абстракции. BLAS, завернутый в CUDA, завернутый в PyTorch, на мой взгляд, отличный и адекватный стек для создания ИИ. Если я продолжу абстрагироваться от этого, я мог бы также использовать API машинного обучения AWS.

Помня об этом, давайте посмотрим, как вырезать fast.ai мусор из их примера с повторяющейся нейронной сетью. Вы можете найти их оригинальную записную книжку на их Github здесь, а мою упрощенную версию - в репозитории здесь.

Предварительная обработка данных

Записная книжка fast.ai генерирует текст, похожий на Ницше, и начинается с получения некоторых входных данных - полных работ Mr Existential Angst - с:

Мне кажется, что упаковка базового метода urllib не нужна. Они делают это в первую очередь для того, чтобы добавить tqdm индикатор выполнения загрузки, но я думаю, что это всего лишь тайна, скрывающая простую цель кода. Вместо этого давайте ПОЦЕЛУЙ, и сделаем все проще, используя немного Шекспира.

Остальная часть предварительной обработки fast.ai просто преобразует этот текст в числа, которые могут быть введены в нейронную сеть (и обеспечивает обратное преобразование). Это не особо интересно и выглядит так:

Окно текста

Теория, лежащая в основе этой сети, заключается в том, что 3 предшествующих символа могут предсказать 4-й символ. fast.ai вполне разумно создает обучающий набор X и y следующим образом (слегка упрощенный мной):

Здесь они используют np.stack как быстрый способ превратить список в массив numpy, но давайте кратко рассмотрим эту функцию, поскольку она также появится позже. Он принимает аргумент ключевого слова, axis, и размещает столбцы вдоль этой оси. Укладка по первой оси не имеет значения, но если вы складываете по второй (axis = 1):

Построение модели

Фактические модели в fast.ai действительно хорошо объяснены и не являются предметом внимания этой статьи, но вот их простой код для принятия этих трех входных символов и прогнозирования следующего. Одно упрощение, которое я сделал: код fast.ai использует метод, называемый V как сокращение от torch.autograd.Variable - полезно, если вы пишете тонну моделей для обучения людей, но непрозрачно для учащихся, поэтому я переключился обратно.

Обратите внимание, в частности, на то, что модель принимает в качестве входных данных три отдельных тензора вместо одного входного тензора более высокого ранга. Оригинальная записная книжка профессора Джереми переходит к этому по мере перехода от базовой модели, описанной выше, к модели со слоем LSTM. Но пока разделение входных данных легче понять, поэтому давайте придерживаться его, даже если это усложняет подачу обучающих данных в модель, как мы увидим.

В других местах код fast.ai использует T как сокращение для (обернутой) функции torch.Tensor (в этом случае наш тензор должен индексироваться в матрицу вложений с целыми числами, поэтому конкретный тип тензора здесь LongTensor) . Это особенно сбивает с толку, когда T также является сокращенным обозначением транспонирования. Я вернулся к длинной форме.

Обучение модели

Это моя особая проблема с кодом fast.ai. Взгляните на оригинал:

Этот класс ColumnarModelData был затушеван в курсе и действительно пытался вникнуть в него с помощью:

приводит к:

Анализ исходного кода показывает, что этот объект наследует или использует следующее:

DataLoader
PassthruDataset
ColumnarDataset
MixedInputModel
BatchSampler, RandomSampler & SequentialSampler (from Torch itself)

DataLoader - еще один недокументированный класс, а PassthruDataset просто говорит:

Абстрактный класс, представляющий набор данных. Все остальные наборы данных должны подклассифицировать его.

По совпадению это та же самая строка документации, что и для класса Dataset. Что ж, мы, по крайней мере, можем видеть, что чем бы ни был ColumnarModelData, он возвращает объект, имеющий свойство, которое является итератором. Фактически, каждая итерация дает набор входных данных, простая идея при визуальном отображении:

Я заменил это своим собственным итератором:

Тогда у вас есть вся * странность, которую fast.ai использует вместе с итератором. Я никогда раньше не сталкивался с таким синтаксисом, и я особенно помню, как в классе профессор Джереми сказал: «Если вы не знаете, что это значит, поищите его». Оказывается, он называется оператором распаковки и делает следующее:

X = [1,2,3,4]
*xs, y = X # xs -> [1,2,3] and y -> 4
fn(*xs) -> fn(1, 2, 3)

Вооружившись этими знаниями, мы можем обучать модель обычным способом PyTorch без использования каких-либо классов-оболочек; особенно не волшебный метод fit, который используется в записной книжке fast.ai:

На самом деле он тренируется быстрее, чем модель fast.ai, при этом обеспечивая аналогичную точность, чего бы это ни стоило (что не так уж много: помните, что это была их дрянная базовая версия). И хотя результаты на самом деле не являются целью этой статьи, было бы неприятно закончить, не приведя пример выходных данных после тренировки на Барде в течение нескольких эпох:

Заключение

Что ж, мы сохранили хорошие вещи из урока 6 fast.ai, без необходимости использовать их библиотеку. Конечно, если вы еще не прошли курс посмотрите; Google позволяет запускать ноутбуки Jupyter с PyTorch на GPU бесплатно в их совместной лаборатории; и вы также можете найти мой полный урезанный блокнот на Github здесь.