机器学习-感知器学习算法
Python语言实现
初学者整理,参考资料均来源于网上
一、 概述
感知器是由美国计算机科学家罗森布拉特(Rosenblatt)于1957年提出的。感知器可谓是最早的人工神经网络。
感知器分单层感知器和多层感知器。
二、 单层感知器(Single Layer Perceptron)
l 简介
单层感知器是一个具有一层神经元、采用阈值激活函数的前向网络。通过对网络权值的训练,可以使感知器对一组输人矢量的响应达到元素为0或1的目标输出,从而实现对输人矢量分类的目的。
单层感知器是一个简单的线性二分类器,它保存着输入权重,根据输入和内置的函数计算输出.人工神经网络中的单个神经元。
单层感知器可以计算逻辑或,逻辑与,逻辑非等运算,但是不能计算异或。因为异或不是平面线性可分的,在多层感知器中解决。
l 模型
单层感知器只有输入和输出,输入和输出直接相连,多个输入一个输出。
模型为每个输入定义为X,输入到输出的权重定义为w,所有的输入和权重的乘积和为输出的值,对于这个乘积和做如下处理,如果乘机和大于临界值(一般是0),输入端就取1;如果小于临界值,就取-1。
l 工作原理
单层感知器可将外部输入分为两类。当感知器的输出为+1时,输入属于L1类,当感知器输出-1时,输入属于L2类,从而实现两类目标的识别。在二维空间,单层感知器进行模式识别的判决超平面由下式决定:。
对于只有两个输入的判别边界是直线(),选择合适的学习算法可训练出满意的w1和w2。将样本中两类数据用直线分开。如下图:
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把结果转换为1或0
#由于默认第一个输入为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,然后循环训练集,读取每一条训练集数据,进行预测,然后把预测结果和实际结果进行对比,如果一样就用下一条训练。如果和实际结果不相等。就使用随机梯度下降算法重新计算权重。
#然后重新训练,直到所有训练数据的预测结果和实际结果一直,则训练结束。
#训练的结果其实就是在平面上找一条直线,该直线能把两类的数据点分割在直线的两边。如果有的点是错误的,就变化w1和w2来旋转直线,变化w0来移动直线找到最佳位置。训练速率太大会找不到最佳位置,
#训练速率太小,训练速度就太慢。期望值和实际值的差值的正负决定了旋转和移动的方向。
#l_rate为学习速率,一般在0~0.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 Propagation,BP)是误差反向传播的简称,这是一种用来训练人工神经网络的常见算法,通常与最优化方法(如梯度下降法)结合使用.多层感知器就是使用BP算法进行训练的。
l 模型
l 工作原理
由前面介绍看到,单个感知器能够完成线性可分数据的分类问题,是一种最简单的可以“学习”的机器。但他无法解决非线性问题。比如下图中的XOR问题:即(1,1)(-1,-1)属于同一类,而(1,-1)(-1,1)属于第二类的问题,不能由单个感知器正确分类。
单个感知器虽然无法解决异或问题,但却可以通过将多个感知器组合,实现复杂空间的分割。如下图:
将两层感知器按照一定的结构和系数进行组合,第一层感知器实现两个线性分类器,把特征空间分割,而在这两个感知器的输出之上再加一层感知器,就可以实现异或运算。
也就是,由多个感知器组合:
来实现非线性分类面,其中θ(·)表示阶跃函数或符号函数。
l 学习算法
经典的BP神经网络通常由三层组成:输入层,隐含层与输出层.通常输入层神经元的个数与特征数相关,输出层的个数与类别数相同,隐含层的层数与神经元数均可以自定义。
每个隐含层和输出层神经元输出与输入的函数关系为:
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是一个特殊的函数用于将任意实数映射到(0,1)区间.
上文中的sigmod函数称为神经元的激励函数(activation function),除了sigmod函数11+e −I l 11+e−Il外,常用还有tanh和ReLU函数.
我们用一个完成训练的神经网络处理回归问题,每个样本拥有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
其中λ λ是一个称为学习率的参数,一般在(0,0.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()