使用 Delta 规则计算 ADALINE 的权重






4.14/5 (3投票s)
2007年6月25日
5分钟阅读

32848

1264
对于一个有两个输入(取值+1或-1)和一个+1偏移量的ADALINE。
引言
这实际上是我完成过的最简单的 C++ 程序之一。它确实没什么复杂的。它不会引起有经验的 C++ 程序员或神经网络专家的兴趣。但我希望,它或许能引起和我一样在学习神经网络的初学者们的兴趣,也就是说,基本上是刚开始学的时候。它也是一个相当容易编写的 Visual C++ 程序。在我写过的其他文章中,我详细描述了如何从头开始构建应用程序,包括如何处理应用程序向导。在这篇文章中,我将假定读者知道如何使用应用程序向导创建应用程序框架,如何在窗体上放置控件并将变量关联到这些控件,以及如何为按钮控件创建一个应用程序处理函数。
对于那些对编码不感兴趣但想玩这个计算器的用户,可以通过点击上面的链接下载编译好的应用程序。它很容易使用。你输入初始权重、训练集、期望输出和 eta 值,然后按“Go”。程序应该会很快运行完毕。编译好的程序将运行一百轮,据我所知,这已经足够了。如果你向下滚动输出框,直到“y”列(输出)中的数字开始与“d”列(期望输出)中的数字相对应,那么在该行的输出框中的“w0”、“w1”和“w2”下显示的值就是网络的正确权重。你可以通过用这些值替换“初始权重”并再次按“Go”来测试这一点。这次,从输出开始,'y'中的每个值都应该等于'd'中对应的那个值。
背景
我一直在看 Phil Picton 的书《神经网络》,这本书我放了好久了,一直没能像我期望的那样快速地读完。直到最近几天,我才突然想到要通过 ADALINE 权重查找的迭代过程来计算。我在一张纸上写下了数字,然后在工作的超市里,在服务顾客的间隙,凭空计算。我感到很高兴,因为我觉得我差不多掌握了这个过程。然而,这个过程对我来说不够快,无法获得令人满意的结果。你知道,商店里的人可能很挑剔。而且,我心算能力很差。所以,在一个周六晚上,我着手编写了这个相当不费力的小程序。
我不想在这里尝试描述 ADALINE。我只是个学习者。我在这里为读者提供一个合适的 Google 搜索链接 - ADALINE 神经网络描述。我确信那里有一些有用的信息。如果没有,那么你买 Phil Picton 的书也不会错。
使用代码
好的,非常简要地说,我通过继承 CFormView 来设置了一个 SDI。在对话框上,我添加了许多用于输入的文本框,一个用于输出,还有一个用于启动一切的按钮。所有有意义的代码都在按钮的事件处理函数 CAdalineView::OnButtonGo()
和我创建的另一个成员函数 CAdalineView::adaline(...)
中。
void CAdalineView::OnButtonGo()
{
// TODO: Add your control notification handler code here
float d1, d2, d3, d4, eta;
float y1, y2, y3, y4;
float w10, w11, w12;
float w20, w21, w22;
float w30, w31, w32;
float w40, w41, w42;
float x10, x11, x12;
float x20, x21, x22;
float x30, x31, x32;
float x40, x41, x42;
float net1, net2, net3, net4;
float dw10, dw11, dw12;
float dw20, dw21, dw22;
float dw30, dw31, dw32;
float dw40, dw41, dw42;
eta = atof(m_strEta);
d1 = atof(m_strD1);
d2 = atof(m_strD2);
d3 = atof(m_strD3);
d4 = atof(m_strD4);
CString str="";
m_strOutput="";
////Starting data
w10 = atof(m_strW0);
w11 = atof(m_strW1);
w12 = atof(m_strW2);
int passes = 0;
int matches = 0;
x10 = atof(m_strX10);
x11 = atof(m_strX11);
x12 = atof(m_strX12);
x20 = atof(m_strX20);
x21 = atof(m_strX21);
x22 = atof(m_strX22);
x30 = atof(m_strX30);
x31 = atof(m_strX31);
x32 = atof(m_strX32);
x40 = atof(m_strX40);
x41 = atof(m_strX41);
x42 = atof(m_strX42);
while (passes <= 100){
///// input pattern 1
adaline(x10, x11, x12, w10, w11, w12, d1, net1, y1,
dw10, dw11, dw12, eta);
//// change weights from pass1 to pass 2
w20 = w10+dw10;
w21 = w11+dw11;
w22 = w12+dw12;
///// input pattern 2
adaline(x20, x21, x22, w20, w21, w22, d2, net2, y2,
dw20, dw21, dw22, eta);
//// change weights from pass2 to pass 3
w30 = w20+dw20;
w31 = w21+dw21;
w32 = w22+dw22;
///// input pattern 3
adaline(x30, x31, x32, w30, w31, w32, d3, net3, y3,
dw30, dw31, dw32, eta);
//// change weights from pass3 to pass 4
w40 = w30+dw30;
w41 = w31+dw31;
w42 = w32+dw32;
///// input pattern 4
adaline(x40, x41, x42, w40, w41, w42, d4, net4, y4,
dw40, dw41, dw42, eta);
UpdateData(FALSE);
//// change weights from pass4 to pass 1
w10 = w40+dw40;
w11 = w41+dw41;
w12 = w42+dw42;
passes++;
}
}
所以,这个函数从编辑框中获取数据,并处理迭代过程,依次将四个输入模式的数据传递给 adaline
函数。权重也是在这里调整的。但是,权重调整的数值是在 adaline
函数中计算的。
void CAdalineView::adaline(float x0, float x1, float x2,
float w0, float w1, float w2,
float d, float net, float y,
float &dw0, float &dw1, float &dw2, float eta)
{
CString str = "";
//Calculate 'net'
net = x0*w0 + x1*w1 + x2*w2;
//Calculate output value by passing 'net' through
//a hard-limiter
if (net >= 0)
y = 1;
else
y = -1;
//Here the 'Delta Rule' is applied
dw0 = eta*x0*(d - net);
dw1 = eta*x1*(d - net);
dw2 = eta*x2*(d - net);
//And the output is shuffled off to the appropriate text box
//inputs
str.Format("%.2f\t%.2f\t%.2f\t",x0,x1,x2);
m_strOutput += str;
//weights
str.Format("%.2f\t%.2f\t%.2f\t",w0,w1,w2);
m_strOutput += str;
//d, net, y
str.Format("%.2f\t%.2f\t%.2f\t",d,net,y);
m_strOutput += str;
//weight adjustments
str.Format("%.2f\t%.2f\t%.2f\t\r\n",dw0,dw1,dw2);
m_strOutput += str;
}
请注意,dw1
、dw2
和 dw3
是“按引用”传递的(由 ampersand 前缀指示)。这样做既简单又高效,允许在被调用的函数中为变量分配新值,并且这些新值在调用函数中立即可用(因为两个函数都指向相同的内存位置)。这避免了通过“按值”传递变量所带来的各种开销。其他参数被保留为“按值”传递,因为它们不需要向调用函数提供信息。如果您正在寻找更高的效率,并希望坚持最小特权原则(即被调用的函数无法干扰调用函数中的变量),这是“按值”传递所提供的,那么您可以让所有变量都“按引用”传递,并将除了您特别想操作的变量之外的所有变量(即 dw0
、dw1
、dw2
)在调用函数中声明为“const
”。
下面的代码片段是应用 Delta 规则的地方
//Here the 'Delta Rule' is applied
dw0 = eta*x0*(d - net);
dw1 = eta*x1*(d - net);
dw2 = eta*x2*(d - net);
这里,输出“y”通过将“net
”中的值传递给“硬限制器”来计算
//Calculate output value by passing 'net' through
//a hard-limiter
if (net >= 0)
y = 1;
else
y = -1;
这不知怎么地,就变成了一对非常友好的代码行来执行如此严厉的函数。
关注点
如果我没有写这个程序,而是继续在纸上做计算,我将做到天荒地老,因为我一直在从权重(w
值)中减去权重变化(dw
值)。我的程序非常有效地表明,这样做毫无进展。经过片刻的思考,我改变了代码(在 OnButtonGo
函数中)来添加权重变化,谢天谢地,它奏效了。
w20 = w10+dw10;
w21 = w11+dw11;
w22 = w12+dw12;