补充(代码)-入门神经网络-Python 实现(下)
回顾
紧接着上篇, 整到了, MES的公式和代码的实现.
n 表示样本数, 这里为 4
y 表示要预测的变量, 这里是 性别
训练的约束, 就是使得 MSE 的值尽可能小. -> 求解参数
MSE 的工作机制, 举个栗子, 假设网络的纵输出是 0, 也就是预测所有的 小伙伴都是 妹子.
姓名 | (真实值) | (预测值) | |
---|---|---|---|
youge | 1 | 0 | 1 |
share | 1 | 0 | 1 |
naive | 0 | 0 | 0 |
beyes | 0 | 0 | 0 |
BP算法本质 - 求导链式法则
现在继续...
始终要明确我们的目标: 最小化神经网络的损失 这个损失呢, 本质也就是一个关于 权重和偏置 的函数
如图所示:
则本例的损失函数可以这样参数化表示为:
现在来考虑对 w 进行优化, 假设要优化 (即当 变化时, L 会如何变化), 也就是:
为了简化一波问题, 假设数据集中就只有一个兄弟.
姓名 | () | ||
---|---|---|---|
youge | 1 | 0 | 1 |
则此时的 MSE =
要计算 根据网络的 反向 方向 (输出 -> 输入), 对应选取相应的中间变量, 这样能求出来呀. 根据求导链式法则:
由本例数据, 已知 , 上面公式的第一部分就可以求出来了:
然后是 第二部分 观察图中的相关变量, 可看到 分别表示该神经元的输出, 即:
继续向后传播....
而我们关心的是 , 看图中的线路就可知, w1 跟 h2 是没有关系的, 只跟 h1有关, 于是, 再来一波 求导链式法则
同样套路, 第一部分
这个其实就 看作 f(x), 里面不论多少项, 都是该 函数的自变量取值而已呀.
对 第二部分 也是一样滴处理
终于走到输入值啦, 本例这里的 x_1 是身高, x_2 是体重. 这里的 f(x) 就是咱的 激活函数 (映射实值到0-1)
之前推导 逻辑回归的时候, 也是用的这个函数哦, 当时有个技巧点是, 其求导为:
利用 分式 求导法则:
这个结果在推导逻辑回归的时候, 非常重要的哦, 求一阶导和二阶导都要用到
小结上边的一波操作, 其实就是一个 求导的链式法则:
从网络的方向上来看呢, 是从 output -> input 这样的 反向 误差传递, 这其实就是咱平时说的 BP算法, 而核心就是求导的链式法则而已呀.
所以嘛, 神经网络很多名词, 就是为了唬人, 当你扒开一看, 哦哦, 原来都只是用到一些 基础的数学知识而已
case1: 计算偏导数(Link Rule)
输入(已中心化):
姓名 | 体重 | 身高 | 性别 (y) |
---|---|---|---|
youge | -2 | 5 | 1 |
输出比较
姓名 | () | ||
---|---|---|---|
youge | 1 | 0 | 1 |
同样, 为计算更加方便, 假设所有的 权重 为1, 所有的偏置为 0
继续,
继续,
即本例的网络输出是 比较有倾向性的哦, 计算来算一下 应用上面的结论.
同样分解为 3个部分:
继续...
继续...
因此
意义: 随着 w_1 的增加, 损失 L 会随着减少.
随机梯度减小(SGD)
本质就是更新参数 w, 沿着 梯度的反方向微调一个步长, 直到算法收敛 或者 是随机选择一个样本, 每次做更迭,, 求解出最优的权重参数向量 w
这个 也称为 学习率, 也就是步长呗, 或者速率都可以的, 理解上面这句话是最关键的.
为啥是沿着 梯度的反向方, 这个涉及方向导数这一块的, 自己看大一的高数吧, 懒得解释了.
- 偏导数 大于0, 则说明要将 w_1 调小, 使得 L 变小
- 偏导数 小于0, 则说明要将 w_1 调大, 使得 L 变小
因为网络中有 多个 w_i 嘛, 如果我们对每一个 w_i 都 进行这样的优化, 则整个网络的损失则会不断下降, 也就意味着网络的预测性能在不断地上升.
训练过程
-
从数据集中随机选取一个样本, 用 SGD 进行优化, (每次只针对一个样本进行优化)
-
计算每个权重 w_i 和 偏置 bias, ( 计算 ) 等
-
更新权重和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 哦, 绝大部分都不是我自己的代码, 目的是做个笔记参考而已, 参考思路而非真正用这样而代码做生产.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通