機械学習の専門知識がなくてもC#＋ML.NETで高性能なAIが作成できるAutoMLを試してみた


## はじめに
ML.NET 3.0が2023年11月27日（米国時間）に発表されて様々な新機能が追加されました。ますますMicrosoftのAIへの注力が増す中、今回アップグレードされたモデルを探索からパラメータのチューニングまで自動的に行うAutoMLの利用方法について紹介したいと思います。

#### AutoMLとは
>自動機械学習 (自動 ML または AutoML とも呼ばれます) は、時間のかかる反復的な機械学習モデルの開発タスクを自動化するプロセスです。 これにより、データ サイエンティスト、アナリスト、開発は、モデルの品質を維持しながら、高いスケール、効率性、生産性で ML モデルを構築することができます。 Azure Machine Learning の 自動 ML は、Microsoft Research 部門の最先端技術に基づいています。

#### ML.NET 3.0で追加されたAutoML最新機能

- AutoML SweeperでのQAや物体検出機能に対応
- サンプリングキー名を容易に設定できる「Sampling Key Column」を追加
- バイナリ分類をサポートする「AutoZero Tuner」を追加
- 最大モデル数を指定できるパラメーターの追加
- メモリ使用量やデータ量を確認できるリソース監視機能を追加

## 環境
- .NET 8
- Microsoft.ML
- Microsoft.ML.AutoML

## コード
https://github.com/q0v0p/AutoML-for-Titanic

## 内容
データは[前回](https://qiita.com/TechNinja/items/2e9d9fca8f9a65258922)のC#でLightGBMを実装した際と同様に[Kaggle](https://www.kaggle.com/c/titanic)のものを使用しています。なるべく簡単でわかりやすい実装にするために[TrainTestSplit](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.ml.dataoperationscatalog.traintestsplit?view=ml-dotnet)を使ってtrain.csvからtrainデータとtestデータを作成しています。

今回はAutoMLを活用してモデルの選定からパラメータのチューニングまですべて自動で行ってみます。

では早速実装したコードになります。

```csharp
// Initialize MLContext
MLContext mlContext = new MLContext();

var dataPath = Path.GetFullPath(@"..\..\..\Data\train.csv");

// Infer column information
ColumnInferenceResults columnInference =
    mlContext.Auto().InferColumns(dataPath, labelColumnName: "Survived", groupColumns: true);

// Create text loader
TextLoader loader = mlContext.Data.CreateTextLoader(columnInference.TextLoaderOptions);

// Load data into IDataView
IDataView data = loader.Load(dataPath);

// Split into train (80%), validation (20%) sets
TrainTestData trainValidationData = mlContext.Data.TrainTestSplit(data, testFraction: 0.2);

//Define pipeline
SweepablePipeline pipeline =
    mlContext.Auto().Featurizer(data, columnInformation: columnInference.ColumnInformation)
        .Append(mlContext.Auto().BinaryClassification(labelColumnName: columnInference.ColumnInformation.LabelColumnName, useFastForest:false, useLgbm: true));

// Create AutoML experiment
AutoMLExperiment experiment = mlContext.Auto().CreateExperiment();

// Configure experiment
experiment
    .SetPipeline(pipeline)
    .SetBinaryClassificationMetric(BinaryClassificationMetric.Accuracy, labelColumn: columnInference.ColumnInformation.LabelColumnName)
    .SetTrainingTimeInSeconds(120)
    .SetEciCostFrugalTuner()
    .SetDataset(trainValidationData);

// Log experiment trials
var monitor = new AutoMLMonitor(pipeline);
experiment.SetMonitor(monitor);

// Run experiment
var cts = new CancellationTokenSource();
TrialResult experimentResults = await experiment.RunAsync(cts.Token);

// Get best model
var model = experimentResults.Model;
```

## 実行結果
```
Trial 0 finished training in 496ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 1 finished training in 190ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 2 finished training in 155ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 3 finished training in 156ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 4 finished training in 988ms with pipeline ReplaceMissingValues=>OneHotHashEncoding=>FeaturizeText=>Concatenate=>LightGbmBinary
Trial 5 finished training in 194ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 6 finished training in 212ms with pipeline ReplaceMissingValues=>OneHotHashEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 7 finished training in 623ms with pipeline ReplaceMissingValues=>OneHotHashEncoding=>FeaturizeText=>Concatenate=>LightGbmBinary
Trial 8 finished training in 160ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 9 finished training in 205ms with pipeline ReplaceMissingValues=>OneHotHashEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 10 finished training in 222ms with pipeline ReplaceMissingValues=>OneHotHashEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 11 finished training in 152ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 12 finished training in 1446ms with pipeline ReplaceMissingValues=>OneHotHashEncoding=>FeaturizeText=>Concatenate=>LightGbmBinary
Trial 13 finished training in 188ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 14 finished training in 1009ms with pipeline ReplaceMissingValues=>OneHotHashEncoding=>FeaturizeText=>Concatenate=>LightGbmBinary
Trial 15 finished training in 156ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 16 finished training in 2368ms with pipeline ReplaceMissingValues=>OneHotHashEncoding=>FeaturizeText=>Concatenate=>LightGbmBinary
Trial 17 finished training in 165ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 18 finished training in 257ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>LightGbmBinary
Trial 19 finished training in 3126ms with pipeline ReplaceMissingValues=>OneHotHashEncoding=>FeaturizeText=>Concatenate=>LbfgsLogisticRegressionBinary
Trial 20 finished training in 66494ms with pipeline ReplaceMissingValues=>OneHotHashEncoding=>FeaturizeText=>Concatenate=>SdcaLogisticRegressionBinary
Trial 21 finished training in 293ms with pipeline ReplaceMissingValues=>OneHotHashEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 22 finished training in 261ms with pipeline ReplaceMissingValues=>OneHotHashEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 23 finished training in 181ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 24 finished training in 317ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>LightGbmBinary
Trial 25 finished training in 504ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>LightGbmBinary
Trial 26 finished training in 864ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>LightGbmBinary
Trial 27 finished training in 214ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 28 finished training in 236ms with pipeline ReplaceMissingValues=>OneHotHashEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 29 finished training in 300ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>LightGbmBinary
Trial 30 finished training in 203ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>FastTreeBinary
Trial 31 finished training in 324ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>LightGbmBinary
Trial 32 finished training in 246ms with pipeline ReplaceMissingValues=>OneHotEncoding=>FeaturizeText=>Concatenate=>LbfgsLogisticRegressionBinary

Model quality metrics evaluation
--------------------------------
Accuracy: 83.13%
Auc: 83.82%
F1Score: 76.67%
=============== End of model evaluation ===============
```

## 感想
この実行結果を見てわかるようにAutoMLの機械学習パイプラインを作成することにより、AutoMLが自動的に様々なモデルを試しながらデータに基づいた最良のモデルを探索している様子がわかります。今回は機械学習の基礎的な問題であるタイタニックの生存率予測を行ってみました。この問題をAutoMLに与えるとAutoMLが自動的にモデルを探索して自動的にパラメータをチューニングすることにより、実装時に特別なチューニングが一切不要で**正答率83.13%という高い数字**を達成することが出来ました。

モデルの選定からハイパーパラメータの調整などデータサイエンティストでもないエンジニアが行うには壁が高い作業もAutoMLによる自動探索によって改善できると感じます。

新規で機械学習を行う場合に一先ずAutoMLに投入してパフォーマンスを確認する方法は有効だと思います。

最近はMicrosoftがOpenAIに買収を仕掛けたり、AI専用チップMicrosoft Azure Maiaを独自開発したりとますますMicrosoft+C#によるAI開発が進んできています。ぜひこの機会に**C#\:heart:AI**の世界を体験してみてください！

皆様もC#で機械学習を楽しみましょう！

## 参考
https://devblogs.microsoft.com/dotnet/announcing-ml-net-3-0/
https://learn.microsoft.com/ja-jp/azure/machine-learning/concept-automated-ml?view=azureml-api-2
https://github.com/dotnet/machinelearning-samples/tree/main/samples/csharp/getting-started/MLNET2