見出し画像

VB.netからC#で書かれた回帰予測(AI・機械学習)の処理を呼び出す・その2

前回掲載したソースコード、何度も書きますが、例外処理もロギング機能もありません。
例外処理とロギング機能については、各自C#の学習をするときに学んでくださいね。

そして、肝心のソースコードが動かない件の対応法を考えます。
何が原因かと言えば、予測に必要なCSVファイルをプログラムが読み込めないことです。

今回のソースコードは2種類のCSVファイルを使っています。
1つは学習用データ、もう1つは評価に使うデータです。

この他にVB.netからC#で書かれたクラスライブラリーの機械学習を呼び出すには、2つのデータが必要です。
予測してほしいデータと予測値です。

合計4つのデータを受け渡しする必要がありますが、簡単な部分から作成したいと思います。
とりあえず動くコードにすることが目標になります。

適当なフォルダを作成し、ネットからtaxi-fare-train.csvとtaxi-fare-test.csvをダウンロードしてください。
githubで公開されているので、Downloadボタンを押してください。
https://github.com/dotnet/machinelearning/blob/master/test/data/taxi-fare-train.csv
https://github.com/dotnet/machinelearning/blob/master/test/data/taxi-fare-test.csv


実際にコードを書いて動かしてみると、C#側のソースコードに問題があることが分かります。
コード内で利用するCSVファイル、固定している位置から呼び出しています。

少なくとも、CSVファイル2つを保存しているフォルダを指定しなければなりません。
今回は、学習用と確認用のCSVファイルをVB.netから指定し、とりあえず最小限の改良でプログラムが動くようにします。

ステップ実行も可能ですので、機械学習のコードへの理解が進むと思います。

#学習 #勉強 #AI #機械学習 #プログラミング

#Visual #CSharp #BASIC #ソースコード #Windows #自動判定
#数列 #予測 #次の値

VB.net側のソースコードです。

Imports System.Reflection
Imports System.Windows.Forms
Imports System.IO
Imports System.Text
Imports Microsoft.VisualBasic.FileIO
Imports System.Globalization


Public Class Form1


Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

'クラスライブラリーを使う宣言
Dim ClassLibraryObject As Object = New ClassLibrary1.Class1

'csvファイルが保存されている位置を指定する
Dim DataForLearning As String = "C:\Ml\ClassLibrary1\data\taxi-fare-train.csv"
Dim VerificationData As String = "C:\Ml\ClassLibrary1\data\taxi-fare-test.csv"

'C#のクラスライブラリーで機械学習を行わせる
Dim BooleanReturn As Boolean = ClassLibraryObject.SampleMachineLearning(DataForLearning, VerificationData)


End Sub
End Class

C#側のソースコードです。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.ML;
using Microsoft.ML.Data;

namespace ClassLibrary1
{

//TaxiTrip は入力データ クラスであり、各データ セット列の定義が含まれています。
//LoadColumn 属性を使用して、データ セット内のソース列のインデックスを指定します。
public class TaxiTrip
{
[LoadColumn(0)]
public string VendorId;

[LoadColumn(1)]
public string RateCode;

[LoadColumn(2)]
public float PassengerCount;

[LoadColumn(3)]
public float TripTime;

[LoadColumn(4)]
public float TripDistance;

[LoadColumn(5)]
public string PaymentType;

[LoadColumn(6)]
public float FareAmount;
}


//TaxiTripFarePrediction クラスは予測結果を表します。
//このクラスには、Score ColumnName 属性が適用された 1 つの浮動小数点型フィールド FareAmount が含まれています。
//回帰タスクの場合、Score 列には、予測ラベル値が含まれます。
public class TaxiTripFarePrediction
{
[ColumnName("Score")]
public float FareAmount;
}


//VB.netから呼び出されるクラスライブラリーのクラス
public class Class1
{

//VB.netから呼び出される関数
public Boolean SampleMachineLearning(string SynthesizedCsvFileForLearning,string SynthesizedCsvFileForEvaluation)
{

//すべての ML.NET 操作は、MLContext クラスで始まります。
//mlContext を初期化することで、モデル作成ワークフローのオブジェクト間で共有できる新しい ML.NET 環境が作成されます。
//これは Entity Framework における DBContext と概念的には同じです。
//seed、ML.net環境で仕様する乱数の指定?
MLContext mlContext = new MLContext(seed: 0);

//トレーニング用のCSVファイルを指定し、予測計算式を取得する。
var model = Train(mlContext, SynthesizedCsvFileForLearning);

//評価用のCSVファイルを指定し、予測計算式の正確性を確認する
Evaluate(mlContext, model, SynthesizedCsvFileForEvaluation);


//予測計算式を使い、予測を行う。
TestSinglePrediction(mlContext, model);

return true;
}


//Train メソッドは、モデルをトレーニングします。
//次の処理を実行します。
//・データを読み込みます。
//・データを抽出して変換します。
//・モデルをトレーニングします。
//・モデル(予測計算式)を返します。
public static ITransformer Train(MLContext mlContext, string dataPath)
{
//データを読み込んで変換する
//ML.NET では、数値またはテキストの表形式データを記述する柔軟で効率的な方法として、IDataView クラスを使います。
//IDataView は、テキスト ファイルを読み込むか、またはリアルタイムで読み込むことができます
//(たとえば、SQL データベースまたはログ ファイル)。
//入力データのクラス TaxiTrip
//トレーニング用CSV dataPath
//CSVにヘッダがあるか true=ある false=なし
//CSVの区切文字 ,
IDataView dataView = mlContext.Data.LoadFromTextFile<TaxiTrip>(dataPath, hasHeader: true, separatorChar: ',');


//注意:Appendで始まる行も含めて、C#では1行として処理されます
//予測に使わないtrip_time_in_secsが処理されていない事が重要です

//タクシー運賃を予測したいので、FareAmount 列を、予測する Label に指定します。
//CopyColumnsEstimator 変換クラスを使って FareAmount をコピーします。
var pipeline = mlContext.Transforms.CopyColumns(outputColumnName: "Label", inputColumnName: "FareAmount")

//モデルをトレーニングするアルゴリズムには、数値が必要であるため、
//カテゴリ データ (VendorId、RateCode、PaymentType) の値を文字列から
//(VendorIdEncoded、RateCodeEncoded、PaymentTypeEncoded)へ数値に変換する必要があります。
//それを行うには、異なる数値キーの値を各列内の異なる値に割り当てる OneHotEncodingTransformer 変換クラスを使用します。
.Append(mlContext.Transforms.Categorical.OneHotEncoding(outputColumnName: "VendorIdEncoded", inputColumnName: "VendorId"))
.Append(mlContext.Transforms.Categorical.OneHotEncoding(outputColumnName: "RateCodeEncoded", inputColumnName: "RateCode"))
.Append(mlContext.Transforms.Categorical.OneHotEncoding(outputColumnName: "PaymentTypeEncoded", inputColumnName: "PaymentType"))

//mlContext.Transforms.Concatenate 変換クラスを使用して、すべての特徴列を Features 列に結合します。
//既定では、学習アルゴリズムは Features 列の特徴のみを処理します。
.Append(mlContext.Transforms.Concatenate("Features", "VendorIdEncoded", "RateCodeEncoded", "PassengerCount", "TripDistance", "PaymentTypeEncoded"))

//学習アルゴリズムでFastTreeを選択する
//この問題の中心となるのは、ニューヨーク市のタクシー運賃の予測です。
//一見すると、単に乗車距離に依存すると思われるかもしれません。
//しかし、ニューヨークのタクシー会社は追加の乗客数や現金でなくクレジット カードによる支払いなど、その他の要因によって請求額を変えます。
//データ セット内の他の要因に基づき、実際の値である価格値を予測します。
//それを行うには、回帰機械学習タスクを選択します。
//データ変換定義に FastTreeRegressionTrainer 機械学習タスクを追加します。
//ここまで1行で処理が行われます。
.Append(mlContext.Regression.Trainers.FastTree());

//モデルをトレーニングする
//Fit() メソッドでは、データセットを変換して、トレーニングを適用することにより、モデルがトレーニングされます。
var model = pipeline.Fit(dataView);

//トレーニング済みモデルを返します。
return model;

}

//モデルを評価する
//品質保証と検証のためのテスト データを使って、モデルのパフォーマンスを評価します。
//Evaluate メソッドは次の処理を実行します。
//・テスト データ セットを読み込む。
//・回帰エバリュエーターを作成する。
//・モデルを評価し、メトリック(結果)を作成する。
//・メトリック(結果)を表示する。

private static void Evaluate(MLContext mlContext, ITransformer model, string dataPath)
{
//LoadFromTextFile() メソッドを使って、テスト データセットを読み込みます。
//このデータセットを品質チェックとして使って、モデルを評価します。
IDataView dataView = mlContext.Data.LoadFromTextFile<TaxiTrip>(dataPath, hasHeader: true, separatorChar: ',');

//Test データを変換します。
//Transform() メソッドによって、テスト データセットの入力行に対する予測が行われます。
//RegressionContext.Evaluate メソッドは、指定されたデータセットを使用して PredictionModel の品質メトリックを計算します。
//これによって返される RegressionMetrics オブジェクトには、回帰エバリュエーターによって計算されるメトリック全体が含まれます。
var predictions = model.Transform(dataView);


//これらを表示してモデルの品質を判定するには、最初にメトリックを取得する必要があります。
var metrics = mlContext.Regression.Evaluate(predictions, "Label", "Score");


//予測セットができたら、Evaluate() メソッドがモデルを評価します。
//これは予測された値をテスト データセット内の実際の Labels と比較して、モデルのパフォーマンスのメトリックを返します。
Console.WriteLine();
Console.WriteLine($"*************************************************");
Console.WriteLine($"* Model quality metrics evaluation ");
Console.WriteLine($"*------------------------------------------------");

//RSquared は回帰モデルのもう 1 つの評価メトリックです。
//RSquared は 0 から 1 までの値を取ります。
//値が 1 に近づくほど、優れたモデルになります。
//次のコードを Evaluate メソッドに追加して、RSquared 値を表示します。
Console.WriteLine($"* RSquared Score: {metrics.RSquared:0.##}");

//RMS は回帰モデルの評価メトリックの 1 つです。
//RMS が低いほど、優れたモデルになります。
//Evaluate メソッドに次のコードを追加することで、RMS 値を表示します。
Console.WriteLine($"* Root Mean Squared Error: {metrics.RootMeanSquaredError:#.##}");

}


//モデルを使用して予測を行う
//TestSinglePrediction メソッドは次の処理を実行します。
//・テスト データの 1 つのコメントを作成します。
//・テスト データに基づいて運賃合計を予測します。
//・テスト データと予測をレポート用に結合する。
//・予測された結果を表示する。
private static void TestSinglePrediction(MLContext mlContext, ITransformer model)
{
//PredictionEngine を使って運賃を予測します。
//PredictionEngine は、データの 1 つのインスタンスに対して予測を実行できる便利な API です。
//PredictionEngine はスレッド セーフではありません。
//シングル スレッド環境またはプロトタイプ環境で使用できます。

//運用環境でパフォーマンスとスレッド セーフを向上させるには、PredictionEnginePool サービスを使用します。
//これにより、アプリケーション全体で使用するできる PredictionEngine オブジェクトの ObjectPool が作成されます。
//ASP.NET Core Web API で PredictionEnginePool を使用する方法については、こちらのガイドを参照してください。
//https://docs.microsoft.com/ja-jp/dotnet/machine-learning/how-to-guides/serve-model-web-api-ml-net
//PredictionEnginePool サービスの拡張機能は、現在プレビュー段階です。

//学習用クラスと予測用クラスを指定し、予測用計算式を受け渡します
var predictionFunction = mlContext.Model.CreatePredictionEngine<TaxiTrip, TaxiTripFarePrediction>(model);

//予測値を計算させるための課題を作成します。
//TaxiTrip のインスタンスを作成して、TestSinglePrediction() メソッドでコストのトレーニング済みモデルの予測をテストするために、
//旅行データを作成します。
var taxiTripSample = new TaxiTrip()
{
VendorId = "VTS",
RateCode = "1",
PassengerCount = 1,
TripTime = 1140,
TripDistance = 3.75f,
PaymentType = "CRD",
FareAmount = 0 // 予測値を入れるため0指定. 実際/観察 = 15.5
};

//タクシー乗車データを1個与えて、運賃を予測させます。
//Predict() 関数では、データ1個で1つの予測を行います。
var prediction = predictionFunction.Predict(taxiTripSample);

//指定された旅行の予測運賃を表示します。
Console.WriteLine($"**********************************************************************");
Console.WriteLine($"Predicted fare: {prediction.FareAmount:0.####}, actual fare: 15.5");
Console.WriteLine($"**********************************************************************");
}

}

}


この記事が気に入ったらサポートをしてみませんか?