使用 CNTK 和 C# 进行线性回归






4.82/5 (5投票s)
使用 CNTK 和 C# 进行线性回归。
CNTK是微软用于训练非常庞大且复杂的神经网络模型的深度学习工具。然而,您也可以将CNTK用于其他各种目的。在之前的一些文章中,我们已经看到了如何使用CNTK执行矩阵乘法,以计算数据集上的描述性统计参数。
在这篇博文中,我们将实现一个简单的线性回归模型,即LR。该模型仅包含一个神经元。模型还包含偏置参数,因此总的来说,线性回归只有两个参数:w
和b
。
下图显示了LR模型
我们使用CNTK来解决如此简单任务的原因非常直接。通过学习这样的简单模型,我们可以了解CNTK库的工作原理,并看到CNTK中一些并非微不足道的操作。
上述模型可以通过添加激活函数轻松扩展为逻辑回归模型。除了表示没有激活函数的神经网络配置的线性回归外,逻辑回归是包含激活函数的最简单的神经网络配置。
下图显示了逻辑回归模型
如果您想了解更多关于如何使用CNTK创建逻辑回归的信息,可以参阅此官方演示示例。
现在我们已经对神经网络模型进行了一些介绍,可以开始定义数据集了。假设我们有一个简单的数据集,它代表简单的线性函数 。生成的数据集如下表所示。
我们已经知道,所提供数据集的线性回归参数是: 和
,因此我们希望利用CNTK库来获取这些值,或者至少是非常接近它们的值。
通过使用CNTK开发LR模型的所有任务可以分为几个步骤。
步骤 1
在Visual Studio中创建一个C#控制台应用程序,将当前体系结构更改为 ,并将最新的“
CNTK.GPU
”NuGet包添加到解决方案中。下图显示了在Visual Studio中执行的这些操作。
第二步
开始编写代码,添加两个变量: – 特征,以及标签
。一旦定义了变量,就可以通过创建批次来开始定义训练数据集。下面的代码片段展示了如何创建变量和批次,以及如何开始编写基于CNTK的C#代码。
首先,我们需要添加一些using
语句,并定义计算将发生在哪种设备上。通常,如果机器包含NVIDIA兼容显卡,我们可以定义CPU或GPU。因此,演示从以下代码片段开始。
using System;
using System.Linq;
using System.Collections.Generic;
using CNTK;
namespace LR_CNTK_Demo
{
class Program
{
static void Main(string[] args)
{
//Step 1: Create some Demo helpers
Console.Title = "Linear Regression with CNTK!";
Console.WriteLine("#### Linear Regression with CNTK! ####");
Console.WriteLine("");
//define device
var device = DeviceDescriptor.UseDefaultDevice();
现在定义两个变量,以及前表中呈现的数据集。
//Step 2: define values, and variables
Variable x = Variable.InputVariable(new int[] { 1 }, DataType.Float, "input");
Variable y = Variable.InputVariable(new int[] { 1 }, DataType.Float, "output");
//Step 2: define training data set from table above
var xValues = Value.CreateBatch(new NDShape(1, 1), new float[] { 1f, 2f, 3f, 4f, 5f }, device);
var yValues = Value.CreateBatch(new NDShape(1, 1), new float[] { 3f, 5f, 7f, 9f, 11f }, device);
步骤 3
通过传递输入变量和计算设备来创建线性回归网络模型。正如我们已经讨论过的,该模型由一个神经元和一个偏置参数组成。以下方法实现了LR网络模型。
private static Function createLRModel(Variable x, DeviceDescriptor device)
{
//initializer for parameters
var initV = CNTKLib.GlorotUniformInitializer(1.0, 1, 0, 1);
//bias
var b = new Parameter(new NDShape(1,1), DataType.Float, initV, device, "b"); ;
//weights
var W = new Parameter(new NDShape(2, 1), DataType.Float, initV, device, "w");
//matrix product
var Wx = CNTKLib.Times(W, x, "wx");
//layer
var l = CNTKLib.Plus(b, Wx, "wx_b");
return l;
}
首先,我们创建一个初始化器,它将初始化网络参数的初始值。然后,我们定义偏置和权重参数,并将它们组合成线性模型“”的形式,并以
Function
类型返回。createModel
函数在main
方法中被调用。模型创建后,我们可以检查它,并证明模型中只有两个参数。以下代码创建了线性回归模型,并打印了模型参数。
//Step 3: create linear regression model
var lr = createLRModel(x, device);
//Network model contains only two parameters b and w, so we query
//the model in order to get parameter values
var paramValues = lr.Inputs.Where(z => z.IsParameter).ToList();
var totalParameters = paramValues.Sum(c => c.Shape.TotalSize);
Console.WriteLine($"LRM has {totalParameters} params,
{paramValues[0].Name} and {paramValues[1].Name}.");
在前面的代码中,我们已经看到了如何从模型中提取参数。一旦有了参数,我们就可以更改它们的值,或者仅仅打印这些值以供进一步分析。
步骤 4
创建Trainer
,它将用于训练网络参数w
和b
。以下代码片段显示了Trainer
方法的实现。
public Trainer createTrainer(Function network, Variable target)
{
//learning rate
var lrate = 0.082;
var lr = new TrainingParameterScheduleDouble(lrate);
//network parameters
var zParams = new ParameterVector(network.Parameters().ToList());
//create loss and eval
Function loss = CNTKLib.SquaredError(network, target);
Function eval = CNTKLib.SquaredError(network, target);
//learners
//
var llr = new List();
var msgd = Learner.SGDLearner(network.Parameters(), lr,l);
llr.Add(msgd);
//trainer
var trainer = Trainer.CreateTrainer(network, loss, eval, llr);
//
return trainer;
}
首先,我们定义了主神经网络参数的学习率。然后,我们创建Loss
和Evaluation
函数。有了这些参数,我们就可以创建SGD学习器。一旦实例化了SGD学习器对象,通过调用CreateTrainer static
CNTK方法并将其作为函数返回值来创建训练器。createTrainer
方法在main
方法中被调用。
//Step 4: create trainer
var trainer = createTrainer(lr, y);
步骤 5
训练过程:变量、数据集、网络模型和训练器都定义好后,就可以开始训练过程了。
//Ştep 5: training
for (int i = 1; i <= 200; i++)
{
var d = new Dictionary();
d.Add(x, xValues);
d.Add(y, yValues);
//
trainer.TrainMinibatch(d, true, device);
//
var loss = trainer.PreviousMinibatchLossAverage();
var eval = trainer.PreviousMinibatchEvaluationAverage();
//
if (i % 20 == 0)
Console.WriteLine($"It={i}, Loss={loss}, Eval={eval}");
if(i==200)
{
//print weights
var b0_name = paramValues[0].Name;
var b0 = new Value(paramValues[0].GetValue()).GetDenseData(paramValues[0]);
var b1_name = paramValues[1].Name;
var b1 = new Value(paramValues[1].GetValue()).GetDenseData(paramValues[1]);
Console.WriteLine($" ");
Console.WriteLine($"Training process finished with the following regression parameters:");
Console.WriteLine($"b={b0[0][0]}, w={b1[0][0]}");
Console.WriteLine($" ");
}
}
}
正如可以看到的,仅用200次迭代,回归参数就获得了我们几乎预期的值 和
。由于训练过程与经典回归参数确定不同,我们无法获得精确值。为了估计回归参数,神经网络使用称为随机梯度下降(SGD)的迭代方法。另一方面,经典回归使用回归分析过程,通过最小化最小二乘误差来求解以
b
和w
为未知数的方程组。
一旦实现了以上所有代码,我们就可以通过按F5键启动LR演示。应该会显示类似的输出窗口。
希望这篇博文能提供足够的信息来开始使用CNTK C#和机器学习。本文的源代码可以在此处下载。