Учим нейронку писать как Фёдор Михайлович Достоевский

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

Приведенный здесь результат работы получен за примерно 1 час обучения на ПК с видеокартой. Результат можно улучшить, увеличив время обучения или размер сети (её глубину или ширину слоев).

Итак, приступим!

Писать нейросеть мы будем на python, сейчас это фактически основной язык для Data Scientist. Использовать будем популярный фреймворк Keras, который позволяет очень просто описывать структуру нейронной сети и абстрагироваться от деталей её реализации. Keras внутри себя может использовать для вычислений библиотеки Tensorflow от Google или Theano. В нашем случае это будет Tensorflow. Библиотека поддерживает расчеты на GPU, так что мощная видеокарта от NVidia может ощутимо сократить время работы.

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

Ввод:

 import tensorflow as tf
 from keras import backend as K
 config = tf.ConfigProto()
 config.gpu_options.allow_growth=True
 sess = tf.Session(config=config)
 K.set_session(sess) # разрешаем использовать больше видеопамяти
 from keras.models import Sequential
 from keras.layers import Dense, Activation, Dropout, LSTM,TimeDistributed, Embedding
 from keras.callbacks import ModelCheckpoint, Callback, EarlyStopping, ReduceLROnPlateau
 from keras.optimizers import RMSprop, Adam, SGD
 import sys
 import numpy as np

Using TensorFlow backend.

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

Ввод:

fname = 'dostoevsky.txt' #тексты основных романов Федора Михайловича, объединенные в один файл
text = open(fname, 'r', encoding='utf-8').read()
print('corpus length:', len(text))
Вывод
corpus length: 9469628

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

  1. Число уникальных букв
  2. Число уникальных пар букв (биграмм)
  3. Число уникальных троек букв (триграмм)
  4. Общее число слов и число уникальных слов

Для простоты дальше мы будем обучать нашу модель только на отдельных буквах.

Ввод:

chars = sorted(list(set(text)))
print('unique chars:', len(chars))
char_idx = dict((c, i) for i, c in enumerate(chars))
idx_char = dict((i, c) for i, c in enumerate(chars))

bigrams = []
for i in range(0, len(text)-1, 1):
   bigrams.append(text[i] + text[i+1])
bigrams = sorted(list(set(bigrams)))
print('unique bigrams:', len(bigrams))
bigram_idx = dict((c, i) for i, c in enumerate(bigrams))
idx_bigram = dict((i, c) for i, c in enumerate(bigrams))

trigrams = []
for i in range(0, len(text)-2, 1):
   trigrams.append(text[i] + text[i+1] + text[i+2])
trigrams = sorted(list(set(trigrams)))
print('unique trigrams:', len(trigrams))
trigram_idx = dict((c, i) for i, c in enumerate(trigrams))
idx_trigram = dict((i, c) for i, c in enumerate(trigrams))

words = text.split()
print('word count:', len(words))
words = sorted(list(set(words)))
print('unique words:', len(words))
Вывод
unique chars: 162

unique bigrams: 3566 

unique trigrams: 26082 

word count: 1528727 

unique words: 165930
Обучать сеть будем мини-батчами. Все элементы батча могут быть обработаны параллельно, особенно если речь идёт об обработке на GPU. Это значительно ускоряет процесс обучения по сравнению с подачей сети отдельных примеров. Для этого поделим весь наш текст на несколько равных последовательностей, число их будет равно числу батчей обучения. Как это делается, на примере батча размером 3 — в первый трек попадет текст от начала и до трети всей длины, во второй трек текст от 1/3 до 2/3 длины, а в третий — от 2/3 и до конца текста.

Ввод:

batch_size = 1024 # можно уменьшить, если при запуску возникает ошибка аллокации памяти
track_size = len(text) // batch_size
tracks = ['' for i in range(batch_size)]
for i in range(0, track_size):
   for track in range(batch_size):
       tracks[track] += text[track * track_size + i]

#  посмотрим что у нас получилось внутри отдельноно трека
print(tracks[4][:1000])
Вывод
только, что сын его, воспитывавшийся сначала у графа, а потом в лицее, окончил тогда курс наук девятнадцати лет от роду. Я написал об этом к Ихменевым, а также и о том, что князь очень любит своего сына, балует его, рассчитывает уже и теперь его будущность. Всё это я узнал от товарищей-студентов, знакомых молодому князю. В это-то время Николай Сергеич в одно прекрасное утро получил от князя письмо, чрезвычайно его удивившее… Князь, который до сих пор, как уже упомянул я, ограничивался в сношениях с Николаем Сергеичем одной сухой, деловой перепиской, писал к нему теперь самым подробным, откровенным и дружеским образом о своих семейных обстоятельствах: он жаловался на своего сына, писал, что сын огорчает его дурным своим поведением; что, конечно, на шалости такого мальчика нельзя еще смотреть слишком серьезно (он видимо старался оправдать его), но что он решился наказать сына, попугать его, а именно: сослать ого на некоторое время в деревню, под присмотр Ихменева. Князь писал, что

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

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

Ввод:

gram_idx = [char_idx, bigram_idx, trigram_idx] 
idx_gram = [idx_char, idx_bigram, idx_trigram]

# конвертируем блоки текста в наборы индексов символов
def grams(tracks, n=1):
   indexed = []
   for t in tracks:  
       track = []
       for i in range(0, len(t)-n+1, n):
           gram = ''
           for j in range(n):
               gram += t[i+j] # склеиваем каждые n символов в последовательность, получая символ, биграмму или триграмму в зависимости от n
           idx = gram_idx[n-1][gram] # ищем индекс последовательности в соответствующем словаре
           track.append(idx) # добавляем этот индекс в результирующий список
       indexed.append(track)
   return indexed

indexed = grams(tracks, 1)
vocab_size = len(gram_idx[0])
print('Vocabulary done: ', vocab_size)
Вывод
Vocabulary done:  162

 

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

Наша сеть будет предсказывать следующую букву для последовательности. Пример с фразой HELLO:

  • мы начинаем подавать в сеть буквы одну за другой с начала. Одновременно на каждом шаге мы проверяем, что на выходе сети появилась следующая буква из этого слова.
  • вначале подаем на вход H, на выходе ждем E
  • на следующем шаге подаем E, ждём на выходе L
  • подаем L, ждём L
  • подаем L, ждём O.

Всё это время сеть сохраняет своё состояние с самого начала, что позволяет ей запомнить, в какой ситуации после L надо ответить L, а в какой O.

Для предсказания индекса буквы нам необходимо преобразовать его в one-hot вектор — длина которого равна размеру нашего словаря, а во всех позициях кроме позиции с номером текущего индекса стоят нули. В позиции индекса же будет стоять единица. Например, для словаря из [E, H, L, O] длина словаря была бы 4, индекс буквы L — 3, а её one-hot вектор 0010.

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

Ввод:

seq_len = 40 # длина последовательности в батче

def onehot(n):
   v = [0 for i in range(vocab_size)]
   v[n] = 1
   return v

# расставляем последовательности из блоков таким образом, что первая последовательность из следующего батча продолжает первую последовательность текущего. Чтобы слой LSTM мог использовать состояние из предыдущего батча
def vectorize(tracks):
   track_size = len(tracks[0])
   X = []
   y = []
   for i in range(0, track_size - seq_len + 1, seq_len):
       for t in tracks:
           X.append(t[i:i + seq_len])
           target = [onehot(c) for c in t[i+1:i + seq_len + 1]]
           y.append(target)
   return X, y
       
x,y = vectorize(indexed)
print('Number of training samples', len(x))
print('Number of training labels', len(y))
print('Label sequence length', len(y[0]))
print('Label character one-hot vector length', len(y[0][0]))
Вывод
Number of training samples 236544
Number of training labels 236544
Label sequence length 40
Label character one-hot vector length 162

 

Приготовим нашу рекуррентную сеть. В начале идёт embedding-слой, преобразующий индексы входной последовательности в dense вектор фиксированного размера. Затем несколько рекуррентных LSTM слоёв и один полносвязный слой с число выходов, равным размеру нашего словаря. В качестве loss-функции нам нужна кросс энтропия, в качестве оптимизатора будем использовать популярный сейчас алгоритм Adam, также в нём важно не забыть установить clipnorm — ограничение на размер градиентов.

Ввод:

cells = 512
drop = 0.2
embed = 30 # размер вектора символа (эмбеддинга)
layers = 2
lr = 0.01
clip = 5.0 # ограничение градиентов при оптимизации
stateful=True # сохраняем состояние слоя между батчами

model = Sequential()
model.add(Embedding(vocab_size, embed, batch_input_shape=(batch_size, seq_len)))
for l in range(layers):
   model.add(LSTM(cells, return_sequences=True, stateful=stateful, dropout=drop))
model.add(Dense(vocab_size))
model.add(Activation('softmax'))
optimizer = Adam(lr, clipnorm=clip)
model.compile(loss="categorical_crossentropy", optimizer=optimizer)
model.summary()
Вывод
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_1 (Embedding)      (1024, 40, 30)            4860      
_________________________________________________________________
lstm_1 (LSTM)                (1024, 40, 512)           1112064   
_________________________________________________________________
lstm_2 (LSTM)                (1024, 40, 512)           2099200   
_________________________________________________________________
dense_1 (Dense)              (1024, 40, 162)           83106     
_________________________________________________________________
activation_1 (Activation)    (1024, 40, 162)           0         
=================================================================
Total params: 3,299,230
Trainable params: 3,299,230
Non-trainable params: 0
_________________________________________________________________

Как видно, размер сети получился порядка 3.3 миллиона параметров.

Ввод:

def get_callbacks(filepath, patience=5):
   learning_rate_reduction = ReduceLROnPlateau(monitor='loss', 
                                           patience=patience, 
                                           verbose=1, 
                                           factor=0.5, 
                                           min_lr=0.00001)
   es = EarlyStopping('loss', verbose=1, min_delta=0.02, patience=patience, mode="min")
   return [learning_rate_reduction, es]

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

Ввод:

def sample(preds, temperature=0.5):
   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)

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

def predictive_model(main_model):

   # меняем размер батча на 1, чтобы подавать всего один блок текста
   model = Sequential()
   model.add(Embedding(vocab_size, embed, batch_input_shape=(1, seq_len)))
   for l in range(layers):
       model.add(LSTM(cells, return_sequences=True, stateful=stateful, dropout=drop))
   model.add(Dense(vocab_size))
   model.add(Activation('softmax'))
   optimizer = Adam(lr, clipnorm=clip)
   model.compile(loss="categorical_crossentropy", optimizer=optimizer)
   old_weights = main_model.get_weights()
   model.set_weights(old_weights)
   return model

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

# генерируем новый символ по имеющимся с помощью нашей модели, добавляем его к строке и опять генерируем уже по этой дополненной строке
def test(model, l=500, seed = None, t=0.5):
   start_from = np.random.randint(len(text)-seq_len)+seq_len
   seed_string = text[start_from:start_from + seq_len*3] if seed is None else seed
   print('\n\nSeed:  ', seed_string)
   print('----')
   sys.stdout.write(seed_string)
   prmodel = predictive_model(model)
   for i in range(l):
       prmodel.reset_states()
       padlen = (len(seed_string) // seq_len +1) * seq_len
       seed_string = seed_string.rjust(padlen)[-seq_len*3:]
       test_tracks = [seed_string]
       tidx = grams(test_tracks)
       xt, _ = vectorize(tidx)
       preds = prmodel.predict(np.array(xt), batch_size=1, verbose=0)
       preds = preds[-1][-1] # last symbol of last sequence
       next_item = idx_char[sample(preds, t)]
       seed_string = seed_string + next_item
       sys.stdout.write(next_item)
       sys.stdout.flush()

Пришло время обучить сеть. Попробуем делать это на протяжении 50 итераций, периодически (раз в 3 итерации) оценивая качество сгенерированного сетью текста

 

Ввод:

for iteration in range(1, 51):
   print('\nIteration', iteration)
   model_name = 'char_%s_%d_%d_%.1f_%d.h5' % (fname, layers, cells, drop, iteration)
   history=model.fit(
       np.array(x), np.array(y), 
       batch_size=batch_size, 
       epochs=1, 
       verbose=1, 
       shuffle=False,
       callbacks=get_callbacks(filepath=model_name)
   )
   model.save_weights(model_name, overwrite=True)
   model.reset_states()
   if iteration%3 == 0:
       test(model)
Вывод
Iteration 1 Epoch 1/1 236544/236544 [==============================] — 196s — loss: 3.3098

Iteration 2 Epoch 1/1 236544/236544 [==============================] — 188s — loss: 3.1676

Iteration 3 Epoch 1/1 236544/236544 [==============================] — 191s — loss: 2.5591    >>>

икальным объяснением, несмотря на то что дело плевое; я знаю его еще с Петербурга. К тому же весь анекдот делает только —- икальным объяснением, несмотря на то что дело плевое; я знаю его еще с Петербурга. К тому же весь анекдот делает только на не воглану и вот двадь не сопривила не на прокоть пробовореться на солать не могда уго всего же самовеле дервать,  потодал что старет придорил отанила спа не прогость же того и не сот отводил все ста стастве. Прегавове,  скоронить е всё логда на с нимененно послей на проемала не не стольто вы мне мни сам, кня на старате телова пословаеть так на на смество миже все преседь слуго все в неста не сами призчите вот, не на тогда не закорорно как трязь потисту сопросенить лестольно из семи его на с

Iteration 4 Epoch 1/1 236544/236544 [==============================] — 186s — loss: 2.0483   

Iteration 5 Epoch 1/1 236544/236544 [==============================] — 190s — loss: 1.8013   

Iteration 6 Epoch 1/1 236544/236544 [==============================] — 190s — loss: 1.6861    >>>

вдруг неожиданно.   — Как тем хуже?   — Хуже.   — Не понимаю.   — Друг мой, друг мой, ну пусть в Сибирь, в Архангел —- вдруг неожиданно.   — Как тем хуже?   — Хуже.   — Не понимаю.   — Друг мой, друг мой, ну пусть в Сибирь, в Архангела Ивановна. То не по своего подсудим на дворе, что так, все все хоть положения и все это было вопросы всё просто под восторгами на него на вами спросил в говорить и в этом вашим с длинным себя. Вы вы ответил в демовым столько не могла какого не верите служал же довольно под всех совершенно закричала, что только было вы в комнате в нем до всеми продолжал и вдруг продолжал было не было образом деле не почему-то с первого не знал, вот тогда уж не знаю, точно так ли сильно в ней положительно всей не

Iteration 7 Epoch 1/1 236544/236544 [==============================] — 188s — loss: 1.6235   

Iteration 8 Epoch 1/1 236544/236544 [==============================] — 188s — loss: 1.5817   

Iteration 9 Epoch 1/1 236544/236544 [==============================] — 189s — loss: 1.5523    >>>

в монастырские ворота пешком. Кроме Федора Павловича, остальные трое кажется никогда не видали никакого монастыря, а Миу —- в монастырские ворота пешком. Кроме Федора Павловича, остальные трое кажется никогда не видали никакого монастыря, а Миусья от дверь обратил весь не полицей, слышал и с тобой на своей нетерпением про себя навсегда в самом долго и получил на того, которого же со столу просто под воротились к деревне, то есть до сих в креста, но не в полу примерника и не потому что тотчас же было не мог подле после него с неестественное все это самовольно и во всем доме проговорил с нелепый и и ответила в каком-то любопытством своего например, что всё равно не только и от после последней стола. Вот по свою для него последнее сторон

Iteration 10 Epoch 1/1 236544/236544 [==============================] — 190s — loss: 1.5294   

Iteration 11 Epoch 1/1 236544/236544 [==============================] — 193s — loss: 1.5108  

Iteration 12 Epoch 1/1 236544/236544 [==============================] — 175s — loss: 1.4962    >>>

чал и долго обдумывал.   — Штука в том: я задал себе один раз такой вопрос: что если бы, например, на моем месте случи —- чал и долго обдумывал.   — Штука в том: я задал себе один раз такой вопрос: что если бы, например, на моем месте случилось и даже до сих пор не было уже, чтобы принял его на «собственных сил за красного стороны, по приходить явился на покойным волнением уверена нахмурился и что он все до рассказывался к нему на полным положении. Пойдемте, она заплачения и не мог от всех пред том и уже не знает и может, с негодованиями человеком, по крайней больше и открылась от меня и умел в болезни послали. Он не простить на меня в однем доме подумали его. Если бы он в том, что вы из него он был в горячее и подле обойхнулся в

Iteration 13 Epoch 1/1 236544/236544 [==============================] — 133s — loss: 1.4842  

Iteration 14 Epoch 1/1 236544/236544 [==============================] — 133s — loss: 1.4732

Iteration 15 Epoch 1/1 236544/236544 [==============================] — 132s — loss: 1.4645    >>>

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

Iteration 16 Epoch 1/1 236544/236544 [==============================] — 131s — loss: 1.4558  

Iteration 17 Epoch 1/1 236544/236544 [==============================] — 132s — loss: 1.4484  

Iteration 18 Epoch 1/1 236544/236544 [==============================] — 132s — loss: 1.4423    >>>

мне было почему-то ужасно совестно заговаривать о Полине; он же сам ни слова о ней не спросил. Я рассказал ему про бабу —- мне было почему-то ужасно совестно заговаривать о Полине; он же сам ни слова о ней не спросил. Я рассказал ему про бабушкой и познакомился образованным под всего последнею голову со голову. Но не понял гостя смотрела на него и про то не обернулся и между тем и больше настоящий и все равно обратилась на два доктор и на положения и не получил на него увидеть на этот раз ответил и должно быть, потому что в этот раз так сказать, и уж не прошептал бы не понять, что она всегда все спокойно прокурор вздрагал за своей руку. Он обойтись, как бы сказать, и он пристально поступила на меня наконец, подхватив перепалительно

Iteration 19 Epoch 1/1 236544/236544 [==============================] — 132s — loss: 1.4355

Iteration 20 Epoch 1/1 236544/236544 [==============================] — 133s — loss: 1.4321

Iteration 21 Epoch 1/1 236544/236544 [==============================] — 134s — loss: 1.4265    >>>

ра кончить!   — Объяснитесь, Наталья Николаевна, — подхватил князь, — убедительно прошу вас! Я уже два часа слышу об —- ра кончить!   — Объяснитесь, Наталья Николаевна, — подхватил князь, — убедительно прошу вас! Я уже два часа слышу обо всем даже теперь в этом обществе и слушать, что он совсем не знал «на нее». Но все могу от него была в том, что назад на него с первого взгляда по крайней мере всегда так всегда подлецой, что не подробно стал в таком половину и с нею ни за что и не сказал его с тем и стало быть все время прошло подумал, что только что она была только хоть и положительно стала допустить глазами, — подумал он вдруг он мелькнула и ответила в своей месте в беспокойстве в дела. Поверьте, что и может быть, не думаю

Iteration 22 Epoch 1/1 236544/236544 [==============================] — 132s — loss: 1.4226

Iteration 23 Epoch 1/1 236544/236544 [==============================] — 131s — loss: 1.4182

Iteration 24 Epoch 1/1 236544/236544 [==============================] — 129s — loss: 1.4148    >>>

ила Авдотья Романовна.   — Вы думаете, — с жаром продолжала Пульхерия Александровна, — его бы остановили тогда мои с —- ила Авдотья Романовна.   — Вы думаете, — с жаром продолжала Пульхерия Александровна, — его бы остановили тогда мои старухи не брать предложение просто подумал, что по крайней мере в моем деле и с тем на него гордости в другой раз, что он не потому в безородном отца в первый раз был не подумали. Пусть на меня показалось в недоумении на ней и вышел к нему. Потому что так на него надо было не надо было сказать на делам и подсудимого и в подлец свои лет в половину в то же моей себе его с первого последнее пользу на меня в показать себя с свою неужели и в нем повторять общество. Он понял теперь в глаза от двух под

Iteration 25 Epoch 1/1 236544/236544 [==============================] — 133s — loss: 1.4118   

Iteration 26 Epoch 1/1 236544/236544 [==============================] — 178s — loss: 1.4093   

Iteration 27 Epoch 1/1 236544/236544 [==============================] — 135s — loss: 1.4065   >>>

рены, благодушнейший, искреннейший и благороднейший князь, — вскричал Лебедев в решительном вдохновении, — будьте увер —- рены, благодушнейший, искреннейший и благороднейший князь, — вскричал Лебедев в решительном вдохновении, — будьте уверяли ваше минуты, — как тот с нею моим добрым с лестнице. И вот я не обращаюсь, что я ведь не отвечал и пристально поступил и понял, что я только уж как я не стал все и так по лежать на восторге в городе и по крайней мере даже не со мной. Но он ведь только не имел в таком случае и при этом странной стороны, но вы его даже не поставить. А он потому что я еще не понимаешь, что я как бы не знаю, что он в нем долгими с тобой в перед нею и не видал его в руках и последних старуха и все это удивлялись

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

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

Ввод:

seed = "У нас в Малом зале до сих пор проходят"
test(model, 300, seed, 0.1)
test(model, 300, seed, 0.5)
test(model, 300, seed, 0.7)
Вывод
Seed:   У нас в Малом зале до сих пор проходят — У нас в Малом зале до сих пор проходят с ним с тобой и подозревали и подозревали и при всех столь и под конец просто подозрения и просто подозревали и при своей стороны и принять своего простодушного старика и подозревали и просто подозревали и подозревали и под конец в своем положении и принять в себе старика, но в том, что он всегда п

Seed:   У нас в Малом зале до сих пор проходят — У нас в Малом зале до сих пор проходят на свою разом и не после прежнего правда с тревожностью, выразился и смотрел на положении. Но просто отвечала она принять меня и сказал его и пред нею, как бы во всех своего проклятий принес в картину и стал простодушное ветром и воздух не понимаю, что не давать предстоящего и судорога. Она убил с

Seed:   У нас в Малом зале до сих пор проходят — У нас в Малом зале до сих пор проходят картины. Теперь сейчас они остановившись пошле дорогу совсем и почти неожиданное  своего  затрастно   на  собой  в  том,  чтобы  не  сказали его ответ к нему прочтить виде и деньги в руках голова  —  учительно  и  старику с каким-то полячной столы погросить, но на него. — Всё больше-то и распоря

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

 

Автор: Олег Шляжко

Ссылка на github: http://github.com/ollmer/neural_experiments/blob/master/char_rnn_dostoevsky.ipynb

 

Загрузка ...
The Robot
Adblock
detector