Sayangnya, dunia pembelajaran mesin adalah milik python.
Ini telah lama mengakar sebagai bahasa kerja untuk Data Silence, tetapi Microsoft memutuskan untuk berdebat dan menyajikan alatnya sendiri yang dapat dengan mudah diintegrasikan dengan ekosistem yang digunakan seluruh dunia sekarang. Beginilah ML.NET, sistem pembelajaran mesin lintas platform dan sumber terbuka untuk pengembang .NET, lahir.
Pada artikel ini, saya ingin menunjukkan bahwa menggunakan ml.net tidak lebih sulit daripada opsi lainnya yang, pada contoh yang benar-benar berfungsi, tautan yang akan saya tinggalkan di bawah. Ini adalah saluran di telegram yang secara otomatis mengambil data, mengklasifikasikannya (inilah yang akan kami pertimbangkan) dan posting. Siapa peduli, selamat datang.
Rumusan masalah
Sebagai seorang remaja, saya sangat ingin memiliki bot keren di mana saya dapat melihat gadis-gadis, yang tidak akan dikemas dengan iklan ke bola mata, tetapi hanya foto dan hanya itu. Jadi, ketika saya punya waktu luang, bintang dan keinginan datang bersama, saya segera mulai menyelesaikan masalah ini.
Pengumpulan data
Pertama-tama, saya membeli unggahan data Twitter untuk tag yang menarik bagi saya, yang diberikan layanan dalam format csv (beberapa file berbeda yang berbeda: tweet itu sendiri, media, tautan). Setelah memilih file yang saya butuhkan, kami dengan cepat menulis kelas untuk mengurai data, menyaring duplikat. Akibatnya, hanya referensi gambar yang akan berpartisipasi dalam pelatihan yang tersisa di memori. Ini bagus, tetapi bagaimanapun, gambar perlu diberi label, yaitu dibagi ke dalam kategori. Dalam kasus saya, saya memilih: anak laki-laki, perempuan, sampah dan lainnya (pada awalnya saya memilih default, tetapi ketika saya beralih dari string ke Enum, saya harus mengubah nama kategori). Semua foto ini, saya unggah, dengan cermat dibagi menjadi ayah yang mencerminkan tag foto, jadi inilah saatnya untuk hal yang paling menarik - kodenya.
Pelatihan model
, , .
â . , .
, .
, , , , . 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} #####");
}
:
Meskipun metriknya agak lemah (saya masih menggunakan 20 gambar untuk pengujian): 0,55, tetapi model mengatasi tugasnya dengan sempurna. Ini adalah model yang saya gunakan untuk bot nsfw saya , yang menerima data dari Twitter, lalu mengklasifikasikan dan mempostingnya.
Jadi tidak cukup sulit untuk melatih model dan menambahkan proyek Anda, keinginan utamanya adalah untuk mengetahuinya. Dan Anda tidak boleh berhenti mempelajari hal-hal baru.