CV&NLP基础7之TensorFlow进阶(上)

Fashion_mnist数据集介绍

Fashion-MNIST是一个替代MNIST手写数字集的图像数据集。它是由Zalando(一家德国的时尚科技公司)旗下的研究部门提供。其涵盖了来自10种类别的共7万个不同商品的正面图片。Fashion-MNIST的大小、格式和训练集/测试集划分与原始的MNIST完全一致。60000/10000的训练、验证数据划分,28x28的灰度图片。你可以直接用它来测试你的机器学习和深度学习算法性能,且不需要改动任何的代码。
Fashion-MNIST相对于MNIST需要更好的模型才能分辨出各个图片的不同,要用更复杂的网络来训练。

这个数据集的样子大致如下(每个类别占三行):
在这里插入图片描述

keras.Sequential()顺序模型

顺序模型是多个网络层的线性堆叠。将想要搭建的模型按顺序排放。

可以通过将网络层实例的列表传递给 Sequential(),来创建一个 Sequential 模型:

model = Sequential([ Dense(32, input_shape=(784,)),

                                   Activation('relu'),

                                   Dense(10),

                                   Activation('softmax')])

或者
model = Sequential([ layers.Dense(32, activation='relu', input_shape=(784,)),

                                  layers.Dense(10,activation='softmax')])

或者
model = Sequential()

model.add(Dense(32, input_dim=784))

model.add(Activation('relu'))

tf.convert_to_tensor函数的使用

函数描述:
将各种类型的python对象转化为tensor对象

将给定value转换为Tensor。
此函数将各种类型的Python对象转换为Tensor 对象。它接受Tensor对象,numpy数组,Python列表和Python标量。
参数:
value:需要转换的
dtype:返回张量的可选元素类型。如果缺少,则从value的类型推断出类型。
name:如果Tensor创建了new,则使用可选名称。
返回:
一个基于value的输出操作(ops),输出一个tensor(自己的理解)

convert_to_tensor(
    value,
    dtype=None,
    name=None,
    preferred_dtype=None
)

示例:

a = np.array([1,2,3])
b = tf.convert_to_tensor(a)
print(type(a)) # <class 'numpy.ndarray'>
print(type(b)) # <class 'tensorflow.python.framework.ops.Tensor'>

Softmax函数、categorical_crossentropy

[0,1]区间,和为 1

输出值𝑜𝑖 ∈ [0,1],且所有输出值之和为 1,这种设定以多分类问题最为常见。如图 6.15 所示。
在这里插入图片描述

输出层的每个输出节点代表了一种类别,图中网络结构用于处理 3 分类任务,3 个节点的输出值分布代表了当前样本属于类别 A、类别 B 和类别 C 的概率𝑃(A|𝒙)、𝑃(B|𝒙)、𝑃(C|𝒙),考虑多分类问题中的样本只可能属于所有类别中的某一种,因此满足所有类别概率之和为 1 的约束。
如何实现此约束逻辑呢?可以通过在输出层添加 Softmax 函数实现。Softmax 函数定义为:
在这里插入图片描述

Softmax 函数不仅可以将输出值映射到[0,1]区间,还满足所有的输出值之和为 1 的特性。 如图 6.14 中的例子:
在这里插入图片描述

输出层的输出为[2.0, 1.0, 0.1],经过 Softmax 函数计算后,得到输出为 [0.7, 0.2, 0.1],每个值代表了当前样本属于每个类别的概率,概率值之和为 1。通过 Softmax 函数可以将输出层的输出转译为类别概率,在分类问题中使用的非常频繁。
在 TensorFlow 中,可以通过 tf.nn.softmax 实现 Softmax 函数,代码如下:

In [12]: z = tf.constant([2.,1.,0.1])
tf.nn.softmax(z) # 通过 Softmax 函数

Out[12]:
<tf.Tensor: id=19, shape=(3,), dtype=float32, numpy=array([0.6590012,
0.242433 , 0.0985659], dtype=float32)>

与 Dense 层类似,Softmax 函数也可以作为网络层类使用,通过类 layers.Softmax(axis=-1) 可以方便添加 Softmax 层,其中 axis 参数指定需要进行计算的维度。

在 Softmax 函数的数值计算过程中,容易因输入值偏大发生数值溢出现象;在计算交叉熵时,也会出现数值溢出的问题。为了数值计算的稳定性,TensorFlow 中提供了一个统一的接口,将 Softmax 与交叉熵损失函数同时实现,同时也处理了数值不稳定的异常,一般推荐使用这些接口函数,避免分开使用 Softmax 函数与交叉熵损失函数。

函数式接口为 tf.keras.losses.categorical_crossentropy(y_true, y_pred, from_logits=False),其中 y_true 代表了 One-hot 编码后的真实标签(ground-truth、label y),y_pred 表示网络的预测值(out、logits)当 from_logits 设置为 True 时, y_pred 表示须为未经过 Softmax 函数的变量;当 from_logits 设置为 False 时,y_pred 表示为经过 Softmax 函数的输出为了数值计算稳定性,一般设置 from_logits 为 True,此时 tf.keras.losses.categorical_crossentropy 将在内部进行 Softmax 函数计算,所以不需要在模型中显式调用 Softmax 函数,例如:

In [13]:
y_pred = tf.random.normal([2, 10]) # 假设这个是输出层的输出
y_true = tf.constant([1, 3]) # 假设这个真实值
y_true_onehot = tf.one_hot(y_true, depth=10) #对y_true进行独热编码

# 输出层未使用Softmax函数,故from_logits设置为True
# 这样“categorical_crossentropy函数”在计算损失函数前,会针对y_pred在内部先调用Softmax函数
loss = keras.losses.categorical_crossentropy(y_true_onehot, y_pred, from_logits=True)
loss = tf.reduce_mean(loss) # 计算平均交叉熵损失
loss

Out [13]:
<tf.Tensor: id=210, shape=(), dtype=float32, numpy= 2.4201946

张量比较(tf.equal())

为了计算分类任务的准确率等指标,一般需要将预测结果和真实标签比较,统计比较结果中正确的数量来计算准确率。考虑 100 个样本的预测结果,通过 tf.argmax 获取预测类别,实现如下:

In [24]:out = tf.random.normal([100,10])
out = tf.nn.softmax(out, axis=1) # 输出转换为概率 
pred = tf.argmax(out, axis=1) # 计算预测值

Out[24]:<tf.Tensor: shape=(100,), dtype=int64, numpy=
array([8, 6, 0, 4, 8, 4, 2, 9, 8, 1, 8, 2, 4, 4, 9, 0, 9, 2, 7, 1, 5, 9,
       0, 8, 5, 5, 6, 6, 7, 4, 0, 7, 7, 1, 5, 5, 2, 3, 0, 8, 2, 6, 6, 5,
       5, 2, 1, 4, 8, 9, 4, 6, 9, 0, 1, 6, 7, 0, 1, 8, 3, 4, 1, 4, 9, 1,
       5, 5, 5, 0, 5, 0, 0, 8, 5, 3, 8, 9, 5, 2, 9, 9, 0, 8, 3, 3, 7, 1,
       3, 3, 2, 2, 9, 1, 4, 3, 9, 6, 6, 0])>

变量 pred 保存了这 100 个样本的预测类别值,我们与这 100 样本的真实标签比较,例如:

In [25]: # 模型生成真实标签
y = tf.random.uniform([100],dtype=tf.int64,maxval=10) 

Out[25]:<tf.Tensor: id=281, shape=(100,), dtype=int64, numpy=
<tf.Tensor: shape=(100,), dtype=int64, numpy=
array([1, 7, 1, 3, 6, 5, 6, 6, 3, 5, 9, 2, 1, 1, 5, 0, 5, 4, 9, 3, 2, 0,
       5, 0, 3, 1, 9, 2, 5, 7, 5, 2, 8, 7, 9, 5, 4, 8, 5, 8, 5, 3, 1, 2,
       9, 9, 1, 4, 9, 2, 1, 2, 8, 9, 1, 0, 6, 0, 0, 1, 8, 3, 6, 1, 9, 7,
       4, 3, 7, 2, 4, 2, 9, 7, 6, 5, 4, 9, 8, 4, 0, 4, 2, 8, 7, 5, 6, 7,
       1, 5, 8, 1, 7, 8, 8, 2, 9, 6, 4, 7])>

即可获得代表每个样本是否预测正确的布尔类型张量。通过 tf.equal(a, b)(或 tf.math.equal(a, b),两者等价)函数可以比较这 2 个张量是否相等,例如:

In [26]:out = tf.equal(pred,y) # 预测值与真实值比较,返回布尔类型的张量 

Out[26]: <tf.Tensor: shape=(100,), dtype=bool, numpy=
array([False, False, False, False, False, False, False, False, False,
       False, False,  True, False, False, False,  True, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False,  True,
       False, False, False,  True, False, False, False, False, False,
       False,  True,  True, False, False, False, False, False, False,
        True, False, False,  True, False, False, False, False, False,
       False,  True, False, False, False, False, False, False, False,
       False, False, False, False, False,  True, False, False, False,
       False, False,  True, False, False, False, False, False, False,
       False, False, False, False, False, False,  True,  True, False,
       False])>

tf.equal()函数返回布尔类型的张量比较结果,只需要统计张量中 True 元素的个数,即可知道预测正确的个数。为了达到这个目的,我们先将布尔类型转换为整形张量,即 True 对应 为 1,False 对应为 0,再求和其中 1 的个数,就可以得到比较结果中 True 元素的个数:

In [27]: out = tf.cast(out, dtype=tf.int32) # 布尔型转int型
correct = tf.reduce_sum(out) # 统计True的个数

Out[27]: <tf.Tensor: shape=(), dtype=int32, numpy=13>

可以看到,我们随机产生的预测数据中预测正确的个数是 13,因此它的准确度是 13/100=13%。这也是随机预测模型的正常水平。

在这里插入图片描述

实战

今天代码实践会使用到验证集计算模型准确率

代码及运行截图

导入各种包

import os # “os”是系统包
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # TensorFlow的最小打印日志的等级为2。
# TensorFlow集成了深度学习环境,每次运行都会显示一些电脑环境信息,设置了这个值,就可以隐藏一些不紧要的输出,更简洁。
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, optimizers, datasets, Sequential
from __future__ import division

运行截图:
在这里插入图片描述

获取数据

(x, y), (x_val, y_val) = datasets.fashion_mnist.load_data() 
# (x,y)是训练集;(x_val, y_val)是验证集。
print("训练集: ", x.shape, y.shape)
print(type(x),'/',type(y))
print(x.dtype, ' / ', y.dtype)
print('-----------------------------')
print("验证集: ", x_val.shape, y_val.shape)
print(type(x_val),'/',type(y_val))
print(x_val.dtype, ' / ', y_val.dtype)

运行截图:
在这里插入图片描述

处理数据

# 预处理函数,处理训练集、验证集时都用这个函数
def preprocessing(x_data, y_data): 
    x_data = tf.cast(x_data, dtype = tf.float32)/255 #不要忘记图片输入数据除以255
    y_data = tf.cast(y_data, dtype = tf.int32)
    #y_data = tf.one_hot(y_data, depth=10) #将标量y_data转换成独热编码y_data
    
    print(f'类型为: {type(x_data)} ; {type(y_data)}')
    print(f"各自里面元素类型为: {x_data.dtype} ; {y_data.dtype}")
    print('数据格式为: ', x_data.shape, y_data.shape)
    # print(tf.reduce_max(x_data), tf.reduce_min(x_data))
    print('--------------------------------------')
    
    return x_data, y_data
# 数据预处理,切片。600组,每组100对。切片、预处理顺序无所谓。一般是先切片。
# 下面两行是把数据切成“散片”,还没有batch
db_train = tf.data.Dataset.from_tensor_slices((x, y))
db_test = tf.data.Dataset.from_tensor_slices((x_val, y_val))

# 重点: 将 切成 散片的数据“反射”到与preprocessing()函数。
# 输出完毕后,再以100张图片为一组
print("训练集: ")
db_train = db_train.map(preprocessing).batch(100) # 每组100张图片,共 600 组
print("验证集: ")
db_test = db_test.map(preprocessing).batch(100)# 每组100张图片,共 100 组

运行截图:
在这里插入图片描述


# 看一下是否每一组是100张图片

# iter()将db_train变成可迭代对象,上一篇文章里面的train_dataset也可以这样操作
# sample是元组,保存了100张图片的输入、标签。里面有2个元素,某个100张图片的每张图片输入x、标签y
sample = next(iter(db_train)) 

 # 直接用sample.shape错误。sample[0]是100个图片的输入x,sample[1]是100个标签y
print(sample[0].shape, sample[1].shape)
print('-----------------------')
print(sample[0])
print('-----------------------')
print(sample[1])

运行截图:
在这里插入图片描述
在这里插入图片描述

搭建模型

model = keras.Sequential([
    layers.Dense(512, activation='relu', name='layer1'),
    layers.Dense(256, activation='relu', name='layer2'),
    layers.Dense(128, activation='relu', name='layer3'),
    layers.Dense(64, activation='relu', name='layer4'),
    layers.Dense(10, activation='relu', name='layer5'),
])
# 这里优化器不用SGD,换个用Adam,不用管具体区别,只要牢记优化器里的所有优化函数的核心就是“梯度下降”
optimizer = optimizers.Adam(learning_rate=0.0001)
# 下面两行代码只是用来看看这个框架都有些什么参数
model.build(input_shape=(None, 784)) # None意思是没有输入图片, 仅是测试而已
model.summary() # 用来看框架内部结构的函数

# 下面输出解读:
# 第一列Layer层:这里是Dense函数产生的层
# 第二列就是每道工序的“输出维度”
# 第三列的参数就是 w、b的个数,以第一层工序举例: 第一层的输入是[None ,784],
# 那么第一层的“W参数”就是[784, 512],“b参数”就是[512],所以784*512+512=401920个参数

运行截图:
在这里插入图片描述

使用“训练数据集”训练模型过程

# 大循环,整个数据集(这里是6万张图片)循环的次数
for epoch in range(20): 
    print(f"当前是 第 {epoch} 次 大循环")
    
    # 小循环,一次循环处理一个batch(这里是100张图片),共600次小循环
    for step, (x, y) in enumerate(db_train):
        
        # 打平操作
        # x:[b, 28, 28] => [b, 784]
        # y:[b, 1] => [b, 10]
        x = tf.reshape(x, (-1, 28*28))
        
        #将y转换成独热编码,可以放在预处理函数里,也可以放在这里
        y = tf.one_hot(y, depth=10)
        
        # 构建梯度环境
        with tf.GradientTape() as tape:
            
            # 用单词“logits”代表多分类问题的预测输出,也可以跟上一篇文章一样用“out”
            # x的维度,注意要与模型搭建时给的输入维度一致
            logits = model(x)
            
            # 计算loss。下面loss_mse、loss_ce是两种计算损失值的方法
            # 均方差
            # tf.losses.MSE(y, logits)相当于tf.square(logits-y)
            loss_mse = tf.reduce_mean(tf.losses.MSE(y, logits))
            
            # 多分类常用的一种loss函数
            # categorical: 分类;crossentropy: 交叉熵
            loss_ce = tf.losses.categorical_crossentropy(y, logits, from_logits=True)
            loss_ce = tf.reduce_mean(loss_ce) # 计算平均交叉熵损失
            
        # 注意缩进
        # 计算梯度,“model.trainable_variables”里面是各个w、b参数,更新参数w、b后也放入其中 
        # grads 里面是 w、b的梯度
        grads = tape.gradient(loss_ce, model.trainable_variables)
        
        # 更新梯度 w' = w - learning rate * grad w      b' = b - learning rate * grad b
        optimizer.apply_gradients(zip(grads, model.trainable_variables))
        
        if step % 100 == 0:
            print('epoch: {}, step: {}, loss: {}'.format(epoch, step, float(loss_ce)))    

部分运行截图:
在这里插入图片描述

在这里插入图片描述

使用“验证数据集”来看看模型准确率

total_correct = 0 # 输入验证集后,记录“模型输出值“和”真实值“相比后,相等的个数
total_number = 0 # 验证集图片总数(fashion-mnist的验证集图片数就是10000)

print("db_test的长度:", len(db_test))

#下面循环时用的x_val, y_val和获取数据时的不一样哟
for (x_val, y_val) in db_test: #100个图片为一组进行循环,一共100次循环
    # print(x_val.shape, y_val.shape)

    # x_val:[b, 28, 28] => [b, 784]
    # y_val:[b, 1] => [b, 10]
    x_val = tf.reshape(x_val, (-1, 28*28))
    
    logits_test = model(x_val) #logits_test的维度是[100, 10]
    
    probility = tf.nn.softmax(logits_test, axis=1)#probility的维度是[100, 10]
    
    # 取出各行概率最大的坐标。argmax默认返回int64。但是y_val是int32,所以下面要改变predict类型。
    # 每轮循环predict、y_val都是100个标量数字
    predict = tf.argmax(probility, axis=1) 
    predict = tf.cast(predict, dtype=tf.int32) #predict是100个标量数字
    
    bool_correct = tf.equal(predict, y_val) # 返回布尔型。每轮循环predict、y_val都是100个标量数字,不需要独热编码
    correct = tf.cast(bool_correct, dtype=tf.int32) # True转换为 1, False转换为 0
    
    total_true = tf.reduce_sum(correct) # 加起来的和就是True的个数
    total_correct += int(total_true) #把每次循环的True的个数逐渐累加起来
    total_number += x_val.shape[0] #每次循环都是100张图片,这里把每次循环的图片数累加起来
    
accuracy = total_correct / total_number
print(f"正确个数为: {total_correct}, 总图片数: {total_number}")
print('----------------------')
print("准确率 = {}".format(accuracy))

运行截图:
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值