如何实现一个感知器
对于一个进入深度学习的进步菜鸟来说,刚开始的庞大的知识库让我觉得无所适从,但是经过后期的调整和规划,已经决定把从前认为麻烦的事情都要尝试一遍,挖掘一下自身的潜能,送给想要学习或者正在进入学习状态的同志们一句话:无论遇到多大困难,都要平心静气的继续下去,就像此时的我,已经在准备写第二遍(之前第一遍的内容丢失)。相信坚持就是胜利!话不多说,进入正题。
在实现感知器之前,一定要有耐心的看一下感知器的基础理论(两遍最佳,第二遍往往有最深的感悟),推荐官方文档:https://www.zybuluo.com/hanbingtao/note/433855
简单的来说感知器:一个二元分类器,就是把输入向量映射到输出值f(x)(二元的值0或1)上。
当然,对于怎么实现一个感知器,前提是需要有一个非常清楚的思想(编程最重要的就是编程思想,写代码的能力可以慢慢的练习),没有思想无法往下进行。
在构建一个模型之前,我先把涉及到的公式全部写一下(虽然我觉得写起来很麻烦,但是为了清楚的理解每一步需要做什么?为什么要这么做?尤其是后边提到的预测函数和更新权重函数都是要联系公式的)
- y=f(Wx+b) W是权值 b是偏置量(bias)→其实可写成y=f(WTx) 因为可以b可以处理成W0*x0
- Wi←Wi+ΔWi
- b←b+Δb
- ΔWi=η(t-y)xi η是学习率(learning-rate),t是真实值(label),y是预测值(output)
- Δb=η(t-y)
现在要进入程序的大门:
- 初始化一个感知器:输入参数的个数和激活函数。个人觉得权重向量的初始化应该在一个对称范围内随机取值,比如这样的形式[-1,1],而他的代码中是将权重向量初始化为0,个人觉得这一步不太恰当(后续请教一下师兄,再来更新)
- 训练函数:输入训练数据,一组向量、和每个向量对应的label值、训练轮数、学习率。
- 迭代函数:一次迭代把所有的训练数据(input_vec,label)过一遍。在感知器规则下更新权重。
- 预测函数:输入向量,输出感知结果,也就是y
- 更新权重函数的权重:这一步就用到了 湖蓝色标记的公式 。可结合代码和公式加以理解。
涉及到的map()函数的详细用法,请结合官方文档:http://www.runoob.com/python/python-func-map.html
涉及到的lambda匿名函数的用法,结合CSDN上的文档:https://blog.csdn.net/u010602026/article/details/67662004
涉及到的zip()函数的用法,结合 http://www.cnblogs.com/frydsh/archive/2012/07/10/2585370.html
1 #!/usr/bin/env python 2 # -*- coding: UTF-8 -*- 3 4 import functools as fc 5 6 7 class Perceptron(object): 8 def __init__(self, input_num): 9 ''' 10 初始化感知器,设置输入参数的个数,以及激活函数。 11 激活函数的类型为double -> double 12 ''' 13 #self.activator = activator 14 # 权重向量初始化为0 15 self.weights = [0.0 for _ in range(input_num)] 16 # 偏置项初始化为0 17 self.bias = 0.0 18 19 def __str__(self): 20 ''' 21 打印学习到的权重、偏置项 22 ''' 23 return 'weights\t:%s\nbias\t:%f\n' % (self.weights, self.bias) 24 25 26 def predict(self, input_vec): 27 ''' 28 输入向量,输出感知器的计算结果 29 ''' 30 # 把input_vec[x1,x2,x3...]和weights[w1,w2,w3,...]打包在一起 31 # # 变成[(x1,w1),(x2,w2),(x3,w3),...] 32 # 然后利用map函数计算[x1*w1, x2*w2, x3*w3] 33 # 最后利用reduce求和 34 value = fc.reduce(lambda a, b: a + b, map(lambda x, w: x * w, input_vec, self.weights), 0) + self.bias 35 return self.f(value)#f是用于计算的激活函数 36 37 def train(self, input_vecs, labels, iteration, rate): 38 ''' 39 输入训练数据:一组向量、与每个向量对应的label;以及训练轮数、学习率 40 ''' 41 for i in range(iteration): 42 self._one_iteration(input_vecs, labels, rate) 43 44 def f(self, x): 45 ''' 46 定义激活函数f 47 ''' 48 return 1 if x > 0 else 0 49 50 def _one_iteration(self, input_vecs, labels, rate): 51 ''' 52 一次迭代,把所有的训练数据过一遍 53 ''' 54 # 把输入和输出打包在一起,成为样本的列表[(input_vec, label), ...] 55 # 而每个训练样本是(input_vec, label) 56 samples = zip(input_vecs, labels) 57 # 对每个样本,按照感知器规则更新权重 58 for (input_vec, label) in samples: 59 # 计算感知器在当前权重下的输出 60 output = self.predict(input_vec) 61 # 更新权重 62 self._update_weights(input_vec, output, label, rate) 63 64 def _update_weights(self, input_vec, output, label, rate): 65 ''' 66 按照感知器规则更新权重 67 ''' 68 # 把input_vec[x1,x2,x3,...]和weights[w1,w2,w3,...]打包在一起 69 # 变成[(x1,w1),(x2,w2),(x3,w3),...] 70 # 然后利用感知器规则更新权重 71 delta = label - output 72 weight_iter = map( 73 lambda x, w: w + rate * delta * x, 74 input_vec, self.weights) 75 # 更新bias 76 self.weights = [i for i in weight_iter] 77 self.bias += rate * delta 78 79 80 81 82 83 def get_training_dataset(): 84 ''' 85 基于and真值表构建训练数据 86 ''' 87 # 构建训练数据 88 # 输入向量列表 89 input_vecs = [[1,1], [0,0], [1,0], [0,1]] 90 # 期望的输出列表,注意要与输入一一对应 91 # [1,1] -> 1, [0,0] -> 0, [1,0] -> 0, [0,1] -> 0 92 labels = [1, 0, 0, 0] 93 return input_vecs, labels 94 95 96 def train_and_perceptron(): 97 ''' 98 使用and真值表训练感知器 99 ''' 100 # 创建感知器,输入参数个数为2(因为and是二元函数),激活函数为f 101 p = Perceptron(2) 102 # 训练,迭代10轮, 学习速率为0.1 103 input_vecs, labels = get_training_dataset() 104 p.train(input_vecs, labels, 10, 0.1) 105 #返回训练好的感知器+ 106 107 return p 108 109 110 if __name__ == '__main__': 111 # 训练and感知器 112 and_perception = train_and_perceptron() 113 # 打印训练获得的权重 114 print(and_perception) 115 # 测试 116 print('1 and 1 = %d' % and_perception.predict([1, 1])) 117 print('0 and 0 = %d' % and_perception.predict([0, 0])) 118 print('1 and 0 = %d' % and_perception.predict([1, 0])) 119 print('0 and 1 = %d' % and_perception.predict([0, 1]))
后续新增知识可以返回补充。