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

495 lines
28 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Redes Generativas\n",
"\n",
"Redes Neurais Recorrentes (RNNs) e suas variantes com células controladas, como Células de Memória de Longo Prazo (LSTMs) e Unidades Recorrentes Controladas (GRUs), fornecem 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 nos permite usar 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, apenas recebem um vetor de estado inicial e, em seguida, produzem uma sequência de saídas.\n",
"\n",
"Neste notebook, vamos nos concentrar em modelos generativos simples que nos ajudam a gerar texto. Para simplificar, vamos construir uma **rede em nível de caracteres**, que gera texto letra por letra. Durante o treinamento, precisamos pegar algum 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": [
"## Construindo um vocabulário de caracteres\n",
"\n",
"Para construir uma rede generativa em nível de caracteres, precisamos dividir o texto em caracteres individuais em vez de palavras. A camada `TextVectorization` que usamos anteriormente não consegue fazer isso, então temos duas opções:\n",
"\n",
"* Carregar o texto manualmente e fazer a tokenização \"manualmente\", como neste [exemplo oficial do Keras](https://keras.io/examples/generative/lstm_character_level_text_generation/)\n",
"* Usar a classe `Tokenizer` para tokenização em nível de caracteres.\n",
"\n",
"Vamos seguir com a segunda opção. `Tokenizer` também pode ser usado para tokenizar em palavras, então deve ser possível alternar facilmente entre tokenização em nível de caracteres e em nível de palavras.\n",
"\n",
"Para realizar a tokenização em 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 denotar **fim da 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": [
"## Treinando uma RNN generativa para criar títulos\n",
"\n",
"A maneira como treinaremos a RNN para gerar títulos de notícias é a seguinte. A cada etapa, pegaremos um título, que será alimentado em uma 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'.](../../../../../translated_images/pt-BR/rnn-generate.56c54afb52f9781d.webp)\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 usando aqui é que pegaremos a saída de cada etapa da RNN, e não apenas da célula final. Isso pode ser alcançado especificando o parâmetro `return_sequences` na célula da RNN.\n",
"\n",
"Assim, durante o treinamento, a entrada para a rede será uma sequência de caracteres codificados de algum comprimento, e a saída será uma sequência do mesmo comprimento, mas deslocada por um elemento e terminada com `<eos>`. O minibatch consistirá de várias dessas sequências, e precisaremos usar **padding** para alinhar todas as sequências.\n",
"\n",
"Vamos criar funções que transformarão o conjunto de dados para nós. Como queremos preencher as sequências no nível do minibatch, primeiro agruparemos o conjunto de dados chamando `.batch()`, e depois usaremos `map` para realizar a transformação. Portanto, a função de transformação 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 string\n",
"* `text_to_sequences` converte a lista de strings em uma lista de tensores inteiros\n",
"* `pad_sequences` ajusta esses tensores ao seu comprimento máximo\n",
"* Por fim, codificamos todos os caracteres em one-hot, além de realizar o deslocamento e adicionar `<eos>`. Em breve veremos por que precisamos de caracteres codificados em one-hot\n",
"\n",
"No entanto, essa função é **Pythonic**, ou seja, ela não pode ser automaticamente traduzida para o grafo computacional do Tensorflow. Receberemos erros se tentarmos usar essa função diretamente na função `Dataset.map`. Precisamos encapsular essa chamada Pythonic usando 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 funções de transformação do Tensorflow pode parecer um pouco complexo demais, e você pode estar se perguntando por que não transformamos o conjunto de dados usando funções padrão do Python antes de passá-lo para `fit`. Embora isso definitivamente possa ser feito, usar `Dataset.map` tem uma grande vantagem, pois 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 nossa rede geradora e começar o treinamento. Ela pode ser baseada em qualquer célula recorrente que discutimos na unidade anterior (simples, LSTM ou GRU). Em 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 lidando com sequências de comprimento variável, podemos usar a camada `Masking` para criar uma máscara que ignorará a parte preenchida da string. Isso não é estritamente necessário, porque não estamos muito interessados em tudo que vai além do token `<eos>`, mas usaremos essa camada para ganhar alguma experiência com esse 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 também será `(None, vocab_size)`, como você 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": [
"## Gerando saída\n",
"\n",
"Agora que treinamos o modelo, queremos usá-lo para gerar alguma saída. Antes de tudo, precisamos de uma maneira de decodificar o 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, ela não funciona bem com tokenização em nível de caracteres. Portanto, vamos pegar um dicionário de tokens do tokenizer (chamado `word_index`), construir um mapa reverso e escrever 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 realizar a geração. Começaremos com uma string `start`, codificaremos ela em uma sequência `inp`, e então, a cada etapa, chamaremos nossa rede para inferir o próximo caractere.\n",
"\n",
"A saída da rede `out` é um vetor de `vocab_size` elementos que representa as probabilidades de cada token, e podemos encontrar o número do token mais provável usando `argmax`. Em seguida, adicionamos esse caractere à lista de tokens gerados e continuamos com a geração. Esse processo de gerar um caractere é repetido `size` vezes para gerar o número necessário de caracteres, e encerramos 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": [
"## Amostrando saída durante o treinamento\n",
"\n",
"Como não temos métricas úteis como *precisão*, a única maneira de verificar se nosso modelo está melhorando é **amostrando** strings geradas durante o treinamento. Para isso, usaremos **callbacks**, ou seja, funções que podemos passar para a função `fit`, e que serão chamadas periodicamente durante o treinamento.\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**. Usamos apenas títulos para nossa tarefa, mas você pode querer experimentar com texto completo. Lembre-se de que RNNs não lidam muito bem com sequências longas, então faz sentido dividi-las em frases mais curtas ou sempre treinar com um comprimento de sequência fixo de algum valor predefinido `num_chars` (por exemplo, 256). Você pode tentar modificar 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 tentar 2 ou 3 camadas de células LSTM. Como mencionamos na unidade anterior, cada camada de LSTM extrai certos padrões do texto, e no caso de um gerador em nível de caracteres, podemos esperar que o nível mais baixo do LSTM seja responsável por extrair sílabas, e os níveis mais altos - por palavras e combinações de palavras. Isso pode ser implementado simplesmente passando o parâmetro de número de camadas para o construtor do LSTM.\n",
"\n",
"* Você também pode querer experimentar com **unidades GRU** e ver quais apresentam melhor desempenho, além de **diferentes tamanhos de camadas ocultas**. Camadas ocultas muito grandes podem resultar em overfitting (por exemplo, a rede aprenderá o texto exato), e tamanhos menores podem 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`, sempre escolhemos o caractere com a maior probabilidade como o próximo caractere no texto gerado. Isso resultava no fato de que o texto frequentemente \"ciclava\" 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 probabilidade para o próximo caractere, pode ser que a diferença entre algumas das maiores probabilidades não seja tão grande. Por exemplo, um caractere pode ter probabilidade de 0,2, enquanto outro tem 0,19, etc. Por exemplo, ao procurar o próximo caractere na sequência '*play*', o próximo caractere pode ser tanto um espaço quanto **e** (como na palavra *player*).\n",
"\n",
"Isso nos leva à conclusão de que não é sempre \"justo\" selecionar o caractere com maior probabilidade, pois escolher o segundo mais provável ainda pode nos levar a um texto significativo. É mais sensato **amostrar** caracteres a partir da distribuição de probabilidade fornecida pela saída da rede.\n",
"\n",
"Essa amostragem pode ser feita usando a função `np.multinomial`, que implementa a chamada **distribuição multinomial**. Uma função que implementa essa 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 rigidamente devemos nos ater à maior probabilidade. Se a temperatura for 1.0, fazemos uma amostragem multinomial justa, e quando a temperatura vai para o infinito - todas as probabilidades se tornam iguais, e selecionamos o próximo caractere aleatoriamente. No exemplo abaixo, podemos observar que o texto se torna sem sentido quando aumentamos demais a temperatura, e se assemelha 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, esteja ciente de que traduções automatizadas podem conter erros ou imprecisões. O documento original em seu idioma nativo deve ser considerado a fonte autoritativa. 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 equivocadas 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-28T14:03:06+00:00",
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb",
"language_code": "br"
}
},
"nbformat": 4,
"nbformat_minor": 4
}