美文网首页机器学习算法实现
机器学习笔记-Python实现感知机(Perceptron)

机器学习笔记-Python实现感知机(Perceptron)

作者: JianlingZhou | 来源:发表于2017-01-30 23:23 被阅读2947次

    寒假在看机器学习这本书,看神经网络这一章的时候开始手动敲一些代码来实现一些基本的神经网络程序。首先介绍一下基本概念。

    神经元

    神经元是神经网络的基本单元,接收多个神经元传递过来的输入信号,然后通过激活函数计算输出信号。

    神经元
    图是机器学习这本书里的。从图里可以看到每个输入信号都有一个权重w,这个权重是动态改变的,我们平时所说的训练神经网络主要就是训练(修正)这个权重w。

    同时每个神经元有一个参数θ,这个θ是阈值,生物意义上,如果输入信号的加权和比阈值高,意味着这个神经元被激活(处于兴奋状态),信号向下一个神经元传递。但是在这里的感知机模型里,θ不过是个公式里的参数罢了。

    感知机(Perceptron)

    感知机本质上就是个两层神经元构成的简单神经网络。一层是输入层,全部是输入神经元,直接把输入信号传递给下一层神经元,第二层就是输出层。

    感知机使用Sigmoid函数作为激活函数:


    使用Python可以用一行代码表示出来,不过得借助math库。

    lambda s: 1.0 / (1 + math.exp(-s))
    

    每次感知机有输出时,内部参数(w)都需要被训练和调整。其训练算法如下:


    学习算法

    学习率介于0和1之间,我们可以手动设置。算法本身很简单。

    接下来就是思考一下代码实现的问题了。

    • 首先我们可以定义一个结点类Node用来表示一个神经元节点,从这个Node类可以进一步派生出输入节点类和输出节点类。
    • 我们可以认为每个节点Node都有一个输入节点列表。对于输入节点,它的输入节点列表就是空集 [] 。
    • 节点Node的默认激活函数为sigmoid函数。
    • 本来输入节点的职能就是把输入信号传递给输出层,不需要激活函数的。但是我们也可以把它视为一个M-P神经元,它的输入信号加权和为0(它本身就是输入层,不存在额外的输入),将它的阈值θ视为它要传递给输出层的数值的相反数。将它的激活函数设置为f(x)=x。就可以等同于一个M-P神经元了。

    代码实现

    完整python代码如下:

    # coding:utf-8
    import math
    
    
    def sigmoid(x):
        return 1.0/(1+math.exp(-x))
    
    
    class Record:# 输入的一条训练数据
        def __init__(self):
            feature_vector = []# 特征向量
            label = None# 标签
            return
    
    
    class Node: # 神经元节点
        def __init__(self):
            self.input_list = []# 输入的神经元列表
            self.activated = False# 这个神经元是否已经计算出输出信号
            self.recent_output = None# 上一次的输出信号
            self.threshold = 0.0# 阈值θ
            self.activation_func = lambda s: 1.0 / (1 + math.exp(-s))  # default func: sigmoid  function
            return
    
        def add_input(self, node):# 添加输入结点
            self.input_list.append([node, 1.0])# [结点对象,权重w] 权重默认为1
            return
    
        def set_threshold(self, th):# 设置阈值θ
            self.threshold = th
            return
    
        def output(self):# 通过激活函数计算输出信号
            sum_ = 0.0
            for p in self.input_list:
                prev_node = p[0]
                sum_ += prev_node.output() * p[1]
            self.recent_output = self.activation_func(sum_ - self.threshold)
            return self.recent_output
    
    
    class InputNode(Node):# 神经元输入节点
        def __init__(self):
            Node.__init__(self)
            self.activation_func = lambda s: s# 注意激活函数是 f(x)=x 
            return
    
        def set_input_val(self, val):
            Node.set_threshold(self, -val)# 输入的结点列表为空,设置阈值为 -val
            return
    
    
    class OutputNode(Node):# 神经元输出节点
        def __init__(self):
            Node.__init__(self)
            self.threshold = 4.0
            return
    
    
    class NeuralNetwork:# 抽象神经网络
        def __init__(self):
            self.eta = 0.5# 学习率η
            self.data_set = []# 输入的数据集,列表内的元素为 Record对象
    
        def set_data_set(self, data_set_):# 设置数据集
            self.data_set = data_set_
            return
    
    
    class SingleLayerNeuralNetwork(NeuralNetwork):# 感知机模型
        def __init__(self):
            NeuralNetwork.__init__(self)
            self.perceptron = OutputNode()# 一个输出结点
            self.input_node_list = []
            self.perceptron.threshold = 4.0
            return
    
        def add_input_node(self):# 添加输入结点
            inode = InputNode()
            self.input_node_list.append(inode)
            self.perceptron.add_input(inode)
            return
    
        def set_input(self, value_list):# 给每个输入结点设置输入数据
            assert len(value_list) == len(self.input_node_list)
            for index in range(0, len(value_list), 1):
                value = value_list[index]
                node = self.input_node_list[index]
                node.set_input_val(value)
            return
    
        def adjust(self, label):# 每条记录训练后,自动调整内部参数w
            for prev_node_pair in self.perceptron.input_list:
                delta_weight = self.eta * (label - self.perceptron.recent_output) * prev_node_pair[0].recent_output
                origin_weight = prev_node_pair[1]
                prev_node_pair[1] = origin_weight + delta_weight
            return
    
        def run(self):# 开始跑训练集
            for data in self.data_set:# 遍历训练集
                self.set_input(data.feature_vector)
                record_ = self.perceptron.output()
                print record_
                self.adjust(data.label)# 每条记录训练后,自动调整内部参数w
            return
    
    
    

    现在让这个感知机来解决或问题(x∨𝑦),假设1表示真,0表示假,那么1和1进行或运算结果就是1,1和0进行或运算结果就是1,0和0进行或运算结果就是0。
    输入数据如下(一条记录保存为一行,第一个和第二个数字是输入,第3个数字是标签。数据假设保存在E://data/ann/train_0.txt):

    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    1 0 1
    0 0 0
    1 0 1
    1 1 1
    1 1 1
    0 1 1
    1 0 1
    0 0 0
    

    有了以上代码和数据后,用以下Python代码开始跑训练集:

    
    file_handler = open('E://data/ann/train_0.txt')
    data_set = []
    line = file_handler.readline()
    while line:# 遍历文件里的数据
        record = Record()
        item_feature_vector = []
        str_list = line.split()
        item_feature_vector.append(float(str_list[0]))
        item_feature_vector.append(float(str_list[1]))
    
        record.feature_vector = item_feature_vector
        record.label = float(str_list[2])
        data_set.append(record)
        line = file_handler.readline()
    print len(data_set)
    
    ann = SingleLayerNeuralNetwork()# 实例化感知机对象
    ann.add_input_node()
    ann.add_input_node()# 添加两个输入结点
    ann.set_data_set(data_set)# 设置数据集
    ann.run()# 等待结果
    

    我跑的结果如下图所示:

    一开始的输出

    可以看到一开始的输出有点离谱,但是随着数据的增多输出变得越来越像与函数的输出了。


    结果

    实际上感知机能解决的问题很少,为了解决复杂问题,需要引入多层神经网络和新的学习算法:BP误差逆传播算法

    相关文章

      网友评论

      • 3e9117e7f3a4:感谢你的分享,对感知机有两个问题想请教一下:
        1、adjust函数在调整参数时,是不是没有考虑到阈值\theta
        2、能否解释一下感知机的权重调整规则?神经网络中不是要先定义损失函数么,感知机的损失函数是什么?谢谢!
        JianlingZhou:1、output() 函数中设置了recent_output,设置的时候减去了threshold,也就是阈值θ
        2、权重调整规则见 机器学习.周志华 P.99

      本文标题:机器学习笔记-Python实现感知机(Perceptron)

      本文链接:https://www.haomeiwen.com/subject/ippwbttx.html