Enero de 2019

Volumen 34, número 1

[.NET]

Aprendizaje automático mediante programación probabilística

Por Yordan Zaykov | Enero de 2019

¿Programación probabilística? ¿En serio? No tiene mucho sentido... O eso es lo que pensé cuando comencé a trabajar en este dominio. Los investigadores a los que prestaba atención no tenían la visión tradicional de clasificar los problemas del aprendizaje automático (ML) en categorías. En su lugar, solo expresaban los procesos del mundo real que llevan a la generación de sus datos de una forma legible por ordenador. Eso es lo que denominan un modelo y su representación: un programa probabilístico.

El paradigma en realidad es bastante atractivo. En primer lugar, no necesita aprender los cientos de algoritmos de ML que hay disponibles. Solo debe aprender a expresar sus problemas en un programa probabilístico. Esto implica tener cierto conocimiento sobre las estadísticas, ya que está modelando la incertidumbre del mundo real. Digamos, por ejemplo, que quiere predecir el precio de una casa y decide que se trata de una combinación lineal de algunas características (ubicación, tamaño, etc.). Su modelo es que el precio es la suma de los productos de cada característica y algo de peso, con ruido agregado. Esto es lo que se denomina regresor lineal. En términos abstractos:

For each house:
  score = Weights * house.Features;
  house.Price = score + Noise;

Puede aprender los pesos durante el aprendizaje y, a continuación, usarlos directamente en la predicción. Si establece la puntuación de ruido en cero al final del umbral a fin de realizar un pequeño cambio en su modelo, la nueva etiqueta pasará a ser una de dos clases de forma inmediata. Acaba de modelar un clasificador lineal binario y tal vez ni siquiera sabe cómo se denomina.

En segundo lugar, no es necesario que intente adaptar su problema y sus datos a uno de los algoritmos de ML existentes. Debería ser obvio: diseñó el modelo para su problema a fin de que se ajuste a sus datos. Las herramientas de programación probabilísticas modernas pueden generar automáticamente un algoritmo de ML a partir del modelo que especificó, mediante un método de inferencia de uso general. Ni siquiera necesita saber mucho al respecto, ya que ya se implementa de forma automática. Por lo tanto, un modelo específico de la aplicación combinado con un método de inferencia de uso general proporcionan un algoritmo de ML específico de la aplicación.

Finalmente, el enfoque parece resistir el paso del tiempo. La mayoría de los algoritmos de ML correctos se ajustan a este paradigma: un modelo, que representa un conjunto de suposiciones, más un método de inferencia, que realiza los cálculos. La metodología ha evolucionado a lo largo del tiempo. En la actualidad, las redes neuronales profundas son populares; el modelo es una composición de las funciones lineales con umbral, y el método de inferencia recibe el nombre de descenso de gradiente estocástico (SGD). Se trata de modificar la estructura de la red para que sea recurrente o convolucional; esto significa que, si cambia de modelo, podrá dirigirse a diferentes aplicaciones. Sin embargo, el método de inferencia puede seguir siendo el mismo: SGD, tal y como se muestra en la Figura 1 . Por lo tanto, aunque la elección del modelo ha evolucionado a lo largo de los años, el enfoque general de diseñar un modelo y aplicar un método de inferencia ha permanecido constante.

Diferentes modelos para distintas aplicaciones, todo mediante el mismo método de inferencia
Figura 1 Diferentes modelos para distintas aplicaciones, todo mediante el mismo método de inferencia

Por lo tanto, la tarea para el desarrollador consiste en elaborar un modelo para su aplicación. Vamos a aprender a hacerlo.

Hello, Uncertain World! (Hola mundo incierto)

El primer programa que todo el mundo escribe en un nuevo lenguaje es "Hola mundo". Así pues, el equivalente en una configuración probabilística es "Hello, Uncertain World!" ("Hola mundo incierto"). Puede considerar el programa probabilístico como una simulación o una muestra de datos. Comenzaré con algunos parámetros y los usaré para generar datos. Por ejemplo, vamos a tomar dos cadenas que son desconocidas por completo. Es decir, es probable que sean unas cadenas cualesquiera o bien, en términos más estadísticos, que sean variables aleatorias de cadenas extraídas de una distribución uniforme. Las concatenaré con un espacio en el medio y restringiré el resultado para que sea la cadena “Hello, uncertain world” ("Hola mundo incierto"):

str1 = String.Uniform()
str2 = String.Uniform()
Constrain(str1 + " " + str2 == "Hello, uncertain world")
Print(str1)
Print(str2)

Este modelo es probabilístico: expuse mis suposiciones de cómo se generaron los datos. Ahora puedo ejecutar algún método de inferencia que realizará los cálculos necesarios. El espacio entre las dos cadenas puede ser cualquiera de los dos: el que está entre "Hello" y "uncertain", o el que está entre "uncertain" y "world". Así que no puedo estar seguro sobre cuál será el valor de las dos variables. Por lo tanto, el resultado es una distribución que captura la incertidumbre sobre los valores posibles. Es igual de probable que Str1 sea "Hello" o "Hello uncertain" y que str2 sea el 50 por ciento "uncertain world" y el 50 por ciento "world".

Un ejemplo real

Ahora vamos a verlo en un ejemplo más realista. La programación probabilística se puede utilizar para resolver una gran cantidad de problemas de ML. Por ejemplo, hace un tiempo, mi equipo desarrolló un sistema de recomendación y lo envió a Azure Machine Learning. Previamente, creamos un clasificador de correo electrónico en Exchange. Ahora, estamos trabajando para mejorar el matchmaking de jugadores en Xbox mediante la actualización del sistema de clasificación de aptitudes. También estamos en proceso de desarrollar un sistema para Bing que extraiga automáticamente el conocimiento de Internet mediante el modelado de texto no estructurado. Para lograr todo esto, se usa el mismo paradigma: defina el modelo como un conjunto de suposiciones representadas en un programa probabilístico y, luego, haga que un método de inferencia de uso general realice los cálculos necesarios.

El sistema de clasificación de aptitudes, denominado TrueSkill, demuestra muchas de las ventajas de la programación probabilística, incluida la capacidad de interpretar el comportamiento del sistema, incorporar el conocimiento del dominio en el modelo y aprender a medida que llegan nuevos datos. Por lo tanto, vamos a implementar una versión simplificada del modelo que se ejecuta en modo de producción en juegos de gran éxito como "Halo" y "Gears of War".

Problema y datos: el problema que se debe resolver es la clasificación de los jugadores en los juegos. Esto tiene muchos usos, por ejemplo, el matchmaking de jugadores (en el que se logran juegos justos y agradables al hacer coincidir jugadores o equipos con aptitudes similares). Por motivos de simplicidad, supongamos que solo hay dos participantes en cada partida y que el resultado es una victoria o una derrota sin empates. Por lo tanto, los datos de cada partida serán los identificadores únicos de los dos jugadores y un indicador de quién es el ganador. En este artículo, usaré un pequeño conjunto de datos creados manualmente, pero el enfoque se extiende a cientos de millones de coincidencias en Xbox.

Lo que me gustaría hacer es aprender las aptitudes de todos los jugadores y poder predecir el ganador de los futuros enfrentamientos. Un enfoque ingenuo sería simplemente contar el número de victorias y derrotas de cada jugador, pero de este modo no se tiene en cuenta el potencial de los oponentes en estas partidas. Existe una forma mucho mejor.

Diseño del modelo: para empezar, vamos a hacer suposiciones sobre cómo se generaron los datos. Primero, cada jugador tiene alguna aptitud oculta (o latente) que nunca se observa directamente; solo son visibles los efectos de dicha aptitud. Supondré que se trata de un número real, pero también debo especificar cómo se generó. Una opción razonable es considerar que se generó a partir de una distribución normal (o gaussiana). En un ejemplo más complejo, los parámetros de esta distribución gaussiana serían desconocidos y fáciles de aprender. Por motivos de simplicidad, los estableceré directamente.

El modelo de la aptitud se puede representar gráficamente, según se muestra en el boceto de la izquierda de la Figura 2, en el que simplemente se indica que la aptitud de la variable aleatoria se extrae de una distribución normal.

Composición de un juego de dos jugadores
Figura 2 Composición de un juego de dos jugadores

Otra suposición que puedo hacer es que en cada partida los jugadores tienen un número de rendimiento. Este número está cerca de su aptitud latente, pero puede oscilar y estar por encima o por debajo, en función de si el jugador juega mejor o peor que su nivel típico. En otras palabras, el rendimiento es una versión con ruido de la aptitud. Y, por lo general, el ruido también se modela para ser gaussiano, tal y como se muestra en el diagrama del centro de la Figura 2. En este caso, el rendimiento es una variable aleatoria extraída de una distribución gaussiana cuya media es la aptitud del jugador y cuya varianza es fija, indicada por la variable de ruido manipulada. En un modelo más complejo, trataría de aprender la varianza del ruido de los datos, pero por motivos simplicidad, la fijaré en 1.

El jugador que tiene un mejor rendimiento mejor gana. En una partida de dos jugadores puedo representarlo gráficamente, tal y como se muestra en el diagrama que hay más a la derecha de la Figura 2. En esta notación, hice un poco de trampa, ya que manipulé de algún modo las victorias del jugador del booleano 1. Esto se debe a que su valor se observa durante el aprendizaje, donde se proporcionan los resultados de las partidas, pero no se observa durante la predicción.

Antes de agrupar todo esto, necesito presentar un par de notaciones nuevas. La primera se llama placa y representa un bucle Para cada uno. Se trata de un rectángulo que captura partes del modelo que debe repetirse en un intervalo determinado (por ejemplo, en jugadores o en juegos). La segunda es que usaré líneas discontinuas para indicar la selección, como cuando selecciono las aptitudes de los dos jugadores en cada juego. El modelo TrueSkill simplificado se muestra en la Figura 3.

Modelo TrueSkill simplificado
Figura 3 Modelo TrueSkill simplificado

En este caso, uso una placa durante el intervalo de jugadores. Luego, para cada partida, selecciono las aptitudes latentes de los dos jugadores del juego, agrego algo de ruido y comparo los rendimientos. La variable de ruido no se encuentra en ninguna placa, ya que se asume que su valor no cambia por jugador o juego.

Esta visualización del modelo, también llamada gráfica factorial, es una representación adecuada para este caso simple. Pero cuando los modelos crecen, el diagrama se vuelve confuso y complicado de mantener. Por este motivo, los desarrolladores prefieren expresarlo en código, como un programa probabilístico.

Infer.NET

El marco de programación probabilístico para .NET se denomina Infer.NET. Lo desarrolló Microsoft Research y pasó a ser de código abierto hace un par de meses. Infer.NET proporciona una API de modelado para especificar el modelo estadístico, un compilador para generar el algoritmo de ML a partir del modelo definido por el usuario y un tiempo de ejecución en el que se ejecuta el algoritmo.

Infer.NET se está integrando más profundamente con ML.NET y ahora se encuentra bajo el espacio de nombres Microsoft.ML.Probabilistic. Para instalar Infer.NET, ejecute lo siguiente:

dotnet add package Microsoft.ML.Probabilistic.Compiler

El compilador extraerá automáticamente el paquete de tiempo de ejecución. Tenga en cuenta que Infer.NET se ejecuta en .NET Standard y, por lo tanto, lo hace en Windows, Linux y macOS.

Usaremos C# y, para empezar, incluiremos los espacios de nombres de Infer.NET:

using Microsoft.ML.Probabilistic.Models;
using Microsoft.ML.Probabilistic.Utilities;
using Microsoft.ML.Probabilistic.Distributions;

A continuación, implementaré el modelo que definí previamente y veré cómo puedo entrenarlo y hacer predicciones.

Implementación del modelo: el modelo está escrito como un programa que simula un juego, por lo que respecta a la aptitud, el rendimiento y el resultado del jugador, pero este programa no se ejecutará realmente. En segundo plano, acumula una estructura de datos que representa el modelo. Cuando se proporciona esta estructura de datos a un motor de inferencia, se compila en el código de ML, que luego se ejecuta para realizar el cálculo real. Vamos a implementar el modelo en tres pasos: definiremos el esqueleto de las placas sobre los jugadores y los juegos, el contenido de la placa de los jugadores y el contenido de la placa del juego.

Las placas se implementan en Infer.NET mediante la clase Range. Básicamente, sus instancias son variables de control en bucles Para cada uno probabilísticos. Debo definir el tamaño de estas placas, que corresponde al número de jugadores y juegos, respectivamente. Dado que no conozco estos datos por adelantado, serán variables que se usarán como marcadores de posición para estos valores. Para su comodidad, Infer.NET proporciona la clase Variable únicamente para este fin:

var playerCount = Variable.New<int>();
var player = new Range(playerCount);
var gameCount = Variable.New<int>();
var game = new Range(gameCount);

Una vez hemos definido las placas, debemos centrarnos en su contenido. Para la placa de los jugadores, necesitaré una matriz de variables aleatorias dobles de las aptitudes de cada jugador. En Infer.NET, esto se realiza mediante Variable.Array<T>. También necesitaré una matriz de variables aleatorias gaussianas para la distribución previa sobre las aptitudes de los jugadores. A continuación, revisaré los jugadores y conectaré sus aptitudes con las anteriores. Para lograrlo, se debe usar el método Variable<T>.Random. Observe cómo el método Variable.ForEach(Range) proporciona los medios necesarios para implementar los elementos internos de la placa:

var playerSkills = Variable.Array<double>(player);
var playerSkillsPrior = Variable.Array<Gaussian>(player);
using (Variable.ForEach(player))
{
  playerSkills[player] = Variable<double>.Random(playerSkillsPrior[player]);
}

La última pieza del modelo es la placa de juego. Para empezar, definiré las matrices que contendrán los datos de aprendizaje: el primer y el segundo jugador de cada juego y los resultados de las partidas. Observe cómo creo un modelo específicamente para mis datos, en lugar de intentar cambiar los datos para que se ajusten a un algoritmo. Una vez los contenedores de datos están listos, debo revisar los juegos, seleccionar las aptitudes de los jugadores en cada juego, agregarles algo de ruido y comparar los rendimientos a fin de generar el resultado del juego:

var players1 = Variable.Array<int>(game);
var players2 = Variable.Array<int>(game);
var player1Wins = Variable.Array<bool>(game);
  const double noise = 1.0;
    using (Variable.ForEach(game))
{
  var player1Skill = playerSkills[players1[game]];
  var player1Performance =
    Variable.GaussianFromMeanAndVariance(player1Skill, noise);
  var player2Skill = playerSkills[players2[game]];
  var player2Performance =
    Variable.GaussianFromMeanAndVariance(player2Skill, noise);
      player1Wins[game] = player1Performance > player2Performance;
}

Curiosamente, se utiliza el mismo modelo tanto para el aprendizaje como para la predicción. La diferencia es que los datos observados son distintos. Durante el aprendizaje, conoce los resultados de la partida, mientras que durante la predicción no. Así que, aunque el modelo sigue siendo el mismo, los algoritmos generados serán distintos. Afortunadamente, el compilador Infer.NET se encarga de todo eso.

Aprendizaje: todas las consultas al modelo (aprendizaje, predicción, etc.) siguen un proceso de tres pasos: el establecimiento de prioridades, la observación de datos y la ejecución de la inferencia. Tanto el aprendizaje como la predicción reciben el nombre de "inferencia", ya que fundamentalmente hacen lo mismo: Utilizan los datos observados para pasar de una distribución previa a una de posterior. En el aprendizaje, se comienza con una amplia distribución previa sobre las aptitudes, que indica que la incertidumbre sobre las aptitudes es alta. Usaré una distribución gaussiana para dicha distribución. Después de observar los datos, obtengo una distribución posterior gaussiana más reducida sobre las aptitudes.

Para la distribución previa sobre las aptitudes, solo tomaré prestados los parámetros aprendidos de "Halo 5" (una buena opción para la media y la varianza son los valores 6,0 y 9,0, respectivamente). Para establecer estos valores, los asigné a la propiedad ObservedValue de la variable que contiene la distribución previa. Para cuatro jugadores, el código tendría el aspecto siguiente:

const int PlayerCount = 4;
playerSkillsPrior.ObservedValue =
  Enumerable.Repeat(Gaussian.FromMeanAndVariance(6, 9),
  PlayerCount).ToArray();

A continuación, es turno para los datos. Para cada partida, tengo los dos jugadores y el resultado. Vamos a trabajar con un ejemplo fijo. En la Figura 4 se muestran tres partidas jugadas por cuatro jugadores, con flechas que indican la partida que se jugó y que apuntan al jugador derrotado en la partida.

Resultados de tres coincidencias
Figura 4 Resultados de tres coincidencias

Con tal de simplificar el código, supondré que se ha asignado un identificador único a cada jugador. En este ejemplo, el primer juego es entre Julia y David, y la flecha indica que Julia ha ganado. En el segundo juego, David vence a Carlos, y finalmente, Dalia derrota a Carlos. A continuación, se expresa en código, de nuevo mediante ObservedValue:

playerCount.ObservedValue = PlayerCount;
gameCount.ObservedValue = 3;
players1.ObservedValue = new[] { 0, 1, 2 };
players2.ObservedValue = new[] { 1, 2, 3 };
player1Wins.ObservedValue = new[] { true, true, false };

Finalmente, ejecuto la inferencia mediante la creación de una instancia de un motor de inferencia y llamando a Infer en relación con las variables que quiero obtener. En este caso, solo me interesa la distribución posterior sobre las aptitudes del jugador:

var inferenceEngine = new InferenceEngine();
var inferredSkills = inferenceEngine.Infer<Gaussian[]>(playerSkills);

Aquí, la instrucción Infer realmente hace mucho trabajo, ya que realiza dos compilaciones (Infer.NET y C#) y una ejecución. Lo que sucede es que el compilador de Infer.NET hace un seguimiento de la variable playerSkills que se pasa al modelo probabilístico, compila un árbol de sintaxis abstracta del código del modelo y genera un algoritmo de inferencia en C#. A continuación, se invoca el compilador de C# sobre la marcha y se ejecuta el algoritmo según los datos observados. Debido a todo esto, es posible que encuentre que las llamadas a Infer son un poco lentas, aunque en este caso esté trabajando con muy pocos datos. En el manual de Infer.NET se describe cómo se puede acelerar este proceso al trabajar con algoritmos precompilados. Para ello, debe realizar los pasos de compilación por adelantado, de modo que solo se ejecute la parte de cálculo en el modo de producción.

Para este ejemplo, las aptitudes inferidas son las siguientes:

Alice: Gaussian(8.147, 5.685)
Bob: Gaussian(5.722, 4.482)
Charlie: Gaussian(3.067, 4.814)
Donna: Gaussian(7.065, 6.588)

Predicción: una vez he inferido las aptitudes de los jugadores, ya puedo hacer predicciones sobre las futuras partidas. Seguiré los mismos tres pasos que en el aprendizaje, pero esta vez me interesará inferir la distribución posterior sobre la variable player1Wins. La forma en que me gusta pensar en esto es que, durante el aprendizaje, la información fluye hacia la parte superior del gráfico factorial (desde los datos de la parte inferior hasta los parámetros del modelo de la parte superior). Por el contrario, en la predicción ya cuenta con los parámetros del modelo (obtenidos en el aprendizaje) y la información fluye hacia la parte inferior.

En mi conjunto de datos, Julia y Dalia tienen una victoria y ninguna derrota. Sin embargo, de forma intuitiva parece que en una partida entre las dos, Julia tiene mayores posibilidades de ganar porque su victoria es más significativa (es contra David, que es más probable que sea mejor jugador que Carlos). Vamos a intentar predecir el resultado de la partida entre Julia y Dalia (véalo en la Figura 5).

Predecir el resultado de un partido
Figura 5 Predecir el resultado de un partido

En este caso, la distribución previa sobre las aptitudes es la distribución posterior inferida en el aprendizaje. Los datos observados son un juego entre la jugadora 0 (Julia) y la jugadora 3 (Dalia), con un resultado desconocido. Para hacer que el resultado sea desconocido, debo borrar el valor observado previamente de player1Wins, ya que de este es el que quiero obtener una distribución posterior:

playerSkillsPrior.ObservedValue = inferredSkills;
gameCount.ObservedValue = 1;
players1.ObservedValue = new[] { 0 };
players2.ObservedValue = new[] { 3 };
player1Wins.ClearObservedValue();
var player0Vs3 = inferenceEngine.Infer<Bernoulli[]>(player1Wins).First();

Vale la pena mencionar que la incertidumbre se propaga por este modelo hasta que se obtiene el resultado de la partida. Esto significa que la distribución posterior obtenida sobre el resultado predicho no es simplemente una variable booleana, sino un valor que indica la probabilidad de que el primer jugador gane la partida. Dicha distribución se llama Bernoulli.

El valor de la variable inferida es Bernoulli(0.6127). Esto significa que Julia tiene más de un 60 por ciento de posibilidades de ganar la partida contra Dalia, lo cual coincide con mi intuición.

Evaluación: en este ejemplo se muestra cómo compilar un modelo probabilístico conocido: TrueSkill. En la práctica, idear el modelo correcto requiere varias iteraciones sobre su diseño. Se comparan diferentes modelos y, para ello, se selecciona cuidadosamente un conjunto de métricas que indican el rendimiento del modelo sobre los datos determinados.

La evaluación es un tema central de ML, pero es demasiado amplio para tratarlo aquí. Asimismo, tampoco es un asunto específico de la programación probabilística. Sin embargo, vale la pena mencionar que el hecho de tener un modelo le permite calcular una "métrica" única: la evidencia del modelo. Esta es la probabilidad de que este modelo específico haya generado los datos de aprendizaje. Es ideal para comparar diferentes modelos y no se necesita ningún conjunto de pruebas.

Ventajas de la programación probabilística

El enfoque que se muestra en este artículo probablemente le parece más difícil de lo que ha visto anteriormente. Por ejemplo, en el número especial de Connect(); de la revista (msdn.com/magazine/mt848634) se le presentó ML.NET, que se centra menos en el diseño de modelos y más en la transformación de datos. Y, en muchos casos, ese es el camino correcto a seguir: si los datos parecen coincidir con un algoritmo existente y se siente cómodo tratando el modelo como una caja negra, comience con algo listo para usar. Sin embargo, si necesita un modelo a medida, la programación probabilística puede ser la opción ideal para usted. 

Hay otros casos en los que es posible que quiera usar la programación probabilística. Una de las principales ventajas de tener un modelo que comprende es su capacidad mejorada para explicar el comportamiento del sistema. Cuando el modelo no es una caja negra, puede examinar sus elementos internos y observar los parámetros aprendidos. Esto tendría sentido para usted porque es quien diseñó el modelo. Por ejemplo, en el ejemplo anterior puede ver las aptitudes aprendidas de los jugadores. En una versión más avanzada de TrueSkill, denominada TrueSkill 2, se modelan muchos más aspectos del juego, incluida la forma en que el rendimiento de un modo de juego se conecta con el otro. Comprender esta conexión permite a los diseñadores de juegos percatarse de lo similares que son los diferentes modos de juego. La interpretabilidad de un sistema de ML también es fundamental para la depuración. Cuando un modelo de caja negra no genera los resultados deseados, es posible que ni siquiera sepa por dónde empezar a buscar el problema.

Otra ventaja de la programación probabilística es la capacidad de incorporar el conocimiento de dominio en el modelo. Esto se realiza tanto en la estructura del modelo como en la capacidad de establecer distribuciones previas. Por el contrario, generalmente los enfoques tradicionales solo observan los datos, sin permitir que el experto en dominios se informe sobre el comportamiento del sistema. Esta funcionalidad es necesaria en ciertos dominios, como la asistencia sanitaria, en el que existen conocimientos sólidos del dominio y los datos pueden ser escasos.

Una ventaja del enfoque bayesiano, que se admite sin problemas en Infer.NET, es la capacidad de aprender a medida que llegan nuevos datos. Esto recibe el nombre de inferencia en línea y es especialmente útil en sistemas que interactúan con los datos del usuario. Por ejemplo, TrueSkill debe actualizar las aptitudes de los jugadores después de cada partida, y el sistema de extracción de conocimientos necesita aprender continuamente de Internet a medida que crece. Sin embargo, todo esto es muy fácil: simplemente se conectan las distribuciones posteriores inferidas como nuevas distribuciones previas y así el sistema está listo para aprender de los nuevos datos.

Evidentemente, la programación probabilística también se aplica a problemas con ciertos rasgos de datos, como datos heterogéneos, datos escasos, datos sin etiquetar, datos con partes que faltan y datos recopilados con sesgos conocidos.

¿Qué sigue?

Hay dos ingredientes para compilar correctamente un modelo probabilístico. El primero, obviamente, es aprender a modelar. En este artículo presenté los conceptos y las técnicas principales de la metodología, pero si quiere obtener más información, recomiendo un libro en línea disponible gratuitamente, "Model-Based Machine Learning" ("Aprendizaje automático basado en modelos") (mbmlbook.com). Proporciona una ligera introducción sobre ML basada en modelos en el modo de producción, dirigida a desarrolladores (en lugar de científicos).

Una vez sepa cómo diseñar modelos, debe aprender a expresarlos en código. El manual del usuario de Infer.NET es un recurso excelente para ello. También hay tutoriales y ejemplos que abarcan muchos escenarios, a los que se puede acceder desde nuestro repositorio en github.com/dotnet/infer (también estaremos encantados de que se una a él y realice contribuciones).


Yordan Zaykov  es el director principal de ingeniería de software de investigación del equipo de desarrollo de inferencia probabilística de Microsoft Research Cambridge. Se centra en aplicaciones basadas en el marco de ML de Infer.NET, que incluye trabajo sobre clasificación de correo electrónico, recomendaciones, clasificación de jugadores y matchmaking, asistencia sanitaria y minería de conocimientos.

Gracias a los siguientes expertos técnicos de Microsoft por revisar este artículo: John Guiver, James McCaffrey, Tom Minka y John Winn