Neste artigo irei demonstrar como utilizar o ML.NET para realizar análise de sentimentos a partir de vídeos utilizando um modelo pré-treinado a partir de um dataset muito conhecido na comunidade o fer2013.

Se você não sabe o que é o ML.NET, veja este post que escrevi para mais detalhes.

No momento em que escrevo esse artigo, ainda não é possível construir modelos de deep learning muito customizados com o ML.NET, entretanto podemoçs nos utilizar de modelos pré-treinados em outras plataformas como TensorFlow por exemplo.

Neste exemplo, irei utilizar um modelo do keras que pode ser encontrado neste link, no caso do ML.NET, ele ainda não reconhece modelos do keras e não temos ainda nenhuma previsão se isso irá acontecer, então precisamos converter nosso modelo para um formato que ele reconheça. Para nossos exemplos, irei utilizar o formato ONNX (Open Neural Networks Exchange) que se trata de uma iniciativa com o intuito de tornar os modelos de IA interoperáveis entre as diversas ferramentas para construção de modelos de machine learning.

Para realizar a conversão para ONNX, utilizei um código em python bem simples:

 

Aqui nós estamos carregando nosso modelo com o keras, convertendo para onnx utilizando a biblioteca onnxtools e salvando o modelo para utilizá-lo mais tarde no ML.NET.

Um detalhe é que o parametro target_opset indica qual versão de modelo onnx você irá utilizar, a última versão é a 11 e você pode utilizá-la sem problemas. Caso queira ver a última versão, acesse este link.

importante: para rodar esse arquivo é necessário ter o python3 instalado. Para executar, basta ir no console e rodar o comando: python converter_keras_to_onnx.py

Após rodar esse código, será criado um arquivo com o nome “modelo_analise_sentimento.onnx”, vamos dar uma olhada nele em uma ferramenta que nos mostra cada camada da nossa rede neural diretamente no browser. Acesse aqui e importe o arquivo .onnx para visualizar.

 

Nossa rede neural

Repare nas partes em vermelho, aqui nos é mostrado os inputs e outputs do nosso modelo, no caso recebemos como input uma variável input_1 com as dimensões 64x64x1, ou seja, uma matriz que representa uma imagem de um canal, nosso output é um array do tipo float com 7 posições com nome predictions/Softmax:0. Vamos utilizar isso em nosso código ML.NET daqui a pouco.

Um detalhe importante, quando fui utilizar o modelo diretamente dessa forma, tive problemas com a variável de output predictions/Softmax:0, pelo que vi parecia algo relacionado a ter barra e dois pontos no nome, para resolver isso, precisei de outro código python para corrigir o modelo:

 

Código para corrigir o modelo

Nesse código, estamos removendo a parte a partir da barra e deixando o nome da variável de saída apenas como predictions.

Após rodar esse código e importar novamente no netron para visualizar a rede, podemos ver o modelo corrigido:

 

Variável de output modificada

Beleza! Vamos começar a mexer no ML.NET, aqui vamos criar um console app para fins didáticos, mas você pode utilizar esse modelo em uma web api, azure function e etc!

Importante: ML.NET ainda não roda na arquitetura ARM, por isso, não é possível rodá-lo diretamente em apps xamarin e dispositivos como raspberry py.

Vamos criar um console app com .NET Core 3.0 com os seguintes pacotes nuget (desculpa, sempre gostei de escrever na mão os comandos nuget):

Install-Package Microsoft.ML -Version 1.4.0-preview2
Install-Package Microsoft.ML.ImageAnalytics -Version 1.4.0-preview2
Install-Package Microsoft.ML.OnnxTransformer -Version 1.4.0-preview2
Install-Package OpenCvSharp4.runtime.win -Version 4.1.1.20191026

Nossa estrutura de pastas ficou assim:

 

Vamos passar por cada uma das classes:
 

A classe InputModeloEmocao.cs é responsável por definir as configurações dos inputs do nosso modelo, se lembra que quando visualizamos a estrutura do modelo vimos que ele recebe uma matriz 64x64x1? Aqui estamos definindo o mesmo tamanho para o ML.NET.

 

A classe ModeloEmocao.cs é uma representação em C# do nosso modelo, aqui definimos a variável de input, output e o caminho do modelo (as variáveis você deve se lembrar da etapa em que estavamos convertendo do keras para onnx).

A classe OutputModeloEmocao.cs é a representação do nosso output, aqui temos uma variável do tipo float (você deve se lembrar novamente da etapa de conversão, onde vimos que ele retornava um float de 7 posições).

 

A classe ConfiguracaoModeloOnnx.cs é responsável pela lógica de todo o pipeline da imagem, o redimensionamento da imagem, conversão para escala de cinza, extração dos pixels até a definição do arquivo a ser utilizado que no caso é nosso arquivo .onnx.

Em nosso arquivo Program.cs, temos um código um pouco mais verboso, entretanto está dessa forma para facilitar a análise posterior, mas é muito fácil em seu projeto você segregar os comportamentos em classes/métodos distintos para cada etapa. No projeto que estou trabalhando atualmente segreguei todos os comportamentos e ficou muito legível!

 

No método Main do nosso console, utilizamos o open cv para capturar imagens da câmera e identificar se possui alguma face na imagem, para evitar chamadas desnecessárias para o nosso modelo e para passar apenas as informações (pixels) da face como o modelo espera.

Quando uma face é encontrada, ela é cortada e passada para o ML.NET, que em seu método predict pega a imagem e roda todo o pipeline de transformação da imagem para então rodar o modelo e nos trazer as informações relacionadas ao sentimento identificado naquela face.

Então, temos o resultado tão esperado:

 

O código está no github abaixo, espero que tenham gostado, até a próxima!

https://github.com/sergioprates/AnaliseDeSentimentosOpenCV.ML.NET

Sergio Prates