Connect(); Edição Especial de 2018

Volume 33 – Número 13

Machine Learning – ML.NET: O Machine Learning Framework para Desenvolvedores do .NET

Por James McCaffrey

A biblioteca do ML.NET ainda está na versão prévia. Todas as informações estão sujeitas a alterações.

A biblioteca do ML.NET é uma nova coleção de software livre do código de aprendizado de máquina (ML) que pode ser usada para criar sistemas avançados de previsão. Muitas bibliotecas de ML são escritas em C++ com uma API do Python para facilitar a programação. Os exemplos incluem scikit-learn, TensorFlow, CNTK e PyTorch. No entanto, se você usar uma biblioteca de ML baseada em Python para criar um modelo de previsão, não será tão fácil para um aplicativo .NET usar um modelo treinado. Felizmente, a biblioteca do ML.NET pode ser usada diretamente nos aplicativos .NET. E como o ML.NET pode ser executado no .NET Core, você também pode criar sistemas de previsão para macOS e Linux.

A melhor maneira de saber o rumo que este artigo tomará é examinar o programa de demonstração na Figura 1. A demonstração cria um modelo de ML que prevê a renda anual de uma pessoa com base na sua idade, sexo e inclinação política (conservadora, moderada, liberal). Como o objetivo é prever um valor numérico, este é um exemplo de um problema de regressão. Se a meta fosse prever a inclinação política a partir da idade, sexo e renda, seria um problema de classificação.

Programa de Demonstração Herdado do ML.NET em Ação
Figura 1 Programa de Demonstração Herdado do ML.NET em Ação

A demonstração usa um conjunto de dados de treinamento fictício com 30 itens. Depois de treinado, o modelo foi aplicado aos dados de origem e atingiu um erro médio quadrático de 1,2630. Esse valor de erro é difícil de interpretar por si só e o erro de regressão é melhor usado para comparar os diferentes modelos.

A demonstração é concluída usando o modelo treinado para prever a renda anual de um homem de 40 anos com uma inclinação política conservadora. A renda prevista é US$ 72.401,38. A demonstração na Figura 1 foi escrita usando a abordagem herdada do ML.NET, que é uma boa maneira para os iniciantes terem uma ideia do ML.NET. Na segunda metade deste artigo introdutório, falarei sobre uma abordagem mais recente que é um pouco mais difícil de compreender, mas é a melhor abordagem para o novo desenvolvimento.

Este artigo pressupõe que você tenha habilidades de programação intermediárias ou melhores com C#, mas não pressupõe que você tenha familiaridade com a biblioteca ML.NET. O código completo e os dados para o programa de demonstração são apresentados neste artigo e também estão disponíveis no download do arquivo que acompanha este artigo. Enquanto escrevo este artigo, a biblioteca do ML.NET ainda está em modo de versão prévia e sendo desenvolvida muito rapidamente, portanto, algumas das informações apresentadas aqui podem ter sido um pouco alteradas no momento em que você estiver lendo esse artigo.

O programa de demonstração

Para criar o programa de demonstração, iniciei o Visual Studio 2017. A biblioteca do ML.NET funcionará com a Community Edition gratuita ou com uma das edições comerciais do Visual Studio 2017. A documentação do ML.NET declara que o Visual Studio 2017 é necessário e, de fato, não consegui fazer o programa de demonstração funcionar com o Visual Studio 2015. Criei um novo aplicativo de console em C# e o chamei de IncomePredict. A biblioteca do ML.NET funcionará com um .NET Framework clássico ou um tipo de aplicativo do .NET Core.

Depois de carregar o código de modelo, eu cliquei com o botão direito do mouse no arquivo Program.cs na janela do Gerenciador de Soluções e o renomeei como IncomeProgram.cs, permitindo que o Visual Studio renomeasse automaticamente a classe Program para mim. Em seguida, na janela do Gerenciador de Soluções, cliquei com o botão direito do mouse no projeto IncomePredict e selecionei a opção Gerenciar Pacotes NuGet. Na janela do NuGet, selecionei a guia Procurar e, em seguida, inseri “ML.NET” no campo de pesquisa. A biblioteca do ML.NET está hospedada no pacote Microsoft.ML. Selecionei a versão mais recente (0.7.0) e cliquei no botão Instalar. Depois de alguns segundos, o Visual Studio respondeu com uma mensagem “Microsoft.ML 0.7.0 instalado com êxito para IncomePredict”.

Neste ponto, fiz um Compilar | Recompilar Solução e obtive uma mensagem de erro “compatível somente com arquiteturas x64”. Na janela do Gerenciador de Soluções, cliquei com o botão direito do mouse no projeto IncomePredict e selecionei a entrada Propriedades. Na janela Propriedades, selecionei a guia Compilar à esquerda e alterei, em seguida, a entrada de destino da plataforma de “Qualquer CPU” para “x64”. Também me assegurei de que tinha como destino a versão 4.7 do .NET Framework. Com as versões anteriores do Framework, eu obtinha um erro relacionado a uma das dependências de biblioteca de matemática. Novamente, fiz Compilar | Recompilar Solução e obtive êxito. Ao trabalhar com bibliotecas de modo de versão prévia, como ML.NET, você deve esperar algumas interrupções como essa.

Os dados de demonstração

Depois de criar o esqueleto do programa de demonstração, a próxima etapa será criar o arquivo de dados de treinamento. Os dados são apresentados na Figura 2. Se você estiver acompanhando, na janela do Gerenciador de Soluções, clique com botão direito no projeto IncomePredict e selecione Adicionar | Nova Pasta e a nomeie como “Dados”. Não é necessário colocar seus dados em uma pasta chamada Dados, mas é uma prática padrão. Clique com o botão direito do mouse na pasta Dados e selecione Adicionar Novo Item. Na janela de diálogo de novo item, selecione o tipo de arquivo de texto e o chame de DadosPessoas.txt.

Figura 2 Dados de Pessoas

48, +1, 4.40, liberal
60, -1, 7.89, conservative
25, -1, 5.48, moderate
66, -1, 3.41, liberal
40, +1, 8.05, conservative
44, +1, 4.56, liberal
80, -1, 5.91, liberal
52, -1, 6.69, conservative
56, -1, 4.01, moderate
55, -1, 4.48, liberal
72, +1, 5.97, conservative
57, -1, 6.71, conservative
50, -1, 6.40, liberal
80, -1, 6.67, moderate
69, +1, 5.79, liberal
39, -1, 9.42, conservative
68, -1, 7.61, moderate
47, +1, 3.24, conservative
18, +1, 4.29, liberal
79, +1, 7.44, conservative
44, -1, 2.55, liberal
52, +1, 4.71, moderate
55, +1, 5.56, liberal
76, -1, 7.80, conservative
32, -1, 5.94, liberal
46, +1, 5.52, moderate
48, -1, 7.25, conservative
58, +1, 5.71, conservative
44, +1, 2.52, liberal
68, -1, 8.38, conservative

Copie os dados da Figura 2 e cole-os na janela do editor, tomando cuidado para não ter qualquer linha em branco extra à direita.

O conjunto de dados de 30 itens é artificial. A primeira coluna é a idade de uma pessoa. A segunda coluna indica o sexo e é pré-codificada como masculino = -1 e feminino = + 1. A biblioteca do ML.NET tem métodos para codificar os dados de texto, portanto, os dados poderiam ter usado “masculino” e “feminino”. A terceira coluna é a renda anual a prever, com valores divididos por 10.000. A última coluna especifica a inclinação política (conservadora, moderada, liberal).

Como os dados têm três variáveis de previsão (idade, sexo, política), não é possível exibi-los em um grafo bidimensional. Mas você pode ter uma boa ideia da estrutura dos dados examinando o grafo apenas da idade e da renda anual na Figura 3. O grafo mostra que a idade por si só não pode ser usada para obter uma previsão precisa da renda.

Dados de Renda
Figura 3 Dados de Renda

Depois de criar os dados de treinamento em uma pasta chamada Dados, você deve criar uma pasta chamada Modelos para manter o modelo salvo porque o código de demonstração pressupõe que exista uma pasta Modelos.

O código do programa

O código de demonstração completo, com algumas pequenas edições para economizar espaço, é apresentado na Figura 4. Depois que o código modelo foi carregado no Visual Studio, na parte superior da janela do Editor, removi todas as referências de namespace e as substitui por aquelas mostradas na lista de códigos. Os diversos namespaces Microsoft.ML abrigam toda a funcionalidade do ML.NET. O namespace Threading.Tasks é necessário para salvar ou carregar um modelo herdado do ML.NET treinado no arquivo.

Figura 4 Programa de exemplo herdado do ML.NET

using System;
using Microsoft.ML.Runtime.Api;
using Microsoft.ML.Legacy;
using Microsoft.ML.Legacy.Data;
using Microsoft.ML.Legacy.Transforms;
using Microsoft.ML.Legacy.Trainers;
using Microsoft.ML.Legacy.Models;
using System.Threading.Tasks;
// Microsoft.ML 0.7.0  Framework 4.7 Build x64
namespace IncomePredict
{
  class IncomeProgram
  {
    public class IncomeData {
      [Column("0")] public float Age;
      [Column("1")] public float Sex;
      [Column("2")] public float Income;
      [Column("3")] public string Politic;
    }
    public class IncomePrediction {
      [ColumnName("Score")]
      public float Income;
    }
    static void Main(string[] args)
    {
      Console.WriteLine("Begin ML.NET demo run");
      Console.WriteLine("Income from age, sex, politics");
      var pipeline = new LearningPipeline();
      string dataPath = "..\\..\\Data\\PeopleData.txt";
      pipeline.Add(new TextLoader(dataPath).
        CreateFrom<IncomeData>(separator: ','));
      pipeline.Add(new ColumnCopier(("Income", "Label")));
      pipeline.Add(new CategoricalOneHotVectorizer("Politic"));
      pipeline.Add(new ColumnConcatenator("Features", "Age",
        "Sex", "Politic"));
      var sdcar = new StochasticDualCoordinateAscentRegressor();
      sdcar.MaxIterations = 1000;
      sdcar.NormalizeFeatures = NormalizeOption.Auto;
      pipeline.Add(sdcar);
      // pipeline.N
      Console.WriteLine("\nStarting training \n");
      var model = pipeline.Train<IncomeData, IncomePrediction>();
      Console.WriteLine("\nTraining complete \n");
      string modelPath = "..\\..\\Models\\IncomeModel.zip";
      Task.Run(async () =>
      {
        await model.WriteAsync(modelPath);
      }).GetAwaiter().GetResult();
      var testData = new TextLoader(dataPath).
        CreateFrom<IncomeData>(separator: ',');
      var evaluator = new RegressionEvaluator();
      var metrics = evaluator.Evaluate(model, testData);
      double rms = metrics.Rms;
      Console.WriteLine("Root mean squared error = " +
        rms.ToString("F4"));
      Console.WriteLine("Income age 40 conservative male: ");
      IncomeData newPatient = new IncomeData() { Age = 40.0f,
        Sex = -1f, Politic = "conservative" };
      IncomePrediction prediction = model.Predict(newPatient);
      float predIncome = prediction.Income * 10000;
      Console.WriteLine("Predicted income = $" +
        predIncome.ToString("F2"));
      Console.WriteLine("\nEnd ML.NET demo");
      Console.ReadLine();
    } // Main
  } // Program
} // ns

Observe que a maioria dos namespaces tem um identificador “Herdado”. O programa de demonstração usa o que é chamado de API de pipeline, que é simples e eficiente. A equipe do ML.NET está adicionando uma API nova e mais flexível, que discutiremos em breve.

O programa define uma classe aninhada chamada IncomeData que descreve a estrutura interna dos dados de treinamento. Por exemplo, a primeira coluna é:

[Column("0")]
public float Age;

Observe que o campo de idade é declarado como do tipo float em vez do tipo double. Na maioria dos sistemas ML, o tipo float é o tipo numérico padrão, porque o aumento na precisão obtido com o uso do tipo double raramente vale a penalidade de desempenho e de memória resultante. Nomes de campo de previsão podem ser especificados usando o atributo ColumnName. Eles são opcionais e podem ser o que você desejar, por exemplo [ColumnName("Age")].

O programa de demonstração define uma classe aninhada chamada IncomePrediction para manter as previsões do modelo:

public class IncomePrediction {
  [ColumnName("Score")]
  public float Income;
}

A coluna chamada “Pontuação” é necessário mas, conforme mostrado, o identificador de variável de cadeia de caracteres associado não precisa corresponder.

Criação e treinamento do modelo

O programa de demonstração define um modelo de ML não treinado usando estas instruções:

var pipeline = new LearningPipeline();
string dataPath = "..\\..\\Data\\IncomeData.txt";
pipeline.Add(new TextLoader(dataPath).
  CreateFrom<IncomeData>(separator: ','));

Você pode pensar em um objeto LearningPipeline como um metacontêiner que contém os dados de treinamento e um algoritmo de treinamento. Esse paradigma é muito diferente dos usados por outras bibliotecas ML. Em seguida, o pipeline executa uma manipulação de dados:

pipeline.Add(new ColumnCopier(("Income", "Label")));
pipeline.Add(new CategoricalOneHotVectorizer("Politic"));
pipeline.Add(new ColumnConcatenator("Features", "Age",
  "Sex", "Politic"));

A versão herdada do ML.NET requer que a coluna que contém os valores a serem previstos seja identificada como “Rótulo”, então o método ColumnCopier cria uma duplicata na memória da coluna Renda. Uma abordagem alternativa é simplesmente nomear a coluna Renda como Rótulo na definição de classe que define a estrutura dos dados de treinamento.

O treinador só funciona com dados numéricos, sendo assim, os valores de texto da coluna Política devem ser convertidos para números inteiros. O método CategoricalOneHotVectorizer converte “conservador”, “moderado” e “liberal” para (1, 0, 0), (0, 1, 0) e (0, 0, 1). Uma abordagem alternativa é pré-codificar manualmente os dados de texto.

O método ColumnConcatenator combina as três colunas de previsão em uma única coluna, chamada Recursos. Esse esquema de nomenclatura é necessário. O algoritmo de treinamento é adicionado ao pipeline e o modelo é treinado da seguinte forma:

var sdcar = new StochasticDualCoordinateAscentRegressor();
sdcar.MaxIterations = 1000;
sdcar.NormalizeFeatures = NormalizeOption.Auto;
pipeline.Add(sdcar);
var model = pipeline.Train<IncomeData, IncomePrediction>();

Elevação de coordenada dupla aleatória é um algoritmo relativamente simples para treinar um modelo de regressão linear. Outros treinadores de regressão herdados incluem FastForestRegressor, FastTreeRegressor, GeneralizedAdditiveModelRegressor, LightGbmRegressor, OnlineGradientDescentRegressor e OrdinaryLeastSquaresRegressor. Cada um deles tem vantagens e desvantagens, portanto, não há um melhor algoritmo para um problema de regressão. Entender as diferenças entre cada tipo de regressor e classificador não é simples e requer um estudo mais aprofundado da documentação.

Depois que o objeto de pipeline foi configurado, treinar o modelo é uma operação de uma instrução. Se você consultar novamente a saída mostrada na Figura 1, você observará que o método Train faz bastante trabalho nos bastidores para você. Como o pipeline usa a normalização automática, o treinador analisou as colunas Idade e Renda e decidiu que elas deveriam ser dimensionadas usando a normalização Mín-Máx. Isso converte todos os valores de idade e renda em valores entre 0,0 e 1,0, de modo que os valores relativamente grandes (por exemplo, uma idade de 52) não sobrecarregam os valores menores (por exemplo, uma Renda de 4,58). A normalização, geralmente, mas nem sempre, aumenta a precisão do modelo resultante.

O método Train também usa a regularização L1 e L2, que é uma outra técnica de ML para melhorar a precisão de um modelo. Em resumo, a regularização desestimula valores de peso extremo no modelo que, por sua vez, desestimula o sobreajuste do modelo. Para reiterar, o ML.NET faz todos os tipos de processamento avançado, sem que seja necessário configurar explicitamente os valores de parâmetro. Ótimo!

Como salvar e avaliar o modelo

Depois que o modelo de regressão for treinado, ele será salvo em disco da seguinte forma:

string modelPath = "..\\..\\Models\\IncomeModel.zip";
Task.Run(async () =>
{
  await model.WriteAsync(modelPath);
}).GetAwaiter().GetResult();

O código pressupõe a existência de um diretório chamado Modelo, que tem dois níveis acima do executável do programa. Uma alternativa é codificar o caminho. Como o método WriteAsync é assíncrono, não é tão fácil chamá-lo. Podem ser usadas várias abordagens. A abordagem que eu prefiro é a técnica de wrapper mostrada. A falta de um método não assíncrono para salvar um modelo do ML.NET é bastante surpreendente, mesmo para uma biblioteca que esteja no modo de visualização.

O modelo é avaliado com estas instruções:

var testData = new TextLoader(dataPath).
  CreateFrom<IncomeData>(separator: ',');
var evaluator = new RegressionEvaluator();
var metrics = evaluator.Evaluate(model, testData);
double rms = metrics.Rms;
Console.WriteLine("Model root mean squared error = " +
  rms.ToString("F4"));

Na maioria dos cenários de ML, você teria dois arquivos de dados — um usado somente para treinamento e um segundo conjunto de dados de teste usado somente para avaliação do modelo. Para simplificar, o programa de demonstração reutiliza o arquivo de dados de 30 itens único para avaliação de modelo.

O método Evaluate retorna um objeto de agregação que contém a raiz do valor quadrático médio para o modelo treinado aplicado aos dados de teste. Outras métricas retornadas por um avaliador de regressão incluem R-quadrado (o coeficiente de determinação) e L1 (soma de erros absolutos).

Para muitos problemas de ML, a métrica mais útil é a precisão da previsão. Não há nenhuma definição inerente de precisão para um problema de regressão porque você deve definir o que significa para uma previsão estar correta. A abordagem comum é escrever uma função personalizada em que um valor previsto é contabilizado como correto se ele estiver dentro de um determinado percentual do valor true nos dados de treinamento. Por exemplo, se você definir a porcentagem de delta como 0,10 e se um valor de renda real for 6,00, uma previsão correta seria entre 5,40 e 6,60.

Como usar o modelo treinado

O programa de demonstração prevê a renda anual para um homem conservador de 40 anos, assim:

Console.WriteLine("Income for age 40 conservative male: ");
IncomeData newPatient = new IncomeData() { Age = 40.0f,
  Sex = -1f, Politic = "conservative" };
IncomePrediction prediction = model.Predict(newPatient);
float predIncome = prediction.Income * 10000;
Console.WriteLine("Predicted income = $" +
  predIncome.ToString("F2"));

Observe que os literais numéricos para idade e sexo usam o modificador “f”, porque o modelo está esperando valores do tipo float. Neste exemplo, o modelo treinado estava disponível porque o programa acabou o treinamento. Se você quisesse fazer uma previsão de um programa diferente, seria necessário carregar o modelo treinado usando o método ReadAsync ao longo das linhas de:

PredictionModel<IncomeData, IncomePrediction> model = null;
Task.Run(async () =>
{
  model = await PredictionModel.ReadAsync<IncomeData,
    IncomePrediction>(modelPath);
}).GetAwaiter().GetResult();

Depois de carregar o modelo na memória, você o usaria chamando o método Predict, como mostrado anteriormente.

A nova abordagem de API do ML.NET

A abordagem de pipeline herdado é simples e eficaz e fornece uma interface consistente para usar os classificadores e regressores do ML.NET. Mas a abordagem herdada tem algumas características arquitetônicas que limitam a extensibilidade da biblioteca, de modo que a equipe do ML.NET criou uma abordagem nova e mais flexível, que é melhor explicada através de um exemplo.

Suponha que você tenha exatamente o mesmo conjunto de dados — idade, sexo, renda e política. E suponha que você deseja prever a inclinação política a partir das outras três variáveis. Um programa de demonstração para criar um classificador usando a nova abordagem de API do ML.NET é apresentado na Figura 5. Esse programa usa uma combinação híbrida de estilo antigo e estilo novo e destina-se a criar uma ponte entre os dois. Deixe-me enfatizar que os exemplos de código mais recentes na documentação do ML.NET fornecerão técnicas mais sofisticadas e, em alguns casos, melhores.

Figura 5 Lista de Códigos de Exemplo de Classificação

using System;
using Microsoft.ML;
using Microsoft.ML.Runtime.Api;
using Microsoft.ML.Runtime.Data;
using Microsoft.ML.Transforms.Conversions;
// Microsoft.ML 0.7.0  Framework 4.7 Build x64
namespace PoliticPredict
{
  class PoliticProgram
  {
    public class PoliticData {
      [Column("0")] public float Age;
      [Column("1")] public float Sex;
      [Column("2")] public float Income;
      [Column("3")]
      [ColumnName("Label")]
      public string Politic;
    }
    public class PoliticPrediction  {
      [ColumnName("PredictedLabel")]
      public string PredictedPolitic;
    }
    static void Main(string[] args)
    {
      var ctx = new MLContext(seed: 1);
      string dataPath = "..\\..\\Data\\PeopleData.txt";
      TextLoader textLoader =
        ctx.Data.TextReader(new TextLoader.Arguments()
      {
        Separator = ",", HasHeader = false,
        Column = new[] {
          new TextLoader.Column("Age", DataKind.R4, 0),
          new TextLoader.Column("Sex", DataKind.R4, 1),
          new TextLoader.Column("Income", DataKind.R4, 2),
          new TextLoader.Column("Label", DataKind.Text, 3)
        }
      });
      var data = textLoader.Read(dataPath);
      var est = ctx.Transforms.Categorical.MapValueToKey("Label")
       .Append(ctx.Transforms.Concatenate("Features", "Age",
         "Sex", "Income"))
       .Append(ctx.MulticlassClassification.Trainers
         .StochasticDualCoordinateAscent("Label", "Features",
         maxIterations: 1000))
       .Append(new KeyToValueEstimator(ctx, "PredictedLabel"));
      var model = est.Fit(data);
      var prediction = model.MakePredictionFunction<PoliticData,
        PoliticPrediction>(ctx).Predict(
          new PoliticData() {
            Age = 40.0f, Sex = -1.0f, Income = 8.55f
          });
      Console.WriteLine("Predicted party is: " +
        prediction.PredictedPolitic);
      Console.ReadLine();
    } // Main
  } // Program
} // ns

Em um nível muito alto, muitas tarefas de ML tem cinco fases: carregar e transformar dados de treinamento na memória, criar um modelo, treinar o modelo, avaliar e salvar o modelo, usar o modelo. Tanto as APIs herdadas quanto as novas APIs do ML.NET podem realizar essas operações, mas a nova abordagem é claramente superior (na minha opinião) para cenários realistas de ML em um sistema de produção.

Um recurso importante da nova API do ML.NET é a classe MLContext. Observe que o objeto ctx é usado durante a leitura de dados de treinamento, ao criar o modelo de previsão e ao fazer uma previsão.

Embora não seja aparente do código, outra vantagem da nova API em relação à abordagem herdada é que você pode ler dados de treinamento de vários arquivos. Eu não encontro esse cenário com frequência, mas quando o faço, a capacidade de ler vários arquivos é uma grande economia de tempo.

Outro recurso da nova API é a capacidade de criar modelos de previsão de duas maneiras diferentes, chamadas estática e dinâmica. A abordagem estática oferece recursos completos do IntelliSense do Visual Studio durante o desenvolvimento. A abordagem dinâmica pode ser usada quando a estrutura de dados deve ser determinada em tempo de execução.

Conclusão

Se você leu este artigo e executou e entendeu o código de demonstração relativamente simples, sua próxima etapa é mergulhar de cabeça na nova API do ML.NET. Ao contrário de muitos projetos de software livre que têm uma documentação fraca ou limitada, a documentação do ML.NET é excelente. Recomendo os exemplos na bit.ly/2AVM1oL como um ótimo lugar para começar.

Embora a biblioteca do ML.NET seja nova, suas origens remontam a muitos anos atrás. Logo após o lançamento do Microsoft .NET Framework em 2002, a Microsoft Research iniciou um projeto chamado TMSN (“navegação e pesquisa de mineração de texto”) para permitir que os desenvolvedores de software incluíssem o código de ML em produtos e tecnologias Microsoft. O projeto foi muito bem-sucedido e, ao longo dos anos. aumentou em tamanho e uso interno na Microsoft. Em meados de 2011, a biblioteca foi renomeada para TLC (“o código de aprendizado”). O TLC é amplamente usado dentro da Microsoft e está atualmente na versão 3.10. A biblioteca do ML.NET é um descendente do TLC, com recursos específicos da Microsoft removidos. Eu usei as duas bibliotecas e, em muitos aspectos, o filho do ML.NET ultrapassou seu pai.

Este artigo abordou apenas superficialmente a biblioteca do ML.NET. Um novo recurso interessante e poderoso do ML.NET é a capacidade de consumir e usar modelos de rede neural profunda criados por outros sistemas, como o PyTorch e CNTK. A chave para essa interoperabilidade é o padrão Open Neural Network Exchange (ONNX). Mas esse é um assunto para um artigo futuro.


Dr. James McCaffreytrabalha para a Microsoft Research em Redmond, Washington. Ele trabalhou em vários produtos importantes da Microsoft, incluindo o Internet Explorer e o Bing. Dr. McCaffrey pelo email jamccaff@microsoft.com.

Agradecemos aos seguintes especialistas técnicos da Microsoft pela revisão deste artigo: Ankit Asthana, Chris Lauren, Cesar De la Torre Llorente, Beth Massi, Shahab Moradi, Gal Oshri, Shauheen Zahirazami


Discuta esse artigo no fórum do MSDN Magazine