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

单层感知机作为线性分类器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.74/5 (56投票s)

2010年11月7日

CPOL

3分钟阅读

viewsIcon

148941

downloadIcon

7395

在本文中,我将向您展示如何使用单层感知器作为2个类别的线性分类器。

PerceptronForm.jpg

引言

感知器是最简单的 feed forward 神经网络类型。它由 Frank Rosenblatt 设计,作为两个线性可分类的二分分类器。这意味着网络可以解决的问题类型必须是线性可分的。基本感知器由 3 层组成

  • 传感器层
  • 关联层
  • 输出神经元

在传感器层中有许多输入 (xn)、权重 (wn) 和一个输出。 有时 w0 被称为偏差,x0 = +1/-1(在本例中 x0=-1)。

percept.jpg

对于感知器上的每个输入(包括偏差),都有一个相应的权重。 为了计算感知器的输出,每个输入乘以其对应的权重。 然后计算所有输入的加权和,并通过一个限制器函数评估感知器的最终输出。

神经元的输出由输出神经元的激活形成,这是输入的函数

(1) eq1.JPG

激活函数 F 可以是线性的,这样我们就有一个线性网络,或者是非线性的。 在此示例中,我决定使用阈值(符号)函数

(2) eq2.JPG

在这种情况下,网络的输出是 +1 或 -1,具体取决于输入。 如果总输入(所有输入的加权和)为正,则该模式属于 +1 类,否则属于 -1 类。 由于这种行为,我们可以使用感知器进行分类任务。

让我们考虑我们有一个带有 2 个输入的感知器,我们想将输入模式分成 2 个类。 在这种情况下,类之间的分隔是直线,由等式给出

(3) eq3.gif

当我们设置 x0=-1 并标记 w0=? 时,我们可以将等式 (3) 重写为形式

(4) eq4.gif

在这里,我将描述感知器的学习方法。 感知器的学习方法是一个调整权重的迭代过程。 将一个学习样本呈现给网络。 对于每个权重,通过将校正添加到旧值来计算新值。 阈值以相同的方式更新

(5) eq5.gif

eq5b.gif

其中y是感知器的输出,d是期望的输出,?是学习参数。

使用程序

当您运行该程序时,您会看到一个可以输入样本的区域。 在此区域上单击鼠标左键,您将添加第一个类别的样本(蓝色十字)。 在此区域上单击鼠标右键,您将添加第一个类别的样本(红色十字)。 样本被添加到samples列表中。 您还可以设置学习率和迭代次数。 当您设置所有这些值后,您可以单击“学习”按钮开始学习。

Using the Code

所有样本都存储在通用列表samples中,该列表仅保存Sample类对象。

public class Sample
{
    double x1;
    double x2;
    double cls;

    public Sample(double x1, double x2, int cls)
    {
        this.x1 = x1;
        this.x2 = x2;
        this.cls = cls;
    }

    public double X1
    {
        get { return x1; }
        set { this.x1 = value; }
    }

    public double X2
    {
        get { return x2; }
        set { this.x2 = value; }
    }

    public double Class
    {
        get { return cls; }
        set { this.cls = value; }
    }
}

在运行感知器学习之前,重要的是设置学习率和迭代次数。 感知器有一个很棒的属性。 如果存在解决方案,感知器总是能找到它,但当解决方案不存在时就会出现问题。 在这种情况下,感知器将尝试在无限循环中找到解决方案,为了避免这种情况,最好设置最大迭代次数。

下一步是为权重(w0、w1 和 w2)分配随机值。

Random rnd = new Random();

w0 = rnd.NextDouble();
w1 = rnd.NextDouble();
w2 = rnd.NextDouble();

当随机值被分配给权重时,我们可以循环遍历样本并计算每个样本的输出,并将其与期望的输出进行比较。

double x1 = samples[i].X1;
double x2 = samples[i].X2;
int y;

if (((w1 * x1) + (w2 * x2) - w0) < 0)
{
    y = -1;
}
else
{
    y = 1;
}

我决定设置x0=-1,因此,感知器的输出由等式给出:y=w1*w1+w2*w2-w0. 当感知器输出和期望输出不匹配时,我们必须计算新的权重

if (y != samples[i].Class)
{
    error = true;

    w0 = w0 + alpha * (samples[i].Class - y) * x0 / 2;
    w1 = w1 + alpha * (samples[i].Class - y) * x1 / 2;
    w2 = w2 + alpha * (samples[i].Class - y) * x2 / 2;
}

Y 是感知器的输出,samples[i].Class 是期望的输出。 最后 2 个步骤(循环遍历样本和计算新权重),我们必须重复,直到error变量为<> 0并且当前迭代次数(iterations)小于maxIterations

int i;
int iterations = 0;
bool error = true;

maxIterations = int.Parse(txtIterations.Text);

Random rnd = new Random();

w0 = rnd.NextDouble();
w1 = rnd.NextDouble();
w2 = rnd.NextDouble();

alpha = (double)trackLearningRate.Value / 1000;

while (error && iterations < maxIterations)
{
    error = false;

    for (i = 0; i <= samples.Count - 1; i++)
    {
        double x1 = samples[i].X1;
        double x2 = samples[i].X2;
        int y;

        if (((w1 * x1) + (w2 * x2) - w0) < 0)
        {
            y = -1;
        }
        else
        {
            y = 1;
        }

        if (y != samples[i].Class)
        {
            error = true;

            w0 = w0 + alpha * (samples[i].Class - y) * x0 / 2; 
            w1 = w1 + alpha * (samples[i].Class - y) * x1 / 2;
            w2 = w2 + alpha * (samples[i].Class - y) * x2 / 2;
        }
    }
    objGraphics.Clear(Color.White);
    DrawSeparationLine();
    iterations++;
}

函数DrawSeparationLine绘制 2 个类的分离线。

历史

  • 2010 年 11 月 07 日 - 发布原始版本
© . All rights reserved.