ML.NET 1.0 による回帰分析

#ML.NET 1.0 がリリース
2019/05/06 に ML.NET 1.0.0 がリリースされ、ML.NET では初めての正式リリースとなりました。[(ML.NET 1.0.0 Release Notes)](https://github.com/dotnet/machinelearning/blob/master/docs/release-notes/1.0.0/release-1.0.0.md)

[以前の投稿](https://qiita.com/hiromasa-masuda/items/67920b4dba478d3f83c9)では、最初のプレビューリリース ML.NET 0.1 について書きましたが、ちょうど ML.NET 0.1 のリリースから約一年がたったところで、ML.NET 1.0.0 として正式リリースを迎えました。

ML.NET 1.0.0 は、0.1 と比較すると大きく変更されています。0.1 のコードは、1.0.0 では動作しません。
そこで、以前紹介した ML.NET を利用したワインの品質を回帰分析で推論する機械学習モデルを作成する手順を ML.NET 1.0.0 ベースであらためて紹介したいと思います。

(*) この記事の公開当時は、ML.NET 1.0 を対象としていましたが、2020 年 9 月現在では、ML.NET 1.5.2 がリリースされています。.NET Core も現在では、.NET Core 3.1 がリリースされています。その後、ここに記載した手順は .NET Core 3.1、ML.NET 1.5.2 でも動作することを確認しました。以下のリポジトリに .NET Core 3.1、ML.NET 1.5.2 ベースのサンプル コードを配置したので必要に応じて参照ください。

- [GitHub MlnetSamples](https://github.com/Hiromasa-Masuda/MlnetSamples)

#事前に準備しくておくもの

データセットは、以前と同じく、UCI が公開している [UC Irvine Machine Learning Repository](https://archive.ics.uci.edu/ml/index.php) の [Wine Quality Data Set](https://archive.ics.uci.edu/ml/datasets/Wine+Quality) を利用します。

ML.NET 1.0.0 は、.NET Core 2.1 以降が必要となります。.NET Framework 4.6.1 以降でも動作するとのことです。
ML.NET は、.NET Core ベースでなので、クロスプラットフォームで開発、動作させることができますが、今回は、Visual Studio 2019 での手順を説明しますので、必要に応じて準備を行ってください。

#.NET Core プロジェクトの作成

Visual Studio 2019 を起動し、[新しいプロジェクトの作成] - [コンソール アプリ(.NET Core)] を選択し、[次へ] を選択、[プロジェクト名] に任意のプロジェクト名(ここでは、 "WinequalityDemo002" ) を入力し [作成] を選択します。

#Microsoft.ML のインストール

[ソリューション エクスプローラー] から、プロジェクトを選択し、右クリックメニューから、[NuGet パッケージの管理] を選択します。

[参照] タブの [検索]　テキスト ボックスに "Microsoft.ML" と入力し、[Microsoft.ML] を選択し、[インストール] を選択し、以降画面の指示に従い "Microsoft.ML" をインストールします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172205/3f99fb4f-03f0-9efd-2a15-2e9e002a5ec6.png)

#学習データの準備

[ソリューション エクスプローラー] から、プロジェクトを選択し、右クリックメニューから、[追加] - [新しいフォルダー] を選択しフォルダーを作成し、任意の名前 (ここでは、"Data") を付けます。

[Wine Quality Data Set](https://archive.ics.uci.edu/ml/datasets/Wine+Quality) - [Data Folder](https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/) から、[winequality-red.csv](https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv) をダウンロードし、作成した "Data" フォルダーに格納します。

[ソリューション エクスプローラー] から、"winequality-red.csv" を選択し、[プロパティ] - [出力ディレクトリにコピー] から、"常にコピーする" を選択します。

今回利用するデータは、赤ワインのアルコール度数、pH、塩化ナトリウム濃度等の 10 種類の属性と品質が数値で定義されたものです。

各属性値と品質の対で機械学習を行い、未知の各属性値が与えられた場合に、品質を算出できるような機械学習モデルを生成します。

#ワインの各属性値データセットの定義

まず、データセットのカラムの定義を行います。ワインの品質を表現するクラスを定義します。

[ソリューション エクスプローラー] から、プロジェクトを選択し、右クリックメニューから、[追加] - [クラス] - [名前] に "WineQualityData" と入力し、[追加] を選択します。

以下のコードを記述します。

```c#:WineQuality.cs

using Microsoft.ML.Data;

namespace WinequalityDemo002
{
    public class WineQualityData
    {
        [LoadColumn(0)]
        public float FixedAcidity;

        [LoadColumn(1)]
        public float VolatileAcidity;

        [LoadColumn(2)]
        public float CitricAcid;

        [LoadColumn(3)]
        public float ResidualSugar;

        [LoadColumn(4)]
        public float Chlorides;

        [LoadColumn(5)]
        public float FreeSulfurDioxide;

        [LoadColumn(6)]
        public float TotalSulfurDioxide;

        [LoadColumn(7)]
        public float Density;

        [LoadColumn(8)]
        public float Ph;

        [LoadColumn(9)]
        public float Sulphates;

        [LoadColumn(10)]
        public float Alcohol;

        [LoadColumn(11)]
        public float Quality;
    }

    public class WineQualityPrediction
    {
        [ColumnName("Score")]
        public float Quality;
    }
}
```
"WineQualityData" というクラスは、読み込むデータのカラムを定義をしています。各フィールドの属性でデータファイルに対応するインデックスを定義しています。

ML.NET 0.1 では、カラムのインデックスを文字列で指定していたものが、数値で指定するように変更されています。

"WineQualityPrediction" というクラスでは、推論時に出力するデータセットを定義しています。

#学習データの読み込み

以降から、"Program.cs" にコードを追加していきます。
はじめに、以下のコードで各ライブラリを using しておきます。

```c#:Program.cs
using System.Linq;
using Microsoft.ML;
```

以降のコードを Main 関数に記述していきます。

まずは、データのロードです。

```c#:Program.cs
//コンテキストの生成
MLContext mlContext = new MLContext(seed: 1);
//データのロード
string dataPath = @".\Data\winequality-red.csv";
IDataView data = mlContext.Data.LoadFromTextFile<WineQualityData>(dataPath, hasHeader: true, separatorChar: ';');

//学習データとテストデータに分割
var split = mlContext.Data.TrainTestSplit(data, testFraction: 0.2, seed: 0);
```
コンテキストを生成し、学習データと、テストデータをロードします。
今回は、[DataOperationsCatalog.TrainTestSplit](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.ml.dataoperationscatalog.traintestsplit) を使って、学習データとテストデータを分割していますが、もちろん、ファイルレベルであらかじめ分割しておき、それぞれを [TextLoaderSaverCatalog.LoadFromTextFile](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.ml.textloadersavercatalog.loadfromtextfile) でロードしても構いません。

ML.NET 0.1 時点では、データ加工がサポートされていませんでしたが、.NET 1.0.0 では、ある程度のデータ加工ができるようになりました。

#学習データの定義

以降で、学習パイプラインを定義していきます。
まずは、学習データの定義から。

```c#:Program.cs
//学習パイプラインの定義
//学習データの定義
var dataProcessPipeline = mlContext.Transforms.Concatenate(
  outputColumnName: "Features",
  nameof(WineQualityData.FixedAcidity),
  nameof(WineQualityData.VolatileAcidity),
  nameof(WineQualityData.CitricAcid),
  nameof(WineQualityData.ResidualSugar),
  nameof(WineQualityData.Chlorides),
  nameof(WineQualityData.FreeSulfurDioxide),
  nameof(WineQualityData.TotalSulfurDioxide),
  nameof(WineQualityData.Ph),
  nameof(WineQualityData.Sulphates),
  nameof(WineQualityData.Alcohol));
```
学習に用いるデータの列を一つにまとめて "Features" という列で、再定義しています。

ここで、[NormalizationCatalog.NormalizeMeanVariance](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.ml.normalizationcatalog.normalizemeanvariance) 等を使って、データの正規化を行うこともできます。

#学習アルゴリズムの定義
次に、学習アルゴリズムを定義します。

```c#:Program.cs
//学習アルゴリズムの定義
var trainer = mlContext.Regression.Trainers.Sdca(labelColumnName: nameof(WineQualityData.Quality), featureColumnName: "Features");
//var trainer = mlContext.Regression.Trainers.FastForest(labelColumnName: "Label", featureColumnName: "Features");

//学習アルゴリズムをパイプラインに設定
var trainingPipeline = dataProcessPipeline.Append(trainer);

//学習データを用いて学習モデルを生成
var trainedModel = trainingPipeline.Fit(split.TrainSet);
```
ここでは、学習アルゴリズムには、線形回帰(SDCA) を用い、"Quality" を目的変数、"Features" 列を説明変数として定義しています。
[EstimatorChain<TLastTransformer>.Fit](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.ml.data.estimatorchain-1.fit) でパイプラインにもとづき学習を実行します。学習が完了後、学習モデルが返却されます。

#学習モデルの評価

最後に、生成した学習モデルの評価を行います。

```c#:Program.cs
//テストデータによるモデルの評価
//生成した学習モデルにテストデータを設定
IDataView predictions = trainedModel.Transform(split.TestSet);
//学習モデルの評価
var metrics = mlContext.Regression.Evaluate(predictions, labelColumnName: nameof(WineQualityData.Quality), scoreColumnName: "Score");

Console.WriteLine($"*   損失関数(LossFn): {metrics.LossFunction}");
Console.WriteLine($"*   決定係数(R2 Score): {metrics.RSquared}");
Console.WriteLine($"*   平均絶対誤差(Absolute loss): {metrics.MeanAbsoluteError}");
Console.WriteLine($"*   平均二乗誤差(Squared loss): {metrics.MeanSquaredError}");
Console.WriteLine($"*   平均二乗誤差平方根(RMS loss): {metrics.RootMeanSquaredError}");

```
ここまでのコードをそのまま実行させるとわかりますが、あまり良いモデルを作成することができません。
実際には、データを加工したり、アルゴリズムを変更したり、アルゴリズムのパラメーターを調整して、これらの手順を繰り返し、より良いモデルを作成していきます。

#学習モデルの保存と復元

調整を繰り返し、良いモデルができたらモデルをファイルに保存します。

```c#:Program.cs
string modelFilePath = $@".\Data\model{DateTimeOffset.Now:yyyyMMddHmmss}.zip";
//学習モデルをファイルに保存
mlContext.Model.Save(trainedModel, split.TrainSet.Schema, modelFilePath);

```

他のアプリケーション等で、学習モデルをロードし、推論を行う場合は、以下の様にします。

```c#
//学習モデルのロード
ITransformer model = mlContext.Model.Load(modelFilePath, out DataViewSchema inputSchema);
//推論エンジンの生成
var predictionEngine = mlContext.Model.CreatePredictionEngine<WineQualityData, WineQualityPrediction>(model);

//各説明変数を定義したオブジェクトを生成
WineQualityData wineQualityData = new WineQualityData()
  {
   //TODO: 各属性の設定
   //FixedAcidity = ....
  };

// 推論の実行
WineQualityPrediction predictionResult = predictionEngine.Predict(wineQualityData);

```

モデルをロードして利用する場合、目的変数、説明変数である WineQualityData, WineQualityPrediction が必要となるので、これらのクラスを学習用のプログラム、参照するプログラムで横断して利用できるように、別のプロジェクトや DLL として参照できるようにしておくことが良いかもしれません。

#参考サイト

今回の手順においては、以下のサイトを参考に記載しました。

- [ML.NET 公式](https://dotnet.microsoft.com/apps/machinelearning-ai/ml-dotnet)
- [ML.NET Samples at GitHub](https://github.com/dotnet/machinelearning-samples)
- [ML.NET API リファレンス](https://docs.microsoft.com/ja-jp/dotnet/api/?view=ml-dotnet)
- [赤ワインと深層学習をGUIで](https://qiita.com/ttanimura/items/5f844885c36e9fffe28a)
- [ワインの味（美味しさのグレード）は予測できるか？（２）](http://sudillap.hatenablog.com/entry/2013/04/27/204140)
- [過去の記事 - ML.NET による回帰分析](https://qiita.com/hiromasa-masuda/items/67920b4dba478d3f83c9)