일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Statistics
- 데이터 과학
- data science
- 인공지능
- 빅데이타
- 주일설교
- node.js
- Big Data
- nodeJS
- 김양재 목사님
- 빅데이터
- 딥러닝
- 김양재
- 통계
- 김양재 목사
- No SQL
- 확률
- Deep learning
- MongoDB
- probability
- openCV
- WebGL
- 우리들교회
- Artificial Intelligence
- 몽고디비
- 빅 데이터
- Machine Learning
- 빅 데이타
- c++
- R
- Today
- Total
Scientific Computing & Data Science
[Artificial Intelligence / CNTK] CNTK CPU Example 코드 작성하기 본문
[Artificial Intelligence / CNTK] CNTK CPU Example 코드 작성하기
cinema4dr12 2018. 5. 13. 01:30Written by Geol Choi |
이번 포스팅에서는 Microsoft Research에서 개발한 CNTK(Cognitive ToolKit)의 간단한 C# 예제를 만드는 방법에 대하여 알아보도록 하겠습니다.
참고로, 개발환경은 다음과 같습니다:
OS : Windows 10 64bit
IDE : Visual Studio 2015(Ver. 14)
.NET Framework : 4.5 or higher
Runtime Env. : CPU
설명은 자세한 튜토리얼 형식으로 진행하도록 하겠습니다.
Visual Studio 프로젝트 생성
Visual Studio 메뉴에서 File > New > Project를 클릭하여 새로운 프로젝트를 생성합니다.
Template은 Visual C# > Console Application을 선택한 후, CntkExample이라는 이름으로 적당한 경로에 프로젝트를 새로 생성합니다.
CNTK C# NuGet 패키지 설치
새로운 프로젝트를 생성하였으면, CNTK NuGet 패키지 설치를 하여야 하는데 온라인으로 설치가 가능합니다.
아래 이미지와 같이, Visual Studio Solution Explorer로부터 프로젝트 이름(CntkExample)을 오른쪽 마우스 클릭하여 Manage NuGet Packages...를 NuGet 패키지 추가 창으로 이동합니다.
아래 이미지와 같이, NuGet Package Manager 창이 열리면 Browse 탭에서 "cntk" 입력하면, CNTK 문자열이 포함된 모든 패키지 검색결과를 얻을 수 있는데, 본 튜토리얼에서는 CNTK CPU 예제 코드를 작성할 것이므로 이 중 CNTK.CPUOnly를 선택하여 설치합니다.
설치 시, CNTK.CPUOnly 패키지의 타 의존성(Dependencies)으로 인해 아래 이미지와 같이 세 개의 패키지가 우선적으로 설치될 것입니다.
[I Accept] 버튼을 클릭하여 설치를 진행합니다. 설치가 모두 완료되면 Installed 탭으로 이동하여 설치된 NuGet 패키지 목록을 확인할 수 있습니다.
Solution Platforms 설정
현재 CNTK는 64bit 플랫폼만을 지원합니다. Visual Studio에서 생성된 프로젝트는 기본적으로 "Any CPU"로 플랫폼이 설정되어 있는데, Solution Platforms 버튼을 클릭하여 드롭다운 항목에서 Configuration Manager...를 클릭합니다.
Configuration Manager 창이 열리면, Platform 항목에서 <New...>를 클릭하고,
New Project Platform 창으로부터 New platform:을 x64를 선택합니다.
Example Code 작성
다음과 같이, Program.cs 코드에서 using 지시문으로 사용할 namespace를 정의합니다:
1 2 3 4 5 6 | using CNTK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; | cs |
위의 코드까지 작성한 후, 빌드했을 때 빌드가 성공되어야 합니다.
Program.cs의 namespace를 CNTK.CntkExample으로 수정하도록 하겠습니다 (Line 8):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | using CNTK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CNTK.CntkExample { class Program { static void Main(string[] args) { } } } | cs |
이제 C# Class를 하나 추가하도록 하겠습니다. Visual Studio의 Solution Explorer에서 프로젝트 이름(CntkExample)을 오른쪽 마우스 버튼을 클릭하여 나오는 Context Menu에서 Add > New Item...을 클릭하여 새로운 아이템을 추가합니다.
Add New Item 창이 열리면, Visual C# Items에서 Class 항목을 선택 후, TestHelper.cs라는 이름으로 새로운 Class를 생성합니다.
TestHlelper.cs는 이름에서 알 수 있듯이 CNTK 코드를 테스트하는데 도움을 주는 유용한 코드이며, CNTK 공식 C# 예제 솔루션에 포함되어 있는 예제입니다.
아래와 같이, TestHelper.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 | // // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE.md file in the project root for full license information. // // TestHelper.cs -- Help functions for CNTK Library C# model training tests. // using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace CNTK.CntkExample { public enum Activation { None, ReLU, Sigmoid, Tanh } public class TestHelper { public static Function Dense(Variable input, int outputDim, DeviceDescriptor device, Activation activation = Activation.None, string outputName = "") { if (input.Shape.Rank != 1) { // int newDim = input.Shape.Dimensions.Aggregate((d1, d2) => d1 * d2); input = CNTKLib.Reshape(input, new int[] { newDim }); } Function fullyConnected = FullyConnectedLinearLayer(input, outputDim, device, outputName); switch (activation) { default: case Activation.None: return fullyConnected; case Activation.ReLU: return CNTKLib.ReLU(fullyConnected); case Activation.Sigmoid: return CNTKLib.Sigmoid(fullyConnected); case Activation.Tanh: return CNTKLib.Tanh(fullyConnected); } } public static Function FullyConnectedLinearLayer(Variable input, int outputDim, DeviceDescriptor device, string outputName = "") { System.Diagnostics.Debug.Assert(input.Shape.Rank == 1); int inputDim = input.Shape[0]; int[] s = { outputDim, inputDim }; var timesParam = new Parameter((NDShape)s, DataType.Float, CNTKLib.GlorotUniformInitializer( CNTKLib.DefaultParamInitScale, CNTKLib.SentinelValueForInferParamInitRank, CNTKLib.SentinelValueForInferParamInitRank, 1), device, "timesParam"); var timesFunction = CNTKLib.Times(timesParam, input, "times"); int[] s2 = { outputDim }; var plusParam = new Parameter(s2, 0.0f, device, "plusParam"); return CNTKLib.Plus(plusParam, timesFunction, outputName); } public static float ValidateModelWithMinibatchSource( string modelFile, MinibatchSource testMinibatchSource, int[] imageDim, int numClasses, string featureInputName, string labelInputName, string outputName, DeviceDescriptor device, int maxCount = 1000) { Function model = Function.Load(modelFile, device); var imageInput = model.Arguments[0]; var labelOutput = model.Outputs.Single(o => o.Name == outputName); var featureStreamInfo = testMinibatchSource.StreamInfo(featureInputName); var labelStreamInfo = testMinibatchSource.StreamInfo(labelInputName); int batchSize = 50; int miscountTotal = 0, totalCount = 0; while (true) { var minibatchData = testMinibatchSource.GetNextMinibatch((uint)batchSize, device); if (minibatchData == null || minibatchData.Count == 0) break; totalCount += (int)minibatchData[featureStreamInfo].numberOfSamples; // expected lables are in the minibatch data. var labelData = minibatchData[labelStreamInfo].data.GetDenseData<float>(labelOutput); var expectedLabels = labelData.Select(l => l.IndexOf(l.Max())).ToList(); var inputDataMap = new Dictionary<Variable, Value>() { { imageInput, minibatchData[featureStreamInfo].data } }; var outputDataMap = new Dictionary<Variable, Value>() { { labelOutput, null } }; model.Evaluate(inputDataMap, outputDataMap, device); var outputData = outputDataMap[labelOutput].GetDenseData<float>(labelOutput); var actualLabels = outputData.Select(l => l.IndexOf(l.Max())).ToList(); int misMatches = actualLabels.Zip(expectedLabels, (a, b) => a.Equals(b) ? 0 : 1).Sum(); miscountTotal += misMatches; Console.WriteLine($"Validating Model: Total Samples = {totalCount}, Misclassify Count = {miscountTotal}"); if (totalCount > maxCount) break; } float errorRate = 1.0F * miscountTotal / totalCount; Console.WriteLine($"Model Validation Error = {errorRate}"); return errorRate; } public static void SaveAndReloadModel(ref Function function, IList<Variable> variables, DeviceDescriptor device, uint rank = 0) { string tempModelPath = "feedForward.net" + rank; File.Delete(tempModelPath); IDictionary<string, Variable> inputVarUids = new Dictionary<string, Variable>(); IDictionary<string, Variable> outputVarNames = new Dictionary<string, Variable>(); foreach (var variable in variables) { if (variable.IsOutput) outputVarNames.Add(variable.Owner.Name, variable); else inputVarUids.Add(variable.Uid, variable); } function.Save(tempModelPath); function = Function.Load(tempModelPath, device); File.Delete(tempModelPath); var inputs = function.Inputs; foreach (var inputVarInfo in inputVarUids.ToList()) { var newInputVar = inputs.First(v => v.Uid == inputVarInfo.Key); inputVarUids[inputVarInfo.Key] = newInputVar; } var outputs = function.Outputs; foreach (var outputVarInfo in outputVarNames.ToList()) { var newOutputVar = outputs.First(v => v.Owner.Name == outputVarInfo.Key); outputVarNames[outputVarInfo.Key] = newOutputVar; } } public static bool MiniBatchDataIsSweepEnd(ICollection<MinibatchData> minibatchValues) { return minibatchValues.Any(a => a.sweepEnd); } public static void PrintTrainingProgress(Trainer trainer, int minibatchIdx, int outputFrequencyInMinibatches) { if ((minibatchIdx % outputFrequencyInMinibatches) == 0 && trainer.PreviousMinibatchSampleCount() != 0) { float trainLossValue = (float)trainer.PreviousMinibatchLossAverage(); float evaluationValue = (float)trainer.PreviousMinibatchEvaluationAverage(); Console.WriteLine($"Minibatch: {minibatchIdx} CrossEntropyLoss = {trainLossValue}, EvaluationCriterion = {evaluationValue}"); } } public static void PrintOutputDims(Function function, string functionName) { NDShape shape = function.Output.Shape; if (shape.Rank == 3) { Console.WriteLine($"{functionName} dim0: {shape[0]}, dim1: {shape[1]}, dim2: {shape[2]}"); } else { Console.WriteLine($"{functionName} dim0: {shape[0]}"); } } } } | cs |
가장 간단한 예제 중 하나인 Logistic Regression 예제 코드를 작성하겠습니다. 참고로, 본 튜토리얼은 CNTK의 개발환경을 구성하고 간단한 예제를 실행해 보는 것에 초점을 두었기 때문에 코드 자체의 설명은 생략하도록 하겠습니다.
다음과 같이 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 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 | using CNTK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CNTK.CntkExample { class Program { static int inputDim = 3; static int numOutputClasses = 2; static public void TrainAndEvaluate(DeviceDescriptor device) { // build a logistic regression model Variable featureVariable = Variable.InputVariable(new int[] { inputDim }, DataType.Float); Variable labelVariable = Variable.InputVariable(new int[] { numOutputClasses }, DataType.Float); var classifierOutput = CreateLinearModel(featureVariable, numOutputClasses, device); var loss = CNTKLib.CrossEntropyWithSoftmax(classifierOutput, labelVariable); var evalError = CNTKLib.ClassificationError(classifierOutput, labelVariable); // prepare for training CNTK.TrainingParameterScheduleDouble learningRatePerSample = new CNTK.TrainingParameterScheduleDouble(0.02, 1); IList<Learner> parameterLearners = new List<Learner>() { Learner.SGDLearner(classifierOutput.Parameters(), learningRatePerSample) }; var trainer = Trainer.CreateTrainer(classifierOutput, loss, evalError, parameterLearners); int minibatchSize = 64; int numMinibatchesToTrain = 1000; int updatePerMinibatches = 50; // train the model for (int minibatchCount = 0; minibatchCount < numMinibatchesToTrain; minibatchCount++) { Value features, labels; GenerateValueData(minibatchSize, inputDim, numOutputClasses, out features, out labels, device); //TODO: sweepEnd should be set properly instead of false. #pragma warning disable 618 trainer.TrainMinibatch( new Dictionary<Variable, Value>() { { featureVariable, features }, { labelVariable, labels } }, device); #pragma warning restore 618 TestHelper.PrintTrainingProgress(trainer, minibatchCount, updatePerMinibatches); } // test and validate the model int testSize = 100; Value testFeatureValue, expectedLabelValue; GenerateValueData(testSize, inputDim, numOutputClasses, out testFeatureValue, out expectedLabelValue, device); // GetDenseData just needs the variable's shape IList<IList<float>> expectedOneHot = expectedLabelValue.GetDenseData<float>(labelVariable); IList<int> expectedLabels = expectedOneHot.Select(l => l.IndexOf(1.0F)).ToList(); var inputDataMap = new Dictionary<Variable, Value>() { { featureVariable, testFeatureValue } }; var outputDataMap = new Dictionary<Variable, Value>() { { classifierOutput.Output, null } }; classifierOutput.Evaluate(inputDataMap, outputDataMap, device); var outputValue = outputDataMap[classifierOutput.Output]; IList<IList<float>> actualLabelSoftMax = outputValue.GetDenseData<float>(classifierOutput.Output); var actualLabels = actualLabelSoftMax.Select((IList<float> l) => l.IndexOf(l.Max())).ToList(); int misMatches = actualLabels.Zip(expectedLabels, (a, b) => a.Equals(b) ? 0 : 1).Sum(); Console.WriteLine($"Validating Model: Total Samples = {testSize}, Misclassify Count = {misMatches}"); } private static void GenerateValueData(int sampleSize, int inputDim, int numOutputClasses, out Value featureValue, out Value labelValue, DeviceDescriptor device) { float[] features; float[] oneHotLabels; GenerateRawDataSamples(sampleSize, inputDim, numOutputClasses, out features, out oneHotLabels); featureValue = Value.CreateBatch<float>(new int[] { inputDim }, features, device); labelValue = Value.CreateBatch<float>(new int[] { numOutputClasses }, oneHotLabels, device); } private static void GenerateRawDataSamples(int sampleSize, int inputDim, int numOutputClasses, out float[] features, out float[] oneHotLabels) { Random random = new Random(0); features = new float[sampleSize * inputDim]; oneHotLabels = new float[sampleSize * numOutputClasses]; for (int sample = 0; sample < sampleSize; sample++) { int label = random.Next(numOutputClasses); for (int i = 0; i < numOutputClasses; i++) { oneHotLabels[sample * numOutputClasses + i] = label == i ? 1 : 0; } for (int i = 0; i < inputDim; i++) { features[sample * inputDim + i] = (float)GenerateGaussianNoise(3, 1, random) * (label + 1); } } } /// <summary> /// https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform /// https://stackoverflow.com/questions/218060/random-gaussian-variables /// </summary> /// <returns></returns> static double GenerateGaussianNoise(double mean, double stdDev, Random random) { double u1 = 1.0 - random.NextDouble(); double u2 = 1.0 - random.NextDouble(); double stdNormalRandomValue = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); return mean + stdDev * stdNormalRandomValue; } private static Function CreateLinearModel(Variable input, int outputDim, DeviceDescriptor device) { int inputDim = input.Shape[0]; var weightParam = new Parameter(new int[] { outputDim, inputDim }, DataType.Float, 1, device, "w"); var biasParam = new Parameter(new int[] { outputDim }, DataType.Float, 0, device, "b"); return CNTKLib.Times(weightParam, input) + biasParam; } static void Main(string[] args) { var device = DeviceDescriptor.CPUDevice; Console.WriteLine($"======== running LogisticRegression.TrainAndEvaluate using {device.Type} ========"); TrainAndEvaluate(device); } } } | cs |
위의 Logistic Regression 코드는 CNTK의 공식 C# Example의 LogisticRegression.cs 소스를 참고한 것입니다.
이제 빌드 후 실행하면, 다음과 같이 Command Line 창이 열리고 Logistic Regression의 Training이 진행되는 것을 확인하실 수 있습니다.
Visual Studio Template으로부터 프로젝트 생성
CNTK CPU의 새로운 프로젝트 생성하여 매번 개발환경을 구축하는 것은 매우 번거롭습니다. 따라서, 편의를 위하여 Visual Studio Template을 배포하고자 합니다.
우선, 다음 첨부된 파일을 다운로드를 하시기 바랍니다: CntkCpuExample.zip
다운로드한 파일을 (압축 해제하지 않고) 다음 경로에 복사합니다:
C:\Users\{USERNAME}\Documents\Visual Studio 2015\Templates\ProjectTemplates\
Visual Studio를 닫은 후 재실행하여 새로운 프로젝트를 생성하면 아래 이미지와 같이 Templates > Visual C# > CntkCpuExample 템플릿이 등록되어 있음을 확인할 수 있습니다.
원하는 경로에 원하는 이름으로 프로젝트를 생성한 후, Solution Platform을 x64로 설정하여 빌드하면 자동으로 CNTK NuGet 패키지가 로딩되고 빌드가 될 것입니다.
이상으로, CNTK CPU 예제 코드를 작성하는 방법에 대하여 알아보았고, 다음 튜토리얼에서는 CNTK GPU 실행환경을 구축하는 방법에 대하여 알아보도록 하겠습니다.
'Artificial Intelligence > CNTK' 카테고리의 다른 글
[Artificial Intelligence / CNTK] CNTK GPU Example 코드 작성하기 (0) | 2018.05.13 |
---|