热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

前向传播实战(手写梯度下降与前向计算,不做调参侠)

前向传播实战1、简介2、前向传播实战2.1导入依赖2.2加载数据集2.3创建每个非线性层的W和b参数2.4前向计算2.5自动梯度与梯度更新2.6完整代码3、总结1、简介我们这里使用

前向传播实战

  • 1、简介
  • 2、前向传播实战
    • 2.1 导入依赖
    • 2.2 加载数据集
    • 2.3 创建每个非线性层的W和b参数
    • 2.4 前向计算
    • 2.5 自动梯度与梯度更新
    • 2.6 完整代码
  • 3、总结


1、简介

  我们这里使用张量的基本操作去完成三层神经网络的实现:
out=ReLU(ReLu(ReLU(X@W1+b1)@W2+b2)@W3+b3)out=ReLU(ReLu(ReLU(X@W_1+b_1)@W_2+b_2)@W_3+b3) out=ReLU(ReLu(ReLU(X@W1+b1)@W2+b2)@W3+b3)
  采用的数据集是MNIST手写数字图片数据集,输入节点数为784,第一层的输出节点数是256,第二层的输出节点数是128,第三层的输出节点数是10,也就是当前样本属于10个类别的概率。

2、前向传播实战

2.1 导入依赖

import os
import matplotlib.pyplot as plt
import tensorflow as tf
import tensorflow.keras.datasets as datasets
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
plt.rcParams['font.size'] = 16
plt.rcParams['font.family'] = ['STKaiti']
plt.rcParams['axes.unicode_minus'] = False

2.2 加载数据集

def load_data():# 加载 MNIST 数据集(x, y), (x_val, y_val) = datasets.mnist.load_data()# 转换为浮点张量, 并缩放到-1~1x = tf.convert_to_tensor(x, dtype=tf.float32) / 255.# 转换为整形张量y = tf.convert_to_tensor(y, dtype=tf.int32)# one-hot 编码y = tf.one_hot(y, depth=10)# 在前向计算时,首先将shape为[b,28,28]的输入张量的视图调整为[b,784],即将每个图片的矩阵数据调整为向量特征,这样才适合网络的输入格式:# 改变视图, [b, 28, 28] => [b, 28*28]x = tf.reshape(x, (-1, 28 * 28))# 构建数据集对象train_dataset = tf.data.Dataset.from_tensor_slices((x, y))# 批量训练train_dataset = train_dataset.batch(200)return train_dataset

2.3 创建每个非线性层的W和b参数

  每个张量都需要被优化,所以使用Variable类型,并使用截断的正态分布初始化权值向量

# 创建每个非线性层的W和b张量参数
def init_paramaters():# 每层的张量都需要被优化,故使用 Variable 类型,并使用截断的正太分布初始化权值张量# 偏置向量初始化为 0 即可# 第一层的参数w1 = tf.Variable(tf.random.truncated_normal([784, 256], stddev=0.1))b1 = tf.Variable(tf.zeros([256]))# 第二层的参数w2 = tf.Variable(tf.random.truncated_normal([256, 128], stddev=0.1))b2 = tf.Variable(tf.zeros([128]))# 第三层的参数w3 = tf.Variable(tf.random.truncated_normal([128, 10], stddev=0.1))b3 = tf.Variable(tf.zeros([10]))return w1, b1, w2, b2, w3, b3

2.4 前向计算

  第一层计算,这里显示地进行自动扩展操作:

# 第一层计算, [b, 784]@[784, 256] + [256] => [b, 256] + [256] => [b,256] + [b, 256]h1 = x @ w1 + tf.broadcast_to(b1, (x.shape[0], 256))h1 = tf.nn.relu(h1) # 通过激活函数

  用同样地方法完成第二个和第三个非线性函数层地前向计算,输出层可以不使用ReLU激活函数:

# 第二层计算, [b, 256] => [b, 128]h2 = h1 @ w2 + b2h2 = tf.nn.relu(h2)# 输出层计算, [b, 128] => [b, 10]out = h2 @ w3 + b3

  将真实地标注张量y转变为独热编码,并计算与out的均方误差,代码如下:

# 计算网络输出与标签之间的均方差, mse = mean(sum(y-out)^2)# [b, 10]loss = tf.square(y - out)# 误差标量, mean: scalarloss = tf.reduce_mean(loss)

  上述的前向计算过程都包裹在with tf.GradientTape() as tape上下文中,使得前向计算时候能够保存计算图信息,方便自动求导操作。

2.5 自动梯度与梯度更新

  通过tape.gradient()函数求得网络参数得到梯度信息,结果保存在grads列表变量中,实现如下:

# 自动梯度,需要求梯度的张量有[w1, b1, w2, b2, w3, b3]grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3])

  并按照公式:
θ′=θ−η⋅∂ζ∂θ\theta '=\theta -\eta \cdot \frac{\partial \zeta }{\partial \theta } θ=θηθζ
  来更新网络参数:

# 梯度更新, assign_sub 将当前值减去参数值,原地更新w1.assign_sub(lr * grads[0])b1.assign_sub(lr * grads[1])w2.assign_sub(lr * grads[2])b2.assign_sub(lr * grads[3])w3.assign_sub(lr * grads[4])b3.assign_sub(lr * grads[5])

  其中,assign_sub()将自身减去给定的参数值,实现参数的原地(In-place)更新操作。

  网络训练误差值的变化曲线如下图所示:

image-20220712220249459

2.6 完整代码

import os
import matplotlib.pyplot as plt
import tensorflow as tf
import tensorflow.keras.datasets as datasets
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
plt.rcParams['font.size'] = 16
plt.rcParams['font.family'] = ['STKaiti']
plt.rcParams['axes.unicode_minus'] = Falsedef load_data():# 加载 MNIST 数据集(x, y), (x_val, y_val) = datasets.mnist.load_data()# 转换为浮点张量, 并缩放到-1~1x = tf.convert_to_tensor(x, dtype=tf.float32) / 255.# 转换为整形张量y = tf.convert_to_tensor(y, dtype=tf.int32)# one-hot 编码y = tf.one_hot(y, depth=10)# 改变视图, [b, 28, 28] => [b, 28*28]x = tf.reshape(x, (-1, 28 * 28))# 构建数据集对象train_dataset = tf.data.Dataset.from_tensor_slices((x, y))# 批量训练train_dataset = train_dataset.batch(200)return train_dataset# 创建每个非线性层的W和b张量参数
def init_paramaters():# 每层的张量都需要被优化,故使用 Variable 类型,并使用截断的正太分布初始化权值张量# 偏置向量初始化为 0 即可# 第一层的参数w1 = tf.Variable(tf.random.truncated_normal([784, 256], stddev=0.1))b1 = tf.Variable(tf.zeros([256]))# 第二层的参数w2 = tf.Variable(tf.random.truncated_normal([256, 128], stddev=0.1))b2 = tf.Variable(tf.zeros([128]))# 第三层的参数w3 = tf.Variable(tf.random.truncated_normal([128, 10], stddev=0.1))b3 = tf.Variable(tf.zeros([10]))return w1, b1, w2, b2, w3, b3def train_epoch(epoch, train_dataset, w1, b1, w2, b2, w3, b3, lr=0.001):for step, (x, y) in enumerate(train_dataset):with tf.GradientTape() as tape: # 默认跟踪的是tf.Variable类型# 第一层计算, [b, 784]@[784, 256] + [256] => [b, 256] + [256] => [b,256] + [b, 256]h1 = x @ w1 + tf.broadcast_to(b1, (x.shape[0], 256))h1 = tf.nn.relu(h1) # 通过激活函数# 第二层计算, [b, 256] => [b, 128]h2 = h1 @ w2 + b2h2 = tf.nn.relu(h2)# 输出层计算, [b, 128] => [b, 10]out = h2 @ w3 + b3# 计算网络输出与标签之间的均方差, mse = mean(sum(y-out)^2)# [b, 10]loss = tf.square(y - out)# 误差标量, mean: scalarloss = tf.reduce_mean(loss)# 自动梯度,需要求梯度的张量有[w1, b1, w2, b2, w3, b3]grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3])# 梯度更新, assign_sub 将当前值减去参数值,原地更新w1.assign_sub(lr * grads[0])b1.assign_sub(lr * grads[1])w2.assign_sub(lr * grads[2])b2.assign_sub(lr * grads[3])w3.assign_sub(lr * grads[4])b3.assign_sub(lr * grads[5])if step % 100 == 0:print(epoch, step, 'loss:', loss.numpy())return loss.numpy()def train(epochs):losses = []train_dataset = load_data()w1, b1, w2, b2, w3, b3 = init_paramaters()for epoch in range(epochs): # 20loss = train_epoch(epoch, train_dataset, w1, b1, w2, b2, w3, b3, lr=0.001)losses.append(loss)x = [i for i in range(0, epochs)]# 绘制曲线plt.plot(x, losses, color='blue', marker='s', label='训练')plt.xlabel('Epoch')plt.ylabel('MSE')plt.legend()plt.savefig('MNIST数据集的前向传播训练误差曲线.png')plt.show()if __name__ == '__main__':train(epochs=20)

看懂上面的代码必须将tensorflow的张量计算十分熟练才可以。


3、总结

  刚开始我只会调用keras的API,不知道底层是怎么计算的,导致遇到复杂的模型看不懂别人的代码,现在终于将梯度下降法和前向传播搞明白了(无非就是通过偏导数去更新参数W和b,然后再计算损失)


推荐阅读
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • 也就是|小窗_卷积的特征提取与参数计算
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了卷积的特征提取与参数计算相关的知识,希望对你有一定的参考价值。Dense和Conv2D根本区别在于,Den ... [详细]
  • 本文介绍了利用ARMA模型对平稳非白噪声序列进行建模的步骤及代码实现。首先对观察值序列进行样本自相关系数和样本偏自相关系数的计算,然后根据这些系数的性质选择适当的ARMA模型进行拟合,并估计模型中的位置参数。接着进行模型的有效性检验,如果不通过则重新选择模型再拟合,如果通过则进行模型优化。最后利用拟合模型预测序列的未来走势。文章还介绍了绘制时序图、平稳性检验、白噪声检验、确定ARMA阶数和预测未来走势的代码实现。 ... [详细]
  • pytorch Dropout过拟合的操作
    这篇文章主要介绍了pytorchDropout过拟合的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完 ... [详细]
  • 本文介绍了机器学习手册中关于日期和时区操作的重要性以及其在实际应用中的作用。文章以一个故事为背景,描述了学童们面对老先生的教导时的反应,以及上官如在这个过程中的表现。同时,文章也提到了顾慎为对上官如的恨意以及他们之间的矛盾源于早年的结局。最后,文章强调了日期和时区操作在机器学习中的重要性,并指出了其在实际应用中的作用和意义。 ... [详细]
  • 本文介绍了在Python张量流中使用make_merged_spec()方法合并设备规格对象的方法和语法,以及参数和返回值的说明,并提供了一个示例代码。 ... [详细]
  • 本文介绍了使用Spark实现低配版高斯朴素贝叶斯模型的原因和原理。随着数据量的增大,单机上运行高斯朴素贝叶斯模型会变得很慢,因此考虑使用Spark来加速运行。然而,Spark的MLlib并没有实现高斯朴素贝叶斯模型,因此需要自己动手实现。文章还介绍了朴素贝叶斯的原理和公式,并对具有多个特征和类别的模型进行了讨论。最后,作者总结了实现低配版高斯朴素贝叶斯模型的步骤。 ... [详细]
  • Opencv提供了几种分类器,例程里通过字符识别来进行说明的1、支持向量机(SVM):给定训练样本,支持向量机建立一个超平面作为决策平面,使得正例和反例之间的隔离边缘被最大化。函数原型:训练原型cv ... [详细]
  • 文章目录前言pandas主要分为如下几个阶段:表格数据操作:增删改查实现多个表格的处理数据清洗操作:缺失值、重复值、异常值、数据标准化、数 ... [详细]
  • keras归一化激活函数dropout
    激活函数:1.softmax函数在多分类中常用的激活函数,是基于逻辑回归的,常用在输出一层,将输出压缩在0~1之间,且保证所有元素和为1,表示输入值属于每个输出值的概率大小2、Si ... [详细]
  • 解决python matplotlib画水平直线的问题
    本文介绍了在使用python的matplotlib库画水平直线时可能遇到的问题,并提供了解决方法。通过导入numpy和matplotlib.pyplot模块,设置绘图对象的宽度和高度,以及使用plot函数绘制水平直线,可以解决该问题。 ... [详细]
  • Day2列表、字典、集合操作详解
    本文详细介绍了列表、字典、集合的操作方法,包括定义列表、访问列表元素、字符串操作、字典操作、集合操作、文件操作、字符编码与转码等内容。内容详实,适合初学者参考。 ... [详细]
  • 颜色迁移(reinhard VS welsh)
    不要谈什么天分,运气,你需要的是一个截稿日,以及一个不交稿就能打爆你狗头的人,然后你就会被自己的才华吓到。------ ... [详细]
  • Window10+anaconda+python3.5.4+ tensorflow1.5+ keras(GPU版本)安装教程 ... [详细]
  • html结构 ... [详细]
author-avatar
miedao1592_460
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有