曹健老师 TensorFlow2.1 —— 第一章 神经网络计算

看该视频前建议先看一看神经网络基础,推荐吴恩达老师课程。

吴恩达老师视频课:https://pan.baidu.com/s/18JaAUNlYxO2g2_Xh7Mgmgw  提取码:m6kv

曹健老师视频课程链接:https://www.bilibili.com/video/BV1B7411L7Qt?p=1

课程源码:https://pan.baidu.com/s/1nug4Jl5KZduPxPGruoi-kQ   提取码:nsrl

环境:Python 3.7 , TensorFlow 2.1

1.1 连接主义

用计算机仿出神经网络连接关系,让计算机具备感性思维。

  1. 准备数据:采集大量“特征/标签”数据
  2. 搭建网络:搭建神经网络结构
  3. 优化参数:训练网络获取最佳参数(反向传播)
  4. 应用网络:将网络保存为模型,输入新数据,输出分类或预测结果(概率值)(前向传播)

1.2 神经网络设计过程

  • 把数据喂入搭建好的神经网络结构
  • 网络优化参数得到模型

神经元计算模型:y = x*w + b,随机初始化 w 和 b,代入数据 x ,计算出 y ,这个过程称为前向传播,但由于 w 和 b 是随机产生的,因此产生的结果可信度不高,利用损失函数反向传播,不断优化 w 和 b 。

损失函数:预测值(网络前向推理)与标准答案的差距,可以定量判断 w 、b 的优劣,当损失函数输出最小时,参数 w 、b 会出现最优值。

损失函数的梯度:表示函数对各参数求偏导后的向量,函数梯度下降的方向就是损失函数减小得最快的方向,从而得到损失函数最小值,得到最优的参数 w 和 b 。

梯度下降法:沿损失函数梯度下降的方向,寻找损失函数的最小值,得到最优参数的方法。梯度下降法更新参数的公式为:w_{t+1} = w_{t} - lr * \frac{\partial loss}{\partial w_{t}}

学习率( learning rate , lr ):是梯度下降的速度,是一个超参数,当学习率设置得过小时,参数更新会很慢,收敛过程将变得十分缓慢;当学习率设置得过大时,参数更新可能会跳过最小值,梯度可能会在最小值附近来回震荡,甚至可能无法收敛。

反向传播:从后向前,逐层求损失函数对每层神经元参数的参数的偏导数,迭代更新所有参数。

# backPropagation
import tensorflow as tf

w = tf.Variable(tf.constant(5, dtype=tf.float32))
lr = 0.999
epoch = 40    # 循环迭代次数定义为40

for epoch in range(epoch):  # for epoch 定义顶层循环,表示对数据集循环 epoch 次
    with tf.GradientTape() as tape:    # with 结构到 grads 框起了梯度的计算过程。
        loss = tf.square(w + 1)       # 用 with 结构让损失函数 loss 对参数 w 求梯度
    grads = tape.gradient(loss, w)    # .gradient()告知谁对谁求导
    
    w.assign_sub(lr * grads)    # .assign_sub 对变量做自减 即 w -= lr * grads
    print("After %s epoch, w is %f, loss is %f"%(epoch, w.numpy(),loss))
    
# lr 初始值为 0.2  可以更改学习率为 0.001 和 0.999 看收敛过程
# 最终目的:找到 loss 最小,即 w = -1 的最优参数 w
  • 模型读入新输入特征
  • 输出识别结果

1.3 张量生成

张量(Tensor):多维数组(列表),用 “ 阶 ” 表示张量的维数。张量可以表示 0 阶到 n 阶数组(列表)

 

  • 使用 constant 创建张量
# tf.constant( 张量内容, dtype=数据类型(可选))
a = tf.constant([1,4], dtype=tf.int64)
print(a)          # 打印 a ,包括 a 的内容、形状、类型
print(a.dtype)    # 打印 a 的类型
print(a.shape)    # 打印 a 的形状,结果中逗号隔开了几个数字,这个 tensor 就是几维的,数字代表当前维度有几个元素
# tensor 的默认数据类型为 float32
  • 将 numpy 的数据类型转换成 Tensor 数据类型
# tf.convert_to_tensor(数据名,dtype=数据类型(可选))
a = np.arange(0, 5)
b = tf.convert_to_tensor(a, dtype=tf.int64)
  • zeros 、 ones 、 fill
# 创建全为 0 的张量            # 维度表示:
tf.zeros(维度)                # 1-D 直接写个数
# 创建全为 1 的张量            # 2-D 用[行,列]
tf.ones(维度)                 # 多维 用[n,m,j,k, ...]
# 创建全为指定值的张量
tf.fill(维度, 指定值)
  • 生成正态分布的随机数,默认均值为 0,标准差为 1
tf.random.normal(维度, mean=均值, stddev=标准差)
d = tf.random.noraml([2, 2], mean=0.5, stddev=1)
  • 生成截断式正态分布的随机数(使生成的随机数在均值正负两倍标准差之内,数据更向均值集中)
tf.random.truncated_normal(维度, mean=均值, stddev=标准差)
e = tf.random.truncated_normal([2, 2], mean=0.5, stddev=1)
  • 生成指定维度的均匀分布随机数, [minval, maxval) 左闭右开
tf.random.uniform(维度, minval=最小值, maxval=最大值)
f = tf.random.uniform([2, 2], minval=0, maxval=1)

1.4/5 TF2 常用函数

  • 强制 tensor 转换为该数据类型:
tf.cast(张量名, dtype=数据类型)
x1 = tf.constant([1., 2., 3.], dtype=tf.float64)
x2 = tf.cast(x1, tf.int32)
  • 计算张量维度上的元素的最小 / 大值:
tf.reduce_min / max(张量名)
tf.reduce_min(x2)
  • 理解 axis :axis 可以指定操作的方向,对于二维的张量,axis=0 表示对第一个维度进行操作(跨行),axis=1 表示对第二个维度进行操作(跨列)。如果不指定 axis ,则所有的元素参与计算。
  • 计算张量沿着指定维度的平均值:
tf.reduce_mean(张量名, axis=操作轴)
x = tf.constant([[1,2,3],[2,3,4]])
tf.reduce_mean(x)
  • 计算张量沿着指定维度的和:
tf.reduce_sum(张量名, axis=操作轴)
tf.reduce_sum(x, axis=1)
  • tf.Variable() 可以将变量标记为“可训练”,被标记的变量会在反向传播中记录梯度信息,神经网络训练中,常用该函数标记待训练参数。。
tf.Variable(初始值)
w = tf.Variable(tf.random.normal([2, 2], mean=0, stddev=1))
  • TensorFlow 中的数学运算:
加减乘除:tf.add, tf.subtract, tf.multiply, tf.divide   
(只有维度相同的两个张量才能进行四则运算)
平方、次方和开方:tf.square, tf.pow(张量名, n次方数), tf.sqrt
矩阵乘:tf.matmul
  • tf.data.Dataset.from_tensor_slices() 把特征和标签配对(numpy 和 tensor 格式都可以使用该语句读入数据)。
data = tf.data.Dataset.from_tensor_slices((输入特征, 标签))  神经网络训练时,是把输入特征和标签配对后喂入网络

features = tf.constant([12, 23, 10, 17])
labels = tf.constant([0, 1, 1, 0])
dataset = tf.data.Dataset.from_tensor_slices((features, labels))
print(dataset)
for element in dataset:
    print(element)
  • 在 with 结构中,使用 tf.GradientTape() 实现某个函数对指定参数的求导运算。
with 结构记录计算过程,gradient 求出张量的梯度
with tf.GradientTape() as tape:
    若干个计算过程
grad = tape.gradient(函数, 对谁求导)

with tf.GradientTape() as tape:
    w = tf.Variable(tf.constant(3.0))
    loss = tf.pow(w, 2)
grad = tape.gradient(loss, w)
  • enumerate(列表名) 可以枚举出每一个元素,并在元素前配上对应的索引号,组合为:索引 元素,常在 for 循环中使用。
seq = ['one', 'two', 'three']
for i,element in enumerate(seq):    # i 接收索引号,element 接收元素
    print(i,element)
  • 在实现分类问题时,常用独热码(one-hot encoding)表示标签,1 表示是,0 表示非。
tf.one_hot(待转换数据, depth=几分类)
# 对于三分类
classes = 3
labels = tf.constant([1, 0, 2])
output = tf.one_hot(labels, depth=classes)
  • 对于分类问题,神经网络完成前向传播,计算出每种类型的可能性大小,这些可能性只有符合概率分布后才可以与独热码的标签作比较,因此使用到了 tf.softmax() (柔性最大值,使结果具有统计数据特性) ,使得输出的结果符合概率分布。当 n 分类的 n 个输出(y0,y1,...,yn-1)通过 softmax() ,得到的结果符合概率分布 每个结果的值在[0, 1],且和为 1。
y = tf.constant([1.01, 2.01, -0.66])
y_pro = tf.nn.softmax(y)
  • assign_sub(w要自减的内容) 常用于参数的自更新,赋值操作,更新参数的值并返回,调用 assign_sub() 前,先用 tf.Variable 定义变量 w 为可训练(可自更新)。
w = tf.Variable(4)
w.assign_sub(1)
# w 做自减 1 操作,被更新为 3 
  • tf.argmax(张量名, axis=操作轴) 返回指定操作轴方向最大值的索引号。
test = np.array([[1,2,3],[2,3,4],[5,4,3],[8,7,2]])
tf.argmax(test, axis=0)    # 返回每一列最大值的索引(跨行操作)
tf.argmax(test, axis=1)    # 返回每一行最大值的索引(跨列操作)

1.6 鸢尾花数据集读入

from sklearn import datasets
from sklearn.datasets import load_iris
from pandas import DataFrame
import pandas as pd

x_data = datasets.load_iris().data    # 返回 iris 数据集所有输入特征
y_data = datasets.load_iris().target  # 返回 iris 数据集所有标签

# 增加可读性,用 DataFrame 把数据变成表格形式
x_data = DataFrame(x_data, columns=['花萼长度', '花萼宽度', '花瓣长度', '花瓣宽度'])
pd.set_option('display.unicode.east_asian_width', True)   # 设置列名对齐
print('x_data add index: \n', x_data)

x_data['类别'] = y_data    # 新加一列,列标签为‘类别’,数据为 y_data
print("x_data add a column: \n", x_data)

# 类型维度不确定时,建议用 print 函数打印出来确认效果

1.7 神经网络实现鸢尾花分类

  • 准备数据:
from sklearn.datasets import datasets
x_data = datasets.load_iris().data      # 返回 iris 数据集所有输入特征
y_data = datasets.load_iris().target    # 返回 iris 数据集所有标签
# 数据集乱序
np.random.seed(116)      # 使用相同的 seed ,使输入特征/标签一一对应  同样的随机种子生成同样的随机值
np.random.shuffle(x_data)
np.random.seed(116)
np.random.shuffle(y_data)
tf.random.set_seed(116)  # 最后这一个 seed 的目的是每次运行这个代码文件时,结果跟上次运行的结果一样
# 生成训练集和测试集(即x_train/y_train,x_test/y_test)
x_train = x_data[:-30]
y_train = y_data[:-30]
x_test = x_data[-30:]
y_test = y_data[-30:]
    
# 数据类型转换,转换 x 的数据类型,否则后面矩阵相乘时会因数据类型不一致报错
x_train = tf.cast(x_train, tf.float32)
x_test = tf.cast(x_test, tf.float32)
    
# 配成(输入特征,标签)对,每次读入一小批(batch)
train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32)   # 每 32 组(输入特征, 标签)对打包为一个 batch 
test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32)      # 喂入神经网络时,以 batch 为单位喂入
  • 搭建网络:
# 定义神经网络中所有可训练参数
# 4 个输入特征,故输入层为 4 个输入节点,三分类,故输出层为 3 个神经元
w1 = tf.Variable(tf.random.truncated_normal([4, 3], stddev=0.1, seed=1))  # [4, 3]:输入特征是 4 个,输出三分类
b1 = tf.Variable(tf.random.truncated_normal([3], stddev=0.1, seed=1))     # b1 与 w1 的第二个维度相同
  • 参数优化:
# 嵌套循环迭代,with 结构更新参数,显示当前 loss
for epoch in range(epoch):  # 数据集级别迭代
    for step, (x_train, y_train) in enumerate(train_db):    # batch 级别迭代
        with tf.GradientTape() as tape:        # 记录梯度信息
            前向传播过程计算 y
            计算总 Loss
        grads = tape.gradient(loss, [w1, b1])
        w1.assign_sub(lr * grads[0])           # 参数自更新
        b1.assign_sub(lr * grads[1])
    print("Epoch{},loss:{}",format(epoch, loss_all/4))
  • 测试效果
# 计算当前参数前向传播后的准确率,显示当前 acc
for x_test, y_test in test_db:
    y = tf.matmul(h, w) + b   # y 为预测结果
    y = tf.nn.softmax(y)      # y 符合概率分布
    pred = tf.argmax(y, axis=1)   # 返回 y 中最大值的索引,即预测的分类
    pred = tf.cast(pred, dtype=y_test.dtype)    # 调整数据类型与标签一致
    correct = tf.cast(tf.equal(pred, y_test), dtype=tf.int32)
    correct = tf.reduce_sum(correct)    # 将每个 batch 的 correct 数加起来
    total_correct += int(correct)    # 将所有 batch 中的 correct 数加起来
    total_number += x_test.shape[0]
acc = total_correct / total_number
print("test_acc:",acc)
# acc/loss 可视化
plt.title('Acc Curve')    # 图片标题
plt.xlabel('Epoch')       # x 轴名称
plt.ylabel('Acc')         # y 轴名称
plt.plot(test_acc, label='$Accuracy$')    # 逐点画出 test_acc 值并连线
plt.legend()
plt.show()

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值