Voyons les filles? Ou ml.net au travail

Malheureusement, le monde de l'apprentissage automatique appartient à python.





Il a longtemps été enraciné comme langue de travail pour Data Silence, mais Microsoft a décidé de discuter et a présenté son propre outil qui peut être facilement intégré à l'écosystème que le monde entier utilise maintenant. C'est ainsi qu'est né ML.NET, un système d'apprentissage automatique multiplateforme et open source pour les développeurs .NET.





Dans cet article, je veux montrer que l'utilisation de ml.net n'est pas plus difficile que le reste des options qui sont, sur un exemple vraiment fonctionnel, le lien vers lequel je vais laisser ci-dessous. Il s'agit d'un canal dans un télégramme qui récupère automatiquement les données, les classe (c'est ce que nous allons considérer) et les publie. Peu importe, bienvenue.





Formulation du problème

En tant qu'adolescent, je voulais vraiment avoir un robot cool où je puisse regarder des filles, qui ne sera pas rempli de publicités sur les globes oculaires, mais juste une photo et c'est tout. Alors, quand j'avais du temps libre, les étoiles et le désir se sont réunis, j'ai immédiatement commencé à résoudre ce problème.





Collecte de données

Pour commencer, j'ai acheté un téléchargement de données Twitter pour la balise qui m'intéresse, que le service donne au format csv (plusieurs fichiers différents qui diffèrent: le tweet lui-même, les médias, les liens). Après avoir sélectionné le fichier dont j'ai besoin, nous écrivons rapidement une classe pour analyser les données, filtrer les doublons. Par conséquent, seules les références aux images qui participeront à la formation sont laissées en mémoire. C'est bien, mais tout de même, les images doivent être étiquetées, c'est-à-dire divisées en catégories. Dans mon cas, j'ai choisi: les garçons, les filles, les poubelles et autres (au début j'ai choisi par défaut, mais quand je suis passé des chaînes à Enum, j'ai dû changer le nom de la catégorie). Toutes ces photos, j'ai téléchargées, méticuleusement divisées en papas qui reflétaient l'étiquette de la photo, il est donc temps pour la chose la plus intéressante - le code.





Formation de modèle

, , .





 

 — .  , .





, .





( ; . Deep learning) — ( , , , ), (. feature/representation learning), .





, , , , . TensorFlow Inception , ImageNet.





" ", ( 2000 , 2 , , +- ).





, , , , , . 4 500 .





. , model nuget :





using Microsoft.ML; 
using Microsoft.ML.Data; 
      
      



, :





    private readonly string _inceptionTensorFlowModel; //    Inception 
    private MLContext mlContext;
    private ITransformer model;
    private DataViewSchema schema;
    private string modelName = "model.zip"; //     
    private string _setsPath = @"C:\datasets"; //     ,      
    
    
        public Model(string inceptionTensorFlowModel)
        {
            mlContext = new MLContext();
            _inceptionTensorFlowModel = inceptionTensorFlowModel;
        }
      
      



MLContext - .NET. "" , , DbContext EntityFramework.





ITransformer - , , , .





DataViewSchema - .





, "", , .





public class ImageData
    {
        [LoadColumn(0)]
        public string ImagePath;

        [LoadColumn(1)]
        public string Label;

  		//,   ,        
        public static (IEnumerable<ImageData> train, IEnumerable<ImageData> test) ReadData(string pathToFolder)
        {
            List<ImageData> list = new List<ImageData>();
            var directories = Directory.EnumerateDirectories(pathToFolder);
            foreach (var dir in directories)
            {
                if (!dir.Contains("girls") && !dir.Contains("boys") && !dir.Contains("trash") && !dir.Contains("other"))
                    continue;
                var label = dir.Split(@"\").Last();
                foreach (var file in Directory.GetFiles(dir))
                {
                    list.Add(new ImageData()
                    {
                        ImagePath = file,
                        Label = label
                    });
                }
            }
            list = list.Shuffle().ToList();
            return GetSets(list);
        }

				//      
        public static (IEnumerable<ImageData> train, IEnumerable<ImageData> test) GetSets(IEnumerable<ImageData> data)
        {
            var trainCount = data.Count() / 100 * 99;
            var train = data.Take(trainCount);
            var test = data.Skip(trainCount);
            return (train, test);
        }
    }
    public class ImagePrediction : ImageData
    {
        [ColumnName("Score")]
        public float[] Score;

        public string PredictedLabelValue;
    }
      
      



IEnumerable :





,





 public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
        {
            return source.Shuffle(new Random());
        }
        public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
        {
            if (source == null) throw new ArgumentNullException("source");
            if (rng == null) throw new ArgumentNullException("rng");

            return source.ShuffleIterator(rng);
        }

        private static IEnumerable<T> ShuffleIterator<T>(
            this IEnumerable<T> source, Random rng)
        {
            var buffer = source.ToList();
            for (int i = 0; i < buffer.Count; i++)
            {
                int j = rng.Next(i, buffer.Count);
                yield return buffer[j];

                buffer[j] = buffer[i];
            }
        }
      
      



, :





private struct InceptionSettings
        {
            public const int ImageHeight = 224;
            public const int ImageWidth = 224;
            public const float Mean = 117;
            public const float Scale = 1;
            public const bool ChannelsLast = true;
        }
      
      



, .





:





private double TrainModel()
        {
            IEstimator<ITransformer> pipeline = mlContext.Transforms.LoadImages(outputColumnName: "input", imageFolder: "", inputColumnName: nameof(ImageData.ImagePath))
                           .Append(mlContext.Transforms.ResizeImages(outputColumnName: "input", imageWidth: InceptionSettings.ImageWidth, imageHeight: InceptionSettings.ImageHeight, inputColumnName: "input"))
                           .Append(mlContext.Transforms.ExtractPixels(outputColumnName: "input", interleavePixelColors: InceptionSettings.ChannelsLast, offsetImage: InceptionSettings.Mean))
                           .Append(mlContext.Model.LoadTensorFlowModel(_inceptionTensorFlowModel).
                               ScoreTensorFlowModel(outputColumnNames: new[] { "softmax2_pre_activation" }, inputColumnNames: new[] { "input" }, addBatchDimensionInput: true))
                           .Append(mlContext.Transforms.Conversion.MapValueToKey(outputColumnName: "LabelKey", inputColumnName: "Label"))
                           .Append(mlContext.MulticlassClassification.Trainers.LbfgsMaximumEntropy(labelColumnName: "LabelKey", featureColumnName: "softmax2_pre_activation"))
                           .Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabelValue", "PredictedLabel"))
                           .AppendCacheCheckpoint(mlContext);

            var loadImages = ImageData.ReadData(_setsPath);
            IDataView trainingData = mlContext.Data.LoadFromEnumerable<ImageData>(loadImages.train);
            ITransformer model = pipeline.Fit(trainingData);
            IDataView testData = mlContext.Data.LoadFromEnumerable<ImageData>(loadImages.test);
            IDataView predictions = model.Transform(testData);
            List<ImagePrediction> imagePredictionData = mlContext.Data.CreateEnumerable<ImagePrediction>(predictions, true).ToList();
            MulticlassClassificationMetrics metrics =
                mlContext.MulticlassClassification.Evaluate(predictions,
                  labelColumnName: "LabelKey",
                  predictedLabelColumnName: "PredictedLabel");
            schema = trainingData.Schema;
            return metrics.LogLoss;
        }
      
      



:





IEstimator<ITransformer> pipeline = mlContext.Transforms.LoadImages(outputColumnName: "input", imageFolder: "", inputColumnName: nameof(ImageData.ImagePath))
     .Append(mlContext.Transforms.ResizeImages(outputColumnName: "input", imageWidth: InceptionSettings.ImageWidth, imageHeight: InceptionSettings.ImageHeight, inputColumnName: "input"))
     .Append(mlContext.Transforms.ExtractPixels(outputColumnName: "input", interleavePixelColors: InceptionSettings.ChannelsLast, offsetImage: InceptionSettings.Mean))
                           
      
      



. , :





.Append(mlContext.Model.LoadTensorFlowModel(_inceptionTensorFlowModel).
    ScoreTensorFlowModel(outputColumnNames: new[] { "softmax2_pre_activation" }, inputColumnNames: new[] { "input" }, addBatchDimensionInput: true))
      
      



 . , :





.Append(mlContext.Transforms.Conversion.MapValueToKey(outputColumnName: "LabelKey", inputColumnName: "Label"))
      
      



ml.net, , .





:





.Append(mlContext.MulticlassClassification.Trainers.LbfgsMaximumEntropy(labelColumnName: "LabelKey", featureColumnName: "softmax2_pre_activation"))
      
      



:





.Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabelValue", "PredictedLabel"))
.AppendCacheCheckpoint(mlContext);
      
      



:





var loadImages = ImageData.ReadData(_setsPath);
            IDataView trainingData = mlContext.Data.LoadFromEnumerable<ImageData>(loadImages.train);
            model = pipeline.Fit(trainingData);
      
      



, .





						IDataView testData = mlContext.Data.LoadFromEnumerable<ImageData>(loadImages.test);
            IDataView predictions = model.Transform(testData);
            List<ImagePrediction> imagePredictionData = mlContext.Data.CreateEnumerable<ImagePrediction>(predictions, true).ToList();
            MulticlassClassificationMetrics metrics =
                mlContext.MulticlassClassification.Evaluate(predictions,
                  labelColumnName: "LabelKey",
                  predictedLabelColumnName: "PredictedLabel");
      
      



. . , "" .





            schema = trainingData.Schema;
            return metrics.LogLoss;
      
      



LogLoss( ).





, .





, , :





  public void SaveModel() => mlContext.Model.Save(model, schema, Path.Combine(_setsPath, modelName));
      
      



, , :





    public void FitModel()
    {
        var LogLoss = TrainModel();
        Console.WriteLine($"LogLoss is {LogLoss}");
        SaveModel();
    }

      
      



, , , , , .





, , , .





:





    private PredictionEngine<ImageData, ImagePrediction> predictor;
      
      



, (+ ):





        public ImagePrediction ClassifySingleImage(string filePath)
        {
            if (model == null)
                LoadModel();
            if (predictor == null)
                predictor = mlContext.Model.CreatePredictionEngine<ImageData, ImagePrediction>(model);
            var imageData = new ImageData()
            {
                ImagePath = filePath
            };
            return predictor.Predict(imageData);
        }
        public void LoadModel() =>
            model = mlContext.Model.Load(Path.Combine(_setsPath, modelName), out schema);
      
      



, .





, :





 static void Main(string[] args)
        {
            Console.ForegroundColor = ConsoleColor.White;
            Stopwatch s = new Stopwatch();
            s.Start();

            Model model = new Model(@"C:\tensorflow_inception_graph.pb");
            model.FitModel();
            Console.WriteLine($"##### Model train ended for {s.Elapsed.Minutes}:{s.Elapsed.Seconds} #####");

            s.Restart();

            var res1 = model.ClassifySingleImage(@"C:\EugRqKFXUAYMTWz.jpg");
            Console.WriteLine($" > It's trash. Classification result is {res1.PredictedLabelValue} with score: {res1.Score.Max()}");
            Console.WriteLine($"##### Ended for {s.Elapsed.Minutes}:{s.Elapsed.Seconds} #####");

            s.Restart();

            var res2 = model.ClassifySingleImage(@"C:\EvpmOjIXcAMgj5r.jpg");
            Console.WriteLine($" > It's girl. Classification result is {res2.PredictedLabelValue} with score: {res1.Score.Max()}");
            Console.WriteLine($"##### Ended for {s.Elapsed.Minutes}:{s.Elapsed.Seconds} #####");
        }
      
      



:





Malgré les métriques plutôt faibles (j'ai encore utilisé 20 images pour les tests): 0,55, mais le modèle a parfaitement fait face à ses tâches. C'est le modèle que j'utilise pour mon bot nsfw , qui reçoit des données de Twitter, puis les classe et les publie.





Il n'est donc pas assez difficile de former le modèle et de l'ajouter à votre projet, le principal désir est de le comprendre. Et vous ne devriez jamais arrêter d'apprendre de nouvelles choses.








All Articles