Este bloco de notas classifica resenhas de filmes como positivas ou negativas usando o texto da resenha. Este é um exemplo de classificação binária , um tipo de problema de aprendizado de máquina importante e amplamente aplicável.
Demonstraremos o uso de regularização de gráfico neste notebook, construindo um gráfico a partir da entrada fornecida. A receita geral para construir um modelo regularizado por gráfico usando o framework Neural Structured Learning (NSL) quando a entrada não contém um gráfico explícito é a seguinte:
Crie embeddings para cada amostra de texto na entrada. Isso pode ser feito usando modelos pré-treinados, como word2vec , Swivel , BERT etc.
Construa um gráfico com base nesses embeddings usando uma métrica de similaridade, como a distância 'L2', distância 'cosseno' etc. Os nós no gráfico correspondem a amostras e as bordas no gráfico correspondem à similaridade entre pares de amostras.
Nota: Esperamos que os leitores levem cerca de 1 hora para ler este tutorial.
O conjunto de dados IMDB contém o texto de 50.000 resenhas de filmes do Internet Movie Database . Elas são divididas em 25.000 análises para treinamento e 25.000 análises para teste. Os conjuntos de treinamento e teste são equilibrados , o que significa que contêm um número igual de avaliações positivas e negativas.
O argumento num_words=10000 mantém as 10.000 palavras que ocorrem com mais frequência nos dados de treinamento. As palavras raras são descartadas para manter o tamanho do vocabulário gerenciável.
Vamos dedicar um momento para entender o formato dos dados. O conjunto de dados vem pré-processado: cada exemplo é um array de inteiros que representam as palavras da crítica do filme. Cada rótulo é um valor inteiro de 0 ou 1, em que 0 é uma resenha negativa e 1 é uma resenha positiva.
Pode ser útil saber como converter números inteiros de volta ao texto correspondente. Aqui, criaremos uma função auxiliar para consultar um objeto de dicionário que contém o mapeamento inteiro para string:
Agora podemos usar a função decode_review para exibir o texto da primeira revisão:
Usaremos embeddings Swivel pré-treinados para criar embeddings no formato tf.train.Example para cada amostra na entrada. Armazenaremos os embeddings resultantes no formato TFRecord junto com um recurso adicional que representa o ID de cada amostra. Isso é importante e nos permitirá combinar os embeddings de amostra com os nós correspondentes no gráfico posteriormente.
O Neural Structured Learning fornece uma biblioteca de construção de gráficos para construir um gráfico baseado em exemplos de incorporação. Ele usa similaridade de cosseno como medida de similaridade para comparar embeddings e construir arestas entre eles. Também nos permite especificar um limite de similaridade, que pode ser usado para descartar arestas diferentes do gráfico final. Neste exemplo, usando 0,99 como o limite de similaridade e 12345 como a semente aleatória, terminamos com um gráfico que tem 429.415 arestas bidirecionais. Aqui, estamos usando o suporte do construtor de gráficos para hashing sensível à localidade (LSH) para acelerar a construção de gráficos. Para obter detalhes sobre como usar o suporte LSH do construtor gráfico, consulte a documentação da API build_graph_from_config .
Observação: a qualidade do gráfico e, por extensão, a qualidade de incorporação, são muito importantes para a regularização do gráfico. Embora tenhamos usado embeddings Swivel neste notebook, usar embeddings BERT, por exemplo, provavelmente irá capturar a semântica da revisão com mais precisão. Nós encorajamos os usuários a usarem embeddings de sua escolha e conforme apropriado às suas necessidades.
Criamos recursos de amostra para nosso problema usando o formato tf.train.Example e os TFRecord formato TFRecord . Cada amostra incluirá os três recursos a seguir:
id : o ID do nó da amostra.
palavras : uma lista int64 contendo IDs de palavras.
rótulo : Um singleton int64 identificando a classe alvo da revisão.
num_classes : Existem 2 classes - positiva e negativa .
max_seq_length : Este é o número máximo de palavras consideradas para cada crítica de filme neste exemplo.
vocab_size : este é o tamanho do vocabulário considerado para este exemplo.
num_neighs : o número de vizinhos usados ​​para regularização do gráfico. Este valor deve ser menor ou igual ao argumento max_nbrs usado acima ao invocarnsl.tools.pack_nbrs .
num_fc_units : O número de unidades na camada totalmente conectada da rede neural.
Converta as matrizes em vetores de 0 1 s, indicando a ocorrência da palavra, semelhante a uma codificação one-hot. Por exemplo, a sequência [3, 5] se tornaria um vetor de 10000 dimensões que são todos zeros, exceto para os índices 3 e 5 , que são uns. Em seguida, torne esta a primeira camada em nossa rede - uma camada Dense - que pode lidar com dados vetoriais de ponto flutuante. Essa abordagem num_words * num_reviews memória, porém, requer uma matriz de tamanho num_words * num_reviews .
Como alternativa, podemos preencher os arrays para que todos tenham o mesmo comprimento e, em seguida, criar um tensor inteiro de formato max_length * num_reviews . Podemos usar uma camada de incorporação capaz de lidar com essa forma como a primeira camada em nossa rede.
Como as resenhas do filme devem ter a mesma duração, usaremos a função pad_sequence definida abaixo para padronizar as durações.
A primeira camada é uma camada de Input que leva o vocabulário codificado por inteiro.
A próxima camada é uma camada de Embedding , que pega o vocabulário codificado por inteiro e procura o vetor de incorporação para cada índice de palavra. Esses vetores são aprendidos à medida que o modelo treina. Os vetores adicionam uma dimensão ao array de saída. As dimensões resultantes são: (batch, sequence, embedding) .
Este vetor de saída de comprimento fixo é canalizado por meio de uma camada totalmente conectada ( Dense ) com 64 unidades ocultas.
A última camada está densamente conectada a um único nó de saída. Usando a função de ativação sigmoid , este valor é uma flutuação entre 0 e 1, representando uma probabilidade ou nível de confiança.
O modelo acima tem duas camadas intermediárias ou "ocultas", entre a entrada e a saída, e excluindo a camada de Embedding . O número de saídas (unidades, nós ou neurônios) é a dimensão do espaço representacional para a camada. Em outras palavras, a quantidade de liberdade que a rede tem ao aprender uma representação interna.
Se um modelo tiver mais unidades ocultas (um espaço de representação de dimensão superior) e / ou mais camadas, a rede pode aprender representações mais complexas. No entanto, torna a rede mais cara computacionalmente e pode levar ao aprendizado de padrões indesejados - padrões que melhoram o desempenho nos dados de treinamento, mas não nos dados de teste. Isso é chamado de overfitting .
Um modelo precisa de uma função de perda e um otimizador para treinamento. Como este é um problema de classificação binária e o modelo gera uma probabilidade (uma camada de unidade única com ativação sigmóide), usaremos a função de perda binary_crossentropy .
Ao treinar, queremos verificar a precisão do modelo em dados que não vimos antes. Crie um conjunto de validação separando uma fração dos dados de treinamento originais. (Por que não usar o conjunto de teste agora? Nosso objetivo é desenvolver e ajustar nosso modelo usando apenas os dados de treinamento, depois usar os dados de teste apenas uma vez para avaliar nossa precisão)
model.fit() retorna um objeto History que contém um dicionário com tudo o que aconteceu durante o treinamento:
Observe que a perda de treinamento diminui a cada época e a precisão do treinamento aumenta a cada época. Isso é esperado ao usar uma otimização de gradiente descendente - deve minimizar a quantidade desejada em cada iteração.
Agora estamos prontos para tentar a regularização de gráfico usando o modelo básico que construímos acima. Usaremos a classe wrapper GraphRegularization fornecida pelo framework Neural Structured Learning para envolver o modelo básico (bi-LSTM) para incluir a regularização de gráfico. O restante das etapas para treinar e avaliar o modelo regularizado por gráfico são semelhantes às do modelo básico.
Criar modelo regularizado por gráfico
Para avaliar o benefício incremental da regularização do gráfico, criaremos uma nova instância do modelo base. Isso ocorre porque o model já foi treinado para algumas iterações e reutilizar esse modelo treinado para criar um modelo regularizado por gráfico não será uma comparação justa para o model .
Definimos a proporção de supervisão como a proporção de amostras de treinamento para o número total de amostras que inclui treinamento, validação e amostras de teste. Neste notebook, usamos uma taxa de supervisão de 0,05 (ou seja, 5% dos dados rotulados) para treinar tanto o modelo básico quanto o modelo regularizado por gráfico. Ilustramos o impacto da taxa de supervisão na precisão do modelo na célula abaixo.
Pode-se observar que conforme a razão de superivisão diminui, a precisão do modelo também diminui. Isso é verdadeiro para o modelo básico e para o modelo regularizado por gráfico, independentemente da arquitetura do modelo usada. No entanto, observe que o modelo regularizado por gráfico tem um desempenho melhor do que o modelo básico para ambas as arquiteturas. Em particular, para o modelo Bi-LSTM, quando a taxa de supervisão é 0,01, a precisão do modelo regularizado por gráfico é ~ 20% maior do que a do modelo base. Isso se deve principalmente ao aprendizado semissupervisionado para o modelo regularizado por gráfico, em que a similaridade estrutural entre as amostras de treinamento é usada além das próprias amostras de treinamento.
Demonstramos o uso de regularização de gráfico usando o framework Neural Structured Learning (NSL), mesmo quando a entrada não contém um gráfico explícito. Consideramos a tarefa de classificação de sentimento das resenhas de filmes IMDB para as quais sintetizamos um gráfico de similaridade com base em embeddings de resenhas. Incentivamos os usuários a experimentar mais, variando hiperparâmetros, a quantidade de supervisão e usando diferentes arquiteturas de modelo.