일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 몽고디비
- 김양재 목사
- c++
- Artificial Intelligence
- R
- 인공지능
- 빅데이타
- 딥러닝
- Big Data
- 빅 데이터
- Machine Learning
- data science
- Statistics
- Deep learning
- 확률
- 김양재 목사님
- 김양재
- node.js
- 주일설교
- 우리들교회
- 통계
- nodeJS
- MongoDB
- No SQL
- WebGL
- probability
- 빅데이터
- 데이터 과학
- 빅 데이타
- openCV
- Today
- Total
Scientific Computing & Data Science
[Artificial Intelligence / CNTK] CNTK GPU Example 코드 작성하기 본문
[Artificial Intelligence / CNTK] CNTK GPU Example 코드 작성하기
cinema4dr12 2018. 5. 13. 12:47Written by Geol Choi |
이번 포스팅에서는 지난 포스팅(CNTK CPU Example 코드 작성하기)에 이어 CNTK의 GPU 개발환경을 구성하고 예제(Logistic Regression) 코드를 작성하는 것에 대하여 설명하도록 하고자 합니다.
참고로, 개발환경은 다음과 같습니다:
OS : Windows 10 64bit
IDE : Visual Studio 2015(Ver. 14)
.NET Framework : 4.5 or higher
Runtime Env. : GPU
설명은 자세한 튜토리얼 형식으로 진행하도록 하겠습니다.
Visual Studio 프로젝트 생성
Visual Studio 메뉴에서 File > New > Project를 클릭하여 새로운 프로젝트를 생성합니다.
Template은 Visual C# > Console Application을 선택한 후, CntkGpuExample이라는 이름으로 적당한 경로에 프로젝트를 새로 생성합니다.
CNTK C# NuGet 패키지 설치
새로운 프로젝트를 생성하였으면, CNTK NuGet 패키지 설치를 하여야 하는데 온라인으로 설치가 가능합니다.
아래 이미지와 같이, Visual Studio Solution Explorer로부터 프로젝트 이름(CntkGpuExample)을 오른쪽 마우스 클릭하여 Manage NuGet Packages...를 NuGet 패키지 추가 창으로 이동합니다.
아래 이미지와 같이, NuGet Package Manager 창이 열리면 Browse 탭에서 "cntk" 입력하면, CNTK 문자열이 포함된 모든 패키지 검색결과를 얻을 수 있는데, 본 튜토리얼에서는 CNTK GPU 예제 코드를 작성할 것이므로 이 중 CNTK.GPU를 선택하여 설치합니다.
설치 시, CNTK.GPU 패키지의 타 의존성(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.CntkGpuExample으로 수정하도록 하겠습니다 (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.CntkGpuExample { class Program { static void Main(string[] args) { } } } | cs |
이제 C# Class를 하나 추가하도록 하겠습니다. Visual Studio의 Solution Explorer에서 프로젝트 이름(CntkGpuExample)을 오른쪽 마우스 버튼을 클릭하여 나오는 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 { 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.CntkGpuExample { 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.GPUDevice(0); Console.WriteLine($"======== running LogisticRegression.TrainAndEvaluate using {device.Type} ========"); TrainAndEvaluate(device); } } } | cs |
위의 Logistic Regression 코드는 CNTK의 공식 C# Example의 LogisticRegression.cs 소스를 참고한 것입니다.
이제 빌드 후 실행하면, 다음과 같이 Command Line 창이 열리고 Logistic Regression의 Training이 진행되는 것을 확인하실 수 있습니다. CPU로 연산할 때와는 달리 GPU를 이용하고 있다는 메시지가 나옵니다.
아래 이미지의 작업관리자의 성능탭에서 볼 수 있듯이, GPU(NVIDIA GeForce GTX 1050)로 연산이 수행되고 있음을 알 수 있습니다.
Visual Studio Template으로부터 프로젝트 생성
CNTK CPU의 새로운 프로젝트 생성하여 매번 개발환경을 구축하는 것은 매우 번거롭습니다. 따라서, 편의를 위하여 Visual Studio Template을 배포하고자 합니다.
우선, 다음 첨부된 파일을 다운로드를 하시기 바랍니다: CntkGpuExample.zip
다운로드한 파일을 (압축 해제하지 않고) 다음 경로에 복사합니다:
C:\Users\{USERNAME}\Documents\Visual Studio 2015\Templates\ProjectTemplates\
Visual Studio를 닫은 후 재실행하여 새로운 프로젝트를 생성하면 아래 이미지와 같이 Templates > Visual C# > CntkGpuExample 템플릿이 등록되어 있음을 확인할 수 있습니다.
원하는 경로에 원하는 이름으로 프로젝트를 생성한 후, Solution Platform을 x64로 설정하여 빌드하면 자동으로 CNTK NuGet 패키지가 로딩되고 빌드가 될 것입니다.
'Artificial Intelligence > CNTK' 카테고리의 다른 글
[Artificial Intelligence / CNTK] CNTK CPU Example 코드 작성하기 (0) | 2018.05.13 |
---|