文章目录
- 一、数据集加载与处理流程
- 二、数据集处理
- 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()