www.machinelearningmastery.ru

Машинное обучение, нейронные сети, искусственный интеллект
Header decor

Home

Генерация текста с помощью LSTM рекуррентных нейронных сетей в Python с Keras

Дата публикации 2016-08-04

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

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

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

В этом посте вы узнаете, как создать генеративную модель для текста, посимвольный, используя рекуррентные нейронные сети LSTM в Python с Keras.

Прочитав этот пост, вы узнаете:

  • Где скачать бесплатный корпус текста, который вы можете использовать для обучения генеративных моделей текста.
  • Как поставить проблему текстовых последовательностей в рекуррентную модель генерации нейронной сети.
  • Как разработать LSTM для генерации правдоподобных текстовых последовательностей для данной проблемы.

Давайте начнем.

Заметка: Рекуррентные нейронные сети LSTM могут работать медленно, поэтому настоятельно рекомендуется обучать их на оборудовании с графическим процессором. Вы можете получить доступ к оборудованию графического процессора в облаке очень дешево, используя Amazon Web Services,см. учебник здесь,

  • Обновление октябрь 2016: Исправлено несколько незначительных опечаток в коде.
  • Обновление март / 2017: Обновлен пример для Keras 2.0.2, TensorFlow 1.0.1 и Theano 0.9.0.

Описание проблемы: проект Гутенберг

Многие из классических текстов больше не защищены авторским правом.

Это означает, что вы можете скачать весь текст этих книг бесплатно и использовать их в экспериментах, например, при создании генеративных моделей. Возможно, лучшее место для получения доступа к бесплатным книгам, которые больше не защищены авторским правом, этоПроект Гутенберг,

В этом уроке мы собираемся использовать любимую книгу из детства в качестве набора данных:Приключения Алисы в Стране Чудес Льюиса Кэрролла,

Мы собираемся изучить зависимости между символами и условные вероятности символов в последовательностях, чтобы мы могли, в свою очередь, генерировать совершенно новые и оригинальные последовательности символов.

Это очень весело, и я рекомендую повторить эти эксперименты с другими книгами из проекта Гутенберга,вот список самых популярных книг на сайте,

Эти эксперименты не ограничиваются текстом, вы также можете поэкспериментировать с другими данными ASCII, такими как компьютерный исходный код, размеченные документы в LaTeX, HTML или Markdown и другие.

Вы можетескачать полный текст в формате ASCII(Обычный текст UTF-8) для этой книги бесплатно и поместите ее в свой рабочий каталог с именем файлаwonderland.txt,

Теперь нам нужно подготовить набор данных к моделированию.

Project Gutenberg добавляет стандартный колонтитул к каждой книге, и это не является частью исходного текста. Откройте файл в текстовом редакторе и удалите верхний и нижний колонтитулы.

Заголовок очевиден и заканчивается текстом:

*** START OF THIS PROJECT GUTENBERG EBOOK ALICE'S ADVENTURES IN WONDERLAND ***

Нижний колонтитул - весь текст после строки текста, которая говорит:

THE END

Вы должны остаться с текстовым файлом, который содержит около 3330 строк текста.

Нужна помощь с LSTM для прогнозирования последовательности?

Пройдите мой бесплатный 7-дневный курс по электронной почте и откройте для себя 6 различных архитектур LSTM (с кодом).

Нажмите, чтобы зарегистрироваться, а также получите бесплатную PDF-версию курса Ebook.

Начни свой БЕСПЛАТНЫЙ мини-курс сейчас!

Разработка малой рекуррентной нейронной сети LSTM

В этом разделе мы разработаем простую сеть LSTM для изучения последовательностей персонажей Алисы в стране чудес. В следующем разделе мы будем использовать эту модель для генерации новых последовательностей символов.

Давайте начнем с импорта классов и функций, которые мы намереваемся использовать для обучения нашей модели.

import numpy
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import LSTM
from keras.callbacks import ModelCheckpoint
from keras.utils import np_utils

Затем нам нужно загрузить текст ASCII для книги в память и преобразовать все символы в нижний регистр, чтобы уменьшить словарный запас, который должна выучить сеть.

# load ascii text and covert to lowercase
filename = "wonderland.txt"
raw_text = open(filename).read()
raw_text = raw_text.lower()

Теперь, когда книга загружена, мы должны подготовить данные для моделирования нейронной сетью. Мы не можем моделировать символы напрямую, вместо этого мы должны преобразовать символы в целые числа.

Мы можем сделать это легко, сначала создав набор всех отдельных символов в книге, а затем создав карту каждого символа с уникальным целым числом.

# create mapping of unique chars to integers
chars = sorted(list(set(raw_text)))
char_to_int = dict((c, i) for i, c in enumerate(chars))

Например, список уникальных отсортированных строчных символов в книге выглядит следующим образом:

['\n', '\r', ' ', '!', '"', "'", '(', ')', '*', ',', '-', '.', ':', ';', '?', '[', ']', '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '\xbb', '\xbf', '\xef']

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

Теперь, когда книга загружена и карта подготовлена, мы можем суммировать набор данных.

n_chars = len(raw_text)
n_vocab = len(chars)
print "Total Characters: ", n_chars
print "Total Vocab: ", n_vocab

Запуск кода до этой точки приводит к следующему выводу.

Total Characters:  147674
Total Vocab:  47

Мы видим, что книга содержит менее 150000 символов и что при преобразовании в строчные буквы в словаре есть только 47 различных символов для изучения сетью. Гораздо больше, чем 26 в алфавите.

Теперь нам нужно определить данные обучения для сети Существует большая гибкость в том, как вы решаете разбивать текст и выставлять его в сети во время обучения.

В этом уроке мы разделим текст книги на подпоследовательности с фиксированной длиной в 100 символов произвольной длины. Мы могли бы так же легко разделить данные по предложениям, дополнить более короткие последовательности и укоротить более длинные.

Каждый обучающий шаблон сети состоит из 100 временных шагов одного символа (X), за которыми следует один символьный вывод (y). При создании этих последовательностей мы перемещаем это окно по всей книге по одному символу за раз, позволяя каждому персонажу выучить шанс из 100 предшествующих ему символов (кроме, конечно, первых 100 символов).

Например, если длина последовательности равна 5 (для простоты), то первые два шаблона обучения будут следующими:

CHAPT -> E
HAPTE -> R

Разделяя книгу на эти последовательности, мы конвертируем символы в целые числа, используя нашу таблицу поиска, которую мы подготовили ранее.

# prepare the dataset of input to output pairs encoded as integers
seq_length = 100
dataX = []
dataY = []
for i in range(0, n_chars - seq_length, 1):
	seq_in = raw_text[i:i + seq_length]
	seq_out = raw_text[i + seq_length]
	dataX.append([char_to_int[char] for char in seq_in])
	dataY.append(char_to_int[seq_out])
n_patterns = len(dataX)
print "Total Patterns: ", n_patterns

Выполнение кода к этому моменту показывает нам, что когда мы разбили набор данных на тренировочные данные для сети, чтобы узнать, что у нас чуть менее 150 000 обучающих патентов. Это имеет смысл, поскольку исключая первые 100 символов, у нас есть один тренировочный шаблон для прогнозирования каждого из оставшихся символов.

Total Patterns:  147574

Теперь, когда мы подготовили наши тренировочные данные, нам нужно преобразовать их так, чтобы они подходили для использования с Keras.

Сначала мы должны преобразовать список входных последовательностей в форму[образцы, временные шаги, особенности]ожидается сетью LSTM.

Затем нам нужно изменить масштаб целых чисел в диапазоне от 0 до 1, чтобы облегчить изучение шаблонов сетью LSTM, которая по умолчанию использует функцию активации сигмовидной кишки.

Наконец, нам нужно преобразовать выходные шаблоны (отдельные символы, преобразованные в целые числа) в одну горячую кодировку. Это сделано для того, чтобы мы могли настроить сеть так, чтобы она предсказывала вероятность каждого из 47 различных символов в словаре (более простое представление), а не пыталась заставить ее предсказать точно следующий символ. Каждое значение y преобразуется в разреженный вектор длиной 47, полный нулей, за исключением 1 в столбце для буквы (целое число), которую представляет шаблон.

Например, когда «n» (целочисленное значение 31) является горячим кодированием, оно выглядит следующим образом:

[ 0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.
  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  1.  0.  0.  0.  0.
  0.  0.  0.  0.  0.  0.  0.  0.]

Мы можем реализовать эти шаги, как показано ниже.

# reshape X to be [samples, time steps, features]
X = numpy.reshape(dataX, (n_patterns, seq_length, 1))
# normalize
X = X / float(n_vocab)
# one hot encode the output variable
y = np_utils.to_categorical(dataY)

Теперь мы можем определить нашу модель LSTM. Здесь мы определяем один скрытый слой LSTM с 256 единицами памяти. Сеть использует выпадение с вероятностью 20. Выходной уровень - это Плотный уровень, использующий функцию активации softmax для вывода прогнозирования вероятности для каждого из 47 символов в диапазоне от 0 до 1.

Эта проблема на самом деле представляет собой проблему классификации отдельных символов с 47 классами, и поэтому она определяется как оптимизация потерь в журнале (перекрестная энтропия) с использованием алгоритма оптимизации ADAM по скорости.

# define the LSTM model
model = Sequential()
model.add(LSTM(256, input_shape=(X.shape[1], X.shape[2])))
model.add(Dropout(0.2))
model.add(Dense(y.shape[1], activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam')

Тестового набора данных нет. Мы моделируем весь обучающий набор данных, чтобы узнать вероятность каждого персонажа в последовательности.

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

Сеть работает медленно (около 300 секунд на эпоху на графическом процессоре Nvidia K520). Из-за медлительности и из-за наших требований по оптимизации мы будем использовать контрольные точки модели для записи всех сетевых весов, чтобы каждый раз регистрировать улучшение потерь в конце эпохи. Мы будем использовать лучший набор весов (наименьшая потеря), чтобы реализовать нашу генеративную модель в следующем разделе.

# define the checkpoint
filepath="weights-improvement-{epoch:02d}-{loss:.4f}.hdf5"
checkpoint = ModelCheckpoint(filepath, monitor='loss', verbose=1, save_best_only=True, mode='min')
callbacks_list = [checkpoint]

Теперь мы можем приспособить нашу модель к данным. Здесь мы используем скромное количество из 20 эпох и большой размер пакета из 128 шаблонов.

model.fit(X, y, epochs=20, batch_size=128, callbacks=callbacks_list)

Полный список кодов приведен ниже для полноты.

# Small LSTM Network to Generate Text for Alice in Wonderland
import numpy
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import LSTM
from keras.callbacks import ModelCheckpoint
from keras.utils import np_utils
# load ascii text and covert to lowercase
filename = "wonderland.txt"
raw_text = open(filename).read()
raw_text = raw_text.lower()
# create mapping of unique chars to integers
chars = sorted(list(set(raw_text)))
char_to_int = dict((c, i) for i, c in enumerate(chars))
# summarize the loaded data
n_chars = len(raw_text)
n_vocab = len(chars)
print "Total Characters: ", n_chars
print "Total Vocab: ", n_vocab
# prepare the dataset of input to output pairs encoded as integers
seq_length = 100
dataX = []
dataY = []
for i in range(0, n_chars - seq_length, 1):
	seq_in = raw_text[i:i + seq_length]
	seq_out = raw_text[i + seq_length]
	dataX.append([char_to_int[char] for char in seq_in])
	dataY.append(char_to_int[seq_out])
n_patterns = len(dataX)
print "Total Patterns: ", n_patterns
# reshape X to be [samples, time steps, features]
X = numpy.reshape(dataX, (n_patterns, seq_length, 1))
# normalize
X = X / float(n_vocab)
# one hot encode the output variable
y = np_utils.to_categorical(dataY)
# define the LSTM model
model = Sequential()
model.add(LSTM(256, input_shape=(X.shape[1], X.shape[2])))
model.add(Dropout(0.2))
model.add(Dense(y.shape[1], activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam')
# define the checkpoint
filepath="weights-improvement-{epoch:02d}-{loss:.4f}.hdf5"
checkpoint = ModelCheckpoint(filepath, monitor='loss', verbose=1, save_best_only=True, mode='min')
callbacks_list = [checkpoint]
# fit the model
model.fit(X, y, epochs=20, batch_size=128, callbacks=callbacks_list)

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

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

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

weights-improvement-19-1.9435.hdf5

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

В следующем разделе мы рассмотрим использование этой модели для генерации новых текстовых последовательностей.

Генерация текста с помощью сети LSTM

Генерация текста с использованием обученной сети LSTM относительно проста.

Во-первых, мы загружаем данные и определяем сеть точно таким же образом, за исключением того, что веса сети загружаются из файла контрольных точек, и сеть не нуждается в обучении.

# load the network weights
filename = "weights-improvement-19-1.9435.hdf5"
model.load_weights(filename)
model.compile(loss='categorical_crossentropy', optimizer='adam')

Кроме того, при подготовке сопоставления уникальных символов с целыми числами мы также должны создать обратное отображение, которое мы можем использовать для преобразования целых чисел обратно в символы, чтобы мы могли понять предсказания.

int_to_char = dict((i, c) for i, c in enumerate(chars))

Наконец, нам нужно делать прогнозы.

Простейший способ использования модели Keras LSTM для прогнозирования - сначала начать с последовательности начальных чисел в качестве входных данных, сгенерировать следующий символ, затем обновить последовательность начальных чисел, чтобы добавить сгенерированный символ в конце, и обрезать первый символ. Этот процесс повторяется до тех пор, пока мы хотим предсказать новые символы (например, последовательность длиной 1000 символов).

Мы можем выбрать случайный шаблон ввода в качестве нашей начальной последовательности, а затем распечатать сгенерированные символы по мере их генерации.

# pick a random seed
start = numpy.random.randint(0, len(dataX)-1)
pattern = dataX[start]
print "Seed:"
print "\"", ''.join([int_to_char[value] for value in pattern]), "\""
# generate characters
for i in range(1000):
	x = numpy.reshape(pattern, (1, len(pattern), 1))
	x = x / float(n_vocab)
	prediction = model.predict(x, verbose=0)
	index = numpy.argmax(prediction)
	result = int_to_char[index]
	seq_in = [int_to_char[value] for value in pattern]
	sys.stdout.write(result)
	pattern.append(index)
	pattern = pattern[1:len(pattern)]
print "\nDone."

Полный пример кода для генерации текста с использованием загруженной модели LSTM приведен ниже для полноты.

# Load LSTM network and generate text
import sys
import numpy
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import LSTM
from keras.callbacks import ModelCheckpoint
from keras.utils import np_utils
# load ascii text and covert to lowercase
filename = "wonderland.txt"
raw_text = open(filename).read()
raw_text = raw_text.lower()
# create mapping of unique chars to integers, and a reverse mapping
chars = sorted(list(set(raw_text)))
char_to_int = dict((c, i) for i, c in enumerate(chars))
int_to_char = dict((i, c) for i, c in enumerate(chars))
# summarize the loaded data
n_chars = len(raw_text)
n_vocab = len(chars)
print "Total Characters: ", n_chars
print "Total Vocab: ", n_vocab
# prepare the dataset of input to output pairs encoded as integers
seq_length = 100
dataX = []
dataY = []
for i in range(0, n_chars - seq_length, 1):
	seq_in = raw_text[i:i + seq_length]
	seq_out = raw_text[i + seq_length]
	dataX.append([char_to_int[char] for char in seq_in])
	dataY.append(char_to_int[seq_out])
n_patterns = len(dataX)
print "Total Patterns: ", n_patterns
# reshape X to be [samples, time steps, features]
X = numpy.reshape(dataX, (n_patterns, seq_length, 1))
# normalize
X = X / float(n_vocab)
# one hot encode the output variable
y = np_utils.to_categorical(dataY)
# define the LSTM model
model = Sequential()
model.add(LSTM(256, input_shape=(X.shape[1], X.shape[2])))
model.add(Dropout(0.2))
model.add(Dense(y.shape[1], activation='softmax'))
# load the network weights
filename = "weights-improvement-19-1.9435.hdf5"
model.load_weights(filename)
model.compile(loss='categorical_crossentropy', optimizer='adam')
# pick a random seed
start = numpy.random.randint(0, len(dataX)-1)
pattern = dataX[start]
print "Seed:"
print "\"", ''.join([int_to_char[value] for value in pattern]), "\""
# generate characters
for i in range(1000):
	x = numpy.reshape(pattern, (1, len(pattern), 1))
	x = x / float(n_vocab)
	prediction = model.predict(x, verbose=0)
	index = numpy.argmax(prediction)
	result = int_to_char[index]
	seq_in = [int_to_char[value] for value in pattern]
	sys.stdout.write(result)
	pattern.append(index)
	pattern = pattern[1:len(pattern)]
print "\nDone."

При выполнении этого примера сначала выводится выбранное случайное начальное число, а затем каждый символ по мере его создания.

Например, ниже приведены результаты одного запуска этого текстового генератора. Случайное семя было:

be no mistake about it: it was neither more nor less than a pig, and she
felt that it would be quit

Сгенерированный текст со случайным начальным числом (очищенный для представления) был:

be no mistake about it: it was neither more nor less than a pig, and she
felt that it would be quit e aelin that she was a little want oe toiet
ano a grtpersent to the tas a little war th tee the tase oa teettee
the had been tinhgtt a little toiee at the cadl in a long tuiee aedun
thet sheer was a little tare gereen to be a gentle of the tabdit  soenee
the gad  ouw ie the tay a tirt of toiet at the was a little 
anonersen, and thiu had been woite io a lott of tueh a tiie  and taede
bot her aeain  she cere thth the bene tith the tere bane to tee
toaete to tee the harter was a little tire the same oare cade an anl ano
the garee and the was so seat the was a little gareen and the sabdit,
and the white rabbit wese tilel an the caoe and the sabbit se teeteer,
and the white rabbit wese tilel an the cade in a lonk tfne the sabdi
ano aroing to tea the was sf teet whitg the was a little tane oo thete
the sabeit  she was a little tartig to the tar tf tee the tame of the
cagd, and the white rabbit was a little toiee to be anle tite thete ofs
and the tabdit was the wiite rabbit, and

Можно отметить некоторые замечания по поводу сгенерированного текста.

  • Как правило, он соответствует формату строки, наблюдаемому в исходном тексте длиной менее 80 символов перед новой строкой.
  • Символы разделены на словесные группы, и большинство групп представляют собой настоящие английские слова (например, «the», «little» и «was»), но многие этого не делают (например, «lott», «tiie» и «taede»).
  • Некоторые слова в последовательности имеют смысл (например, «и белый кролик«», Но многие этого не делают (например, «Wese Tilel«).

Тот факт, что эта модель книги, основанная на персонажах, дает такой результат, очень впечатляет. Это дает вам представление о возможностях обучения сетей LSTM.

Результаты не идеальны. В следующем разделе мы рассмотрим улучшение качества результатов за счет развития гораздо большей сети LSTM.

Большая LSTM Рекуррентная Нейронная Сеть

Мы получили результаты, но не отличные результаты в предыдущем разделе. Теперь мы можем попытаться улучшить качество сгенерированного текста, создав гораздо большую сеть.

Мы оставим количество блоков памяти равным 256, но добавим второй слой.

model = Sequential()
model.add(LSTM(256, input_shape=(X.shape[1], X.shape[2]), return_sequences=True))
model.add(Dropout(0.2))
model.add(LSTM(256))
model.add(Dropout(0.2))
model.add(Dense(y.shape[1], activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam')

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

filepath="weights-improvement-{epoch:02d}-{loss:.4f}-bigger.hdf5"

Наконец, мы увеличим количество обучающих эпох с 20 до 50 и уменьшим размер пакета со 128 до 64, чтобы дать сети больше возможностей для обновления и обучения.

Полный список кодов представлен ниже для полноты.

# Larger LSTM Network to Generate Text for Alice in Wonderland
import numpy
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import LSTM
from keras.callbacks import ModelCheckpoint
from keras.utils import np_utils
# load ascii text and covert to lowercase
filename = "wonderland.txt"
raw_text = open(filename).read()
raw_text = raw_text.lower()
# create mapping of unique chars to integers
chars = sorted(list(set(raw_text)))
char_to_int = dict((c, i) for i, c in enumerate(chars))
# summarize the loaded data
n_chars = len(raw_text)
n_vocab = len(chars)
print "Total Characters: ", n_chars
print "Total Vocab: ", n_vocab
# prepare the dataset of input to output pairs encoded as integers
seq_length = 100
dataX = []
dataY = []
for i in range(0, n_chars - seq_length, 1):
	seq_in = raw_text[i:i + seq_length]
	seq_out = raw_text[i + seq_length]
	dataX.append([char_to_int[char] for char in seq_in])
	dataY.append(char_to_int[seq_out])
n_patterns = len(dataX)
print "Total Patterns: ", n_patterns
# reshape X to be [samples, time steps, features]
X = numpy.reshape(dataX, (n_patterns, seq_length, 1))
# normalize
X = X / float(n_vocab)
# one hot encode the output variable
y = np_utils.to_categorical(dataY)
# define the LSTM model
model = Sequential()
model.add(LSTM(256, input_shape=(X.shape[1], X.shape[2]), return_sequences=True))
model.add(Dropout(0.2))
model.add(LSTM(256))
model.add(Dropout(0.2))
model.add(Dense(y.shape[1], activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam')
# define the checkpoint
filepath="weights-improvement-{epoch:02d}-{loss:.4f}-bigger.hdf5"
checkpoint = ModelCheckpoint(filepath, monitor='loss', verbose=1, save_best_only=True, mode='min')
callbacks_list = [checkpoint]
# fit the model
model.fit(X, y, epochs=50, batch_size=64, callbacks=callbacks_list)

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

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

weights-improvement-47-1.2219-bigger.hdf5

Достижение потери 1.2219 в эпоху 47.

Как и в предыдущем разделе, мы можем использовать эту лучшую модель из прогона для генерации текста.

Единственное изменение, которое мы должны внести в скрипт генерации текста из предыдущего раздела, заключается в спецификации топологии сети и из какого файла нужно заполнить сетевые веса.

Полный список кодов приведен ниже для полноты.

# Load Larger LSTM network and generate text
import sys
import numpy
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import LSTM
from keras.callbacks import ModelCheckpoint
from keras.utils import np_utils
# load ascii text and covert to lowercase
filename = "wonderland.txt"
raw_text = open(filename).read()
raw_text = raw_text.lower()
# create mapping of unique chars to integers, and a reverse mapping
chars = sorted(list(set(raw_text)))
char_to_int = dict((c, i) for i, c in enumerate(chars))
int_to_char = dict((i, c) for i, c in enumerate(chars))
# summarize the loaded data
n_chars = len(raw_text)
n_vocab = len(chars)
print "Total Characters: ", n_chars
print "Total Vocab: ", n_vocab
# prepare the dataset of input to output pairs encoded as integers
seq_length = 100
dataX = []
dataY = []
for i in range(0, n_chars - seq_length, 1):
	seq_in = raw_text[i:i + seq_length]
	seq_out = raw_text[i + seq_length]
	dataX.append([char_to_int[char] for char in seq_in])
	dataY.append(char_to_int[seq_out])
n_patterns = len(dataX)
print "Total Patterns: ", n_patterns
# reshape X to be [samples, time steps, features]
X = numpy.reshape(dataX, (n_patterns, seq_length, 1))
# normalize
X = X / float(n_vocab)
# one hot encode the output variable
y = np_utils.to_categorical(dataY)
# define the LSTM model
model = Sequential()
model.add(LSTM(256, input_shape=(X.shape[1], X.shape[2]), return_sequences=True))
model.add(Dropout(0.2))
model.add(LSTM(256))
model.add(Dropout(0.2))
model.add(Dense(y.shape[1], activation='softmax'))
# load the network weights
filename = "weights-improvement-47-1.2219-bigger.hdf5"
model.load_weights(filename)
model.compile(loss='categorical_crossentropy', optimizer='adam')
# pick a random seed
start = numpy.random.randint(0, len(dataX)-1)
pattern = dataX[start]
print "Seed:"
print "\"", ''.join([int_to_char[value] for value in pattern]), "\""
# generate characters
for i in range(1000):
	x = numpy.reshape(pattern, (1, len(pattern), 1))
	x = x / float(n_vocab)
	prediction = model.predict(x, verbose=0)
	index = numpy.argmax(prediction)
	result = int_to_char[index]
	seq_in = [int_to_char[value] for value in pattern]
	sys.stdout.write(result)
	pattern.append(index)
	pattern = pattern[1:len(pattern)]
print "\nDone."

Один из примеров запуска этого сценария генерации текста приводит к выводу ниже.

Случайно выбранный начальный текст был:

d herself lying on the bank, with her
head in the lap of her sister, who was gently brushing away s

Сгенерированный текст с семенами (очищенный для представления) был:

herself lying on the bank, with her
head in the lap of her sister, who was gently brushing away
so siee, and she sabbit said to herself and the sabbit said to herself and the sood
way of the was a little that she was a little lad good to the garden,
and the sood of the mock turtle said to herself, 'it was a little that
the mock turtle said to see it said to sea it said to sea it say it
the marge hard sat hn a little that she was so sereated to herself, and
she sabbit said to herself, 'it was a little little shated of the sooe
of the coomouse it was a little lad good to the little gooder head. and
said to herself, 'it was a little little shated of the mouse of the
good of the courte, and it was a little little shated in a little that
the was a little little shated of the thmee said to see it was a little
book of the was a little that she was so sereated to hare a little the
began sitee of the was of the was a little that she was so seally and
the sabbit was a little lad good to the little gooder head of the gad
seared to see it was a little lad good to the little good

Мы видим, что, как правило, ошибок орфографии меньше, и текст выглядит более реалистичным, но все же совершенно бессмысленным.

Например, одни и те же фразы повторяются снова и снова, как «сказала себе" а также "немного«. Котировки открыты, но не закрыты.

Это лучшие результаты, но есть еще много возможностей для улучшения.

10 идей расширения для улучшения модели

Ниже приведены 10 идей, которые могут еще больше улучшить модель, с которой вы можете поэкспериментировать:

  • Прогнозировать менее 1000 символов в качестве вывода для данного семени.
  • Удалите все знаки препинания из исходного текста и, следовательно, из словаря моделей.
  • Попробуйте один горячий код для входных последовательностей.
  • Тренируйте модель на дополненных предложениях, а не на случайных последовательностях символов.
  • Увеличьте количество тренировочных эпох до 100 или многих сотен.
  • Добавьте отсев к видимому входному слою и рассмотрите возможность настройки процента отсева.
  • Настройте размер партии, попробуйте размер партии 1 в качестве (очень медленной) базовой линии и увеличьте размеры оттуда.
  • Добавьте больше блоков памяти к слоям и / или нескольким слоям.
  • Эксперимент с масштабными коэффициентами (температура) при интерпретации вероятностей прогноза.
  • Измените слои LSTM на «сохраняющие состояние», чтобы поддерживать состояние между партиями

Вы пробовали какие-либо из этих расширений? Поделитесь своими результатами в комментариях.

Ресурсы

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

Ниже приведены дополнительные ресурсы и учебные материалы по этой теме, если вы заинтересованы в углублении. Пожалуй, самым популярным является учебник Андрея Карпати под названием «Необоснованная эффективность рекуррентных нейронных сетей«.

Резюме

В этой статье вы узнали, как можно разработать рекуррентную нейронную сеть LSTM для генерации текста в Python с помощью библиотеки глубокого обучения Keras.

После прочтения этого поста вы знаете:

  • Где бесплатно скачать текст ASCII для классических книг, который вы можете использовать для обучения.
  • Как обучить сеть LSTM текстовым последовательностям и как использовать обученную сеть для генерации новых последовательностей.
  • Как разрабатывать стекированные сети LSTM и повышать производительность модели.

У вас есть вопросы о генерации текста в сетях LSTM или об этом посте? Задайте свои вопросы в комментариях ниже, и я сделаю все возможное, чтобы ответить на них.

Оригинальная статья

Footer decor

© www.machinelearningmastery.ru | Ссылки на оригиналы и авторов сохранены. | map