TensorFlow学习笔记4—TensorFlow2—向前传播(张量)- 手写数字问题

1、问题描述

MNIST数据集包含70K张手写数字体,如下图,每张为28*28的像素图,使用其中的60K张手写数字体图片训练模型,使用10K张手写数字体测试模型的权重w和偏置b的性能

2、解决原理

数据处理:

(1)将每张图片28*28的二维数组表示为一个长度为28*28=784的一维数组存储图片信息

(2)多张图片表示方式为X:[b,784],其中b表示图片数量

(3)将标签进行独热编码:

dog = [1, 0, 0, …]

cat = [0, 1, 0, …]

fish = [0, 0, 1, …]

...

 

计算:

(1)简单流程

▪ out = X@W + b                            //输出out=矩阵X乘矩阵W加类别b

▪ X : [b, 784]                           

▪ W: [784, 10]                                 //W根据相应矩阵维度匹配

▪ b : [10]                                           //标签为10类

▪ out: [b, 10]

 

       (2)添加激活函数与隐藏层

▪out = relu(X@W + b)                 //添加relu函数(引入非线性因子)

 

▪h1= relu(X@W1 + b1)              //隐藏层1

▪h2 = relu(h1@W2 + b2)           //隐藏层2

▪out = relu(h2@W3 + b3)          //输出层

         ▪loop

 

(3)预测与loss

pred = argmax(out)                       //最大值为预测结果

loss=MSE(out, label)                  //loss值为预测结果与真实值的欧氏距离

 

3、代码实现

import os

# cpp会打印无关信息,默认为0全部打印,设置为2只打印error信息
# 该行代码有时候写在import tensorflow前才有效
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

import tensorflow as tf
from tensorflow.keras import datasets
from tensorflow import keras


# 自动下载数据集,x:[60, 28, 28] y:[60k]
(x, y), _ = datasets.mnist.load_data()
# 将numpy数据转变为Tensor数据,且将x:[0~255]->[0~1.]
x = tf.convert_to_tensor(x, dtype=tf.float32) / 255.
y = tf.convert_to_tensor(y, dtype=tf.int32)


# 观察数据shape与类型是否与预期一致
print(x.shape, y.shape, x.dtype, y.dtype)
# 输出x、y最大值与最小值
print(tf.reduce_min(x), tf.reduce_max(x))
print(tf.reduce_min(y), tf.reduce_max(y))


# 创建数据集对象
train_db = tf.data.Dataset.from_tensor_slices((x, y)).batch(128)
# 创建迭代器
train_iter = iter(train_db)
# 用next对迭代器不停迭代,直到获得最终数据
sample = next(train_iter)
# 输出一个batch的第一个元素和第二个元素
print('batch:', sample[0].shape, sample[1].shape)
# batch: (128, 28, 28) (128,)


# 降维:[b, 784] => [b, 256] => [b, 128] => [b, 10]
# w.shape:[dim_in, dim_out], b.shape:[dim_out]
# 用tf.Variable方法包装权值,使之可以求导训练
# w使用截断分布truncated_normal随机初始化,默认方差mean=0,均值stddev=1
# 如果stddev设置不恰当可能会出现梯度爆炸(not a number)、梯度离散的问题
w1 = tf.Variable(tf.random.truncated_normal([784, 256], stddev=0.1))
# b一般初始化为0
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]))

# 设置参数lr,表示梯度步长(学习速度)
lr = 1e-3

#增加迭代次数
for epoch in range(10): # iterate db for 10
    # 使train_db进行迭代,使用enumerate、step会在每一个batch后返回step数值
    for step,(x, y) in enumerate(train_db):
        # train_db中x与y的shape,x:[128, 28,28],y:[128]
        # 维度变换:[b, 28, 28] => [b, 28 * 28],“-1”使b自动计算
        x = tf.reshape(x, [-1, 28 * 28])

        # 使用自动求导过程
        with tf.GradientTape() as tape:  # tf.Variable,使tensor数据增加自动求导属性

            # 设置神经网络层
            # 向前传播中x的shape,x:[b, 28 * 28]
            # h1 = x @ w1 + b1
            # x[b,784] @ w1[784,256] + b1[256] => h1[b,256] + b2[256] => h2[b,256] + b3[b,256]
            # b1可自动broadcast,使b1升维并可以与x@w1结果相加得到h1
            # 完成第一层嵌套,相当于神经网络隐藏层:layers.Dense(256)
            h1 = x @ w1 + tf.broadcast_to(b1, [x.shape[0], 256])
            # 完成第二层嵌套,相当于神经网络隐藏层:layers.Dense(128, activation='relu')
            # [b, 256] => [b, 128]
            h2 = h1 @ w2 + b2
            # 引入非线性因子
            h2 = tf.nn.relu(h2)
            # 完成第三层嵌套,相当于神经网络输出层:layers.Dense(10)
            # [b, 128] => [b, 10]
            out = h2 @ w3 + b3

            # compute loss 计算误差,mse = mean(sum(y-out)^2)
            # out: [b, 10],使y: [b] => y_onehot: [b, 10]
            # 使分类进行独热编码(实际上是一种升维),depth表示类型(标签)数量
            y_onehot = tf.one_hot(y, depth=10)
            # 每个元素进行平方,得到[b, 10]
            loss = tf.square(y_onehot - out)
            # mean: 求均值(相当于loss/b/10),得到scalar
            loss = tf.reduce_mean(loss)

        # 计算梯度compute gradients,得到grads列表,按照顺序存储[w1, b1, w2, b2, w3, b3]
        grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3])
        # print(grads)
        # 更新公式w1 = w1 - lr * w1_grad

        # 易错点:新的w1为Tenor类型,没有经过Variable,不能自动求导
        # w1 = w1 - lr * grads[0]
        # b1 = b1 - lr * grads[1]
        # w2 = w2 - lr * grads[2]
        # b2 = b2 - lr * grads[3]
        # w3 = w3 - lr * grads[4]
        # b3 = b3 - lr * grads[5]

        # 原地更新方法:assign_sub(ref, value)方法:ref = ref - value
        # tensor. assign_sub(value):功能tensor = tensor - 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])

        # 每100个batch计算之后输出loss数值,用float将loss由tensor转换为numpy数值
        if step % 100 == 0:
            print(epoch,step, 'loss:', float(loss))

运行结果:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值