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

497 lines
27 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Generativne mreže\n",
"\n",
"Rekurentne nevronske mreže (RNN) in njihove različice z vrati, kot so celice Long Short Term Memory (LSTM) in Gated Recurrent Units (GRU), omogočajo modeliranje jezika, tj. lahko se naučijo vrstnega reda besed in napovedujejo naslednjo besedo v zaporedju. To nam omogoča uporabo RNN za **generativne naloge**, kot so običajno generiranje besedila, strojno prevajanje in celo opisovanje slik.\n",
"\n",
"V arhitekturi RNN, ki smo jo obravnavali v prejšnji enoti, je vsaka enota RNN ustvarila naslednje skrito stanje kot izhod. Vendar pa lahko vsaki rekurentni enoti dodamo še en izhod, kar nam omogoča, da ustvarimo **zaporedje** (ki je enako dolžini izvirnega zaporedja). Poleg tega lahko uporabimo RNN enote, ki ne sprejemajo vhoda na vsakem koraku, ampak le začetni vektorski stanji, nato pa ustvarijo zaporedje izhodov.\n",
"\n",
"V tem zvezku se bomo osredotočili na preproste generativne modele, ki nam pomagajo ustvarjati besedilo. Za enostavnost bomo zgradili **mrežo na ravni znakov**, ki generira besedilo črko za črko. Med učenjem moramo vzeti nek korpus besedila in ga razdeliti na zaporedja črk.\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": [
"## Gradnja besedišča znakov\n",
"\n",
"Za izgradnjo generativne mreže na ravni znakov moramo besedilo razdeliti na posamezne znake namesto na besede. Plast `TextVectorization`, ki smo jo uporabljali prej, tega ne omogoča, zato imamo dve možnosti:\n",
"\n",
"* Ročno naložimo besedilo in izvedemo tokenizacijo \"na roke\", kot je prikazano v [tem uradnem primeru Keras](https://keras.io/examples/generative/lstm_character_level_text_generation/)\n",
"* Uporabimo razred `Tokenizer` za tokenizacijo na ravni znakov.\n",
"\n",
"Odločili se bomo za drugo možnost. `Tokenizer` se lahko uporablja tudi za tokenizacijo na ravni besed, zato je prehod z tokenizacije na ravni znakov na tokenizacijo na ravni besed precej preprost.\n",
"\n",
"Za izvedbo tokenizacije na ravni znakov moramo podati parameter `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": [
"Prav tako želimo uporabiti en poseben znak za označevanje **konca zaporedja**, ki ga bomo imenovali `<eos>`. Dodajmo ga ročno v besednjak:\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": [
"Zdaj lahko za kodiranje besedila v zaporedja števil uporabimo:\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": [
"## Učenje generativnega RNN za generiranje naslovov\n",
"\n",
"Način, kako bomo učili RNN za generiranje novičarskih naslovov, je naslednji. Na vsakem koraku bomo vzeli en naslov, ki bo podan v RNN, in za vsak vhodni znak bomo od mreže zahtevali, da generira naslednji izhodni znak:\n",
"\n",
"![Slika, ki prikazuje primer generiranja besede 'HELLO' z RNN.](../../../../../translated_images/sl/rnn-generate.56c54afb52f9781d.webp)\n",
"\n",
"Za zadnji znak našega zaporedja bomo od mreže zahtevali, da generira `<eos>` token.\n",
"\n",
"Glavna razlika med generativnim RNN, ki ga uporabljamo tukaj, je v tem, da bomo vzeli izhod iz vsakega koraka RNN, ne le iz zadnje celice. To lahko dosežemo z nastavitvijo parametra `return_sequences` za RNN celico.\n",
"\n",
"Tako bo med učenjem vhod v mrežo zaporedje kodiranih znakov določene dolžine, izhod pa bo zaporedje iste dolžine, vendar premaknjeno za en element in zaključeno z `<eos>`. Minibatch bo sestavljen iz več takšnih zaporedij, pri čemer bomo morali uporabiti **polnjenje** (padding), da uskladimo vsa zaporedja.\n",
"\n",
"Ustvarimo funkcije, ki bodo za nas preoblikovale podatkovni niz. Ker želimo zaporedja polniti na ravni minibatch-a, bomo najprej razvrstili podatkovni niz z uporabo `.batch()`, nato pa ga `map`-ali, da izvedemo preoblikovanje. Tako bo funkcija za preoblikovanje kot parameter sprejela celoten minibatch:\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": [
"Nekaj pomembnih stvari, ki jih tukaj počnemo:\n",
"* Najprej izluščimo dejansko besedilo iz niza tensorja\n",
"* `text_to_sequences` pretvori seznam nizov v seznam celoštevilskih tensorjev\n",
"* `pad_sequences` dopolni te tensorje do njihove največje dolžine\n",
"* Na koncu vse znake kodiramo v enovrstično kodiranje, prav tako izvedemo premik in dodamo `<eos>`. Kmalu bomo videli, zakaj potrebujemo znake v enovrstičnem kodiranju.\n",
"\n",
"Vendar pa je ta funkcija **Pythonic**, kar pomeni, da je ni mogoče samodejno prevesti v Tensorflow računski graf. Če poskušamo to funkcijo neposredno uporabiti v funkciji `Dataset.map`, bomo dobili napake. To Pythonic klic moramo oviti z uporabo ovojnice `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": [
"> **Opomba**: Razlikovanje med Pythonovimi in Tensorflow funkcijami za transformacijo podatkov se morda zdi nekoliko zapleteno, in morda se sprašujete, zakaj ne transformiramo nabora podatkov z uporabo standardnih Pythonovih funkcij, preden ga posredujemo funkciji `fit`. Čeprav je to vsekakor mogoče, ima uporaba `Dataset.map` veliko prednost, saj se cevovod za transformacijo podatkov izvaja z uporabo Tensorflow računalniškega grafa, kar omogoča izkoriščanje GPU izračunov in zmanjšuje potrebo po prenašanju podatkov med CPU in GPU.\n",
"\n",
"Zdaj lahko zgradimo našo generativno mrežo in začnemo z učenjem. Temeljiti mora na katerikoli rekurentni celici, o kateri smo govorili v prejšnji enoti (enostavna, LSTM ali GRU). V našem primeru bomo uporabili LSTM.\n",
"\n",
"Ker mreža prejme znake kot vhod, velikost besedišča pa je precej majhna, ne potrebujemo vgraditvene plasti (embedding layer), saj lahko enovročno kodiran vhod neposredno vstopi v LSTM celico. Izhodna plast bo `Dense` klasifikator, ki bo pretvoril izhod LSTM v enovročno kodirane številke tokenov.\n",
"\n",
"Poleg tega, ker delamo z zaporedji spremenljive dolžine, lahko uporabimo plast `Masking`, da ustvarimo masko, ki bo ignorirala dopolnjene dele niza. To sicer ni strogo potrebno, saj nas ne zanima preveč vse, kar presega token `<eos>`, vendar jo bomo uporabili, da pridobimo nekaj izkušenj s to vrsto plasti. `input_shape` bo `(None, vocab_size)`, kjer `None` označuje zaporedje spremenljive dolžine, izhodna oblika pa je prav tako `(None, vocab_size)`, kot lahko vidite iz `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": [
"## Generiranje izhoda\n",
"\n",
"Zdaj, ko smo model izurili, ga želimo uporabiti za generiranje izhoda. Najprej potrebujemo način za dekodiranje besedila, predstavljenega kot zaporedje številk žetonov. Za to bi lahko uporabili funkcijo `tokenizer.sequences_to_texts`; vendar ta ne deluje dobro pri žetonizaciji na ravni znakov. Zato bomo vzeli slovar žetonov iz žetonizatorja (imenovan `word_index`), zgradili obratni zemljevid in napisali svojo funkcijo za dekodiranje:\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": [
"Zdaj bomo začeli z generacijo. Začeli bomo z neko začetno vrednostjo `start`, jo zakodirali v zaporedje `inp`, nato pa bomo v vsakem koraku poklicali našo mrežo, da izračuna naslednji znak.\n",
"\n",
"Izhod mreže `out` je vektor z `vocab_size` elementi, ki predstavljajo verjetnosti za vsak token. Najbolj verjetno številko tokena lahko najdemo z uporabo `argmax`. Ta znak nato dodamo v seznam generiranih tokenov in nadaljujemo z generacijo. Ta postopek generiranja enega znaka ponovimo `size`-krat, da ustvarimo želeno število znakov, pri čemer pa se postopek predčasno zaključi, če naletimo na `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": [
"## Vzorčenje izhoda med učenjem\n",
"\n",
"Ker nimamo nobenih uporabnih metrik, kot je *natančnost*, je edini način, da vidimo, ali se naš model izboljšuje, **vzorčenje** generiranih nizov med učenjem. Za to bomo uporabili **povratne klice**, tj. funkcije, ki jih lahko podamo funkciji `fit` in ki se bodo občasno klicale med učenjem.\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": [
"Ta primer že ustvarja precej dober tekst, vendar ga je mogoče izboljšati na več načinov:\n",
"\n",
"* **Več besedila**. Uporabili smo samo naslove za našo nalogo, vendar bi morda želeli eksperimentirati s celotnim besedilom. Ne pozabite, da RNN-ji niso najboljši pri obdelavi dolgih zaporedij, zato je smiselno, da jih razdelite na krajše stavke ali pa vedno trenirate na fiksni dolžini zaporedja z vnaprej določeno vrednostjo `num_chars` (recimo 256). Poskusite zgornji primer spremeniti v takšno arhitekturo, pri čemer uporabite [uradni Kerasov vodič](https://keras.io/examples/generative/lstm_character_level_text_generation/) kot navdih.\n",
"\n",
"* **Večplastni LSTM**. Smiselno je poskusiti z 2 ali 3 plastmi LSTM celic. Kot smo omenili v prejšnji enoti, vsaka plast LSTM izlušči določene vzorce iz besedila, in v primeru generatorja na ravni znakov lahko pričakujemo, da bo nižja raven LSTM odgovorna za izluščanje zlogov, višje ravni pa za besede in besedne zveze. To je mogoče preprosto implementirati z dodajanjem parametra za število plasti v konstruktor LSTM.\n",
"\n",
"* Prav tako bi morda želeli eksperimentirati z **GRU enotami** in preveriti, katere delujejo bolje, ter z **različnimi velikostmi skritih plasti**. Prevelika skrita plast lahko povzroči prekomerno prileganje (npr. mreža se bo naučila točno določenega besedila), premajhna velikost pa morda ne bo dala dobrih rezultatov.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Generiranje mehkejšega besedila in temperatura\n",
"\n",
"V prejšnji definiciji funkcije `generate` smo vedno izbrali znak z najvišjo verjetnostjo kot naslednji znak v generiranem besedilu. To je pogosto povzročilo, da se je besedilo \"vrtelo\" med istimi zaporedji znakov znova in znova, kot v tem primeru:\n",
"```\n",
"today of the second the company and a second the company ...\n",
"```\n",
"\n",
"Če pa pogledamo porazdelitev verjetnosti za naslednji znak, lahko opazimo, da razlika med nekaj najvišjimi verjetnostmi ni velika, npr. en znak ima verjetnost 0.2, drugi pa 0.19 itd. Na primer, ko iščemo naslednji znak v zaporedju '*play*', je naslednji znak lahko enako verjetno presledek ali **e** (kot v besedi *player*).\n",
"\n",
"To nas pripelje do zaključka, da ni vedno \"pravično\" izbrati znak z višjo verjetnostjo, saj lahko izbira drugega najverjetnejšega znaka še vedno vodi do smiselnega besedila. Bolj smiselno je **vzorec** znakov vzeti iz porazdelitve verjetnosti, ki jo poda izhod mreže.\n",
"\n",
"To vzorčenje lahko izvedemo z uporabo funkcije `np.multinomial`, ki implementira tako imenovano **multinomsko porazdelitev**. Funkcija, ki implementira to **mehko** generiranje besedila, je definirana spodaj:\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": [
"Predstavili smo še en parameter, imenovan **temperatura**, ki se uporablja za označevanje, kako strogo se moramo držati najvišje verjetnosti. Če je temperatura 1,0, izvajamo pošteno multinomno vzorčenje, in ko temperatura gre proti neskončnosti - vse verjetnosti postanejo enake, naključno izberemo naslednji znak. V spodnjem primeru lahko opazimo, da besedilo postane nesmiselno, ko preveč povečamo temperaturo, in spominja na \"ciklirano\" težko generirano besedilo, ko se približa 0.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za prevajanje z umetno inteligenco [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo profesionalni človeški prevod. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki bi nastale zaradi uporabe tega prevoda.\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-30T07:59:38+00:00",
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb",
"language_code": "sl"
}
},
"nbformat": 4,
"nbformat_minor": 4
}