One place for hosting & domains

      Construir

      Como Construir uma Aplicação Node.js com o Docker


      Introdução

      A plataforma Docker permite aos desenvolvedores empacotar e executar aplicações como containers. Um container é um processo isolado que executa em um sistema operacional compartilhado, oferecendo uma alternativa mais leve às máquinas virtuais. Embora os containers não sejam novos, eles oferecem benefícios — incluindo isolamento do processo e padronização do ambiente — que estão crescendo em importância à medida que mais desenvolvedores usam arquiteturas de aplicativos distribuídos.

      Ao criar e dimensionar uma aplicação com o Docker, o ponto de partida normalmente é a criação de uma imagem para a sua aplicação, que você pode então, executar em um container. A imagem inclui o código da sua aplicação, bibliotecas, arquivos de configuração, variáveis de ambiente, e runtime. A utilização de uma imagem garante que o ambiente em seu container está padronizado e contém somente o que é necessário para construir e executar sua aplicação.

      Neste tutorial, você vai criar uma imagem de aplicação para um website estático que usa o framework Express e o Bootstrap. Em seguida, você criará um container usando essa imagem e a enviará para o Docker Hub para uso futuro. Por fim, você irá baixar a imagem armazenada do repositório do Docker Hub e criará outro container, demonstrando como você pode recriar e escalar sua aplicação.

      Pré-requisitos

      Para seguir este tutorial, você vai precisar de:

      Passo 1 — Instalando as Dependências da Sua Aplicação

      Para criar a sua imagem, primeiro você precisará produzir os arquivos de sua aplicação, que você poderá copiar para o seu container. Esses arquivos incluirão o conteúdo estático, o código e as dependências da sua aplicação.

      Primeiro, crie um diretório para o seu projeto no diretório home do seu usuário não-root. Vamos chamar o nosso de node_project, mas sinta-se à vontade para substituir isso por qualquer outra coisa:

      Navegue até esse diretório:

      Esse será o diretório raiz do projeto:

      Em seguida, crie um arquivo package.json com as dependências do seu projeto e outras informações de identificação. Abra o arquivo com o nano ou o seu editor favorito:

      Adicione as seguintes informações sobre o projeto, incluindo seu nome, autor, licença, ponto de entrada e dependências. Certifique-se de substituir as informações do autor pelo seu próprio nome e seus detalhes de contato:

      ~/node_project/package.json

      
      {
        "name": "nodejs-image-demo",
        "version": "1.0.0",
        "description": "nodejs image demo",
        "author": "Sammy the Shark <[email protected]>",
        "license": "MIT",
        "main": "app.js",
        "keywords": [
          "nodejs",
          "bootstrap",
          "express"
        ],
        "dependencies": {
          "express": "^4.16.4"
        }
      }
      

      Este arquivo inclui o nome do projeto, autor e a licença sob a qual ele está sendo compartilhado. O npm recomenda manter o nome do seu projeto curto e descritivo, evitando duplicidades no registro npm. Listamos a licença do MIT no campo de licença, permitindo o uso e a distribuição gratuitos do código do aplicativo.

      Além disso, o arquivo especifica:

      • "main": O ponto de entrada para a aplicação, app.js. Você criará esse arquivo em seguida.

      • "dependencies": As dependências do projeto — nesse caso, Express 4.16.4 ou acima.

      Embora este arquivo não liste um repositório, você pode adicionar um seguindo estas diretrizes em adicionando um repositório ao seu arquivo package.json. Esse é um bom acréscimo se você estiver versionando sua aplicação.

      Salve e feche o arquivo quando você terminar de fazer as alterações.

      Para instalar as dependências do seu projeto, execute o seguinte comando:

      Isso irá instalar os pacotes que você listou em seu arquivo package.json no diretório do seu projeto.

      Agora podemos passar para a construção dos arquivos da aplicação.

      Passo 2 — Criando os Arquivos da Aplicação

      Vamos criar um site que oferece aos usuários informações sobre tubarões. Nossa aplicação terá um ponto de entrada principal, app.js, e um diretório views, que incluirá os recursos estáticos do projeto. A página inicial, index.html, oferecerá aos usuários algumas informações preliminares e um link para uma página com informações mais detalhadas sobre tubarões, sharks.html. No diretório views, vamos criar tanto a página inicial quanto sharks.html.

      Primeiro, abra app.js no diretório principal do projeto para definir as rotas do projeto:

      A primeira parte do arquivo irá criar a aplicação Express e os objetos Router, e definir o diretório base, a porta, e o host como variáveis:

      ~/node_project/app.js

      
      var express = require("express");
      var app = express();
      var router = express.Router();
      
      var path = __dirname + '/views/';
      const PORT = 8080;
      const HOST = '0.0.0.0';
      

      A função require carrega o módulo express, que usamos então para criar os objetos app e router. O objeto router executará a função de roteamento do aplicativo e, como definirmos as rotas do método HTTP, iremos incluí-las nesse objeto para definir como nossa aplicação irá tratar as solicitações.

      Esta seção do arquivo também define algumas variáveis, path, PORT, e HOST:

      • path: Define o diretório base, que será o subdiretório views dentro do diretório atual do projeto.

      • HOST: Define o endereço ao qual a aplicação se vinculará e escutará. Configurar isto para 0.0.0.0 ou todos os endereços IPv4 corresponde ao comportamento padrão do Docker de expor os containers para 0.0.0.0, a menos que seja instruído de outra forma.

      • PORT: Diz à aplicação para escutar e se vincular à porta 8080.

      Em seguida, defina as rotas para a aplicação usando o objeto router:

      ~/node_project/app.js

      
      ...
      
      router.use(function (req,res,next) {
        console.log("/" + req.method);
        next();
      });
      
      router.get("/",function(req,res){
        res.sendFile(path + "index.html");
      });
      
      router.get("/sharks",function(req,res){
        res.sendFile(path + "sharks.html");
      });
      

      A função router.use carrega uma função de middleware que registrará as solicitações do roteador e as transmitirá para as rotas da aplicação. Estas são definidas nas funções subsequentes, que especificam que uma solicitação GET para a URL base do projeto deve retornar a página index.html, enquanto uma requisição GET para a rota /sharks deve retornar sharks.html.

      Finalmente, monte o middleware router e os recursos estáticos da aplicação e diga à aplicação para escutar na porta 8080:

      ~/node_project/app.js

      
      ...
      
      app.use(express.static(path));
      app.use("/", router);
      
      app.listen(8080, function () {
        console.log('Example app listening on port 8080!')
      })
      

      O arquivo app.js finalizado ficará assim:

      ~/node_project/app.js

      
      var express = require("express");
      var app = express();
      var router = express.Router();
      
      var path = __dirname + '/views/';
      const PORT = 8080;
      const HOST = '0.0.0.0';
      
      router.use(function (req,res,next) {
        console.log("/" + req.method);
        next();
      });
      
      router.get("/",function(req,res){
        res.sendFile(path + "index.html");
      });
      
      router.get("/sharks",function(req,res){
        res.sendFile(path + "sharks.html");
      });
      
      app.use(express.static(path));
      app.use("/", router);
      
      app.listen(8080, function () {
        console.log('Example app listening on port 8080!')
      })
      

      Salve e feche o arquivo quando tiver terminado.

      Em seguida, vamos adicionar algum conteúdo estático à aplicação. Comece criando o diretório views:

      Abra a página inicial, index.html:

      Adicione o seguinte código ao arquivo, que irá importar o Bootstrap e criar o componente jumbotron com um link para a página de informações mais detalhadas sharks.html

      ~/node_project/views/index.html

      
      <!DOCTYPE html>
      <html lang="en">
      
      <head>
          <title>About Sharks</title>
          <meta charset="utf-8">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
          <link href="css/styles.css" rel="stylesheet">
          <link href="https://fonts.googleapis.com/css?family=Merriweather:400,700" rel="stylesheet" type="text/css">
      </head>
      
      <body>
          <nav class="navbar navbar-dark navbar-static-top navbar-expand-md">
              <div class="container">
                  <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span>
                  </button> <a class="navbar-brand" href="#">Everything Sharks</a>
                  <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                      <ul class="nav navbar-nav mr-auto">
                          <li class="active nav-item"><a href="/" class="nav-link">Home</a>
                          </li>
                          <li class="nav-item"><a href="http://www.digitalocean.com/sharks" class="nav-link">Sharks</a>
                          </li>
                      </ul>
                  </div>
              </div>
          </nav>
          <div class="jumbotron">
              <div class="container">
                  <h1>Want to Learn About Sharks?</h1>
                  <p>Are you ready to learn about sharks?</p>
                  <br>
                  <p><a class="btn btn-primary btn-lg" href="http://www.digitalocean.com/sharks" role="button">Get Shark Info</a>
                  </p>
              </div>
          </div>
          <div class="container">
              <div class="row">
                  <div class="col-lg-6">
                      <h3>Not all sharks are alike</h3>
                      <p>Though some are dangerous, sharks generally do not attack humans. Out of the 500 species known to researchers, only 30 have been known to attack humans.
                      </p>
                  </div>
                  <div class="col-lg-6">
                      <h3>Sharks are ancient</h3>
                      <p>There is evidence to suggest that sharks lived up to 400 million years ago.
                      </p>
                  </div>
              </div>
          </div>
      </body>
      
      </html>
      

      A navbar de nível superior aqui, permite que os usuários alternem entre as páginas Home e Sharks. No subcomponente navbar-nav, estamos utilizando a classe active do Bootstrap para indicar a página atual ao usuário. Também especificamos as rotas para nossas páginas estáticas, que correspondem às rotas que definimos em app.js:

      ~/node_project/views/index.html

      
      ...
      <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
         <ul class="nav navbar-nav mr-auto">
            <li class="active nav-item"><a href="/" class="nav-link">Home</a>
            </li>
            <li class="nav-item"><a href="http://www.digitalocean.com/sharks" class="nav-link">Sharks</a>
            </li>
         </ul>
      </div>
      ...
      

      Além disso, criamos um link para nossa página de informações sobre tubarões no botão do nosso jumbotron:

      ~/node_project/views/index.html

      
      ...
      <div class="jumbotron">
         <div class="container">
            <h1>Want to Learn About Sharks?</h1>
            <p>Are you ready to learn about sharks?</p>
            <br>
            <p><a class="btn btn-primary btn-lg" href="http://www.digitalocean.com/sharks" role="button">Get Shark Info</a>
            </p>
         </div>
      </div>
      ...
      

      Há também um link para uma folha de estilo personalizada no cabeçalho:

      ~/node_project/views/index.html

      ...
      <link href="css/styles.css" rel="stylesheet">
      ...
      

      Vamos criar esta folha de estilo no final deste passo.

      Salve e feche o arquivo quando terminar.

      Com a página inicial da aplicação funcionando, podemos criar nossa página de informações sobre tubarões, sharks.html, que oferecerá aos usuários interessados mais informações sobre os tubarões.

      Abra o arquivo:

      Adicione o seguinte código, que importa o Bootstrap e a folha de estilo personalizada, e oferece aos usuários informações detalhadas sobre determinados tubarões:

      ~/node_project/views/sharks.html

      <!DOCTYPE html>
      <html lang="en">
      
      <head>
          <title>About Sharks</title>
          <meta charset="utf-8">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
          <link href="css/styles.css" rel="stylesheet">
          <link href="https://fonts.googleapis.com/css?family=Merriweather:400,700" rel="stylesheet" type="text/css">
      </head>
      <nav class="navbar navbar-dark navbar-static-top navbar-expand-md">
          <div class="container">
              <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span>
              </button> <a class="navbar-brand" href="/">Everything Sharks</a>
              <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                  <ul class="nav navbar-nav mr-auto">
                      <li class="nav-item"><a href="/" class="nav-link">Home</a>
                      </li>
                      <li class="active nav-item"><a href="http://www.digitalocean.com/sharks" class="nav-link">Sharks</a>
                      </li>
                  </ul>
              </div>
          </div>
      </nav>
      <div class="jumbotron text-center">
          <h1>Shark Info</h1>
      </div>
      <div class="container">
          <div class="row">
              <div class="col-lg-6">
                  <p>
                      <div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.
                      </div>
                      <img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">
                  </p>
              </div>
              <div class="col-lg-6">
                  <p>
                      <div class="caption">Other sharks are known to be friendly and welcoming!</div>
                      <img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">
                  </p>
              </div>
          </div>
      </div>
      
      </html>
      

      Observe que neste arquivo, usamos novamente a classe active para indicar a página atual.

      Salve e feche o arquivo quando tiver terminado.

      Finalmente, crie a folha de estilo CSS personalizada que você vinculou em index.html e sharks.html criando primeiro uma pasta css no diretório views:

      Abra a folha de estilo:

      • nano views/css/styles.css

      Adicione o seguinte código, que irá definir a cor desejada e a fonte para nossas páginas:

      ~/node_project/views/css/styles.css

      
      .navbar {
          margin-bottom: 0;
          background: #000000;
      }
      
      body {
          background: #000000;
          color: #ffffff;
          font-family: 'Merriweather', sans-serif;
      }
      
      h1,
      h2 {
          font-weight: bold;
      }
      
      p {
          font-size: 16px;
          color: #ffffff;
      }
      
      .jumbotron {
          background: #0048CD;
          color: white;
          text-align: center;
      }
      
      .jumbotron p {
          color: white;
          font-size: 26px;
      }
      
      .btn-primary {
          color: #fff;
          text-color: #000000;
          border-color: white;
          margin-bottom: 5px;
      }
      
      img,
      video,
      audio {
          margin-top: 20px;
          max-width: 80%;
      }
      
      div.caption: {
          float: left;
          clear: both;
      }
      

      Além de definir a fonte e a cor, esse arquivo também limita o tamanho das imagens especificando max-width ou largura máxima de 80%. Isso evitará que ocupem mais espaço do que gostaríamos na página.

      Salve e feche o arquivo quando tiver terminado.

      Com os arquivos da aplicação no lugar e as dependências do projeto instaladas, você está pronto para iniciar a aplicação.

      Se você seguiu o tutorial de configuração inicial do servidor nos pré-requisitos, você terá um firewall ativo que permita apenas o tráfego SSH. Para permitir o tráfego para a porta 8080, execute:

      Para iniciar a aplicação, certifique-se de que você está no diretório raiz do seu projeto:

      Inicie sua aplicação com node app.js:

      Dirija seu navegador para http://ip_do_seu_servidor:8080. Você verá a seguinte página inicial:

      Clique no botão Get Shark Info. Você verá a seguinte página de informações:

      Agora você tem uma aplicação instalada e funcionando. Quando estiver pronto, saia do servidor digitando CTRL + C. Agora podemos passar a criar o Dockerfile que nos permitirá recriar e escalar essa aplicação conforme desejado.

      Step 3 — Escrevendo o Dockerfile

      Seu Dockerfile especifica o que será incluído no container de sua aplicação quando for executado. A utilização de um Dockerfile permite que você defina seu ambiente de container e evite discrepâncias com dependências ou versões de runtime.

      Seguindo estas diretrizes na construção de containers otimizados, vamos tornar nossa imagem o mais eficiente possível, minimizando o número de camadas de imagem e restringindo a função da imagem a uma única finalidade — recriar nossos arquivos da aplicação e o conteúdo estático.

      No diretório raiz do seu projeto, crie o Dockerfile:

      As imagens do Docker são criadas usando uma sucessão de imagens em camadas que são construídas umas sobre as outras. Nosso primeiro passo será adicionar a imagem base para a nossa aplicação que formará o ponto inicial da construção da aplicação.

      Vamos utilizar a imagem node:10, uma vez que, no momento da escrita desse tutorial, esta é a versão LTS reomendada do Node.js. Adicione a seguinte instrução FROM para definir a imagem base da aplicação:

      ~/node_project/Dockerfile

      FROM node:10
      

      Esta imagem inclui Node.js e npm. Cada Dockerfile deve começar com uma instrução FROM.

      Por padrão, a imagem Node do Docker inclui um usuário não-root node que você pode usar para evitar a execução de seu container de aplicação como root. Esta é uma prática de segurança recomendada para evitar executar containers como root e para restringir recursos dentro do container para apenas aqueles necessários para executar seus processos. Portanto, usaremos o diretório home do usuário node como o diretório de trabalho de nossa aplicação e o definiremos como nosso usuário dentro do container. Para mais informações sobre as melhores práticas ao trabalhar com a imagem Node do Docker, veja este guia de melhores práticas.

      Para um ajuste fino das permissões no código da nossa aplicação no container, vamos criar o subdiretório node_modules em /home/node juntamente com o diretório app. A criação desses diretórios garantirá que eles tenham as permissões que desejamos, o que será importante quando criarmos módulos de node locais no container com npm install. Além de criar esses diretórios, definiremos a propriedade deles para o nosso usuário node:

      ~/node_project/Dockerfile

      ...
      RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
      

      Para obter mais informações sobre o utilitário de consolidação das instruções RUN, veja esta discussão sobre como gerenciar camadas de container.

      Em seguida, defina o diretório de trabalho da aplicação para /home/node/app:

      ~/node_project/Dockerfile

      ...
      WORKDIR /home/node/app
      

      Se WORKDIR não estiver definido, o Docker irá criar um por padrão, então é uma boa ideia defini-lo explicitamente.

      A seguir, copie os arquivos package.json e package-lock.json (para npm 5+):

      ~/node_project/Dockerfile

      ...
      COPY package*.json ./
      

      Adicionar esta instrução COPY antes de executar o npm install ou copiar o código da aplicação nos permite aproveitar o mecanismo de armazenamento em cache do Docker. Em cada estágio da compilação ou build, o Docker verificará se há uma camada armazenada em cache para essa instrução específica. Se mudarmos o package.json, esta camada será reconstruída, mas se não o fizermos, esta instrução permitirá ao Docker usar a camada de imagem existente e ignorar a reinstalação dos nossos módulos de node.

      Depois de copiar as dependências do projeto, podemos executar npm install:

      ~/node_project/Dockerfile

      ...
      RUN npm install
      

      Copie o código de sua aplicação para o diretório de trabalho da mesma no container:

      ~/node_project/Dockerfile

      ...
      COPY . .
      

      Para garantir que os arquivos da aplicação sejam de propriedade do usuário não-root node, copie as permissões do diretório da aplicação para o diretório no container:

      ~/node_project/Dockerfile

      ...
      COPY --chown=node:node . .
      

      Defina o usuário para node:

      ~/node_project/Dockerfile

      ...
      USER node
      

      Exponha a porta 8080 no container e inicie a aplicação:

      ~/node_project/Dockerfile

      ...
      EXPOSE 8080
      
      CMD [ "node", "app.js" ]
      

      EXPOSE não publica a porta, mas funciona como uma maneira de documentar quais portas no container serão publicadas em tempo de execução. CMD executa o comando para iniciar a aplicação - neste caso, node app.js. Observe que deve haver apenas uma instrução CMD em cada Dockerfile. Se você incluir mais de uma, somente a última terá efeito.

      Há muitas coisas que você pode fazer com o Dockerfile. Para obter uma lista completa de instruções, consulte a documentação de referência Dockerfile do Docker

      O Dockerfile completo estará assim:

      ~/node_project/Dockerfile

      
      FROM node:10
      
      RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
      
      WORKDIR /home/node/app
      
      COPY package*.json ./
      
      RUN npm install
      
      COPY . .
      
      COPY --chown=node:node . .
      
      USER node
      
      EXPOSE 8080
      
      CMD [ "node", "app.js" ]
      

      Salve e feche o arquivo quando terminar a edição.

      Antes de construir a imagem da aplicação, vamos adicionar um arquivo .dockerignore. Trabalhando de maneira semelhante a um arquivo .gitignore, .dockerignore especifica quais arquivos e diretórios no diretório do seu projeto não devem ser copiados para o seu container.

      Abra o arquivo .dockerignore:

      Dentro do arquivo, adicione seus módulos de node, logs npm, Dockerfile, e o arquivo .dockerignore:

      ~/node_project/.dockerignore

      node_modules
      npm-debug.log
      Dockerfile
      .dockerignore
      

      Se você estiver trabalhando com o Git, então você também vai querer adicionar o seu diretório .git e seu arquivo .gitignore.

      Salve e feche o arquivo quando tiver terminado.

      Agora você está pronto para construir a imagem da aplicação usando o comando docker build. Usar a flag -t com o docker build permitirá que você marque a imagem com um nome memorizável. Como vamos enviar a imagem para o Docker Hub, vamos incluir nosso nome de usuário do Docker Hub na tag. Vamos marcar a imagem como nodejs-image-demo, mas sinta-se à vontade para substituir isto por um nome de sua escolha. Lembre-se também de substituir seu_usuário_dockerhub pelo seu nome real de usuário do Docker Hub:

      • docker build -t seu_usuário_dockerhub/nodejs-image-demo .

      O . especifica que o contexto do build é o diretório atual.

      Levará um ou dois minutos para construir a imagem. Quando estiver concluído, verifique suas imagens:

      Você verá a seguinte saída:

      Output

      REPOSITORY TAG IMAGE ID CREATED SIZE seu_usuário_dockerhub/nodejs-image-demo latest 1c723fb2ef12 8 seconds ago 895MB node 10 f09e7c96b6de 17 hours ago 893MB

      É possível criar um container com essa imagem usando docker run. Vamos incluir três flags com esse comando:

      • -p: Isso publica a porta no container e a mapeia para uma porta em nosso host. Usaremos a porta 80 no host, mas sinta-se livre para modificá-la, se necessário, se tiver outro processo em execução nessa porta. Para obter mais informações sobre como isso funciona, consulte esta discussão nos documentos do Docker sobre port binding.

      • -d: Isso executa o container em segundo plano.

      • --name: Isso nos permite dar ao container um nome memorizável.

      Execute o seguinte comando para construir o container:

      • docker run --name nodejs-image-demo -p 80:8080 -d seu_usuário_dockerhub/nodejs-image-demo

      Depois que seu container estiver em funcionamento, você poderá inspecionar uma lista de containers em execução com docker ps:

      Você verá a seguinte saída:

      Output

      CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e50ad27074a7 seu_usuário_dockerhub/nodejs-image-demo "node app.js" 8 seconds ago Up 7 seconds 0.0.0.0:80->8080/tcp nodejs-image-demo

      Com seu container funcionando, você pode visitar a sua aplicação apontando seu navegador para http://ip_do_seu_servidor. Você verá a página inicial da sua aplicação novamente:

      Agora que você criou uma imagem para sua aplicação, você pode enviá-la ao Docker Hub para uso futuro.

      Ao enviar sua imagem de aplicação para um registro como o Docker Hub, você a torna disponível para uso subsequente à medida que cria e escala seus containers. Vamos demonstrar como isso funciona, enviando a imagem da aplicação para um repositório e, em seguida, usando a imagem para recriar nosso container.

      A primeira etapa para enviar a imagem é efetuar login na conta do Docker Hub que você criou nos pré-requisitos:

      • docker login -u seu_usuário_dockerhub -p senha_do_usuário_dockerhub

      Efetuando o login dessa maneira será criado um arquivo ~/.docker/config.json no diretório home do seu usuário com suas credenciais do Docker Hub.

      Agora você pode enviar a imagem da aplicação para o Docker Hub usando a tag criada anteriormente, seu_usuário_dockerhub/nodejs-image-demo:

      • docker push seu_usuário_dockerhub/nodejs-image-demo

      Vamos testar o utilitário do registro de imagens destruindo nosso container e a imagem de aplicação atual e reconstruindo-os com a imagem em nosso repositório.

      Primeiro, liste seus containers em execução:

      Você verá a seguinte saída:

      Output

      CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e50ad27074a7 seu_usuário_dockerhub/nodejs-image-demo "node app.js" 3 minutes ago Up 3 minutes 0.0.0.0:80->8080/tcp nodejs-image-demo

      Usando o CONTAINER ID listado em sua saída, pare o container da aplicação em execução. Certifique-se de substituir o ID destacado abaixo por seu próprio CONTAINER ID:

      Liste todas as suas imagens com a flag -a:

      Você verá a seguinte saída com o nome da sua imagem, seuusuáriodockerhub/nodejs-image-demo, juntamente com a imagem node e outras imagens do seu build.

      Output

      REPOSITORY TAG IMAGE ID CREATED SIZE seu_usuário_dockerhub/nodejs-image-demo latest 1c723fb2ef12 7 minutes ago 895MB <none> <none> e039d1b9a6a0 7 minutes ago 895MB <none> <none> dfa98908c5d1 7 minutes ago 895MB <none> <none> b9a714435a86 7 minutes ago 895MB <none> <none> 51de3ed7e944 7 minutes ago 895MB <none> <none> 5228d6c3b480 7 minutes ago 895MB <none> <none> 833b622e5492 8 minutes ago 893MB <none> <none> 5c47cc4725f1 8 minutes ago 893MB <none> <none> 5386324d89fb 8 minutes ago 893MB <none> <none> 631661025e2d 8 minutes ago 893MB node 10 f09e7c96b6de 17 hours ago 893MB

      Remova o container parado e todas as imagens, incluindo imagens não utilizadas ou pendentes, com o seguinte comando:

      Digite y quando solicitado na saída para confirmar que você gostaria de remover o container e as imagens parados. Esteja ciente de que isso também removerá seu cache de compilação.

      Agora você removeu o container que está executando a imagem da sua aplicação e a própria imagem. Para obter mais informações sobre como remover containers, imagens e volumes do Docker, consulte How To Remove Docker Images, Containers, and Volumes.

      Com todas as suas imagens e containers excluídos, agora você pode baixar a imagem da aplicação do Docker Hub:

      • docker pull seu_usuário_dockerhub/nodejs-image-demo

      Liste suas imagens mais uma vez:

      Você verá a imagem da sua aplicação:

      Output

      REPOSITORY TAG IMAGE ID CREATED SIZE seu_usuário_dockerhub/nodejs-image-demo latest 1c723fb2ef12 11 minutes ago 895MB

      Agora você pode reconstruir seu container usando o comando do Passo 3:

      • docker run --name nodejs-image-demo -p 80:8080 -d seu_usuário_dockerhub/nodejs-image-demo

      Liste seus containers em execução:

      docker ps
      

      Output

      CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f6bc2f50dff6 seu_usuário_dockerhub/nodejs-image-demo "node app.js" 4 seconds ago Up 3 seconds 0.0.0.0:80->8080/tcp nodejs-image-demo

      Visite http://ip_do_seu_servidor mais uma vez para ver a sua aplicação em execução.

      Conclusão

      Neste tutorial, você criou uma aplicação web estática com Express e Bootstrap, bem como uma imagem do Docker para esta aplicação. Você utilizou essa imagem para criar um container e enviou a imagem para o Docker Hub. A partir daí, você conseguiu destruir sua imagem e seu container e recriá-los usando seu repositório do Docker Hub.

      Se você estiver interessado em aprender mais sobre como trabalhar com ferramentas como o Docker Compose e o Docker Machine para criar configurações de vários containers, consulte os seguintes guias:

      Para dicas gerais sobre como trabalhar com dados de container, consulte:

      Se você estiver interessado em outros tópicos relacionados ao Docker, consulte nossa biblioteca completa de tutoriais do Docker.

      Por Kathleen Juell



      Source link

      Como Construir uma Rede Neural para Reconhecer Dígitos Manuscritos com o TensorFlow


      Introdução

      Redes neurais são usadas como um método de deep learning ou aprendizado profundo, um dos vários subcampos da inteligência artificial. Elas foram propostas pela primeira vez há cerca de 70 anos como uma tentativa de simular a maneira como o cérebro humano funciona, embora de uma forma muito mais simplificada. Os “neurônios” individuais são conectados em camadas, com pesos designados para determinar como o neurônio responde quando os sinais são propagados pela rede. Anteriormente, as redes neurais eram limitadas no número de neurônios que elas eram capazes de simular e, portanto, a complexidade do aprendizado que podiam alcançar. Mas nos últimos anos, devido aos avanços no desenvolvimento de hardware, pudemos construir redes muito profundas e treiná-las em enormes datasets ou conjuntos de dados para obter avanços na inteligência de máquinas.

      Essas inovações permitiram que as máquinas correspondessem e excedessem as capacidades dos humanos em realizar certas tarefas. Uma dessas tarefas é o reconhecimento de objetos. Embora as máquinas tenham sido historicamente incapazes de corresponder à visão humana, avanços recentes em deep learning tornaram possível construir redes neurais capazes de reconhecer objetos, rostos, textos e até mesmo emoções.

      Neste tutorial, você implementará uma pequena subseção de reconhecimento de objeto recognition—digit. Utilizando o TensorFlow, uma biblioteca Python open-source desenvolvida pelos laboratórios do Google Brain para pesquisa em deep learning, você pegará imagens desenhadas à mão dos números de 0 a 9 e construirá e treinará uma rede neural para reconhecer e prever o rótulo correto para o dígito exibido.

      Embora você não precise de experiência prévia em deep learning prático ou de uso do TensorFlow para acompanhar este tutorial, vamos assumir alguma familiaridade com termos e conceitos de machine learning, como treinamento e testes, recursos e rótulos, otimização e avaliação. Você pode aprender mais sobre esses conceitos em Uma Introdução ao Machine Learning.

      Pré-requisitos

      Para completar esse tutorial, você vai precisar de:

      Passo 1 — Configurando o Projeto

      Antes de desenvolver o programa de reconhecimento, você precisará instalar algumas dependências e criar um espaço de trabalho para armazenar seus arquivos.

      Usaremos um ambiente virtual do Python 3 para gerenciar as dependências do nosso projeto. Crie um novo diretório para o seu projeto e navegue até o novo diretório:

      • mkdir tensorflow-demo
      • cd tensorflow-demo

      Execute os seguintes comandos para configurar o ambiente virtual para este tutorial:

      • python3 -m venv tensorflow-demo
      • source tensorflow-demo/bin/activate

      Em seguida, instale as bibliotecas que você usará neste tutorial. Usaremos versões específicas dessas bibliotecas criando um arquivo requirements.txt no diretório do projeto, que especifica o requisito e a versão que precisamos. Crie o arquivo requirements.txt:

      Abra o arquivo em seu editor de textos e adicione as seguintes linhas para especificar as bibliotecas Image, NumPy, e TensorFlow e suas versões:

      requirements.txt

      image==1.5.20
      numpy==1.14.3
      tensorflow==1.4.0
      

      Salve o arquivo e saia do editor. Em seguida instale estas bibliotecas com o seguinte comando:

      • pip install -r requirements.txt

      Com as dependências instaladas, podemos começar a trabalhar no nosso projeto.

      Passo 2 — Importando o Dataset MNIST

      O dataset que estaremos utilizando neste tutorial é chamado de dataset MNIST, e ele é um clássico na comunidade de machine learning. Este dataset é composto de imagens de dígitos manuscritos, com 28x28 pixels de tamanho. Aqui estão alguns exemplos dos dígitos incluídos no dataset:

      Vamos criar um programa Python para trabalhar com este dataset. Usaremos um arquivo para todo o nosso trabalho neste tutorial. Crie um novo arquivo chamado main.py:

      Agora abra este arquivo no editor de textos de sua preferência e adicione esta linha de código ao arquivo para importar a biblioteca do TensorFlow:

      main.py

      import tensorflow as tf
      

      Adicione as seguintes linhas de código ao seu arquivo para importar o dataset MNIST e armazenar os dados da imagem na variável mnist:

      main.py

      from tensorflow.examples.tutorials.mnist import input_data
      mnist = input_data.read_data_sets("MNIST_data/", one_hot=True) # y labels are oh-encoded
      

      Ao ler os dados, estamos usando one-hot-encoding para representar os rótulos (o dígito real desenhado, por exemplo "3") das imagens. O one-hot-encoding utiliza um vetor de valores binários para representar valores numéricos ou categóricos. Como nossos rótulos são para os dígitos de 0 a 9, o vetor contém dez valores, um para cada dígito possível. Um desses valores é definido como 1, para representar o dígito nesse índice do vetor, e o restante é difinido como 0. Por exemplo, o dígito 3 é representado usando o vetor [0, 0, 0, 1, 0, 0, 0, 0, 0, 0]. Como o valor no índice 3 está armazenado como 1, o vetor representa o dígito 3.

      Para representar as imagens, os 28x28 pixels são achatados em um vetor 1D com 784 pixels de tamanho. Cada um dos 784 pixels que compõem a imagem é armazenado como um valor entre 0 e 255. Isso determina a escala de cinza do pixel, pois nossas imagens são apresentadas apenas em preto e branco. Portanto, um pixel preto é representado por 255 e um pixel branco por 0, com os vários tons de cinza em algum lugar entre eles.

      Podemos usar a variável mnist para descobrir o tamanho do dataset que acabamos de importar. Observando os num_examples para cada um dos três subconjuntos, podemos determinar que o dataset foi dividido em 55.000 imagens para treinamento, 5000 para validação e 10.000 para teste. Adicione as seguintes linhas ao seu arquivo:

      main.py

      
      n_train = mnist.train.num_examples # 55,000
      n_validation = mnist.validation.num_examples # 5000
      n_test = mnist.test.num_examples # 10,000
      

      Agora que temos nossos dados importados, é hora de pensar sobre a rede neural.

      Passo 3 — Definindo a Arquitetura da Rede Neural

      A arquitetura da rede neural refere-se a elementos como o número de camadas na rede, o número de unidades em cada camada e como as unidades são conectadas entre as camadas. Como as redes neurais são vagamente inspiradas no funcionamento do cérebro humano, aqui o termo unidade é usado para representar o que seria biologicamente um neurônio. Assim como os neurônios transmitem sinais pelo cérebro, as unidades tomam alguns valores das unidades anteriores como entrada, realizam uma computação e, em seguida, transmitem o novo valor como saída para outras unidades. Essas unidades são colocadas em camadas para formar a rede, iniciando no mínimo com uma camada para entrada de valores e uma camada para valores de saída. O termo hidden layer ou camada oculta é usado para todas as camadas entre as camadas de entrada e saída, ou seja, aquelas "ocultas" do mundo real.

      Arquiteturas diferentes podem produzir resultados drasticamente diferentes, já que o desempenho pode ser pensado como uma função da arquitetura entre outras coisas, como os parâmetros, os dados e a duração do treinamento.

      Adicione as seguintes linhas de código ao seu arquivo para armazenar o número de unidades por camada nas variáveis globais. Isso nos permite alterar a arquitetura de rede em um único lugar e, no final do tutorial, você pode testar por si mesmo como diferentes números de camadas e unidades afetarão os resultados de nosso modelo:

      main.py

      
      n_input = 784   # input layer (28x28 pixels)
      n_hidden1 = 512 # 1st hidden layer
      n_hidden2 = 256 # 2nd hidden layer
      n_hidden3 = 128 # 3rd hidden layer
      n_output = 10   # output layer (0-9 digits)
      

      O diagrama a seguir mostra uma visualização da arquitetura que projetamos, com cada camada totalmente conectada às camadas adjacentes:

      O termo "deep neural network" ou rede neural profunda se relaciona com o número de camadas ocultas, com "superficial" geralmente significando apenas uma camada oculta e "profunda", referindo-se a várias camadas ocultas. Fornecidos dados de treinamento suficientes, uma rede neural superficial com um número suficiente de unidades deve teoricamente ser capaz de representar qualquer função que uma rede neural profunda possa. Mas é mais eficiente computacionalmente usar uma rede neural profunda menor para realizar a mesma tarefa que exigiria uma rede superficial com exponencialmente mais unidades ocultas. Redes neurais superficiais também freqüentemente encontram overfitting, onde a rede essencialmente memoriza os dados de treinamento que viu e não é capaz de generalizar o conhecimento para novos dados. É por isso que as redes neurais profundas são mais comumente usadas: as várias camadas entre os dados brutos de entrada e o rótulo de saída permitem que a rede aprenda recursos em vários níveis de abstração, tornando a própria rede mais capaz de generalizar.

      Outros elementos da rede neural que precisam ser definidos aqui são os hiperparâmetros. Ao contrário dos parâmetros que serão atualizados durante o treinamento, esses valores são definidos inicialmente e permanecem constantes durante todo o processo. No seu arquivo, defina as seguintes variáveis e valores:

      main.py

      
      learning_rate = 1e-4
      n_iterations = 1000
      batch_size = 128
      dropout = 0.5
      

      A taxa de aprendizado, learningrate, representa o quanto os parâmetros serão ajustados em cada etapa do processo de aprendizado. Esses ajustes são um componente-chave do treinamento: depois de cada passagem pela rede, ajustamos os pesos ligeiramente para tentar reduzir a perda. Taxas de aprendizado maiores podem convergir mais rapidamente, mas também têm o potencial de ultrapassar os valores ideais à medida que são atualizados. O número de iterações, niterations, refere-se a quantas vezes passamos pela etapa de treinamento e o tamanho do lote ou batch_size se refere a quantos exemplos de treinamento estamos usando em cada etapa. A variável dropout representa um limiar no qual eliminamos algumas unidades aleatoriamente. Estaremos usando dropout em nossa última camada oculta para dar a cada unidade 50% de chance de ser eliminada em cada etapa de treinamento. Isso ajuda a evitar o overfitting.

      Agora já definimos a arquitetura de nossa rede neural e os hiperparâmetros que impactam o processo de aprendizagem. O próximo passo é construir a rede como um gráfico do TensorFlow.

      Passo 4 — Construindo o Gráfico do TensorFlow

      Para construir nossa rede, vamos configurará-la como um gráfico computacional para o TensorFlow executar. O conceito central do TensorFlow é o tensor, uma estrutura de dados semelhante a uma matriz ou lista inicializada, manipulada à medida que passa pelo gráfico e atualizada através do processo de aprendizado.

      Começaremos definindo três tensores como placeholders ou marcadores de posição, que são tensores nos quais alimentaremos os valores posteriormente. Adicione o seguinte ao seu arquivo:

      main.py

      
      X = tf.placeholder("float", [None, n_input])
      Y = tf.placeholder("float", [None, n_output])
      keep_prob = tf.placeholder(tf.float32) ^
      

      O único parâmetro que precisa ser especificado em sua declaração é o tamanho dos dados os quais estaremos alimentando. Para X usamos um formato [None, 784], onde None representa qualquer quantidade, pois estaremos alimentando em um número indefinido de imagens de 784 pixels. O formato de Y é [None, 10] pois iremos usá-lo para um número indefinido de saídas de rótulo, com 10 classes possíveis. O tensor keep_prob é usado para controlar a taxa de dropout, e nós o inicializamos como um placeholder ao invés de uma variável imutável porque queremos usar o mesmo tensor tanto para treinamento (quando dropout é definido para 0.5) quanto para testes (quando dropout é definido como 1.0).

      Os parâmetros que a rede atualizará no processo de treinamento são os valores weight e bias, portanto, precisamos definir um valor inicial em vez de um placeholder vazio. Esses valores são essencialmente onde a rede faz seu aprendizado, pois são utilizados nas funções de ativação dos neurônios, representando a força das conexões entre as unidades.

      Como os valores são otimizados durante o treinamento, podemos defini-los para zero por enquanto. Mas o valor inicial realmente tem um impacto significativo na precisão final do modelo. Usaremos valores aleatórios de uma distribuição normal truncada para os pesos. Queremos que eles estejam próximos de zero, para que possam se ajustar em uma direção positiva ou negativa, e um pouco diferente, para que gerem erros diferentes. Isso garantirá que o modelo aprenda algo útil. Adicione estas linhas:

      main.py

      
      weights = {
          'w1': tf.Variable(tf.truncated_normal([n_input, n_hidden1], stddev=0.1)),
          'w2': tf.Variable(tf.truncated_normal([n_hidden1, n_hidden2], stddev=0.1)),
          'w3': tf.Variable(tf.truncated_normal([n_hidden2, n_hidden3], stddev=0.1)),
          'out': tf.Variable(tf.truncated_normal([n_hidden3, n_output], stddev=0.1)),
      }
      

      Para o bias ou tendência, usamos um pequeno valor constante para garantir que os tensores se ativem nos estágios iniciais e, portanto, contribuam para a propagação. Os pesos e tensores de bias são armazenados em objetos de dicionário para facilitar o acesso. Adicione este código ao seu arquivo para definir cada bias:

      main.py

      
      biases = {
          'b1': tf.Variable(tf.constant(0.1, shape=[n_hidden1])),
          'b2': tf.Variable(tf.constant(0.1, shape=[n_hidden2])),
          'b3': tf.Variable(tf.constant(0.1, shape=[n_hidden3])),
          'out': tf.Variable(tf.constant(0.1, shape=[n_output]))
      }
      

      Em seguida, configure as camadas da rede definindo as operações que manipularão os tensores. Adicione estas linhas ao seu arquivo:

      main.py

      
      layer_1 = tf.add(tf.matmul(X, weights['w1']), biases['b1'])
      layer_2 = tf.add(tf.matmul(layer_1, weights['w2']), biases['b2'])
      layer_3 = tf.add(tf.matmul(layer_2, weights['w3']), biases['b3'])
      layer_drop = tf.nn.dropout(layer_3, keep_prob)
      output_layer = tf.matmul(layer_3, weights['out']) + biases['out']
      

      Cada camada oculta executará a multiplicação da matriz nas saídas da camada anterior e os pesos da camada atual e adicionará o bias a esses valores. Na última camada oculta, aplicaremos uma operação de eliminação usando nosso valor keep_prob de 0.5.

      O passo final na construção do gráfico é definir a função de perda que queremos otimizar. Uma escolha popular da função de perda nos programas do TensorFlow é a cross-entropy ou entropia cruzada, também conhecida como log-loss, que quantifica a diferença entre duas distribuições de probabilidade (as predições e os rótulos). Uma classificação perfeita resultaria em uma entropia cruzada de 0, com a perda completamente minimizada.

      Também precisamos escolher o algoritmo de otimização que será usado para minimizar a função de perda. Um processo denominado otimização gradiente descendente é um método comum para encontrar o mínimo (local) de uma função, tomando etapas iterativas ao longo do gradiente em uma direção negativa (descendente). Existem várias opções de algoritmos de otimização de gradiente descendente já implementados no TensorFlow, e neste tutorial vamos usar o otimizador Adam. Isso se estende à otimização de gradiente descendente usando o momento para acelerar o processo através do cálculo de uma média exponencialmente ponderada dos gradientes e usando isso nos ajustes. Adicione o seguinte código ao seu arquivo:

      main.py

      
      cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=Y, logits=output_layer))
      train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
      

      Agora acabamos de definir a rede e a construímos com o TensorFlow. O próximo passo é alimentar os dados através do gráfico para treiná-los e, em seguida, testar se realmente aprendeu alguma coisa.

      Passo 5 — Treinando e Testando

      O processo de treinamento envolve alimentar o dataset de treinamento através do gráfico e otimizar a função de perda. Toda vez que a rede itera um lote de mais imagens de treinamento, ela atualiza os parâmetros para reduzir a perda, a fim de prever com mais precisão os dígitos exibidos. O processo de teste envolve a execução do nosso dataset de teste através do gráfico treinado e o acompanhamento do número de imagens que são corretamente previstas, para que possamos calcular a precisão.

      Antes de iniciar o processo de treinamento, definiremos nosso método de avaliação da precisão para que possamos imprimi-lo em mini-lotes de dados enquanto treinamos. Estas declarações impressas nos permitem verificar que, da primeira iteração até a última, a perda diminui e a precisão aumenta; elas também nos permitem rastrear se executamos ou não repetições suficientes para alcançar um resultado consistente e ideal:

      main.py

      correct_pred = tf.equal(tf.argmax(output_layer, 1), tf.argmax(Y, 1))
      accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
      

      Em correct_pred, usamos a função arg_max para comparar quais imagens estão sendo previstas corretamente observando output_layer (predições) e Y (labels), e usamos a função equal para retornar isso como uma lista de Booleanos. Podemos, então, converter essa lista em floats e calcular a média para obter uma pontuação total da precisão.

      Agora estamos prontos para inicializar uma sessão para executar o gráfico. Nesta sessão, vamos alimentar a rede com nossos exemplos de treinamento e, uma vez treinados, alimentamos o mesmo gráfico com novos exemplos de teste para determinar a precisão do modelo. Adicione as seguintes linhas de código ao seu arquivo:

      main.py

      
      init = tf.global_variables_initializer()
      sess = tf.Session()
      sess.run(init)
      

      A essência do processo de treinamento em deep learning é otimizar a função de perda. Aqui, pretendemos minimizar a diferença entre os rótulos previstos das imagens e os rótulos verdadeiros das imagens. O processo envolve quatro etapas que são repetidas para um número definido de iterações:

      Em cada etapa de treinamento, os parâmetros são ligeiramente ajustados para tentar reduzir a perda para a próxima etapa. À medida que o aprendizado avança, devemos ver uma redução na perda e, eventualmente, podemos parar de treinar e usar a rede como um modelo para testar nossos novos dados.

      Adicione este código ao arquivo:

      main.py

      
      # train on mini batches
      for i in range(n_iterations):
          batch_x, batch_y = mnist.train.next_batch(batch_size)
          sess.run(train_step, feed_dict={X: batch_x, Y: batch_y, keep_prob:dropout})
      
          # print loss and accuracy (per minibatch)
          if i%100==0:
              minibatch_loss, minibatch_accuracy = sess.run([cross_entropy, accuracy], feed_dict={X: batch_x, Y: batch_y, keep_prob:1.0})
              print("Iteration", str(i), "t| Loss =", str(minibatch_loss), "t| Accuracy =", str(minibatch_accuracy))
      

      Após 100 iterações de cada etapa de treinamento em que alimentamos um mini-lote de imagens através da rede, imprimimos a perda e a precisão desse lote. Observe que não devemos esperar uma perda decrescente e uma precisão crescente aqui, pois os valores são por lote, não para o modelo inteiro. Usamos mini-lotes de imagens em vez de alimentá-las individualmente para acelerar o processo de treinamento e permitir que a rede veja vários exemplos diferentes antes de atualizar os parâmetros.

      Quando o treinamento estiver concluído, podemos executar a sessão nas imagens de teste. Desta vez estamos usando uma taxa de dropout keep_prob de 1.0 para garantir que todas as unidades estejam ativas no processo de teste.

      Adicione este código ao arquivo:

      main.py

      
      test_accuracy = sess.run(accuracy, feed_dict={X: mnist.test.images, Y: mnist.test.labels, keep_prob:1.0})
      print("nAccuracy on test set:", test_accuracy)
      

      Agora é hora de executar nosso programa e ver com que precisão nossa rede neural pode reconhecer esses dígitos manuscritos. Salve o arquivo main.py e execute o seguinte comando no terminal para executar o script:

      Você verá uma saída semelhante à seguinte, embora os resultados individuais de perda e precisão possam variar um pouco:

      Output

      Iteration 0 | Loss = 3.67079 | Accuracy = 0.140625 Iteration 100 | Loss = 0.492122 | Accuracy = 0.84375 Iteration 200 | Loss = 0.421595 | Accuracy = 0.882812 Iteration 300 | Loss = 0.307726 | Accuracy = 0.921875 Iteration 400 | Loss = 0.392948 | Accuracy = 0.882812 Iteration 500 | Loss = 0.371461 | Accuracy = 0.90625 Iteration 600 | Loss = 0.378425 | Accuracy = 0.882812 Iteration 700 | Loss = 0.338605 | Accuracy = 0.914062 Iteration 800 | Loss = 0.379697 | Accuracy = 0.875 Iteration 900 | Loss = 0.444303 | Accuracy = 0.90625 Accuracy on test set: 0.9206

      Para tentar melhorar a precisão do nosso modelo, ou para saber mais sobre o impacto dos hiperparâmetros de ajuste, podemos testar o efeito de alterar a taxa de aprendizado, o limite de dropout, o tamanho do lote e o número de iterações. Também podemos alterar o número de unidades em nossas camadas ocultas e alterar a quantidade das próprias camadas ocultas, para ver como diferentes arquiteturas aumentam ou diminuem a precisão do modelo.

      Para demonstrar que a rede está realmente reconhecendo as imagens desenhadas à mão, vamos testá-la em uma única imagem nossa.

      Primeiro, faça o download dessa amostra de imagem de teste ou abra um editor gráfico e crie sua própria imagem de 28x28 pixels de um dígito.

      Abra o arquivo main.py no seu editor e adicione as seguintes linhas de código ao topo do arquivo para importar duas bibliotecas necessárias para a manipulação de imagens.

      main.py

      
      import numpy as np
      from PIL import Image
      ...
      

      Em seguida, no final do arquivo, adicione a seguinte linha de código para carregar a imagem de teste do dígito manuscrito:

      main.py

      
      img = np.invert(Image.open("test_img.png").convert('L')).ravel()
      

      A função open da bibliotecaImage carrega a imagem de teste como um array 4D contendo os três canais de cores RGB e a transparência Alpha. Esta não é a mesma representação que usamos anteriormente ao ler o dataset com o TensorFlow, portanto, precisamos fazer algum trabalho extra para corresponder ao formato.

      Primeiro, usamos a função convert com o parâmetro L para reduzir a representação 4D RGBA para um canal de cor em escala de cinza. Aarmazenamos isso como um array numpy e o invertemos usando np.invert, porque a matriz atual representa o preto como 0 e o branco como 255, porém, precisamos do oposto. Finalmente, chamamos ravel para achatar o array.

      Agora que os dados da imagem estão estruturados corretamente, podemos executar uma sessão da mesma forma que anteriormente, mas desta vez apenas alimentando uma imagem única para teste. Adicione o seguinte código ao seu arquivo para testar a imagem e imprimir o rótulo de saída.

      [labe main.py]
      prediction = sess.run(tf.argmax(output_layer,1), feed_dict={X: [img]})
      print ("Prediction for test image:", np.squeeze(prediction))
      

      A função np.squeeze é chamada na predição para retornar o único inteiro da matriz (ou seja, para ir de [2] para 2). A saída resultante demonstra que a rede reconheceu essa imagem como o dígito 2.

      Output

      Prediction for test image: 2

      Você pode tentar testar a rede com imagens mais complexas - dígitos que se parecem com outros dígitos, por exemplo, ou dígitos que foram mal desenhados ou desenhados incorretamente - para ver como ela se sai.

      Conclusão

      Neste tutorial você treinou com sucesso uma rede neural para classificar o dataset MNIST com cerca de 92% de precisão e testou em uma imagem sua. O estado da arte em pesquisa atual alcança cerca de 99% neste mesmo problema, usando arquiteturas de rede mais complexas envolvendo camadas convolucionais. Elas usam a estrutura 2D da imagem para melhor representar o conteúdo, ao contrário do nosso método que achata todos os pixels em um vetor de 784 unidades. Você pode ler mais sobre esse tópico no website do TensorFlow, e ver os documentos de pesquisa detalhando os resultados mais precisos no wesite do MNIST.

      Agora que você sabe como construir e treinar uma rede neural, pode tentar usar essa implementação em seus próprios dados ou testá-la em outros datasets populares, como o Google StreetView House Numbers, ou o dataset CIFAR-10 para um reconhecimento de imagem mais genérico.

      Por Ellie Birbeck



      Source link

      Como Construir um Classificador de Machine Learning em Python com Scikit-learn


      Introdução

      Machine learning ou Aprendizado de máquina é um campo de pesquisa em ciência da computação, inteligência artificial, e estatística. O foco do Machine Learning é treinar algoritmos para aprender padrões e fazer previsões a partir de dados. Machine learning é especialmente valioso porque ele nos leva a utilizar computadores para automatizar o processo de tomada de decisões.

      Você encontrará aplicações de Machine learning em todos os lugares. Netflix e Amazon usam machine learning para fazer novas recomendações de produtos. Bancos usam machine learning para detectar atividades fraudulentas em transações de cartões de crédito, e empresas de assistência à saúde estão começando a usar machine learning para monitorar, avaliar e diagnosticar pacientes.

      Neste tutorial vamos implementar um algoritmo simples de machine learning em Python utilizando Scikit-learn, uma ferramenta de machine learning para Python. Usando um banco de dados de informações sobre tumores de câncer de mama, iremos usar um classificador Naive Bayes (NB) que prevê se um tumor é maligno ou benigno.

      No final deste tutorial, você saberá como construir o seu próprio modelo de machine learning em Python.

      Pré-requisitos

      Para completar este tutorial, você precisará de:

      Passo 1 — Importando o Scikit-learn

      Vamos começar instalando o módulo Python Scikit-learn, um das melhores e mais bem documentadas bibliotecas de machine learning para Python.

      Para começar com nosso projeto de codificação, vamos ativar nosso ambiente de programação Python 3. Certifique-se de estar no diretório onde o seu ambiente está localizado, e execute o seguinte comando:

      Com seu ambiente de programação ativado, verifique se o módulo Scikit-learn já está instalado:

      • python -c "import sklearn"

      Se o sklearn estiver instalado, este comando irá completar sem erros. Se ele não estiver instalado, você verá a seguinte mensagem de erro:

      Output

      Traceback (most recent call last): File "<string>", line 1, in <module> ImportError: No module named 'sklearn'

      A mensagem de erro indica que o módulo sklearn não está instalado, então baixe o biblioteca usando o pip:

      • pip install scikit-learn[alldeps]

      Quando a instalação estiver concluída, inicie o Jupyter Notebook:

      No Jupyter, crie um novo Python Notebook chamado ML Tutorial. Na primeira célula do Notebook, importe o módulo sklearn.

      ML Tutorial

      
      import sklearn
      
      

      Seu notebook deve se parecer com a figura a seguir:

      Agora que temos o sklearn importado em nosso notebook, podemos começar a trabalhar com o dataset para o nosso modelo de machine learning.

      Passo 2 — Importando o Dataset do Scikit-learn

      O dataset com o qual estaremos trabalhando neste tutorial é o Breast Cancer Wisconsin Diagnostic Database. O dataset inclui várias informações sobre tumores de câncer de mama, bem como rótulos de classificação como malignos ou benignos. O dataset tem 569 instâncias, ou dados, sobre 569 tumores e inclui informações sobre 30 atributos, ou características, tais como o raio do tumor, textura, suavidade, e área.

      Utilizando este dataset, construiremos um modelo de machine learning para utilizar as informações sobre tumores para prever se um tumor é maligno ou benigno.

      O Scikit-learn vem instalado com vários datasets que podemos carregar no Python, e o dataset que queremos está incluído. Importe e carregue o dataset:

      ML Tutorial

      
      ...
      
      from sklearn.datasets import load_breast_cancer
      
      # Carregar o dataset
      data = load_breast_cancer()
      
      

      A variável data representa um objeto Python que funciona como um dicionário. As chaves importantes do dicionário a considerar são os nomes dos rótulos de classificação (target_names), os rótulos reais (target), os nomes de atributo/característica (feature_names), e os atributos (data).

      Atributos são uma parte crítica de qualquer classificador. Os atributos capturam características importantes sobre a natureza dos dados. Dado o rótulo que estamos tentando prever (tumor maligno versus benigno), os possíveis atributos úteis incluem o tamanho, raio, e a textura do tumor.

      Crie novas variáveis para cada conjunto importante de informações e atribua os dados:

      ML Tutorial

      
      ...
      
      # Organizar nossos dados
      label_names = data['target_names']
      labels = data['target']
      feature_names = data['feature_names']
      features = data['data']
      
      

      Agora temos listas para cada conjunto de informações. Para entender melhor nosso conjunto de dados, vamos dar uma olhada em nossos dados imprimindo nossos rótulos de classe, o primeiro rótulo da instância de dados, nossos nomes de características, e os valores das características para a primeira instância de dados.

      ML Tutorial

      
      ...
      
      # Olhando para os nossos dados
      print(label_names)
      print(labels[0])
      print(feature_names[0])
      print(features[0])
      
      

      Você verá os seguintes resultados se você executar o código:

      Como mostra a imagem, nossos nomes de classes são malignant and benign (maligno e benigno), que são então mapeados para valores binários de 0 e 1, onde 0 representa tumores malignos e 1 representa tumores benignos. Portanto, nossa primeira instância de dados é um tumor maligno cujo raio médio é 1.79900000e+01.

      Agora que temos nossos dados carregados, podemos trabalhar com eles para construir nosso classificador de machine learning.

      Passo 3 — Organizando Dados em Conjuntos

      Para avaliar o desempenho de um classificador, você deve sempre testar o modelo em dados não visualizados. Portanto, antes da construção de um modelo, divida seus dados em duas partes: um conjunto de treinamento e um conjunto de testes.

      Você usa o conjunto de testes para treinar e avaliar o modelo durante o estágio de desenvolvimento. Então você usa o modelo treinado para fazer previsões no conjunto de testes não visualizado. Essa abordagem lhe dá uma noção do desempenho e robustez do modelo.

      Felizmente, o sklearn tem uma função chamada train_test_split(), que divide seus dados nesses conjuntos. Importe a função e em seguida utilize-a para dividir os dados:

      ML Tutorial

      
      ...
      
      from sklearn.model_selection import train_test_split
      
      # Dividir nossos dados
      train, test, train_labels, test_labels = train_test_split(features,
                                                                labels,
                                                                test_size=0.33,
                                                                random_state=42)
      
      

      A função divide aleatoriamente os dados usando o parâmetro test_size. Neste exemplo, agora temos um conjunto de testes (test) que representa 33% do dataset original. Os dados restantes (train) formam então os dados de treinamento. Também temos os respectivos rótulos para ambas as variáveis train/test, ou seja, train_labels e test_labels.

      Agora podemos passar para o treinamento do nosso primeiro modelo.

      Passo 4 — Construindo e Avaliando o Modelo

      Existem muitos modelos para machine learning, e cada modelo tem seus pontos fortes e fracos. Neste tutorial, vamos nos concentrar em um algoritmo simples que geralmente funciona bem em tarefas de classificação binária, a saber Naive Bayes (NB).

      Primeiro, importe o módulo GaussianNB. Em seguida inicialize o modelo com a função GaussianNB(), depois treine o modelo, ajustando-o aos dados usando gnb.fit():

      ML Tutorial

      
      ...
      
      from sklearn.naive_bayes import GaussianNB
      
      # Inicializar nosso classificador
      gnb = GaussianNB()
      
      # Treinar nosso classificador
      model = gnb.fit(train, train_labels)
      
      

      Depois de treinarmos o modelo, podemos usar o modelo treinado para fazer previsões no nosso conjunto de teste, o que fazemos utilizando a função predict(). A função predict() retorna uma matriz de previsões para cada instância de dados no conjunto de testes. Podemos então, imprimir nossas previsões para ter uma ideia do que o modelo determinou.

      Utilize a função predict() com o conjunto test e imprima os resultados:

      ML Tutorial

      
      ...
      
      # Fazer previsões
      preds = gnb.predict(test)
      print(preds)
      
      

      Execute o código e você verá os seguintes resultados:

      Como você vê na saída do Jupyter Notebook, a função predict() retornou uma matriz de 0s e 1s que representa nossos valores previstos para a classe tumor (maligno vs. benigno).

      Agora que temos nossas previsões, vamos avaliar o desempenho do nosso classificador.

      Passo 5 — Avaliando a Precisão do Modelo

      Usando a matriz de rótulos de classe verdadeira, podemos avaliar a precisão dos valores previstos do nosso modelo comparando as duas matrizes (test_labels vs. preds). Utilizaremos a função accuracy_score() do sklearn para determinar a precisão do nosso classificador de machine learning.

      ML Tutorial

      
      ...
      
      from sklearn.metrics import accuracy_score
      
      # Avaliar a precisão
      print(accuracy_score(test_labels, preds))
      
      

      Você verá os seguintes resultados:

      Como você vê na saída, o classificador NB é 94.15% preciso. Isso significa que 94,15 porcento do tempo o classificador é capaz de fazer a previsão correta se o tumor é maligno ou benigno. Esses resultados sugerem que nosso conjunto de características de 30 atributos são bons indicadores da classe do tumor.

      Você construiu com sucesso seu primeiro classificador de machine learning. Vamos reorganizar o código colocando todas as declarações import no topo do Notebook ou script. A versão final do código deve ser algo assim:

      ML Tutorial

      
      from sklearn.datasets import load_breast_cancer
      from sklearn.model_selection import train_test_split
      from sklearn.naive_bayes import GaussianNB
      from sklearn.metrics import accuracy_score
      
      # Carregar o dataset
      data = load_breast_cancer()
      
      # Organizar nossos dados
      label_names = data['target_names']
      labels = data['target']
      feature_names = data['feature_names']
      features = data['data']
      
      # Olhando para os nossos dados
      print(label_names)
      print('Class label = ', labels[0])
      print(feature_names)
      print(features[0])
      
      # Dividir nossos dados
      train, test, train_labels, test_labels = train_test_split(features,
                                                                labels,
                                                                test_size=0.33,
                                                                random_state=42)
      
      # Inicializar nosso classificador
      gnb = GaussianNB()
      
      # Treinar nosso classificador
      model = gnb.fit(train, train_labels)
      
      # Fazer previsões
      preds = gnb.predict(test)
      print(preds)
      
      # Avaliar a precisão
      print(accuracy_score(test_labels, preds))
      
      

      Agora você pode continuar trabalhando com seu código para ver se consegue fazer com que seu classificador tenha um desempenho ainda melhor. Você pode experimentar com diferentes subconjuntos de características ou mesmo tentar algoritmos completamente diferentes. Confira o website do Scikit-learn para mais ideias sobre machine learning.

      Conclusão

      Neste tutorial, você aprendeu como construir um classificador de machine learning em Python. Agora você pode carregar dados, organizar dados, treinar, prever e avaliar classificadores de machine learning em Python usando o Scikit-learn. Os passos deste tutorial devem ajudá-lo a facilitar o processo de trabalhar com seus próprios dados no Python.

      Traduzido Por Fernando Pimenta



      Source link