深度学习_06_1_神经网络与全连接层_数据加载&测试(张量)实战

06_1_神经网络与全连接层_数据加载&测试(张量)实战

数据集加载

Outline

这次主要讲解一些小型常用的经典数据集的加载步骤

  • keras.datasets

  • tf.data.Dataset.from_tensor_slices 将数据加载进Tensor

    • 一些操作

    • shuffle

    • map 转换

    • batch 多个样本组成一个batch

    • repeat

  • we will talk input pipeline later(大型的数据集,根据自己的需求,一项一项支持多线程的加载方式)

keras.datasets

  • 波士顿的房价,提供一些指定策略的因子,区域、面积、地铁、小区居民来源……通过考虑一些因子,制定价格
  • 手写数字mnist
  • 小型的图片识别数据集 cifar10/100
  • 对数据处理的入门数据集,类似于淘宝好评的数据集,电影的评语,会有标注好评还是差评,做用户评语的分析

MNIST

28*28,1

70K/60K(training)/10K(test)

MNIST加载案例:

import  os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'

import  tensorflow as tf
from tensorflow import keras

#返回numpy的格式,4个对象
# 前2个是tuple,train,60K;后2个是tuple,test,10K
(x,y),(x_test,y_test) = keras.datasets.mnist.load_data()
print(x.shape) #(60000, 28, 28)
#0~9的编码,都后面需要onehot encoding
print(y.shape) #(60000,)
#这里可以调用min,因为x还是numpy类型,因此是numpy的api
#像素点的灰度值,通常会除以255,使得数据scale(缩放)/归一到[0,1]或[-1,1]或[-0.5,0.5],有利于优化
print(x.min(),x.max(),x.mean())
#0 255 33.318421449829934

#(10000, 28, 28) (10000,)
print(x_test.shape,y_test.shape)

#[5 0 4 1] dtype=uint8
print(y[:4])

#onehot encoding,depth可以转换为多长的向量,然后会把对应index的值变为1
y_onehot = tf.one_hot(y,depth=10)
#[[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
#[1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]], shape=(2, 10), dtype=float32)
print(y_onehot[:2])

CIFAR10/100

小型图片识别的数据集。10和100是指类别。100是对于每一个大类再细分分为10类,共100类。因此是同一个数据集(图片)。

CIFAR10加载:

import  os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'

import  tensorflow as tf
from tensorflow import keras
#或cifar100
#50K train,10K test
(x,y),(x_test,y_test) = keras.datasets.cifar10.load_data()
#(50000, 32, 32, 3) (50000, 1) (10000, 32, 32, 3) (10000, 1)
print(x.shape,y.shape,x_test.shape,y_test.shape)
#现在x,y是numpy格式,之后需要转换为Tensor格式,之后需要iter(迭代)
#0 255
print(x.min(),x.max())
 #[[6]
 # [9]
 # [9]
 # [4]]
print(y[:4])

tf.data.Dataset

Dataset是专门用来数据集迭代的类,只需要numpy类型数据通过dataset提供的from_tensor_slices()接口,直接转换为Dataset类的对象。

先拿到numpy的数据,转换到Dataset里去,通过db就可以进行迭代,it = iter(db),it就是一个迭代器,再对迭代器做迭代-next(it),可以得到数据里的每一项内容,第三部分是得到了一张了image。

x-》x_Tensor,直接for x in x_Tensor: 这样也可以进行读取操作,为什么一定用Dataset?

  • x只能一张一张image的拿,不能读取一个batch,还没有做图片的预处理;Dataset可以完成这些内容,它还支持多线程

实际情况,我们要image和label同时拿取,Dataset可以一次接收2个数据,迭代的时候会一次返回两个这样的样本。[32,32,3]和[1]

在这里插入图片描述

.shuffle

为什么?因为neuro network(NN)的能力非常强,记忆能力非常强,0,1,2,3,4,5,6……按顺序学习可能会不利于。

所以需要shuffle打散,把图片的顺序打散,但是也要注意x和y的相对位置不能打散。当然也可以利用idx功能,在这里直接利用shuffle。

10000只是buffer,对功能不是很影响。

在这里插入图片描述

.map

数据预处理的功能

for x,y in (X,Y):

x,y->Tensor
->32位
->one-hot

预处理函数preprocess,它是对于每一个sample。我们只需要把它map到db后,它就能对于dataset中的每一个sample做一个预处理。

经过处理后image[32,32,3];label[1,10],1表示batch,10表示onehot过

在这里插入图片描述

.batch

db2.batch(32)

我们看到y是[32,1,10],希望把1 squize(压扁)掉。如果[60K,1]把1 squize掉,这里就会变成[32,10]

这些操作后,for x,y in db3:

结果是,x[b,32,32,3] y[b,10]

还有1个问题,如果是for没有问题,但是如果使用while,最后1次的x,y=next(it),会引发错误。

在这里插入图片描述

StopIteration

当对数据集完成了一次迭代,就会发生异常StopIteration。

取完60K后,如果下次还想循环地再从头取数据集:可以catch StopIteration,再构造一次db的iterator,再一次next(db_iter)。

在这里插入图片描述

.repeat()

如果你想好了只需要数据集迭代2次或n次,只需要repeat(2),它可以很好地帮助你完成。这样for x,y in db4: 它会迭代2次。

如果想永远迭代的话,db4 = db3.repeat(),不需要填写参数。

在这里插入图片描述

For example

比较完整的流程:

onehot->Dataset->预处理->打散、batch……

import  os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'

import  tensorflow as tf
from tensorflow.keras import datasets

def prepare_mnist_features_and_labels(x,y):
    #数据类型的转换,除以255,scale(缩放)到0~1的范围内
    x = tf.cast(x,tf.float32) / 32
    y = tf.cast(y,tf.int64)
    return x,y

def mnist_dataset():
    #fashion_mnist类似mnist,大小也是28*28的灰度图片,实例不再是0~9,是衣服、帽子……
    #60K 10K
    (x,y),(x_val,y_val) = datasets.fashion_mnist.load_data()
    #进行onehot
    y = tf.one_hot(y,depth=10)
    y_val = tf.one_hot(y_val,depth=10)
    
    #转换到Dataset中去
    ds = tf.data.Dataset.from_tensor_slices((x,y))
    #预处理
    ds = ds.map(prepare_mnist_features_and_labels)
    #打散,100的batch,这里没有repeat
    ds = ds.shuffle(60000).batch(100)
    ds_val = tf.data.Dataset.from_tensor_slices((x_val,y_val))
    ds_val = ds_val.map(prepare_mnist_features_and_labels)
    ds_val = ds_val.shuffle(10000).batch(100)
    return ds,ds_val

测试(张量)实战

import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets

# 加载数据集,自动从网上下载
# x:[60k,28,28],[10k,28,28] y:[60k],[10k]
(x, y), (x_test,y_test) = datasets.mnist.load_data()
# 转化成Tensor
# 原来x的范围为[0,255],y的范围为[0,9],255是灰度值,习惯把数据转换为0~1的数值适合优化
# x: [0,255] => [0,1.]
# y: [0,9]
x = tf.convert_to_tensor(x, dtype=tf.float32) / 255.
y = tf.convert_to_tensor(y, dtype=tf.int32)

x_test = tf.convert_to_tensor(x_test, dtype=tf.float32) / 255.
y_test = tf.convert_to_tensor(y_test, dtype=tf.int32)

print(x.shape, y.shape, x.dtype, y.dtype)
# 看x和y的最小值和最大值
print(tf.reduce_min(x), tf.reduce_max(x))
print(tf.reduce_min(y), tf.reduce_max(y))

# 设置batch,希望一次能取128张照片
train_db = tf.data.Dataset.from_tensor_slices((x, y)).batch(128)
test_db = tf.data.Dataset.from_tensor_slices((x_test,y_test)).batch(128)
# train_iter可以对train_db做next方法
train_iter = iter(train_db)
# 一直取,直到得到一个最终的元素。这里是一个sample例子
# x:(128, 28, 28) y:(128,)
sample = next(train_iter)
print('batch: ', sample[0].shape, sample[1].shape)

# 创建权值
# 层是降维的过程,一开始是[b,784] => 降维[b,256] => 降维[b,128] => 降维[b,10]
# w的shape要满足运算规则,因此为[dim_in,dim_out],初始化为截断的正态分布
# b的shape为[dim_out],初始化为0
w1 = tf.Variable(tf.random.truncated_normal([784, 256], stddev=0.1))
b1 = tf.Variable(tf.zeros([256]))
w2 = tf.Variable(tf.random.truncated_normal([256, 128], stddev=0.1))
b2 = tf.Variable(tf.zeros([128]))
w3 = tf.Variable(tf.random.truncated_normal([128, 10], stddev=0.1))
b3 = tf.Variable(tf.zeros([10]))

lr = 1e-3  # 10^-3

for epoch in range(100):  # 对整个数据集迭代10次,iterate db for 10,表示对数据集迭代多少次
    # 对一次数据集的所有图片做一个循环
    # for (x, y) in train_db:
    for step, (x, y) in enumerate(train_db):
    # for every batch,表示对当前的数据集,你迭代的数据集,迭代哪个进度。比如说batch为128,一共有60k图片,一共取多少次,step表示当前进度的表示条
        # x:[128,28,28]
        # y:[128]

        # 维度变换 [b,28,28] => [b,28*28]
        x = tf.reshape(x, [-1, 28 * 28])

        # 使得梯度信息被记录下来
        with tf.GradientTape() as tape:  # 默认只会跟踪tf.Variable类型
            # 在这里我们希望x:[b,28*28]
            # h1 = x@w1 + b1
            # [b,784]@[784,256] + [256] => [b,256]+[256] 加号会自动做broadcast/自己做broadcast
            # b是batch,在这里b为128,也就是x.shape[0]
            # => [b,256] + [b,256] => [b,256]
            h1 = x @ w1 + tf.broadcast_to(b1, [x.shape[0], 256])
            # 非线性转化
            h1 = tf.nn.relu(h1)
            # [b,256] => [b,128]
            h2 = h1 @ w2 + b2
            h2 = tf.nn.relu(h2)
            # [b,128] => [b,10]
            out = h2 @ w3 + b3

            # compute loss
            # 输出out:[b,10]
            # 真实结果y:[b],因此要将y进行one-hot encoding
            # =>[b,10]
            y_onehot = tf.one_hot(y, depth=10)

            # mse(均方误差) = mean(sum(y-out)^2) y-out的平方和,之后平均
            # y-out 还是shape为[b,10]
            loss = tf.square(y_onehot - out)
            # 均值mean:得到scalar
            # 相当于 loss / b / 10。/b是求出每一个batch上的均值;/10是求每个instance上每一个点的均值
            loss = tf.reduce_mean(loss)
            # 需要自动求导,需要把前向计算的过程,也就是需要求梯度的过程,把它包在GradientTape

        # compute gradients
        grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3])
        # print(grads)
        # 本来optimizer可以一次w1 = w1 - lr * w1_grad,在这里进行手写
        # 在这里相减后,w1又从tf.Variable变回了tf.Tensor,这里使用assign_sub进行原地更新,保持数据类型(Variable类型)不变
        w1.assign_sub(lr * grads[0])
        # w1 = w1 - lr * grads[0]
        b1.assign_sub(lr * grads[1])
        w2.assign_sub(lr * grads[2])
        b2.assign_sub(lr * grads[3])
        w3.assign_sub(lr * grads[4])
        b3.assign_sub(lr * grads[5])

        # print(isinstance(b3,tf.Variable))
        # print(isinstance(b3,tf.Tensor))
        if step % 100 == 0:
            print(epoch, step, 'loss:', float(loss))
    # 100 loss: nan 梯度爆炸,解决方案:给6个参数一个范围值,stddev=0.1



    # test/evaluation 测试/评估集
    total_correct, total_num = 0, 0
    #使用测试集
    #使用当前的w1,b1,w2,b2,w3,b3
    for step,(x,y) in enumerate(test_db):
        # x的shape:[b,28,28] => [b,28*28]
        x = tf.reshape(x,[-1,28*28])

        # [b, 784] => [b,256] => [b,128] => [b,10]
        h1 = tf.nn.relu(x@w1+b1)
        h2 = tf.nn.relu(h1@w2+b2)
        out = h2@w3+b3

        # out是属于实数的范围,out: [b,10] ~ R
        # 概率需要转化到0~1的范围,prob: [b,10] ~ [0,1]
        # softmax实现1对1的映射,缩放映射到0~1的范围,并且保持概率之和为1
        prob = tf.nn.softmax(out,axis=1)
        #这样还是会得到[b,10]的shape

        # 选取概率最大的那个值所在的索引,并且会消掉1维度,[b,10] => [b]
        pred = tf.argmax(prob,axis=1)
        # pred是int64不能和int32的y比较,所以需要转化为int32
        pred = tf.cast(pred,dtype=tf.int32)
        # 这里y: [b],不需要转换成onehot;但是训练的时候需要,因为要计算mse(Mean Square Error)
        #equal做比较,返回boolean的Tensor,然后利用cast转化为int32
        correct = tf.cast(tf.equal(pred,y),dtype=tf.int32)
        correct = tf.reduce_sum(correct)

        #累加一次batch中正确的次数,最后获得总的正确的个数
        total_correct+=int(correct)
        #x.shape[0]等于batch
        #最后获得总的测试样本的数量
        total_num += x.shape[0]
    #准确率
    acc = total_correct / total_num
    print('test acc:',acc)

结果:

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值