Gradientes Adagrad, Adadec e a biblioteca MPI em GPU com C++

Estamos atualmente explorando o AdaGrad e uma variante, AdaDec⇑-Gradient Descent (Ada-Dec), neste artigo. A escolha do algoritmo a ser usado para o treinamento de um algoritmo de descida de gradiente estocástico dependerá da natureza do problema a ser resolvido. Em nosso próprio trabalho de reconhecimento da fala estamos usando a nova variante ʼAdaDec” de AdaGrad. Nossa variante ʼAdaGrad” é uma versão do AdaGrad com uma taxa de aprendizagem não-negativa explícita e um kernel linear de um único parâmetro. Também consideramos ʼAdaDec” a principal variante do AdaGrad, descrita em detalhes nos dois últimos artigos desta série. Neste artigo mostramos uma maneira diferente e mais geral de combinar máquinas vetoriais de suporte linear não-negativo com a descida de gradiente estocástico AdaGrad para aprendizagem de algoritmos de descida de gradiente estocástico. Em particular, consideramos uma ampla gama de tipos de problemas, com vários parâmetros de estocasticidade. Aplicamos esta abordagem, que atinge um desempenho muito forte em muitas das aplicações do código.

Também descrevemos uma simplificação significativa do código que diminui significativamente o tamanho do código de teste, sem comprometer sua qualidade. Finalmente, para ilustrar alguns dos resultados desta abordagem, fornecemos uma ilustração da máquina vetorial de suporte não-negativo com sua camada gradiente de descida combinada com uma máquina vetorial de suporte linear estocástico.

Aprendizagem simultânea em computação de alto desempenho com AdaGrad

Apresentaremos o algoritmo AdaGrad para aprendizado simultâneo em grandes espaços latentes, e apresentaremos o AdaGrad com AdaGradGrad. Usamos um grande conjunto de algoritmos AdaGrad na aplicação paralela a um sistema de computação paralela com vários núcleos. Combinando diferentes técnicas de aprendizagem, como a descida de gradiente local, o kernel AdaGrad pode ser aplicado através de vários núcleos para uma paralelização eficiente. Ele também torna o cálculo mais simples em comparação com os métodos tradicionais de machine learning (referência: o que é machine learning) para a paralelização em larga escala. Usando um classificador distribuído, ganhamos uma descida de gradiente em dados complexos sobre um ambiente altamente complexo para encontrar as funções ótimas de ativação a partir das variáveis latentes de muitas amostras. O núcleo do AdaGrad é escalável porque não precisa acompanhar o estado durante o treinamento.

Análise Dimensional usando a biblioteca MLE C++

Embora a biblioteca padrão já contenha uma grande quantidade de ferramentas para análise dimensional, ainda é preciso recorrer ao uso da GPU para realizar a análise dimensional. Entretanto, as GPUs só são capazes de realizar cálculos numéricos simples e podem facilmente levar à perda de precisão. Para superar esta limitação, introduzimos a nova biblioteca MLE-MPI para análise dimensional usando a biblioteca MLE C++. Apresentamos quatro abordagens diferentes para realizar a análise dimensional com a GPU:

A biblioteca mleFunc suporta a análise dimensional em matrizes únicas e múltiplas.

A biblioteca mleFunc suporta a análise dimensional em matrizes simples e múltiplas. A biblioteca mleMpI é baseada na biblioteca MPI.

A biblioteca mleMpI é baseada na biblioteca MPI. A biblioteca MLE-HDF combina as funções MPI para realizar análise de dimensão única e de múltiplas dimensões que tem a mesma velocidade e desempenho das bibliotecas acima.

A biblioteca MPI combina as funções MPI para realizar análise de dimensão única e de múltiplas dimensões que tem a mesma velocidade e desempenho que as bibliotecas acima. As bibliotecas C++ AMP usam bibliotecas MPI, que não funcionam por muito tempo na GPU devido à sua latência computacional.

Testamos o desempenho das quatro bibliotecas e as comparamos com a implementação da CPU. Em um único nó de CPU, as bibliotecas MPI têm um bom desempenho (menos de 50% do tempo da CPU). Entretanto, em um cluster de 6 nós de CPU, a biblioteca MPI-C++ tem um desempenho significativamente melhor – ela corresponde à implementação da CPU em 60-70% do tempo da CPU. Da mesma forma, com o MPI-HDF, o desempenho combina muito melhor com a implementação da CPU. Entretanto, com MPI-CUDA, o desempenho é menor que a implementação da CPU e é ainda pior quando se usa MPI-HDF, e para MPI-CUDA, o desempenho não é nem mesmo próximo da implementação da CPU. Note que a implementação da GPU em CUDA e CUDA-MC usa dois MPIs diferentes; a CUDA-MC implementa dois MPIs e pode rodar em uma GPU e em máquinas com vários núcleos de CPU, enquanto a CUDA e a CUDA-MC usam MPI tanto para a GPU quanto para a performance da CUDA.

O uso da biblioteca de MPI com CUDA e CUDA-MC (neste caso de teste) leva à seguinte imagem: as bibliotecas de MPIs parecem estar super otimizadas. Uma abordagem melhor seria projetar a biblioteca MPI apenas para funcionar bem em nós de CPU com vários núcleos para melhorar a performance, depois reutilizá-la ao usar a MPI-CUDA. Se você tiver tempo, esta é uma otimização que vale a pena realizar.

Ao utilizar a biblioteca MPI com CUDA e CUDA-MC neste caso de teste, o desempenho é pior que a implementação da CPU e ligeiramente pior que o MPI-HDF. Não posso dizer muito sobre a diferença ao usar CUDA-MC e CUDA, porque a biblioteca CUDA usa dois MPIs, então não tenho idéia de como comparar a CPU com o MPI nessa implementação. Entretanto, encontrei uma implementação CUDA-CPU escrita pela NVIDIA que vale a pena dar uma olhada.

A biblioteca MPI na verdade não tem nenhuma informação de paralelismo sobre os dados que ela trata. Portanto, você precisa entender os tipos de dados para escrever o código correto. Por exemplo, ela não permite que você faça coisas como o uso em pontos ou flutuações em contextos numéricos. Você também não pode realizar os seguintes comandos:

MPI_Init(&numNamedObjects); MPI_SetMaxNumNamedObjects(&numNamedObjects);

Como explicado acima, a biblioteca MPI fará um trabalho em todos os membros MPI do objeto para inicializar todos os membros simultaneamente. Neste caso, o código acabaria funcionando lentamente devido à grande quantidade de comunicação com a biblioteca MPI. Também poderia ser útil poder passar em um conjunto específico de valores de parâmetros que seriam usados pela biblioteca MPI ao executar o trabalho paralelo.

Por outro lado, o trabalho paralelo ainda é executado com a mesma base de código e, portanto, tem que funcionar mais lentamente em máquinas com vários núcleos. Isto porque o trabalho paralelo depende do tamanho dos conjuntos de dados que a biblioteca de MPI está gerenciando e pode assim ocupar uma quantidade maior de espaço de dados.

Note que a MPI tem que gerenciar duas estruturas de dados diferentes:

m_pvPar.Data: é usado para armazenar todos os itens de trabalho que podem ser executados em um trabalho paralelo. É semelhante, de uma maneira, à forma como o código MPI armazena as estruturas de dados que a biblioteca MPI gerencia.

Também é usado para armazenar todos os itens de trabalho que podem ser executados em um trabalho paralelo. É semelhante, de uma maneira, ao modo como o código MPI armazena as estruturas de dados que a biblioteca MPI gerencia.

m_cpArr.Data: Contém todos os dados para cada um dos itens de trabalho que podem ser executados em um trabalho paralelo. É similar ao modo como o código MPI lida com a matriz MPI_COUNTERS. As únicas diferenças estão na forma como eles são gerenciados.

Ao executar qualquer item de trabalho em paralelo, o tempo de execução precisa garantir que a biblioteca MPI seja responsável por lidar com MPI_COUNTERS e executar os processos do trabalhador. O tempo de execução também pode ser gerenciado.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *