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

497 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",
"Рекурентните невронни мрежи (RNNs) и техните варианти с управлявани клетки, като клетки с дългосрочна памет (LSTMs) и управлявани рекурентни единици (GRUs), предоставят механизъм за моделиране на език, т.е. те могат да научат подредбата на думите и да предсказват следващата дума в последователност. Това ни позволява да използваме RNNs за **генеративни задачи**, като обикновено генериране на текст, машинен превод и дори създаване на описания за изображения.\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": [
"Сега, за да кодираме текст в последователности от числа, можем да използваме:\n"
]
},
{
"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/bg/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",
"* Накрая извършваме еднократно кодиране на всички символи, както и изместване и добавяне на `<eos>`. Скоро ще видим защо ни трябват еднократно кодирани символи\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",
"Тъй като мрежата приема символи като вход, а размерът на речника е сравнително малък, нямаме нужда от 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",
"Тъй като нямаме полезни метрики като *точност*, единственият начин да видим дали нашият модел се подобрява, е чрез **извличане на примерен** генериран низ по време на обучението. За да направим това, ще използваме **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",
"* **Повече текст**. Използвахме само заглавия за нашата задача, но може да опитате с пълен текст. Имайте предвид, че RNNs не се справят добре с обработката на дълги последователности, затова има смисъл или да ги разделите на по-кратки изречения, или винаги да тренирате с фиксирана дължина на последователността с предварително зададена стойност `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 &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": [
"Въведохме още един параметър, наречен **температура**, който се използва, за да покаже колко строго трябва да се придържаме към най-високата вероятност. Ако температурата е 1.0, правим честно мултиномиално извадково вземане, а когато температурата достигне до безкрайност - всички вероятности стават равни и случайно избираме следващия символ. В примера по-долу можем да наблюдаваме, че текстът става безсмислен, когато увеличим температурата твърде много, и прилича на \"циклично\" твърдо генериран текст, когато се доближи до 0.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [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-30T00:35:18+00:00",
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 4
}