Leider gehört die Welt des maschinellen Lernens zu Python.
Es ist seit langem als Arbeitssprache für Data Silence verankert, aber Microsoft entschied sich zu argumentieren und präsentierte ein eigenes Tool, das sich problemlos in das Ökosystem integrieren lässt, das die ganze Welt jetzt nutzt. So entstand ML.NET, ein plattformübergreifendes und Open-Source-System für maschinelles Lernen für .NET-Entwickler.
In diesem Artikel möchte ich zeigen, dass die Verwendung von ml.net nicht schwieriger ist als die übrigen Optionen, die in einem wirklich funktionierenden Beispiel der Link sind, zu dem ich unten gehen werde. Dies ist ein Kanal in einem Telegramm, der Daten automatisch aufnimmt, klassifiziert (dies wird berücksichtigt) und Beiträge veröffentlicht. Wen kümmert es, willkommen.
Formulierung des Problems
Als Teenager wollte ich unbedingt einen coolen Bot haben, in dem ich Mädchen anschauen kann, die nicht mit Werbung für die Augäpfel gefüllt sind, sondern nur ein Foto und das war's. Als ich Freizeit hatte, kamen die Sterne und das Verlangen zusammen und ich begann sofort, dieses Problem zu lösen.
Datensammlung
Zunächst kaufte ich einen Upload von Twitter-Daten für das für mich interessante Tag, den der Dienst im CSV-Format bereitstellt (verschiedene Dateien, die sich unterscheiden: der Tweet selbst, Medien, Links). Nachdem wir die benötigte Datei ausgewählt haben, schreiben wir schnell eine Klasse, um die Daten zu analysieren und Duplikate herauszufiltern. Infolgedessen bleiben nur Verweise auf Bilder im Speicher, die am Training teilnehmen. Das ist gut, aber trotzdem müssen die Bilder beschriftet, dh in Kategorien unterteilt werden. In meinem Fall habe ich gewählt: Jungen, Mädchen, Müll und andere (zuerst habe ich Standard gewählt, aber als ich von Strings zu Enum gewechselt bin, musste ich den Namen der Kategorie ändern). Alle diese Fotos, die ich hochgeladen habe, sind akribisch in Väter unterteilt, die das Tag des Fotos widerspiegeln. Es ist also Zeit für das Interessanteste - den Code.
Modelltraining
, , .
— . , .
, .
, , , , . 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} #####");
}
:
Trotz der eher schwachen Metriken (ich habe immer noch 20 Bilder für Tests verwendet): 0,55, aber das Modell hat seine Aufgaben perfekt gemeistert. Dies ist das Modell, das ich für meinen nsfw-Bot verwende , der Daten von Twitter empfängt und diese dann klassifiziert und veröffentlicht.
Es ist also nicht schwierig genug, das Modell zu trainieren und es zu Ihrem Projekt hinzuzufügen. Der Hauptwunsch ist es, es herauszufinden. Und Sie sollten nie aufhören, neue Dinge zu lernen.