Febbraio 2019

Volume 34 Numero 2

Il presente articolo è stato tradotto automaticamente.

[Test Run]

Classificazione della concorrenza con Infer.NET

Dal James McCaffrey

James McCaffreyInfer.NET è una libreria di codice open source che può essere usata per creare sistemi di programmazione probabilistici. Ho la tendenza a pensare a un programma o-dinary come uno che si basa principalmente su variabili che hanno un valore di un tipo specificato, ad esempio una variabile char con valore "Q". Programmazione probabilistica si basa principalmente sulle distribuzioni di probabilità, ad esempio una distribuzione di Gauss con 0,0 deviazione media e standard 1.0.

In questo articolo, mostrerò come iniziare a usare la libreria Infer.NET dal calcolo le classificazioni per un set di utilizzo dei risultati combattimenti testa a testa i concorrenti. Un ottimo modo per avere un'idea della programmazione probabilistica e vedere futuro in questo articolo è esaminiamo il programma demo nella figura 1. La demo Configura sei concorrenti (squadre sportive interazione utente): Angels, Bruins, Comets, Demons, Eagles e volantini.

Infer.NET Rating in azione
Figura 1 Infer.NET classificazione in azione

Provare a sei team rispetto a altro. Ogni team svolge tre volte in modo che siano presenti un totale di nove giochi. La demo Pro-gramma definisce un modello probabilistico che presuppone che ogni team ha una complessità intrinseca e che ogni livello può essere descritto da un Gauss (detto anche normale o a campana) distribuzione con una media pari a 2000 e deviazione standard pari a 200 unità arbitrarie.

Usando i dati di vincita-perdita, il programma demo deduce i punti di forza dei sei team. Il Angels vinto tre partite e persa nessuna e hanno un derivato vantaggio offerto da 2256.8 unit, che è meglio l'attendibilità media presunto di 2000 unità di circa 1,25 unità di deviazione standard. I volantini perso tutti e tre i propri giochi e avere un vantaggio offerto dedotto da 1739.7 unità.

Usare i punti di forza di termini in questo caso, ma è possibile considerare i valori numerici derivati come classificazioni. Si noti che se è in grado di dedurre il rat-impostazioni di un set di elementi, si otterrà automaticamente una classificazione degli elementi. La classificazione finale, dalla migliore alla peggiore, è Angels, Bruins, Com-ets, Eagles, Demons, volantini.

Questo articolo si presuppone intermedio o programmazione meglio le competenze con C#, ma non presume si riconosce i Infer.NET o programmazione probabilistica. Infer.NET supporta solo C# e F#, pertanto è possibile eseguire il refactoring di demo per F# se si desidera. Dopo aver appreso le nozioni di base della programmazione probabilistica, dovrebbe essere possibile riscrivere il programma demo usando uno dei molti altri probabilistico programmazione Framework, ad esempio Stan o Edward.

Il codice sorgente completo per il programma demo è presentato in questo articolo. Il codice sorgente è anche disponibile nel download del file accompa nying. Controllo degli errori è stata rimossa per mantenere le idee principali più chiare possibili.

Informazioni sulle variabili di Random

Il programma di dimostrazione si presuppone che forza di ogni team una variabile casuale Gauss distribuita con una specificata (età aver) deviazione media e standard. Esattamente questa novità inciderà e da dove ne questo presupposto derivano?

Esistono molti tipi di distribuzioni variabile casuale. Ognuno ha uno o più parametri tipici. Ad esempio, una variabile di dom è stato eseguito che segue una distribuzione uniforme con una = 2.0 e b = 5.0 può essere qualsiasi valore compreso tra 2.0 e 5.0, dove ogni valore pos sible è ugualmente probabile. Per il problema di demo sarebbe possibile presupporre che forza team in modo uniforme distribuita con un = 1000 e b = 3000, che potrebbe implicare che molti tali punti di forza avrebbe una media pari a 0,5 * (1000 + 3000) = 2000.0 unità e una deviazione standard di sqrt((1/12) * (3000-10 00) ^ 2) = 577.35 unità.

La demo presuppone che i punti di forza di team siano gaussiana con Media = deviazione standard e 2000 = 200 e pertanto l'efficacia di aver-age di molti team di questo tipo sarebbe circa 2000 e circa il 99% dei molti tale team avrebbe punti di forza tra 2000, (3 * 200) = 1400 e 2000 + (3 * 200) = 2600.

A questo punto, si potrebbe pensare, "Fantastico, pertanto è necessario avere una laurea in distribuzione statistiche usare Infer.NET e creare programmi prob abilistic." Ciò non rappresenta completamente vera. Infer.NET supporta molte distribuzioni, ma in pratica è in genere necessario stand correggerà pochi. Quelli che usare più di frequente sono Gauss, uniforme, beta, binomiale, multinomiali, della gamma e la probabilità di Poisson. Un'analogia è di molti tipi di strutture di dati supportati dallo spazio dei nomi System. Collections di .NET Framework. Anche se sono presenti molti, in genere si usa solo alcuni (stack, code, i dizionari) e fornite informazioni sui nuovi come si encoun ter problemi che ne hanno bisogno.

Il valore medio è il valore medio dei dati e la deviazione standard è una misura della modalità spread i dati. La varianza è il quadrato della deviazione standard e la precisione è l'inverso della varianza. Il motivo per cui vengono tre diversi termini che contengono gli stessi dati è che in alcuni casi un form è più pratico rispetto agli altri quando viene usato in un'equazione matematica di qualche tipo. La libreria Infer.NET tende a usare varianza e precisione anziché la deviazione standard.

La struttura del programma Demo

Il codice sorgente completo per il programma demo, con alcune modifiche di lieve entità per risparmiare spazio, viene visualizzato nella figura 2.

Codice sorgente completo nella figura 2

using System;
using Microsoft.ML.Probabilistic.Models;
using Microsoft.ML.Probabilistic.Algorithms;
using Microsoft.ML.Probabilistic.Distributions;
// VS2017 (Framework 4.7) Infer.NET 0.3.1810.501

namespace InferStrengths
{
  class InferStrengthsProgram
  {
    static void Main(string[] args)
    {
      Console.WriteLine("Begin Infer.NET demo ");

      // ===== Set up teams and win-loss data =====================

      string[] teamNames = new string[] { "Angels", "Bruins",
        "Comets", "Demons", "Eagles", "Flyers" };
      int N = teamNames.Length; 

      int[] winTeamIDs =  new int[] { 0, 2, 1, 0, 1, 3, 0, 2, 4 };
      int[] loseTeamIDs = new int[] { 1, 3, 2, 4, 3, 5, 5, 4, 5 };

      Console.WriteLine("Data: \n");
      for (int i = 0; i < winTeamIDs.Length; ++i) {
        Console.WriteLine("game: " + i + "   winning team: " +
          teamNames[winTeamIDs[i]] + "   losing team: " +
          teamNames[loseTeamIDs[i]]);
      }

      // ===== Define a probabilistic model =======================

      Range teamIDsRange = new Range(N).Named("teamsIDRange");
      Range gameIDsRange =
        new Range(winTeamIDs.Length).Named("gameIDsRange");

      double mean = 2000.0;
      double sd = 200.0;
      double vrnc = sd * sd;
      
      Console.WriteLine("\nDefining Gaussian model mean = " +
        mean.ToString("F1") + " and sd = " + sd.ToString("F1"));
      VariableArray<double> strengths =
        Variable.Array<double>(teamIDsRange).Named("strengths");
      
      strengths[teamIDsRange] =
        Variable.GaussianFromMeanAndVariance(mean,
        vrnc).ForEach(teamIDsRange);

      VariableArray<int> winners =
        Variable.Array<int>(gameIDsRange).Named("winners");
      VariableArray<int> losers =
        Variable.Array<int>(gameIDsRange).Named("losers");

      winners.ObservedValue = winTeamIDs;
      losers.ObservedValue = loseTeamIDs;

      using (Variable.ForEach(gameIDsRange))
      {
        var ws = strengths[winners[gameIDsRange]];
        var ls = strengths[losers[gameIDsRange]];
        Variable<double> winnerPerf =
      Variable.GaussianFromMeanAndVariance(ws, 400).Named("winPerf");
        Variable<double> loserPerf =
      Variable.GaussianFromMeanAndVariance(ls, 400).
        Named("losePerf");

        Variable.ConstrainTrue(winnerPerf > loserPerf);
      }

      // ===== Infer team strengths using win-loss data ===========

      Console.WriteLine("\nInferring strengths from win-loss data");
      var iengine = new InferenceEngine();
      iengine.Algorithm = new ExpectationPropagation();
      iengine.NumberOfIterations = 40;
      // iengine.ShowFactorGraph = true;  // needs Graphviz install

      Gaussian[] inferredStrengths =
        iengine.Infer<Gaussian[]>(strengths);
      Console.WriteLine("Inference complete. Inferred strengths: ");

      // ===== Show results =======================================

      for (int i = 0; i < N; ++i) {
        double strength = inferredStrengths[i].GetMean();
        Console.WriteLine(teamNames[i] + ": " +
          strength.ToString("F1"));
      }

      Console.WriteLine("\nEnd demo ");
      Console.ReadLine();
    } // Main
  }
} // ns

Per creare il programma di dimostrazione, avviato Visual Studio 2017 Community Edition e selezionato il progetto di modello di applicazione console con .NET Framework versione 4.7 e denominato InferStrengths. Infer.NET eseguibili in un'applicazione .NET Core, ma personalmente preferisco classica .NET Framework.

Dopo aver caricato il codice del modello, I pulsante destro del mouse sul file Program.cs nella finestra Esplora soluzioni e rinominato il file in InferStrengthsProgram.cs e quindi consentito Visual Studio di ridenominazione automatica della classe Program per l'utente corrente.

Ho pulsante destro del mouse sul nome del progetto nella finestra Esplora soluzioni e si seleziona l'opzione Gestisci pacchetti NuGet. Nella finestra di NuGet, è selezionata la scheda Sfoglia e quindi cercare 'Infer.NET.' Nell'elenco dei risultati è selezionato il pacchetto Microsoft.ML.Probabilistic.Compiler (versione 0.3.1810.501) e fatto clic sul pulsante di installazione. Microsoft prevede di eseguire la migrazione Infer.NET nella libreria a ML.NET in futuro, pertanto, se non si trova Infer.NET come pacchetto autonomo, cercare l'elemento all'interno del pacchetto a ML.NET.

Configurazione dei dati

Imposta la demo le squadre sei sportive nel modo seguente:

string[] teamNames = new string[] { "Angels", "Bruins",
  "Comets", "Demons", "Eagles", "Flyers" };
int N = teamNames.Length;

Poiché Infer.NET viene utilizzato principalmente con variabili casuali, in molti programmi tutti i dati sono rigorosamente numerici. In questo caso, i nomi del team vengono specificati come stringhe anziché gli indici integer semplicemente per migliorare la leggibilità. I dati di vincita-perdita sono:

int[] winTeamIDs =
  new int[]  { 0, 2, 1, 0, 1, 3, 0, 2, 4 };
int[] loseTeamIDs = 
  new int[]  { 1, 3, 2, 4, 3, 5, 5, 4, 5 };

Significa che i dati in gioco [0], 0 (Angels) team battuta team 1 (Bruins). In gioco [1], team 2 (Comets) battuta team (Demons), 3 e così via tramite gioco [8]. Il linguaggio di programmazione numerico, utilizzando le matrici parallele simile al seguente tende a essere un modello più comune di inserimento dei dati in un oggetto di classe o struct.

Si noti che a questo punto la demo Usa tipi di dati .NET nativi. Tuttavia, Infer.NET ha un proprio sistema di tipi e i dati verranno convertiti a breve in tipi utilizzabili dal Infer.NET. La demo consente di visualizzare i dati di origine:

for (int i = 0; i < winTeamIDs.Length; ++i) {
  Console.WriteLine("game: " + i + " winning team: " +
    teamNames[winTeamIDs[i]] + " losing team: " +
    teamNames[loseTeamIDs[i]]);
}

Nella demo è pochi dati. La possibilità di lavorare con dati limitati è applicato un livello di programmazione probabilistico rispetto a machine learning tecniche, ad esempio le reti neurali o ottimizzazione dell'apprendimento, che spesso richiedono grandi quantità di dati di training per essere efficace, alcune.

La definizione del modello probabilistico

La demo Prepara il modello di Gauss con queste istruzioni:

Range teamIDsRange = new Range(N).Named("teamsIDRange");
Range gameIDsRange =
  new Range(winTeamIDs.Length).Named("gameIDsRange");
double mean = 2000.0;
double sd = 200.0;
double vrnc = sd * sd;

L'idea di un oggetto intervallo è importante in Infer.NET. A differenza funzionale e programmazione procedurale standard in cui è comune in modo esplicito l'iterazione tramite un per un ciclo foreach o ciclo, in Infer.NET è più comune al quale applicare operazioni di metadati tramite un oggetto Range. Questo è un paradigma di scrittura di codice che può essere un po' difficile da acquisire familiarità.

Quasi tutti gli oggetti Infer.NET possono essere forniti con un nome di stringa facoltativa usando il metodo denominato concatenato alla chiamata al costruttore. Il nome dell'oggetto stringa non deve corrispondere al nome di identificatore di oggetto, ma è buona norma per renderli uguali. Come si vedrà a breve, utilizzando il metodo denominati è utile perché Infer.NET ha un metodo incorporato per visualizzare calcolo grafico di un modello e il grafico userà un nome di stringa se è disponibile invece un nome generato della libreria, ad esempio vdouble23.

I punti di forza del team per dedurre sono impostati con le due istruzioni seguenti:

VariableArray<double> strengths =
  Variable.Array<double>(teamIDsRange).Named("strengths");
strengths[teamIDsRange] =
  Variable.GaussianFromMeanAndVariance(mean,
  vrnc).ForEach(teamIDsRange);

La prima istruzione imposta backup di un singolo oggetto come un tipo speciale di VariableArray costituito da sei oggetti variabile casuale. La seconda istruzione Inizializza ogni variabile casuale come una Gauss con Media = 2000 e varianza = 4000 (che è equivalente alla deviazione standard Customer_ID=200). È possibile configurare una matrice di oggetti variabile singoli lungo le righe di:

Variable<double>[] arr = new Variable<double>[6];
for (int i = 0; i < 6; ++i)
  arr[i] = Variable.GaussianFromMeanAndVariance(2000, 4000);

Tuttavia, la documentazione Infer.NET consiglia di usare l'approccio VariableArray per motivi di prestazioni. Successivamente, vengono create variabili casuali di tipo int per contenere gli indici del confermati e non i team e quindi i dati di int .NET native viene trasformati in oggetti Infer.NET:

VariableArray<int> winners =
  Variable.Array<int>(gameIDsRange).Named("winners");
VariableArray<int> losers =
  Variable.Array<int>(gameIDsRange).Named("losers");
winners.ObservedValue = winTeamIDs;
losers.ObservedValue = loseTeamIDs;

In questo caso, le variabili di random non seguono una distribuzione di probabilità; invece si tratta essenzialmente valori interi appena ordinario. La proprietà ObservedValue consente di impostare valori corretti in questa situazione.

Le istruzioni principali che definiscono le caratteristiche del modello probabilistico sono:

using (Variable.ForEach(gameIDsRange)) {
  var ws = strengths[winners[gameIDsRange]];
  var ls = strengths[losers[gameIDsRange]];
  Variable<double> winnerPerf =
    Variable.GaussianFromMeanAndVariance(ws,
    400.0).Named("winPerf");
  Variable<double> loserPerf =
    Variable.GaussianFromMeanAndVariance(ls,
    400.0).Named("losePerf");

  Variable.ConstrainTrue(winnerPerf > loserPerf);
}

Questo codice è piuttosto complesso. Il codice si trova all'interno di un blocco Variable.ForEach in modo che le operazioni vengono applicate a ogni squadra e ogni risultato di gioco in una modalità di metadati. Variabili casuali che corrispondono alle prestazioni dei confermati e non i team per ogni gioco sono definite in modo che questi elementi sono basati su forza del team, ma hanno un componente di disturbo che viene distribuito Gauss con varianza o uguale a 400. Un modo per descrivere questa situazione è che quando vengono dedotti i punti di forza di team, è probabile che un team può leggermente underperform e l'avversario potrebbe prestazioni leggermente. Il valore di varianza rumore di 400 deve essere determinato dalla versione di valutazione ed errori.

L'istruzione ConstrainTrue è fondamentale e aggiunge la logica che consente al motore di inferenza dei tipi di attendibilità di ogni team di calcolo. Gli utilizzi del motore di inferenza un sofisticato algoritmo per esaminare i diversi valori di medie e varianze per ognuna delle sei team e quindi determina quale probabilità i risultati osservati vincita-perdita sono, dato il mezzo assunto e varianze. L'algoritmo di inferenza consente di trovare i sei mezzi e variazioni che meglio corrispondano ai dati osservati. Clever.

Deduzione di punti di forza del Team

Una volta definito il modello probabilistico, viene creato un motore a inferenza con queste istruzioni:

var iengine = new InferenceEngine();
iengine.Algorithm = new ExpectationPropagation();
iengine.NumberOfIterations = 40;
// iengine.ShowFactorGraph = true;

Infer.NET supporta tre diversi algoritmi: la propagazione di previsione, variational passaggio dei messaggi e Gibbs campionamento. Algoritmi differenti si applicano a tipi diversi di modelli probabilistici. Per il modello di demo solo la propagazione di previsione è valida. La propagazione di previsione è univoca per Infer.NET e riduce al minimo la metrica di divergenza Kullback Liebler per simulare la distribuzione di probabilità per un set di dati osservati. Il valore della proprietà NumberOfIterations deve essere determinato dalla versione di valutazione ed errori.

Una funzionalità interessante di Infer.NET è che è possibile generare automaticamente una rappresentazione visiva del grafico di calcolo del modello, come quello usato in figura 3 che corrisponde al programma demo. Questa funzionalità presenta una dipendenza sul programma Graphviz open source. Se si imposta ShowFactorGraph su true e Graphviz è installato, verrà salvato un grafo in formato SVG nella directory di lavoro corrente e può essere visualizzato in un browser.

Rappresentazione visiva del grafico del calcolo
Figura 3 rappresentazione visiva del grafico del calcolo

Dopo aver creato il motore a inferenza, è facile calcolare e visualizzare i punti di forza di team utilizzando il metodo Infer:

Gaussian[] inferredStrengths =
  iengine.Infer<Gaussian[]>(strengths);
for (int i = 0; i < N; ++i) {
  double strength = inferredStrengths[i].GetMean();
  Console.WriteLine(teamNames[i] + ": " + strength);
}

L'oggetto di punti di forza viene passato per Optino Infer. Tenere presente che i punti di forza è di tipo VariableArray, ovvero una raccolta di Gauss variabile casuale di oggetti che sono associate ai vincitori e vinti VariableArray oggetti, che i dati di vincita-perdita. Si noti che un modello Infer.NET è una raccolta di oggetti liberamente connessi anziché un singolo oggetto di primo livello. Per me, almeno, questa idea ha impiegato un po' di tempo per acquisire familiarità quando ero familiarità con Infer.NET.

Conclusioni

Probabilistic programmazione usando Infer.NET ha un aspetto molto diverso ad esso di programmazione utilizzando linguaggi procedurali standard e non prevede una curva di apprendimento non semplice. L'esempio presentato in questo articolo dipende regime di controllo libero di TrueSkill sistema sviluppato da Microsoft Research per l'uso in Xbox matchmaking di classificazione. Ma probabilistica programmazione può essere applicata a molti problemi diversi da rating e ordine di priorità. Per una libreria di codice che è nato nel settore della ricerca, in qualche modo insolitamente Infer.NET ha documentazione eccellente. Se vuoi altre informazioni sulla programmazione probabilistica, è consigliabile che esaminiamo le esercitazioni nella bit.ly/2rEr784.


Dr. James McCaffreylavora per Microsoft Research Redmond, WA Ha lavorato su diverse Microsoft tra cui Internet Explorer e Bing. Dr. È possibile contattarlo McCaffrey jamccaff@microsoft.com.

Grazie per i seguenti esperti tecnici Microsoft che ha esaminato in questo articolo: Chris Lee, Ricky Loynd


Discutere di questo articolo nel forum di MSDN Magazine