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

495 lines
28 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Redes generativas\n",
"\n",
"Redes Neuronais Recorrentes (RNNs) e suas variantes com células controladas, como Células de Memória de Longo e Curto Prazo (LSTMs) e Unidades Recorrentes Controladas (GRUs), proporcionaram um mecanismo para modelagem de linguagem, ou seja, elas podem aprender a ordem das palavras e fornecer previsões para a próxima palavra em uma sequência. Isso permite que utilizemos RNNs para **tarefas generativas**, como geração de texto comum, tradução automática e até mesmo legendagem de imagens.\n",
"\n",
"Na arquitetura de RNN que discutimos na unidade anterior, cada unidade RNN produzia o próximo estado oculto como saída. No entanto, também podemos adicionar outra saída a cada unidade recorrente, o que nos permitiria gerar uma **sequência** (que tem o mesmo comprimento da sequência original). Além disso, podemos usar unidades RNN que não aceitam uma entrada em cada etapa, mas apenas recebem um vetor de estado inicial e, em seguida, produzem uma sequência de saídas.\n",
"\n",
"Neste notebook, vamos focar em modelos generativos simples que nos ajudam a gerar texto. Para simplificar, vamos construir uma **rede ao nível de caracteres**, que gera texto letra por letra. Durante o treino, precisamos pegar um corpus de texto e dividi-lo em sequências de letras.\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": [
"## Construir um vocabulário de caracteres\n",
"\n",
"Para construir uma rede generativa a nível de caracteres, precisamos dividir o texto em caracteres individuais em vez de palavras. A camada `TextVectorization` que temos usado anteriormente não consegue fazer isso, por isso temos duas opções:\n",
"\n",
"* Carregar o texto manualmente e fazer a tokenização 'à mão', como neste [exemplo oficial do Keras](https://keras.io/examples/generative/lstm_character_level_text_generation/)\n",
"* Usar a classe `Tokenizer` para a tokenização a nível de caracteres.\n",
"\n",
"Vamos optar pela segunda opção. A `Tokenizer` também pode ser usada para tokenizar em palavras, por isso deve ser possível alternar facilmente entre a tokenização a nível de caracteres e a nível de palavras.\n",
"\n",
"Para realizar a tokenização a nível de caracteres, precisamos passar o parâmetro `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": [
"Também queremos usar um token especial para indicar **fim de sequência**, que chamaremos de `<eos>`. Vamos adicioná-lo manualmente ao vocabulário:\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": [
"## Treinar uma RNN generativa para criar títulos\n",
"\n",
"A forma como iremos treinar a RNN para gerar títulos de notícias é a seguinte. Em cada passo, iremos pegar um título, que será alimentado numa RNN, e para cada caractere de entrada pediremos à rede que gere o próximo caractere de saída:\n",
"\n",
"![Imagem mostrando um exemplo de geração de RNN da palavra 'HELLO'.](../../../../../lessons/5-NLP/17-GenerativeNetworks/images/rnn-generate.png)\n",
"\n",
"Para o último caractere da nossa sequência, pediremos à rede que gere o token `<eos>`.\n",
"\n",
"A principal diferença da RNN generativa que estamos a usar aqui é que iremos utilizar a saída de cada passo da RNN, e não apenas da célula final. Isto pode ser conseguido ao especificar o parâmetro `return_sequences` na célula da RNN.\n",
"\n",
"Assim, durante o treino, a entrada para a rede será uma sequência de caracteres codificados de um determinado comprimento, e a saída será uma sequência do mesmo comprimento, mas deslocada por um elemento e terminada com `<eos>`. O minibatch consistirá em várias dessas sequências, e será necessário usar **padding** para alinhar todas as sequências.\n",
"\n",
"Vamos criar funções que irão transformar o conjunto de dados para nós. Como queremos adicionar padding às sequências ao nível do minibatch, primeiro agruparemos o conjunto de dados ao chamar `.batch()`, e depois aplicaremos `map` para realizar a transformação. Portanto, a função de transformação irá receber um minibatch inteiro como parâmetro:\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": [
"Algumas coisas importantes que fazemos aqui: \n",
"* Primeiro extraímos o texto real do tensor de strings \n",
"* `text_to_sequences` converte a lista de strings numa lista de tensores inteiros \n",
"* `pad_sequences` preenche esses tensores até ao seu comprimento máximo \n",
"* Finalmente, fazemos a codificação one-hot de todos os caracteres, além de realizar o deslocamento e a adição de `<eos>`. Em breve veremos por que precisamos de caracteres codificados em one-hot \n",
"\n",
"No entanto, esta função é **Pythonic**, ou seja, não pode ser automaticamente traduzida para o grafo computacional do Tensorflow. Iremos obter erros se tentarmos usar esta função diretamente na função `Dataset.map`. Precisamos encapsular esta chamada Pythonic utilizando o wrapper `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": [
"> **Nota**: Diferenciar entre funções de transformação Pythonic e Tensorflow pode parecer um pouco complexo, e pode estar a questionar-se por que não transformamos o conjunto de dados usando funções padrão de Python antes de passá-lo para `fit`. Embora isso definitivamente possa ser feito, usar `Dataset.map` tem uma grande vantagem, porque o pipeline de transformação de dados é executado usando o grafo computacional do Tensorflow, que aproveita os cálculos na GPU e minimiza a necessidade de transferir dados entre CPU/GPU.\n",
"\n",
"Agora podemos construir a nossa rede geradora e começar o treino. Pode ser baseada em qualquer célula recorrente que discutimos na unidade anterior (simples, LSTM ou GRU). No nosso exemplo, usaremos LSTM.\n",
"\n",
"Como a rede recebe caracteres como entrada e o tamanho do vocabulário é relativamente pequeno, não precisamos de uma camada de embedding; a entrada codificada em one-hot pode ir diretamente para a célula LSTM. A camada de saída será um classificador `Dense` que converterá a saída do LSTM em números de tokens codificados em one-hot.\n",
"\n",
"Além disso, como estamos a lidar com sequências de comprimento variável, podemos usar a camada `Masking` para criar uma máscara que ignorará a parte preenchida da string. Isto não é estritamente necessário, porque não estamos muito interessados em tudo o que vai além do token `<eos>`, mas usaremos esta camada para ganhar alguma experiência com este tipo de camada. O `input_shape` será `(None, vocab_size)`, onde `None` indica a sequência de comprimento variável, e o formato de saída será `(None, vocab_size)` também, como pode ver no `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": [
"## Gerar resultados\n",
"\n",
"Agora que treinámos o modelo, queremos utilizá-lo para gerar alguns resultados. Antes de mais, precisamos de uma forma de decodificar texto representado por uma sequência de números de tokens. Para isso, poderíamos usar a função `tokenizer.sequences_to_texts`; no entanto, esta não funciona bem com tokenização a nível de caracteres. Por isso, vamos pegar num dicionário de tokens do tokenizer (chamado `word_index`), construir um mapa inverso e escrever a nossa própria função de decodificação:\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": [
"Agora, vamos começar a geração. Iniciaremos com uma string `start`, codificá-la-emos numa sequência `inp`, e em cada passo chamaremos a nossa rede para inferir o próximo carácter.\n",
"\n",
"O output da rede `out` é um vetor de elementos `vocab_size` que representam as probabilidades de cada token, e podemos encontrar o número do token mais provável utilizando `argmax`. Em seguida, adicionamos este carácter à lista de tokens gerados e continuamos com a geração. Este processo de gerar um carácter é repetido `size` vezes para gerar o número necessário de caracteres, e terminamos antecipadamente quando o `eos_token` é encontrado.\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": [
"## Amostragem de saída durante o treino\n",
"\n",
"Como não temos métricas úteis como *precisão*, a única forma de verificar se o nosso modelo está a melhorar é através da **amostragem** de strings geradas durante o treino. Para isso, utilizaremos **callbacks**, ou seja, funções que podemos passar para a função `fit`, e que serão chamadas periodicamente durante o treino.\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": [
"Este exemplo já gera um texto bastante bom, mas pode ser melhorado de várias maneiras:\n",
"\n",
"* **Mais texto**. Utilizámos apenas títulos para a nossa tarefa, mas pode querer experimentar com texto completo. Lembre-se de que as RNNs não lidam muito bem com sequências longas, por isso faz sentido dividi-las em frases mais curtas ou treinar sempre com uma sequência fixa de comprimento pré-definido `num_chars` (por exemplo, 256). Pode tentar alterar o exemplo acima para essa arquitetura, usando o [tutorial oficial do Keras](https://keras.io/examples/generative/lstm_character_level_text_generation/) como inspiração.\n",
"\n",
"* **LSTM com várias camadas**. Faz sentido experimentar 2 ou 3 camadas de células LSTM. Como mencionámos na unidade anterior, cada camada de LSTM extrai certos padrões do texto, e no caso de um gerador a nível de caracteres, podemos esperar que o nível inferior do LSTM seja responsável por extrair sílabas, e os níveis superiores - palavras e combinações de palavras. Isto pode ser implementado facilmente passando o parâmetro número-de-camadas ao construtor do LSTM.\n",
"\n",
"* Também pode querer experimentar com **unidades GRU** e ver quais produzem melhores resultados, bem como com **diferentes tamanhos de camadas ocultas**. Uma camada oculta demasiado grande pode resultar em overfitting (por exemplo, a rede aprenderá o texto exato), e um tamanho menor pode não produzir bons resultados.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Geração de texto suave e temperatura\n",
"\n",
"Na definição anterior de `generate`, estávamos sempre a escolher o carácter com a maior probabilidade como o próximo carácter no texto gerado. Isto resultava no facto de o texto frequentemente \"ciclar\" entre as mesmas sequências de caracteres repetidamente, como neste exemplo:\n",
"```\n",
"today of the second the company and a second the company ...\n",
"```\n",
"\n",
"No entanto, se analisarmos a distribuição de probabilidades para o próximo carácter, pode acontecer que a diferença entre algumas das probabilidades mais altas não seja muito grande, por exemplo, um carácter pode ter uma probabilidade de 0.2, outro de 0.19, etc. Por exemplo, ao procurar o próximo carácter na sequência '*play*', o próximo carácter pode ser igualmente um espaço ou **e** (como na palavra *player*).\n",
"\n",
"Isto leva-nos à conclusão de que nem sempre é \"justo\" selecionar o carácter com maior probabilidade, porque escolher o segundo mais provável ainda pode levar-nos a um texto significativo. É mais sensato **amostrar** caracteres a partir da distribuição de probabilidades fornecida pela saída da rede.\n",
"\n",
"Esta amostragem pode ser feita utilizando a função `np.multinomial`, que implementa a chamada **distribuição multinomial**. Uma função que implementa esta geração de texto **suave** está definida abaixo:\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": [
"Introduzimos mais um parâmetro chamado **temperatura**, que é usado para indicar o quão rigorosamente devemos aderir à maior probabilidade. Se a temperatura for 1.0, fazemos uma amostragem multinomial justa, e quando a temperatura vai para infinito - todas as probabilidades tornam-se iguais, e selecionamos aleatoriamente o próximo caractere. No exemplo abaixo, podemos observar que o texto torna-se sem sentido quando aumentamos demasiado a temperatura, e assemelha-se a um texto \"ciclado\" gerado rigidamente quando se aproxima de 0.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Aviso Legal**: \nEste documento foi traduzido utilizando o serviço de tradução por IA [Co-op Translator](https://github.com/Azure/co-op-translator). Embora nos esforcemos para garantir a precisão, é importante notar que traduções automáticas podem conter erros ou imprecisões. O documento original na sua língua nativa deve ser considerado a fonte autoritária. Para informações críticas, recomenda-se a tradução profissional realizada por humanos. Não nos responsabilizamos por quaisquer mal-entendidos ou interpretações incorretas decorrentes do uso desta tradução.\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-31T11:55:50+00:00",
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb",
"language_code": "pt"
}
},
"nbformat": 4,
"nbformat_minor": 4
}