BT

Início Artigos Conquistando os desafios de preparação de dados para manutenção preditiva

Conquistando os desafios de preparação de dados para manutenção preditiva

Favoritos

Pontos Principais

  • O Aprendizado de Máquina (ML) desempenha um papel significativo na área industrial de IoT (IIoT) para gerenciamento de dados e análise preditiva;
  • As Aplicações de Manutenção Preditiva (PdM) visam aplicar ML nos conjuntos de dados da IIoT, a fim de reduzir os riscos ocupacionais, o tempo de inatividade da máquina e outros custos;
  • Aprenda sobre os desafios de preparação de dados enfrentados pelos profissionais industriais de ML e as soluções para ingestão de dados e aprendizado de máquina aplicada, relacionados a PdM;
  • Processos de ingestão de dados podem ser mais fáceis de desenvolver e gerenciar usando uma ferramenta de gerenciamento de fluxo de dados, como StreamSets ou Apache Nifi;
  • Os algoritmos Long Short-Term Memory (LSTM) são normalmente usados para prever eventos raros em dados de séries temporais. Como as falhas geralmente são muito raras em aplicativos PdM, os algoritmos LSTM são adequados nessa modelagem.

O Aprendizado de Máquina (ML) tornou possível aos tecnólogos fazer coisas incríveis com dados. Sua chegada coincide com a evolução dos sistemas de fabricação em rede impulsionados pela IoT, também conhecida como IoT Industrial (IIoT), o que levou a um crescimento exponencial dos dados disponíveis para modelagem estatística.

As aplicações de manutenção preditiva (PdM) visam aplicar ML nos conjuntos de dados da IIoT, com o intuito de reduzir os riscos ocupacionais, o tempo de inatividade da máquina e outros custos, detectando o momento que as máquinas exibem características associadas a falhas passadas. Ao fazer isso, o PdM fornece aos operadores de fábrica informações que podem ajudá-los a executar ações preventivas ou corretivas, como:

  • executar máquinas em velocidade mais baixa ou menor pressão para adiar uma falha total;
  • ter equipamento sobressalente disponível no local;
  • agendar a manutenção em horários convenientes.

A implementação do PdM envolve um processo que começa com a preparação de dados e termina com o ML aplicado. Os profissionais sabem bem que a maior parte do esforço necessário para o uso efetivo ML lida com a preparação de dados, e ainda, esses desafios continuam sendo sub-representados na literatura de ML, no qual os autores tendem a demonstrar conceitos com conjuntos de dados planejados.

Neste artigo, serão abordados alguns dos mais difíceis desafios de preparação de dados enfrentados pelos profissionais de ML na área da indústria, discutindo soluções para ingestão de dados e aprendizado de máquina aplicada relacionados ao PdM.

Ingestão de dados

O primeiro passo necessário para o PdM envolve a aquisição de dados. A instrumentação industrial é mais frequentemente associada a medições de grandezas físicas, como vibração, calor de infravermelho, corrente elétrica, partículas de metal em graxa, etc. Esses dados normalmente são originados de sensores conectados a controladores lógicos programáveis dentro de uma rede de controle industrial. Os portais de dados que conectam redes corporativas e de controle facilitam o acesso a esses dados por meio de protocolos amigáveis de desenvolvimento, como REST e MQTT. Também é importante considerar as fontes de dados externos, como logs do operador ou de dados climáticos, porque elas também podem conter sinais relacionados a eventos de falha. A Figura 1 a seguir ilustra as interconexões entre esses tipos de dados.

Figura 1. Interconexões entre os ativos de dados da IIoT 

Design do pipeline de dados

A ingestão de dados é realizada por processos que coletam e armazenam dados continuamente. Esses processos podem ser implementados como aplicativos personalizados, mas geralmente são muito mais fáceis de desenvolver e gerenciar usando uma ferramenta de gerenciamento de fluxo de dados, como o StreamSets ou o Apache Nifi. Essas ferramentas oferecem diversas vantagens para criar e gerenciar pipelines de dados, como:

  1. Simplificando o desenvolvimento e implantação de pipeline. Ambientes de desenvolvimento integrados (IDEs), como os fornecidos pelo StreamSets e Nifi, ajudam a minimizar o código necessário para a criação de pipelines. Eles também integram funcionalidades de monitoramento e depuração de fluxo de dados além de suportar processos de DevOps, como controle de versão e entrega contínua.
  2. Prevenindo falhas devido à escala, desvio de esquema e migrações de topologia. As ferramentas de gerenciamento de fluxo de dados podem atuar como um importante agente de mudança dentro de um sistema distribuído. Eles fornecem maneiras razoáveis ​​de escalonamento para lidar com o aumento de carga. Por exemplo, o StreamSets utiliza o Kubernetes para escalabilidade elástica e espera-se que o Nifi faça o mesmo no futuro próximo. As fontes de dados também podem introduzir alterações significativas ao desenvolver esquemas de linha de base. O StreamSets e o Nifi permitem manipular o desvio de esquema com estágios de validação de dados que redirecionam ou reformatam mensagens rapidamente. As topologias de infraestrutura também podem mudar ao longo da vida de uma aplicação. O StreamSets e o Nifi permitem definir fluxos de dados independentes de topologia que podem ser executados em toda a infraestrutura de borda, interna/local (on-premise), na nuvem e em nuvem híbrida, sem sacrificar a resiliência ou a privacidade dos dados.

Para ter uma ideia das ferramentas de gerenciamento de fluxo de dados, foi preparado um projeto StreamSets simples que pode executar ser executado em um laptop com o Docker. Este projeto demonstra um pipeline que transmite dados de séries temporais gravados de um sistema industrial de aquecimento, ventilação e de ar condicionado (HVAC) em um OpenTSDB para uma visualização no Grafana.

  1. Crie uma rede docker para ligar os containers:
    docker network create mynetwork
  1. Inicie o StreamSets, OpenTSDB e Grafana:
    docker run -it -p 18630:18630 -d --name sdc --network mynetwork \
    streamsets/datacollector
    docker run -dp 4242:4242 --name hbase --network mynetwork \
    petergrace/opentsdb-docker
    docker run -d -p 3000:3000 --name grafana --network mynetwork \
    grafana/grafana
  1. Abra o Grafana em http://localhost:3000 e efetue o acesso como admin / admin.
  1. Adicionehttp://hbase:4242 como uma fonte de dados do OpenTSDB para o Grafana. Se não souber como adicionar uma fonte de dados, consulte os documentos do Grafana. Sua definição de fonte de dados devem se parecer com a captura de tela mostrada na Figura 2 a seguir.

Figura 2. Definição da fonte de dados do OpenTSDB no Grafana

  1. Baixe este painel do Grafana..
  1. Importe o arquivo baixado no Grafana. Se não souber como importar um painel, consulte os documentos do Grafana. Sua caixa de diálogo de importação deve parecer com a captura de tela a seguir.


Figura 3. Tela de importação do painel no Grafana

  1. Baixe, descompacte, e copie estes dados HVAC para o container do StreamSets:
    unzip mqtt.json.gz
    docker cp mqtt.json sdc:/tmp/mqtt.json
  1. Abra o StreamSets no http://localhost:18630 e acesse como admin / admin.
  1. Baixe e importe o seguinte pipeline no StreamSets. Se não souber como importar um pipeline, leia a documentação do StreamSets.
  1. Haverá uma mensagem de aviso sobre uma ausência de uma biblioteca no estágio "Parse MQTT JSON". Clique naquele estágio e siga as instruções de como instalar a biblioteca Jython.

Figura 4. O aviso mostrado aqui indica a ausência de uma biblioteca que precisa ser instalada.

  1. Execute o pipeline do StreamSets. Após alguns minutos, o painel do StreamSets deve mostrar uma tela similar a mostrada na Figura 5 a seguir.

Figura 5. Painel do fluxo de dados do StreamSets.

  1. Após deixar o pipeline rodar por alguns minutos, o painel do Grafana apresentará algo similar à tela apresentada na Figura 6.

Figura 6. Painel do Grafana com dados da execução do pipeline.

Espero que, configurando este pipeline e explorando o StreamSets, seja possível entender a essência do que as ferramentas de gerenciamento de fluxo de dados podem fazer.

Destinos de um Pipeline - Arquivo, Tabela ou Stream?

Os pipelines de dados têm um começo e um fim (também conhecido como uma fonte e um coletor). Como mencionado anteriormente, as APIs MQTT e REST geralmente são usadas para ler dados, mas o término dos pipelines varia muito, dependendo do caso de uso. Por exemplo, se planejar simplesmente arquivar dados para estar em conformidade com as regulamentações, pode encerrar os pipelines em um arquivo porque os arquivos são fáceis de criar e compactar para minimizar o custo de armazenamento. Se o seu objetivo é desenvolver painéis e alertas em uma ferramenta de monitoramento como o Grafana para métricas-chave em uma linha de montagem, é possível enviar a um banco de dados de séries temporais como o OpenTSDB. No caso do PdM, existem outros requisitos que entram em ação ao determinar como persistir os dados. Vamos considerar as vantagens relativas de arquivos, tabelas e fluxos para determinar a melhor maneira de projetar pipelines de dados para o PdM:

Arquivos: Arquivos podem ser usados para ler e gravar dados de forma eficiente. Eles também podem ser facilmente comprimidos e movimentados se não forem muito grandes. No entanto, os problemas surgem quando os arquivos passam a ser grandes (pense em gigabytes) ou se tornam numerosos (pense em milhares) tornando-se difíceis de gerenciar. Além de ser difícil de mover, pesquisar e atualizar dados em arquivos grandes, podem se tornar extremamente lento, pois seu conteúdo não é indexado. Além disso, embora os arquivos forneçam o máximo de flexibilidade para salvar dados em qualquer formato, eles não possuem funções internas para validação de esquema. Portanto, se esquecer de validar e descartar registros corrompidos antes de salvá-los, será forçado a resolver a difícil tarefa de limpeza de dados mais adiante.

Streams: como o Apache Kafka, streams são fluxos que foram projetados para distribuir dados para qualquer número de consumidores por meio de uma interface de publicação/assinatura. Isso é útil ao executar vários processadores de dados (como tarefas de inferência de ML), de modo que nem todos precisam se conectar a fontes de dados brutos, o que seria redundante e não seria escalável. Como arquivos, os fluxos podem ingerir dados muito rapidamente. Diferente de arquivos, os fluxos fornecem a capacidade de validar dados recebidos em esquemas (como o uso de "case classes" com o Apache Spark - que será mostrado mais adiante). A desvantagem de encerrar pipelines como fluxos é que eles são imutáveis. Quando os dados estão em um fluxo, eles não podem ser modificados. A única maneira de atualizar dados em um fluxo é copiá-lo para um novo fluxo. A imutabilidade nos dados de treinamento é indesejável para o PdM porque evita que os recursos, como a Vida Útil restante (RUL - Remaining Useful Life), sejam atualizados retroativamente após a ocorrência de eventos importantes, como falhas da máquina.

Tabelas de banco de dados: validação de esquema? Sim. Atualizável? Sim. Indexável? Sim! Índices de tabela são especialmente úteis se o banco de dados fornecer índices secundários, pois eles podem acelerar solicitações que consultam mais de uma variável. Foi mencionado as vantagens das interfaces pub/sub para fluxos; Bancos de dados também podem oferecer essas vantagens? A resposta é novamente sim, supondo que o banco de dados forneça fluxos de captura de dados alterados (CDC - Changed Data Capture). A única desvantagem dos bancos de dados é que os dados não podem ser gravados o mais rápido possível como em arquivos ou fluxos. No entanto, existem inúmeras maneiras de acelerar as gravações. Uma maneira é finalizar um pipeline em um fluxo. Fluxos podem servir a dois propósitos neste caso. Primeiro, eles podem armazenar em buffer envios de alta velocidade, e segundo, eles podem distribuir pipelines de alta velocidade para vários consumidores, que podem escalar para atingir coletivamente o throughput de gravação necessário para um banco de dados. Isso é eficaz quando os fluxos e o banco de dados são executados na mesma plataforma de dados subjacente, como é o caso do MapR. Também é importante notar que o MapR-DB fornece índices secundários e fluxos de CDC.

MapR como uma plataforma de dados para PdM

O IIoT precisa de uma plataforma de dados que é dimensionada em termos de velocidade e capacidade. Além disso, o desenvolvimento dos modelos precisa que os engenheiros da ML possam interagir com os conceitos rapidamente. O MapR faz isso convergindo fluxos, bancos de dados e armazenamento de arquivos em uma plataforma de dados de alto desempenho que é dimensionada linearmente e fornece recursos que capacitam os cientistas de dados a explorar dados, desenvolver modelos e operacionalizar esses modelos sem atrito.

Feature Engineering

O potencial do ML reside na sua capacidade de encontrar padrões generalizáveis dos dados. Embora as estatísticas tradicionais usem frequentemente técnicas de redução de dados para consolidar amostras de dados, o ML prospera em conjuntos de dados com fidelidade (linhas) e dimensionalidade (colunas). Para lhe dar uma apreciação da quantidade de dados que os modelos de inferência de PdM precisam digerir, considere isto:

  • Os processos de fabricação podem ser instrumentados por dispositivos que medem centenas de métricas em velocidades que às vezes excedem milhares de amostras por segundo (por exemplo, sensores de vibração);
  • As falhas são geralmente pouco frequentes (por exemplo, uma vez por mês);
  • O ML só pode prever eventos que podem ser generalizados a partir dos dados de treinamento. Se os eventos forem raros, a coleta de dados deve ser muito mais longa. Uma boa regra é treinar modelos com conjuntos de dados que abrangem várias centenas de eventos.

Assim, dada a natureza dos dados da IIoT serem densos com fidelidade e dimensionalidade, e considerando que o PdM depende de ver centenas de exemplos de falhas pouco frequentes, a plataforma de dados usada para armazenar dados de treinamento deve ser dimensionada não apenas em termos de velocidade de ingestão e armazenamento expansível, mas também em termos das operações usadas para encontrar e derivar recursos relevantes nos dados de treinamento. Esse processo, conhecido como feature engineering, é essencial para o sucesso do ML, pois é o ponto em que o conhecimento específico do domínio entra em ação.

Criação de Features

A feature engineering frequentemente envolve a adição de novas colunas aos dados de treinamento para facilitar a análise manual ou automatizada. Esses features podem ajudar as pessoas a explorar dados com ferramentas analíticas e são frequentemente críticos para que os algoritmos de ML detectem padrões desejáveis. A criação de features pode acontecer aumentando os dados brutos durante a ingestão ou atualizando os dados de treinamento retroativamente. Para ver como isso funciona, vamos ver um exemplo.

Um conjunto de dados IIoT simples para um processo de fabricação é representado a seguir:

Figura 7. Conjunto de dados do sensor IIoT.

Os atributos de Time, Device ID e as três métricas chamadas x, y e z representam as medidas de desempenho de uma rede de controle. Quando expandimos a tabela para incluir logs do operador e outras origens de dados, parecerá desta forma:

Figura 8. Conjunto de dados da IIoT com detalhes do operador e do tempo.

O que deve ter acontecido para adicionar as colunas operador e clima nesta tabela?

Fontes de dados em redes corporativas e de controle geralmente não são sincronizadas. Portanto, os registros de data e hora nos registros do operador e do clima serão diferentes da data e hora nos dados da IIoT. A unificação desses atributos de data e hora preserva um nível de densidade que é vantajoso para fazer perguntas, como "mostra todos os valores de IoT para máquinas operadas por Joe". Esse tipo de integração de dados é adequado para um trabalho em lotes noturno, pois poderia levar um tempo grande para atualizar o valor de um dia de registros de IIoT. Também é mais conveniente pagar a penalidade da integração de dados de uma vez (por exemplo, todas as noites) do que repetir essa tarefa toda vez que alguém quiser acessar os campos IoT e log juntos em uma consulta baseada em tempo. Portanto, ao observar a tabela anterior, reconheça que, nos bastidores, uma tarefa Spark, ou alguma outra tarefa de integração de dados, deve ter se juntado a logs de operador/clima com logs IIoT e unificado por um atributo de data e hora.

Há muitas formas de implementar essa tarefa, mas quando esses logs residem em diferentes conjuntos de dados, combiná-los em uma tabela de recursos unificada é lento. É por isso que é importante que os pipelines de dados coloquem dados em uma plataforma em que a integração de dados possa ser executada com o mínimo de movimentação de dados.

Extração de features

A extração de features envolve a combinação de variáveis para produzir campos mais úteis. Por exemplo, pode ser útil dividir um campo de data e hora em partes/componentes, para que seja possível treinar um subconjunto de dados por horas do dia, dias da semana, fases da lua (quem sabe, certo?), e assim por diante. Esse tipo de extração de recurso é fácil de implementar dentro de um "batch job" ou em um "steaming job", pois eles podem ser implementados em linguagens como Java, Python e Scala, que incluem bibliotecas projetadas para facilitar a manipulação de data/hora. Ao implementar uma função SQL para determinar se um valor de data/hora cai em um fim de semana é muito mais difícil. Adicionar um _weekendattribute ao aumentar uma tabela de recursos dentro de tarefas de fluxo contínuo ou em lote poderia facilitar muito a análise manual e ajudar os algoritmos de ML a generalizar padrões durante a semana de trabalho.

Features de Atraso

O PdM é um aprendizado de máquina supervisionado porque envolve a construção de um modelo para prever rótulos, com base em como esses rótulos são mapeados para os recursos nos dados de treinamento. Os dois rótulos mais comuns usados para o PdM são:

  1. A possibilidade de falha nas próximas n etapas (por exemplo, "Sobre a falhar");
  2. O tempo (ou ciclos da máquina) deixado antes da próxima falha (por exemplo, "Vida útil restante").

O primeiro recurso pode ser previsto usando um modelo de classificação binária que gera as probabilidades de falha dentro de uma janela de tempo prescrita (por exemplo, "tenho 90% de certeza de que uma falha ocorrerá nas próximas 50 horas"). O segundo recurso pode ser previsto usando um modelo de regressão que produz um valor para a vida útil restante (RUL - Remaining Useful Life). Essas variáveis estão atrasadas, o que significa que elas não podem receber um rótulo até que ocorra um evento de falha.

Figura 9. Conjunto de dados IIoT antes de ocorrer o evento de falha

Quando ocorre uma falha, os valores podem ser calculados para essas variáveis de atraso e aplicadas retroativamente à tabela de recursos.

Figura 10. Conjunto de dados com vida útil restante

Se os eventos de falha forem raros, mas os dados de IIoT forem frequentes, os recursos atrasados de rotulação retroativa podem levar a uma atualização de tabela massivamente grande. Na próxima seção, falarei sobre como duas tecnologias, o Apache Spark e o MapR-DB, podem funcionar juntas para solucionar esse desafio.

Feature engineering escalável com o MapR-DB + Spark

As tabelas de recursos para PdM podem facilmente exceder a capacidade do que pode ser armazenado e processado em um único computador. A distribuição desses dados em um cluster de máquinas pode aumentar a capacidade, mas não faça isso se, no final, ainda precisar transferir os dados de volta para uma única máquina para o treinamento de análise e modelo. Para evitar a movimentação de dados e pontos únicos de falha, o armazenamento e a computação precisam ser distribuídos. O Apache Spark e o MapR-DB fornecem uma solução conveniente para essa tarefa.

O MapR-DB é um banco de dados NoSQL distribuído que fornece a escalabilidade e flexibilidade de esquema necessária para construir grandes tabelas de recursos. O Apache Spark fornece funcionalidade de computação distribuída que permite que as tabelas de recursos sejam analisadas além dos limites do que pode caber na memória em uma única máquina. O conector MapR-DB para Spark elimina a necessidade de copiar tabelas de recursos inteiras para os processos do Spark. Em vez disso, o MapR-DB executa localmente filtros e classificações enviados do Spark SQL e retorna apenas os dados resultantes de volta ao Spark.

Figura 11. Integração do MapR-DB e do Apache Spark

Exemplo de feature engineering para PdM

Construí um aplicativo nominal de PdM para um sistema HVAC industrial que mostra vários exemplos de feature engineering usando MapR-DB e Spark. O código e a documentação para esta demonstração podem ser encontrados neste projeto do Github. Trechos foram incluídos a seguir para ilustrar os conceitos de feature engineering discutidos anteriormente.

O pipeline de dados para este aplicativo consiste em um cliente MQTT, que publica dados de HVAC em um fluxo usando a API do Kafka. O fluxo de entrada armazena esses registros enquanto um processo Spark consome e os persiste com recursos derivados para uma tabela no MapR-DB. O pipeline é assim:

Figura 12. Pipeline de dados da IIoT.

O código Scala a seguir mostra como os registros de fluxo são lidos com o Spark:

O código anterior cria um objeto Dataset contendo registros MQTT brutos. Esse Dataset pode ser enriquecido com recursos derivados, como este:

(Observe que os sublinhados são usados no início dos nomes de campo para denotar recursos derivados).

Este conjunto de dados enriquecido é então salvo no MapR-DB usando a API OJAI, desta forma:

Até agora, a tabela de recursos no MapR-DB contém valores de sensor de HVAC e alguns recursos derivados, como _weekend, mas os valores para as variáveis de atraso AboutToFail e RemainingUsefulLife ainda não foram atribuídos. O trabalho do Spark para receber notificações de falha em um fluxo e atualizar variáveis de atraso é assim:

Quando esse script do Spark recebe uma notificação de falha, ele calcula valores para variáveis de atraso e, em seguida, aplica-os retroativamente à tabela de recursos no MapR-DB. A saída desse processo no aplicativo de demonstração é semelhante à captura de tela da Figura 13 a seguir.

Figura 13. Saída do processo de aplicativo de demonstração da IIoT.

Exemplo de Algoritmo PdM

Nas seções anteriores, foi descrito técnicas para registrar dados sobre as condições que levaram a eventos de falha, para que os modelos de PdM possam ser treinados com aprendizado de máquina supervisionado. Nesta seção, abordo o que fazer com esses dados e como realmente treinar um modelo que prevê falhas.

Os algoritmos LSTM (Long Short-Term Memory) são comumente usados ​​para prever eventos raros em dados de séries temporais. Como as falhas geralmente são muito raras em aplicativos PdM, os algoritmos LSTM são adequados para modelagem. Existem exemplos de LSTM prontamente disponíveis para as estruturas de aprendizado de máquina mais populares. Foi escolhido o Keras para implementar um algoritmo LSTM para a métrica "About To Fail" discutida anteriormente. Se preferir ver os detalhes, leia o Jupyter notebook que foi postado no GitHub.

A implementação do LSTM usa uma tabela de recursos, como o descrito anteriormente, para treinar um modelo que prevê a probabilidade de uma máquina falhar em 50 ciclos. Um bom resultado é assim:

Figura 14. Resultados da implementação do LSTM.

Esse é um bom resultado porque prevê o fracasso antes que ele realmente ocorra, e prevê isso com uma alta confiança (> 90%). Veja um exemplo em que o modelo faz uma previsão que não é útil, porque prevê falha apenas depois que a falha já ocorreu:

Figura 15. Resultado da implementação do LSTM - segundo exemplo.

É divertido brincar com diferentes conjuntos de dados de treinamento para entender melhor as sensibilidades do LSTM. No Jupyter notebook, explica-se como sintetizar um conjunto de dados e treinar o modelo, para que seja possível experimentar o LSTM no seu laptop. Intencionalmente foi usado apenas alguns recursos neste exercício para tornar as etapas de preparação de dados e implementação do LSTM mais fáceis de entender. Os detalhes podem ser obtidos novamente no repositório do GitHub.

Conclusão

O aprendizado de máquina tem o potencial de tornar as estratégias de manutenção preditiva muito mais eficazes do que os métodos convencionais usados nos últimos anos. No entanto, a manutenção preditiva apresenta desafios significativos de engenharia de dados, devido à alta quantidade de fontes de dados de IoT industriais, à raridade de falhas mecânicas na vida real e à necessidade de dados de alta resolução para modelos de treinamento. A eficácia de qualquer empreendimento para desenvolver e implantar aprendizado de máquina para aplicativos de manutenção preditiva também dependerá de uma plataforma de dados subjacente que possa atender às demandas exclusivas de armazenamento de dados, mas também de acesso a dados desimpedidos, conforme os cientistas de dados realizam iterações em conceitos de feature engineering e desenvolvimento de modelos.

Sobre o autor

Ian Downard é engenheiro de dados e desenvolvedor evangelista da MapR. Gosta de aprender e compartilhar conhecimento sobre as ferramentas e processos que permitem às equipes de DataOps colocar o aprendizado de máquina em produção. Ian coordena o Java User Group em Portland, Oregon e escreve sobre Big Data aqui e aqui.

Avalie esse artigo

Relevância
Estilo/Redação

Olá visitante

Você precisa cadastrar-se no InfoQ Brasil ou para enviar comentários. Há muitas vantagens em se cadastrar.

Obtenha o máximo da experiência do InfoQ Brasil.

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

Comentários da comunidade

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

BT

Seu cadastro no InfoQ está atualizado? Poderia rever suas informações?

Nota: se você alterar seu email, receberá uma mensagem de confirmação

Nome da empresa:
Cargo/papel na empresa:
Tamanho da empresa:
País:
Estado:
Você vai receber um email para validação do novo endereço. Esta janela pop-up fechará em instantes.