import numpy as np
import tensorflow as tf
import os
os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID" # 运行硬件选择
os.environ["CUDA_VISIBLE_DEVICES"] = "0" #-1 为 CPU , 0 为 GPU1,1 为 GPU2 ,以此类推
''' 数据获取及预处理 '''
class MNISTLoader():
# 从网络上自动下载 MNIST 数据集并加载。如果运行时出现网络连接错误,可以从
# https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
# 或
# https://s3.amazonaws.com/img-datasets/mnist.npz
# 下载 MNIST 数据集 mnist.npz 文件,并放置于用户目录的 .keras/dataset 目录下( Windows 下用户目录为 C:\Users\ 用户名 ,
# Linux 下用户目录为 /home/ 用户名 )
def __init__(self):
mnist = tf.keras.datasets.mnist
(self.train_data, self.train_label), (self.test_data, self.test_label) = mnist.load_data()
# MNIST 中的图像默认为 uint8 ( 0-255 的数字)。以下代码将其归一化到 0-1 之间的浮点数,并在最后增加一维作为颜色通道
self.train_data = np.expand_dims(self.train_data.astype(np.float32)/255.0, axis=-1) # [60000, 28, 28, 1]
# 保存训练数据,手动增加一个张量维度给通道数( size=-1 )
self.test_data = np.expand_dims(self.test_data.astype(np.float32)/255.0, axis=-1) # [10000, 28, 28, 1]
# 保存测试数据,手动增加一个张量维度给通道数( size=-1 )
self.train_label = self.train_label.astype(np.float32) # [60000]
self.test_label = self.test_label.astype(np.float32) # [10000]
self.num_train_data, self.num_test_data = self.train_data.shape[0],self.test_data.shape[0]
# 提取训练集和测试集图片数量
def get_batch(self, batch_size):
index = np.random.randint(0, self.num_train_data, batch_size)
return self.train_data[index, :], self.train_label[index]
'''end'''
''' 模型构建 '''
class MLP(tf.keras.Model):
def __init__(self):
super().__init__()
self.flatten = tf.keras.layers.Flatten() # 将 28*28 的图片拉直成 1*784 向量
self.dense1 = tf.keras.layers.Dense(units=100, activation=tf.nn.relu) # 增加隐层 1 , 100 个神经元,激活函数 ReLu
self.dense2 = tf.keras.layers.Dense(units=10) # 增加隐层 2,10 个神经元, 无激活函数
def call(self, inputs): # 调用数据运算,输入张量外汇跟单gendan5.com [batch_size, 28, 28, 1]
x = self.flatten(inputs) # 拉直输入 [batch_size, 784]
x = self.dense1(x) # 隐层 1[batch_size, 100]
x = self.dense2(x) # 隐层 2[batch_size, 10]
output = tf.nn.softmax(x) # 输出 softmax 分类
return output
'''end'''
''' 模型训练 '''
# 定义超参数,参数定义参见 https://blog.csdn.net/u013041398/article/details/72841854/
num_epochs = 5 # 轮次、周期,完整遍历数据集的次数,过小可能会欠拟合,过大可能会过拟合
batch_size = 50 # 批次,通常使用 mini-batch 方法,训练一批后计算损失函数进行梯度下降和权重更新
learning_rate = 0.001 # 学习率
# 实例化
model = MLP() # 实例化模型
data_loader = MNISTLoader() # 实例化数据读取类
optimizer = tf.keras.optimizers.Adam(learning_rate=learning_rate) # 实例化优化器,使用 Adam 优化器,算法讲解
# 参见 https://www.jianshu.com/p/aebcaf8af76e
'''end'''
''' 训练与迭代 '''
num_batches = int(data_loader.num_train_data // batch_size * num_epochs) # 求训练集 batch 总个数,即训练总周期数
for batch_index in range(num_batches):
X, Y = data_loader.get_batch(batch_size) # 提取输入输出
with tf.GradientTape() as tape:
y_pred = model(X) # 计算模型输出
loss = tf.keras.losses.sparse_categorical_crossentropy(y_true=Y, y_pred=y_pred) # 使用交叉熵函数做损失函数,
# 算法讲解参见 https://blog.csdn.net/tsyccnh/article/details/79163834
# loss = tf.keras.losses.categorical_crossentropy(
# y_true=tf.one_hot(Y, depth=tf.shape(y_pred)[-1]),
# y_pred=y_pred
# ) # 与上式等效
loss = tf.reduce_mean(loss) # 求一维均方误差 MSE
print("batch %d: loss %f" % (batch_index, loss.numpy()))
grads = tape.gradient(loss, model.variables) # 损失函数关于自变量的梯度自动计算,
# 使用 model.variables 属性直接获得模型中所有变量
optimizer.apply_gradients(grads_and_vars=zip(grads, model.variables)) # 根据梯度自动更新参数,
# zip() 为拼装成元组函数
'''end'''
''' 模型评估 '''
sparse_categorical_accuracy = tf.keras.metrics.SparseCategoricalAccuracy() # 计算预测正确样本数占总样本数的比例实例化
num_batches = int(data_loader.num_test_data // batch_size) # 计算测试集批次 batch 数
for batch_index in range(num_batches):
start_index, end_index = batch_index * batch_size, (batch_index + 1) * batch_size # 每个 batch 逐个样本标号
y_pred = model.predict(data_loader.test_data[start_index: end_index]) # 计算一个 batch 预测模型输出
sparse_categorical_accuracy.update_state(y_true=data_loader.test_label[start_index: end_index], y_pred=y_pred)
# 更新预测精度
print("test accuracy: %f" % sparse_categorical_accuracy.result())
'''end'''