• 代码视角-神经网络-Python 实现(上)


    说明: 就是巩固一下认识而已, 也是找了篇网上大佬的文章, 看了下写得还行, 抄一抄, 权当编程练习了, 目的成为了, 从代码的角度去认识这些, 莫名其妙的, 让人生畏的, 但其实简单的, 生物学名词 , 至于编码, 毕竟, 基本原理懂了, 剩下的其实就是去复制粘贴了呀.

    我自己其实一直非常抵触 ML 的很多名词, 总结: 将简单概念复杂化, 这些老外搞的名词, 是真的恶心(当然也有可能是翻译过来哈哈) , 像什么, 监督学习, 机器学习, 人工智能, 学习率, 惩罚因子, 神经元, 神经网络, 卷积层, 池化, 全连接, 递归神经网络... 这尼玛, 光听上去就想放弃. 这写代码能跟生物扯上关系? 机器真能是智能呢, 还是智障? ..

    真的是感觉, 是市场的推动, 搞这些莫名其妙的名词, 当我试着去, 以编程的视角来看时, 从数学视角来看时, 其实, 无非就是一些, 加权求和, 求平均, 多元函数求偏导, 最小二乘法, 参数估计, 高斯分布, 贝叶斯... 这些理论的综合应用而已.

    我真的很不理解, 为什么那么多兄弟, 总喜欢把简单的东西复杂化, 故弄玄虚, 当然也许还是我太浅薄, 其实我自己很相信老子说的 为学日溢, 为道日损, 但我现在感觉, 有时候, 也需要 为学日损, 回归到最本真, 最基础的知识中来, 理论和实践要一起来整的.

    抄一抄, 也见证一波, 从代码角度理解神经网络, 入门我感觉还是可以理解的.

    神经元

    其实就是一个计算过程, 输入一个向量, 然后每个分量有一个权值, 然后神经元的值就是, 输入向量 * 权值 再求和, 最后再加上一个偏置即可.

    然后把这个加权求和的值, 传入一个 映射到 [0,1] 的实值的函数 (激活函数) , 这就是输出呀, 这个输出的过程, 也就前向传播 (feedforward). 当然, 叫前馈或者, 正反馈 可能会更准确一点.

    case1

    • 输入: 一个向量 (x = [2, 3]^T)

    • 权重: 分量权重 (w = [0, 1]^T​)

    • 偏置: b=4 (作用是为了防止输出为0)

    • 函数: (f(x) = 1 / (1+e^{-x}))

    则轻易算出:

    该神经元的值为: (w^Tx = (2*0 + 3*1) + 4 = 7)

    经激活函数后值: (f(7) = 1/(1+e^{-7}) = 0.999)

    代码实现 - 神经元

    import numpy as np
    
    
    def sigmoid(x):
        """激活函数, 将输入实值x, 映射到[0-1]之间"""
        return 1 / (1 + np.exp(-x))
    
    
    class Neuron:
        """神经元及正反馈过程"""
    
        def __init__(self, weights, bias):
            self.weights = weights
            self.bias = bias
    
        def feedforward(self, inputs):
            """正反馈"""
            node_value = self.weights.dot(inputs) + self.bias
            return sigmoid(node_value)
    
    
    if __name__ == '__main__':
        arr = np.array([2, 3])
        weights = np.array([0, 1])
        bias = 4
    
        neuron = Neuron(weights, bias)
        print(neuron.feedforward(arr))
        
        
    # out
    0.9990889488055994
    

    神经网络

    就是多个神经元之间有联系了, 还有一些其他的操作等.

    图示:

    输入还是一个向量, 假设是 ([x_1, x_2]^T) 然后呢, 多了一个隐含层, 也是两个神经元 ((h_1, h2)) 和一个输出值 (o), 其输入变成了 (h1, h2) . 这样, 就组成了一个网络.

    case2

    • 输入: 一个向量 (x = [2, 3]^T)
    • 权重: 分量权重 (w = [0, 1]^T)
    • 偏置: b=0
    • 函数: (f(x) = 1 / (1+e^{-x}))

    节点 h1, h2 对应的输入都是 ([x_1, x_2]^T) 因此, h1, h2 的节点值是一样的

    (h_1 = h_2 = f(w^Tx + b))

    (=f(2*0 + 3*1))

    $=f(3) $

    (= 0.952)

    继续往后

    (o_1 = f(w^T, [h1, h2]^T + b))

    (=f(0*h_1 + 1*h_2 + 0))

    (=f(0.952))

    (=0.722)

    此处加了一层后的网络, 输入([2, 3]^T) 时, 输出是 0.722

    当然, 一个神经网络的层数, 每层的神经元数都是可任意的. 学界已经证明了, 理论上, 一个3层(输入层, 隐含层, 输出层) 可以模拟出世界上几乎所有的函数, 只要节点数够多.

    运作的前向逻辑都大致都是一样的: 输入在神经网络中向前传输, 最终得到输出. 而误差呢, 则通过实际值 与预测值 误差, 向后传播, 对应的调整则是, 每个神经元节点的输入权值线条呀.

    一言蔽之神经网络: 正反馈 VS 负反馈

    代码实现-正反馈

    import numpy as np
    
    
    def sigmoid(x):
        """激活函数, 将输入实值x, 映射到[0-1]之间"""
        return 1 / (1 + np.exp(-x))
    
    class Neuron:
        """神经元及正反馈过程"""
        def __init__(self, weights, bias):
            self.weights = weights
            self.bias = bias
    
        def feedforward(self, inputs):
            """正反馈"""
            node_value = self.weights.dot(inputs) + self.bias
            return sigmoid(node_value)
        
    class Network:
        """神经网络"""
        def __init__(self):
            # 定义这两个本地变量, 没人能管到的那种
            weights = np.array([0, 1])
            bias = 0
            self.h1 = Neuron(weights, bias)
            self.h1 = Neuron(weights, bias)
            self.o1 = Neuron(weights, bias)
    
        def feedforward(self, x):
            """正反馈"""
            out_h1 = self.h1.feedforward(x)
            out_h2 = self.h1.feedforward(x)
            # 输出值
            out_o1 = self.o1.feedforward(np.array([out_h1, out_h2]))
            return out_o1
    
    
    if __name__ == '__main__':
        network = Network()
        x = np.array([2, 3])
        print(network.feedforward(x))
    
    # out
    0.7216325609518421
    

    神经网络-训练

    还是来案例吧. 假设有这样的一组原始数据

    姓名 体重 身高 性别 (y)
    youge 66 182 M
    share 63 175 M
    naive 45 166 F
    beyes 98 185 F

    稍微处理一下, 给数据做一个中心化, (易知 (体重_{mean} = 68; 身高_{mean}=177)) 然后 类别变量用 1 表示 M, 0 表示 F

    姓名 体重 身高 性别 (y)
    youge -2 5 1
    share -5 -2 1
    naive -23 -11 0
    beyes 30 8 0

    Loss

    损失部分, 假设这里我们用 参数估计中的 均方误差 MSE 即误差平方的期望: (E[ (参数值 - 真实值)^2])

    (MSE = frac {1}{n} sumlimits_{i=1}^n (y_i - hat y_i)^2)

    n 表示样本数, 这里为 4

    y 表示要预测的变量, 这里是 性别

    训练的约束, 就是使得 MSE 的值尽可能小. -> 求解参数

    MSE 的工作机制, 举个栗子, 假设网络的纵输出是 0, 也就是预测所有的 小伙伴都是 妹子.

    姓名 (y_i) (真实值) (hat y_i) (预测值) ((y_i - hat y_i))
    youge 1 0 1
    share 1 0 1
    naive 0 0 0
    beyes 0 0 0

    (MSE = frac {1}{4} (1 + 1 + 0 + 1) = 0.5)

    代码实现 - MSE

    
    
    def mes_loss(y_true, y_predict):
        """
        计算均方误差
        :param y_true, arr 真实样本值组成的array
        :param y_predict, arr 预测样本值组成的array
        :return: float, 总损失
        """
        return ((y_true - y_predict) ** 2).mean()
    
    
    if __name__ == '__main__':
        y = np.array([1, 1, 0, 0])
        y_hat = np.array([0, 0, 0, 0])
    
        print("MSE: ", mes_loss(y, y_hat))
    
    # out
    MSE:  0.5
    

    好了, 上篇就搬砖到这里吧, 目的是, 如何直观来理解神经网络, 从代码的角度来理解, 真的可能秒懂, 而看图和概念, 反而老是会想到什么 生物学的概念, 莫名其妙.

    然后就是, 反复又反复地 认识 ML 的核心问题, 如何定义模型, 如何衡量损失, 如何优化损失以求解最优模型参数 , 还是蛮好理解的, 其实. 下篇搬砖, 就接着, 后面的 损失优化和参数估计来展开呀.

  • 相关阅读:
    css设置页面内容不能被选中
    bootstrap栅格系统
    MVC框架
    类模板
    c++编译器模板机制剖析
    函数模板与函数重载
    函数模板当参数强化
    泛型编程—函数模板
    用友GRP-u8 注入-RCE漏洞复现
    漏洞代码调试(二):Strtus2-001代码分析调试
  • 原文地址:https://www.cnblogs.com/chenjieyouge/p/12219750.html
Copyright © 2020-2023  润新知