"La longue route vous attend ..." ou résoudre le problème de prévision en C # en utilisant Ml.NET (DataScience)

Récemment, j'ai rencontré de plus en plus d'informations sur le framework d'apprentissage automatique Ml.NET . Le nombre de références à celui-ci a grandi en qualité, et j'ai décidé de regarder au moins en un coup d'œil de quel type d'animal il s'agissait.

Plus tôt, nous avons essayé de résoudre le problème de prédiction le plus simple en utilisant la régression linéaire dans l'écosystème .NET. Pour cela, nous avons utilisé Accord.NET Framework. À ces fins, un petit ensemble de données a été préparé à partir de données ouvertes sur les appels des citoyens aux autorités exécutives et personnellement au maire de Moscou .

Après quelques années sur un ensemble de données mis à jour, nous essaierons de résoudre le problème le plus simple . En utilisant le modèle de régression dans Ml.NET Framework, nous prédisons combien de demandes par mois obtiendront une solution positive. En cours de route, nous comparerons Ml.NET avec Accord. Bibliothèques NET et Python.

Voulez-vous maîtriser la force et la puissance du prédicteur? Alors vous êtes les bienvenus sous chat.



PS Soit S.S. Sobyanine, l'article ne dira pas un mot sur la politique.


Contenu:

Partie I: introduction et un peu sur les données
Partie II: écrire du code C #
Partie III: Conclusion

Je pense qu'il est nécessaire de vous avertir immédiatement que je ne suis pas un pro de l'analyse des données et de la programmation en général, et que je ne suis pas engagé avec la mairie de Moscou. Par conséquent, l'article est plus probable d'un débutant à un débutant. Mais malgré mes connaissances limitées, j'espère que l'article vous sera utile.

Les personnes qui connaissent déjà les articles du cycle précédents peuvent se rappeler que nous avons déjà essayé de résoudre le problème de la prédiction du nombre de problèmes résolus positivement à partir des appels des citoyens adressés à l'exécutif de Moscou. Pour cela, nous avons utilisé Python et Accord.Net Framework .



Dans tous les cas, il ne sera pas superflu d'analyser à nouveau l'ensemble de données utilisé.

Tous les matériaux d'article, y compris le code et l'ensemble de données, sont disponibles gratuitement sur GitHub .

Les données sur GitHub sont présentées au format csv, contiennent 44 entrées et, en principe, elles peuvent (et devraient) être utilisées non seulement pour l'analyse de l'exemple.

Les colonnes de données signifient ce qui suit:

  • num - index des enregistrements
  • année - année d'enregistrement
  • mois - mois d'enregistrement
  • total_appeals - nombre total de visites par mois
  • appeals_to_mayor - nombre total d'appels au maire
  • res_positive- nombre de décisions positives
  • res_explained - nombre d'appels de clarification
  • res_negative - nombre d'appels avec une décision négative
  • El_form_to_mayor - le nombre d'appels au maire sous forme électronique
  • Pap_form_to_mayor - le nombre d'appels au maire sur papier to_10K_total_VAO ... to_10K_total_YUZAO - le nombre d'appels pour 10 000 habitants dans divers quartiers de Moscou
  • to_10K_mayor_VAO ... to_10K_mayor_YUZAO– le nombre d'appels au maire et au gouvernement de Moscou pour 10 000 habitants dans divers quartiers de la ville

Je n'ai pas trouvé de moyen d'automatiser le processus de collecte de données et les ai collectées manuellement, donc je peux me tromper légèrement. Sinon, la fiabilité des données sera laissée à la conscience des auteurs.

Actuellement, les données sur le site Web du gouvernement à Moscou sont présentées dans leur intégralité de janvier 2016 à août 2019 (certaines données manquent en septembre). Ainsi, nous aurons 44 entrées. Un peu, bien sûr, mais pour nous la démonstration ce sera suffisant.

Avant de commencer, quelques mots sur le héros de notre article.
ML.NET Framework - Développement open source de Microsoft. Selon la publicité sur les médias sociaux, c'est leur réponse aux bibliothèques d'apprentissage automatique Python. Le cadre est multiplateforme et vous permet de résoudre un large éventail de problèmes, de la simple régression et classification à l'apprentissage en profondeur. Sur Habr, des camarades ont déjà effectué l'analyse de ML.NET et des bibliothèques en Python. Peu importe, voici le lien .

Je ne donnerai pas de guide détaillé sur l'installation et l'utilisation de Ml.NET car, en substance, tout a été arraché "adapté" sur la base d'un manuel du site officiel de Microsoft . Là, le problème avec les prix d'un voyage en taxi a été résolu, et pour être honnête, il y a plus d'avantages

Mais je pense que les petites explications ne seront pas superflues.

J'ai utilisé Visual Studio 2017 avec les dernières mises à jour.
Le projet était basé sur le modèle d'application de console .NET Core (version 2.1).
Le projet devait installer les packages NuGet Microsoft.ML, Microsoft.ML.FastTree. En fait, c'est toute la préparation.


Nous procédons directement au code.

Tout d'abord, j'ai créé la classe MayorAppel, dans laquelle j'ai décrit dans l'ordre les colonnes avec les données des fichiers csv.
Comment pas difficile à deviner [LoadColumn (0)]
- nous indique quelle colonne du fichier csv nous prenons.
Ensuite, après le tutoriel, j'ai créé la classe MayorAppelPrediction - pour les résultats de prédiction

Malgré le fait que presque toutes les colonnes de l'ensemble de données ont des valeurs entières, afin d'éviter les erreurs au stade du collage des données dans le pipeline, j'ai dû leur attribuer un type flottant (afin que tous les types de données soient identiques).
La liste est assez grande, alors mettez-la sous le spoiler.

Code de classe pour la description des données
using Microsoft.ML.Data; namespace app_to_mayor_mlnet { class MayorAppel { [LoadColumn(0)] public float Year; [LoadColumn(1)] public string Month; [LoadColumn(2)] public float TotalAppeals; [LoadColumn(3)] public float AppealsToMayor; [LoadColumn(4)] public float ResPositive; [LoadColumn(5)] public float ResExplained; [LoadColumn(6)] public float ResNegative; [LoadColumn(7)] public float ElFormToMayor; [LoadColumn(8)] public float PapFormToMayor; [LoadColumn(9)] public float To10KTotalVAO; [LoadColumn(10)] public float To10KMayorVAO; [LoadColumn(11)] public float To10KTotalZAO; [LoadColumn(12)] public float To10KMayorZAO; [LoadColumn(13)] public float To10KTotalZelAO; [LoadColumn(14)] public float To10KMayorZelAO; [LoadColumn(6)] public float To10KTotalSAO; [LoadColumn(15)] public float To10KMayorSAO; [LoadColumn(16)] public float To10KTotalSVAO; [LoadColumn(17)] public float To10KMayorSVAO; [LoadColumn(18)] public float To10KTotalSZAO; [LoadColumn(19)] public float To10KMayorSZAO; [LoadColumn(20)] public float To10KTotalTiNAO; [LoadColumn(21)] public float To10KMayorTiNAO; [LoadColumn(22)] public float To10KTotalCAO; [LoadColumn(23)] public float To10KMayorCAO; [LoadColumn(24)] public float To10KTotalYUAO; [LoadColumn(25)] public float To10KMayorYUAO; [LoadColumn(26)] public float To10KTotalYUVAO; [LoadColumn(27)] public float To10KMayorYUVAO; [LoadColumn(28)] public float To10KTotalYUZAO; [LoadColumn(29)] public float To10KMayorYUZAO; } public class MayorAppelPrediction { [ColumnName("Score")] public float ResPositive; } } 


Passons au code de programme principal.
N'oubliez pas d'ajouter au tout début:

 using System.IO; using Microsoft.ML; 


Ce qui suit est une description des champs de données.

 namespace app_to_mayor_mlnet { class Program { static readonly string _trainDataPath = Path.Combine(Environment.CurrentDirectory, "Data", "train_data.csv"); static readonly string _testDataPath = Path.Combine(Environment.CurrentDirectory, "Data", "test_data.csv"); static readonly string _modelPath = Path.Combine(Environment.CurrentDirectory, "Data", "Model.zip"); 


Dans ces domaines, en effet, les chemins d'accès aux fichiers de données sont stockés, cette fois j'ai décidé de les séparer à l'avance (contrairement au cas avec Accord.NET)

Soit dit en passant, si vous faites votre projet, n'oubliez pas de définir l'option «Copier la version ultérieure» dans les propriétés des fichiers de données pour éviter une erreur due au manque de fichiers d'assemblage.

Vient ensuite le défi des méthodes qui forment le modèle, conduisent son évaluation et nous donnent une prédiction.

  static void Main(string[] args) { MLContext mlContext = new MLContext(seed: 0); var model = Train(mlContext, _trainDataPath); Evaluate(mlContext, model); TestSinglePrediction(mlContext, model); } 


Allons dans l'ordre

La méthode Train est nécessaire pour former le modèle.

 public static ITransformer Train(MLContext mlContext, string dataPath) { IDataView dataView = mlContext.Data.LoadFromTextFile<MayorAppel>(dataPath, hasHeader: true, separatorChar: ','); var pipeline = mlContext.Transforms.CopyColumns(outputColumnName: "Label", inputColumnName: "ResPositive") .Append(mlContext.Transforms.Categorical.OneHotEncoding(outputColumnName: "MonthEncoded", inputColumnName: "Month")) .Append(mlContext.Transforms.Concatenate("Features", "Year", "MonthEncoded", "TotalAppeals", "AppealsToMayor", "ResExplained", "ResNegative", "ElFormToMayor", "PapFormToMayor", "To10KTotalVAO", "To10KMayorVAO", "To10KTotalZAO", "To10KMayorZAO", "To10KTotalZelAO", "To10KMayorZelAO", "To10KTotalSAO", "To10KMayorSAO" , "To10KTotalSVAO", "To10KMayorSVAO", "To10KTotalSZAO", "To10KMayorSZAO", "To10KTotalTiNAO", "To10KMayorTiNAO" , "To10KTotalCAO", "To10KMayorCAO", "To10KTotalYUAO", "To10KMayorYUAO", "To10KTotalYUVAO", "To10KMayorYUVAO" , "To10KTotalYUZAO", "To10KMayorYUZAO")).Append(mlContext.Regression.Trainers.FastTree()); var model = pipeline.Fit(dataView); return model; } 


Au début, nous lisons les données de l'échantillon d'apprentissage. Ensuite, dans la chaîne, nous déterminons le paramètre qui prédira (étiquette).

Dans notre cas, il s'agit du nombre de problèmes résolus avec succès concernant les appels des citoyens par mois.
Étant donné que dans ce cas, le modèle de renforcement des arbres de décision basé sur la régression est utilisé, nous devons amener tous les signes à des valeurs numériques.
Contrairement au cas avec Accord.NET, la solution OneHotEncoding prête à l'emploi est immédiatement présentée ici dans la documentation.

Après qu'il reste à former les colonnes, comme je l'ai dit ci-dessus, elles devraient toutes être du même type de données, dans ce cas, un flottant.

En conclusion, nous formons et retournons le modèle fini.

Ensuite, nous évaluons la qualité de la prédiction par notre modèle.

  private static void Evaluate(MLContext mlContext, ITransformer model) { IDataView dataView = mlContext.Data.LoadFromTextFile<MayorAppel>(_testDataPath, hasHeader: true, separatorChar: ','); var predictions = model.Transform(dataView); var metrics = mlContext.Regression.Evaluate(predictions, "Label", "Score"); Console.WriteLine(); Console.WriteLine($"*************************************************"); Console.WriteLine($"* Model quality metrics evaluation "); Console.WriteLine($"*------------------------------------------------"); Console.WriteLine($"* RSquared Score: {metrics.RSquared:0.##}"); Console.WriteLine($"* Root Mean Squared Error: {metrics.RootMeanSquaredError:#.##}"); } 

Nous chargeons notre échantillon de test (les 4 derniers mois de l'ensemble), nous obtenons la prédiction de nos données de test sur le modèle formé en utilisant la méthode Transform (). Ensuite, nous calculons les métriques et les imprimons. Dans ce cas, il s'agit du coefficient de détermination et de l'écart type. Idéalement, le premier devrait tendre à 1 et le second essentiellement à zéro.

En principe, pour faire une prédiction, nous n'avions pas besoin de cette méthode, mais il est agréable de comprendre à quel point notre modèle prédit quelque chose.

La dernière méthode demeure - la prédiction elle-même.
Nous le cacherons également sous le spoiler.

méthode et données de prédiction
 private static void TestSinglePrediction(MLContext mlContext, ITransformer model) { var predictionFunction = mlContext.Model.CreatePredictionEngine<MayorAppel, MayorAppelPrediction>(model); var MayorAppelSampleMinData = new MayorAppel() { Year = 2019, Month = "August", ResPositive = 0 }; var MayorAppelSampleMediumData = new MayorAppel() { Year = 2019, Month = "August", TotalAppeals = 111340, AppealsToMayor = 17932, ResExplained = 66858, ResNegative = 8945, ElFormToMayor = 14931, PapFormToMayor = 2967, ResPositive = 0 }; var MayorAppelSampleMaxData = new MayorAppel() { Year = 2019, Month = "August", TotalAppeals = 111340, AppealsToMayor = 17932, ResExplained = 66858, ResNegative = 8945, ElFormToMayor = 14931, PapFormToMayor = 2967, To10KTotalVAO = 67, To10KMayorVAO = 13, To10KTotalZAO = 57, To10KMayorZAO = 13, To10KTotalZelAO = 49, To10KMayorZelAO = 9, To10KTotalSAO = 71, To10KMayorSAO = 14, To10KTotalSVAO = 86, To10KMayorSVAO = 27, To10KTotalSZAO = 68, To10KMayorSZAO = 12, To10KTotalTiNAO = 93, To10KMayorTiNAO = 36, To10KTotalCAO = 104, To10KMayorCAO = 24, To10KTotalYUAO = 56, To10KMayorYUAO = 12, To10KTotalYUVAO = 59, To10KMayorYUVAO = 13, To10KTotalYUZAO = 78, To10KMayorYUZAO = 23, ResPositive = 0 }; var predictionMin = predictionFunction.Predict(MayorAppelSampleMinData); var predictionMed = predictionFunction.Predict(MayorAppelSampleMediumData); var predictionMax = predictionFunction.Predict(MayorAppelSampleMaxData); Console.WriteLine($"**********************************************************************"); Console.WriteLine($"Prediction for August 2019"); Console.WriteLine($"Predicted Positive decisions (Minimum Features): {predictionMin.ResPositive:0.####}, actual res_positive : 22313"); Console.WriteLine($"Predicted Positive decisions (Medium Features: {predictionMed.ResPositive:0.####}, actual res_positive : 22313"); Console.WriteLine($"Predicted Positive decisions (Maximum Features): {predictionMax.ResPositive:0.####}, actual res_positive : 22313"); Console.WriteLine($"**********************************************************************"); } 



Dans l'exemple, nous avons utilisé la classe PredictionEngine, qui nous permet d'obtenir une seule prédiction basée sur le modèle formé et l'ensemble de données de test.

Nous allons créer trois «sondes» avec des données pour la prédiction.
Le premier avec un ensemble minimum de données (seulement un mois et un an), le second avec une moyenne et le troisième avec un ensemble complet d'attributs - respectivement.

Nous obtenons trois prédictions différentes et les imprimons.

Comme vous pouvez le voir sur la capture d'écran (Windows 10 x64), l'ajout de données sur le nombre d'appels pour 10000 habitants dans les districts, dans ce cas, ne fait que gâcher tout, mais l'ajout du reste des données augmente légèrement la précision de la prédiction.



Sous Linux, Mint 19 compile également à merveille en Mono.
Il s'avère que le cadre est assez multiplateforme.




En conclusion, comme promis, je donnerai une petite analyse comparative subjective de ML.NET avec Accord.NET et les bibliothèques d'apprentissage automatique Python.

1. On estime que les développeurs tentent de se conformer aux tendances dans le domaine de l'apprentissage automatique. Bien sûr, en Python avec un tas de bibliothèques installées dans Anaconda, cette tâche pourrait être résolue de manière plus compacte et passer moins de temps sur le développement. Mais en général, il me semble que l'approche de résolution des problèmes avec ML.NET est conviviale pour les personnes habituées à résoudre les problèmes d'apprentissage automatique à l'aide de Python.

2. Comparé à Accord.NET Framework , ML.NET semble plus pratique et prometteur pour une personne qui a essayé l'apprentissage automatique en Python. Je me souviens quand j'ai essayé d'écrire quelque chose sur Accord.NET il y a deux ans, je manquais terriblement d'explications et d'exemples pour certaines classes et méthodes. À cet égard, Ml.NET avec documentation se porte légèrement mieux, malgré le fait que le cadre soit beaucoup plus jeune qu'Accord.NET. Un autre facteur important est que ML.NET, à en juger par l'activité sur GitHub, se développe beaucoup plus intensément qu'Accord.NET et dispose de plus de supports de formation en russe.

Par conséquent, à première vue, ML.NET ressemble à un outil pratique qui complète votre arsenal s'il n'est pas possible d'utiliser Python ou R (par exemple, lorsque vous travaillez avec des API CAD exécutées sur .NET).
Bonne semaine de travail!

Source: https://habr.com/ru/post/fr473342/


All Articles