AI-For-Beginners/translations/ru/lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb

492 lines
36 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Генеративные сети\n",
"\n",
"Рекуррентные нейронные сети (RNN) и их варианты с управляемыми ячейками, такие как ячейки долгой краткосрочной памяти (LSTM) и управляемые рекуррентные блоки (GRU), предоставляют механизм для языкового моделирования, то есть они могут обучаться порядку слов и предсказывать следующее слово в последовательности. Это позволяет использовать RNN для **генеративных задач**, таких как обычная генерация текста, машинный перевод и даже создание подписей к изображениям.\n",
"\n",
"В архитектуре RNN, которую мы обсуждали в предыдущем разделе, каждая RNN-ячейка выдавала следующее скрытое состояние в качестве результата. Однако мы также можем добавить еще один выход к каждой рекуррентной ячейке, что позволит нам выдавать **последовательность** (равную по длине исходной последовательности). Более того, мы можем использовать RNN-ячейки, которые не принимают входные данные на каждом шаге, а просто используют некоторый начальный вектор состояния и затем генерируют последовательность выходных данных.\n",
"\n",
"В этом ноутбуке мы сосредоточимся на простых генеративных моделях, которые помогают нам генерировать текст. Для упрощения давайте создадим **символьную сеть**, которая генерирует текст по буквам. Во время обучения нам нужно взять некоторый текстовый корпус и разбить его на последовательности букв.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import tensorflow as tf\n",
"from tensorflow import keras\n",
"import tensorflow_datasets as tfds\n",
"import numpy as np\n",
"\n",
"ds_train, ds_test = tfds.load('ag_news_subset').values()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Создание словаря символов\n",
"\n",
"Чтобы построить генеративную сеть на уровне символов, необходимо разбить текст на отдельные символы, а не на слова. Слой `TextVectorization`, который мы использовали ранее, не может выполнить эту задачу, поэтому у нас есть два варианта:\n",
"\n",
"* Загрузить текст вручную и выполнить токенизацию \"вручную\", как показано в [этом официальном примере Keras](https://keras.io/examples/generative/lstm_character_level_text_generation/)\n",
"* Использовать класс `Tokenizer` для токенизации на уровне символов.\n",
"\n",
"Мы выберем второй вариант. `Tokenizer` также можно использовать для токенизации на уровне слов, поэтому переключение между токенизацией символов и слов должно быть достаточно простым.\n",
"\n",
"Для токенизации на уровне символов необходимо передать параметр `char_level=True`:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"def extract_text(x):\n",
" return x['title']+' '+x['description']\n",
"\n",
"def tupelize(x):\n",
" return (extract_text(x),x['label'])\n",
"\n",
"tokenizer = keras.preprocessing.text.Tokenizer(char_level=True,lower=False)\n",
"tokenizer.fit_on_texts([x['title'].numpy().decode('utf-8') for x in ds_train])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Мы также хотим использовать один специальный токен для обозначения **конца последовательности**, который мы назовем `<eos>`. Давайте добавим его вручную в словарь:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"eos_token = len(tokenizer.word_index)+1\n",
"tokenizer.word_index['<eos>'] = eos_token\n",
"\n",
"vocab_size = eos_token + 1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[[48, 2, 10, 10, 5, 44, 1, 25, 5, 8, 10, 13, 78]]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tokenizer.texts_to_sequences(['Hello, world!'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Обучение генеративной RNN для создания заголовков\n",
"\n",
"Метод, который мы будем использовать для обучения RNN создавать заголовки новостей, следующий. На каждом шаге мы берем один заголовок, который подается в RNN, и для каждого входного символа сеть должна сгенерировать следующий выходной символ:\n",
"\n",
"![Изображение, показывающее пример генерации слова 'HELLO' с помощью RNN.](../../../../../translated_images/ru/rnn-generate.56c54afb52f9781d.webp)\n",
"\n",
"Для последнего символа нашей последовательности мы попросим сеть сгенерировать токен `<eos>`.\n",
"\n",
"Основное отличие генеративной RNN, которую мы используем здесь, заключается в том, что мы будем брать выходные данные с каждого шага RNN, а не только из последней ячейки. Это можно сделать, указав параметр `return_sequences` для ячейки RNN.\n",
"\n",
"Таким образом, во время обучения входом для сети будет последовательность закодированных символов определенной длины, а выходом — последовательность той же длины, но сдвинутая на один элемент и завершенная токеном `<eos>`. Минибатч будет состоять из нескольких таких последовательностей, и нам потребуется использовать **дополнение** (padding), чтобы выровнять все последовательности.\n",
"\n",
"Давайте создадим функции, которые будут преобразовывать набор данных для нас. Поскольку мы хотим дополнять последовательности на уровне минибатча, сначала мы сгруппируем набор данных, вызвав `.batch()`, а затем применим `map`, чтобы выполнить преобразование. Таким образом, функция преобразования будет принимать целый минибатч в качестве параметра:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"def title_batch(x):\n",
" x = [t.numpy().decode('utf-8') for t in x]\n",
" z = tokenizer.texts_to_sequences(x)\n",
" z = tf.keras.preprocessing.sequence.pad_sequences(z)\n",
" return tf.one_hot(z,vocab_size), tf.one_hot(tf.concat([z[:,1:],tf.constant(eos_token,shape=(len(z),1))],axis=1),vocab_size)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Несколько важных моментов, которые мы делаем здесь:\n",
"* Сначала мы извлекаем фактический текст из строкового тензора\n",
"* `text_to_sequences` преобразует список строк в список целочисленных тензоров\n",
"* `pad_sequences` дополняет эти тензоры до их максимальной длины\n",
"* В конце мы выполняем one-hot кодирование всех символов, а также сдвиг и добавление `<eos>`. Скоро мы увидим, почему нам нужны символы в формате one-hot\n",
"\n",
"Однако эта функция является **Pythonic**, то есть она не может быть автоматически преобразована в вычислительный граф Tensorflow. Если мы попытаемся использовать эту функцию напрямую в `Dataset.map`, возникнут ошибки. Нам нужно обернуть этот вызов с помощью оболочки `py_function`:\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def title_batch_fn(x):\n",
" x = x['title']\n",
" a,b = tf.py_function(title_batch,inp=[x],Tout=(tf.float32,tf.float32))\n",
" return a,b"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Note**: Различие между функциями преобразования в стиле Python и Tensorflow может показаться слишком сложным, и вы можете задаться вопросом, почему мы не преобразуем набор данных с помощью стандартных функций Python перед передачей его в `fit`. Хотя это действительно возможно, использование `Dataset.map` имеет огромное преимущество, так как конвейер преобразования данных выполняется с использованием вычислительного графа Tensorflow, который использует преимущества вычислений на GPU и минимизирует необходимость передачи данных между CPU и GPU.\n",
"\n",
"Теперь мы можем построить нашу генеративную сеть и начать обучение. Она может быть основана на любой рекуррентной ячейке, которую мы обсуждали в предыдущем разделе (простой, LSTM или GRU). В нашем примере мы будем использовать LSTM.\n",
"\n",
"Поскольку сеть принимает символы в качестве входных данных, а размер словаря довольно мал, нам не нужен слой embedding — входные данные в формате one-hot могут напрямую подаваться в ячейку LSTM. Выходной слой будет классификатором `Dense`, который преобразует выход LSTM в номера токенов в формате one-hot.\n",
"\n",
"Кроме того, так как мы работаем с последовательностями переменной длины, мы можем использовать слой `Masking`, чтобы создать маску, которая будет игнорировать дополненные части строки. Это не является строго необходимым, так как нас не слишком интересует всё, что идёт после токена `<eos>`, но мы будем использовать этот слой для получения опыта работы с ним. `input_shape` будет иметь вид `(None, vocab_size)`, где `None` указывает на последовательность переменной длины, а выходная форма также будет `(None, vocab_size)`, как вы можете увидеть из `summary`:\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"sequential\"\n",
"_________________________________________________________________\n",
"Layer (type) Output Shape Param # \n",
"=================================================================\n",
"masking (Masking) (None, None, 84) 0 \n",
"_________________________________________________________________\n",
"lstm (LSTM) (None, None, 128) 109056 \n",
"_________________________________________________________________\n",
"dense (Dense) (None, None, 84) 10836 \n",
"=================================================================\n",
"Total params: 119,892\n",
"Trainable params: 119,892\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n",
"15000/15000 [==============================] - 229s 15ms/step - loss: 1.5385\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7fa40c1245e0>"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = keras.models.Sequential([\n",
" keras.layers.Masking(input_shape=(None,vocab_size)),\n",
" keras.layers.LSTM(128,return_sequences=True),\n",
" keras.layers.Dense(vocab_size,activation='softmax')\n",
"])\n",
"\n",
"model.summary()\n",
"model.compile(loss='categorical_crossentropy')\n",
"\n",
"model.fit(ds_train.batch(8).map(title_batch_fn))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Генерация вывода\n",
"\n",
"Теперь, когда мы обучили модель, мы хотим использовать её для генерации вывода. Прежде всего, нам нужен способ декодировать текст, представленный последовательностью чисел токенов. Для этого мы могли бы использовать функцию `tokenizer.sequences_to_texts`; однако она плохо работает с токенизацией на уровне символов. Поэтому мы возьмём словарь токенов из токенизатора (называемый `word_index`), создадим обратную карту и напишем собственную функцию декодирования:\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"reverse_map = {val:key for key, val in tokenizer.word_index.items()}\n",
"\n",
"def decode(x):\n",
" return ''.join([reverse_map[t] for t in x])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Теперь начнем генерацию. Мы начнем с некоторой строки `start`, закодируем ее в последовательность `inp`, а затем на каждом шаге будем вызывать нашу сеть для предсказания следующего символа.\n",
"\n",
"Выход сети `out` — это вектор из `vocab_size` элементов, представляющий вероятности каждого токена, и мы можем найти наиболее вероятный номер токена, используя `argmax`. Затем мы добавляем этот символ в сгенерированный список токенов и продолжаем генерацию. Этот процесс генерации одного символа повторяется `size` раз для создания необходимого количества символов, и мы завершаем процесс досрочно, если встречается `eos_token`.\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'Today #39;s lead to strike for the strike for the strike for the strike (AFP)'"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def generate(model,size=100,start='Today '):\n",
" inp = tokenizer.texts_to_sequences([start])[0]\n",
" chars = inp\n",
" for i in range(size):\n",
" out = model(tf.expand_dims(tf.one_hot(inp,vocab_size),0))[0][-1]\n",
" nc = tf.argmax(out)\n",
" if nc==eos_token:\n",
" break\n",
" chars.append(nc.numpy())\n",
" inp = inp+[nc]\n",
" return decode(chars)\n",
" \n",
"generate(model)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Выборка результатов во время обучения\n",
"\n",
"Поскольку у нас нет полезных метрик, таких как *точность*, единственный способ увидеть, что наша модель становится лучше — это **выборка** сгенерированных строк во время обучения. Для этого мы будем использовать **колбэки**, то есть функции, которые мы можем передать в функцию `fit`, и которые будут вызываться периодически в процессе обучения.\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch 1/3\n",
"15000/15000 [==============================] - 226s 15ms/step - loss: 1.2703\n",
"Today #39;s a lead in the company for the strike\n",
"Epoch 2/3\n",
"15000/15000 [==============================] - 227s 15ms/step - loss: 1.2057\n",
"Today #39;s the Market Service on Security Start (AP)\n",
"Epoch 3/3\n",
"15000/15000 [==============================] - 226s 15ms/step - loss: 1.1752\n",
"Today #39;s a line on the strike to start for the start\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7fa40c74e3d0>"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sampling_callback = keras.callbacks.LambdaCallback(\n",
" on_epoch_end = lambda batch, logs: print(generate(model))\n",
")\n",
"\n",
"model.fit(ds_train.batch(8).map(title_batch_fn),callbacks=[sampling_callback],epochs=3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Этот пример уже генерирует довольно хороший текст, но его можно улучшить несколькими способами:\n",
"* **Больше текста**. Мы использовали только заголовки для нашей задачи, но вы можете попробовать работать с полным текстом. Помните, что RNN не слишком хорошо справляются с обработкой длинных последовательностей, поэтому имеет смысл либо разбивать их на более короткие предложения, либо всегда обучать на фиксированной длине последовательности с заранее заданным значением `num_chars` (например, 256). Вы можете попробовать изменить пример выше на такую архитектуру, используя [официальный учебник Keras](https://keras.io/examples/generative/lstm_character_level_text_generation/) в качестве вдохновения.\n",
"* **Многослойный LSTM**. Имеет смысл попробовать 2 или 3 слоя ячеек LSTM. Как мы упоминали в предыдущем разделе, каждый слой LSTM извлекает определенные шаблоны из текста, и в случае генератора на уровне символов можно ожидать, что нижний уровень LSTM будет отвечать за извлечение слогов, а более высокие уровни — за слова и их комбинации. Это можно легко реализовать, передав параметр количества слоев в конструктор LSTM.\n",
"* Вы также можете попробовать экспериментировать с **GRU-ячейками** и посмотреть, какие из них работают лучше, а также с **различными размерами скрытых слоев**. Слишком большой скрытый слой может привести к переобучению (например, сеть будет запоминать точный текст), а меньший размер может не дать хорошего результата.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Генерация мягкого текста и температура\n",
"\n",
"В предыдущем определении функции `generate` мы всегда выбирали символ с наибольшей вероятностью в качестве следующего символа в генерируемом тексте. Это приводило к тому, что текст часто \"зацикливался\" на одних и тех же последовательностях символов снова и снова, как в этом примере:\n",
"```\n",
"today of the second the company and a second the company ...\n",
"```\n",
"\n",
"Однако, если мы посмотрим на распределение вероятностей для следующего символа, может оказаться, что разница между несколькими наивысшими вероятностями незначительна, например, один символ может иметь вероятность 0.2, а другой — 0.19 и т.д. Например, при поиске следующего символа в последовательности '*play*', следующим символом может быть как пробел, так и **e** (как в слове *player*).\n",
"\n",
"Это приводит нас к выводу, что не всегда \"справедливо\" выбирать символ с наивысшей вероятностью, так как выбор второго по величине вероятности также может привести к осмысленному тексту. Более разумным подходом будет **выбор** символов из распределения вероятностей, предоставленного выходом сети.\n",
"\n",
"Этот выбор можно осуществить с помощью функции `np.multinomial`, которая реализует так называемое **многомерное распределение**. Функция, реализующая эту **мягкую** генерацию текста, определена ниже:\n"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"--- Temperature = 0.3\n",
"Today #39;s strike #39; to start at the store return\n",
"On Sunday PO to Be Data Profit Up (Reuters)\n",
"Moscow, SP wins straight to the Microsoft #39;s control of the space start\n",
"President olding of the blast start for the strike to pay &lt;b&gt;...&lt;/b&gt;\n",
"Little red riding hood ficed to the spam countered in European &lt;b&gt;...&lt;/b&gt;\n",
"\n",
"--- Temperature = 0.8\n",
"Today countie strikes ryder missile faces food market blut\n",
"On Sunday collores lose-toppy of sale of Bullment in &lt;b&gt;...&lt;/b&gt;\n",
"Moscow, IBM Diffeiting in Afghan Software Hotels (Reuters)\n",
"President Ol Luster for Profit Peaced Raised (AP)\n",
"Little red riding hood dace on depart talks #39; bank up\n",
"\n",
"--- Temperature = 1.0\n",
"Today wits House buiting debate fixes #39; supervice stake again\n",
"On Sunday arling digital poaching In for level\n",
"Moscow, DS Up 7, Top Proble Protest Caprey Mamarian Strike\n",
"President teps help of roubler stepted lessabul-Dhalitics (AFP)\n",
"Little red riding hood signs on cash in Carter-youb\n",
"\n",
"--- Temperature = 1.3\n",
"Today wits flawer ro, pSIA figat's co DroftwavesIs Talo up\n",
"On Sunday hround elitwing wint EU Powerburlinetien\n",
"Moscow, Bazz #39;s sentries olymen winnelds' next for Olympite Huc?\n",
"President lost securitys from power Elections in Smiltrials\n",
"Little red riding hood vides profit, exponituity, profitmainalist-at said listers\n",
"\n",
"--- Temperature = 1.8\n",
"Today #39;It: He deat: N.KA Asside\n",
"On Sunday i arry Par aldeup patient Wo stele1\n"
]
},
{
"ename": "KeyError",
"evalue": "0",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-33-db32367a0feb>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"\\n--- Temperature = {i}\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mj\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 20\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgenerate_soft\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0msize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m300\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mstart\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwords\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mj\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtemperature\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m<ipython-input-33-db32367a0feb>\u001b[0m in \u001b[0;36mgenerate_soft\u001b[0;34m(model, size, start, temperature)\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0mchars\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnc\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0minp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0minp\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mnc\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 13\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchars\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 14\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0mwords\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m'Today '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'On Sunday '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'Moscow, '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'President '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'Little red riding hood '\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m<ipython-input-10-3f5fa6130b1d>\u001b[0m in \u001b[0;36mdecode\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m''\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mreverse_map\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m<ipython-input-10-3f5fa6130b1d>\u001b[0m in \u001b[0;36m<listcomp>\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m''\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mreverse_map\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mKeyError\u001b[0m: 0"
]
}
],
"source": [
"def generate_soft(model,size=100,start='Today ',temperature=1.0):\n",
" inp = tokenizer.texts_to_sequences([start])[0]\n",
" chars = inp\n",
" for i in range(size):\n",
" out = model(tf.expand_dims(tf.one_hot(inp,vocab_size),0))[0][-1]\n",
" probs = tf.exp(tf.math.log(out)/temperature).numpy().astype(np.float64)\n",
" probs = probs/np.sum(probs)\n",
" nc = np.argmax(np.random.multinomial(1,probs,1))\n",
" if nc==eos_token:\n",
" break\n",
" chars.append(nc)\n",
" inp = inp+[nc]\n",
" return decode(chars)\n",
"\n",
"words = ['Today ','On Sunday ','Moscow, ','President ','Little red riding hood ']\n",
" \n",
"for i in [0.3,0.8,1.0,1.3,1.8]:\n",
" print(f\"\\n--- Temperature = {i}\")\n",
" for j in range(5):\n",
" print(generate_soft(model,size=300,start=words[j],temperature=i))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Мы ввели еще один параметр, называемый **temperature**, который используется для указания того, насколько строго мы должны придерживаться наивысшей вероятности. Если temperature равно 1.0, мы выполняем честное мультиномиальное выборочное моделирование, а когда temperature стремится к бесконечности, все вероятности становятся равными, и мы случайным образом выбираем следующий символ. В приведенном ниже примере можно наблюдать, что текст становится бессмысленным, если слишком сильно увеличить значение temperature, и он напоминает \"циклически\" жестко сгенерированный текст, когда значение приближается к 0.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от ответственности**: \nЭтот документ был переведен с использованием сервиса автоматического перевода [Co-op Translator](https://github.com/Azure/co-op-translator). Несмотря на наши усилия обеспечить точность, автоматические переводы могут содержать ошибки или неточности. Оригинальный документ на его родном языке следует считать авторитетным источником. Для получения критически важной информации рекомендуется профессиональный перевод человеком. Мы не несем ответственности за любые недоразумения или неправильные интерпретации, возникающие в результате использования данного перевода.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('py38')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"coopTranslator": {
"original_hash": "9fbb7d5fda708537649f71f5f646fcde",
"translation_date": "2025-08-28T12:00:00+00:00",
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb",
"language_code": "ru"
}
},
"nbformat": 4,
"nbformat_minor": 4
}