Resolvendo equações matemáticas básicas usando RNN [com exemplo de codificação]
Publicados: 2020-12-07Se a vida te der RNN, faça uma calculadora
Uma Rede Neural Recorrente é uma das redes neurais artificiais clássicas, onde as conexões entre os nós formam um grafo direcionado sequencial. As RNNs são famosas por aplicações como reconhecimento de fala, reconhecimento de escrita, etc, devido à sua memória de estado interna para processamento de sequências de comprimento variável.
As RNNs são classificadas em dois tipos. O primeiro é um impulso finito cuja rede neural está na forma de um grafo acíclico direcionado onde um nó pode ser conectado a um ou mais nós que estão à frente sem nenhum ciclo visível na rede. Outro é um impulso infinito cuja rede neural está na forma de um gráfico cíclico direcionado que não pode ser desenrolado em uma rede neural feed-forward.
Índice
O que vamos fazer?
Vamos construir um modelo que prevê a saída de uma expressão aritmética. Por exemplo, se eu der uma entrada '11+88', o modelo deve prever a próxima palavra na sequência como '99'. A entrada e a saída são uma sequência de caracteres, pois uma RNN lida com dados sequenciais.
Agora, projetar a arquitetura do modelo parece uma tarefa simples quando comparada à coleta do conjunto de dados. Gerar dados ou coletar conjuntos de dados é uma tarefa árdua porque os modelos de IA com fome de dados exigem uma quantidade razoável de dados para uma precisão aceitável.
Assim, este modelo pode ser implementado em 6 passos básicos:

- Gerando dados
- Construindo um modelo
- Vetorizando e desvetorizando os dados
- Fazendo um conjunto de dados
- Treinando o modelo
- Testando o modelo
Antes de nos aprofundarmos na implementação do modelo, vamos importar todas as bibliotecas necessárias.
importar numpy como np importe tensorflow como tf de tensorflow.keras.models import Sequencial de tensorflow.keras.layers import Dense, Dropout, SimpleRNN, RepeatVector, TimeDistributed de tensorflow.keras.callbacks import EarlyStopping, LambdaCallback da termcolor importação colorida |
1. Gerando Dados
Vamos definir uma cadeia de caracteres contendo todos os caracteres que precisamos para escrever uma equação aritmética básica. Assim, a string consiste em todos os caracteres de 0-9 e todos os operadores aritméticos como /, *, +, -, .(decimal).
Não podemos alimentar diretamente os dados numéricos em nosso modelo, precisamos passar os dados na forma de tensores. Converter a string nos dados em um vetor codificado one-hot nos dará um desempenho de modelo otimizado. Um vetor codificado one-hot é um array com um comprimento igual ao comprimento de nossa string char, cada vetor one-hot possui apenas no respectivo índice de caractere presente em cada string.
Por exemplo, digamos que nossa string de caracteres seja '0123456789', e se quisermos codificar uma string como '12', o vetor one-hot seria [[0,1,0,0,0,0,0,0 ,0,0], [0,0,1,0,0,0,0,0,0,0] ]. Para fazer isso, precisamos criar dois dicionários com um índice como chaves e chars como valores e o outro como vice-versa.
char_string = ' 0123456789/*+-. ' num_chars = len (char_string) character_to_index = dict ((c, i) for i, c in enumerate (char_string)) index_to_character = dict ((i, c) for i, c in enumerate (char_string)) |
Agora vamos escrever uma função que retorna uma equação aritmética aleatória junto com o resultado dessa equação.
divisão def (n, d): retorne n / d se d != 0 senão 0 def datagen (): random1 = np.random.randint(baixo = 0 , alto = 100 ) random2 = np.random.randint(baixo = 0 , alto = 100 ) op = np.random.randint(baixo = 0 , alto = 4 ) se op == 1 : arith = str (aleatório1) + ' + ' + str (aleatório2) res = str (aleatório1 + aleatório2 ) elif op == 1 : arith = str (aleatório1) + ' – ' + str (aleatório2) res = str (aleatório1 – aleatório2 ) elif op == 2 : arith = str (aleatório1) + ' * ' + str (aleatório2) res = str (random1 * random2) mais : arith = str (aleatório1) + ' / ' + str (aleatório2) res = str ( round (divisão(random1, random2), 2 )) retornar arith, res |
Leia também: Ideias interessantes de projetos de redes neurais
2. Construindo um modelo
O modelo terá um codificador e um decodificador. O codificador é um modelo RNN simples com formato de entrada como (None,num_chars) e 128 unidades ocultas, a razão pela qual escolhemos unidades ocultas como 32,64,128, etc é devido ao melhor desempenho da CPU ou GPU com unidades ocultas como potências de 2.
Nosso codificador será uma rede totalmente conectada e a saída destes será realimentada na rede, é assim que um RNN funciona. Uma camada RNN usa ativação 'tanh' por padrão, não vamos mudar porque ela se ajusta melhor ao codificador. A saída desta camada será um único vetor e para obter um único vetor de toda a saída usaremos a camada RepeatVector() com o número de vezes necessário como parâmetro.
Agora o vetor de saída terá a essência da entrada dada, e este vetor será alimentado no decodificador.
O decodificador é composto por uma camada RNN simples e isso gerará a sequência de saída, pois precisamos que a camada RNN retorne a sequência prevista, vamos sinalizar as 'return_sequences' como True. Ao atribuir o 'return_sequences' como True, a camada RNN retornará a sequência prevista para cada passo de tempo (muitos para muitos RNN).
A saída desta camada RNN é alimentada em uma camada Dense com número 'num_chars' de unidades ocultas e usaremos a ativação softmax, pois precisamos da probabilidade de cada caractere. Antes de implantar uma camada Dense, precisamos abreviar essa camada em uma camada TimeDistributed porque precisamos implantar a camada Dense para a saída de cada etapa de tempo.
unidades_escondidas = 128 max_time_steps = 5 #estamos codificando a saída para ter 5 caracteres modelo def (): modelo = Sequencial() model.add(SimpleRNN(hidden_units, input_shape = ( None , num_chars))) model.add(RepeatVector(max_time_steps)) model.add(SimpleRNN(hidden_units, return_sequences = True )) model.add(TimeDistributed(Dense(num_chars, ativação = ' softmax ' ))) modelo de devolução modelo = modelo() model.summary() model.compile(loss = ' categorical_crossentropy ' , otimizador = ' adam ' , métricas = [ ' precisão ' ]) |
A arquitetura do modelo será como mostrado acima
Deve ler: Tutorial de rede neural
3. Vetorização e desvetorização dos dados
Vamos definir funções para vetorizar e desvetorizar os dados.
Aqui está a função para vetorizar a expressão aritmética e o resultado juntos.
def vetorizar (arith, res): x = np.zeros((max_time_steps, num_chars)) y = np.zeros((max_time_steps, num_chars)) x_remaining = max_time_steps – len (arith) ![]() y_remaining = max_time_steps – len (res) para i, c em enumerar (arith): x[x_remaining + i, character_to_index[c]] = 1 para i no intervalo (x_remaining): x[i, character_to_index[ ' 0 ' ]] = 1 para i, c em enumerar (res): y[y_remaining + i, character_to_index[c]] = 1 para i no intervalo (y_remaining): y[i, character_to_index[ ' 0 ' ]] = 1 retorna x, y |
Da mesma forma, aqui está a função para desvetorizar a string. Como a saída que recebemos é um vetor de probabilidades, usaremos np.argmax() para escolher o caractere com maior probabilidade. Agora o dicionário index_to_character é usado para rastrear o caractere nesse índice.
def devectorize (entrada): res = [index_to_character[np.argmax(vec)] for i, vec in enumerate ( input )] return ' ' .join(res) |
Agora, a restrição que temos com a função 'devectorize' é preencher os caracteres à direita com zeros. Por exemplo, se o vetor de entrada for ('1-20', '-19'), a saída desvetorizada será ('01-20', '00-19'). Precisamos cuidar desses zeros acolchoados extras. Vamos escrever uma função para remover a string.
def stripping (entrada): bandeira = falso saída = ' ' para c na entrada : se não sinalizar e c == ' 0 ' : Prosseguir se c == ' + ' ou c == ' – ' ou c == ' * ' ou c == ' / ' ou c == ' . ' : bandeira = falso mais : bandeira = Verdadeiro saída += c saída de retorno |
4. Criando um conjunto de dados
Agora que terminamos de definir uma função para gerar os dados, vamos usar essa função e criar um conjunto de dados com muitos desses pares (expressão aritmética, resultado).
def create_dataset (num_equations): x_train = np.zeros((num_equations, max_time_steps, num_chars)) y_train = np.zeros((num_equations, max_time_steps, num_chars)) para i no intervalo (num_equations): e, l = datagen() x, y = vetorizar(e, l) x_train[i] = x y_train[i] = y return x_train, y_train |
5. Treinamento do Modelo
Vamos criar um conjunto de dados de 50.000 amostras que é um número justo para treinar nosso modelo de fome de dados, usaremos 25% desses dados para validação. Além disso, vamos criar um retorno de chamada para interrupção de treinamento inteligente se a precisão permanecer inalterada por 8 épocas. Isso pode ser alcançado definindo o parâmetro de paciência para 8.

x_train, y_train = create_dataset( 50000 ) simple_logger = LambdaCallback( on_epoch_end = lambda e, l: print ( ' {:.2f} ' .format(l[ ' val_accuracy ' ]), end = ' _ ' ) ) early_stopping = EarlyStopping(monitor = ' val_loss ' , paciência = 8 ) model.fit(x_train, y_train, epochs = 100 , validation_split = 0.25 , verbose = 0 , callbacks = [simple_logger, early_stopping]) |
6. Testando o Modelo
Agora vamos testar nosso modelo criando um conjunto de dados do tamanho 30.
x_test, y_test = create_dataset(num_equations = 20 ) preds = model.predict(x_test) full_seq_acc = 0 para i, pred in enumerate (preds): pred_str = stripping(devectorize(pred)) y_test_str = stripping(devectorize(y_test[i])) x_test_str = stripping(devectorize(x_test[i])) col = ' verde ' if pred_str == y_test_str else ' vermelho ' full_seq_acc += 1 / len (preds) * int (pred_str == y_test_str) outstring = ' Entrada: {}, Saída: {}, Previsão: {} ' .format(x_test_str, y_test_str, pred_str) print (colorido(outstring, col)) print ( ' \n Precisão da sequência completa: {:.3f} % ' .format( 100 * full_seq_acc)) |
A saída será a seguinte
Podemos ver que a precisão é um pouco ruim aqui, de qualquer forma, podemos otimizá-la ajustando alguns hiperparâmetros como o número de unidades ocultas, divisão de validação, número de épocas, etc.
Conclusão
Entendemos o fluxo de trabalho básico de uma RNN, entendemos que as RNNs são mais adequadas para dados sequenciais, geramos um conjunto de dados de equações aritméticas aleatórias, desenvolvemos um modelo sequencial para prever a saída de uma expressão aritmética básica, treinamos esse modelo com o conjunto de dados que criamos e, finalmente, testamos esse modelo com um pequeno conjunto de dados que o modelo nunca viu antes.
Se você estiver interessado em saber mais sobre RNN, aprendizado de máquina, confira o Diploma PG do IIIT-B e do upGrad em aprendizado de máquina e IA, projetado para profissionais que trabalham e oferece mais de 450 horas de treinamento rigoroso, mais de 30 estudos de caso e atribuições, Status de ex-aluno do IIIT-B, mais de 5 projetos práticos práticos e assistência de trabalho com as principais empresas.
Quais são os diferentes tipos de redes neurais no aprendizado de máquina?
No aprendizado de máquina, as redes neurais artificiais são basicamente modelos computacionais que foram projetados para se assemelhar ao cérebro humano. Existem diferentes tipos de redes neurais artificiais que o aprendizado de máquina emprega com base na computação matemática que precisa ser alcançada. Essas redes neurais são um subconjunto de diferentes técnicas de aprendizado de máquina que aprendem com os dados de maneiras diferentes. Alguns dos tipos mais utilizados de redes neurais são – rede neural recorrente – memória de longo prazo, rede neural feedforward – neurônio artificial, rede neural de função de base radial, rede neural auto-organizada de Kohonen, rede neural convolucional e rede neural modular, entre outros.
Quais são as vantagens de uma rede neural recorrente?
As redes neurais recorrentes estão entre as redes neurais artificiais mais usadas em aprendizado profundo e aprendizado de máquina. Nesse tipo de modelo de rede neural, o resultado obtido na etapa anterior é alimentado como entrada para a etapa subsequente. Uma rede neural recorrente vem com várias vantagens, como - ela pode reter todas as informações ao longo do tempo, incluindo suas entradas anteriores, o que a torna ideal para previsão de séries temporais. Esse tipo é a melhor instância de memória longa e curta. Além disso, as redes neurais recorrentes fornecem vizinhança de pixel construtiva usando camadas convolucionais.
Como as redes neurais são empregadas em aplicações do mundo real?
As redes neurais artificiais são parte integrante do aprendizado profundo, que novamente é um ramo superespecializado de aprendizado de máquina e inteligência artificial. As redes neurais são usadas em diferentes indústrias para atingir vários objetivos críticos. Algumas das aplicações reais mais interessantes de redes neurais artificiais incluem previsão do mercado de ações, reconhecimento facial, piloto automático de alto desempenho e diagnóstico de falhas na indústria aeroespacial, análise de ataques armados e localização de objetos no setor de defesa, processamento de imagens, descoberta de medicamentos e detecção de doenças no setor de saúde, verificação de assinatura, análise de caligrafia, previsão do tempo e previsão de tendências de mídia social, entre outros.