Примерный ввод в LSTM для генерации текста – с помощью Keras и ядра Kaggle с поддержкой GPU

1656617670 primernyj vvod v lstm dlya generaczii teksta – s pomoshhyu

автор Меган Рисдал

Недавно Kaggle дал ученым из данных возможность добавить графический процессор в Kernels (облачная платформа для ноутбуков Kaggle). Я знал, что это будет идеальной возможностью для меня научиться строить и учить более интенсивные вычислительные модели.

Благодаря Kaggle Learn, документации Keras и крутым данным природного языка с freeCodeCamp я имел все необходимое для продвижения от случайных лесов до повторяющихся нейронных сетей.

Fr8gPPB9gylc-IX97WKJdBm1d8we94yx152e
Набор данных freeCodeCamp на Kaggle Datasets.

В этой публикации блога я покажу вам, как я использовал текст из набора данных журналов чата Gitter FreeCodeCamp, опубликованного на Kaggle Datasets для обучения сети LSTM, которая генерирует новый текстовый выход.

Вы можете найти весь мой воспроизводимый код в этом ядре блокнота Python.

Теперь вы можете использовать графические процессоры в Kernels — облачная платформа для ноутбуков Kaggle — с 6 часов работывы можете обучать гораздо более интенсивные вычислительные модели, чем когда-либо ранее, на Kaggle.

Я использую графический процессор для обучения модели в этом ноутбуке. (Вы можете запросить графический процессор для своего сеанса, нажав вкладку «Настройки» в редакторе ядра.)

import tensorflow as tfprint(tf.test.gpu_device_name())# See  = tf.ConfigProto()config.gpu_options.allow_growth = True
PxD98069C0tEmNjWCZeqi2R8X2PJKAT8l4RW

Я буду использовать текст одного из самых распространенных идентификаторов пользователей канала в качестве учебных данных. Этот блокнот состоит из двух частей:

  1. Чтение, изучение и подготовка данных
  2. Обучение LSTM на журналах чата с одним идентификатором пользователя и создание нового текста как результата

Вы можете продолжить, просто читая блокнот, или вы можете разделить его (нажать «Форк тетради») и запустить ячейки самостоятельно, чтобы узнать, что каждая часть делает в интерактивном режиме. До конца ты научишься как отформатировать текстовые данные как входные данные для модели LSTM на уровне символов, реализованной в Keras и, в свою очередь, использовать предсказание модели на уровне символов для создавать новые последовательности текста.

Прежде чем перейти к коду, что такое сеть LSTM («Долговременная память»)?

В этом учебнике мы рассмотрим практический подход к реализации этой разновидности рекуррентной нейронной сети, специально оборудованной для обработки зависимостей на большем расстоянии (включая те, которые вы получаете с языком) в Keras, фреймворке глубокого обучения.

Если вы хотите просмотреть больше теоретических основ, я рекомендую вам ознакомиться с этой замечательной корреспонденцией в блоге «Понимание сетей LSTM» Кристофера Ола.

Часть первая: Подготовка данных

В первой части я сначала прочту данные и попробую изучить их достаточно, чтобы дать вам представление о том, с чем мы работаем. Одно из моих разочарований в связи со следующими неинтерактивными учебниками (например, статическим кодом, доступным на GitHub) заключается в том, что часто трудно понять, чем данные, с которыми вы хотите работать, отличаются от образца кода. Вы должны загрузить его и сравнить его локально, что вызывает боль.

Две приятные моменты, связанные с этим учебником с использованием ядра, это то, что а) я постараюсь предоставить вам представление о данных на каждом важном шаге; и 2) вы всегда можете развить этот блокнот и ?бум? у вас есть копия моей среды, данные, образ Docker и все это без загрузок или установки. Особенно если у вас есть опыт установки CUDA для использования графических процессоров для глубокого обучения, вы оцените, как прекрасно, что среда уже настроена для вас.

Прочтите данные

import pandas as pdimport numpy as np# Read in only the two columns we need chat = pd.read_csv('../input/freecodecamp_casual_chatroom.csv', usecols = ['fromUser.id', 'text'])
# Removing user id for CamperBotchat = chat[chat['fromUser.id'] != '55b977f00fc9f982beab7883'] chat.head()
JdWmZVD9DDxPuIZ8YrbIzCWjGzMBM7FvNLTZ

Выглядит отлично!

Изучите данные

На моем графике ниже вы можете увидеть количество сообщений от десяти активных участников чата по их идентификатору пользователя в Gitter freeCodeCamp:

import matplotlib.pyplot as pltplt.style.use('fivethirtyeight')f, g = plt.subplots(figsize=(12, 9))chat['fromUser.id'].value_counts().head(10).plot.bar(color="green")g.set_xticklabels(g.get_xticklabels(), rotation=25)plt.title("Most active users in freeCodeCamp's Gitter channel")plt.show(g)
T-vw8lpBrUrSMircm9JvOvhtIF2yV9IPVFYh

Итак, идентификатор пользователя 55a7c9e08a7b72f55c3f991e является самым активным пользователем канала из более чем 140 000 сообщений. Мы будем использовать их сообщения, чтобы научить LSTM генерировать роман 55a7c9e08a7b72f55c3f991e— как предложение. Но сначала давайте посмотрим на первые несколько сообщений от 55a7c9e08a7b72f55c3f991e чтобы понять, о чем они общаются:

chat[chat['fromUser.id'] == "55a7c9e08a7b72f55c3f991e"].text.head(20)
q1Eul6QWB93FqoxHrsZettuYle2z6vZQ21a6

Я вижу такие слова и фразы, как «документация», «парная кодировка», «BASH», «Bootstrap», «CSS» и т.д. И я могу лишь предположить, что предложение, которое начинается «Со всеми разными фреймворками…», относится к JavaScript. Да, похоже, что они по теме, что касается freeCodeCamp. Поэтому мы ожидаем, что наши новые предложения будут выглядеть примерно так, если мы добьемся успеха.

Подготовьте данные последовательности для ввода в LSTM

Сейчас у нас есть фрейм данных со столбцами, соответствующими идентификаторам пользователей и тексту сообщения, где каждая строка соответствует одному отправленному сообщению. Это достаточно далеко от 3D-формы, которой требует входной уровень нашей сети LSTM: model.add(LSTM(batch_size, input_shape=(time_steps, features))) где batch_size — количество последовательностей в каждой выборке (может быть одна или несколько), time_steps – размер наблюдений в каждой выборке, и features – количество возможных наблюдаемых признаков (т.е. символов в нашем случае).

Итак, как нам перейти от кадра данных к последовательности данных в правильной форме? Разоблю это на три шага:

  1. Умножьте данные, чтобы сформировать корпус
  2. Форматируйте корпус из №1 в полуперекрывающиеся массивы последовательностей одинаковой длины и следующих символов.
  3. Представить данные последовательности из №2 в виде разреженных логических тензоров

Умножьте данные, чтобы сформировать корпус

В следующих двух ячейках мы будем захватывать только сообщения 55a7c9e08a7b72f55c3f991e ('fromUser.id' == '55a7c9e08a7b72f55c3f991e'), чтобы подмножить данные и свернуть вектор строк в одну строку. Поскольку нам безразлично, генерирует ли наша модель текст с правильным использованием заглавных букв, мы используем tolower(). Это дает модели на одно измерение меньшее для изучения.

Я также просто собираюсь использовать первые 20% данных в качестве образца, поскольку нам не нужно больше этого, чтобы создать полуприличный текст. Вы можете попытаться разделить это ядро ​​и поэкспериментировать с большим (или меньшим) количеством данных, если хотите.

user = chat[chat['fromUser.id'] == '55a7c9e08a7b72f55c3f991e'].textn_messages = len(user)n_chars = len(' '.join(map(str, user)))print("55a7c9e08a7b72f55c3f991e accounts for %d messages" % n_messages)print("Their messages add up to %d characters" % n_chars)
JkGCjTyc6B2LeUxyAZhboKGyO6Ru7v9UHVe4
sample_size = int(len(user) * 0.2)user = user[:sample_size]user=" ".join(map(str, user)).lower()user[:100] # Show first 100 characters
PZb-K0vqqMNwYkucZlLRGp32crjFnS664ksC

Форматируйте корпус в массивы полуперекрывающихся последовательностей одинаковой длины и следующих символов

Остальные коды, использованные здесь, адаптированы из этого примера сценария, первоначально написанного Франсуа Шолле (автор Кераса и Кагглера), чтобы подготовить данные в правильном формате для обучения LSTM. Поскольку мы обучаем модели на уровне символов, мы связываем уникальные символы (например, «a», «b», «c», …) с числовыми индексами в ячейке ниже. Если вы запустите этот код самостоятельно, нажав «Форк Notebook», вы сможете распечатать все использованные символы.

chars = sorted(list(set(user)))print('Count of unique characters (i.e., features):', len(chars))
char_indices = dict((c, i) for i, c in enumerate(chars))indices_char = dict((i, c) for i, c in enumerate(chars))
J-Dyhx6j1PSRK3zsijxFrF8Atvvzgy5F41En

Этот следующий шаг ячейки дает нам массив, sentencesсделано из maxlen (40) последовательности символов, разделенные на 3 символа из нашего корпуса userи next_charsмассив отдельных символов из user на i + maxlen для каждого i. Я распечатал первые 10 строк в массиве, чтобы вы могли видеть, что мы разбиваем корпус на частично перекрывающиеся, равной длине «предложения».

maxlen = 40step = 3sentences = []next_chars = []for i in range(0, len(user) - maxlen, step):    sentences.append(user[i: i + maxlen])    next_chars.append(user[i + maxlen])print('Number of sequences:', len(sentences), "\n")print(sentences[:10], "\n")print(next_chars[:10])
GOwu0LKKiu9bwftZwaTNu0FapNo6BKbpS182

Вы можете увидеть, как следующий символ следует за первой последовательностью 'hi folks. just doing the new signee stuf' это характер f закончить слово «вещь». И следующий символ по последовательности 'folks. just doing the new signee stuff. ' это характер h чтобы начать слово «привет». Таким образом, теперь должно быть понятно, как next_chars это «метки данных» или основная истина для наших последовательностей в sentences и наша модель, обученная на этих обозначенных данных, сможет генерировать новые следующие персонажи как предсказания, заданные вводом последовательности.

Представить данные последовательности в виде разреженных логических тензоров

Запуск следующей ячейки занимает несколько секунд, если вы следите за интерактивным процессом в ядре. Мы создаем разреженные логические тензоры x и y кодирование признаков уровня символов с sentences и next_chars использовать в качестве входных данных для модели, которую мы учим. Форма, которую мы получим, будет: input_shape=(maxlen, len(chars)) где maxlen составляет 40 и len(chars) – количество признаков (т.е. уникальное количество символов из нашего корпуса).

x = np.zeros((len(sentences), maxlen, len(chars)), dtype=np.bool)y = np.zeros((len(sentences), len(chars)), dtype=np.bool)for i, sentence in enumerate(sentences):    for t, char in enumerate(sentence):        x[i, t, char_indices[char]] = 1    y[i, char_indices[next_chars[i]]] = 1

Часть вторая: Моделирование

Во второй части мы выполняем собственное обучение модели и генерируем текст. Мы исследовали данные и изменили их правильно, чтобы мы могли использовать их в качестве входных данных для нашей модели LSTM. В этой части есть два раздела:

  1. Определение модели сети LSTM
  2. Обучение модели и генерирование прогнозов

Определение модели сети LSTM

Начнём с чтения в наших библиотеках. Я использую Keras, который является популярным и простым в использовании интерфейсом для бэкенда TensorFlow. Подробнее о том, почему использовать Keras в качестве фреймворка глубокого обучения, читайте здесь. Ниже можно увидеть модели, слои, оптимизаторы и обратные вызовы, которые мы будем использовать.

from keras.models import Sequentialfrom keras.layers import Dense, Activationfrom keras.layers import LSTMfrom keras.optimizers import RMSpropfrom keras.callbacks import LambdaCallback, ModelCheckpointimport randomimport sysimport io
oh7JrJZ0CoIJR1YWCTL-045pe0jRxVzhcovb

В ячейке ниже мы определяем модель. Мы начинаем с последовательной модели и добавляем LSTM в качестве входного слоя. Форма, которую мы определяем для нашего введения, до сих пор идентична нашим данным, а именно то, что нам нужно. Я выбрал а batch_size из 128, являющихся количеством образцов или последовательностей, которые наша модель рассматривает во время обучения перед обновлением. Вы можете экспериментировать с разными числами, если хотите. Я тоже добавляю плотный исходный слой. Наконец, мы используем добавление слоя активации из softmax как наша функция активации, поскольку мы, в сущности, выполняем мультиклассовую классификацию, чтобы предусмотреть следующий символ в последовательности.

model = Sequential()model.add(LSTM(128, input_shape=(maxlen, len(chars))))model.add(Dense(len(chars)))model.add(Activation('softmax'))

Теперь мы можем собрать нашу модель. Мы воспользуемся RMSprop со скоростью обучения 0.1 оптимизировать весы в нашей модели (здесь можно поэкспериментировать с разными темпами обучения) и categorical_crossentropy как наша функция утраты. Перекрестная энтропия – это то же, что потеря журнала обычно используется как показатель оценки в соревнованиях по бинарной классификации на Kaggle (за исключением того, что в нашем случае есть более двух возможных результатов).

optimizer = RMSprop(lr=0.01)model.compile(loss="categorical_crossentropy", optimizer=optimizer)

Теперь наша модель готова. Прежде чем мы предоставляем ему какие-либо данные, ячейка ниже определяет пару вспомогательных функций с кодом, измененным по этому сценарию. Первый, sample()выборка индекса из массива вероятностей с некоторыми temperature. Быстрая пауза, чтобы спросить, что такое температура?

Температура является коэффициентом масштабирования, применяемым к выходам нашего плотного слоя перед применением. softmaxфункция активации Короче говоря, он определяет, насколько консервативными или креативными являются догадки модели относительно следующего персонажа в последовательности. Низшие значения temperature (например, 0.2) создаст «безопасные» предположения, тогда как значение temperature выше 1.0 начнет генерировать более «рисковые» догадки. Подумайте об этом как о количестве удивления, которое вы получите, увидев, что английское слово начинается на «st» против «sg». Когда температура низкая, мы можем получить много «i» и «i»; когда температура высока, все становится непредсказуемым.

В любом случае, второй определяет функцию обратного вызова для печати предполагаемого текста, сгенерированного нашим обученным LSTM в первую, а затем каждую последующую пятую эпоху с пятью разными настройками. temperature каждый раз (см. строку for diversity in [0.2, 0.5, 1.0, 1.2]: для значений temperature; не стесняйтесь настроить их тоже!). Таким образом мы можем возиться с temperature регулятор, чтобы увидеть, что обеспечивает лучший генерируемый текст, начиная от консервативного до креативного. Обратите внимание, что мы используем нашу модель для прогнозирования на основе случайной последовательности, или «зерна», из наших исходных подмножеств данных, user: start_index = random.randint(0, len(user) - maxlen - 1).

Наконец, мы называем нашу функцию обратного вызова generate_text которую мы добавим в список обратных вызовов, когда установим нашу модель в клетку после этой.

def sample(preds, temperature=1.0):    # helper function to sample an index from a probability array    preds = np.asarray(preds).astype('float64')    preds = np.log(preds) / temperature    exp_preds = np.exp(preds)    preds = exp_preds / np.sum(exp_preds)    probas = np.random.multinomial(1, preds, 1)    return np.argmax(probas)def on_epoch_end(epoch, logs):    # Function invoked for specified epochs. Prints generated text.    # Using epoch+1 to be consistent with the training epochs printed by Keras    if epoch+1 == 1 or epoch+1 == 15:        print()        print('----- Generating text after Epoch: %d' % epoch)        start_index = random.randint(0, len(user) - maxlen - 1)        for diversity in [0.2, 0.5, 1.0, 1.2]:            print('----- diversity:', diversity)            generated = ''            sentence = user[start_index: start_index + maxlen]            generated += sentence            print('----- Generating with seed: "' + sentence + '"')            sys.stdout.write(generated)            for i in range(400):                x_pred = np.zeros((1, maxlen, len(chars)))                for t, char in enumerate(sentence):                    x_pred[0, t, char_indices[char]] = 1.                preds = model.predict(x_pred, verbose=0)[0]                next_index = sample(preds, diversity)                next_char = indices_char[next_index]                generated += next_char                sentence = sentence[1:] + next_char                sys.stdout.write(next_char)                sys.stdout.flush()            print()    else:        print()        print('----- Not generating text after Epoch: %d' % epoch)generate_text = LambdaCallback(on_epoch_end=on_epoch_end)

Обучение модели и генерирование прогнозов

Наконец-то нам это удалось! Наши данные готовы (x для последовательностей, y для следующих символов), мы выбрали a batch_size с 128и мы определили функцию обратного вызова, которая будет печатать сгенерированный текст с помощью model.predict() в конце первой эпохи следует каждая пятая эпоха с пятью разными temperature настройка каждый раз. У нас есть еще один обратный звонок, ModelCheckpointкоторый сохранит лучшую модель в каждую эпоху, если ее улучшить на основе нашего значения потерь (найдите сохраненный файл весов weights.hdf5 на вкладке «Выход» (ядра).

Давайте подогнать нашу модель к этим характеристикам и epochs = 15 на количество эпох для учёбы. И, конечно, давайте не забываем использовать наш графический процессор! Это сделает обучение/прогнозирование гораздо быстрее, чем если бы мы использовали центральный процессор. В любом случае, вам все равно захочется перекусить или погулять, пока вы ждете, пока модель начнет тренироваться и генерировать прогнозы, если вы запускаете этот код в интерактивном режиме.

PS Если вы запускаете это в интерактивном режиме в своем блокноте на Kaggle, вы можете нажать кнопку «Стоп» в синем квадрате у консоли внизу экрана, чтобы прервать обучение модели.

# define the checkpointfilepath = "weights.hdf5"checkpoint = ModelCheckpoint(filepath,                              monitor="loss",                              verbose=1,                              save_best_only=True,                              mode="min")# fit model using our gpuwith tf.device('/gpu:0'):    model.fit(x, y,              batch_size=128,              epochs=15,              verbose=2,              callbacks=[generate_text, checkpoint])
qbPSuBNNnf0SNAxvcj6UoQT7ieWC3zeqMNIx
Пример вывода после первой эры.

Вывод

И вот оно! Если вы запустили этот блокнот в ядрах Kaggle, вы надеемся поймали модель, которая распечатывает сгенерированный текст по символам для драматического эффекта.

Надеюсь, вам понравилось учиться, как начать от фрейма данных, содержащего строки текста, до использования модели LSTM, реализуемой с помощью Keras в Kernels для создания новых предложений благодаря мощности графических процессоров. Вы можете увидеть, как наша модель усовершенствовалась от первой до последней эпохи. Текст, созданный прогнозами модели в первую эпоху, отнюдь не напоминал английский. И в целом, более низкие уровни разнообразия генерируют текст с большим количеством повторов, тогда как более высокие уровни разнообразия соответствуют большему gobbledegook.

Можете ли вы настроить модель или ее гиперпараметры, чтобы создать еще лучший текст? Попробуйте это сами, разделив ядро ​​этого ноутбука (нажмите «Форк Notebook» вверху).

Вдохновение для следующих шагов

Вот несколько идей относительно того, как взять то, что вы научились здесь, и расширить его:

  1. Экспериментируйте с различными (гипер) параметрами, такими как количество обучающих данных, количество эпох или размеры пакетов, temperatureтому подобное
  2. Попробуйте тот же код с разными данными; Разделите этот блокнот, перейдите на вкладку «Данные» и удалите источник данных freeCodeCamp, затем добавьте другой набор данных (хорошие примеры здесь).
  3. Попробуйте более сложную сетевую архитектуру, например добавление слоев выпадения.
  4. Узнайте больше о глубоком обучении на Kaggle Learn, серии видео и практических пособий для блокнотов в Kernels.
  5. Используйте weights.hdf5 в «Выводе», чтобы предусмотреть на основе различных данных в новом ядре, что было бы, если бы пользователь в этом учебнике завершил чужие предложения.
  6. Сравните эффект ускорения использования ЦП и графического процессора на минимальном примере.

Добавить комментарий

Ваш адрес email не будет опубликован.