■ 모델 빌더를 사용해 택시 요금을 예측하는 방법을 보여준다.
[TestLibrary 프로젝트]
▶ ModelInput.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
using Microsoft.ML.Data; namespace TestLibrary { /// <summary> /// 모델 입력 /// </summary> public class ModelInput { //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 공급자 ID - VendorID /// <summary> /// 공급자 ID /// </summary> [ColumnName("vendor_id"), LoadColumn(0)] public string VendorID { get; set; } #endregion #region 요금 코드 - RateCode /// <summary> /// 요금 코드 /// </summary> [ColumnName("rate_code"), LoadColumn(1)] public float RateCode { get; set; } #endregion #region 승객 수 - PassengerCount /// <summary> /// 승객 수 /// </summary> [ColumnName("passenger_count"), LoadColumn(2)] public float PassengerCount { get; set; } #endregion #region 이동 시간 (단위 : 초) - TripTimeInSeconds /// <summary> /// 이동 시간 (단위 : 초) /// </summary> [ColumnName("trip_time_in_secs"), LoadColumn(3)] public float TripTimeInSeconds { get; set; } #endregion #region 이동 거리 - TripDistance /// <summary> /// 이동 거리 /// </summary> [ColumnName("trip_distance"), LoadColumn(4)] public float TripDistance { get; set; } #endregion #region 지불 타입 - PaymentType /// <summary> /// 지불 타입 /// </summary> [ColumnName("payment_type"), LoadColumn(5)] public string PaymentType { get; set; } #endregion #region 요금 - FareAmount /// <summary> /// 요금 /// </summary> [ColumnName("fare_amount"), LoadColumn(6)] public float FareAmount { get; set; } #endregion } } |
▶ ModelOutput.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
namespace TestLibrary { /// <summary> /// 모델 입력 /// </summary> public class ModelOutput { //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 점수 - Score /// <summary> /// 점수 /// </summary> public float Score { get; set; } #endregion } } |
▶ ConsumeModel.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
using Microsoft.ML; using System; namespace TestLibrary { /// <summary> /// 모델 소비 /// </summary> public class ConsumeModel { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 예측 엔진 LAZY /// </summary> private static Lazy<PredictionEngine<ModelInput, ModelOutput>> _predictionEngineLazy = new Lazy<PredictionEngine<ModelInput, ModelOutput>>(CreatePredictionEngine); #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Public #region 예측 엔진 생성하기 - CreatePredictionEngine() /// <summary> /// 예측 엔진 생성하기 /// </summary> /// <returns>예측 엔진</returns> public static PredictionEngine<ModelInput, ModelOutput> CreatePredictionEngine() { MLContext context = new MLContext(); string filePath = @"WEIGHT\MLModel.zip"; ITransformer mlModel = context.Model.Load(filePath, out var modelInputSchema); PredictionEngine<ModelInput, ModelOutput> predictionEngine = context.Model.CreatePredictionEngine<ModelInput, ModelOutput>(mlModel); return predictionEngine; } #endregion #region 예측하기 - Predict(input) /// <summary> /// 예측하기 /// </summary> /// <param name="input">모델 입력</param> /// <returns>모델 출력</returns> public static ModelOutput Predict(ModelInput input) { ModelOutput result = _predictionEngineLazy.Value.Predict(input); return result; } #endregion } } |
[TestProtect 프로젝트]
▶ ModelBuilder.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
using Microsoft.ML; using Microsoft.ML.Data; using System; using System.Collections.Generic; using System.IO; using System.Linq; using TestLibrary; namespace TestProject { /// <summary> /// 모델 빌더 /// </summary> public static class ModelBuilder { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 훈련 데이터 파일 경로 /// </summary> private static string TRAINING_DATA_FILE_PATH = @"DATA\taxi-fare-train.csv"; /// <summary> /// 가중치 모델 파일 경로 /// </summary> private static string WEIGHT_MODEL_FILE_PATH = @"WEIGHT\MLModel.zip"; /// <summary> /// ML 컨텍스트 /// </summary> private static MLContext _mlContext = new MLContext(seed : 1); #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Public #region 모델 생성하기 - CreateModel() /// <summary> /// 모델 생성하기 /// </summary> public static void CreateModel() { IDataView trainingDataView = _mlContext.Data.LoadFromTextFile<ModelInput> ( path : TRAINING_DATA_FILE_PATH, hasHeader : true, separatorChar : ',', allowQuoting : true, allowSparse : false ); IEstimator<ITransformer> trainingPipeline = BuildTrainingPipeline(_mlContext); ITransformer mlModel = TrainModel(_mlContext, trainingDataView, trainingPipeline); Evaluate(_mlContext, trainingDataView, trainingPipeline); SaveModel(_mlContext, mlModel, WEIGHT_MODEL_FILE_PATH, trainingDataView.Schema); } #endregion #region 훈련 파이프라인 만들기 - BuildTrainingPipeline(mlContext) /// <summary> /// 훈련 파이프라인 만들기 /// </summary> /// <param name="mlContext">ML 컨텍스트</param> /// <returns>훈련 파이프라인</returns> public static IEstimator<ITransformer> BuildTrainingPipeline(MLContext mlContext) { var dataProcessPipeline = mlContext.Transforms.Categorical.OneHotEncoding ( new[] { new InputOutputColumnPair("vendor_id" , "vendor_id" ), new InputOutputColumnPair("payment_type", "payment_type") } ) .Append ( mlContext.Transforms.Concatenate ( "Features", new[] { "vendor_id", "payment_type", "rate_code", "passenger_count", "trip_distance" } ) ); var trainer = mlContext.Regression.Trainers.LightGbm(labelColumnName : "fare_amount", featureColumnName : "Features"); var trainingPipeline = dataProcessPipeline.Append(trainer); return trainingPipeline; } #endregion #region 모델 훈련하기 - TrainModel(mlContext, trainingDataView, trainingPipeline) /// <summary> /// 모델 훈련하기 /// </summary> /// <param name="mlContext">ML 컨텍스트</param> /// <param name="trainingDataView">훈련 데이터 뷰</param> /// <param name="trainingPipeline">훈련 파이프라인</param> /// <returns>모델</returns> public static ITransformer TrainModel(MLContext mlContext, IDataView trainingDataView, IEstimator<ITransformer> trainingPipeline) { Console.WriteLine("모델 훈련을 시작합니다."); ITransformer model = trainingPipeline.Fit(trainingDataView); Console.WriteLine("훈련 프로세스를 종료합니다."); return model; } #endregion #region 회귀 메트릭 출력하기 - PrintRegressionMetrics(metrics) /// <summary> /// 회귀 메트릭 출력하기 /// </summary> /// <param name="metrics">메트릭</param> public static void PrintRegressionMetrics(RegressionMetrics metrics) { Console.WriteLine($"회귀 모델 메트릭"); Console.WriteLine($" LossFn : {metrics.LossFunction :0.##}"); Console.WriteLine($" R2 Score : {metrics.RSquared :0.##}"); Console.WriteLine($" Absolute loss : {metrics.MeanAbsoluteError :#.##}"); Console.WriteLine($" Squared loss : {metrics.MeanSquaredError :#.##}"); Console.WriteLine($" RMS loss : {metrics.RootMeanSquaredError:#.##}"); } #endregion #region 회귀 폴드 평균 메트릭 출력하기 - PrintRegressionFoldsAverageMetrics(crossValidationResults) /// <summary> /// 회귀 폴드 평균 메트릭 출력하기 /// </summary> /// <param name="crossValidationResults">교차 검증 결과</param> public static void PrintRegressionFoldsAverageMetrics(IEnumerable<TrainCatalogBase.CrossValidationResult<RegressionMetrics>> crossValidationResults) { IEnumerable<double> l1LossEnumerable = crossValidationResults.Select(r => r.Metrics.MeanAbsoluteError ); IEnumerable<double> l2LossEnumerable = crossValidationResults.Select(r => r.Metrics.MeanSquaredError ); IEnumerable<double> rmsEnumerable = crossValidationResults.Select(r => r.Metrics.RootMeanSquaredError); IEnumerable<double> lossFunctionEnumerable = crossValidationResults.Select(r => r.Metrics.LossFunction ); IEnumerable<double> rSquaredEnumerable = crossValidationResults.Select(r => r.Metrics.RSquared ); Console.WriteLine($"회귀 모델 메트릭"); Console.WriteLine($" 평균 L1 Loss : {l1LossEnumerable.Average() :0.###}"); Console.WriteLine($" 평균 L2 Loss : {l2LossEnumerable.Average() :0.###}"); Console.WriteLine($" 평균 RMS : {rmsEnumerable.Average() :0.###}"); Console.WriteLine($" 평균 Loss Function : {lossFunctionEnumerable.Average():0.###}"); Console.WriteLine($" 평균 R-squared : {rSquaredEnumerable.Average() :0.###}"); } #endregion #region 절대 경로 구하기 - GetAbsolutePath(relativePath) /// <summary> /// 절대 경로 구하기 /// </summary> /// <param name="relativePath">상대 경로</param> /// <returns>절대 경로</returns> public static string GetAbsolutePath(string relativePath) { FileInfo fileInfo = new FileInfo(typeof(Program).Assembly.Location); string assemblyFolderPath = fileInfo.Directory.FullName; string fullPath = Path.Combine(assemblyFolderPath, relativePath); return fullPath; } #endregion //////////////////////////////////////////////////////////////////////////////// Private #region 평가하기 - Evaluate(mlContext, trainingDataView, trainingPipeline) /// <summary> /// 평가하기 /// </summary> /// <param name="mlContext">ML 컨텍스트</param> /// <param name="trainingDataView">훈련 데이터 뷰</param> /// <param name="trainingPipeline">훈련 파이프라인</param> private static void Evaluate(MLContext mlContext, IDataView trainingDataView, IEstimator<ITransformer> trainingPipeline) { Console.WriteLine("모델 정확도 메트릭을 구하기 위한 교차 검증"); var crossValidationResults = mlContext.Regression.CrossValidate ( trainingDataView, trainingPipeline, numberOfFolds : 5, labelColumnName : "fare_amount" ); PrintRegressionFoldsAverageMetrics(crossValidationResults); } #endregion #region 모델 저장하기 - SaveModel(mlContext, mlModel, modelRelativePath, modelInputSchema) /// <summary> /// 모델 저장하기 /// </summary> /// <param name="mlContext">ML 컨텍스트</param> /// <param name="mlModel">ML 모델</param> /// <param name="modelRelativePath">모델 상대 경로</param> /// <param name="modelInputSchema">모델 입력 스키마</param> private static void SaveModel(MLContext mlContext, ITransformer mlModel, string modelRelativePath, DataViewSchema modelInputSchema) { Console.WriteLine("모델 저장"); mlContext.Model.Save(mlModel, modelInputSchema, GetAbsolutePath(modelRelativePath)); Console.WriteLine($"모델을 저장했습니다 : {GetAbsolutePath(modelRelativePath)}"); } #endregion } } |
▶ Program.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
using System; using TestLibrary; namespace TestProject { /// <summary> /// 프로그램 /// </summary> class Program { //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region 프로그램 시작하기 - Main() /// <summary> /// 프로그램 시작하기 /// </summary> private static void Main() { ModelInput input = new ModelInput() { VendorID = "CMT", RateCode = 1f, PassengerCount = 1f, TripDistance = 3.8f, PaymentType = "CRD", }; ModelOutput output = ConsumeModel.Predict(input); Console.WriteLine($"공급자 ID : {input.VendorID }"); Console.WriteLine($"요금 코드 : {input.RateCode }"); Console.WriteLine($"승객 수 : {input.PassengerCount}"); Console.WriteLine($"이동 거리 : {input.TripDistance }"); Console.WriteLine($"지불 타입 : {input.PaymentType }"); Console.WriteLine(); Console.WriteLine($"예측 운임 : {output.Score }"); Console.ReadKey(true); } #endregion } } |