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

497 lines
27 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Generatiiviset verkot\n",
"\n",
"Toistuvat neuroverkot (RNN:t) ja niiden portitetut solumuunnelmat, kuten Long Short Term Memory -solut (LSTM:t) ja Gated Recurrent Units (GRU:t), tarjoavat mekanismin kielen mallintamiseen, eli ne voivat oppia sanajärjestyksen ja ennustaa seuraavan sanan sekvenssissä. Tämä mahdollistaa RNN:ien käytön **generatiivisissa tehtävissä**, kuten tavallisessa tekstin generoinnissa, konekäännöksessä ja jopa kuvatekstien luomisessa.\n",
"\n",
"RNN-arkkitehtuurissa, jota käsittelimme edellisessä osiossa, jokainen RNN-yksikkö tuotti seuraavan piilotetun tilan ulostulona. Voimme kuitenkin lisätä jokaiselle toistuvalle yksikölle toisen ulostulon, joka mahdollistaa **sekvenssin** tuottamisen (joka on yhtä pitkä kuin alkuperäinen sekvenssi). Lisäksi voimme käyttää RNN-yksiköitä, jotka eivät ota syötettä jokaisessa vaiheessa, vaan ainoastaan jonkin alkuperäisen tilavektorin, ja tuottavat sitten ulostulosekvenssin.\n",
"\n",
"Tässä muistikirjassa keskitymme yksinkertaisiin generatiivisiin malleihin, jotka auttavat meitä tuottamaan tekstiä. Yksinkertaisuuden vuoksi rakennetaan **merkki-tason verkko**, joka tuottaa tekstiä kirjain kerrallaan. Koulutuksen aikana meidän täytyy ottaa jokin tekstikorpus ja jakaa se kirjainsekvensseihin.\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": [
"## Rakennetaan merkkisanasto\n",
"\n",
"Rakentaaksemme merkkitason generatiivisen verkon, meidän täytyy jakaa teksti yksittäisiin merkkeihin sanojen sijaan. `TextVectorization`-kerros, jota olemme aiemmin käyttäneet, ei pysty tähän, joten meillä on kaksi vaihtoehtoa:\n",
"\n",
"* Ladata teksti manuaalisesti ja tehdä tokenisointi \"käsin\", kuten [tässä Kerasin virallisessa esimerkissä](https://keras.io/examples/generative/lstm_character_level_text_generation/)\n",
"* Käyttää `Tokenizer`-luokkaa merkkitason tokenisointiin.\n",
"\n",
"Valitsemme toisen vaihtoehdon. `Tokenizer`-luokkaa voidaan myös käyttää sanojen tokenisointiin, joten sen avulla voi helposti vaihtaa merkkitason ja sanatasoisen tokenisoinnin välillä.\n",
"\n",
"Merkkitason tokenisointia varten meidän täytyy välittää parametri `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": [
"Haluamme myös käyttää yhtä erityistä tokenia merkitsemään **sekvenssin loppua**, jota kutsumme nimellä `<eos>`. Lisätään se manuaalisesti sanastoon:\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": [
"Nyt, tekstin koodaamiseksi numerosarjoiksi, voimme käyttää:\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": [
"## Generatiivisen RNN:n kouluttaminen otsikoiden luomiseen\n",
"\n",
"Tapa, jolla koulutamme RNN:n luomaan uutisotsikoita, on seuraava. Jokaisella askeleella otamme yhden otsikon, joka syötetään RNN:ään, ja jokaiselle syötehahmolle pyydämme verkkoa tuottamaan seuraavan ulostulohahmon:\n",
"\n",
"![Kuva, joka näyttää esimerkin RNN:n sanan 'HELLO' generoinnista.](../../../../../translated_images/fi/rnn-generate.56c54afb52f9781d.webp)\n",
"\n",
"Sekvenssimme viimeisen hahmon kohdalla pyydämme verkkoa tuottamaan `<eos>`-tokenin.\n",
"\n",
"Suurin ero generatiivisen RNN:n välillä, jota käytämme tässä, on se, että otamme ulostulon jokaiselta RNN:n askeleelta, emmekä vain viimeisestä solusta. Tämä voidaan saavuttaa määrittämällä `return_sequences`-parametri RNN-solulle.\n",
"\n",
"Näin ollen koulutuksen aikana verkon syöte olisi tietyn pituisen koodattujen hahmojen sekvenssi, ja ulostulo olisi saman pituisen sekvenssi, mutta siirretty yhdellä elementillä ja päätetty `<eos>`-tokenilla. Minibatch koostuu useista tällaisista sekvensseistä, ja meidän täytyy käyttää **täydennystä** kaikkien sekvenssien tasaamiseksi.\n",
"\n",
"Luodaan funktiot, jotka muuntavat datan meille. Koska haluamme täyttää sekvenssit minibatch-tasolla, ryhmittelemme ensin datan kutsumalla `.batch()`, ja sitten `map`-toiminnolla teemme muunnoksen. Muunnosfunktio ottaa siis kokonaisen minibatchin parametrina:\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": [
"Muutamia tärkeitä asioita, joita teemme tässä:\n",
"* Ensin eristämme varsinaisen tekstin merkkijonotensorista\n",
"* `text_to_sequences` muuntaa merkkijonojen listan kokonaislukutensoreiden listaksi\n",
"* `pad_sequences` täyttää nämä tensorit niiden maksimi pituuteen\n",
"* Lopuksi teemme yhden kuuman koodauksen kaikille merkeille, sekä siirron ja `<eos>`-lisäyksen. Pian näemme, miksi tarvitsemme yhden kuuman koodattuja merkkejä\n",
"\n",
"Tämä funktio on kuitenkin **Python-tyylinen**, eli sitä ei voida automaattisesti muuntaa Tensorflow'n laskentagrafiksi. Saamme virheitä, jos yritämme käyttää tätä funktiota suoraan `Dataset.map`-funktiossa. Meidän täytyy sulkea tämä Python-tyylinen kutsu käyttämällä `py_function`-käärettä:\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": [
"> **Huomio**: Python- ja Tensorflow-muunnostoimintojen erottaminen toisistaan saattaa tuntua hieman monimutkaiselta, ja saatat pohtia, miksi emme muunna datasettiä tavallisilla Python-funktioilla ennen sen syöttämistä `fit`-funktiolle. Vaikka tämä onkin mahdollista, `Dataset.map`-funktion käyttö tarjoaa suuren edun, koska datan muunnosputki suoritetaan Tensorflow'n laskentakaavion avulla. Tämä hyödyntää GPU-laskentaa ja minimoi datan siirtämisen tarpeen CPU:n ja GPU:n välillä.\n",
"\n",
"Nyt voimme rakentaa generaattoriverkkomme ja aloittaa koulutuksen. Se voi perustua mihin tahansa rekursiiviseen soluun, joita käsittelimme edellisessä osiossa (yksinkertainen, LSTM tai GRU). Esimerkissämme käytämme LSTM:ää.\n",
"\n",
"Koska verkko ottaa syötteenä merkkejä ja sanaston koko on melko pieni, emme tarvitse upotustasoa (embedding layer). Yksinkertaisesti one-hot-koodattu syöte voidaan syöttää suoraan LSTM-soluun. Ulostulokerros olisi `Dense`-luokittelija, joka muuntaa LSTM:n ulostulon one-hot-koodatuiksi token-numeroiksi.\n",
"\n",
"Lisäksi, koska käsittelemme vaihtelevan pituisia sekvenssejä, voimme käyttää `Masking`-kerrosta luomaan maskin, joka ohittaa merkkijonon täytetyt osat. Tämä ei ole ehdottoman välttämätöntä, koska emme ole erityisen kiinnostuneita kaikesta, mikä tulee `<eos>`-tokenin jälkeen, mutta käytämme sitä saadaksemme kokemusta tämän tyyppisestä kerroksesta. `input_shape` olisi `(None, vocab_size)`, missä `None` osoittaa vaihtelevan pituisen sekvenssin, ja ulostulon muoto on myös `(None, vocab_size)`, kuten voit nähdä `summary`-tulosteesta:\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": [
"## Tuloksen luominen\n",
"\n",
"Nyt kun olemme kouluttaneet mallin, haluamme käyttää sitä tuottamaan jonkinlaista tulosta. Ensinnäkin tarvitsemme tavan purkaa tekstin, joka on esitetty token-numeroiden sekvenssinä. Tähän voisimme käyttää `tokenizer.sequences_to_texts` -funktiota; se ei kuitenkaan toimi hyvin merkkitasoisessa tokenoinnissa. Siksi otamme sanaston tokenoijasta (nimeltään `word_index`), rakennamme käänteisen kartan ja kirjoitamme oman purkufunktion:\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": [
"Aloitetaan generointi. Ensin otamme jonkin merkkijonon `start`, koodaamme sen sekvenssiksi `inp`, ja sitten jokaisessa vaiheessa kutsumme verkkoamme ennustamaan seuraavan merkin.\n",
"\n",
"Verkon tulos `out` on vektori, jossa on `vocab_size` elementtiä, jotka edustavat kunkin tokenin todennäköisyyksiä. Voimme löytää todennäköisimmän tokenin numeron käyttämällä `argmax`. Tämän jälkeen lisäämme tämän merkin generoituun tokenien listaan ja jatkamme generointia. Tämä prosessi, jossa generoidaan yksi merkki, toistetaan `size` kertaa, jotta saadaan tarvittava määrä merkkejä, ja lopetamme aikaisemmin, jos `eos_token` kohdataan.\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äytteen ottaminen koulutuksen aikana\n",
"\n",
"Koska meillä ei ole käytettävissä hyödyllisiä mittareita, kuten *tarkkuutta*, ainoa tapa nähdä, että mallimme paranee, on **ottaa näytteitä** luoduista merkkijonoista koulutuksen aikana. Tätä varten käytämme **takaisinkutsuja** eli funktioita, jotka voimme välittää `fit`-funktiolle ja jotka kutsutaan säännöllisin väliajoin koulutuksen aikana.\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": [
"Tämä esimerkki tuottaa jo melko hyvää tekstiä, mutta sitä voidaan parantaa useilla tavoilla:\n",
"\n",
"* **Enemmän tekstiä**. Olemme käyttäneet tehtävässämme vain otsikoita, mutta voit kokeilla koko tekstin käyttöä. Muista, että RNN:t eivät ole kovin hyviä käsittelemään pitkiä sekvenssejä, joten on järkevää joko jakaa ne lyhyempiin lauseisiin tai aina kouluttaa kiinteällä sekvenssipituudella, joka on ennalta määritelty arvo `num_chars` (esimerkiksi 256). Voit yrittää muuttaa yllä olevan esimerkin tällaiseksi arkkitehtuuriksi käyttämällä [virallista Keras-opasta](https://keras.io/examples/generative/lstm_character_level_text_generation/) inspiraationa.\n",
"\n",
"* **Monikerroksinen LSTM**. Kannattaa kokeilla 2 tai 3 kerrosta LSTM-soluja. Kuten mainitsimme edellisessä osiossa, jokainen LSTM-kerros tunnistaa tiettyjä tekstin piirteitä, ja merkkitason generaattorin tapauksessa voimme odottaa alemman LSTM-tason vastaavan tavujen tunnistamisesta ja ylempien tasojen sanojen ja sanayhdistelmien tunnistamisesta. Tämä voidaan toteuttaa yksinkertaisesti antamalla kerrosten lukumäärä -parametri LSTM:n rakentajalle.\n",
"\n",
"* Voit myös kokeilla **GRU-yksiköitä** ja katsoa, mitkä toimivat paremmin, sekä **erilaisia piilotettujen kerrosten kokoja**. Liian suuri piilotettu kerros voi johtaa ylisovittamiseen (esim. verkko oppii tarkan tekstin), kun taas liian pieni koko ei ehkä tuota hyviä tuloksia.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Pehmeä tekstin generointi ja lämpötila\n",
"\n",
"Aiemmassa `generate`-määritelmässä valitsimme aina seuraavaksi merkiksi sen, jolla oli korkein todennäköisyys. Tämä johti usein siihen, että teksti \"kierrätti\" samoja merkkijonoja yhä uudelleen, kuten tässä esimerkissä:\n",
"```\n",
"today of the second the company and a second the company ...\n",
"```\n",
"\n",
"Jos kuitenkin tarkastelemme seuraavan merkin todennäköisyysjakaumaa, voi olla, että muutaman korkeimman todennäköisyyden ero ei ole suuri, esimerkiksi yksi merkki voi olla todennäköisyydellä 0.2 ja toinen 0.19, jne. Esimerkiksi, kun etsitään seuraavaa merkkiä jaksossa '*play*', seuraava merkki voi yhtä hyvin olla joko välilyönti tai **e** (kuten sanassa *player*).\n",
"\n",
"Tämä johtaa siihen päätelmään, että ei ole aina \"reilua\" valita merkkiä, jolla on korkeampi todennäköisyys, koska toisen korkein todennäköisyys voi silti tuottaa merkityksellistä tekstiä. Onkin viisaampaa **näytteistää** merkkejä verkon tuottaman todennäköisyysjakauman perusteella.\n",
"\n",
"Tämä näytteistäminen voidaan toteuttaa `np.multinomial`-funktiolla, joka toteuttaa niin kutsutun **multinomiaalisen jakauman**. Alla on määritelty funktio, joka toteuttaa tämän **pehmeän** tekstin generoinnin:\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": [
"Olemme ottaneet käyttöön yhden lisäparametrin nimeltä **lämpötila**, jota käytetään osoittamaan, kuinka tiukasti meidän tulisi noudattaa korkeinta todennäköisyyttä. Jos lämpötila on 1.0, teemme tasapuolista multinomiaalista otantaa, ja kun lämpötila kasvaa äärettömyyteen - kaikki todennäköisyydet muuttuvat yhtä suuriksi, ja valitsemme seuraavan merkin satunnaisesti. Alla olevassa esimerkissä voimme havaita, että teksti muuttuu merkityksettömäksi, kun nostamme lämpötilaa liikaa, ja se muistuttaa \"kierrätettyä\" tiukasti generoituvaa tekstiä, kun lämpötila lähestyy arvoa 0.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Vastuuvapauslauseke**: \nTämä asiakirja on käännetty käyttämällä tekoälypohjaista käännöspalvelua [Co-op Translator](https://github.com/Azure/co-op-translator). Vaikka pyrimme tarkkuuteen, huomioithan, että automaattiset käännökset voivat sisältää virheitä tai epätarkkuuksia. Alkuperäinen asiakirja sen alkuperäisellä kielellä tulisi pitää ensisijaisena lähteenä. Kriittisen tiedon osalta suositellaan ammattimaista ihmiskäännöstä. Emme ole vastuussa väärinkäsityksistä tai virhetulkinnoista, jotka johtuvat tämän käännöksen käytöstä.\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-28T21:31:08+00:00",
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb",
"language_code": "fi"
}
},
"nbformat": 4,
"nbformat_minor": 4
}