简介:
本教程的代码使用 神经网络来训练可以对手写数字 0-9 进行分类的图像分类机器学习模型。 它通过创建一个神经网络来实现此目的。该神经网络将“28 像素 x 28 像素”图像的像素值作为输入,输出一个包含 10 个概率的列表,一个概率对应于要分类的一个数字。 下面是数据的外观示例。
一,读取数据
首先导入神经网络集所需要的包,其中keras层方式做前向传播,服装图片分类每一张图片28*28,x和y中保存训练集的图像和目标。x_test和y_test中保存测试集需要的图像和目标。
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets,layers,optimizers,Sequential,metrics
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
#(1)导入数据集,数组类型
(x,y),(x_test,y_test) = datasets.fashion_mnist.load_data()
# 查看数据集信息
print(f'x.shape={x.shape},y.shape={y.shape}') # 查看训练集xy的大小
print(f'x_test.shape={x_test.shape},y_test.shape={y_test.shape}') #查看测试集的大小
print(f'y[:5]={y[:5]}') # 查看y的前5项数据
读取的数据集如下,变量x中有60k张图片,每张图片的都是28*28的大小,其中变量y保存的是每张图片属于哪个分类。如,y[:5]=[9 0 0 3 0],第一张图片属于第10个类别,第二张图片属于第0个类别。
我们为了能让这个数据集可以更直接的认识,将图片信息绘制出来展示前10张图像。
# 数据集展示
import matplotlib.pyplot as plt
import numpy as np
class_names = ['Tshirt','Trouser','Pullover','Dress','Coat','Sandal','Shirt','Sneaker','Bag','Ankle boot']
# 绘制图像
for i in range(0,10):
plt.subplot(2,5,i+1)
plt.imshow(x[i])
plt.xlabel(class_names[y[i]]) #y[i]代表所属分类的标签值
plt.xticks([])
plt.yticks([])
二,数据集加载
在数据加载时我们首先要对训练集的目标值y进行one-hot编码,有利于后面的预测结果计算损失。以标量变成向量,其对应的数值变成1。如编码前y[0]=9,第一张图片对应的分类是第9类,编码后y[0]=[0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],第9个索引对应的值变成1,其他值都是0。
使用ds_train创建一个数据集,将自动输入的x和y转换为tensor类型。
使用map()函数对数据集中的所有元素执行函数内容,使用batch()函数指定每次代从数据集中取多少个数据,shuffle()打乱数据集,但不改变xy的对应关系,避免结果出现偶然性。
在对训练集预处理的时候,不需要对y_test数据进行one-hot编码,因为在怎么编码测试的结果都是一个值,和y_test比较,测试是不是相同就好了。
def processing(x,y):
x = tf.cast(x,tf.float32)/255.0
y = tf.cast(y,tf.int32)
return(x,y)
#(2)数据加载
y = tf.one_hot(y,depth=10)
ds_train = tf.data.Dataset.from_tensor_slices((x,y))
ds_train = ds_train.map(processing).batch(128).shuffle(10000) # 设置每次采样大小,并打乱
# 对测试集预处理
ds_test = tf.data.Dataset.from_tensor_slices((x_test,y_test))
ds_test = ds_test.map(processing).batch(128) #一次测试完所有测试集样本呢,不需要打乱
# 生成迭代器,检测数据加载是否正确
sample = next(iter(ds_train))
print('x_batch:',sample[0].shape,'y_batch:',sample[1].shape)
为了查看我们预处理的数据是否正确,我们来构建一个迭代器来查看,起检验作用。iter()函数生成一个迭代器,每执行一次next()函数,从ds_train中获取一组batch的样本(x,y),sample[0]保存x的数据,sample[1]保存y的数据。
三,网络构建
在网络构建模型中,使用堆层的方法构建全连接层tf.keras.Sequential(),使用 tf.keras.layers.Dense()添加每一层,构建5层的全连接层,指定激活函数为relu函数,使网络的维度从[b,28*28]变换到最终的[b,10],即输出10个分类的结果。
#(3)构建网络
# [b,784]=>[b,256]=>[b,128]=>[b,64]=>[b,32]=>[b,10],中间层一般从大到小降维
model = Sequential([
layers.Dense(256, activation=tf.nn.relu), #第一个连接层,输出256个特征
layers.Dense(128, activation=tf.nn.relu), #第二个连接层
layers.Dense(64, activation=tf.nn.relu), #第三个连接层
layers.Dense(32, activation=tf.nn.relu), #第四个连接层
layers.Dense(10), #最后一层不需要激活函数,输出10个分类
])
model.build(input_shape=[None, 28*28])
model.summary()
# 完成权重更新 w = w - lr * grad
optimizer = optimizers.Adam(lr=1e-3)
通过运行得出的网络结果如下, param代表每一层的参数个数,以最后一层为例,权重w的shape为[32,10],偏置b的shape为[10],参数个数为32*10+10=330。
四,网络集的训练
在网络集的训练中我们设置整个网络循环20次,接下来计算损失函数,使用交叉熵更新权重偏置。使用优化器更新梯度,相当于计算w1 = w1 - lr * grads[0],并原地更新权重偏置不改变数据类型。
for epoch in range(20): # 运行20次
for step,(x,y) in enumerate(ds_train):
# ds_train中x的shape是[b,28,28],由于输入层是[b,28*28],需要类型转换
x = tf.reshape(x, [-1, 28*28]) #-1会自动结算第0维
# 梯度计算
with tf.GradientTape() as tape:
# 网络自动运行:[b,784]=>[b,10]
logits = model(x) #得到最后一层的输出
loss1 = tf.reduce_mean(tf.losses.MSE(y, logits))
# 计算交叉熵损失,真实值y(onehot编码后的)和输出概率(logits会自动进行softmax变成概率值)
loss2 = tf.reduce_mean(tf.losses.categorical_crossentropy(y, logits, from_logits=True))
# 梯度计算,
grads = tape.gradient(loss2, model.trainable_variables)
# 更新权重,zip将grads的元素和model.trainable_variables中的元素结合在一起
optimizer.apply_gradients(zip(grads, model.trainable_variables)) # 完成任务:w1.assign_sub(lr * grads[0])
if step % 100 == 0:
print(f'epochs:{epoch}, step:{step}, loss_MSE:{loss1}, loss_CE:{loss2}')
五,测试网络集
在网络训练中是在20次的大循环内部的,使用总预测对了的个数除以总测试样本数来计算模型准确率。同样,每次迭代从测试集中取batch个样本,放入网络计算,得到输出层的结果logits需要求预测结果概率最大值所在下标,知道了下标索引也就知道了每个测试样本属于第几个分类。
#(6)网络测试
total_correct = 0 # 总预测对了的个数
total_sum = 0 # 总统计的个数
for (x,y) in ds_test: #返回测试集的x和y
x = tf.reshape(x, [-1,28*28])
logits = model(x)
# 计算概率最大的值所在的索引
# 将logits转换为probility
prob = tf.nn.softmax(logits, axis=1) # 在最后一个维度上转换概率,且概率和为1
predict = tf.argmax(prob, axis=1) # 找到最大值所在位置,得到一个标量
# y 是int32类型,shape为[128]
# predict 是int64类型,shape为[128]
predict = tf.cast(predict, dtype=tf.int32)
# 只要看两个变量的值是否相同
correct = tf.equal(y, predict) #返回True和False
# True和False变成1和0,统计1的个数,一共有多少个预测对了
correct = tf.reduce_sum(tf.cast(correct, dtype=tf.int32))
total_correct += int(correct)
total_sum += x.shape[0]
# 计算一次大循环之后的模型准确率
acc = total_correct/total_sum
print(f'acc: {acc}')
六,整体运行展示
该模型运行结果如下:第一次循环,以及经过20次循环的结果的出的结果是,第二十次循环的结果为:0.893。