版权所有:基础软件。作者邮箱:sun.j.l.studio@gmail.com。本文首发于 http://www.cnblogs.com/FoundationSoft。文章转载请保持此版权信息并注明出处。
最近在看一本书, Introduction to Neural Networks for CSharp。其中有一章介绍了Hopfield网络。这章对于Hopfield的介绍很简单,其运行过程用C#来实现也很容易,只是没有介绍网络运行的原理、数学证明,程序即使运行出来了,也是知其然不知其所以然。我从网上查了一些资料,期望找到Hopfied网络的数学理论基础和训练方法有效性的数学证明,但是暂时没有找到和书上所说的训练方法一致的相关内容,却意外发现另外一个问题对:大家对Hopfield网络模型以及训练的描述都有一些差异,没有完全一致的定义。看来以后还要多看各种资料,自己消化理解,对别人所说的做出甄别。
不管怎么说,书上介绍的内容简单实用,按照书上的训练方法能够识别一些简单的模式。这篇文章把书中讲的Hopfield网络训练和识别方法做一归纳总结。
书中定义的Hopfield网络是一种单层全互连自动联想网络(fully connected single layer autoassociative network)。书中所讲的Hopfield网络有以下几个特点: (1)网络有N个神经元,则权值矩阵W为N*N的方阵。权值矩阵W为对称阵,且对角线元素为0.即神经元到自身的权值为0。
(2)神经元输入为二值(即0、1或者-1、1),书中采用0和1.
(3)神经元输出为二值(即0、1或者-1、1),书中采用0和1.
训练过程如下:
(1)初始化权值矩阵为0.
(2)输入下一个模式P,P为N维向量(p1,p2,p3...pn)。
(3)将p乘以p的转置得到一个N*N的方阵A。
(4)将第3步的方阵A对角线元素设置为0.
(5)将方阵A回到权值矩阵W上。
(6)如果有更多的模式,则转到第2步。
(7)训练结束。 训练结束后,可以用Hopfield网络识别前面的训练模式,或者与它们相近的模式,这体现了Hopfield的联想功能。Hopfield能够自动识别与其训练模式相反的模式,例如训练模式为[0,1,1,0],则训练后,网络也能识别[1,0,0,1]。
利用Hopfield网络可以实现简单的图形识别,如数字识别。把图形拆成点阵,形成一个二维矩阵,把矩阵拉成一维的向量,这就是Hopfield网络要识别的模式了。将这个模式输入给Hopfield网络训练即可。
Hopfield网络的C#代码实现如下。下面的代码中用到了矩阵类Matrix,这是我写的一个类,封装了常用的矩阵运算,代码在这里。
/// Hopfield网络,单层全互联自动联想网络,输入为二值。
/// </summary>
/// <remarks>
/// 孙继磊,2010-10-18
/// sun.j.l.studio@gmail.com
/// </remarks>
public class HopfieldNetwork
{
#region 属性和字段
private Matrix weightMatrix;
/// <summary>
/// 权值矩阵
/// </summary>
public Matrix weight
{
get
{
return weightMatrix;
}
}
/// <summary>
/// 神经元数量
/// </summary>
public int size
{
get
{
return weightMatrix.rowNum;
}
}
#endregion
#region 构造函数
/// <summary>
/// 构造一个指定大小的Hopfield网络
/// </summary>
/// <param name="size"> 神经元数量 </param>
public HopfieldNetwork( int size)
{
this .weightMatrix = new Matrix(size, size);
}
#endregion
/// <summary>
/// 输入一个模式计算得到输出(输入输出都是二值向量)
/// </summary>
/// <param name="pattern"> 输入的模式 </param>
/// <returns> 输出结果 </returns>
public bool [] Present( bool [] pattern)
{
bool [] output = new bool [pattern.Length];
// 将模式转换为矩阵(行向量)
Matrix row = new Matrix(BipolarUtil.Bipolar2double(pattern));
// 计算输出
for ( int c = 0 ; c < pattern.Length; c ++ )
{
Matrix column = weightMatrix.getColumn(c);
double dotProduct = (row * column)[ 0 , 0 ];
output[c] = dotProduct > 0 ;
}
return output;
}
/// <summary>
/// 针对某一模式进行训练
/// </summary>
/// <param name="pattern"> 要训练的模式 </param>
public void Train( bool [] pattern)
{
if (pattern.Length != weightMatrix.rowNum)
throw new AnnException( " 输入模式维数与Hopfield神经元数量不匹配。 " );
// 将输入模式转换为行向量
Matrix input = new Matrix(BipolarUtil.Bipolar2double(pattern));
// 输入行向量转置得到列向量
Matrix inpute2 = Matrix.transpose(input);
// 输入行向量与转置相乘,得到矩阵后,对角线设置为0
Matrix t = inpute2 * input;
for ( int i = 0 ; i < input.columnNum; i ++ )
t[i, i] = 0 ;
// 将前面计算得到的矩阵回到原来的权值矩阵
weightMatrix = weightMatrix + t;
}
}
用于测试Hopfield网络的代码如下。这段代码用8个神经元识别2个预定义的模式01011100和00111111,而且由于Hopfield网络的联想记忆功能,可以识别与训练样本近似的模式,这在模糊识别时非常有用。
根据这个例子的代码和思想,较容易写出数字识别的程序来。就是把数字图形编成点阵,并将这个点阵拉直成行向量,作为Hopfield网络的训练样本即可。例如将0-9数字每个图案做成16*16的点阵,则需要16*16=256个神经元。当然应该用不了这么多,具体数量可通过实验来决定。
下面的代码是我用8个神经元识别2个模式的例子。先用标准样本训练,后让用户输入模式并识别。
2 /// 由8个神经元组成简单的Hopfield网络,识别简单的模式
3 /// </summary>
4 public void simplePattern()
5 {
6 const int SIZE = 8 ;
7 HopfieldNetwork hopfield = new HopfieldNetwork(SIZE);
8 Console.WriteLine( " 原始权值为: " );
9 Console.WriteLine(hopfield.weight.toMatrixString( 0 ));
10 // 以下为2个模式
11 bool [] sample1 = new bool [] { false , true , false , true , true , true , false , false }; // 01011100
12 bool [] sample2 = new bool [] { false , false , true , true , true , true , true , true }; // 00111111
13 // 训练2个模式并输出训练后的权值
14 hopfield.train(sample1);
15 Console.WriteLine( " 训练样本01010101后权值为: " );
16 Console.WriteLine(hopfield.weight.toMatrixString( 0 ));
17 hopfield.train(sample2);
18 Console.WriteLine( " 训练样本0011后权值为: " );
19 Console.WriteLine(hopfield.weight.toMatrixString( 0 ));
20 string input = null ;
21 bool [] pattern = new bool [SIZE];
22 bool [] result = new bool [SIZE];
23 StringBuilder sb = new StringBuilder();
24 // 让用户循环输入8维向量并尝试识别
25 // 结果证明,能够正确识别被训练的2个模式,而且能够识别与之相近的模式
26 while ( true )
27 {
28 Console.Write( " 输入一个模式(能够识别01011100, 00111111),exit退出: " );
29 input = Console.ReadLine();
30 if (input == " exit " ) break ;
31 if (input.Length != SIZE) continue ;
32 for ( int i = 0 ; i < SIZE; i ++ )
33 pattern[i] = (input[i] != ' 0 ' );
34 result = hopfield.present(pattern);
35 sb.Clear();
36 for ( int i = 0 ; i < SIZE; i ++ )
37 sb.Append(result[i] ? " 1 " : " 0 " );
38 Console.WriteLine( " 识别结果为: " + sb.ToString());
39 }
40 }
the end of this article