【深度学习】(5) 简单网络,案例:服装图片分类,附python完整代码

1. 数据获取

使用系统内部的服装数据集构建神经网络。首先导入需要的库文件,xy中保存训练集的图像和目标。x_testy_test中保存测试集需要的图像和目标。(x, y)及(x_test, y_test)都是数组类型。

# keras层方式做前向传播,服装图片分类每一张图片28*28
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' # '2'输出栏只打印error信息,其他乱七八糟的信息不打印

#(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个类别。

x.shape=(60000, 28, 28),y.shape=(60000,)
x_test.shape=(10000, 28, 28),y_test.shape=(10000,)
y[:5]=[9 0 0 3 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) # 当前的图绘制在2行5列的第i+1个位置
    plt.imshow(x[i]) 
    plt.xlabel(class_names[y[i]]) #y[i]代表所属分类的标签值
    plt.xticks([])  # 不显示x和y轴坐标
    plt.yticks([])


2. 数据加载

首先对训练集的目标值y进行one-hot编码,便于后续与预测结果计算损失。从标量变成一个向量,索引对应的数值变成1。如编码前y[0]=9,第一张图片对应的分类是第9类,编码后y[0]=[0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],第9个索引对应的值变成1,其他值都是0。

使用tf.data.Dataset.from_tensor_slices()创建数据集,自动将输入的数组类型转变成tensor类型。使用.map()函数对数据集中的所有元素执行函数内容;使用.batch()函数指定每次迭代从数据集中取多少个数据,.shuffle()打乱数据集,但不改变xy的对应关系,避免结果出现偶然性。

在对训练集预处理时,不需要对y_test数据进行one-hot编码,因为测试得到的预测结果是一个数值,和y_test比较,看是不是相同就行。

# 数据预处理函数,转变数据类型
def processing(x,y):
    x = tf.cast(x,tf.float32)/255.0  # x数据改变数据类型,并归一化
    y = tf.cast(y,tf.int32)  # 对目标y改变数据类型
    return(x,y)

#(2)数据加载
# 对训练集预处理
y = tf.one_hot(y,depth=10)  # one-hot编码,转换成长度为10的向量,对应索引的值变为1
ds_train = tf.data.Dataset.from_tensor_slices((x,y)) # 自动将x和y转换为tensor类型
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)) # 运行一次取出一个batch,即128个数据
print('x_batch:',sample[0].shape,'y_batch:',sample[1].shape) # 查看一次取了多少个

构造一个迭代器来查看我们预处理后的数据是否正确,起检验作用。iter()函数生成一个迭代器,每执行一次next()函数,从ds_train中获取一组大小为batch的样本(x, y),sample[0]保存x的数据,sample[1]保存y的数据。

# 输出结果
x.shape=(60000, 28, 28),y.shape=(60000,)
x_test.shape=(10000, 28, 28),y_test.shape=(10000,)
y[:5]=[9 0 0 3 0]
x_batch: (128, 28, 28) y_batch: (128, 10)

3. 构建网络

使用堆层的方法构建全连接层 tf.keras.Sequential(),使用 tf.keras.layers.Dense()添加每一层,构建5层的全连接层,指定激活函数为relu函数,使网络的维度从[b,28*28]变换到最终的[b,10],即输出10个分类的结果。使用model.build()函数指定输入层的输入特征大小,model.summary()查看整个网络的结构,指定优化器跟新权重和偏置optimizers.Adam(),学习率为0.001,即梯度下降速度。

#(3)构建网络
# ==1== 设置全连接层
# [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个分类
    ])

# ==2== 设置输入层维度
model.build(input_shape=[None, 28*28])
# ==3== 查看网络结构
model.summary()
# ==4== 优化器
# 完成权重更新 w = w - lr * grad
optimizer = optimizers.Adam(lr=1e-3) 

网络结构如下,param代表每一层的参数个数,以最后一层为例,权重w的shape为[32,10],偏置b的shape为[10],参数个数为32*10+10=330

Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 dense (Dense)               (None, 256)               200960    
                                                                 
 dense_1 (Dense)             (None, 128)               32896     
                                                                 
 dense_2 (Dense)             (None, 64)                8256      
                                                                 
 dense_3 (Dense)             (None, 32)                2080      
                                                                 
 dense_4 (Dense)             (None, 10)                330       
                                                                 
=================================================================
Total params: 244,522
Trainable params: 244,522
Non-trainable params: 0
_________________________________________________________________

4. 网络训练

设置整个网络循环20次。for step,(x,y) in enumerate(ds_train)代表每次从训练集中取batch个样本用于训练,等把样本都取完了就完成一次循环。每次取出的x的shape为[128, 28, 28],y的shape为[128, 10]

使用model(x)自动对输入的x完成权重和偏置计算,得到隐含层最后一层的输出结果logits。接下来计算损失函数,使用交叉熵更新权重偏置。梯度计算tape.gradient(),以loss2为因变量,权重和偏置为自变量计算梯度,model.trainable_variables()获取网络所有的权重和偏置。

zip(grads, model.trainable_variables) 将梯度和权重及偏置结合在一起。optimizer.apply_gradients(),使用优化器更新梯度,相当于计算w1 = w1 - lr * grads[0],并原地更新权重偏置不改变数据类型,简化了w1.assign_sub(lr * grads[0]),不需要一个一个手敲计算更新梯度。

#(5)前向传播
for epoch in range(20): # 运行20次
    # 运行每一个batch
    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) #得到最后一层的输出
            # 计算均方误差,真实值y(onehot编码后的)和输出结果之间
            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))
            
        # 梯度计算,第1个因变量,第2个自变量,model.trainable_variables获得所有的权重和偏置参数
        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])
         
        # 每次运行完一个batch后打印结果
        if step % 100 == 0:
            print(f'epochs:{epoch}, step:{step}, loss_MSE:{loss1}, loss_CE:{loss2}')

5. 网络测试

网络测试是在20次的大循环内部的,使用总预测对了的个数除以总测试样本数计算模型准确率。同样,每次迭代从测试集中取batch个样本,放入网络计算,得到输出层的结果logits,需要求预测结果概率最大值所在下标,知道了下标索引也就知道了每个测试样本属于第几个分类

使用tf.nn.softmax()函数将输出结果映射到0-1之间,且概率和为1,再配合tf.argmax()就知道概率最大值所在的下标值。将预测值predict和真实值y比较,看是否相同,若相同就是预测对了。tf.equal()返回布尔类型,如果两个变量对应位置的值相同就返回True,再将布尔类型转换位数值类型即可求和tf.reduce_sum()

#(6)网络测试--前向传播
    total_correct = 0 # 总预测对了的个数
    total_sum = 0 # 总统计的个数

    for (x,y) in ds_test: #返回测试集的x和y
        
        # 将x的shape从[b,28,28]=>[b,28*28]
        x = tf.reshape(x, [-1,28*28]) 
        # 计算输出层[b,10]
        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)

        # y是一个向量,每个元素代表属于第几类;predict也是一个向量,下标值指示属于第几类
        # 只要看两个变量的值是否相同
        correct = tf.equal(y, predict) #返回True和False
        # True和False变成1和0,统计1的个数,一共有多少个预测对了
        correct = tf.reduce_sum(tf.cast(correct, dtype=tf.int32))
        
        # 预测对了的个数,correct是tensor类型,变量numpy类型
        total_correct += int(correct) 
        total_sum += x.shape[0] #第0维度,每次测试有多少张图片 
    
    # 计算一次大循环之后的模型准确率
    acc = total_correct/total_sum
    print(f'acc: {acc}')

6. 结果展示

模型第1次循环,以及经过20次循环后的结果如下:

epochs: 0
loss_MSE: 16.709985733032227, loss_CE: 0.4360983967781067
acc: 0.839

epochs: 19
loss_MSE: 130.57691955566406, loss_CE: 0.27311569452285767
acc: 0.8878


完整代码如下:

# keras层方式做前向传播,服装图片分类每一张图片28*28
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' # '2'输出栏只打印error信息,其他乱七八糟的信息不打印

#(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项数据

#(2)数据预处理,转变为tenor类型
def processing(x,y):
    x = tf.cast(x,tf.float32)/255.0  # x数据改变数据类型,并归一化
    y = tf.cast(y,tf.int32)  # 对目标y改变数据类型
    return(x,y)

#(3)数据加载
# 对训练集预处理
y = tf.one_hot(y,depth=10)  # one-hot编码,转换成长度为10的向量,对应索引的值变为1
ds_train = tf.data.Dataset.from_tensor_slices((x,y)) # 自动将x和y转换为tensor类型
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)) # 运行一次取出一个batch,即128个数据
print('x_batch:',sample[0].shape,'y_batch:',sample[1].shape) # 查看一次取了多少个

#(4)构建网络
# ==1== 设置全连接层
# [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个分类
    ])

# ==2== 设置输入层维度
model.build(input_shape=[None, 28*28])

# ==3== 查看网络结构
model.summary()
# 330 = 权重参数32*10 + 偏置参数10

# ==4== 优化器
# 完成权重更新 w = w - lr * grad
optimizer = optimizers.Adam(lr=1e-3) 


#(5)模型训练--前向传播
for epoch in range(20): # 运行20次
    # 运行每一个batch
    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) #得到最后一层的输出
            # 计算均方误差,真实值y(onehot编码后的)和输出结果之间
            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))
            
        # 梯度计算,第1个因变量,第2个自变量,model.trainable_variables获得所有的权重和偏置参数
        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])
         
        # 每次运行完一个batch后打印结果
        # if step % 100 == 0:
        #     print(f'epochs:{epoch}, step:{step}, loss_MSE:{loss1}, loss_CE:{loss2}')
    # 一次循环完成的结果
    print(f'epochs: {epoch}, loss_MSE: {loss1}, loss_CE: {loss2}')
    
            
#(6)网络测试--前向传播
    total_correct = 0 # 总预测对了的个数
    total_sum = 0 # 总统计的个数

    for (x,y) in ds_test: #返回测试集的x和y
        
        # 将x的shape从[b,28,28]=>[b,28*28]
        x = tf.reshape(x, [-1,28*28]) 
        # 计算输出层[b,10]
        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)

        # y是一个向量,每个元素代表属于第几类;predict也是一个向量,下标值指示属于第几类
        # 只要看两个变量的值是否相同
        correct = tf.equal(y, predict) #返回True和False
        # True和False变成1和0,统计1的个数,一共有多少个预测对了
        correct = tf.reduce_sum(tf.cast(correct, dtype=tf.int32))
        
        # 预测对了的个数,correct是tensor类型,变量numpy类型
        total_correct += int(correct) 
        total_sum += x.shape[0] #第0维度,每次测试有多少张图片 
    
    # 计算一次大循环之后的模型准确率
    acc = total_correct/total_sum
    print(f'acc: {acc}')

  • 6
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

立Sir

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值