前言
本文基于CNTK实现逻辑回归二分类,并以之前的不同,本次使用C#实现,不适用python,python版的CNTK比较简单,而且python版的cntk个人感觉没什么必要,毕竟是微软的框架因此本人强迫症犯了,所以使用C#实现CNTK
环境版本:
Visualstudio 2022
C# .net4.6
cntk 2.7
cuda 10.1
一、VS2022+CNTK环境搭建
首先把CNTK的需要的dll解压出来,可直接从此处免费下载:
CNTK安装
然后再项目上引入dll,如图:
二、逻辑回归代码构建
1.逻辑回归构建
从网上对逻辑回归的图的解释,可以使用CNTK很容易就可以构建逻辑回归的结构
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;
}
这里的可以简单的理解为y=kx+b的结构。
2.训练数据的生成
这里主要是每次训练的时候会生成一批数据,首先对输入批次/输出批次构建数组,然后使用随机数种子,生成一批数据,服从高斯分布随机数,注意这里的mean和std
private 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 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);
}
}
}
3.模型训练
static void Main(string[] args)
{
//设置逻辑回归的输入和输出的量,三入二出,因逻辑回归基本是二分类,因此这里输出的是2
int inputDim = 3;
int numOutputClasses = 2;
//使用GPU模块
var device = DeviceDescriptor.GPUDevice(0);
//设置输入输出
Variable featureVariable = Variable.InputVariable(new int[] { inputDim }, DataType.Float);
Variable labelVariable = Variable.InputVariable(new int[] { numOutputClasses }, DataType.Float);
//网络结构的构建及损失计算,这里使用CNTK的Softmax
var classifierOutput = CreateLinearModel(featureVariable, numOutputClasses, device);
var loss = CNTKLib.CrossEntropyWithSoftmax(classifierOutput, labelVariable);
var evalError = CNTKLib.ClassificationError(classifierOutput, labelVariable);
//学习率的设置
TrainingParameterScheduleDouble learningRatePerSample = new 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 = 10000;
int updatePerMinibatches = 10;
// 循环训练
for (int minibatchCount = 0; minibatchCount < numMinibatchesToTrain; minibatchCount++)
{
Value features, labels;
GenerateValueData(minibatchSize, inputDim, numOutputClasses, out features, out labels, device);
trainer.TrainMinibatch(new Dictionary<Variable, Value>() { { featureVariable, features }, { labelVariable, labels } }, device);
PrintTrainingProgress(trainer, minibatchCount, updatePerMinibatches);
}
Console.ReadLine();
}