测试运行

径向基函数网络培训

James McCaffrey

下载代码示例

径向基函数 (RBF) 网络是一个软件系统,可以对数据进行分类和做出的预测。RBF 网络有一些表面的相似性到神经网络,但其实很不同。RBF 网络接受一个或多个数字输入,并生成一个或多个数字输出。由输入的值,再加上一组参数称为 RBF 邮编、 称为 RBF 宽度一套、 称为 RBF 砝码一套称为 RBF 偏见一套确定的输出值。

为简单起见的术语,RBF 权重和偏置值的组合,有时统称为权重。在其中一词权重通常使用的上下文使意思更清楚。更多的信息,请参阅我的文章,"径向基础功能网络为程序员"在 10 月的 MSDN 杂志问题 (msdn.microsoft.com/magazine/dn451445)。

当使用 RBF 网络进行分类和预测,面临的挑战是为邮编、 宽度、 重量和偏见找到一组值,以便计算产出最佳匹配一组已知的产出。这被称为训练 RBF 网络。虽然有研究者 RBF 网络在 1988 年推出以来,并不多解释了如何实现 RBF 网络培训的实际指导。这篇文章将介绍和描述完整演示 RBF 网络。

看一看在演示程序图 1。该程序创建 RBF 模型所预测的鸢尾花 ("setosa","杂色"或"葵") 从花的萼片长度和宽度,和花瓣长度和宽度的四个数值的物种。该演示程序的数据源包括知名 150 项基准集称为费雪的虹膜数据的子集的 30 项。预处理过的 30 数据项目。所以,值小于零均值较短比平均长度或宽度和大于零的值意味着长比平均长度或宽度正常了四个数值 x 值。Y 值为物种已编码 (0,0,1),作为 (0,1,0) 或 (1,0,0) 为 setosa,云芝和葵,分别。


图 1 径向基函数网络的演示程序

该演示将 30 项数据集拆分成 24 项集,用于培训。也是为测试/评估生成的 RBF 模型设置定型六个项目。它实例化 RBF 网络有四个输入节点 (一个用于输入的数据的每个值),五个隐藏的处理节点和三个输出节点 (一个用于每个输出数据值)。确定隐藏节点的最佳数目是主要的试验和错误的问题。五在演示中的选择是任意的。

图 1 你可以看到训练 RBF 网络由三个阶段组成。第一阶段确定的邮编。可以邮编视为代表 x 值从训练数据中选择。RBF 网络需要一个质心隐藏的每个节点,因此该演示需要五个邮编。训练算法选择 x 值从训练数据项目 [9],[19],[21] [20] 和 [4]。换句话说,第一次的质心是 (-0.362、-2.019、 0.074、 0.112)。

第二阶段培训确定宽度。你可以认为的宽度作为描述邮编之间的距离值。RBF 网络需要一个宽度为隐藏的每个节点。演示计算单一常见宽度为所有的五个隐藏节点的值 3.3318,而不是计算五起不同的宽度。

培训的第三阶段确定的 RBF 权重和偏置值。你可以认为的权重和偏置值作为数字常量。如果 RBF 网络有 NI 的输入节点数、 NH 的隐藏节点数和没有数字输出节点,则该网络需要 (NH * 无) 重量值和无偏倚的值。所以,因为演示 RBF 网络有 4-5-3 的体系结构,它需要 5 * 3 = 15 重量再加上三个偏见,总共 18 权重和偏置值。该演示程序使用粒子群优化算法 (PSO) 来确定 18 重量和偏见。

你训练使用 24 项训练数据集的演示 RBF 网络后,你将六项测试数据集送入网络。在此示例中,RBF 网络正确预测的五个六个测试项目,0.8333 分类准确性的物种。

本文假定您拥有先进的与 C# 的编程技巧和径向基函数网络输入过程输出机制基本熟悉。我在我 10 月的专栏中讨论了这一机制。太长,其整体在这篇文章,介绍该演示程序的源代码是完整代码下载就是可在 archive.msdn.microsoft.com/mag201312TestRun

程序的整体结构

若要创建该演示程序,我发起了 Visual Studio 2012 和创建 C# 控制台应用程序命名为 RadialNetworkTrain。演示有没有重大的.NET 依赖关系,因此,任何版本的 Visual Studio 应工作。加载的模板代码后,则在解决方案资源管理器中,窗口改名 Program.cs 文件的更具描述性的 RadialTrainProgram.cs 和 Visual Studio 将自动重命名关联的类的程序。在源代码的顶部,删除所有不必要的引用的.NET 命名空间,离开只是对 System 命名空间的引用。

整体的程序结构,与删除一些 WriteLine 语句和一些少量的编辑,提交在图 2。除了程序类,房子的主要方法、 演示了一个 RadialNetwork 类,封装 RBF 网络建立和培训、 颗粒类的定义与 RBF 训练算法,用于确定权重和偏置值,使用粒子对象和一个佣工类,包含实用程序显示的方法。

图 2 总体 RBF 网络演示的程序结构

 

using System;
namespace RadialNetworkTrain
{
  class RadialTrainProgram
  {
    static void Main(string[] args)
    {
      Console.WriteLine("Begin radial basis function (RBF) network training demo");
      double[][] allData = new double[30][];
      allData[0] = new double[] { -0.784, 1.255, -1.332, -1.306, 0, 0, 1 };
      allData[1] = new double[] { -0.995, -0.109, -1.332, -1.306, 0, 0, 1 };
      // Etc.
      allData[28] = new double[] { 0.904, -1.473, 1.047, 0.756, 1, 0, 0 };
      allData[29] = new double[] { 1.431, 1.528, 1.209, 1.659, 1, 0, 0 };
      Console.WriteLine("First four and last line of normalized, encoded input data:");
      Helpers.ShowMatrix(allData, 4, 3, true, true);
      double[][] trainData = null;
      double[][] testData = null;
      int seed = 8; // Gives a good demo
      GetTrainTest(allData, seed, out trainData, out testData);
      Helpers.ShowMatrix(trainData, trainData.Length, 3, true, false);
      Helpers.ShowMatrix(testData, testData.Length, 3, true, false);
      int numInput = 4;
      int numHidden = 5;
      int numOutput = 3;
      RadialNetwork rn = new RadialNetwork(numInput, numHidden, numOutput);
      Console.WriteLine("Beginning RBF training");
      int maxIterations = 100; // Max for PSO
      double[] bestWeights = rn.Train(trainData, maxIterations);
      Console.WriteLine("Evaluating RBF accuracy on the test data");
      rn.SetWeights(bestWeights);
      double acc = rn.Accuracy(testData);
      Console.WriteLine("Classification accuracy = " + acc.ToString("F4"));
      Console.WriteLine("End RBF network training demo");
    }
    static void GetTrainTest(double[][] allData, int seed,
      out double[][] trainData, out double[][] testData) { . . }
  }
  public class RadialNetwork
  {
    private static Random rnd = null;
    private int numInput;
    private int numHidden;
    private int numOutput;
    private double[] inputs;
    private double[][] centroids;
    private double[] widths;
    private double[][] hoWeights;
    private double[] oBiases;
    private double[] outputs;
    public RadialNetwork(int numInput, int numHidden, int numOutput) { . . }
    private static double[][] MakeMatrix(int rows, int cols) { . . }
    public void SetWeights(double[] weights) { . . }
    public double[] GetWeights() { . . }
    private double MeanSquaredError(double[][] trainData,
      double[] weights) { . . }
    public double Accuracy(double[][] testData) { . . }
    private static int MaxIndex(double[] vector) { . . }
    public double[] ComputeOutputs(double[] xValues) { . . }
    private static double[] Softmax(double[] rawOutputs) { . . }
    public double[] Train(double[][] trainData, int maxIterations) { . . }
    private void DoCentroids(double[][] trainData) { . . }
    private static double AvgAbsDist(double[] v1, double[] v2,
      int numTerms) { . . }
    private int[] DistinctIndices(int n, int range) { . . }
    private void DoWidths(double[][] centroids) { . . }
    private double[] DoWeights(double[][] trainData, int maxIterations) { . . }
    private static double EuclideanDist(double[] v1, double[] v2,
      int numTerms) { . . }
    private static void Shuffle(int[] sequence) { . . }
  }
  public class Particle
  {
    // Implementation here
  }
  public class Helpers
  {
    // Implementation here
  }
}

类 RadialNetwork 不是很复杂的程序结构建议,因为大多数的类方法是佣工。 方法列车执行调用的佣工,DoCentroids,DoWidths 和 DoWeights 的三个阶段的培训过程。 AvgAbsDist 和 DistinctIndices 的私有方法是佣工为 DoCentroids。 方法 DoWeights 在每次迭代粒子群优化算法通过按不同的顺序使用洗牌过程培训数据项目的私有方法。

演示的心脏是相当简单的。 第一,归一化和编码数据设置:

double[][] allData = new double[30][];
allData[0] = new double[] { -0.784, 1.255, -1.332, -1.306, 0, 0, 1 };
allData[1] = new double[] { -0.995, -0.109, -1.332, -1.306, 0, 0, 1 };
// Etc.
allData[28] = new double[] { 0.904, -1.473, 1.047, 0.756, 1, 0, 0 };
allData[29] = new double[] { 1.431, 1.528, 1.209, 1.659, 1, 0, 0 };

这里的数据是硬编码为简单起见。 在大多数情况下您的数据将存储在一个文本文件或 SQL 表。 下一步,数据被分割成培训和测试子集:

double[][] trainData = null;
double[][] testData = null;
int seed = 8;
GetTrainTest(allData, seed, out trainData, out testData);

Rbf 神经网络进行实例化:

int numInput = 4;
int numHidden = 5;
int numOutput = 3;
RadialNetwork rn = new RadialNetwork(numInput,
   numHidden, numOutput);

如在上一节中所述,必须由试验和错误基本上确定最佳的隐藏的处理节点数。 培训网络:

int maxIterations = 100;
double[] bestWeights = rn.Train(trainData, maxIterations);

并且,最后,计算生成的模型:

rn.SetWeights(bestWeights);
double acc = rn.Accuracy(testData);
Console.WriteLine("Classification accuracy = " +
  acc.ToString("F4"));

BestWeights 数组保存的 RBF 权重和偏置值所确定的培训方法。 方法 SetWeights 加载这些权重和偏置值。 你不需要有邮编和显式加载,因为由火车方法设置这些值的宽度。

径向基函数网络输入-过程-输出

若要了解 RBF 网络培训过程,您需要了解 RBF 网络输入-­进程输出机制。 中的关系图图 3 显示如何演示 RBF 网络计算的输出测试的数据项目 [1] = (0.498,0.709 提高,0.452 0.482) 后经过了网络。 输入的 x 值传递到隐藏的每个节点。 每个隐藏的节点计算其本地输出使用其自己的质心和宽度。

Radial Basis Function Network Architecture
图 3 径向基函数网络体系结构

例如,在最顶端隐藏的节点的质心是 (-0.362、-2.019、 0.074、 0.112) 和它的宽度是 3.3318。 隐藏的每个节点的本地输出然后用于通过加权的求和的投入,再加上偏置值计算确定初步的输出值。 例如,如果 hOutput [0] 表示隐藏节点 0 的本地输出,然后在最顶层输出节点的初步输出是 (hOutput [0] * w[0][0]) + (hOutput [1] * w[1][0]) + (hOutput [2] * w[2][0]) + (hOutput [3] * w[3][0]) + (hOutput [4] * w[4][0]) + 偏见 [0] =-12.7999。

三个初步输出值的计算后,他们会被转换成使用 softmax 函数的最后输出值。 这个想法是要修改初步输出值,所以,最终的输出值将所有介于 0.0 和 1.0,并总和为 1.0。 这样,可以宽松地解释为概率的输出值。

图 3,最后产出是 0.0237 0.6865 0.2897)。 因为中间节点具有最高的价值,它被解释为 1,其他两个值被解释为 0,给推断出的输出的 (0,1,0)。 记得的测试数据是 (0.482 0.709 提高、 0.452、 0.498、 0.000 1.000,0.000),那里的第一次四个值是投入和最后三个值有目标值,所以,RBF 网络使得正确预测的物种 (Iris 云芝在这种情况下)。 现在的问题是:RBF 网络邮编、 宽度、 重量和偏见的值是从哪里来的?

确定 RBF 网络邮编

RadialNetwork 类的火车方法是基本上做所有的实际工作的三个帮助器方法的包装:

public double[] Train(double[][] trainData, int maxIterations)
{
  DoCentroids(trainData);
  DoWidths(this.centroids);
  double[] bestWeights = DoWeights(trainData, maxIterations);
  return bestWeights;
}

方法 DoCentroids 确定代表输入的 x 值。 有很多可能性在这里。 一种常用方法是使用一个 k-均值或 k 进行划分的聚类算法,以迭代方式分配和重新分配数据项目,所以类似数据项目组合在一起。 当完成后,每个群集将有代表性的数据成员。 您可以使用这些作为 RBF 邮编。

另一种办法是从随机选定的训练数据项目中提取 x 值。 这是简单的但是有坏邮编可能被选中的机会的风险。

该演示程序使用称之为一个轻量级聚类建议由这个伪代码的方法:

initialize maxDistance
intialize bestIndices
loop
  select numHidden random indices into train data
  compute an estimated distance between selected data items
  if estimated distance > maxDistance then
    set maxDistance = curr distance
    set bestIndices = curr indices
  end if
end loop
fetch the x-values in train data at bestIndices
store x-values into RBF centroids

这个想法是最能说明的示例。 假设训练数据包括 24 项中所示的图 1。 进一步假设,第一次通过加工循环随机选定的四个指数是 [0],[1],[2] 和 [3]。 这些对应:

0: ( 1.537, -0.382,  1.317,  0.756)
  1: (-0.468,  2.346, -1.170, -1.048)
  2: ( 1.115,  0.164,  0.560,  0.370)
  3: ( 1.220,  0.436,  0.452,  0.241)

这些都是候选人邮编。 这个想法是让代表的 x 值,这意味着你不想靠近在一起的值。 所以,你计算某种程度的这些候选人邮编之间的距离。 在这里,有很多可能的办法。 演示估计通过计算相邻的两名候选人,而不是计算所有可能对之间的平均距离之间的平均距离候选人邮编对所有可能的组合之间的平均距离。 对于此示例,它计算候选人 [0] 和 [1] 之间的距离之间 [1] 和 [2],和之间 [2] 和 [3]。

计算距离的常用方法是总和的使用欧几里德距离,是总和的平方根的平方差之和的值。 (注意:演示 RBF 网络使用高斯的内核,使用欧氏距离来计算隐藏的节点本地输出值)。然而,该演示程序使用曼哈顿距离,哪里的距离之差的绝对值平均变的体。 因此,候选人 [0] 和 [1] 之间的距离是:

d = abs(1.537 - (-0.468)) + . . . + abs(0.756 - (-1.048)) / 4
  = 2.256

生成一组候选邮编和计算估计的平均距离为的候选集重复指定的次数和组具有的最大的估计平均距离的候选人被选为 RBF 质心集的过程。

请注意确定 RBF 网络邮编可被视为一种无监督的训练技术因为目标值 (例如 0、 1、 0) 在训练数据中的不需要或使用。 这意味着可以快速确定邮编。 另外,RBF 网络宽度、 权重和偏置值可以 — — 在理论上至少 — — 更快的大致等效的神经网络权值和偏差值比计算。 这给 rbf 神经网络的潜在优势 (但您会看到,还有更多的故事)。

在确定 RBF 网络邮编的过程中,确定候选人指数是有趣的子问题。 该演示程序使用聪明的算法称为水库采样。 想法是来接的第一次可能 n 指数,然后用剩余可能指数概率上替换初始指数:

private int[] DistinctIndices(int n, int range)
{
  // Reservoir sampling. assumes rnd exists
  int[] result = new int[n];
  for (int i = 0; i < n; ++i)
    result[i] = i;
  for (int t = n; t < range; ++t) {
    int m = rnd.Next(0, t + 1);
    if (m < n) result[m] = t;
  }
  return result;
}

虽然该方法是短的它是微妙。 替代办法包括使用强力攻击方式随机指数在生成,然后检查以查看是否存在任何重复项。

确定 RBF 网络宽度

RBF 网络输入过程输出机制为隐藏的每个节点需要一个宽度值。 有很多可能性确定的宽度值。 最简单的方法和使用的演示程序,是计算一个共同的宽度,所有隐藏的处理节点可以使用。 在这一领域的研究往往是朦胧和结论有时是相互矛盾。 该演示程序作为邮编对所有可能的组合之间的欧几里德距离平均计算常见的宽度。 在伪代码:

sumOfDists = 0.0
for each pair of centroids
  accumulate Euclidean distance between curr pair
end loop
return accumulated sumOfDists / number of pairs

根据我的经验,RBF 网络的成效是对用于隐藏的节点宽度的值非常敏感。 研究表明一个太小的宽度往往以过度适合培训数据,导致贫困分类精度。 太大,宽度往往不足合适的数据,这也导致贫穷的分类。 如果您通过手动设置的 RBF 网络宽度值试验演示代码,你可以看到这种效果在行动中。

使用邮编之间的平均距离,davg 常见的隐藏的节点宽度值,研究表明使用 (2 * davg),或 (davg / sqrt(2 * numHidden)),和许多其他值。 而不是使用一个共同的宽度,有很多可能性计算隐藏的每个节点的不同宽度值。 在我看来,高灵敏度的宽度值,相关缺乏令人信服的关于如何向最佳计算宽度值,主要的缺点是基于 RBF 网络的研究成果和 RBF 网络相比,神经网络和支持向量机等替代。

确定 RBF 网络权重和偏见

在确定邮编和宽度之后, 训练 RBF 网络的最后一步确定重量和偏见的值。 从理论上讲,RBF 网络权值,您可以轻松地计算和快速因为笼统地说,有 n 个未知值 n 方程。 因此,标准数值技术可,在理论上,用于为权重值的解决。

不幸的是,在实践中,使用标准技术运行到许多实际问题。 例如,许多标准技术研究方程组求解涉及矩阵求逆的使用。 矩阵求逆可以失败原因有许多。

而不是使用确定性但可能脆性的数值技术来为 RBF 网络权重完全解决,该演示程序使用粒子群优化算法估计的最佳值。 PSO 是鱼的元启发式基于协调的组的行为,如成群的鸟或学校。 在 PSO,粒子有一个位置,它表示一个潜在的解决方案 (在这种情况下的重量值的最佳集)。 每个粒子都有一个确定粒子的下一位置的速度。

在 PSO,创建一套的粒子。 在每个模拟的时间刻度线,每个粒子移动到一个新位置基于粒子的当前的位置和速度、 微粒,最著名的历史地位和任何微粒的最著名的历史地位。 这里是 PSO 在高级别的伪代码:

set number of particles
set maxIterations
initialize all particles to random positions
loop maxIterations times
  for each particle
    update curr particle's velocity
    use new velocity to compute new position
    compute error for new position
    check if new particle best position
    check if new best position for all particles
  end for
end loop
return best position found by any particle

PSO 是一个迷人的话题在它自己的权利。 你可以了解更多关于它通过阅读我 2011 年 8 月的文章,"粒子群优化算法"(msdn.microsoft.com/magazine/hh335067)。 PSO 要求的几个自由参数规格­eters,包括控制粒子的当前位置的相对影响的重量常量的最佳的历史地位和最佳全球的历史地位。 PSO 还需要指定粒子的数量、 迭代和 (可选),一种用于早期算法退出错误阈值的最大数目。 您可以尝试使用演示代码这些因素。

除了 PSO 和传统数值技术,有很多替代品用于查找 RBF 网络权重,包括简单的梯度下降和实数型遗传算法。 虽然已经研究理论的 RBF 网络相当拓­实现渐进,不同的训练技术的对比效果上有相对较少的令人信服的研究结果。

总结

这里提出的解释和演示的程序代码应该调查 RBF 网络给你坚实的基础。 虽然 RBF 网络是知名的在研究界,他们好像在相比,神经网络分类器、 朴素贝叶斯分类器和逻辑回归等替代的软件开发人员社区中经常使用。 一个可能的原因可能是稀缺的实际执行情况的例子。 另一个可能原因是基本 RBF 网络因素,尤其是那些相关的 RBF 网络宽度计算的不确定性。 在我看来,是没有扎实的研究证据来回答 RBF 网络是否更有效、 不太有效,或大约相等于替代机器学习技术的问题。

**博士。**詹姆斯 · 麦卡弗里为微软在华盛顿州雷德蒙德的研究工作 他曾在几个 Microsoft 产品,包括互联网资源管理器和 Bing。可通过 jammc@microsoft.com 与他联系。

衷心感谢以下技术专家对本文的审阅:柯克 Olynyk (Microsoft 研究)