残念ながら、機械学習の世界はPythonに属しています。
Data Silenceの作業言語として長い間定着してきましたが、Microsoftは、全世界が現在使用しているエコシステムと簡単に統合できる独自のツールについて議論し、発表することを決定しました。このようにして、.NET開発者向けのクロスプラットフォームでオープンソースの機械学習システムであるML.NETが誕生しました。
この記事では、ml.netを使用することは、実際に機能する例では、以下に残すリンクである他のオプションよりも難しくないことを示したいと思います。これは、データを自動的に取得して分類し(これを検討します)、投稿する電報のチャネルです。誰が気にする、ようこそ。
問題の定式化
10代の頃、私は女の子を見ることができるクールなボットが本当に欲しかったのです。それは眼球への広告ではなく、写真だけでいっぱいになります。それで、暇な時、星と欲望が一緒になって、すぐにこの問題を解決し始めました。
データ収集
まず、興味のあるタグのTwitterデータのアップロードを購入しました。これは、サービスがcsv形式で提供します(ツイート自体、メディア、リンクなど、いくつかの異なるファイル)。必要なファイルを選択したら、データを解析して重複を除外するクラスをすばやく作成します。その結果、トレーニングに参加する画像への参照のみがメモリに残されます。これは良いことですが、それでも、画像にラベルを付ける、つまりカテゴリに分類する必要があります。私の場合、男の子、女の子、ゴミ箱などを選択しました(最初はデフォルトを選択しましたが、文字列から列挙型に移行するときに、カテゴリの名前を変更する必要がありました)。これらすべての写真をアップロードし、写真のタグを反映したパパに細心の注意を払って分割したので、最も興味深いもの、つまりコードの時間です。
モデルトレーニング
, , .
— . , .
, .
, , , , . 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} #####");
}
:
メトリックがかなり弱いにもかかわらず(私はまだテストに20枚の画像を使用しました):0.55ですが、モデルはそのタスクを完全に処理しました。これは、Twitterからデータを受信し、分類して投稿するnsfwボットに使用するモデルです。
したがって、モデルをトレーニングしてプロジェクトに追加することはそれほど難しくありません。主な目的は、モデルを理解することです。そして、あなたは新しいことを学ぶのをやめるべきではありません。