ML.NET 데이터 정규화
ML.NET으로 데이터 전처리 하는 방법은 다음의 글을 참고하시면 됩니다.
Prepare Data
; https://learn.microsoft.com/en-us/dotnet/machine-learning/how-to-guides/prepare-data-ml-net
현재(2019-05-28)
NormalizationCatalog에서 제공하는 정규화 방법은 대략 다음과 같습니다.
- NormalizeBinning
- NormalizeGlobalContrast
- NormalizeLogMeanVariance
- NormalizeLpNorm
- NormalizeMeanVariance
- NormalizeMinMax
- NormalizeSupervised?Binning
그런데,
"기초 수학으로 이해하는 머신러닝 알고리즘" 책에 보면 z-score 정규화가 나오는데요.
표준 점수
; https://ko.wikipedia.org/wiki/%ED%91%9C%EC%A4%80_%EC%A0%90%EC%88%98
표준값 z는 원수치인 x가 평균에서 얼마나 떨어져 있는지를 나타낸다. 음수이면 평균이하, 양수이면 평균이상이다
이것과 매핑되는 ML.NET의 정규화는 없습니다. 이런 경우, ML.NET에 자연스럽게 녹여낼 수 있도록 사용자 정의 transformer 구현을 제공합니다.
How can I define my own transformation of data?
; https://github.com/dotnet/machinelearning/blob/master/docs/code/MlNetCookBook.md#user-content-how-can-i-define-my-own-transformation-of-data
그런데, 굳이 저렇게 해서 얻는 장점이 얼마나 많을까 싶습니다. 따라서 그냥 다음과 같이 로드된 데이터를 직접 처리해도 상관없겠습니다.
double[] xData = xyList.Select(xy => xy.X).ToArray();
xData = NormalizeZscore(xData);
private static double [] NormalizeZscore(double[] xData)
{
double mean = Statistics.Mean(xData);
double sd = Statistics.PopulationStandardDeviation(xData);
double[] normalized = new double[xData.Length];
for (int i = 0; i < xData.Length; i ++)
{
normalized[i] = (xData[i] - mean) / sd;
}
return normalized;
}
예를 들어 입력 데이터가 다음과 같을 때,
x,y
235,591
216,539
148,413
35,310
85,308
204,519
49,325
25,332
173,498
191,498
134,392
99,334
117,385
112,387
162,425
272,659
159,400
159,427
59,319
198,522
NormalizeZscore가 반환한 x 데이터의 min/max는 -1.7406785589738 ~ 1.94669368859505에 해당합니다. 그 외에, ML.NET의 정규화 관련 메서드를 수행해 보면,
using MathNet.Numerics.Statistics;
using Microsoft.ML;
using Microsoft.ML.Data;
using Microsoft.ML.Transforms;
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
MLContext ctx = new MLContext();
IDataView data = ctx.Data.LoadFromTextFile<ClickData>("click.csv", separatorChar: ',', hasHeader: true);
var xColumn = data.Schema[0];
var yColumn = data.Schema[1];
{
Func<NormalizingEstimator> func = () => ctx.Transforms.NormalizeMeanVariance(xColumn.Name);
ShowResult("NormalizeMeanVariance", ctx, data, func);
}
{
Func<NormalizingEstimator> func = () => ctx.Transforms.NormalizeLogMeanVariance(xColumn.Name);
ShowResult("NormalizeLogMeanVariance", ctx, data, func);
}
{
Func<NormalizingEstimator> func = () => ctx.Transforms.NormalizeMinMax(xColumn.Name);
ShowResult("NormalizeMinMax", ctx, data, func);
}
{
var xData = data.GetColumn<double>(xColumn).NormalizeZscore();
Console.WriteLine($"[NormalizeZscore] Min: {xData.Min()}, Max: {xData.Max()}");
}
}
private static void ShowResult(string title, MLContext ctx, IDataView data, Func<NormalizingEstimator> func)
{
var transformer = func();
ITransformer textTransformer = transformer.Fit(data);
IDataView normalizedData = textTransformer.Transform(data);
var xyList = ctx.Data.CreateEnumerable<ClickData>(normalizedData, false);
var xData = xyList.Select(xy => xy.X);
Console.WriteLine($"[{title}] Min: {xData.Min()}, Max: {xData.Max()}");
}
}
public static class Extension
{
public static IEnumerable<double> NormalizeZscore(this IEnumerable<double> data)
{
double mean = Statistics.Mean(data);
double std = Statistics.PopulationStandardDeviation(data);
foreach (var item in data)
{
yield return (item - mean) / std;
}
}
}
각각의 종류에 따라 다음과 같은 결과를 갖습니다.
[NormalizeMeanVariance] Min: 0.159596722144764, Max: 1.73641233693504
[NormalizeLogMeanVariance] Min: 0.00667093285901543, Max: 0.899555532449876
[NormalizeMinMax] Min: 0.0919117647058823, Max: 1
(
첨부 파일은 이 글의 예제 코드를 포함합니다.)
참고로, Fit 호출 시 오류가 발생한다면?
Unhandled Exception: System.ArgumentOutOfRangeException: Wrong column type for column X. Expected: Single, Double, Vector of Single or Vector of Double. Got: Int32.
Parameter name: column
at Microsoft.ML.Transforms.NormalizeTransform.LogMeanVarUtils.CreateBuilder(LogMeanVarianceColumnOptions column, IHost host, Int32 srcIndex, DataViewType srcType, DataViewRowCursor cursor)
at Microsoft.ML.Transforms.NormalizingTransformer.Train(IHostEnvironment env, IDataView data, ColumnOptionsBase[] columns)
at Program.Main(String[] args) in F:\ConsoleApp1\ConsoleApp1\Program.cs:line 18
해당 transformer의 대상 타입이 int가 들어왔는데 float, double, vector of float/double/vector 유형이어야만 하기 때문입니다. 따라서 정규화 대상이 되는 칼럼의 모델 타입을,
class ClickData
{
[LoadColumn(0)]
public int X { get; set; }
[LoadColumn(1)]
public int Y { get; set; }
}
다음과 같이 적절하게 변경하면 됩니다.
class ClickData
{
[LoadColumn(0)]
public double X { get; set; }
[LoadColumn(1)]
public int Y { get; set; }
}
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]