Toolkit de sobrevivência em GPU para a era da IA
(journal.hexmos.com)- O desenvolvimento de IA já não se basta apenas com a execução sequencial típica da CPU; é preciso entender o modelo de processamento massivamente paralelo da GPU para lidar corretamente com o desempenho de treinamento e inferência
- Em CPUs de consumo, o normal é ter 2 a 16 núcleos, com força em tarefas de thread única e ramificações condicionais; já a GPU usa milhares de pequenos núcleos, sendo vantajosa para operações matriciais, processamento de imagens e deep learning
- A AWS oferece ambientes de execução com GPU como P3/P4, P5/Inf1, G4 e Amazon SageMaker; a p3.2xlarge custa cerca de US$ 3,06 por hora, a p5.48xlarge US$ 98,32, e a g4dn.xlarge US$ 0,526
- O CUDA da NVIDIA permite que o desenvolvedor lide diretamente com o fluxo de execução paralela que vai da alocação de memória da GPU à cópia de dados, execução de kernels e compilação
- Exemplos de soma de arrays, geração de Mandelbrot e uma CNN para classificar gatos e cães mostram como dividir loops sequenciais em threads de GPU; no Mandelbrot, o tempo caiu de 4,07 segundos na CPU para 0,0046 segundo na GPU
Por que só conhecimento de CPU não basta
- Muitos desenvolvedores aprenderam e resolveram problemas de uma forma centrada na CPU, mas a CPU funciona basicamente apoiada em uma arquitetura sequencial
- CPUs tradicionais executam instruções de forma linear e otimizam um pequeno número de núcleos potentes para desempenho em thread única
- Em situações que exigem processar várias tarefas ao mesmo tempo, o custo de tratar cada tarefa em sequência aumenta por causa do modelo de execução sequencial
- Embora seja possível melhorar o desempenho com multithreading, a filosofia básica de projeto da CPU ainda está mais próxima da execução sequencial
Modelos de IA e processamento paralelo
- Arquiteturas modernas de IA, como Transformers, usam processamento paralelo para aumentar o desempenho de treinamento
- RNNs operam sequencialmente, mas Transformers como o GPT conseguem processar várias palavras ao mesmo tempo, melhorando a eficiência de treinamento e a capacidade do modelo
- O treinamento paralelo viabiliza modelos maiores, e modelos maiores se tornam a base para gerar saídas melhores
- O paralelismo se aplica não só ao processamento de linguagem natural, mas também ao reconhecimento de imagens
- AlexNet é um exemplo de processamento simultâneo de várias partes de uma imagem para identificar padrões
- Por seu projeto focado no desempenho em thread única, a CPU tem dificuldade para distribuir e executar com eficiência a grande quantidade de cálculos paralelos necessária em modelos complexos de IA
Como a GPU reduz gargalos
- A GPU é projetada com uma estrutura que usa muitos núcleos pequenos e especializados, em vez dos núcleos grandes e potentes da CPU
- O paralelismo da GPU aparece bem em workloads que repetem em grande escala o mesmo tipo de operação, como renderização gráfica e cálculos matemáticos complexos
- Frameworks de deep learning como TensorFlow são otimizados para aproveitar o desempenho da GPU e acelerar treinamento e inferência de modelos
- O treinamento de redes neurais envolve muitas operações matriciais, e a GPU é forte em paralelizar esse tipo de operação graças ao seu grande número de núcleos
Diferenças de papel entre CPU e GPU
-
CPU
- A CPU é projetada com foco em processamento sequencial, sendo forte em tarefas que executam linearmente um único fluxo de instruções
- É adequada para computação de propósito geral, tarefas de sistema e processamento de algoritmos complexos com ramificações condicionais
- CPUs de consumo geralmente têm uma quantidade relativamente pequena de núcleos, na faixa de 2 a 16 núcleos
- Cada núcleo pode processar de forma independente seu próprio conjunto de instruções
-
GPU
- A GPU é projetada com uma arquitetura paralela, eficiente para processar muitas subtarefas ao mesmo tempo
- É vantajosa para renderização gráfica, cálculos matemáticos complexos e execução de algoritmos paralelizáveis
- Divide uma tarefa em unidades paralelas menores para processar várias operações simultaneamente
- Os núcleos de GPU frequentemente chegam aos milhares e são organizados em streaming multiprocessors (SMs) ou estruturas semelhantes
- É adequada para tarefas que lidam com muitos dados ao mesmo tempo, como processamento de imagem e vídeo, deep learning e simulações científicas
Ambientes de GPU disponíveis na AWS
- A AWS oferece várias instâncias com GPU que podem ser usadas em tarefas como machine learning
-
Instâncias de GPU de uso geral
-
Instâncias otimizadas para inferência
- Inferência é o processo de inserir dados em tempo real em um modelo de IA treinado para fazer previsões ou resolver tarefas
- P5 e Inf1 são voltadas para inferência de machine learning quando baixa latência e eficiência de custo são importantes
- A p5.48xlarge custa US$ 98,32 por hora e oferece 8 GPUs NVIDIA H100 com 80 GB de memória cada, totalizando 640 GB de memória de vídeo
-
Instâncias otimizadas para gráficos
- G4 instances são projetadas para processar tarefas intensivas em gráficos
- Desenvolvedores de videogames podem usar instâncias G4 para renderizar gráficos 3D de jogos
- A g4dn.xlarge custa US$ 0,526 por hora e usa 1 GPU NVIDIA T4 com 16 GB de memória
-
Serviço gerenciado de machine learning
- Amazon SageMaker é um serviço gerenciado para machine learning que oferece acesso a instâncias baseadas em GPU, como P3, P4 e P5
- O SageMaker é adequado para organizações que querem começar com machine learning sem gerenciar diretamente a infraestrutura subjacente
- A documentação de preços do Amazon SageMaker é fornecida separadamente
Uso básico do NVIDIA CUDA
- CUDA é uma plataforma de computação paralela e um modelo de programação desenvolvidos pela NVIDIA, permitindo executar aplicações rapidamente usando aceleradores de GPU
- Os exemplos mostram o fluxo de desenvolvimento em CUDA: alocação de memória na GPU, cópia de dados, execução de kernel e recuperação dos resultados
-
Fluxo de instalação
- Baixe o base installer e o driver installer em CUDA
- Adicione as seguintes variáveis de ambiente ao
.bashrcna pasta homeexport PATH="/usr/local/cuda-12.3/bin:$PATH"export LD_LIBRARY_PATH="/usr/local/cuda-12.3/lib64:$LD_LIBRARY_PATH"
- Execute os seguintes comandos
sudo apt-get install cuda-toolkitsudo apt-get install nvidia-gds
- Reinicie o sistema para aplicar as mudanças
-
Comandos úteis de verificação
lspci | grep VGA: identifica e lista as GPUs do sistemanvidia-smi: fornece informações detalhadas como uso, temperatura e consumo de memória das GPUs NVIDIAsudo lshw -C display: fornece informações sobre controladores de display, como placas gráficasinxi -G: mostra informações do subsistema gráfico, incluindo GPU e displaysudo hwinfo --gfxcard: usado para verificar informações detalhadas sobre a placa gráfica do sistema
Paralelizando soma de arrays com CUDA
- Soma de arrays é um problema adequado para explicar paralelização em GPU
- Os arrays de exemplo são
A = [1,2,3,4,5,6],B = [7,8,9,10,11,12], e o resultado éC = [8,10,12,14,16,18] - A abordagem com CPU percorre os elementos do array um a um e realiza a soma
- À medida que a quantidade de dados cresce, o tempo do método sequencial aumenta, enquanto a GPU pode executar simultaneamente operações como
1+7,2+8e3+9 - O exemplo em CUDA usa um arquivo de kernel
.cu__global__indica uma função de kernel chamada na GPUvectorAddrecebe três ponteiros inteiros,a,bec, e realiza a soma de vetoresthreadIdx.xobtém o índice da thread atual- Cada thread armazena em
c[i]a soma do elemento correspondente
- A função
mainsegue a ordem de alocação de memória na GPU, cópia de dados, execução do kernel e cópia do resultadocudaMallocaloca na GPU memória paracudaA,cudaBecudaCcudaMemcpycopiaaebdo host para a GPUvectorAdd <<<1, sizeof(a) / sizeof(a[0])>>>executa o kernel- O vetor de resultado
cudaCé copiado da GPU para o host
- Para compilar e executar, usa-se o comando
nvcc - O código completo é disponibilizado
Uso de GPU na geração de imagens em Python
- A geração do conjunto de Mandelbrot é uma tarefa que cria padrões visuais complexos com base no comportamento dos números em uma equação específica, e é intensiva em recursos
- O exemplo em Python baseado em CPU percorre cada pixel e calcula o valor de Mandelbrot, levando 4,07 segundos para gerar uma imagem de 1024×1536
- A versão acelerada por GPU usa a biblioteca Numba
- O decorador
@jitrealiza compilação Just-In-Time, convertendo código Python em código de máquina cuda.jitcriamandel_gpu, edevice=Trueé definido para que ele execute na GPUmandel_kernelé executado em uma GPU CUDA e divide a tarefa de geração do Mandelbrot entre threads da GPU
- O decorador
create_fractal_gpurealiza alocação de memória na GPU, configuração de threads e blocos, execução do kernel da GPU, sincronização e cópia do resultado- Usa
threadsperblock = (16, 16) cuda.synchronize()aguarda a conclusão do trabalho da GPUd_image.copy_to_host(image)copia o resultado para o lado da CPU
- Usa
- O tempo de execução na GPU é de 0,0046 segundo, muito mais rápido que o código baseado em CPU
- O código completo é disponibilizado
Treinando uma rede neural para classificar gatos e cães com GPU
- Para mostrar como GPUs são usadas em IA, é usado um exemplo de rede neural que distingue gatos e cães
- Os itens de preparação são CUDA e TensorFlow
- O TensorFlow pode ser instalado com
pip install tensorflow[and-cuda] - O dataset usado é o Kaggle Dogs vs. Cats
- Após o download, organize as imagens de gatos e cães em subpastas diferentes dentro da pasta de treinamento
- O TensorFlow pode ser instalado com
- O modelo usa uma rede neural convolucional (CNN)
- pandas e numpy são usados para manipulação de dados
Sequentialé usado para empilhar linearmente as camadas da rede neuralConvolution2D,MaxPooling2D,DenseeFlattensão camadas que compõem a CNNImageDataGeneratoré usado para aumento de dados em tempo real durante o treinamento
- Os dados de treinamento são carregados com
ImageDataGenerator- Nos dados de treinamento, são aplicados
rescale=1./255,shear_range=0.2,zoom_range=0.2ehorizontal_flip=True - As imagens de entrada são configuradas com tamanho
(64, 64), batch size 32 e modo de classificação binária
- Nos dados de treinamento, são aplicados
- A estrutura da CNN é composta por convolução, max pooling, flattening, camadas Dense e saída sigmoid
- O modelo é compilado com o otimizador
adam, a lossbinary_crossentropye a métricaaccuracy - O treinamento é executado com
epochs=25evalidation_steps=2000, e salvo em um arquivo.h5comclassifier.save('trained_model.h5') - O código de inferência carrega
trained_model.h5, converte a imagem para(64, 64)e imprimedogse o valor previsto for 0,5 ou maior; caso contrário, imprimecat - O código completo é disponibilizado
Escopo de uso da GPU
- Na era da IA, é difícil ignorar os recursos da GPU, e desenvolvedores precisam entender melhor suas capacidades
- Ao migrar de algoritmos sequenciais para algoritmos paralelizados, a GPU se torna uma ferramenta para acelerar cálculos complexos
- A capacidade de processamento paralelo da GPU é especialmente vantajosa para lidar com grandes datasets e arquiteturas complexas de redes neurais em tarefas de IA e machine learning
- A GPU é usada além do machine learning tradicional, também em pesquisa científica, simulações e tarefas intensivas em dados
- A capacidade de processamento paralelo é aplicada à solução de problemas em várias áreas, como descoberta de novos medicamentos, modelagem climática e simulações financeiras
1 comentários
Opiniões no Hacker News
O código desse texto está errado. Nenhum kernel CUDA é chamado: https://github.com/RijulTP/GPUToolkit/blob/f17fec12e008d0d37...
90% do tempo gasto para “calcular” o conjunto de Mandelbrot com código compilado por JIT é usado na compilação da função, não no cálculo em si
Se você quer aprender CUDA de verdade, implementar multiplicação de matrizes é um bom exercício, e estes tutoriais valem como referência: https://cnugteren.github.io/tutorial/pages/page1.html e https://siboehm.com/articles/22/CUDA-MMM
Ela recebe os vetores de ponto flutuante de 32 bits X e Y e o escalar A, multiplica cada X[i] por A e soma o resultado a Y[i]: https://developer.nvidia.com/blog/six-ways-saxpy/
O texto afirma que “todo desenvolvedor deveria saber”, mas, na prática, parece mais um texto sobre como GPUs são usadas em IA. A maioria dos desenvolvedores não é desenvolvedor de IA, nem interage diretamente com IA ou usa GPUs diretamente
Além disso, quase não aborda gráficos 3D, que são a principal razão pela qual GPUs passaram a existir
Com conhecimento básico, também fica mais fácil entender as histórias de “IA” que vendem para os gerentes
A atitude de “não preciso de áreas adjacentes” era algo que eu via com frequência na escola. Em administração de sistemas, meus colegas diziam que não precisavam saber programação, mas scripting era necessário; em uma escola de desenvolvimento de software, diziam que não precisavam saber redes, mas alguns anos depois DevOps apareceu amplamente nas vagas
Se o texto tem cerca de 1.500 palavras, mesmo lendo como estudo leva algo como 12 minutos, e passar umas 2 horas executando os exemplos de código não é um grande investimento. Claro, isso pressupõe que o texto seja uma boa introdução
curl. Ainda sou desenvolvedor embarcado, mas desde então aprendi bastante sobre backend, frontend e infraestrutura, e parece bem provável que, nos próximos anos, aconteça algo parecido no setor inteiro em torno de IAAcho que o motivo de Python dominar em IA é que a relação Python-C se parece com a relação CPU-GPU
GPUs têm desempenho muito alto, mas são difíceis de programar diretamente, então as pessoas lidam com GPUs por meio de chamadas a APIs de alto nível como PyTorch
C também tem bom desempenho, mas é difícil de programar, então Python é usado como uma camada de abstração sobre C
Não está claro se as pessoas realmente precisam entender GPUs com tanta profundidade. Isso vale ainda mais se você não está entrando a fundo em treinamento ou operação de IA; e, se a Lei de Moore acabar e multithreading se tornar a principal forma de ganhar velocidade, é bem provável que surjam novas linguagens alinhadas ao paradigma de programação paralela. Mojo parece ser o ponto de partida disso
Algo projetado para, desde cálculos iterativos simples, usar inteligentemente todos os núcleos da CPU em paralelo por trás de cada instrução e passar para a GPU o que fosse possível
Fico curioso se já houve tentativas assim, ou se isso sequer é possível
A explicação de que “quando a CPU encontra várias tarefas, ela aloca recursos para processar uma tarefa de cada vez” é simplista demais. Chego até a pensar que seria bom se CPUs ainda fossem simples assim
É justo que o texto se concentre no modelo de programação, mas, do ponto de vista de desempenho, dizer que “a CPU executa instruções sequencialmente” é basicamente errado. Pipelines executam instruções em paralelo, há SIMD, e vários núcleos também podem trabalhar juntos no mesmo problema
A grande diferença é que CPUs gastam muito silício e energia com tratamento de fluxo de controle para executar uma única thread com eficiência, enquanto GPUs usam esses recursos em mais unidades de cálculo e executam inúmeras threads para esconder latências de fluxo de controle e de memória
Dizer que CPUs são boas para código serial e GPUs são boas para código paralelo é correto até certo ponto, mas é uma aproximação bem grosseira. Assumindo um orçamento de energia parecido, na faixa de centenas de watts, uma CPU tem cerca de 100 “núcleos” que executam tarefas independentes uma a uma, incluindo hyper-threading, e esconde a latência de memória com previsão de desvios e pipelining
Uma GPU tem cerca de 100 “unidades de computação”, e cada unidade intercala a execução de cerca de 80 tarefas independentes, escondendo a latência de memória ao executar a próxima instrução de outra tarefa
A terminologia é bem confusa, e é bastante provável que a CPU tenha uma unidade vetorial de 256 bits de largura, enquanto a GPU tenha uma unidade vetorial de 2048 bits de largura, mas, olhando de um pouco mais longe, as duas arquiteturas parecem bem parecidas
O nome poderia ser Xeon Chi
Como a maioria das linguagens de programação foi projetada para processamento sequencial, como uma CPU, enquanto Erlang/Elixir foi projetado para paralelismo, como uma GPU, fico curioso se Nx / Axon vai ganhar tração: https://github.com/elixir-nx/
Preciso de um guia de compra. Quero saber qual é o mínimo que se deve gastar e qual é a melhor opção em cada faixa de orçamento. O problema é que essas informações mudam de tempos em tempos, e não sei se existe algum material mantido sempre atualizado
https://colab.google/
https://www.kaggle.com/docs/notebooks
https://www.paperspace.com/gradient/free-gpu
Então voltamos de novo aos textos clickbait do tipo “o que todo desenvolvedor precisa saber”?
Gosto quando um texto encara a complexidade de frente e, como conheço em alguma medida tanto os métodos quantitativos quanto os detalhes qualitativos de áreas como hardware de computadores, fico feliz quando um artigo explica direito os detalhes de um campo
Por exemplo, é outra questão se todo programador precisa saber “What every programmer should know about memory”, mas um bom programador deveria ter uma noção de como o computador realmente funciona. O ponto central a tirar daquele texto, a localidade, costuma surgir naturalmente em código bom: rápido, fácil de acompanhar e bem adequado ao problema
É um bom texto, mas as instâncias AWS P5, junto com P4d e P4de, são claramente voltadas para treinamento, não para inferência. Os tipos de instância mais adequados para inferência são G4dn e G5, que usam GPUs T4 e A10G, respectivamente
Sou praticamente iniciante em programação em GPU, mas li este texto com interesse. É surpreendente ver que a área avançou a ponto de ser tão fácil treinar uma rede neural simples de “cachorro ou gato”