495 lines
35 KiB
Plaintext
495 lines
35 KiB
Plaintext
{
|
||
"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",
|
||
"\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`. Нам потрібно обгорнути цей виклик Pythonic, використовуючи обгортку `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": [
|
||
"> **Примітка**: Розрізнення між функціями перетворення Pythonic і Tensorflow може здатися занадто складним, і ви можете запитати, чому ми не трансформуємо набір даних за допомогою стандартних функцій Python перед передачею його в `fit`. Хоча це, безумовно, можливо, використання `Dataset.map` має величезну перевагу, оскільки конвеєр перетворення даних виконується за допомогою обчислювального графа Tensorflow, який використовує переваги обчислень на GPU і мінімізує необхідність передачі даних між CPU/GPU.\n",
|
||
"\n",
|
||
"Тепер ми можемо побудувати нашу генеративну мережу і розпочати навчання. Вона може базуватися на будь-якій рекурентній комірці, яку ми обговорювали в попередньому розділі (простій, LSTM або GRU). У нашому прикладі ми використаємо LSTM.\n",
|
||
"\n",
|
||
"Оскільки мережа приймає символи як вхідні дані, а розмір словника досить невеликий, нам не потрібен шар вбудовування, однокодовий вхід може безпосередньо передаватися в комірку 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",
|
||
"Оскільки у нас немає корисних метрик, таких як *точність*, єдиний спосіб побачити, що наша модель стає кращою, — це **вибірка** згенерованих рядків під час навчання. Для цього ми будемо використовувати **зворотні виклики** (callbacks), тобто функції, які ми можемо передати до функції `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",
|
||
"\n",
|
||
"* **Більше тексту**. Ми використали лише заголовки для нашого завдання, але ви можете спробувати працювати з повним текстом. Пам’ятайте, що RNN не дуже добре справляються з довгими послідовностями, тому має сенс або розбивати їх на коротші речення, або завжди тренувати на фіксованій довжині послідовності з певним попередньо визначеним значенням `num_chars` (наприклад, 256). Ви можете спробувати змінити наведений вище приклад на таку архітектуру, використовуючи [офіційний підручник Keras](https://keras.io/examples/generative/lstm_character_level_text_generation/) як натхнення.\n",
|
||
"\n",
|
||
"* **Багатошаровий LSTM**. Варто спробувати 2 або 3 шари LSTM-комірок. Як ми згадували в попередньому розділі, кожен шар LSTM витягує певні шаблони з тексту, і у випадку генератора на рівні символів можна очікувати, що нижчий рівень LSTM буде відповідати за витягування складів, а вищі рівні — за слова та їх комбінації. Це можна легко реалізувати, передавши параметр кількості шарів до конструктора LSTM.\n",
|
||
"\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 <b>...</b>\n",
|
||
"Little red riding hood ficed to the spam countered in European <b>...</b>\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 <b>...</b>\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** (температура), і він використовується для визначення того, наскільки сильно ми повинні дотримуватися найвищої ймовірності. Якщо температура дорівнює 1.0, ми виконуємо справедливе мультиноміальне вибіркове моделювання, а коли температура наближається до нескінченності - всі ймовірності стають рівними, і ми випадково вибираємо наступний символ. У наведеному нижче прикладі ми можемо спостерігати, що текст стає безглуздим, коли ми надто сильно збільшуємо температуру, і він нагадує \"циклічний\" текст, створений жорстким алгоритмом, коли температура наближається до 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-30T10:10:45+00:00",
|
||
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb",
|
||
"language_code": "uk"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 4
|
||
} |