文章目录
推荐文章
一.合并与分割
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 numpy as np import tensorflow as tf from tensorflow import keras from tensorflow.keras import datasets, layers, optimizers import os from mpl_toolkits.mplot3d import Axes3D
1.1 合并
在 TensorFlow 中,能够经过 tf.concat(tensors, axis)
,其中 tensors 保存了全部须要
合并的张量 List,axis 指定须要合并的维度。合并张量 A,B 以下:
python
a = tf.random.normal([2,4]) # 模拟成绩册 A b = tf.random.normal([2,4]) # 模拟成绩册 B tf.concat([a,b],axis=0)
<tf.Tensor: shape=(4, 4), dtype=float32, numpy= array([[ 0.16198424, -0.7170487 , -0.20940438, -0.46842927], [ 0.48012358, 0.82777774, -0.37541786, -0.6629169 ], [-0.15179256, -0.41726607, -1.9034436 , 0.72443116], [-0.48332193, 0.23101914, 0.87393326, -1.2042308 ]], dtype=float32)>
tf.concat([a,b],axis=1)
<tf.Tensor: shape=(2, 8), dtype=float32, numpy= array([[ 0.16198424, -0.7170487 , -0.20940438, -0.46842927, -0.15179256, -0.41726607, -1.9034436 , 0.72443116], [ 0.48012358, 0.82777774, -0.37541786, -0.6629169 , -0.48332193, 0.23101914, 0.87393326, -1.2042308 ]], dtype=float32)>
使用 tf.stack(tensors, axis)
能够合并多个张量 tensors, 当axis ≥ 0时,在 axis 以前插入;当axis < 0时,在 axis 以后插入新维度。算法
a = tf.random.normal([2,2]) b = tf.random.normal([2,2]) tf.stack([a,b],axis=0) #
<tf.Tensor: shape=(2, 2, 2), dtype=float32, numpy= array([[[-2.1611633 , 0.4318549 ], [-1.7556009 , 0.6960343 ]], [[-0.84239227, 0.9800302 ], [ 0.5497298 , 0.0607984 ]]], dtype=float32)>
一样能够选择在其余位置插入新维度,如在最末尾插入:缓存
a = tf.random.normal([2,2]) b = tf.random.normal([2,2]) tf.stack([a,b],axis=-1)
<tf.Tensor: shape=(2, 2, 2), dtype=float32, numpy= array([[[-2.09798 , 0.5505884 ], [-1.1372471 , 0.08376882]], [[-1.0453051 , 0.47830236], [-1.1234645 , -0.97358865]]], dtype=float32)>
1.2 分割
合并操做的逆过程就是分割,将一个张量分拆为多个张量。网络
经过 tf.split(x, axis, num_or_size_splits)
能够完成张量的分割操做:多线程
-x:待分割张量 -axis:分割的维度索引号 -num_or_size_splits:切割方案。当 num_or_size_splits 为单个数值时,如 10,表示切割为 10 份;当 num_or_size_splits 为 List 时,每一个元素表示每份的长度, 如[2,4,2,2]表示切割为 4 份,每份的长度分别为 2,4,2,2
x = tf.random.normal([4,2]) print(x) result = tf.split(x, axis = 0, num_or_size_splits=2) result
tf.Tensor( [[ 0.77127916 0.62768835] [-0.76758057 1.3676474 ] [-0.10122015 -0.917917 ] [-0.1592799 -0.33670765]], shape=(4, 2), dtype=float32) [<tf.Tensor: shape=(2, 2), dtype=float32, numpy= array([[ 0.77127916, 0.62768835], [-0.76758057, 1.3676474 ]], dtype=float32)>, <tf.Tensor: shape=(2, 2), dtype=float32, numpy= array([[-0.10122015, -0.917917 ], [-0.1592799 , -0.33670765]], dtype=float32)>]
tf.split(x, axis = 0, num_or_size_splits=[1,2,1])
[<tf.Tensor: shape=(1, 2), dtype=float32, numpy=array([[0.77127916, 0.62768835]], dtype=float32)>, <tf.Tensor: shape=(2, 2), dtype=float32, numpy= array([[-0.76758057, 1.3676474 ], [-0.10122015, -0.917917 ]], dtype=float32)>, <tf.Tensor: shape=(1, 2), dtype=float32, numpy=array([[-0.1592799 , -0.33670765]], dtype=float32)>]
若是但愿在某个维度上所有按长度为 1 的方式分割,还能够直接使用 tf.unstack(x, axis)
。这种方式是 tf.split
的一种特殊状况,切割长度固定为 1,只须要指定切割维度即
可。
app
x = tf.random.normal([4,2]) tf.unstack(x, axis = 0)
[<tf.Tensor: shape=(2,), dtype=float32, numpy=array([-0.69661826, 0.42463547], dtype=float32)>, <tf.Tensor: shape=(2,), dtype=float32, numpy=array([ 0.40786335, -0.9408407 ], dtype=float32)>, <tf.Tensor: shape=(2,), dtype=float32, numpy=array([-0.71312106, -0.33494622], dtype=float32)>, <tf.Tensor: shape=(2,), dtype=float32, numpy=array([0.9833806, 0.7918092], dtype=float32)>]
2.数据统计
在神经网络的计算过程当中,常常须要统计数据的各类属性,如最大值,均值,范数等。dom
2.1 向量范数
L1 范数,定义为向量 𝒙 的全部元素绝对值之和svg
x = tf.ones([2,2]) tf.norm(x, ord = 1)
<tf.Tensor: shape=(), dtype=float32, numpy=4.0>
L2 范数,定义为向量 𝒙 的全部元素的平方和,再开根号函数
tf.norm(x, ord = 2)
<tf.Tensor: shape=(), dtype=float32, numpy=2.0>
∞ −范数,定义为向量 𝒙 的全部元素绝对值的最大值工具
tf.norm(x, ord = np.inf)
<tf.Tensor: shape=(), dtype=float32, numpy=1.0>
2.2 最大最小值、均值、和
经过 tf.reduce_max, tf.reduce_min, tf.reduce_mean, tf.reduce_sum
能够求解张量在某个维度上的最大、最小、均值、和,也能够求全局最大、最小、均值、和信息。
x = tf.random.normal([2,3]) tf.reduce_max(x, axis = 1)
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([1.1455595, 0.8110037], dtype=float32)>
tf.reduce_min(x, axis = 1)
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([-0.8374149, -1.2768023], dtype=float32)>
tf.reduce_mean(x, axis = 1)
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([ 0.21712641, -0.16247804], dtype=float32)>
tf.reduce_sum(x, axis = 1)
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([ 0.6513792 , -0.48743412], dtype=float32)>
在求解偏差函数时,经过 TensorFlow 的 MSE 偏差函数能够求得每一个样本的偏差,需
要计算样本的平均偏差,此时能够经过 tf.reduce_mean
在样本数维度上计算均值:
out = tf.random.normal([4,10]) # 网络预测输出 y = tf.constant([1,2,2,0]) # 真实标签 y = tf.one_hot(y,depth=10) # one-hot 编码 loss = keras.losses.mse(y,out) # 计算每一个样本的偏差 loss = tf.reduce_mean(loss) # 平均偏差 loss
<tf.Tensor: shape=(), dtype=float32, numpy=1.0784723>
除了但愿获取张量的最值信息,还但愿得到最值所在的索引号,例如分类任务的标签
预测。考虑 10 分类问题,咱们获得神经网络的输出张量 out,shape 为[2,10],表明了 2 个
样本属于 10 个类别的几率,因为元素的位置索引表明了当前样本属于此类别的几率,预测
时每每会选择几率值最大的元素所在的索引号做为样本类别的预测值:
out = tf.random.normal([2,10]) out = tf.nn.softmax(out, axis=1) # 经过 softmax 转换为几率值 out
<tf.Tensor: shape=(2, 10), dtype=float32, numpy= array([[0.03961995, 0.26136935, 0.01498432, 0.03388612, 0.03053044, 0.05304638, 0.05151249, 0.0134019 , 0.17832054, 0.3233286 ], [0.06895317, 0.13860522, 0.14075696, 0.02185706, 0.04494175, 0.21044637, 0.20726745, 0.04014605, 0.01419329, 0.11283264]], dtype=float32)>
经过 tf.argmax(x, axis)
,tf.argmin(x, axis)
能够求解在 axis 轴上,x 的最大值、最小值所在的索引号:
pred = tf.argmax(out, axis=1) pred
<tf.Tensor: shape=(2,), dtype=int64, numpy=array([9, 5], dtype=int64)>
2.3 张量比较
为了计算分类任务的准确率等指标,通常须要将预测结果和真实标签比较,统计比较
结果中正确的数量来就是计算准确率。考虑 10 个样本的预测结果:
out = tf.random.normal([10,10]) out = tf.nn.softmax(out, axis=1) pred = tf.argmax(out, axis=1) pred
<tf.Tensor: shape=(10,), dtype=int64, numpy=array([3, 2, 4, 3, 0, 4, 5, 0, 2, 6], dtype=int64)>
能够看到咱们模拟的 10 个样本的预测值,咱们与这 10 样本的真实值比较:
y = tf.random.uniform([10],dtype=tf.int64,maxval=10) y
<tf.Tensor: shape=(10,), dtype=int64, numpy=array([7, 3, 9, 2, 7, 4, 3, 1, 4, 5], dtype=int64)>
经过 tf.equal(a, b)
(或 tf.math.equal(a, b)
)函数能够比较这 2个张量是否相等:
out = tf.equal(pred,y) out
<tf.Tensor: shape=(10,), dtype=bool, numpy= array([False, False, False, False, False, True, False, False, False, False])>
tf.equal()
函数返回布尔型的张量比较结果,只须要统计张量中 True 元素的个数,便可知道
预测正确的个数。为了达到这个目的,咱们先将布尔型转换为整形张量,再求和其中 1 的
个数,能够获得比较结果中 True 元素的个数:
out = tf.cast(out, dtype=tf.float32) # 布尔型转 int 型 correct = tf.reduce_sum(out) # 统计 True 的个数 correct
<tf.Tensor: shape=(), dtype=float32, numpy=1.0>
2.4 填充与复制
填充
填充操做能够经过 tf.pad(x, paddings)
函数实现,paddings 是包含了多个
[𝐿𝑒𝑓𝑡 𝑃𝑎𝑑𝑑𝑖𝑛𝑔, 𝑅𝑖𝑔ℎ𝑡 𝑃𝑎𝑑𝑑𝑖𝑛𝑔]的嵌套方案 List,如 [[0,0],[2,1],[1,2]]
表示第一个维度不填
充,第二个维度左边(起始处)填充两个单元,右边(结束处)填充一个单元,第三个维度左边
填充一个单元,右边填充两个单元。
b = tf.constant([1,2,3,4]) tf.pad(b, [[0,2]]) # 第一维,左边不填充,右边填充俩个
<tf.Tensor: shape=(6,), dtype=int32, numpy=array([1, 2, 3, 4, 0, 0])>
tf.pad(b, [[2,2]])#第一维,左边填充俩个,右边填充俩个
<tf.Tensor: shape=(8,), dtype=int32, numpy=array([0, 0, 1, 2, 3, 4, 0, 0])>
复制
经过 tf.tile
函数能够在任意维度将数据重复复制多份
x = tf.random.normal([2,2]) tf.tile(x, [1,2])
<tf.Tensor: shape=(2, 4), dtype=float32, numpy= array([[ 1.462598 , 1.7452018 , 1.462598 , 1.7452018 ], [-1.4659724 , -0.47004214, -1.4659724 , -0.47004214]], dtype=float32)>
3.数据限幅
在 TensorFlow 中,能够经过 tf.maximum(x, a)
实现数据的下限幅:𝑥 ∈ [𝑎, +∞);能够
经过 tf.minimum(x, a)
实现数据的上限幅:𝑥 ∈ (−∞,𝑎],举例以下:
x = tf.range(9) tf.maximum(x, 3) # 下限幅3
<tf.Tensor: shape=(9,), dtype=int32, numpy=array([3, 3, 3, 3, 4, 5, 6, 7, 8])>
tf.minimum(x, 5) # 上限幅5
<tf.Tensor: shape=(9,), dtype=int32, numpy=array([0, 1, 2, 3, 4, 5, 5, 5, 5])>
ReLU
函数能够实现为:
def relu(x): return tf.minimum(x,0.) # 下限幅为 0 便可
经过组合 tf.maximum(x, a)
和 tf.minimum(x, b)
能够实现同时对数据的上下边界限幅:
𝑥 ∈ [𝑎, 𝑏]:
x = tf.range(9) tf.minimum(tf.maximum(x, 2), 7)
<tf.Tensor: shape=(9,), dtype=int32, numpy=array([2, 2, 2, 3, 4, 5, 6, 7, 7])>
更方便地,咱们可使用 tf.clip_by_value
实现上下限幅:
tf.clip_by_value(x,2,7) # 限幅为 2~7
<tf.Tensor: shape=(9,), dtype=int32, numpy=array([2, 2, 2, 3, 4, 5, 6, 7, 7])>
4.高级操做
4.1 tf.gather
x = tf.random.uniform([4,3,2],maxval=100,dtype=tf.int32) tf.gather(x,[0,1],axis=0)
<tf.Tensor: shape=(2, 3, 2), dtype=int32, numpy= array([[[51, 45], [36, 18], [56, 57]], [[18, 16], [64, 82], [13, 4]]])>
实际上,对于上述需求,经过切片𝑥[: 2]能够更加方便地实现。
x[0:2]
<tf.Tensor: shape=(2, 3, 2), dtype=int32, numpy= array([[[51, 45], [36, 18], [56, 57]], [[18, 16], [64, 82], [13, 4]]])>
可是对于不规则的索引方式,好比,须要抽查全部班级的第 1,4,9,12,13,27 号同窗的成绩,则切片方式实现起来很是麻烦,而 tf.gather 则是针对于此需求设计的,使用起来很是方便:
x = tf.random.uniform([10,3,2],maxval=100,dtype=tf.int32) tf.gather(x,[0,3,8],axis=0)
<tf.Tensor: shape=(3, 3, 2), dtype=int32, numpy= array([[[86, 82], [32, 80], [35, 71]], [[97, 16], [22, 83], [20, 82]], [[79, 86], [13, 46], [68, 23]]])>
4.2 tf.gather_nd
经过 tf.gather_nd
,能够经过指定每次采样的坐标来实现采样多个点的目的。
x = tf.random.normal([3,4,4]) tf.gather_nd(x, [[1,2], [2,3]])
<tf.Tensor: shape=(2, 4), dtype=float32, numpy= array([[-0.5388145 , 0.00821999, 0.41113982, 1.0409608 ], [-0.42242923, -0.29552126, 0.6467382 , -1.7555269 ]], dtype=float32)>
tf.gather_nd(x, [[1,1,3], [2,3,3]])
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([ 0.07165062, -1.7555269 ], dtype=float32)>
4.3 tf.boolean_mask
除了能够经过给定索引号的方式采样,还能够经过给定掩码(mask)的方式采样。经过 tf.boolean_mask(x, mask, axis)
能够在 axis 轴上根据 mask 方案进行采样,实现为:
x = tf.random.normal([3,4,4]) tf.boolean_mask(x,mask=[True, True,False],axis=0)
<tf.Tensor: shape=(2, 4, 4), dtype=float32, numpy= array([[[ 1.0542077 , -0.48954943, -0.7491975 , -0.43464097], [-0.46667233, -1.2484705 , -1.7732694 , -1.2128644 ], [ 1.7540843 , 0.48327965, 0.95591843, -1.5143739 ], [ 1.3619318 , 1.168045 , -0.351565 , 0.1630519 ]], [[-0.13046652, -2.2438517 , -2.3416731 , 1.4573859 ], [ 0.3127366 , 1.4858567 , 0.24127336, -1.2466795 ], [-0.05732883, -0.75874144, 0.6504554 , 0.756288 ], [-2.8709486 , 0.11397363, -0.15979192, -0.07177942]]], dtype=float32)>
多维掩码采样
x = tf.random.uniform([2,3,4],maxval=100,dtype=tf.int32) print(x) tf.boolean_mask(x,[[True,True,False],[False,False,True]])
tf.Tensor( [[[63 32 59 60] [56 92 36 63] [53 66 69 30]] [[75 96 67 15] [17 11 64 38] [17 81 53 21]]], shape=(2, 3, 4), dtype=int32) <tf.Tensor: shape=(3, 4), dtype=int32, numpy= array([[63, 32, 59, 60], [56, 92, 36, 63], [17, 81, 53, 21]])>
4.4 tf.where
经过 tf.where(cond, a, b)
操做能够根据 cond 条件的真假从 a 或 b 中读取数据
a = tf.ones([3,3]) b = tf.zeros([3,3]) cond = tf.constant([[True,False,False],[False,True,False],[True,True,False]]) tf.where(cond,a,b)
<tf.Tensor: shape=(3, 3), dtype=float32, numpy= array([[1., 0., 0.], [0., 1., 0.], [1., 1., 0.]], dtype=float32)>
当 a = b = None 即 a,b 参数不指定时,tf.where
会返回 cond 张量中全部 True 的元素的索引坐标。
tf.where(cond)
<tf.Tensor: shape=(4, 2), dtype=int64, numpy= array([[0, 0], [1, 1], [2, 0], [2, 1]], dtype=int64)>
下面咱们来看一个例子
x = tf.random.normal([3,3]) mask = x > 0 mask
<tf.Tensor: shape=(3, 3), dtype=bool, numpy= array([[False, True, False], [ True, False, False], [ True, True, False]])>
经过 tf.where
提取此掩码处 True 元素的索引:
indices=tf.where(mask) # 提取为True 的元素索引 indices
<tf.Tensor: shape=(4, 2), dtype=int64, numpy= array([[0, 1], [1, 0], [2, 0], [2, 1]], dtype=int64)>
拿到索引后,经过 tf.gather_nd
便可恢复出全部正数的元素:
tf.gather_nd(x,indices) # 提取正数的元素值
<tf.Tensor: shape=(4,), dtype=float32, numpy=array([0.8857748 , 0.5748998 , 1.3066388 , 0.82504845], dtype=float32)>
也能够直接用下面的代码一步实现:
tf.boolean_mask(x,x > 0)
<tf.Tensor: shape=(4,), dtype=float32, numpy=array([0.8857748 , 0.5748998 , 1.3066388 , 0.82504845], dtype=float32)>
x[x>0]
<tf.Tensor: shape=(4,), dtype=float32, numpy=array([0.8857748 , 0.5748998 , 1.3066388 , 0.82504845], dtype=float32)>
4.5 scatter_nd
经过 tf.scatter_nd(indices, updates, shape)
能够高效地刷新张量的部分数据,可是只能在
全 0 张量的白板上面刷新,所以可能须要结合其余操做来实现现有张量的数据刷新功能。
scatter_nd
方法的更新示意图以下:
# 构造须要刷新数据的位置 indices = tf.constant([[4], [3],[1],[7]]) # 构造须要写入的数据 updates = tf.constant([4.4,3.3,1.1,7.7]) # 在长度为 8 的全 0 向量上根据 indices 写入 updates tf.scatter_nd(indices, updates, [8])
<tf.Tensor: shape=(8,), dtype=float32, numpy=array([0. , 1.1, 0. , 3.3, 4.4, 0. , 0. , 7.7], dtype=float32)>
咱们来看多维的数据更新状况
# 构造写入位置 indices = tf.constant([[1],[3]]) # 构造写入数据 updates = tf.constant([[[5,5,5,5],[6,6,6,6]], [[1,1,1,1],[2,2,2,2]]]) # 在 shape 为[4,4,4]白板上根据 indices 写入 updates tf.scatter_nd(indices, updates, [4,2,4])
<tf.Tensor: shape=(4, 2, 4), dtype=int32, numpy= array([[[0, 0, 0, 0], [0, 0, 0, 0]], [[5, 5, 5, 5], [6, 6, 6, 6]], [[0, 0, 0, 0], [0, 0, 0, 0]], [[1, 1, 1, 1], [2, 2, 2, 2]]])>
4.6 meshgrid
经过 tf.meshgrid
能够方便地生成二维网格采样点坐标,方即可视化等应用场合。
经过在 x 轴上进行采样 100 个数据点,y 轴上采样 100 个数据点,而后经过tf.meshgrid(x, y)
便可返回这 10000 个数据点的张量数据,shape 为[100,100,2]。为了方便计算,tf.meshgrid
会返回在 axis=2 维度切割后的 2 个张量 a,b,其中张量 a 包含了全部点的 x坐标,b 包含了全部点的 y 坐标,shape 都为[100,100]:
x = tf.linspace(-8.,8,100) # 设置 x 坐标的间隔 y = tf.linspace(-8.,8,100) # 设置 y 坐标的间隔 x,y = tf.meshgrid(x,y) # 生成网格点,并拆分后返回 x.shape,y.shape # 打印拆分后的全部点的 x,y 坐标张量 shape
(TensorShape([100, 100]), TensorShape([100, 100]))
考虑2 个自变量 x,y 的 Sinc 函数表达式为:z = sin(x2 + y2) / (x2 + y2)
Sinc 函数在 TensorFlow 中实现以下:
z = tf.sqrt(x**2+y**2) z = tf.sin(z)/z # sinc 函数实现
fig = plt.figure() ax = Axes3D(fig) ax.contour3D(x.numpy(), y.numpy(), z.numpy(), 50) plt.show()
<Figure size 648x504 with 0 Axes> <Figure size 648x504 with 0 Axes> findfont: Font family ['STKaiTi'] not found. Falling back to DejaVu Sans.
5.数据集加载
在 TensorFlow 中,keras.datasets
模块提供了经常使用经典数据集的自动下载、管理、加载
与转换功能,而且提供了tf.data.Dataset
数据集对象,方便实现多线程(Multi-thread),预处
理(Preprocess),随机打散(Shuffle)和批训练(Train on batch)等经常使用数据集功能。
经过 datasets.xxx.load_data()
便可实现经典数据集的自动加载,其中 xxx 表明具体的数
据集名称。TensorFlow 会默认将数据缓存在用户目录下的 .keras/datasets 文件夹,
所示,用户不须要关心数据集是如何保存的。若是当前数据集不在缓存中,则会自动从网站下载和解压,加载;若是已经在缓存中,自动完成加载:
import tensorflow as tf from tensorflow import keras from tensorflow.keras import datasets # 导入经典数据集加载模块 (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)
x: (60000, 28, 28) y: (60000,) x test: (10000, 28, 28) y test: [7 2 1 ... 4 5 6]
经过 load_data()
会返回相应格式的数据,对于图片数据集 MNIST, CIFAR10 等,会返回 2 个 tuple,第一个 tuple 保存了用于训练的数据 x,y 训练集对象;第 2 个 tuple 则保存了用于
测试的数据 x_test,y_test 测试集对象,全部的数据都用 Numpy.array 容器承载。
数据加载进入内存后,须要转换成 Dataset 对象,以利用 TensorFlow 提供的各类便捷功能。经过 Dataset.from_tensor_slices
能够将训练部分的数据图片 x 和标签 y 都转换成Dataset 对象:
train_db = tf.data.Dataset.from_tensor_slices((x, y))
5.1 随机打乱
经过 Dataset.shuffle(buffer_size)
工具能够设置 Dataset 对象随机打散数据之间的顺序,防止每次训练时数据按固定顺序产生,从而使得模型尝试“记忆”住标签信息:train_db = train_db.shuffle(10000)
其中 buffer_size
指定缓冲池的大小,通常设置为一个较大的参数便可。经过 Dataset 提供的这些工具函数会返回新的 Dataset 对象,能够经过 db = db. shuffle(). step2(). step3. ()
方式完成全部的数据处理步骤,实现起来很是方便
5.2 批训练
为了利用显卡的并行计算能力,通常在网络的计算过程当中会同时计算多个样本,咱们把这种训练方式叫作批训练,其中样本的数量叫作 batch size
。为了一次可以从 Dataset 中产生 batch size 数量的样本,须要设置 Dataset 为批训练方式:
train_db = train_db.batch(128)
其中 128 为 batch size`参数,即一次并行计算 128 个样本的数据。Batch size 通常根据用户的 GPU 显存资源来设置,当显存不足时,能够适量减小 batch size 来减小算法的显存使用量
5.3预处理
从 keras.datasets
中加载的数据集的格式大部分状况都不能知足模型的输入要求,所以须要根据用户的逻辑本身实现预处理函数。Dataset 对象经过提供 map(func)工具函数能够很是方便地调用用户自定义的预处理逻辑,它实如今 func 函数里:
#预处理函数实如今 preprocess 函数中,传入函数引用便可 train_db = train_db.map(preprocess)
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)
5.4 循环训练
对于 Dataset 对象,在使用时能够经过
for step, (x,y) in enumerate(train_db): # 迭代数据集对象,带 step 参数
或:
for x,y in train_db: # 迭代数据集对象
方式进行迭代,每次返回的 x,y 对象即为批量样本和标签,当对 train_db 的全部样本完成
一次迭代后,for 循环终止退出。咱们通常把完成一个 batch 的数据训练,叫作一个 step;
经过多个 step 来完成整个训练集的一次迭代,叫作一个 epoch。在实际训练时,一般须要
对数据集迭代多个 epoch 才能取得较好地训练效果
此外,也能够经过设置:
train_db = train_db.repeat(20) # 数据集跌打 20 遍才终止
使得 for x,y in train_db
循环迭代 20 个 epoch 才会退出。无论使用上述哪一种方式,都能取得同样的效果。
6.MNIST手写数字识别实战
# 导入要使用的库 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] 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 train_db = tf.data.Dataset.from_tensor_slices((x, y)) train_db = train_db.shuffle(1000).batch(batchsz).map(preprocess).repeat(20) 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) # print(x[0], y[0]) def main(): # learning rate 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])) #开始训练 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) # compute loss # [b, 10] - [b, 10] loss = tf.square(y-out) # [b, 10] => scalar 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) # print if step % 80 == 0: print(step, 'loss:', float(loss)) losses.append(float(loss)) if step %80 == 0: # evaluate/test 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) # convert one_hot y to number y y = tf.argmax(y, axis=1) # bool type correct = tf.equal(pred, y) # bool tensor => int tensor => numpy 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') plt.show() if __name__ == '__main__': main()