Эту статью, вероятно, лучше назвать «Деконструкция 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 здесь.