使用 CNTK 和 C# 进行描述性统计和数据归一化





5.00/5 (5投票s)
如何计算数据集的一些基本统计操作。
引言
您可能知道,CNTK 是 Microsoft 用于深度学习的认知工具包。它是一个开源库,被各种 Microsoft 产品使用。此外,CNTK 是一个强大的库,可用于开发来自不同领域、使用不同平台和语言的自定义 ML 解决方案。CNTK 的强大之处还在于其实现方式。实际上,该库被实现为一系列计算图,这些计算图被完全阐述为在深度神经网络训练中执行的步骤序列。
每个 CNTK 计算图都由一组节点创建,其中每个节点代表数值(数学)运算。图中节点之间的边代表运算之间的数据流。这种表示允许 CNTK 在底层硬件 GPU 或 CPU 上调度计算。CNTK 可以动态分析这些图,以优化延迟并有效利用资源。其中最强大的部分是 CNTK 可以计算任何构建的操作集的导数,这可用于网络参数的有效学习过程。下图显示了 CNTK 的核心架构。
另一方面,任何操作都可以在 CPU 或 GPU 上执行,只需进行最少的代码更改。实际上,我们可以实现一种可以自动采用 GPU 计算(如果可用)的方法。CNTK 是第一个为 .NET 开发人员提供开发 GPU 感知 .NET 应用程序的 .NET 库。
这意味着使用这个强大的库,您可以使用 C# 在 .NET 中直接将复杂的数学计算开发到 GPU,这在使用标准 .NET 库时目前是不可能的。
在这篇博文中,我将展示如何计算数据集上的一些基本统计运算。
假设我们有一个包含 4 列(特征)和 20 行(样本)的数据集。此 2D 数组的 C# 实现如下面的代码片段所示
static float[][] mData = new float[][] {
new float[] { 5.1f, 3.5f, 1.4f, 0.2f},
new float[] { 4.9f, 3.0f, 1.4f, 0.2f},
new float[] { 4.7f, 3.2f, 1.3f, 0.2f},
new float[] { 4.6f, 3.1f, 1.5f, 0.2f},
new float[] { 6.9f, 3.1f, 4.9f, 1.5f},
new float[] { 5.5f, 2.3f, 4.0f, 1.3f},
new float[] { 6.5f, 2.8f, 4.6f, 1.5f},
new float[] { 5.0f, 3.4f, 1.5f, 0.2f},
new float[] { 4.4f, 2.9f, 1.4f, 0.2f},
new float[] { 4.9f, 3.1f, 1.5f, 0.1f},
new float[] { 5.4f, 3.7f, 1.5f, 0.2f},
new float[] { 4.8f, 3.4f, 1.6f, 0.2f},
new float[] { 4.8f, 3.0f, 1.4f, 0.1f},
new float[] { 4.3f, 3.0f, 1.1f, 0.1f},
new float[] { 6.5f, 3.0f, 5.8f, 2.2f},
new float[] { 7.6f, 3.0f, 6.6f, 2.1f},
new float[] { 4.9f, 2.5f, 4.5f, 1.7f},
new float[] { 7.3f, 2.9f, 6.3f, 1.8f},
new float[] { 5.7f, 3.8f, 1.7f, 0.3f},
new float[] { 5.1f, 3.8f, 1.5f, 0.3f},};
如果您想使用 CNTK 和数学计算,您需要一些微积分知识,以及向量、矩阵和张量。同样在 CNTK 中,任何操作都作为矩阵操作执行,这可以简化您的计算过程。以标准方式,您必须在计算过程中处理多维数组。据我所知,目前没有可以在 GPU 上执行数学运算的 .NET 库,这限制了 .NET 平台实现高性能应用程序。
如果我们想要计算每列的平均值和标准差,我们可以使用 CNTK 以非常简单的方式完成。一旦我们计算出这些值,我们就可以通过计算标准分数(高斯标准化)来使用它们来标准化数据集。
高斯标准化是通过以下公式计算的
,
其中 X- 是列值, – 列平均值,以及
– 列的标准差。
对于这个例子,我们将执行三个统计操作,CNTK 自动为我们提供了在 GPU 上计算这些值的能力。如果您拥有包含数百万行的数据集,并且计算可以在几毫秒内执行,这一点非常重要。
CNTK 中的任何计算过程都可以通过以下几个步骤实现
- 从外部源或内存数据读取数据
- 定义
Value
和Variable
对象 - 定义用于计算的函数
- 通过传递
Variable
和Value
对象来执行函数的评估 - 检索计算结果并显示结果
以上所有步骤都在以下实现中实现
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CNTK;
namespace DataNormalizationWithCNTK
{
class Program
{
static float[][] mData = new float[][] {
new float[] { 5.1f, 3.5f, 1.4f, 0.2f},
new float[] { 4.9f, 3.0f, 1.4f, 0.2f},
new float[] { 4.7f, 3.2f, 1.3f, 0.2f},
new float[] { 4.6f, 3.1f, 1.5f, 0.2f},
new float[] { 6.9f, 3.1f, 4.9f, 1.5f},
new float[] { 5.5f, 2.3f, 4.0f, 1.3f},
new float[] { 6.5f, 2.8f, 4.6f, 1.5f},
new float[] { 5.0f, 3.4f, 1.5f, 0.2f},
new float[] { 4.4f, 2.9f, 1.4f, 0.2f},
new float[] { 4.9f, 3.1f, 1.5f, 0.1f},
new float[] { 5.4f, 3.7f, 1.5f, 0.2f},
new float[] { 4.8f, 3.4f, 1.6f, 0.2f},
new float[] { 4.8f, 3.0f, 1.4f, 0.1f},
new float[] { 4.3f, 3.0f, 1.1f, 0.1f},
new float[] { 6.5f, 3.0f, 5.8f, 2.2f},
new float[] { 7.6f, 3.0f, 6.6f, 2.1f},
new float[] { 4.9f, 2.5f, 4.5f, 1.7f},
new float[] { 7.3f, 2.9f, 6.3f, 1.8f},
new float[] { 5.7f, 3.8f, 1.7f, 0.3f},
new float[] { 5.1f, 3.8f, 1.5f, 0.3f},};
static void Main(string[] args)
{
//define device where the calculation will executes
var device = DeviceDescriptor.UseDefaultDevice();
//print data to console
Console.WriteLine($"X1,\tX2,\tX3,\tX4");
Console.WriteLine($"-----,\t-----,\t-----,\t-----");
foreach (var row in mData)
{
Console.WriteLine($"{row[0]},\t{row[1]},\t{row[2]},\t{row[3]}");
}
Console.WriteLine($"-----,\t-----,\t-----,\t-----");
//convert data into enumerable list
var data = mData.ToEnumerable<ienumerable<float>>();
//assign the values
var vData = Value.CreateBatchOfSequences<float>(new int[] {4},data, device);
//create variable to describe the data
var features = Variable.InputVariable(vData.Shape, DataType.Float);
//define mean function for the variable
var mean = CNTKLib.ReduceMean(features, new Axis(2));//Axis(2)- means calculate
//mean along the third axes which represent 4 features
//map variables and data
var inputDataMap = new Dictionary<variable, value="">() { { features, vData } };
var meanDataMap = new Dictionary<variable, value="">() { { mean, null } };
//mean calculation
mean.Evaluate(inputDataMap,meanDataMap,device);
//get result
var meanValues = meanDataMap[mean].GetDenseData<float>(mean);
Console.WriteLine($"");
Console.WriteLine($"Average values for each features
x1={meanValues[0][0]},x2={meanValues[0][1]},x3={meanValues[0][2]},
x4={meanValues[0][3]}");
//Calculation of standard deviation
var std = calculateStd(features);
var stdDataMap = new Dictionary<variable, value="">() { { std, null } };
//mean calculation
std.Evaluate(inputDataMap, stdDataMap, device);
//get result
var stdValues = stdDataMap[std].GetDenseData<float>(std);
Console.WriteLine($"");
Console.WriteLine($"STD of features x1={stdValues[0][0]},
x2={stdValues[0][1]},x3={stdValues[0][2]},x4={stdValues[0][3]}");
//Once we have mean and std, we can calculate Standardized values for the data
var gaussNormalization =
CNTKLib.ElementDivide(CNTKLib.Minus(features, mean), std);
var gaussDataMap = new Dictionary<variable, value="">()
{ { gaussNormalization, null } };
//mean calculation
gaussNormalization.Evaluate(inputDataMap, gaussDataMap, device);
//get result
var normValues =
gaussDataMap[gaussNormalization].GetDenseData<float>(gaussNormalization);
//print data to console
Console.WriteLine($"-------------------------------------------");
Console.WriteLine($"Normalized values for the above data set");
Console.WriteLine($"");
Console.WriteLine($"X1,\tX2,\tX3,\tX4");
Console.WriteLine($"-----,\t-----,\t-----,\t-----");
var row2 = normValues[0];
for (int j = 0; j < 80; j += 4)
{
Console.WriteLine($"{row2[j]},
\t{row2[j + 1]},\t{row2[j + 2]},\t{row2[j + 3]}");
}
Console.WriteLine($"-----,\t-----,\t-----,\t-----");
}
private static Function calculateStd(Variable features)
{
var mean = CNTKLib.ReduceMean(features,new Axis(2));
var remainder = CNTKLib.Minus(features, mean);
var squared = CNTKLib.Square(remainder);
//the last dimension indicate the number of samples
var n = new Constant(new NDShape(0),
DataType.Float, features.Shape.Dimensions.Last()-1);
var elm = CNTKLib.ElementDivide(squared, n);
var sum = CNTKLib.ReduceSum(elm, new Axis(2));
var stdVal = CNTKLib.Sqrt(sum);
return stdVal;
}
}
public static class ArrayExtensions
{
public static IEnumerable<t> ToEnumerable<t>(this Array target)
{
foreach (var item in target)
yield return (T)item;
}
}
}
上面源代码的输出应该如下所示
历史
- 2018 年 7 月 2 日:初始版本