460 lines
24 KiB
Plaintext
460 lines
24 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Redes neurais recorrentes\n",
|
|
"\n",
|
|
"No módulo anterior, abordamos representações semânticas ricas de texto. A arquitetura que temos usado captura o significado agregado das palavras em uma sentença, mas não leva em conta a **ordem** das palavras, porque a operação de agregação que segue as embeddings remove essa informação do texto original. Como esses modelos não conseguem representar a ordem das palavras, eles não podem resolver tarefas mais complexas ou ambíguas, como geração de texto ou resposta a perguntas.\n",
|
|
"\n",
|
|
"Para capturar o significado de uma sequência de texto, usaremos uma arquitetura de rede neural chamada **rede neural recorrente**, ou RNN. Ao usar uma RNN, passamos nossa sentença pela rede um token de cada vez, e a rede produz algum **estado**, que então passamos novamente para a rede junto com o próximo token.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Dada a sequência de entrada de tokens $X_0,\\dots,X_n$, a RNN cria uma sequência de blocos de rede neural e treina essa sequência de ponta a ponta usando retropropagação. Cada bloco de rede recebe um par $(X_i,S_i)$ como entrada e produz $S_{i+1}$ como resultado. O estado final $S_n$ ou a saída $Y_n$ é enviado para um classificador linear para produzir o resultado. Todos os blocos de rede compartilham os mesmos pesos e são treinados de ponta a ponta usando uma única passagem de retropropagação.\n",
|
|
"\n",
|
|
"> A figura acima mostra a rede neural recorrente na forma expandida (à esquerda) e em uma representação recorrente mais compacta (à direita). É importante perceber que todas as células RNN têm os mesmos **pesos compartilháveis**.\n",
|
|
"\n",
|
|
"Como os vetores de estado $S_0,\\dots,S_n$ são passados pela rede, a RNN é capaz de aprender dependências sequenciais entre palavras. Por exemplo, quando a palavra *não* aparece em algum lugar da sequência, ela pode aprender a negar certos elementos dentro do vetor de estado.\n",
|
|
"\n",
|
|
"Internamente, cada célula RNN contém duas matrizes de peso: $W_H$ e $W_I$, e um viés $b$. Em cada etapa da RNN, dado o input $X_i$ e o estado de entrada $S_i$, o estado de saída é calculado como $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, onde $f$ é uma função de ativação (frequentemente $\\tanh$).\n",
|
|
"\n",
|
|
"> Para problemas como geração de texto (que abordaremos na próxima unidade) ou tradução automática, também queremos obter algum valor de saída em cada etapa da RNN. Nesse caso, há também outra matriz $W_O$, e a saída é calculada como $Y_i=f(W_O\\times S_i+b_O)$.\n",
|
|
"\n",
|
|
"Vamos ver como redes neurais recorrentes podem nos ajudar a classificar nosso conjunto de dados de notícias.\n",
|
|
"\n",
|
|
"> Para o ambiente de sandbox, precisamos executar a célula a seguir para garantir que a biblioteca necessária esteja instalada e os dados sejam pré-carregados. Se você estiver executando localmente, pode ignorar a célula a seguir.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import sys\n",
|
|
"!{sys.executable} -m pip install --quiet tensorflow_datasets==4.4.0\n",
|
|
"!cd ~ && wget -q -O - https://mslearntensorflowlp.blob.core.windows.net/data/tfds-ag-news.tgz | tar xz"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"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",
|
|
"# We are going to be training pretty large models. In order not to face errors, we need\n",
|
|
"# to set tensorflow option to grow GPU memory allocation when required\n",
|
|
"physical_devices = tf.config.list_physical_devices('GPU') \n",
|
|
"if len(physical_devices)>0:\n",
|
|
" tf.config.experimental.set_memory_growth(physical_devices[0], True)\n",
|
|
"\n",
|
|
"ds_train, ds_test = tfds.load('ag_news_subset').values()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"nteract": {
|
|
"transient": {
|
|
"deleting": false
|
|
}
|
|
}
|
|
},
|
|
"source": [
|
|
"Ao treinar modelos grandes, a alocação de memória da GPU pode se tornar um problema. Também pode ser necessário experimentar diferentes tamanhos de minibatch, para que os dados caibam na memória da GPU e o treinamento seja rápido o suficiente. Se você estiver executando este código em sua própria máquina com GPU, pode experimentar ajustar o tamanho do minibatch para acelerar o treinamento.\n",
|
|
"\n",
|
|
"> **Note**: Certas versões de drivers da NVidia são conhecidas por não liberar a memória após o treinamento do modelo. Estamos executando vários exemplos neste notebook, e isso pode causar o esgotamento da memória em algumas configurações, especialmente se você estiver realizando seus próprios experimentos como parte do mesmo notebook. Se você encontrar erros estranhos ao iniciar o treinamento do modelo, pode ser necessário reiniciar o kernel do notebook.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"metadata": {
|
|
"collapsed": true,
|
|
"jupyter": {
|
|
"outputs_hidden": false,
|
|
"source_hidden": false
|
|
},
|
|
"nteract": {
|
|
"transient": {
|
|
"deleting": false
|
|
}
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"batch_size = 16\n",
|
|
"embed_size = 64"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Classificador RNN simples\n",
|
|
"\n",
|
|
"No caso de uma RNN simples, cada unidade recorrente é uma rede linear simples, que recebe um vetor de entrada e um vetor de estado, e produz um novo vetor de estado. No Keras, isso pode ser representado pela camada `SimpleRNN`.\n",
|
|
"\n",
|
|
"Embora possamos passar tokens codificados em one-hot diretamente para a camada RNN, isso não é uma boa ideia devido à sua alta dimensionalidade. Por isso, utilizaremos uma camada de embedding para reduzir a dimensionalidade dos vetores de palavras, seguida por uma camada RNN e, por fim, um classificador `Dense`.\n",
|
|
"\n",
|
|
"> **Note**: Em casos onde a dimensionalidade não é tão alta, por exemplo, ao usar tokenização a nível de caracteres, pode fazer sentido passar tokens codificados em one-hot diretamente para a célula RNN.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Model: \"sequential\"\n",
|
|
"_________________________________________________________________\n",
|
|
"Layer (type) Output Shape Param # \n",
|
|
"=================================================================\n",
|
|
"text_vectorization (TextVect (None, None) 0 \n",
|
|
"_________________________________________________________________\n",
|
|
"embedding (Embedding) (None, None, 64) 1280000 \n",
|
|
"_________________________________________________________________\n",
|
|
"simple_rnn (SimpleRNN) (None, 16) 1296 \n",
|
|
"_________________________________________________________________\n",
|
|
"dense (Dense) (None, 4) 68 \n",
|
|
"=================================================================\n",
|
|
"Total params: 1,281,364\n",
|
|
"Trainable params: 1,281,364\n",
|
|
"Non-trainable params: 0\n",
|
|
"_________________________________________________________________\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"vocab_size = 20000\n",
|
|
"\n",
|
|
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(\n",
|
|
" max_tokens=vocab_size,\n",
|
|
" input_shape=(1,))\n",
|
|
"\n",
|
|
"model = keras.models.Sequential([\n",
|
|
" vectorizer,\n",
|
|
" keras.layers.Embedding(vocab_size, embed_size),\n",
|
|
" keras.layers.SimpleRNN(16),\n",
|
|
" keras.layers.Dense(4,activation='softmax')\n",
|
|
"])\n",
|
|
"\n",
|
|
"model.summary()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"> **Nota:** Aqui usamos uma camada de embedding não treinada para simplificar, mas para obter melhores resultados, podemos usar uma camada de embedding pré-treinada com Word2Vec, como descrito na unidade anterior. Seria um bom exercício para você adaptar este código para funcionar com embeddings pré-treinados.\n",
|
|
"\n",
|
|
"Agora vamos treinar nossa RNN. RNNs, em geral, são bastante difíceis de treinar, porque, uma vez que as células da RNN são desenroladas ao longo do comprimento da sequência, o número resultante de camadas envolvidas na retropropagação é bastante grande. Por isso, precisamos selecionar uma taxa de aprendizado menor e treinar a rede em um conjunto de dados maior para obter bons resultados. Isso pode levar bastante tempo, então é preferível usar uma GPU.\n",
|
|
"\n",
|
|
"Para acelerar o processo, treinaremos o modelo RNN apenas nos títulos das notícias, omitindo a descrição. Você pode tentar treinar com a descrição e ver se consegue fazer o modelo treinar.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"metadata": {
|
|
"scrolled": true
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Training vectorizer\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"def extract_title(x):\n",
|
|
" return x['title']\n",
|
|
"\n",
|
|
"def tupelize_title(x):\n",
|
|
" return (extract_title(x),x['label'])\n",
|
|
"\n",
|
|
"print('Training vectorizer')\n",
|
|
"vectorizer.adapt(ds_train.take(2000).map(extract_title))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"7500/7500 [==============================] - 82s 11ms/step - loss: 0.6629 - acc: 0.7623 - val_loss: 0.5559 - val_acc: 0.7995\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<tensorflow.python.keras.callbacks.History at 0x7f3e0030d350>"
|
|
]
|
|
},
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
|
"model.fit(ds_train.map(tupelize_title).batch(batch_size),validation_data=ds_test.map(tupelize_title).batch(batch_size))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"nteract": {
|
|
"transient": {
|
|
"deleting": false
|
|
}
|
|
}
|
|
},
|
|
"source": [
|
|
"> **Nota** que a precisão provavelmente será menor aqui, porque estamos treinando apenas em títulos de notícias.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Revisitando sequências de variáveis\n",
|
|
"\n",
|
|
"Lembre-se de que a camada `TextVectorization` irá automaticamente preencher sequências de comprimento variável em um minibatch com tokens de preenchimento (pad tokens). Acontece que esses tokens também participam do treinamento, e isso pode complicar a convergência do modelo.\n",
|
|
"\n",
|
|
"Existem várias abordagens que podemos adotar para minimizar a quantidade de preenchimento. Uma delas é reordenar o conjunto de dados pelo comprimento das sequências e agrupar todas as sequências por tamanho. Isso pode ser feito usando a função `tf.data.experimental.bucket_by_sequence_length` (veja a [documentação](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
|
|
"\n",
|
|
"Outra abordagem é usar **masking**. No Keras, algumas camadas suportam entradas adicionais que indicam quais tokens devem ser considerados durante o treinamento. Para incorporar masking em nosso modelo, podemos incluir uma camada `Masking` separada ([documentação](https://keras.io/api/layers/core_layers/masking/)) ou especificar o parâmetro `mask_zero=True` na nossa camada `Embedding`.\n",
|
|
"\n",
|
|
"> **Note**: Este treinamento levará cerca de 5 minutos para completar uma época em todo o conjunto de dados. Sinta-se à vontade para interromper o treinamento a qualquer momento se perder a paciência. Outra opção é limitar a quantidade de dados usados para o treinamento, adicionando a cláusula `.take(...)` após os conjuntos de dados `ds_train` e `ds_test`.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"7500/7500 [==============================] - 371s 49ms/step - loss: 0.5401 - acc: 0.8079 - val_loss: 0.3780 - val_acc: 0.8822\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<tensorflow.python.keras.callbacks.History at 0x7f3dec118850>"
|
|
]
|
|
},
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"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",
|
|
"model = keras.models.Sequential([\n",
|
|
" vectorizer,\n",
|
|
" keras.layers.Embedding(vocab_size,embed_size,mask_zero=True),\n",
|
|
" keras.layers.SimpleRNN(16),\n",
|
|
" keras.layers.Dense(4,activation='softmax')\n",
|
|
"])\n",
|
|
"\n",
|
|
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
|
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Agora que estamos utilizando mascaramento, podemos treinar o modelo em todo o conjunto de dados de títulos e descrições.\n",
|
|
"\n",
|
|
"> **Note**: Você percebeu que estamos usando um vetorizador treinado nos títulos das notícias, e não no corpo completo do artigo? Isso pode fazer com que alguns tokens sejam ignorados, então é melhor re-treinar o vetorizador. No entanto, isso provavelmente terá um efeito muito pequeno, então vamos continuar utilizando o vetorizador pré-treinado anterior para manter a simplicidade.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## LSTM: Memória de longo e curto prazo\n",
|
|
"\n",
|
|
"Um dos principais problemas das RNNs é o **desvanecimento de gradientes**. As RNNs podem ser bastante longas e podem ter dificuldade em propagar os gradientes até a primeira camada da rede durante a retropropagação. Quando isso acontece, a rede não consegue aprender relações entre tokens distantes. Uma maneira de evitar esse problema é introduzir **gerenciamento explícito de estado** usando **portas**. As duas arquiteturas mais comuns que introduzem portas são **memória de longo e curto prazo** (LSTM) e **unidade de retransmissão com portas** (GRU). Vamos abordar as LSTMs aqui.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Uma rede LSTM é organizada de maneira semelhante a uma RNN, mas há dois estados que são passados de camada para camada: o estado real $c$ e o vetor oculto $h$. Em cada unidade, o vetor oculto $h_{t-1}$ é combinado com a entrada $x_t$, e juntos eles controlam o que acontece com o estado $c_t$ e a saída $h_{t}$ por meio de **portas**. Cada porta tem ativação sigmoide (saída no intervalo $[0,1]$), que pode ser vista como uma máscara binária quando multiplicada pelo vetor de estado. As LSTMs possuem as seguintes portas (da esquerda para a direita na imagem acima):\n",
|
|
"* **porta de esquecimento**, que determina quais componentes do vetor $c_{t-1}$ precisamos esquecer e quais passar adiante.\n",
|
|
"* **porta de entrada**, que determina quanta informação do vetor de entrada e do vetor oculto anterior deve ser incorporada ao vetor de estado.\n",
|
|
"* **porta de saída**, que pega o novo vetor de estado e decide quais de seus componentes serão usados para produzir o novo vetor oculto $h_t$.\n",
|
|
"\n",
|
|
"Os componentes do estado $c$ podem ser vistos como indicadores que podem ser ativados ou desativados. Por exemplo, quando encontramos o nome *Alice* na sequência, deduzimos que se refere a uma mulher e ativamos o indicador no estado que diz que temos um substantivo feminino na frase. Quando encontramos posteriormente as palavras *e Tom*, ativamos o indicador que diz que temos um substantivo no plural. Assim, manipulando o estado, podemos acompanhar as propriedades gramaticais da frase.\n",
|
|
"\n",
|
|
"> **Note**: Aqui está um ótimo recurso para entender os detalhes internos das LSTMs: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) por Christopher Olah.\n",
|
|
"\n",
|
|
"Embora a estrutura interna de uma célula LSTM possa parecer complexa, o Keras oculta essa implementação dentro da camada `LSTM`, então a única coisa que precisamos fazer no exemplo acima é substituir a camada recorrente:\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"15000/15000 [==============================] - 188s 13ms/step - loss: 0.5692 - acc: 0.7916 - val_loss: 0.3441 - val_acc: 0.8870\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<tensorflow.python.keras.callbacks.History at 0x7f3d6af5c350>"
|
|
]
|
|
},
|
|
"execution_count": 8,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"model = keras.models.Sequential([\n",
|
|
" vectorizer,\n",
|
|
" keras.layers.Embedding(vocab_size, embed_size),\n",
|
|
" keras.layers.LSTM(8),\n",
|
|
" keras.layers.Dense(4,activation='softmax')\n",
|
|
"])\n",
|
|
"\n",
|
|
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
|
"model.fit(ds_train.map(tupelize).batch(8),validation_data=ds_test.map(tupelize).batch(8))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## RNNs Bidirecionais e Multicamadas\n",
|
|
"\n",
|
|
"Nos nossos exemplos até agora, as redes recorrentes operam do início de uma sequência até o final. Isso nos parece natural porque segue a mesma direção em que lemos ou ouvimos uma fala. No entanto, para cenários que exigem acesso aleatório à sequência de entrada, faz mais sentido executar o cálculo recorrente em ambas as direções. RNNs que permitem cálculos em ambas as direções são chamadas de **RNNs bidirecionais**, e podem ser criadas envolvendo a camada recorrente com uma camada especial chamada `Bidirectional`.\n",
|
|
"\n",
|
|
"> **Note**: A camada `Bidirectional` faz duas cópias da camada dentro dela e define a propriedade `go_backwards` de uma dessas cópias como `True`, fazendo com que ela percorra a sequência na direção oposta.\n",
|
|
"\n",
|
|
"Redes recorrentes, sejam unidirecionais ou bidirecionais, capturam padrões dentro de uma sequência e os armazenam em vetores de estado ou os retornam como saída. Assim como nas redes convolucionais, podemos construir outra camada recorrente após a primeira para capturar padrões de nível mais alto, construídos a partir de padrões de nível mais baixo extraídos pela primeira camada. Isso nos leva à noção de uma **RNN multicamada**, que consiste em duas ou mais redes recorrentes, onde a saída da camada anterior é passada para a próxima camada como entrada.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"*Imagem retirada [deste post incrível](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) por Fernando López.*\n",
|
|
"\n",
|
|
"O Keras torna a construção dessas redes uma tarefa fácil, porque você só precisa adicionar mais camadas recorrentes ao modelo. Para todas as camadas, exceto a última, precisamos especificar o parâmetro `return_sequences=True`, porque precisamos que a camada retorne todos os estados intermediários, e não apenas o estado final do cálculo recorrente.\n",
|
|
"\n",
|
|
"Vamos construir uma LSTM bidirecional de duas camadas para o nosso problema de classificação.\n",
|
|
"\n",
|
|
"> **Note** este código novamente leva bastante tempo para ser executado, mas nos dá a maior precisão que vimos até agora. Então talvez valha a pena esperar e ver o resultado.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"5044/7500 [===================>..........] - ETA: 2:33 - loss: 0.3709 - acc: 0.8706\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r5045/7500 [===================>..........] - ETA: 2:33 - loss: 0.3709 - acc: 0.8706"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"model = keras.models.Sequential([\n",
|
|
" vectorizer,\n",
|
|
" keras.layers.Embedding(vocab_size, 128, mask_zero=True),\n",
|
|
" keras.layers.Bidirectional(keras.layers.LSTM(64,return_sequences=True)),\n",
|
|
" keras.layers.Bidirectional(keras.layers.LSTM(64)), \n",
|
|
" keras.layers.Dense(4,activation='softmax')\n",
|
|
"])\n",
|
|
"\n",
|
|
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
|
"model.fit(ds_train.map(tupelize).batch(batch_size),\n",
|
|
" validation_data=ds_test.map(tupelize).batch(batch_size))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## RNNs para outras tarefas\n",
|
|
"\n",
|
|
"Até agora, nos concentramos em usar RNNs para classificar sequências de texto. Mas elas podem lidar com muitas outras tarefas, como geração de texto e tradução automática — abordaremos essas tarefas na próxima unidade.\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": {
|
|
"kernel_info": {
|
|
"name": "conda-env-py37_tensorflow-py"
|
|
},
|
|
"kernelspec": {
|
|
"display_name": "py37_tensorflow",
|
|
"language": "python",
|
|
"name": "conda-env-py37_tensorflow-py"
|
|
},
|
|
"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.7.9"
|
|
},
|
|
"nteract": {
|
|
"version": "nteract-front-end@1.0.0"
|
|
},
|
|
"coopTranslator": {
|
|
"original_hash": "81351e61f619b432ff51010a4f993194",
|
|
"translation_date": "2025-08-28T14:20:08+00:00",
|
|
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
|
|
"language_code": "br"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |