Realizando melhorias estruturais em nosso projeto de machine learning ou deep learning com PyTorch: Parte 2/3

Olá, pessoal. Tudo bem? Espero que sim.

No episódio anterior, eu escrevi sobre como fundamentar a estrutura de um projeto de Machine Learning e Deep Learning com Pytorch. Se você ainda não leu esse post, eu aconselho que você o faça agora, visto que as informações disponibilizadas nele serão utilizadas aqui no nosso segundo post da série de estruturação.

Como eu já falei no primeiro post, o nosso segundo será sobre aprimoramento do projeto inicial. Irei adicionar melhorias estruturais ao projeto e, também, no próprio código. A ideia aqui será deixar o nosso projeto com “cara de framework”. Tentarei ser o mais objetivo possível aqui, tudo bem?

2. Melhorias estruturais

Iniciaremos com as melhorias estruturais do nosso projeto. Apesar que ele já está com a base necessária para o treinamento de modelos, o nosso projeto ainda é imaturo. Vamos dar um toque de adolescência ao mesmo. Obs: claro… este aqui é o segundo post. Sendo assim, ainda teremos muita coisa para melhorar no nosso último post, ok?

Trabalharemos na estrutura principal do nosso projeto: train.py.

2.1 Novas bibliotecas de apoio

Então, pessoal, vamos começar os nossos trabalhos. Seguindo o mesmo exemplo do post passado, aconselho que você baixe o projeto do meu repositório para o seu computador. Aconselho, inclusive, que você utilize o projeto da estrutura1, visto que o segundo já estará com todas as modificações implementadas. Os módulos utilizados serão:

  1. tqdm: módulo bastante utilizado para criação de barras de progressão;
  2. logging: módulo utilizado para realizar o tracking do nosso código;
  3. argparse: módulo utilizado para obter/definir valores de argumentos necessários para a execução do programa.

Importe todas as bibliotecas no topo do nosso arquivo train.py conforte a Figura 1.

Figura 1: Importando os módulos de apoio.

2.1.1 Definindo os argumentos do nosso modelo.

Agora iremos criar a variável nativa __name__ definindo-a como __main__ em nosso módulo train.py e criaremos uma instância do ArgumentParser(). Nós definiremos, inicialmente, três argumentos que serão utilizados em nosso projeto, que são: epochs, batch_size e lr (Figura 2). Perceba que nós definimos valores padrão para a inicialização do nosso treinamento, porém nós podemos alterar esses valores via linha de comando, passando o nome do argumento e o valor desejado. Por exemplo: python3 train.py — epochs 2 .

Também foi adicionada uma opcão com o caminho que será utilizado para salvar os pesos treinandos do nosso modelo ( — dir_save). Bem como definimos uma função main e colocaremos todo o nosso código de treinamento do modelo dentro dela. Ela receberá o nosso parser com os valores dos argumentos (Figura 3).

Figura 2: Iniciando o módulo train.
Figura 3: Adicionando o código à função main.

Mudamos os valores fixos do parâmetro lr (learning rate) do otimizador, os parâmetros batch_size dos DataLoader’s e o número de épocas no laço de treinamento foram substituídos, respectivamente, pelos valores que foram/serão passados para o nosso parser. Com essa pequena modificação que fizemos em nosso código, você já poderá realizar algumas combinações de testes, apenas mudando os valores desses parâmetros.

2.2.2 Criando barras de progressão para o treinamento e validação.

Você percebeu que, no momento em que o nosso modelo está sendo treinado, aparecem algumas mensagens sinalizando a época do treinamento em nosso terminal, correto? Isso nos dá pelo menos uma idéia de “em que época o nosso modelo está” no momento do treinamento e na validação, no entanto, essa não é uma forma muito informativa e elegante para o nosso projeto.

Para resolvermos isso, nós iremos fazer duas modificações em nosso código e termos, de cara, uma solução mais elegante e profissional. Vamos “bulir” com o módulo tqdm. Eu disponibilizei o link para a documentação desse cara e você poderá customizar, de acordo com a sua necessidade, a sua barra de progressão! Legal, né? Em nosso exemplo, faremos o básico (mas eficiente).

Se você é uma pessoa atenta à mudanças, você já deve ter percebido que a Figura 3 já está com o código modificado (comparando com a versão da estrutura1). Obs: eu aposto que você voltou na Figura 3 para ver… Bem, se você não viu, verá agora. Perceba na Figura 4 a sutil mudança feita no código, justamente nos laços de treinamento e validação.

Figura 4: Barra de progressão.

Em nosso primeiro exemplo, o código fazia chamada a enumerate(train_loader), por exemplo. Agora, nós temos a chamada ao tqdm, passando o nosso enumerate (com o nosso DataLoader) e o tamanho do mesmo (DataLoader). Perceba que essa modificação foi feita no treinamento e na validação. Execute o nosso train.py e veja como a saída já ficou interessante (Figura 5).

Figura 5: Execução de treinamento do modelo com barra de progressão.

Para concluir, perceba que foi adicionada na etapa de validação um condicional para avaliação de duas métricas: Loss error e Accuracy. Bem, no nosso próximo post iremos trabalhar melhor sobre a etapa de validação e teste do nosso modelo (além de outras melhorias). Porém agora você já tem uma idéia de “onde” será realizada a análise do nosso modelo. Também foi adicionada a opção para salvarmos os pesos treinados de nosso modelo. Mas… será que é uma boa prática salvar todos os pesos? Veremos no próximo post.

Bem, pessoal, é isso por hoje. No próximo post iremos discutir:

a) Como utilizar GPU no treinamento do nosso modelo;

b) Alteraremos o nosso modelo para um modelo de CNN;

c) Otimizaremos alguns parâmetros do PyTorch;

d) Utilizando métricas de otimização para selecionarmos o melhor modelo.

Até lá.

Senior Computer Vision Data Scientist at Conception Ro-Main (Quebec — CA). DSc in Computer Science. MTAC Brazil. https://github.com/adrianosantospb

Senior Computer Vision Data Scientist at Conception Ro-Main (Quebec — CA). DSc in Computer Science. MTAC Brazil. https://github.com/adrianosantospb