Uncategorized

Continuous Machine Learning – Parte II

Reading time: 4 minutes

Esse texto faz parte de uma série de 3 partes sobre Continuous Machine Learning. Você pode conferir a parte I aqui e a parte III aqui. Esse post é uma continuação da parte I, onde iniciamos nossa experiência de automatização em Data Science com GitHub e CML. Nessa parte II, basicamente iremos aprender a utilizar um container do Docker em uma GitHub Action para melhorar o tempo de computação.

Você pode pensar em uma imagem do Docker, Docker Image, como uma foto de um ambiente computacional em um contexto onde você pode entregar essa foto para um servidor e ele irá te retornar uma máquina virtual igual o sistema da sua foto. Quando um gatilho é disparado e o GitHub Actions entra em ação pela primeira vez, ele cria um container a partir da sua imagem e carrega esse container Com o container carregado, executa as tarefas que você solicitou. É por essa razão que utilizando containers, a computação fica mais rápida, pois não há necessidade de instalar pacotes, softwares, dependências, configurá-los, compilá-los, nada do tipo. Já está tudo pronto, de acordo com a sua imagem! De outro modo, todas essas instalações e compilações estariam no seu arquivo YAML e seria executado toda vez que o gatilho fosse disparado, que era como estávamos fazendo na parte I dessa série.

Criando uma imagem do Docker

Image from “Build a Docker Image just like how you would configure a VM”.

Como você pode ver na imagem acima, você deve escrever as instruções em um arquivo de texto chamado Dockerfile e o Docker, um software, irá gerar uma imagem com base nesse Dockerfile. Essa imagem, por sua vez, poderá ser utilizada para criar o seu container, que é a máquina virtual de fato. Na imagem abaixo, por exemplo, nós temos três containers sendo geridos pelo Docker em um servidor.

Image from “Si Docker m’était conté… 2ème partie : plongée à l’intérieur des conteneurs”.

Nós iremos basear o nosso Dockerfile no Dockerfile oficial do proejto CML. Vai ficar assim:

FROM dvcorg/cml
RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9 && \
    add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu bionic-cran35/' && \  
    apt update && \
    apt install -y r-base && \
    R --silent -e "install.packages(c(\"igraph\", \"ppcor\", \"scales\", \"Rcpp\"))" && \
    wget -c https://github.com/miicTeam/miic_R_package/archive/v1.4.2.tar.gz && \
    tar -xvzf v1.4.2.tar.gz && \
    R CMD INSTALL miic_R_package-1.4.2/. --preclean

Como você pode ver, tem uma parte das instruções que costumávamos ter no arquivo cml.yaml (na versão utilizada na parte I dessa série). É aqui que está a mágica! Por causa disso, o cml.yaml vai ser mais curto agora, o que significa que terão menos instruções para serem executadas sempre que o gatilho do GitHub Actions disparar. O novo cml.yaml deve ficar assim:

name: dvc-cml-miic
on: [push]
jobs:
  run:
    runs-on: [ubuntu-latest]
    container: docker://mribeirodantas/cml-test:r
    steps:
      - uses: actions/checkout@v2

      - name: cml_run
        env:
          repo_token: ${{ secrets.GITHUB_TOKEN }}
          GDRIVE_CREDENTIALS_DATA: ${{ secrets.GDRIVE_CREDENTIALS_DATA }}
        run: |
          R --version

          dvc pull
          Rscript infer_network.R

          # Write your CML report
          echo "## Model Metrics" > report.md
          cat metrics.txt >> report.md
          echo "## Data visualization" >> report.md
          cml-publish network_diagram.png --md >> report.md
          cml-send-comment report.md

Na figura abaixo, é possível ver a diferença entre o velho e o novo cml.yaml. Movendo o slider de um lado para o outro, você pode comparar. Você também pode ver diretamente no GitHub clicando aqui.

Salve esse arquivo com o nome Dockerfile na pasta do repositório (seu repositório deve estar como aqui) e atualize o seu cml.yaml como no código acima.

Para gerar a imagem do Docker e publicá-la no Docker Hub, você precisa executar os comandos abaixo. Antes disso, claro, você precisa ter o Docker instalado no seu comptuador (instruções para Ubuntu aqui). O primeiro comando vai demorar um pouco, ele está criando sua imagem, mas não se preocupe: isso só acontece uma vez. É agora que ele irá baixar os arquivos, programas, pacotes, dependências, compilá-los, instalá-los, etc. Parte do que estávamos fazendo toda vez que dávamos um git push, está sendo feito agora mas uma última vez. Se você entendeu a explicação até agora, deve ter ficado claro como vale a pena utilizar containers, principalmente para projetos maiores e mais complexos do que o de exemplo que estamos fazendo aqui 🙂 .

# Create the Docker image from the Dockerfile
sudo docker build -t mribeirodantas/cml:r -f ./Dockerfile .
# Login to Docke Hub
sudo docker login
# Upload it to Docker Hub. GitHub will always download the
# Docker image from there.
sudo docker push mribeirodantas/cml:r

Testando sua imagem do Docker

Ainda não sabemos se de fato isso irá funcionar, portanto não iremos realizar um commit na branch principal, a master. Vamos criar uma branch e criar um pull request, utilizando a ferramenta de linha de comando gh, assim como fizemos na parte I dessa série.

git checkout -b docker_ghactions
git add -A
git commit -m 'Makes use of Docker to speed up GH Actions checks'
git push origin docker_ghactions
gh pr create --title 'Makes use of Docker to speed up GH Actions checks'

No experimento que fizemos na Parte I, você deve se lembrar que demorava cerca de 7 minutos para a checagem do Pull Request chegar ao fim. Agora, utilizando o container do Docker, demorou menos de 2 minutos. Levando em consideração que esse é um projeto simples, com um dataset pequeno, poucas dependências, o ganho pode ser muito maior na realidade. Legal, certo? Vamos aceitar esse Pull Request e mesclar esse commit à nossa branch principal.

# This command will merge all open push requests.
# We only have one, so it will merge the one we have opened.
gh pr merge

Talvez uma pergunta que possa ter vindo à tona é por qual razão estamos deixando o dvc pull no arquivo cml.yaml em vez de colocá-lo no Dockerfile, para que o dataset seja baixado apenas uma vez. Afinal de contas, baixar datasets grandes toda vez que criarmos um Pull Request pode ser um pouco.. desagradável 😀 . Faz sentido, o raciocínio está correto. No entanto, o dvc pull funciona como o git pull. Se não tiver nada de novo, ele não faz download algum. Se tiver algo novo, ele faz o download. Se deixássemos o dvc pull no Dockerfile, o dataset mais atualizado não seria baixado em futuros Pull Requests.

Por hoje é só 🙂