65.9K
CodeProject 正在变化。 阅读更多。
Home

更多神经网络数学和代码(C#)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (13投票s)

2012年11月25日

CPOL

16分钟阅读

viewsIcon

35628

downloadIcon

1643

运行、初始化和训练神经网络。

目录

  • 引言
  • 神经网络变量
  • 运行神经网络
  • 初始化神经网络
    • 初始化变量
    • 网络掩码
    • 选择 A 矩阵
    • 设置方程
    • 当 c >= r 时求解 w[k]
    • 当 c < r 时求解 w[k]
  • 训练神经网络
    • 学习算法
    • 过饱和输入
    • 欠饱和输入
  • 结论
    • 争议点
    • 关注点
    • 未来方向
  • 参考文献
  • 参考文献

引言

关于神经网络已经做了大量工作,其中大部分会给你一组概率,最高概率被选为选择。然而,本文将讨论神经元只能激活或不激活的神经网络,没有中间状态。其灵感来自神经学,并有望像其他类型的神经网络一样有用。

大多数神经网络由三层组成:输入层、隐藏层和输出层,本文中的神经网络也是类似的,但是,隐藏层中的神经元可以相互连接,并且不需要连接到输入层或输出层。

由于本文的数学内容,计数将从数字一(1)开始,而代码中的计数将从零(0)开始。选择这种做法是为了清晰起见,希望它不会太令人困惑。

神经网络变量

这些是神经网络使用的变量

  • n - 神经网络有 n 个神经元。
  • i - 输入向量的长度。
  • o - 输出向量的长度。
  • W - 一个 n*n 矩阵,其中 W[k][j] 是从第 j 个神经元到第 k 个神经元的突触权重。
  • t - 一个长度为 n 的向量,其中 t[k] 是第 k 个神经元的阈值(偏置)。当 k > i 时,t[k] 必须大于零,以便神经元无法自行激活。k <= i 的 t[k] 可以设置为任何值,因为它们对应于输入神经元的阈值,并且不被使用。
  • a - 一个长度为 n 的向量,如果第 k 个神经元未激活,则 a[k] 为 0;如果已激活,则为 1。

运行神经网络

运行神经网络是将长度为 i 的二元输入向量 (I) 输入网络并输出长度为 o 的二元输出向量 (O) 的过程。这是一个三步过程

  1. for (k = 1 to i) a[k] = I[k]
  2. for (k = i+1 to n) if (DotProduct(W[k], a) >= t[k]) a[k] = 1
  3. for (k = n-o+1 to n) O[k + o - n] = a[k]

示例:XOR 问题

根据我的书(Enchanted Looms, Consciousness networks in brains and computers; By Rodney Cotterill; 1998),1957 年,Andrei Kolmogorov 提出了一个定理,该定理被用来证明神经网络可以充当 XOR 逻辑运算符,只要网络包含一个隐藏神经元。书中的 XOR 网络看起来像这样(尽管权重和偏置略有不同)


图 1. XOR 网络及其权重和阈值(偏置)。


XOR 网络的权重矩阵为

及其阈值向量为

如果我们按照运行神经网络的三步过程,让输入向量 (I) 为 [0, 1] 或 [1, 0],则

  1. a = [0, 1, 0, 0] 或 [1, 0, 0, 0]
  2. W[3] (点乘) a = ½,小于 t[3],因此 a[3] 保持为零。W[4] (点乘) a = 1,大于或等于 t[4],因此将 a[4] 设置为 1。
  3. 因此,对于两个输入,O = [1],这与 XOR 逻辑运算符一致。

如果我们让 I = [1, 1],则

  1. a = [1, 1, 0 , 0]
  2. W[3] (点乘) a = ½ + ½ = 1,大于或等于 t[3],因此将 a[3] 设置为 1,现在 a = [1, 1, 1, 0]。W[4] (点乘) a = 1 + 1 – 2 = 0,小于 t[4],因此 a[4] 保持为零。
  3. 因此,对于输入 [1, 1],O = [0],这也与 XOR 逻辑运算符一致。

初始化神经网络

初始化神经网络是为了提供一个允许网络学习的框架。初始化过程涉及使用基本数据输入和输出。例如,如果您希望它学习不同字母的样子,您会使用一些基本外观的“a”、“b”等作为基本数据输入,以及“a”、“b”等的“概念”(为方便起见),作为基本数据输出。网络初始化后,可以开始训练阶段,在此阶段,网络会使用“a”、“b”等的各种外观进行训练。

运行过程涉及将权重矩阵的行与激活向量相乘,然而,在初始化过程中,我们希望找到权重矩阵的条目以及阈值。为此,会创建一个激活矩阵 (A),其列是基本数据输入和输出的激活向量,然后可以使用此矩阵创建一组线性方程,其解是权重矩阵的条目。

初始化变量

用于初始化神经网络的变量是

  • r - 输入/输出对的数量。
  • c - 网络掩码的第 k 行中非 NULL 条目的数量。
  • A - 一个 n*r 矩阵,其中每一列代表一个激活向量 (a)。
  • T - 一个 n*r 矩阵,其中 T[k][j] = DotProduct(W[k], A[j])。如果 A[k][j] = 1,则 T[k][j] >= t[k];如果 A[k][j] = 0,则 T[k][j] < t[k]。

网络掩码

网络掩码是初始权重确定之前的权重矩阵 (W)。它用于了解哪些权重或突触允许存在。网络掩码中的条目如果允许突触存在,则为零 (0);如果不允许,则为 NULL (N)。权重矩阵的区域是

图 2. 网络掩码的区域。

示例:XOR 问题

用于初始化 XOR 问题的网络掩码将如下所示

这里 N 用于表示 NULL,即突触不允许存在。

选择 A 矩阵

A 矩阵表示通过神经网络的路径,并用作一组线性方程的矩阵,其解是权重矩阵的权重。为了获得最大数量的解,A 矩阵的行应线性无关。A 矩阵的区域是。

图 3. A 矩阵及其区域。

在我尝试弄清楚这些东西是如何运作的时候,我得出了一个结论,即 A 的行是二进制码字,并且需要在二进制中保持线性无关,或者对于数学家来说,是在 F2(q)域上。这就是 1 + 1 = 0 而不进位 1,例如 101 + 110 将是 011。但是,我对此不太确定。

由于 A 的元素只能是 0 或 1,因此可以通过使每行的 1 的数量为奇数来实现行的线性无关。这应该适用于隐藏层中的权重,但如果表示输入的行不是线性无关的,则可能会导致连接输入层到隐藏层的权重出现问题。

示例:XOR 问题

A 矩阵有多种选择。输入向量和输出向量输入后,A 矩阵如下

由于有 4 行和 3 列,因此我们无法使 A 的所有行都线性无关,但是,我们只需要前 3 行线性无关。这是由于网络掩码的选择,其中最后一列的所有条目都设置为 NULL。

对于第 3 行有 4 种选择,它们是:[1, 0, 0]、[0, 1, 0]、[0, 0, 1]、[1, 1, 1]。这些将使前 3 行线性无关。我们可以使用这些向量中的任何一个,尽管生成的网络会略有不同,每个网络都有不同的权重,但它们都是同构网络。使用未列出的选项之一作为第 3 行将导致 t[3] 或 t[4] 不大于零的解。

在此示例中,将选择向量 [0, 0, 1] 以与运行部分中给出的 XOR 示例保持一致。A 矩阵是

设置方程

我们知道一个神经元是否激活取决于网络中的其他神经元(从激活矩阵 A)。我们也知道哪些连接(突触)可以存在,哪些不能(从网络掩码)。这意味着我们知道 DotProduct(A[j], W[k]) >= t[k],如果 A[k][j] = 1;或者 < t[k],如果 A[k][j] = 0。

由于我们知道 MatrixProduct(W, A) = T,我们从矩阵代数知道 MatrixProduct(AT, WT) = TT。这使我们可以为 W 的每一行设置线性方程,其形式为:

基本上,对于网络中的每个神经元,我们根据哪些神经元被激活以及何时被激活来找到进入该神经元的突触权重。鉴于我们知道神经元何时被激活以及何时未被激活,我们可以创建一组线性方程。

当 c >= r 时求解 W[k]

AT 的列是线性无关的,因为它们对应于 A 的行,并且 AT 的行数 (r) 小于列数 (c)。这意味着线性方程组 MatrixProduct(AT, WT[k]) = TT[k] 的解跨越了整个向量空间。这允许我们将 t[k] 设置为任何我们喜欢的值,并将 TT[k] 的元素设置为满足其约束的任何向量。

示例:XOR 问题

为了找到权重矩阵的第 4 行,我们需要求解线性方程

在删除 AT 中对应于网络掩码中 NULL 条目的列后,方程组如下所示:

由于我们可以根据其约束将 TT[k] 设置为任何我们喜欢的值,因此我们可以让 TT[4] = AT[4] 且 t[4] = 1。因此我们得到:

这给出 W[4][1] = 1,W[4][2] = 1 且 W[4][3] = 0 – W[4][1] – W[4][2] = 0 – 1 – 1 = -2。因此 W[4] = [1, 1, -2, N]。

当 c < r 时求解 W[k]

在这种情况下,找到突触的权重会有些棘手。该过程涉及三个步骤

  1. 在 r 维空间中找到由 AT 的列形成的 c 维线性子空间。
  2. 找到一个存在于 c 维线性子空间中并满足其约束的向量 TT[k]。
  3. 求解线性方程 MatrixProduct(AT, WT[k]) = TT[k] 以获得权重。

示例:XOR 问题

为了找到权重矩阵的第 3 行,我们需要求解线性方程

在删除 AT 中对应于网络掩码中 NULL 条目的列后,方程组如下所示

  1. 由于 c=2 且 r=3,r 维中的 c 维线性子空间只是 3D 中的一个 2D 平面。AT 列的叉积为 CrossProduct(AT[1], AT [2]) = [1, 1, -1],因此 2D 平面为 x + y – z = 0。
  2. TT[3] 的一个合适向量是 [½, ½, 1],其中 t[3] = 1。由于 DotProduct([1, 1, -1], [½, ½, 1]) = 0,TT[3] 位于由 AT 的列形成的平面上。然而,整个解集可以由 0 < W[3][1] <= W[3][2] < t[3] <= W[3][1] + W[3][2] 来描述。
  3. AT 的上半部分已经处于行阶梯形,因此我们可以直接读出 W[3] = [½, ½, N, N]。

训练神经网络

有许多不同的学习算法是人们随着时间的推移而开发的,最受欢迎的是反向传播算法。我认为问题不在于算法本身,而在于网络的初始化,据我所知,这包括创建随机权重和阈值(偏置)。

理想情况下,一旦网络使用基本数据集进行了初始化,它就能够学习更广泛的训练数据集。这将使网络能够在其输入和输出之间建立更复杂的关联集,从而使其能够对“更嘈杂”的输入给出更准确的输出。

一篇名为 _Neural Networks on C#_ 的好文章,作者是 Andrew Kirillov,其中列出了一些学习算法,可在 https://codeproject.org.cn/Articles/16447/Neural-Networks-on-C 找到。

学习算法

一个相当基础的学习算法涉及三个步骤

  1. 使用所需的输入向量 (I) 运行网络
  2. 无论给出什么输出,都将输出神经元更改为所需的输出向量 (O)
  3. For (k = n to k = i + 1) {For (j = 1 to n) if (a[k] && a[j] && W[k][j] != NULL) W[k][j] += learningRate*(t[k] - DotProduct(W[k], a)else if (!a[k] && a[j] && W[k][j] != NULL) W[k][j] -= learningRate*(t[k] - DotProduct(W[k], a))}

如果期望的输入向量类似于网络初始化时的输入向量之一,那么输入到网络中的期望输入向量 (I) 应该激活了一些在初始化期间创建的路径,从而使像这样的学习算法至少取得一些成功。

用于将输入与输出关联的路径应该更强,而未使用的路径会变弱。这使得路径有一种自然选择,被使用的路径得以保留,未使用的路径则消失。

过饱和输入

在这里,我使用“过饱和”一词来描述输入向量 (I) 激活了太多神经元的情况。以下是字母“A”的过饱和输入的示例。

图 4. 过饱和的“A”示例。

然而,在大量数据的情况下,这应该不成问题,因为期望输出与输入噪声之间的关联强度不会像与期望输入之间的关联那样强。

欠饱和输入

欠饱和输入描述的是输入向量 (I) 没有足够激活的神经元。这在学习方面可能会成为一个问题,因为从输入到输出的路径可能没有被激活。以下是字母“A”的欠饱和输入的示例。

图 5. 欠饱和的“A”示例。

这里的解决方案可能在于初始化网络时使用的网络掩码的选择。如果一个网络设计成从期望的输入到输出有许多不相互交互的路径,那么有可能从输入的部分获得期望的输出。这可以通过创建一个神经元被分组且神经元之间不相互连接的网络来实现,尽管这只是推测。网络掩码可能看起来像

图 6. 网络掩码可能的样子。

结论

总之,神经元只能激活或不激活的神经网络可以通过突触权重的矩阵和阈值(偏置)的向量来表示。神经网络中神经元的状态,即它们是否被激活,也可以由一个向量表示。这些属性可用于创建一组线性方程,其解可用于初始化神经网络,一旦初始化,网络就可以(希望)从关联的输入和输出中学习。

以下是一些争议点、兴趣点和尚未在本文章中涵盖的未来方向,但我认为应该提及

争议点

  • 代码不完整 - NNInitializer 类不完整,对于 c < r 的情况没有代码。这是因为我没有知识或技能来弄清楚它,并且尽我最大的努力,也找不到已经为我完成的。
  • 测试 - 我没有在 XOR 问题之外测试过代码,但就像任何没有测试过自己代码的人一样,我相信它能正常工作。
  • 当 c > 完整 A 中的总列数时 - 如果网络掩码任何一行的非 NULL 条目数量大于激活矩阵的列数,则可能出错,这可以通过减少权重矩阵中可以存在的任何一行的最大非 NULL 条目数量或通过向 A 添加更多列来纠正。

关注点

  • 关联 - 研究大脑的人喜欢说大脑通过关联来学习,而神经网络将输入与输出相关联。看看是否有可能拥有一个“串”的关联,即 A 与 B 相关联,B 与 C 相关联,依此类推,这将很有趣。
  • 编码理论 - 使互联网和手机能够清晰通信的数学分支可能与神经网络密切相关。二进制线性码的奇偶校验校验矩阵可以用作表示神经元隐藏层的激活矩阵的一部分。

    还可以向用于初始化神经网络的基本输入向量添加一个奇偶校验输入神经元。该神经元将为每个基本输入向量提供一个奇偶校验权重,这可能使代表输入神经元的激活矩阵的行保持线性无关。

    最后,可以在输出层神经元添加奇偶校验神经元,然后通过综合解码器对其进行处理,以检测或纠正输出中的任何错误。

未来方向

  • 认知力学 - 我想我最好“创造一个术语”,趁着这个机会。认知力学是神经网络和人工智能之间的研究领域。它涉及为任何给定的应用程序定义输入和输出集,以及网络掩码。
  • 节点缩减 - 我认为,如果可能找到一个网络所需的最小神经元数量而不影响其运行,然后将现有网络中的神经元数量减少到该大小,那将很有趣。
  • 隐藏层输入 - 能够手动激活和停用隐藏层中的神经元,以获得“串”的关联。
  • 神经递质 - 大脑中有许多神经递质,每个神经元对每种神经递质都有不同的受体。使用神经网络建模这些可以通过使用不同的权重矩阵来完成,每个神经递质对应一个矩阵,同时只保留一个阈值向量和一个激活向量。

    每个矩阵将只给出一种神经递质的权重,运行网络将涉及将每个矩阵的权重相加以计算单个神经元,然后再进行下一个神经元。然而,我不确定这些类型的神经网络如何实现初始化和学习。

参考文献

参考书目

  • A first course in coding theory; By Raymond Hill; 1986
  • Biological Psychology; By James W. Kalat; 2009
© . All rights reserved.