回顾

紧接着上篇, 整到了, MES的公式和代码的实现.

\(MSE = \frac {1}{n} \sum\limits_{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\)

BP算法本质 - 求导链式法则

现在继续...

始终要明确我们的目标: 最小化神经网络的损失 这个损失呢, 本质也就是一个关于 权重和偏置 的函数

如图所示:

则本例的损失函数可以这样参数化表示为:

\(L(w_1, w_2, w_3, w_4, w_5, w_6, b1, b_2, b_3)\)

现在来考虑对 w 进行优化, 假设要优化 \(w_1\) (即当 \(w_1\) 变化时, L 会如何变化), 也就是: \(\frac {\partial L}{\partial w_1}\)

为了简化一波问题, 假设数据集中就只有一个兄弟.

姓名 \(y_1\) \(\hat y_1\) (\(y_1 -\hat y_1\))
youge 1 0 1

则此时的 MSE = \((y_1 -\hat y_1)^2 = (1- \hat y_1)^2\)

要计算 \(\frac {\partial L}{\partial w_1}\) 根据网络的 反向 方向 (输出 -> 输入), 对应选取相应的中间变量, 这样能求出来呀. 根据求导链式法则:

\(\frac {\partial L}{\partial w_1} = \frac {\partial L}{\partial \hat y_1} * \frac {\partial \hat y_1}{\partial w_1}\)

由本例数据, 已知 \(L = (1- \hat y_1)^2\) , 上面公式的第一部分就可以求出来了:

\(\frac {\partial L} {\partial y_1} = \frac {\partial (1- \hat y_1)^2} {\partial y_1} = -2(1- \hat y_1)\)

然后是 第二部分 \(\frac {\partial \hat y_1}{\partial w_1}\) 观察图中的相关变量, 可看到 \(h_1, h_2, o_1\) 分别表示该神经元的输出, 即:

\(\hat y_1 = o_1 = f(w_5 h_1 + w_6 h_2 + b_3)\)

继续向后传播....

而我们关心的是 \(w_1\) , 看图中的线路就可知, w1 跟 h2 是没有关系的, 只跟 h1有关, 于是, 再来一波 求导链式法则

$\frac {\partial \hat y_1}{\partial w_1} = \frac {\partial \hat y_1} {\partial h_1} * \frac {\partial h_1}{\partial w_1} $

同样套路, 第一部分

\(\frac {\partial \hat y_1} {\partial h_1} = \frac {f(w_5h_1 + w_6h2 + b_3)} {\partial h_1} = w_5 * [f'(w_5h_1 + w_6h2+b_3)]\)

\(f'(w_5h_1 + w_6h2+b_3)\) 这个其实就 看作 f(x), 里面不论多少项, 都是该 函数的自变量取值而已呀.

第二部分 也是一样滴处理

$\frac {\partial h_1}{\partial w_1} = \frac {f(w_1 x_1 + w_2 x_2 + b_1)} {\partial w_1} = x_1 * [f'(w_1x_1 +w_2 x_2 + b_1)] $

终于走到输入值啦, 本例这里的 x_1 是身高, x_2 是体重. 这里的 f(x) 就是咱的 激活函数 (映射实值到0-1)

\(f(x) = \frac {1}{1+e^{-x}}\)

之前推导 逻辑回归的时候, 也是用的这个函数哦, 当时有个技巧点是, 其求导为: \(f(x)' = f(x)(1-f(x))\)

利用 分式 求导法则:

\(f(x)' = \frac {0 - (-e^{-x)}}{(1+e^{-x})^2}\)

\(= \frac {1}{1+e^{-x}} * \frac {e^{-x}}{1+e^{-x}}\)

\(=f(x)(1-f(x))\)

这个结果在推导逻辑回归的时候, 非常重要的哦, 求一阶导和二阶导都要用到

小结上边的一波操作, 其实就是一个 求导的链式法则:

\(\frac {\partial L}{\partial w_1} = \frac {\partial L}{\partial \hat y_1} * \frac {\partial \hat y_1}{\partial h_1} * \frac {\partial h_1}{\partial w_1}\)

从网络的方向上来看呢, 是从 output -> input 这样的 反向 误差传递, 这其实就是咱平时说的 BP算法, 而核心就是求导的链式法则而已呀.

所以嘛, 神经网络很多名词, 就是为了唬人, 当你扒开一看, 哦哦, 原来都只是用到一些 基础的数学知识而已

case1: 计算偏导数(Link Rule)

输入(已中心化):

姓名 体重 身高 性别 (y)
youge -2 5 1

输出比较

姓名 \(y_i\) \(\hat y_1\) (\(y_1 -\hat y_1\))
youge 1 0 1

同样, 为计算更加方便, 假设所有的 权重 为1, 所有的偏置为 0

\(h_1 = f(w_1 x_1 + w_2 x_2 + b_1)\)

\(= f(-2 + 5 + 0)\)

\(=f(3) = 0.952\)

继续,

\(h_2 = f(w_3x_1 + w_4 x_2 + b_2)\)

\(= f(-2 + 5 + 0) = h_1 = 0.952\)

继续,

\(o_1 = f(w_5h_1 + w_6h_2 + b3)\)

\(=f(0.952 + 0.952 + 0) = 0.871\)

即本例的网络输出是 \(\hat y_1 = 0.871\) 比较有倾向性的哦, 计算来算一下 \(\frac {\partial L}{\partial w_1}\) 应用上面的结论.

\(\frac {\partial L}{\partial w_1} = \frac {\partial L}{\partial \hat y_1} * \frac {\partial \hat y_1}{\partial h_1} * \frac {\partial h_1}{\partial w_1}\)

同样分解为 3个部分:

\(\frac {\partial L}{\partial \hat y_1} = -2(1- \hat y_1)\)

\(= -2(1-0.871)\)

\(=-0.258\)

继续...

$ \frac {\partial \hat y_1}{\partial h_1} = \frac {f(w_5h_1 + w_6h2 + b_3)} {\partial h_1} = x_1 * [f'(w_5h_1 + w_6h2+b_3)]$

\(=(-2) * f'(0.952 +0.952+0)\)

\(=(-2) * [f(1.904) \ f(1-1.904)]\)

\(= -0.502\)

继续...

\(\frac {\partial h_1}{\partial w_1} = \frac {f(w_1 x_1 + w_2 x_2 + b_1)} {\partial w_1} = x_1 * [f'(w_1x_1 +w_2 x_2 + b_1)]\)

\(=(-2) * f'(-2 + 5 + 0)\)

\(= -2 *f'(3)\)

\(=-2 * f(3) \ f(1-3)\)

\(=-0.227\)

因此

\(\frac {\partial L}{\partial w_1} = (-0.258) * (-0.502) * (-0.227)\)

\(=-0.029\)

意义: 随着 w_1 的增加, 损失 L 会随着减少.

随机梯度减小(SGD)

本质就是更新参数 w, 沿着 梯度的反方向微调一个步长, 直到算法收敛 或者 是随机选择一个样本, 每次做更迭,, 求解出最优的权重参数向量 w

\(w \leftarrow w_1 - \alpha \ \frac {\partial L}{\partial w_1}\)

这个 \(\alpha\) 也称为 学习率, 也就是步长呗, 或者速率都可以的, 理解上面这句话是最关键的.

为啥是沿着 梯度的反向方, 这个涉及方向导数这一块的, 自己看大一的高数吧, 懒得解释了.

  • \(\frac {\partial L}{\partial w_1}\) 偏导数 大于0, 则说明要将 w_1 调小, 使得 L 变小
  • \(\frac {\partial L}{\partial w_1}\) 偏导数 小于0, 则说明要将 w_1 调大, 使得 L 变小

因为网络中有 多个 w_i 嘛, 如果我们对每一个 w_i 都 进行这样的优化, 则整个网络的损失则会不断下降, 也就意味着网络的预测性能在不断地上升.

训练过程

  • 从数据集中随机选取一个样本, 用 SGD 进行优化, (每次只针对一个样本进行优化)

  • 计算每个权重 w_i 和 偏置 bias, ( 计算 \(\frac {\partial L}{\partial w_1}, \frac {\partial L}{\partial w_2} ... b_1, b_2...\)) 等

  • 更新权重和bias

  • 重复 第一步 .... 直到将所有的样本遍历完

代码实现 - 完整的神经网络

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

从网上抄的代码, 这个难度不大代码, 就懒得写了, 学会抄, 和改, 我感觉是提升工作能力的必要能力.

import numpy as np

class Network:
def __init__(self):
# 本例的权重w
self.w1 = np.random.normal()
self.w2 = np.random.normal()
self.w3 = np.random.normal()
self.w4 = np.random.normal()
self.w5 = np.random.normal()
self.w6 = np.random.normal() # 偏置 bias
self.b1 = np.random.normal()
self.b2 = np.random.normal()
self.b3 = np.random.normal() def sigmoid(self, x):
"""激活函数, 映射一个实值到 [0,1]"""
return 1 / (1 + np.exp(-x)) def der_of_sigmoid(self, x):
"""激活函数的导数"""
f = self.sigmoid # 地址引用
return f(x) * (1 - f(x)) @staticmethod
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() def feedforward(self, arr):
"""前向算法, arr是一个2个特征的数组"""
h1 = self.sigmoid(self.w1 * arr[0] + self.w2 * arr[1] + self.b1)
h2 = self.sigmoid(self.w3 * arr[0] + self.w4 * arr[1] + self.b2)
o1 = self.sigmoid(self.w5 * h1 + self.w6 * h2 + self.b3) def train(self, data, all_y_true):
"""根据训练数据, 求解参数"""
learn_rate = 0.1
max_iter = 1000 for i in range(max_iter):
for x, y_true in zip(data, all_y_true):
sum_h1 = self.w1 * x[0] + self.w2 * x[1] + self.b1
h1 = self.sigmoid(sum_h1) sum_h2 = self.w3 * x[0] + self.w4 * x[1] + self.b2
h2 = self.sigmoid(sum_h2) sum_o1 = self.w5 * h1 + self.w6 * h2 + self.b3
o1 = self.sigmoid(sum_o1) y_pred = o1
d_L_d_ypred = -2 * (y_true - y_pred) # o1
d_ypred_d_w5 = h1 * self.der_of_sigmoid(sum_o1)
d_ypred_d_w6 = h2 * self.der_of_sigmoid(sum_o1)
d_ypred_d_b3 = self.der_of_sigmoid(sum_o1) d_ypred_d_h1 = self.w5 * self.der_of_sigmoid(sum_o1)
d_ypred_d_h2 = self.w6 * self.der_of_sigmoid(sum_o1) # h1
d_h1_d_w1 = x[0] * self.der_of_sigmoid(sum_h1)
d_h1_d_w2 = x[1] * self.der_of_sigmoid(sum_h1)
d_h1_d_b1 = self.der_of_sigmoid(sum_h1) # h2
d_h1_d_w3 = x[0] * self.der_of_sigmoid(sum_h2)
d_h1_d_w4 = x[1] * self.der_of_sigmoid(sum_h2)
d_h1_d_b2 = self.der_of_sigmoid(sum_h2) # 应用梯度下降, 更新 权重值 和 bias
# h1
self.w1 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_h1_d_w1
self.w2 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_h1_d_w2
self.b1 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_h1_d_b1 # h2
self.w3 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_h1_d_w3
self.w4 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_h1_d_w4
self.b2 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_h1_d_b2 # o1
self.w5 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_ypred_d_w5
self.w6 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_ypred_d_w6
self.b3 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_ypred_d_b3 # 计算总的损失 Loss
if i % 10 == 0:
y_preds = np.apply_along_axis(self.feedforward, 1, data)
loss = self.mes_loss(all_y_true, y_preds) print("Epoch %d loss: %.3f" % (i, loss)) if __name__ == '__main__':
# test
data = np.array([
[-2, 5],
[-5, -2],
[-23, -11],
[30, 8]
]) all_y_trues = np.array([1, 1, 0, 0])
# 训练神经网络
neework = Network()
neework.train(data, all_y_trues)

没有debug 哦, 绝大部分都不是我自己的代码, 目的是做个笔记参考而已, 参考思路而非真正用这样而代码做生产.

补充(代码)-入门神经网络-Python 实现(下)的更多相关文章

  1. 使用2to3将代码移植到Python 3-转

    http://m.blog.csdn.net/blog/merryken/9104199# ❝ Life is pleasant. Death is peaceful. It’s the transi ...

  2. Python Tensorflow下的Word2Vec代码解释

    前言: 作为一个深度学习的重度狂热者,在学习了各项理论后一直想通过项目练手来学习深度学习的框架以及结构用在实战中的知识.心愿是好的,但机会却不好找.最近刚好有个项目,借此机会练手的过程中,我发现其实各 ...

  3. Python 入门之Python基础数据类型及其方法

    Python 入门之Python基础数据类型 1. 整型:int 用于计算,用于比较 (在赋值的时候先执行等号右边的内容) 1.1 整数的加 a = 10 b = 20 print(a + b) 结果 ...

  4. Python 入门之 Python三大器 之 装饰器

    Python 入门之 Python三大器 之 装饰器 1.开放封闭原则: (1)代码扩展进行开放 ​ 任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改.所以我们必须允许代 ...

  5. Python 入门之Python基础知识

    Python 入门之Python基础知识 1.变量 (1)变量就是把程序运行的中间结果临时存在内存中,以便后续代码使用 (2)变量的作用: 昵称,就是代指内存中某个地址中的内容 a = 123 变量名 ...

  6. Python环境下NIPIR(ICTCLAS2014)中文分词系统使用攻略

    一.安装 官方链接:http://pynlpir.readthedocs.org/en/latest/installation.html 官方网页中介绍了几种安装方法,大家根据个人需要,自行参考!我采 ...

  7. ytu 2463:给小鼠补充代码(DFS 深度优先搜索)

    2463: 给小鼠补充代码 Time Limit: 2 Sec  Memory Limit: 64 MBSubmit: 5  Solved: 2[Submit][Status][Web Board] ...

  8. Python的下划线_

    1.单下划线(_) 通常情况下,单下划线(_)会在以下3种场景中使用: 1.1 在解释器中: 在这种情况下,"_"代表交互式解释器会话中上一条执行的语句的结果.这种用法首先被标准C ...

  9. python环境下实现OrangePi Zero寄存器访问及GPIO控制

    最近入手OrangePi Zero一块,程序上需要使用板子上自带的LED灯,在网上一查,不得不说OPi的支持跟树莓派无法相比.自己摸索了一下,实现简单的GPIO控制方法,作者的Zero安装的是Armb ...

  10. 在 C 代码中嵌入 Python 语句或使用 Python 模块 (Visual Studio 2013 环境设置)

    1) 新建一个 内嵌 Python 语句的 C 代码, // This is a test for check insert the Python statements or module in C. ...

随机推荐

  1. 分享一个 Windows 下的透明锁屏工具【开源】

    透明锁屏 担心展示内容时被误操作打断? 害怕离开后忘记锁屏导致隐私泄露? 厌倦了千篇一律的系统锁屏界面? 透明锁屏 了解一下. 功能特点 告别误操作:锁屏状态下,屏幕内容依然可见,视频播放.PPT 演 ...

  2. [MQ] Kafka

    概述: Kafka 安装指南 安装 on Windows Step1 安装 JDK JDK 安装后: 在"系统变量"中,找到 JAVA_HOME,如果没有则新建,将其值设置为 JD ...

  3. 基于GD32的简易示波器

    基于GD32的简易示波器项目学习 根据立创训练营项目:[简易数字示波器设计(入门版) - 立创开源硬件平台(https://oshwhub.com/course-examples/yi-qi-yi-b ...

  4. STM32的SYSTICK 定时器(系统滴答定时器)

    什么是SysTick? 这是一个24位的系统节拍定时器system tick timer,SysTick,具有自动重载和溢出中断功能,所有基于Cortex_M3处理器的微控制器都可以由这个定时器获得一 ...

  5. vue--Element-UI Table 表格指定列添加点击事件

    最近使用了Element-UI中的Table表格,因为需求需要在指定的列点击跳转,所以必须添加个点击事件,我这里是弹框展示table再点击跳转的,如图所示: 下面是我实现具体的代码(只是代码的一部分, ...

  6. vue中的find()函数的用法与扩展用法

    find函数基本格式:let obj=this.list.find(item=>item.code===val)首先在这里我们要知道的两个基础知识一.find是一个查找函数.二.箭头函数find ...

  7. 使用 kubeadm 创建高可用 Kubernetes 及外部 etcd 集群

    博客链接:使用 kubeadm 创建高可用 Kubernetes 及外部 etcd 集群 前言 Kubernetes 的官方中文文档内容全面,表达清晰,有大量示例和解析 无论任何情况下都推荐先花几个小 ...

  8. composer 2 升级操作

    update composer composer self-update // or sudo composer self-update 回滚到版本1 composer self-update --r ...

  9. 微信小程序分包体积优化建议

    代码包体积优化 启动性能优化最直接的手段是降低代码包大小,代码包大小直接影响了下载耗时,影响用户启动小程序时的体验. 开发者可以采取以下手段优化代码包体积: 1. 合理使用分包加载 推荐所有小程序使用 ...

  10. vue 判断某个时间小于当前时间

    如下 new Date().getTime() 获取当前时间(毫秒) 我需要对比的时间为秒,所以需要除于1000 <div v-if="scope.row.created_at < ...