神经网络最基本形式感知器的入门教程3

上页

  一个单一的感知器已经可以学习如何分类,让我们看看编码如何实现?

除了一些标准库,我们只需要一个定制库用来将感知器输出变成一个PNG图片:

package main
import (
       "fmt" 
       "math/rand" 
       "time" 
       "github.com/appliedgo/perceptron/draw" 
)

现在我们定义感知器,一个新的感知器是使用随机权重和偏差,这两个值会在以后训练中修改,感知器执行两个任务:

  • 处理输入信号
  • 根据训练调整输入权重

这是感知器的数据结构,带有权重和偏差:

type Perceptron struct {
       weights []float32 
       bias    float32 
}

下面是感知器的Heaviside Step function

func (p *Perceptron) heaviside(f float32) int32 {
       if f < 0 {
              return 0 
       }
       return 1 
}

创建一个带有N个输入的感知器,权重和偏差使用-1和1之间随机数初始化。

func NewPerceptron(n int32) *Perceptron {
       var i int32 
       w := make([]float32, n, n)
       for i = 0; i < n; i++ {
              w[i] = rand.Float32()*2 - 1 
       }
       return &Perceptron{
              weights: w,
              bias:    rand.Float32()*2 - 1,
       }
}

下面的Process函数将实现感知器的核心功能,它对输入加入权重,汇总它们,增加偏差值,然后通过Heaviside Step function运行结果,会返回一个boolean类型但是是int32,我们就可以直接使用这个值来调整感知器。

func (p *Perceptron) Process(inputs []int32) int32 {
       sum := p.bias
       for i, input := range inputs {
              sum += float32(input) * p.weights[i]
       }
       return p.heaviside(sum)
}

在学习阶段,感知器基于结果和正确结果偏离程度调整其权重和偏差:

func (p *Perceptron) Adjust(inputs []int32, delta int32, learningRate float32) {
       for i, input := range inputs {
              p.weights[i] += float32(input) * float32(delta) * learningRate
       }
       p.bias += float32(delta) * learningRate
}

训练

我们排除线是垂直的,这样我们就能知道一条线等于一个线性函数:

f(x) = ax + b

参数a指定线的梯度(即这条线有多陡),而b代表偏移。

用这种方式描述线,对于一个给定的点,检查这个点是该在线的上方还是下方,对于点(x,y),如果y值大于f(x)结果,那么(x,y)将在线上方。

线性函数

a和b指定线性函数,这个函数描述分离线,看下面细节,它们以全局水平定义,因为我们在几个地方需要它们。

var (
       a, b int32 
)

函数描述下面这条线:

func f(x int32) int32 {
       return a*x + b
}

如果点(x,y)在线y=ax+b上方,上面函数返回1,否则返回0:

func isAboveLine(point []int32, f func(int32) int32) int32 {
       x := point[0]
       y := point[1]
       if y > f(x) {
              return 1 
       }
       return 0 
}

下面函数train是训练老师,老师产生一个随机测试点,然后喂给感知器,老师然后比较输出和正确答案,告诉感知器它离正确答案有多远:

func train(p *Perceptron, iters int, rate float32) {
       for i := 0; i < iters; i++ {

产生-100和100之间随机点:

              point := []int32{
                    rand.Int31n(201) - 101,
                    rand.Int31n(201) - 101,
              }

喂这个点给这个感知器然后评估结果:

              actual := p.Process(point)
              expected := isAboveLine(point, f)

让感知器相应地调整其内部值:

              p.Adjust(point, expected-actual, rate)
       }
}

运行

现在是时候看看感知器如何完成学习任务。我们扔一些随机点,但是这个时候没有老师的反馈。感知器分类会正确吗?

这是我们的测试函数,返回正确答案个数:

func verify(p *Perceptron) int32 {
       var correctAnswers int32 = 0 

创建一个新的画布,x和y范围是-100到100:

       c := draw.NewCanvas()
       for i := 0; i < 100; i++ {

产生-100和100之间的随机点:

              point := []int32{
                    rand.Int31n(201) - 101,
                    rand.Int31n(201) - 101,
              }

喂这个点给感知器,然后评估结果:

              result := p.Process(point)
              if result == isAboveLine(point, f) {
                    correctAnswers += 1 
              }

画这个点,颜色告诉你感知器的回答是“在线上面”还是“下面”:

              c.DrawPoint(point[0], point[1], result == 1)
       }

画这个线y=ax+b

       c.DrawLinearFunction(a, b)

保存图片为./result.png

       c.Save()
       return correctAnswers
}

下面是main函数:是入口执行函数,设置 训练和测试感知器:

func main() {

设置线参数,线的梯度从-5和5之间变化,b偏移在-50和50之间:

       rand.Seed(time.Now().UnixNano())
       a = rand.Int31n(11) - 6 
       b = rand.Int31n(101) - 51 

用两个输入点创建一个新的感知器:

       p := NewPerceptron(2)

开始学习:

       iterations := 1000 
       var learningRate float32 = 0.1 // Allowed range: 0 < learning rate <= 1. 

真正进行训练:

       train(p, iterations, learningRate)

感知器准备好测试:

       successRate := verify(p)
       fmt.Printf("%d%% of the answers were correct.\n", successRate)
}

上面完整代码见: GitHub

你可以通过下面方法获得:

go get -d github.com/appliedgo/perceptron
cd $GOPATH/github.com/appliedgo/perceptron
go build
./perceptron 

打开结果的result.png看看感知器对这些点划分得如何?

 

Hello, TensorFlow入门教程

大数据专题

不久我们将不用计算机编程,只需像狗一样训练它们

深入学习教程:从感知到深度神经网络

Tensorflow简单教程