机器学习-感知器学习算法


由于文章编辑问题,图片和公式都无法展示,需要原文章和源代码的,请下载
链接: http://pan.baidu.com/s/1dE8yK1N 密码: 2c9n

机器学习-感知器学习算法

                                                                                                                        Python语言实现

                                                                                   初学者整理,参考资料均来源于网上

一、           概述

感知器是由美国计算机科学家罗森布拉特(Rosenblatt)于1957年提出的。感知器可谓是最早的人工神经网络。

     感知器分单层感知器和多层感知器。

二、           单层感知器(Single Layer Perceptron

l 简介

单层感知器是一个具有一层神经元、采用阈值激活函数的前向网络。通过对网络权值的训练,可以使感知器对一组输人矢量的响应达到元素为01的目标输出,从而实现对输人矢量分类的目的。

单层感知器是一个简单的线性二分类器,它保存着输入权重,根据输入和内置的函数计算输出.人工神经网络中的单个神经元。

单层感知器可以计算逻辑或,逻辑与,逻辑非等运算,但是不能计算异或。因为异或不是平面线性可分的,在多层感知器中解决。

l 模型

单层感知器只有输入和输出,输入和输出直接相连,多个输入一个输出。

模型为每个输入定义为X,输入到输出的权重定义为w,所有的输入和权重的乘积和为输出的值,对于这个乘积和做如下处理,如果乘机和大于临界值(一般是0),输入端就取1;如果小于临界值,就取-1

l 工作原理

单层感知器可将外部输入分为两类。当感知器的输出为+1时,输入属于L1,当感知器输出-1时,输入属于L2类,从而实现两类目标的识别。在二维空间,单层感知器进行模式识别的判决超平面由下式决定:

对于只有两个输入的判别边界是直线(),选择合适的学习算法可训练出满意的w1w2。将样本中两类数据用直线分开。如下图:

b的值决定了直线的偏移量,w决定了直线的旋转度数。

l 学习算法

第一步、设置变量和参数

f(x)为激活函数,y(n)为实际输出,d(n)为期望输出,λ为学习速率,n为迭代次数,e为实际输出与期望输出的误差。

第二步、初始化

给权值向量w的各个分量赋一个较小的随机非零值,置n=1

第三步、输入一组样本X(n)=[1,x1(n),x2(n),…,xm(n)],并给出它的期望输出d(n)

第四步、计算出实际输出:y(n)=f()

第五步、求出期望输出和实际输出的差 e=d(n)-y(n)。根据误差判断输出是否满足条件,一般为对所有样本误差为零或者均小于预设的值,则算法结束,否则将值增加1,并用下式调整权值:

w(n+1)=w(n)+λ[d(n)-y(n)]x(n)

权值调整公式属于随机梯度下降算法。(待确认)

然后转到第三步,进行下一轮计算过程。

l 代码(Python)

感知器源码(Perceptron.py)

#单层感知器机器学习算法 20170621 gyk

 

#多层感知器使用梯度下降算法进行训练

#单层感知器只能解决线性可分问题

 

#importlogging

#importos

 

#预测函数,训练使用使用公式“输入×权重的和”为结果。使用阶跃函数sigmoid把结果转换为10

#由于默认第一个输入为1,第一个权重为b,所以在公式转换为:w1*x1+w2*x2+b=0

#结果等于0为线性分割线的线上。小于0为线下,大于0为线上。用该线把结果分成两种。

#由于在预测的时候,输入参数比训练参数少一个结果列,所以不能用输入参数数组计算特征列,而是用权重数组减去第一个bias偏移量列来计算特征数。

defpredict(inputs, weights):

   activation = weights[0]

   for i in range(len(weights)-1):

       activation += weights[i+1]*inputs[i]

   return sigmoid(activation)

 

#阶跃函数,把值转换成1/0或者1/-1

defsigmoid(value):

   return 1.0 if value >= 0.0 else 0.0

 

#训练

#初始化权重为0.5,然后循环训练集,读取每一条训练集数据,进行预测,然后把预测结果和实际结果进行对比,如果一样就用下一条训练。如果和实际结果不相等。就使用随机梯度下降算法重新计算权重。

#然后重新训练,直到所有训练数据的预测结果和实际结果一直,则训练结束。

#训练的结果其实就是在平面上找一条直线,该直线能把两类的数据点分割在直线的两边。如果有的点是错误的,就变化w1w2来旋转直线,变化w0来移动直线找到最佳位置。训练速率太大会找不到最佳位置,

#训练速率太小,训练速度就太慢。期望值和实际值的差值的正负决定了旋转和移动的方向。

#l_rate为学习速率,一般在00.1之间取值。

def train(dataSet,l_rate):

   count = 0

   weights = [0.5 for i inrange(len(dataSet[0]))]

   learningOK = False

   while not learningOK:

       if count >= 100:

           print('训练已经超过100次,无法完成训练')

           return None

       else:   

           count += 1

           

       learningOK = True

       for input_row in dataSet:

           prediction = predict(input_row,weights)

           error = input_row[-1] - prediction#期望输出与实际输出误差

           if error != 0:

               weights = sgd(weights,input_row, l_rate, error)

               learningOK = False          

   return weights

 

#随机梯度下降算法,计算新的权重,即改变分割线的位置

defsgd(weights, input_row, l_rate, error):

   for i in range(len(input_row)-1):

       weights[i+1] += l_rate * error*input_row[i]                       

   weights[0] += l_rate * error

   return weights

   

测试源码(Perceptron_UnitTest.py)

#单层感知器单元测试 20170621 gyk

 

from impimport reload

fromPerceptron import *

 

#测试

deftest():

   l_rate = 0.2 #学习速率

   l_while = True

   dict_function ={'or':test_or,'and':test_and,'not':test_not,'xor':test_xor}

 

   while l_while:

       print('输入q退出,输入or实现或功能,输入not实现非功能,输入and实现与功能。输入xor调用异或功能。或直接用命令行调用test_others函数可以实现其他预测')

       type_str = input()

       if type_str == 'q':

           l_while = False

       else:

           dict_function.get(type_str,'nothing')(l_rate)             

 

#计算或使用或数据集训练完模型,实现或的效果。

deftest_or(l_rate):

       weights =train([[1,1,1],[1,0,1],[0,1,1],[0,0,0]] , l_rate)

       test_common([int(input('第一个参数')),int(input('第二个参数'))], weights)

 

#计算与使用或数据集训练完模型,实现或的效果。

deftest_and(l_rate):

       weights =train([[1,1,1],[1,0,0],[0,1,0],[0,0,0]] , l_rate)

       test_common([int(input('第一个参数')),int(input('第二个参数'))],weights)

 

#计算异或使用异或数据集训练完模型,实现异或的效果。单层感知器无法完成异或功能。

deftest_xor(l_rate):

       print('单层感知器无法完成异或功能')

       weights =train([[1,1,0],[1,0,1],[0,1,1],[0,0,0]] , l_rate)

       if weights != None:

           test_common([int(input('第一个参数')),int(input('第二个参数'))],weights)       

 

#计算非使用或数据集训练完模型,实现或的效果。

deftest_not(l_rate):

       weights = train([[0,1],[1,0]] , l_rate)

       test_common([int(input('第一个参数'))], weights)

 

#计算通用

deftest_common(inputs, weights):

   if weights != None:

       print('结果为:%d'%predict1(inputs, weights))

 

#训练其他功能,只要输入训练集和待预测数据,就能预测出结果

deftest_others(trainDataSet, inputs):

       weights = train(trainDataSet , 0.2)

       test_common(inputs, weights)       

       

if__name__=='__main__':

   #logging.basicConfig(filename =os.path.join(os.getcwd(), 'log.txt'), level = logging.DEBUG)

   test() 

三、           多层感知器(Multi-Layer Perceptrons

l 简介

多层感知机(Multi Layer Perceptron MLP)是由多个感知机层全连接组成的前馈神经网络,这种模型在非线性问题中表现出色.

相对於单层感知器,输出端从一个变到了多个;输入端和输出端之间也不光只有一层,增加了隐藏层。

所谓全连接是指层A上任一神经元与临近层B上的任意神经元之间都存在连接.

反向传播(Back PropagationBP)是误差反向传播的简称,这是一种用来训练人工神经网络的常见算法,通常与最优化方法(如梯度下降法)结合使用.多层感知器就是使用BP算法进行训练的。

l 模型

l 工作原理

由前面介绍看到,单个感知器能够完成线性可分数据的分类问题,是一种最简单的可以学习的机器。但他无法解决非线性问题。比如下图中的XOR问题:即(1,1)(-1,-1)属于同一类,而(1,-1)(-1,1)属于第二类的问题,不能由单个感知器正确分类。

https://img-blog.csdn.net/20130601150444390

单个感知器虽然无法解决异或问题,但却可以通过将多个感知器组合,实现复杂空间的分割。如下图:

https://img-blog.csdn.net/20130601150455577

将两层感知器按照一定的结构和系数进行组合,第一层感知器实现两个线性分类器,把特征空间分割,而在这两个感知器的输出之上再加一层感知器,就可以实现异或运算。

也就是,由多个感知器组合:

https://img-blog.csdn.net/20130601151729163

来实现非线性分类面,其中θ(·)表示阶跃函数或符号函数。

l 学习算法

经典的BP神经网络通常由三层组成:输入层,隐含层与输出层.通常输入层神经元的个数与特征数相关,输出层的个数与类别数相同,隐含层的层数与神经元数均可以自定义

http://images2015.cnblogs.com/blog/793413/201610/793413-20161010152810539-341221931.png

每个隐含层和输出层神经元输出与输入的函数关系为:

I j =∑ i W ij O i  Ij=∑iWijOi

O j =sigmod(I l )=11+e −I l    Oj=sigmod(Il)=11+e−Il

其中W ij  Wij表示神经元i与神经元j之间连接的权重,O j  Oj代表神经元j的输出, sigmod是一个特殊的函数用于将任意实数映射到(01)区间.

上文中的sigmod函数称为神经元的激励函数(activation function)除了sigmod函数11+e −I l    11+e−Il外,常用还有tanhReLU函数.

我们用一个完成训练的神经网络处理回归问题,每个样本拥有n个输入.相应地,神经网络拥有n个输入神经元和1个输出神经元.

实际应用中我们通常在输入层额外增加一个偏置神经元,提供一个可控的输入修正;或者为每个隐含层神经元设置一个偏置参数.

我们将n个特征依次送入输入神经元,隐含层神经元获得输入层的输出并计算自己输出值,输出层的神经元根据隐含层输出计算出回归值.

上述过程一般称为前馈(Feed-Forward)过程,该过程中神经网络的输入输出与多维函数无异.

现在我们的问题是如何训练这个神经网络.

作为监督学习算法,BP神经网络的训练过程即是根据前馈得到的预测值和参考值比较,根据误差调整连接权重W ij  Wij的过程.

训练过程称为反向传播过程(BackPropagation)数据流正好与前馈过程相反.

首先我们随机初始化连接权重W ij  Wij对某一训练样本进行一次前馈过程得到各神经元的输出.

首先计算输出层的误差:

E j =sigmod ′ (O j )∗(T j −O j )=O j (1−O j )(T j −O j ) Ej=sigmod′(Oj)∗(Tj−Oj)=Oj(1−Oj)(Tj−Oj)

其中E j  Ej代表神经元j的误差,O j  Oj表示神经元j的输出,T j  Tj表示当前训练样本的参考输出,sigmod ′ (x) sigmod′(x)是上文sigmod函数的一阶导数.

计算隐含层误差:

E j =sigmod ′ (O j )∗∑ k E k W jk =O j (1−O j )∑ k E k W jk  Ej=sigmod′(Oj)∗∑kEkWjk=Oj(1−Oj)∑kEkWjk

隐含层输出不存在参考值,使用下一层误差的加权和代替(T j −O j ) (Tj−Oj) .

计算完误差后就可以更新W ij  Wijθ j  θj :

W ij =W ij +λE j O i  Wij=Wij+λEjOi

其中λ λ是一个称为学习率的参数,一般在(00.1)区间上取值.

实际上为了加快学习的效率我们引入称为矫正矩阵的机制,矫正矩阵记录上一次反向传播过程中的E j O i  EjOi值,这样W j  Wj更新公式变为:

W ij =W ij +λE j O i +μC ij  Wij=Wij+λEjOi+μCij

μ μ是一个称为矫正率的参数.随后更新矫正矩阵:

C ij =E j O i  Cij=EjOi

每一个训练样本都会更新一次整个网络的参数.我们需要额外设置训练终止的条件.

最简单的训练终止条件为设置最大迭代次数,如将数据集迭代1000次后终止训练.

单纯的设置最大迭代次数不能保证训练结果的精确度,更好的办法是使用损失函数(loss function)作为终止训练的依据.

损失函数可以选用输出层各节点的方差:

L=∑ j (T j −O j ) 2  L=∑j(Tj−Oj)2

为了避免神经网络进行无意义的迭代,我们通常在训练数据集中抽出一部分用作校验.当预测误差高于阈值时提前终止训练.

 

l 代码

多层感知器源码(MLP.py):

#多层感知器机器学习算法 20170621 gyk

 

#多层感知器使用BP算法进行训练

 

#多层感知器,可以实现平面无法完成线性分割。把点看成是在立体空间中,用平面对他们进行分割

#隐藏节点数量的计算公式:s=[2*log2(m+n+1)]>=2 s为隐藏节点数,m为输入特征数,n为识别出的类别数

 

import logging

import random

import math

 

class Mlp(object):

   

   #构造器 learn学习速率 limit最大循环次数 correct矫正率   

   def __init__(self, learn=0.05, limit=10000,correct=0.1):

       random.seed(0)

       self.__learn = learn

       self.__limit = limit

       self.__correct = correct

 

       self.__input_count = 0

       self.__hidden_count = 0

       self.__output_count = 0

 

       self.__inputs = []

       self.__hiddens = []

       self.__outputs = []

       self.__input_weights = []

       self.__output_weights = []

       self.__input_correction = []

       self.__output_correction = []     

       

   #初始化 input_count输入节点数量 hidden_count隐藏节点数量 output_count输出节点数量

   #输入节点数量和特征数量相关输出节点数量和输出类别数量相关

   def init(self, input_count, hidden_count,output_count):

       #初始化输入,隐藏,输出数量

       self.__input_count = input_count + 1

       self.__hidden_count = hidden_count

       self.__output_count = output_count

 

       #初始化输入,隐藏,输出节点的值

       self.__inputs = [1.0] *self.__input_count

       self.__hiddens = [1.0] *self.__hidden_count

       self.__outputs = [1.0] *self.__output_count

 

       #初始化存放一级,二级权重数组给权重赋值随机数一级权重多加一列保存偏移量

       self.__input_weights =[[self.rand(-0.2, 0.2) for i in range(self.__hidden_count)]for j in range(self.__input_count)]

       self.__output_weights =[[self.rand(-2.0, 2.0) for i in range(self.__output_count)]for j inrange(self.__hidden_count)]

 

       #初始化纠正数组一级权重多加一列保存偏移量

       self.__input_correction =self.make_matrix(self.__input_count, self.__hidden_count)

       self.__output_correction =self.make_matrix(self.__hidden_count, self.__output_count)

       

   #构造二维数组   

   def make_matrix(self, row_count,column_count, value=0.0):

       mat = []

       for i in range(row_count):

           mat.append([value] * column_count)

       return mat

 

   #生成随机数

   def rand(self, a, b):

       return (b - a) * random.random() + a

 

   #阶跃函数

   def sigmoid(self, x):

       return 1.0 / (1.0 + math.exp(-x))

 

                   

   #阶跃函数导数

   def sigmoid_derivate(self, x):

       return x * (1 - x)

 

   #训练

   def train(self, trainDataSet, labels):

       leaningOK = False

       for i in range(self.__limit):

           if leaningOK: #无损失,退出,训练完成

               return None

           

           error = 0.0

           for j in range(len(trainDataSet)):

               self.predict(trainDataSet[j])

               error +=self.back_propagate(labels[j])

           logging.debug("当前循环次数为:%d当前损失函数错误值为:%f"%(i,error))

           leaningOK = error <= 0.01

 

   #预测

   def predict(self, inputDataSet):

       #计算输入层

       for i in range(self.__input_count-1):

           self.__inputs[i] = inputDataSet[i]

 

       #计算隐藏层

       for h in range(self.__hidden_count):

           total = 0.0        

           for i inrange(self.__input_count):               

               total += self.__inputs[i] *self.__input_weights[i][h]

           self.__hiddens[h] =self.sigmoid(total)

 

       #计算输出层

       for o in range(self.__output_count):

           total = 0.0

           for h inrange(self.__hidden_count):

               total += self.__hiddens[h] *self.__output_weights[h][o]

           self.__outputs[o] =self.sigmoid(total)

 

       return self.__outputs[:]

 

   #反向传播更新权值

   def back_propagate(self, labels):

       #计算输出层误差

       output_deltas = [0.0] *self.__output_count

       for o in range(self.__output_count):

           error = labels[o] -self.__outputs[o]

           output_deltas[o] =self.sigmoid_derivate(self.__outputs[o]) * error

 

       #计算隐藏层误差

       hidden_deltas = [0.0] *self.__hidden_count

       for h in range(self.__hidden_count):

           error = 0.0

           for o inrange(self.__output_count):             

               error += output_deltas[o] *self.__output_weights[h][o]               

           hidden_deltas[h] =self.sigmoid_derivate(self.__hiddens[h]) * error

 

       #更新输出对应的权值

       for h in range(self.__hidden_count):

           for o inrange(self.__output_count):

               change = output_deltas[o] *self.__hiddens[h]

               self.__output_weights[h][o] +=self.__learn * change + self.__correct * self.__output_correction[h][o]

               self.__output_correction[h][o]= change

 

       #更新输入对应的权值

       for i inrange(self.__input_count):       

           for h inrange(self.__hidden_count):

               change = hidden_deltas[h] *self.__inputs[i]

               self.__input_weights[i][h] +=self.__learn * change + self.__correct * self.__input_correction[i][h]

               self.__input_correction[i][h] =change

 

       #返回错误总量

       error = 0.0

       for o in range(self.__output_count):

           error += 0.5 * (labels[o] -self.__outputs[o]) ** 2

       return error

      

测试源码(MLP_UnitTest.py):

#多层感知器单元测试 20170621gyk

 

from imp import reload

from MLP import *

import logging

import os

 

#测试

def test():

   l_while = True

   dict_function = {'or':test_or,'xor':test_xor}

 

   whilel_while:

       print('输入q退出,输入or实现或功能,输入xor调用异或功能。或直接用命令行调用test_others函数可以实现其他预测')

       type_str = input()

       if type_str == 'q':

           l_while = False

       else:

           dict_function.get(type_str,'nothing')()             

 

#计算或使用或数据集训练完模型,实现或的效果。

def test_or():

       test_common([[1,1],[1,0],[0,1],[0,0]], [[1],[1],[1],[0]], [int(input('第一个参数')),int(input('第二个参数'))])

 

 

#计算异或使用异或数据集训练完模型,实现异或的效果。

def test_xor():

       test_common([[1,1],[1,0],[0,1],[0,0]], [[0],[1],[1],[0]], [int(input('第一个参数')),int(input('第二个参数'))])       

 

#计算通用

def test_common(trainDataSet, labels,inputs):

   mlp = Mlp()

   mlp.init(2,5,1) #隐藏节点数量的计算公式:s=[2*log2(m+n+1)]>=2 s为隐藏节点数,m为输入特征数,n为识别出的类别数

   mlp.train(trainDataSet, labels)

   print('结果为:')

   for i in range(1):

       #print(mlp.predict(inputs)[i])

       print(1 if mlp.predict(inputs)[i]>=0.5 else 0)

 

#训练其他功能,只要输入训练集和待预测数据,就能预测出结果

def test_others(trainDataSet, inputs):

       test_common(trainDataSet, inputs)       

       

if __name__=='__main__':

   #logging.basicConfig(filename = os.path.join(os.getcwd(), 'log.txt'),level = logging.DEBUG)

   test() 

 

 

 

 

 



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章