文章目录

  • 一、数据集加载与处理流程
  • 二、数据集处理
  • 2.1 随机打散
  • 2.2 批训练
  • 2.3 预处理
  • 2.4 循环训练
  • 三、数据集加载与处理实战


一、数据集加载与处理流程

  利用tensorflow提供的工具便捷的加载经典数据集。自定义的数据集以后再讲。
  tensorflow中keras.Datasets数据集对象,方便实现多线程、预处理、随机打散、批训练等常用数据集的功能。
  常用经典数据集:
  (1)Boston housing:波士顿房价趋势数据集
  (2)CIFAR10/100:真实图片数据集,用于图片分类
  (3)MINIST/Fashion MINIST:手写数字图片数据集。
  (4)IMDB:情感分类任务数据集,用于文本分类。
   (1)数据加载到内存:通过tf.keras.datasets.xxx.load_data()函数将经典数据集自动加载到内存。 其中xxx代表具体的数据集名称如CIFAR10、MINIST,TensorFlow会默认将数据缓存在用户目录下的.keras/datasets文件夹下。
   (2)数据转为Dataset对象:数据加载到内存后,需要转换成Dataset对象,才能使用tensorflow提供的各种便捷功能。通过tf.data.Datasets.from_tensor_slice((x,y))实现
   (3)数据集处理:数据转换为Datasets对象后,一般需要添加一系列的数据集标准处理步骤,如:随机打散、预处理、按批装载。

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

'''(1)加载minist数据集:load_data返回元组用于保存训练的数据'''
(x,y),(x_test,y_test)=datasets.mnist.load_data()
print('x:',x.shape,'y:',y.shape,'x_test',x_test.shape,'y_test',y_test.shape)
#x: (60000, 28, 28) y: (60000,) x_test (10000, 28, 28) y_test (10000,)

'''(2)加载的数据转换成Dataset对象,才能利用tensorflow提供的便捷功能对数据进行操作。'''
train_db=tf.data.Dataset.from_tensor_slices((x,y))#构建Dataset对象

'''(3)将数据集转换成Dataset对象后,需要添加一系列的数据集处理步骤,如随机打散、预处理、按批装载.'''

二、数据集处理

2.1 随机打散

语法:Dataset.shuffle(buffer_size)随机打散数据集的顺序,防止每次训练时数据按固定顺序产生。使得模型尝试记忆标签信息。

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

#(1)加载minist数据集:load_data返回元祖用于保存训练的数据
(x,y),(x_test,y_test)=datasets.mnist.load_data()
print('x:',x.shape,'y:',y.shape,'x_test',x_test.shape,'y_test',y_test.shape)
#x: (60000, 28, 28) y: (60000,) x_test (10000, 28, 28) y_test (10000,)

#(2)加载的数据转换成Dataset对象,才能利用tensorflow提供的便捷功能对数据进行操作。
train_db=tf.data.Dataset.from_tensor_slices((x,y))#构建Dataset对象

#(3)将数据集转换成Dataset对象后,需要添加一系列的数据集处理步骤,如随机打散、预处理、按批装载

2.2 批训练

语法: Dataset.batch(num)。利用显卡的并行计算能力,在网络的计算过程中会同时计算多个样本,这种训练方式叫做批训练,其中一个批中样本的数量叫做 Batch Size。
  一般批量大小根据用户显存资源来设置,显存不足时,可以适当减小batchsize

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

#(1)加载minist数据集:load_data返回元祖用于保存训练的数据
(x,y),(x_test,y_test)=datasets.mnist.load_data()
print('x:',x.shape,'y:',y.shape,'x_test',x_test.shape,'y_test',y_test.shape)
#x: (60000, 28, 28) y: (60000,) x_test (10000, 28, 28) y_test (10000,)

#(2)加载的数据转换成Dataset对象,才能利用tensorflow提供的便捷功能对数据进行操作。
train_db=tf.data.Dataset.from_tensor_slices((x,y))#构建Dataset对象

#(3)将数据集转换成Dataset对象后,需要添加一系列的数据集处理步骤,如随机打散、预处理、按批装载
#批训练
train_db=train_db.batch(128)#设置批训练,batch size为128

2.3 预处理

  从 keras.datasets 中加载的数据集的格式大部分情况都不能直接满足模型的输入要求,因此需要根据用户的逻辑自行实现预处理步骤。
  语法:Dataset .map(func)。其中Dataset 对象通过提供 map(func)工具函数,调用用户自定义的预处理逻辑函数。

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

#(1)加载minist数据集:load_data返回元祖用于保存训练的数据
(x,y),(x_test,y_test)=datasets.mnist.load_data()
print('x:',x.shape,'y:',y.shape,'x_test',x_test.shape,'y_test',y_test.shape)
#x: (60000, 28, 28) y: (60000,) x_test (10000, 28, 28) y_test (10000,)

#(2)加载的数据转换成Dataset对象,才能利用tensorflow提供的便捷功能对数据进行操作。
train_db=tf.data.Dataset.from_tensor_slices((x,y))#构建Dataset对象

#(3)将数据集转换成Dataset对象后,需要添加一系列的数据集处理步骤,如随机打散、预处理、按批装载
#预处理:调用用户自定义的预处理逻辑
def preprocess(x, y):  # 自定义的预处理函数
  # 调用此函数时会自动传入 x,y 对象,shape 为[b, 28, 28], [b]
  # 标准化到 0~1
  x = tf.cast(x, dtype=tf.float32) / 255.
  x = tf.reshape(x, [-1, 28 * 28])  # 打平
  y = tf.cast(y, dtype=tf.int32)  # 转成整型张量
  y = tf.one_hot(y, depth=10)  # one-hot 编码
  # 返回的 x,y 将替换传入的 x,y 参数,从而实现数据的预处理功能
  return x, y
  
train_db=train_db.map(preprocess)

2.4 循环训练

对于 Dataset 对象,在使用时可以通过
   (1)for step, (x,y) in enumerate(train_db): # 迭代数据集对象,带 step 参数

  (2)for x,y in train_db: # 迭代数据集对象
   方式进行迭代,每次返回的 x 和 y 对象即为批量样本和批量标签。当对 train_db 的所有样本完成一次迭代后,for 循环终止退出。
   完成一个 Batch 的数据训练,叫做一个 Step;通过多个 step 来完成整个训练集的一次迭代,叫做一个 Epoch。
   在实际训练时,通常需要对数据集迭代多个 Epoch 才能取得较好地训练效果。
   例如,固定训练 20 个 Epoch,实现如下:
   for epoch in range(20): # 训练 Epoch 数
      for step, (x,y) in enumerate(train_db): # 迭代 Step 数

三、数据集加载与处理实战

#%%
import  matplotlib
from    matplotlib import pyplot as plt
# Default parameters for plots
matplotlib.rcParams['font.size'] = 20
matplotlib.rcParams['figure.titlesize'] = 20
matplotlib.rcParams['figure.figsize'] = [9, 7]
matplotlib.rcParams['font.family'] = ['STKaiTi']
matplotlib.rcParams['axes.unicode_minus']=False 
import  tensorflow as tf
from    tensorflow import keras
from    tensorflow.keras import datasets, layers, optimizers
import  os


os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
print(tf.__version__)

'''自定义数据预处理函数'''
def preprocess(x, y): 
    # [b, 28, 28], [b]
    print(x.shape,y.shape)
    x = tf.cast(x, dtype=tf.float32) / 255.
    x = tf.reshape(x, [-1, 28*28])
    y = tf.cast(y, dtype=tf.int32)
    y = tf.one_hot(y, depth=10)
    return x,y

#加载数据集:将数据集加载到内存
(x, y), (x_test, y_test) = datasets.mnist.load_data()
print('x:', x.shape, 'y:', y.shape, 'x test:', x_test.shape, 'y test:', y_test)

batchsz = 512
#训练数据集转换为Dataset对象,方便使用tensorflow进行处理
train_db = tf.data.Dataset.from_tensor_slices((x, y))
#训练数据集处理
train_db = train_db.shuffle(1000)#s随机打乱
train_db = train_db.batch(batchsz)#批训练
train_db = train_db.map(preprocess)#预处理
train_db = train_db.repeat(20)#20epoch
#测试数据集转换为Dataset对象,方便使用tensorflow进行处理
test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test))
#测试数据集处理
test_db = test_db.shuffle(1000).batch(batchsz).map(preprocess)

x,y = next(iter(train_db))
print('train sample:', x.shape, y.shape)#(512, 784) (512, 10)


'''主函数'''
def main():
    lr = 1e-2
    accs,losses = [], []
    
    #将权重定义为变量,以便跟踪梯度进行优化更新
    # 784 => 512
    w1, b1 = tf.Variable(tf.random.normal([784, 256], stddev=0.1)), tf.Variable(tf.zeros([256]))
    # 512 => 256
    w2, b2 = tf.Variable(tf.random.normal([256, 128], stddev=0.1)), tf.Variable(tf.zeros([128]))
    # 256 => 10
    w3, b3 = tf.Variable(tf.random.normal([128, 10], stddev=0.1)), tf.Variable(tf.zeros([10]))
    
    #一个epoch训练,使用一遍数据集
    for step, (x,y) in enumerate(train_db):
        # [b, 28, 28] => [b, 784]
        x = tf.reshape(x, (-1, 784))#展平数据
        with tf.GradientTape() as tape:#前项传播,计算损失
            # layer1.
            h1 = x @ w1 + b1
            h1 = tf.nn.relu(h1)
            # layer2
            h2 = h1 @ w2 + b2
            h2 = tf.nn.relu(h2)
            # output
            out = h2 @ w3 + b3
            #out = tf.nn.relu(out)
            # [b, 10] - [b, 10]
            loss = tf.square(y-out)#计算每个样本的损失
            # [b, 10] => 标量
            loss = tf.reduce_mean(loss)#计算平均损失
        
        grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3]) #求梯度
        for p, g in zip([w1, b1, w2, b2, w3, b3], grads):#梯度更新
            p.assign_sub(lr * g)

        if step % 80 == 0:#每训练80批数据,损失这一批的损失并保存
            print(step, 'loss:', float(loss))
            losses.append(float(loss))
 
        if step %80 == 0:#每训练80批量数据,计算这一批的准确率
            total, total_correct = 0., 0
            for x, y in test_db:
                # layer1.
                h1 = x @ w1 + b1
                h1 = tf.nn.relu(h1)
                # layer2
                h2 = h1 @ w2 + b2
                h2 = tf.nn.relu(h2)
                # output
                out = h2 @ w3 + b3
                # [b, 10] => [b]
                pred = tf.argmax(out, axis=1)#计算预测值
                y = tf.argmax(y, axis=1)
                correct = tf.equal(pred, y)#找出预测正确的样本
            
                total_correct += tf.reduce_sum(tf.cast(correct, dtype=tf.int32)).numpy()#统计正确预测的样本的数量
                total += x.shape[0]#总样本的数量
            print(step, 'Evaluate Acc:', total_correct/total)#准确率
            accs.append(total_correct/total)#保存准确率

    plt.figure()
    x = [i*80 for i in range(len(losses))]
    plt.plot(x, losses, color='C0', marker='s', label='训练')
    plt.ylabel('MSE')
    plt.xlabel('Step')
    plt.legend()
    plt.savefig('train.svg')

    plt.figure()
    plt.plot(x, accs, color='C1', marker='s', label='测试')
    plt.ylabel('准确率')
    plt.xlabel('Step')
    plt.legend()
    plt.savefig('test.svg')

if __name__ == '__main__':
    main()