
-
Combine o aprendizado de máquina e os gráficos do Metal 4
Saiba como combinar perfeitamente o aprendizado de máquina em seus apps com recursos gráficos usando o Metal 4. Vamos apresentar o recurso de tensor e o codificador de ML para executar modelos na timeline da GPU, em paralelo às tarefas de renderização e computação. Descubra como o Shader ML permite incorporar redes neurais diretamente em seus sombreadores para criar efeitos avançados e melhorar o desempenho. Também mostraremos novas ferramentas de depuração para cargas de trabalho de ML no Metal 4 em ação usando um app de exemplo.
Capítulos
- 0:00 - Introdução
- 2:52 - O que são tensors
- 6:21 - Codificar redes de ML
- 12:51 - Incorporar ML no shader
- 20:26 - Depurar cargas de trabalho de ML
Recursos
Vídeos relacionados
WWDC25
- Aprimore jogos com o Metal 4
- Conheça o Metal 4
- Explore jogos feitos com o Metal 4
- Novidades na renderização no Metal para apps imersivos
WWDC24
-
Buscar neste vídeo...
Olá! Sou Preston Provins, engenheiro na equipe Metal Framework da Apple. Mais tarde, meu colega Scott se juntará a mim. Vou mostrar os novos recursos do Metal que combinam aprendizado de máquina e jogos, e o Scott mostrará as novas ferramentas de GPU criadas para melhorar a depuração de aprendizado de máquina no Metal 4. Nesta sessão, vou mostrar como combinar o aprendizado de máquina e os gráficos do Metal 4. Para saber tudo o que o Metal 4 oferece, confira a sessão “Fundamentos do Metal 4” para conferir as outras novidades. O aprendizado de máquina está transformando jogos e gráficos com técnicas como upscaling, compressão de ativos, combinação de animações e sombreamento neural. Essas técnicas ultrapassam a fronteira da criatividade e da imersão. Elas simulam fenômenos complexos, aprimoram a fidelidade visual e simplificam a exploração de novos estilos e efeitos. O CoreML é ideal para tarefas de aprendizado de máquina, como segmentação, classificação, IA generativa e outras. Ele facilita a criação de modelos de aprendizado de máquina. Se o seu app de aprendizado de máquina requer uma integração total com a linha do tempo da GPU, o Metal 4 é ideal para você. Em um quadro típico, um jogo pode fazer vertex skinning em um compute pass, rasterizar a cena em um render pass e aplicar antialiasing em outro compute pass. O antialiasing geralmente é feito usando técnicas de processamento de imagem, como antialiasing temporal. Técnicas de ponta substituem esses métodos tradicionais por uma rede de aprendizado de máquina. Essa rede amplia a imagem, e o restante da renderização acontece em resolução mais baixa, melhorando o desempenho.
Também está se tornando mais comum executar pequenas redes neurais dentro de um sombreador. Exemplo: um típico sombreador de fragmento criaria amostras de texturas de materiais, mas técnicas mais recentes usam redes neurais para descompactar texturas em tempo real e obter taxas de compressão mais altas. Essa técnica de renderização neural compacta conjuntos de materiais para até 50% do espaço ocupado pelo bloco. Nesta sessão, conheceremos o MTLTensors, o novo recurso do Metal 4 para fluxos de trabalho de aprendizado de máquina. Vamos ver em detalhes o novo MTL4MachineLearningCommandEncoder, que executa redes inteiras na linha do tempo da GPU junto com as outras retiradas e envios. Mostraremos o Shader ML, que permite incorporar operações de aprendizado de máquina dentro dos seus sombreadores. Por fim, mostraremos como você pode integrar perfeitamente o ML ao seu app com o Metal Debugger. Você já conhece MTLBuffers e MTLTextures. Este ano, o Metal 4 apresenta o MTLTensor, um novo recurso para aplicar aprendizado de máquina a dados com mais facilidade do que nunca. O MTLTensor é um tipo de dado essencial para aprendizado de máquina, usado em contextos de computação, gráficos e ML. As cargas de trabalho de aprendizado de máquina utilizarão muito dos tensores. O MTL4MachineLearningCommandEncoder usa MTLTensors para representar entradas e saídas, e o Shader ML usa MTLTensors para representar pesos, bem como entradas e saídas.
MTLTensors são contêineres multidimensionais para dados, descritos por uma classificação e número de dimensões para cada classificação. MTLTensors podem se estender muito além de duas dimensões, proporcionando a flexibilidade para descrever qualquer layout de dados de que precise para uso prático de aprendizado de máquina. MTLTextures, por exemplo, são limitadas a quatro canais no máximo e têm limites rígidos para as extensões que dependem do formato de textura. Para o aprendizado de máquina, é comum usar dados com mais de duas dimensões, como operações de convolução. Usar uma representação plana de dados como MTLBuffer complicaria esquemas de indexação para dados com várias dimensões. A indexação de dados multidimensionais em um MTLTensor é muito mais simples porque a etapa e a dimensão de cada classificação são incorporadas ao objeto MTLTensor e usados automaticamente nos cálculos de indexação. Vamos trabalhar no processo de criação de um MTLTensor. A classificação do MTLTensor descreve quantos eixos ele tem. Este MTLTensor tem uma classificação de dois. Ele contém linhas de colunas de dados. As extensões da dimensão descrevem a quantidade de pontos de dados ao longo desse eixo. A propriedade dataType define qual formato de dados o MTLTensor está encapsulando. As propriedades usage indicam como o MTLTensor será utilizado. MTLTensorUsageMachineLearning para MTL4MachineLearningCommandEncoder, MTLTensorUsageCompute ou MTLTensorUsageRender para uso nos programas de sombreamento. Também é possível combinar usos como a propriedade usage para texturas. Essas são as propriedades MTLTensor importantes que devem ser preenchidas em um objeto MTLTensorDescriptor. Agora vamos criar um MTLTensor no código. Com as propriedades do descritor preenchidas, crie um MTLTensor chamando newTensorWithDescriptor:offset:error: em um objeto MTLDevice. MTLTensors são criados a partir de um objeto MTLDevice ou MTLBuffer. No entanto, o MTLTensor criado a partir do dispositivo tem o melhor desempenho. De modo semelhante a como MTLTextures podem ser combinadas, criar um MTLTensor a partir do objeto MTLDevice resulta em um layout opaco otimizado para leitura e escrita. Agora, vamos nos concentrar em criar MTLTensors a partir de um MTLBuffer pré-existente. Ao contrário do MTLTensors criado a partir de um MTLDevice, os MTLTensors do MTLBuffer não são bem empacotados, então você precisa especificar as etapas. A etapa mais profunda deve ser sempre a primeira. A segunda etapa indica quantos elementos são ignorados quando o índice de linha é incrementado.
É possível que o MTLBuffer de origem contenha preenchimento, como colunas não utilizadas no final da linha. O preenchimento precisa ser contabilizado para que o MTLTensor envolva os elementos apropriados. Para criar um MTLTensor a partir do buffer subjacente, defina as propriedades dataType e usage como faria para um tensor alocado no dispositivo. Depois, defina a propriedade strides do MTLTensorDescriptor para que o MTLTensor resultante envolva corretamente o conteúdo do MTLBuffer. Por fim, use o newTensorWithDescriptor:offset:error: na origem MTLBuffer. Agora que sabemos como alocar e criar MTLTensors, vamos ver o novo codificador de aprendizado de máquina para adicionar trabalho de ML à linha do tempo da GPU. O Metal 4 permite que comandos de computação e renderização sejam facilmente adicionados à linha do tempo da GPU. Com o MTL4ComputeCommandEncoder e o MTL4RenderCommandEncoder, respectivamente. Este ano, estamos expandindo a unificação com a adição de trabalho de aprendizado de máquina à linha do tempo da GPU.
O MTL4MachineLearningCommandEncoder permite que modelos completos sejam executados e sincronizados com outros comandos do Metal na GPU e garante uma integração perfeita com outros MTLCommands. É um novo codificador de comandos de aprendizado de máquina e tem uma interface semelhante aos codificadores de computação e renderização. As primitivas de sincronização do Metal 4 também operam com comandos de aprendizado de máquina, assim como com computação e renderização. A sincronização permite o controle sobre a orquestração do trabalho e facilita a paralelização para manter o alto desempenho. O fluxo de trabalho de criação MTL4MachineLearningCommandEncoder pode ser separado em duas partes: offline e tempo de execução. A parte offline do fluxo de trabalho ocorre antes da abertura do app, e a parte de tempo de execução acontecerá durante o ciclo de vida do app, por exemplo, no meio de um quadro. Vamos começar com a parte offline do fluxo de trabalho, criando um MTLPackage. Um MTLPackage é um contêiner para uma ou mais funções que representam uma rede de ML, que você pode usar no Metal para executar o trabalho de aprendizado de máquina. Esse formato é otimizado para carregamento e execução com o Metal. Para criar um MTLPackage, primeiro você precisa ter um pacote CoreML. Aqui usamos o conversor CoreML para converter o framework do ML em que a rede foi criada, como PyTorch ou Tensorflow, em um pacote CoreML. Confira um exemplo de exportação de um modelo PyTorch usando a biblioteca CoreML Tools em Python. Basta importar as ferramentas e executar a conversão no modelo para gerar uma exportação. Por fim, salve essa exportação como um pacote de ML. Há uma coisa que gostaria de destacar aqui: Nem todo pacote CoreML é um programa de ML, e somente programas de ML são compatíveis. Se o pacote CoreML foi exportado para um sistema operacional mais antigo, leia este artigo para saber como exportar os arquivos de modelo do CoreML como um pacote de ML. Com o pacote CoreML criado, basta executar a linha de comando metal-package-builder no modelo salvo para produzir um MTLPackage. Isso converte o pacote CoreML para um formato otimizado para carregamento em tempo de execução. Então, é assim que se cria um MTLPackage. A parte offline do fluxo de trabalho é concluída, e o restante é executado em tempo de execução.
Para compilar a rede, primeiro abra o MTLPackage como uma MTLLibrary. Crie um descritor de função usando o nome da função que representa a rede no pacote. Neste caso, a função principal. Para compilar a rede, crie um MTL4MachineLearningPipelineState. Isso é feito usando um MTL4MachineLearningPipelineStateDescriptor com o descritor de função. Se a rede tiver entradas dinâmicas, especifique o tamanho de cada uma no MTL4MachineLearningPipelineStateDescriptor. Compile a rede para o dispositivo específico criando o MTL4MachineLearningPipelineState com o descritor.
É assim que um objeto MTL4MachineLearningPipelineState é criado. A próxima etapa é criar o MTL4MachineLearningCommandEncoder e o trabalho de codificação. Vamos ver o uso do objeto MTL4MachineLearningCommandEncoder para enviar o trabalho na linha do tempo da GPU. Basta criar o objeto MTL4MachineLearningCommandEncoder, assim como criaria e codificaria para computação ou renderização. Defina o objeto MTL4MachineLearningPipelineState criado e vincule entradas e saídas que estão sendo usadas. Por fim, envie o trabalho usando o método dispatchNetworkWithIntermediatesHeap.
O codificador de aprendizado de máquina usa o heap para armazenar dados intermediários entre as operações. Em vez de criar e liberar buffers, ele permite a reutilização de recursos para envios diferentes. Para criar esse MTLHeap, crie um MTLHeapDescriptor e defina a propriedade type como MTLHeapTypePlacement. Para obter o tamanho mínimo da pilha para a rede, consulte o intermediateHeapSize do pipeline e defina a propriedade size da pilha para ser maior ou igual a isso. Depois de codificar seus envios de rede, finalize a codificação e envie seus comandos para executá-los na linha do tempo da GPU.
Como já mencionado, as primitivas de sincronização do Metal 4 também operam com comandos de aprendizado de máquina, assim como com computação e renderização. O trabalho que não depende da saída do aprendizado de máquina pode ocorrer em conjunto, se sincronizado corretamente.
Apenas o trabalho que consome a saída da rede precisa aguardar a conclusão do trabalho de aprendizado de máquina programado.
Para sincronizar envios do MTL4MachineLearningCommandEncoder, você pode usar primitivas de sincronização padrão do Metal 4, como MTLBarriers e MTLFences. O novo MTLStageMachineLearning é usado para identificar cargas de trabalho de ML em barreiras. Por exemplo, para fazer seu trabalho de renderização aguardar em saídas produzidas por uma rede, use uma barreira entre o estágio de renderização apropriado e o de aprendizado de máquina. Vejamos o MTL4MachineLearningCommandEncoder em ação. Neste exemplo, o MTL4MachineLearningCommandEncoder é usado para enviar uma rede totalmente convolucional para prever valores de oclusão por pixel. A avaliação requer uma sincronização cuidadosa. O buffer de profundidade e normais de espaço de visualização são preenchidos antes de iniciar a carga de trabalho de ML. Enquanto a rede está processando os dados, o renderizador envia outras tarefas relacionadas à renderização em paralelo e aguarda os resultados neurais antes de compor o quadro final. O MTL4MachineLearningCommandEncoder não se limita a processar informações de quadro completo para jogos. Você pode usá-lo para qualquer rede que se encaixe em um orçamento em tempo real e aproveitar as primitivas de sincronização do Metal 4 para melhor atender às suas necessidades de integração. É assim que o MTL4MachineLearningCommandEncoder do Metal 4 facilita a execução de grandes cargas de trabalho de aprendizado de máquina na linha do tempo da GPU. Resumindo: O aprendizado de máquina está unindo computação e renderização no Metal 4 pelo MTL4MachineLearningCommandEncoder. O MTL4MachineLearningCommandEncoder permite que redes inteiras sejam executadas na linha do tempo da GPU. Os recursos podem ser compartilhados com outros comandos da GPU, e o conjunto robusto de primitivas de sincronização do Metal 4 ativam recursos de aprendizado de máquina de alto desempenho. O Metal 4 introduz o Shader ML para incorporar operações menores de aprendizado de máquina dentro dos kernels e sombreadores existentes. Jogos de ponta estão adotando o aprendizado de máquina para substituir os algoritmos tradicionais de renderização. As técnicas baseadas em ML podem oferecer soluções para iluminação global, sombreamento de material, compressão geométrica e de material e muito mais. Essas técnicas geralmente melhoram o desempenho ou diminuem o espaço ocupado pela memória. Um bom exemplo é a compressão de material neural, que permite até 50% de compactação em relação a formatos compactados em bloco. Com materiais tradicionais, você testa texturas de materiais, como albedo e mapas normais. Em seguida, use os valores de amostra para executar o sombreamento. Com a compactação de material neural, você cria amostras de dados de textura latente, executa inferências usando os valores amostrados e usa a saída da rede para executar o sombreamento.
Dividir cada etapa no próprio pipeline é algo ineficiente, já que cada etapa precisa sincronizar tensores para a memória do dispositivo, operar e sincronizar as saídas para operações posteriores.
Para ter o melhor desempenho, seu app deve combinar essas etapas em um único envio de sombreador. Com o Shader ML, o Metal permite que você execute sua rede de ML diretamente no sombreador de fragmento, sem precisar aprovar pela memória do dispositivo entre as etapas. Você pode inicializar tensores de entrada, executar a rede e sombrear apenas os pixels necessários a cada quadro. Isso melhora o espaço de memória de tempo de execução e o espaço em disco do jogo. Vamos ver em mais detalhes a avaliação do material neural. A inicialização de MTLTensors de entrada pode ser dividida em duas partes, carregando os pesos de redes e criando o recurso de entrada MTLTensor. O recurso de entrada MTLTensor é criado por amostragem de texturas ligadas com uma coordenada UV para o fragmento.
A inferência é onde o recurso de entrada MTLTensor é transformado por matrizes de peso aprendidas para extrair recursos, computar ativações e propagar informações por meio de camadas. Essa avaliação é repetida para várias camadas, e o resultado é um material descompactado. Por fim, os materiais descompactados são utilizados para os cálculos de sombreamento do fragmento.
Vamos ver como inicializar nossos MTLTensors de entrada com o Shader ML. Primeiro, vamos declarar um sombreador de fragmento que utilizará o Shader ML e as etapas em pesos de rede. Comece incluindo o novo cabeçalho metal_tensor. Usaremos o tipo MTLTensor para acessar os pesos de rede. Os MTLTensors são vinculados ao sombreador usando slots de vinculação de buffer. Também é possível aprovar o MTLTensors usando buffers de argumento. O tipo do MTLTensor é modelado. O primeiro argumento de modelo é o dataType do MTLTensor. Esses MTLTensors foram criados na memória do dispositivo, então usamos o qualificador de espaço de endereço do dispositivo. O segundo argumento representa as dimensões do MTLTensor e o tipo a ser usado para a indexação no MTLTensor. Aqui, estamos usando extensões para definir um tensor de classificação 2 com extensões dinâmicas. Com isso, nosso sombreador de fragmento é configurado. Vamos implementar o algoritmo de compressão de material neural. Com os pesos da rede passados, podemos criar a entrada do MTLTensor por amostragem de quatro texturas latentes. MTLTensors não são apenas um recurso que pode ser vinculado: Você pode criar um MTLTensor em linha direto dentro de seus sombreadores. Crie um MTLTensor encapsulando os valores amostrados e use-o para a avaliação da rede. Presume-se que os MTLTensors em linha sejam bem empacotadas, portanto, não é preciso aprovar pelas etapas na criação. Com isso, a inicialização dos MTLTensors de entrada está completa, e já podemos inferir valores da rede neural. A avaliação transforma entradas usando parâmetros aprendidos que são então ativados. As ativações passam pelas camadas subsequentes, e a camada final gera o material descompactado.
Este ano, o Metal introduz a Metal Performance Primitives para tornar as operações do MTLTensor acessíveis na linguagem de sombreamento. Essa biblioteca é um conjunto de APIs de alto desempenho que possibilitam soluções portáteis de desempenho nos MTLTensors. Proporciona operações de multiplicação e convolução de matrizes. A multiplicação da matriz está no centro da avaliação das redes neurais. Usaremos a implementação matmul2d fornecida pela Metal Performance Primitives para implementar uma rotina de avaliação de rede portátil de desempenho. Para começar, inclua o novo cabeçalho MetalPerformancePrimitives dentro do sombreador do Metal. Os parâmetros da multiplicação da matriz são configurados com o objeto matmul2d_descriptor. O primeiro conjunto de parâmetros de modelo especifica o tamanho do problema da multiplicação da matriz. O próximo conjunto de parâmetros de modelo controla se as entradas para a multiplicação da matriz precisam ser transpostas ao executar a operação. E o último parâmetro de modelo controla seus requisitos de precisão.
Além do descritor, a operação matmul2d deve ser especializada com o número de threads que participarão da operação. Como estamos dentro de um sombreador de fragmento, usaremos execution_thread para indicar que a multiplicação completa da matriz será feita por esse thread. Em seguida, execute a multiplicação da matriz com essa configuração.
Por fim, ative cada elemento do resultado da multiplicação da matriz usando a função de ativação ReLU. Esse processo é repetido para a segunda camada para avaliar totalmente nossa rede em nosso sombreador. Após a conclusão da avaliação, os materiais descompactados são disponibilizados para uso em sombreamento. O MTLTensor de saída contém informações de canal que podem ser usadas como qualquer outro valor amostrado de uma textura. Veja uma demonstração em tempo real da compressão de material neural em comparação com materiais tradicionais. Não há perda de qualidade percebida com o uso de materiais neurais, especialmente quando sombreados. Veja a cor base isoladamente. Ainda é muito difícil notar quaisquer diferenças entre materiais neurais e tradicionais, e ainda assim os materiais neurais usam metade da memória e ocupam metade do espaço em disco.
As operações do MTLTensor não são exclusivas de sombreadores de fragmento. Elas podem ser usadas dentro de todas as funções e estágios do sombreador. Se um simdgroup ou threadgroup inteiro estiver fazendo as mesmas operações nos mesmos dados, utilize o hardware a seu favor escolhendo um grupo de execução maior. Mas se suas operações do MTLTensor forem divergentes em relação aos dados ou exibirem um fluxo de controle não uniforme no site de chamada de operação do MTLTensor, use um único grupo de execução de thread. Outros esquemas de execução pressupõem que não há divergência e fluxo de controle uniforme para o grupo de execução. Para resumir, você pode executar operações de ML como multiplicação e convolução da matriz em seus próprios sombreadores. O Shader ML facilita a execução de várias operações de ML em um só sombreador. Isso é compatível com o cache, requer menos envios e usa menos memória, em especial ao usar redes menores. E o Shader ML oferece o controle refinado de que você precisa para criar operações personalizadas. Nunca foi tão fácil implementar técnicas avançadas de ML em seus apps do Metal. E é assim que você pode usar o Shader ML para incorporar redes neurais ao seu programa de sombreador. Agora, meu colega Scott vai mostrar como as novas ferramentas de depuração do Metal 4 facilitam a depuração de cargas de trabalho de aprendizado de máquina. Olá! Sou Scott Moyers, engenheiro de software da equipe GPU Tools. Preston mostrou um app que usa aprendizado de máquina para calcular a oclusão de ambiente. O app codifica uma rede de aprendizado de máquina diretamente no seu pipeline de renderização do Metal. Enquanto ajudava a desenvolver esse app, me deparei com um problema em que a saída tinha alguns artefatos graves. Vou ativar apenas o passe de oclusão de ambiente para destacar o problema. Deveria haver sombras nos cantos dos objetos, mas em vez disso há muito ruído, e a estrutura da cena é pouco visível. Mostrarei como usei as novas ferramentas para encontrar e corrigir esse problema. Primeiro, vamos capturar um rastreamento de GPU do app no Xcode. Para fazer isso, vou clicar no ícone do Metal na parte inferior da tela e, depois, no botão de captura.
Assim que a captura for concluída, poderei ver meu quadro capturado no resumo. O navegador de depuração à esquerda fornece a lista de comandos que o app usou para construir o quadro.
Por exemplo, o buffer de comandos offscreen contém muitos codificadores, incluindo o G-buffer pass. E o próximo buffer de comando contém meu MTL4MachineLearningCommandEncoder. O uso do Metal 4 me deu um controle refinado da sincronização e, embora eu tenha tido cuidado ao configurar barreiras e eventos entre passes dependentes, eu me perguntava se um problema de sincronização poderia estar causando esses problemas. Para verificar isso, recorri ao Visualizador de Dependências, que é uma ferramenta útil para obter uma visão geral da estrutura do seu app Metal. Vou clicar no ícone Dependências no canto superior esquerdo.
Com essa interface, posso ver todos os comandos do app junto com quaisquer primitivas de sincronização, como barreiras e eventos. A ampliação de um codificador de comandos revela ainda mais detalhes. Aqui está a conclusão do meu primeiro buffer de comando.
O comando abaixo copia os normais para um MTLTensor. Em seguida, há uma barreira seguida por um MTL4MachineLearningCommandEncoder. Vou diminuir o zoom para analisar a estrutura geral. Meu novo passe de oclusão de ambiente está no buffer de comando à direita. Antes de adicionar essa passagem, o app estava funcionando bem, então posso assumir que as dependências dentro do buffer de comandos superior e inferior estão corretas. Vou inspecionar o novo buffer de comandos que contém o MTL4MachineLearningCommandEncoder.
Antes de o buffer de comandos ser iniciado, há uma espera por um sinal de evento compartilhado. Então, no final do buffer de comandos, há um sinal para desbloquear o próximo. Não pode haver nenhum outro comando sendo executado em paralelo com o buffer de comandos. No buffer de comandos, há barreiras entre cada codificador, garantindo que cada comando seja executado um após o outro. Eu estava bastante confiante nessa fase de que não havia problemas de sincronização, pelo menos dentro desse quadro. Com isso descartado, decidi verificar o MTL4MachineLearningCommandEncoder. Ao clicar na chamada de envio para a rede de oclusão de ambiente, sou levado aos recursos vinculados dela. À direita, o editor assistente está exibindo o MTLTensor de saída. Vejo que ele tem os mesmos artefatos que o app em execução, então não está correto. Vou clicar duas vezes no MTLTensor de entrada para exibi-lo ao lado da saída. A entrada tem o que eu esperava para ver os normais do espaço. Os objetos voltados para outra direção têm intensidades de componentes diferentes. Então, o problema deve estar dentro da minha rede de aprendizado de máquina. Vamos voltar à visualização de recursos vinculados. Vou clicar duas vezes em Rede para abri-la no novo Depurador de Rede ML. Essa ferramenta é essencial para entender o que está acontecendo dentro do modelo.
Este gráfico representa a estrutura da minha rede de oclusão de ambiente. Escrevi no PyTorch, e nas fases de criação do alvo, faço o que Preston sugeriu: Exporto como um pacote CoreML e converto em um MTLPackage. As caixas são as operações, e as conexões mostram o fluxo de dados por meio do modelo, da esquerda para a direita. Eu queria descobrir qual operação era responsável pela introdução dos artefatos. Eu sabia que a saída final era ruim e que a entrada era boa, então decidi separar o gráfico para restringi-lo. Vamos escolher uma operação mais ou menos no meio. A seleção de uma operação mostra sua descrição à direita, junto com seus atributos, entradas e saídas. Além disso, posso inspecionar os dados do MTLTensor intermediário que qualquer operação produz. Vou clicar na pré-visualização para abri-la no visualizador do MTLTensor. Posso ver que os artefatos já estão presentes aqui, então vou verificar uma operação anterior.
Essa operação também tem artefatos na saída. Vamos inspecionar a entrada.
No entanto, esse MTLTensor parece estar destacando bordas na cena, o que é esperado, a entrada para a rede são as bordas extraídas do buffer de profundidade. Então, alguma coisa deve estar dando errado nessa região da rede.
Para expandir essa região costurada, clique nas setas no canto superior esquerdo da operação.
Pela ordem e os tipos dessas operações, reconheço isso como minha função SignedSmoothstep. Primeiro, ele assume o valor absoluto da entrada. Em seguida, prende o valor entre 0 e 1. Mas está elevando o resultado para a potência de si mesmo, o que não me parece certo, não me lembro de haver uma operação de potência na função SignedSmoothstep. Vamos entrar no código Python para descobrir o que está acontecendo. Vou parar a sessão de depuração e voltar para o código-fonte.
O modelo que estou executando está nessa classe chamada LightUNet. Vou navegar até a função de propagação direta para verificar se ela está fazendo o que eu espero.
A primeira operação personalizada que ela está executando é SignedSmoothstep, a região costurada que vi no depurador de rede de ML. Vou pular para a função de propagação direta.
Ela deve ser uma operação Smoothstep direta onde mantenho o sinal da entrada. Nesta linha eu posso ver a falha: Digitei muitos asteriscos fazendo do me multiplicar um operador de potência. Vamos excluir esse extra e tentar executá-lo novamente.
E aí está, uma implementação funcional de oclusão de ambiente neural usando o MTL4MachineLearningCommandEncoder integrado do Metal 4.
Nessa demonstração, mostrei como usei o Depurador do Metal para depurar um app de aprendizado de máquina do Metal 4. Primeiro, o visualizador de dependências me ajudou a validar a sincronização. Depois, inspecionei as entradas e saídas da rede com o visualizador do MTLTensor. Isso verificou que o problema estava dentro da rede. Por fim, usei o depurador de rede de ML para percorrer as operações na rede e identificar o problema.
Essas ferramentas fazem parte de uma família maior de ferramentas disponíveis para depuração e otimização de apps do Metal. Agora vamos recapitular o que abordamos hoje. O Metal 4 introduz o MTLTensor, um novo recurso multidimensional criado especificamente para dados de aprendizado de máquina. O MTLTensors fornece flexibilidade para layouts de dados complexos além de duas dimensões e, com informações de etapas e dimensões, simplifica muito a indexação. Os novos recursos do Metal 4 permitem combinar cargas de trabalho de aprendizado de máquina nos seus pipelines do Metal. O MTL4MachineLearningCommandEncoder permite que redes inteiras de aprendizado de máquina sejam executadas direto na linha do tempo da GPU. Isso permite integração e sincronização perfeitas com seu trabalho de computação e renderização. Para redes menores, o Shader ML e a biblioteca Metal Performance Primitives permitem incorporar operações de aprendizado de máquina em seus sombreadores. Por fim, o Depurador do Metal oferece uma visibilidade incrível do que está acontecendo no seu app do Metal 4. O novo depurador de rede de ML facilita a compreensão da sua rede e de como ela é executada no dispositivo. Esse tipo de insight é essencial para garantir a precisão e otimizar o desempenho. Nas próximas etapas, teste o MTL4MachineLearningCommandEncoder e o Shader ML do Metal 4, instalando o sistema operacional e o Xcode mais recentes. Para saber como as ferramentas para desenvolvedores do Metal podem ser úteis, acesse o site Apple Developer. Para aproveitar ao máximo seu app do Metal 4, veja mais palestras do Metal 4. Estamos animados para ver o que você vai criar com esses novos recursos. Obrigado.
-
-
8:13 - Exporting a Core ML package with PyTorch
import coremltools as ct # define model in PyTorch # export model to an mlpackage model_from_export = ct.convert( custom_traced_model, inputs=[...], outputs=[...], convert_to='mlprogram', minimum_deployment_target=ct.target.macOS16, ) model_from_export.save('model.mlpackage')
-
9:10 - Identifying a network in a Metal package
library = [device newLibraryWithURL:@"myNetwork.mtlpackage"]; functionDescriptor = [MTL4LibraryFunctionDescriptor new] functionDescriptor.name = @"main"; functionDescriptor.library = library;
-
9:21 - Creating a pipeline state
descriptor = [MTL4MachineLearningPipelineDescriptor new]; descriptor.machineLearningFunctionDescriptor = functionDescriptor; [descriptor setInputDimensions:dimensions atBufferIndex:1]; pipeline = [compiler newMachineLearningPipelineStateWithDescriptor:descriptor error:&error];
-
9:58 - Dispatching a network
commands = [device newCommandBuffer]; [commands beginCommandBufferWithAllocator:cmdAllocator]; [commands useResidencySet:residencySet]; /* Create intermediate heap */ /* Configure argument table */ encoder = [commands machineLearningCommandEncoder]; [encoder setPipelineState:pipeline]; [encoder setArgumentTable:argTable]; [encoder dispatchNetworkWithIntermediatesHeap:heap];
-
10:30 - Creating a heap for intermediate storage
heapDescriptor = [MTLHeapDescriptor new]; heapDescriptor.type = MTLHeapTypePlacement; heapDescriptor.size = pipeline.intermediatesHeapSize; heap = [device newHeapWithDescriptor:heapDescriptor];
-
10:46 - Submitting commands to the GPU timeline
commands = [device newCommandBuffer]; [commands beginCommandBufferWithAllocator:cmdAllocator]; [commands useResidencySet:residencySet]; /* Create intermediate heap */ /* Configure argument table */ encoder = [commands machineLearningCommandEncoder]; [encoder setPipelineState:pipeline]; [encoder setArgumentTable:argTable]; [encoder dispatchNetworkWithIntermediatesHeap:heap]; [commands endCommandBuffer]; [queue commit:&commands count:1];
-
11:18 - Synchronization
[encoder barrierAfterStages:MTLStageMachineLearning beforeQueueStages:MTLStageVertex visibilityOptions:MTL4VisibilityOptionDevice];
-
15:17 - Declaring a fragment shader with tensor inputs
// Metal Shading Language 4 #include <metal_tensor> using namespace metal; [[fragment]] float4 shade_frag(tensor<device half, dextents<int, 2>> layer0Weights [[ buffer(0) ]], tensor<device half, dextents<int, 2>> layer1Weights [[ buffer(1) ]], /* other bindings */) { // Creating input tensor half inputs[INPUT_WIDTH] = { /* four latent texture samples + UV data */ }; auto inputTensor = tensor(inputs, extents<int, INPUT_WIDTH, 1>()); ... }
-
17:12 - Operating on tensors in shaders
// Metal Shading Language 4 #include <MetalPerformancePrimitives/MetalPerformancePrimitives.h> using namespace mpp; constexpr tensor_ops::matmul2d_descriptor desc( /* M, N, K */ 1, HIDDEN_WIDTH, INPUT_WIDTH, /* left transpose */ false, /* right transpose */ true, /* reduced precision */ true); tensor_ops::matmul2d<desc, execution_thread> op; op.run(inputTensor, layerN, intermediateN); for (auto intermediateIndex = 0; intermediateIndex < intermediateN(0); ++intermediateIndex) { intermediateN[intermediateIndex, 0] = max(0.0f, intermediateN[intermediateIndex, 0]); }
-
18:38 - Render using network evaluation
half3 baseColor = half3(outputTensor[0,0], outputTensor[1,0], outputTensor[2,0]); half3 tangentSpaceNormal = half3(outputTensor[3,0], outputTensor[4,0], outputTensor[5,0]); half3 worldSpaceNormal = worldSpaceTBN * tangentSpaceNormal; return baseColor * saturate(dot(worldSpaceNormal, worldSpaceLightDir));
-
-
- 0:00 - Introdução
Apresentamos o Metal 4, que aprimora a integração de aprendizado de máquina em jogos e gráficos. O Metal 4 permite técnicas como redimensionamento, compactação de ativos e mesclagem de animações usando redes de ML, o que melhora o desempenho e a fidelidade visual. Os principais recursos incluem 'MTLTensors' para fluxos de trabalho de ML, 'MTL4MachineLearningCommandEncoder' para executar redes na linha do tempo da GPU, Shader ML para incorporar operações de ML em shaders, além de ferramentas de depuração aprimoradas. O CoreML é ideal para a criação de modelos de ML, e você pode obter uma integração perfeita da ML aos apps com a ajuda do Metal Debugger.
- 2:52 - O que são tensors
O Metal 4 apresenta o 'MTLTensor', um recurso projetado especificamente para workloads de aprendizado de máquina. 'MTLTensors' são contêineres de dados multidimensionais, permitindo a representação eficiente dos layouts de dados complexos usados em aprendizado de máquina, como aqueles necessários para operações de convolução. Eles simplificam a indexação de dados multidimensionais em comparação com representações planas como 'MTLBuffers'. Um 'MTLTensor' é definido de acordo com a classificação (número de eixos), as extensões (número de pontos de dados ao longo de cada eixo), o tipo de dados e as propriedades de uso. Essas propriedades são especificadas em um objeto 'MTLTensorDescriptor'. É possível criar 'MTLTensors' a partir de um objeto 'MTLDevice', que oferece desempenho otimizado com um layout opaco, ou de um objeto 'MTLBuffer' existente, onde é preciso especificar as etapas a serem consideradas para o preenchimento potencial.
- 6:21 - Codificar redes de ML
O Metal mais recente também apresenta o 'MTL4MachineLearningCommandEncoder', permitindo que o trabalho de aprendizado de máquina seja integrado à linha do tempo da GPU junto com comandos de computação e renderização. Esse novo codificador permite que modelos completos de ML sejam executados na GPU, sincronizando com outros comandos do Metal usando primitivas de sincronização padrão, como barreiras e cercas. O fluxo de trabalho envolve duas partes principais: offline e tempo de execução. Offline, o sistema converte um pacote CoreML em um 'MTLPackage' otimizado usando a ferramenta de linha de comando 'metal-package-builder'. Em tempo de execução, o sistema compila 'MTLPackage' em um 'MTL4MachineLearningPipelineState' e cria o 'MTL4MachineLearningCommandEncoder' configurado com o estado do pipeline, entradas e saídas, e então envia comandos codificados para a GPU. O codificador utiliza um 'MTLHeap' para armazenar dados intermediários, otimizando o uso de recursos. Isso permite a execução paralela de tarefas não dependentes, melhorando o desempenho. Os recursos de sincronização do Metal 4 garantem que as saídas de ML que consomem trabalho aguardem a conclusão da rede, o que o torna adequado para várias aplicações em tempo real, não apenas jogos.
- 12:51 - Incorporar ML no shader
O Metal 4 apresenta o Shader ML, permitindo que desenvolvedores incorporem operações de aprendizado de máquina diretamente nos shaders. Isso melhora o desempenho e reduz o uso de memória ao eliminar a necessidade de sincronizar tensores entre a memória do dispositivo e os shaders. A compressão de material neural, uma técnica específica de ML, é um exemplo. Ela comprime texturas de material em até 50% em comparação com formatos tradicionais compactados em bloco. Com o Shader ML, todo o processo de avaliação de material neural, desde a inicialização dos tensores de entrada até a inferência e sombreamento, pode ser combinado em uma única execução de shader. O Metal Performance Primitives é integrado ao Shader ML, fornecendo APIs de alto desempenho, como multiplicação e convolução de matrizes. Isso permite implementar rotinas de avaliação de redes neurais de forma eficiente dentro de fragment shaders, resultando em aplicações em tempo real sem perda perceptível de qualidade, mas com uso significativamente menor de memória e espaço em disco.
- 20:26 - Depurar cargas de trabalho de ML
No exemplo dado, com as novas ferramentas de GPU no Xcode, é possível depurar um workload de aprendizado de máquina que está causando artefatos graves no cálculo da oclusão de ambiente de um app. Você pode capturar o rastreamento da GPU e utilizar o Visualizador de Dependências para inspecionar a sincronização dos buffers de comando, descartando qualquer problema. Pode, então, examinar os tensores de entrada e saída do 'MTL4MachineLearningCommandEncoder', confirmando que o problema está na própria rede de aprendizado de máquina. Depois, pode abrir a rede no novo Depurador de Rede de ML, uma ferramenta visual que representa a estrutura do modelo (escrita em PyTorch e convertida para CoreML e MTLPackage), o que permite identificar a operação responsável pela introdução dos artefatos. Ao inspecionar o gráfico, os artefatos estão presentes na saída e na entrada de uma operação anterior, indicando um problema dentro da rede. A função 'SignedSmoothstep' é identificada como a área do problema. Ao revisar o código Python, um bug é descoberto: um asterisco extra, que faz com que o sistema interprete uma operação de multiplicação como uma operação de energia. A correção desse erro resolve o problema e a implementação de oclusão de ambiente neural usando o 'MTL4MachineLearningCommandEncoder' do Metal 4 é depurada com êxito.