多层感知机MLP/全连接神经网络FC机器学习编程练习
说明
- 参考教程:简单粗暴TensorFlow 2-TensorFlow模型建立与训练-基础示例:多层感知机(MLP)
- 本文为对教程学习后上机所编写的实践程序,并增加了对大部分语句的个人理解与总结,补充在程序注释中。可作为对参考教程的进一步理解与解释,也可用于对编程环境的可行性评估。欢迎读者交流与沟通,同时支持教程原作者,为大家提供更优质的教程。
编程环境
- OS: Win10 Professional
- IDE: Pycharm Community 2021.1
- Complier: MSVC2019
- Python: 3.9
- TensorFlow: 2.5.0
- CUDA: 10.0
- cuDNN: 7.4.2
程序详情
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): # 调用数据运算,输入张量[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'''
输出结果
。。。
。。。