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

使用 Delta 规则计算 ADALINE 的权重

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.14/5 (3投票s)

2007年6月25日

5分钟阅读

viewsIcon

32848

downloadIcon

1264

对于一个有两个输入(取值+1或-1)和一个+1偏移量的ADALINE。

Screenshot - screenshot.jpg

引言

这实际上是我完成过的最简单的 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;

}

请注意,dw1dw2dw3 是“按引用”传递的(由 ampersand 前缀指示)。这样做既简单又高效,允许在被调用的函数中为变量分配新值,并且这些新值在调用函数中立即可用(因为两个函数都指向相同的内存位置)。这避免了通过“按值”传递变量所带来的各种开销。其他参数被保留为“按值”传递,因为它们不需要向调用函数提供信息。如果您正在寻找更高的效率,并希望坚持最小特权原则(即被调用的函数无法干扰调用函数中的变量),这是“按值”传递所提供的,那么您可以让所有变量都“按引用”传递,并将除了您特别想操作的变量之外的所有变量(即 dw0dw1dw2)在调用函数中声明为“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;
© . All rights reserved.