TensorFlow 中,tensor 通常分为:常量 tensor 与变量 tensor:
常量 tensor 定义后值和维度不可变,变量定义后值可变而维度不可变。
在神经网络中,变量 tensor 一般可作为储存权重和其他信息的矩阵,是可训练的数据类型。而常量tensor 可作为储存超参数或其他结构信息的变量
常量 tensor 的创建方式比较多,常见的有一下几种方式:
l tf.constant():创建常量 tensor;
l tf.zeros(), tf.zeros_like(), tf.ones(),tf.ones_like(): 创建全零或者全一的常量 tensor;
l tf.fill(): 创建自定义数值的 tensor;
l tf.random: 创建已知分布的 tensor;
l 从 numpy,list 对象创建,再利用 tf.convert_to_tensor 转换为类型。
tf.constant(value, dtype=None, shape=None, name='Const', verify_shape=False):
l value:值;
l dtype:数据类型;
l shape:张量形状;
l name:常量名称;
l verify_shape:布尔值,用于验证值的形状,默认 False。verify_shape 为 True 的话表示检查 value 的形状与 shape 是否相符,如果不符会报错。
代码:
import tensorflow as tf
const_a = tf.constant([[1, 2, 3, 4]], shape=[2, 2], dtype=tf.float32) # 创建 2x2 矩阵,值 1,2,3,4
const_a
输出:
<tf.Tensor: id=2, shape=(2, 2), dtype=float32, numpy=
array([[1., 2.],
[3., 4.]], dtype=float32)>
代码:
# 查看常见属性
print("常量const_a 的数值为:", const_a.numpy())
print("常量const_a 的数据类型为:", const_a.dtype)
print("常量const_a 的形状为:", const_a.shape)
print("常量const_a 将被产生的设备名称为:", const_a.device)
输出:
常量const_a 的数值为: [[1. 2.]
[3. 4.]]
常量const_a 的数据类型为: <dtype: 'float32'>
常量const_a 的形状为: (2, 2)
常量const_a 将被产生的设备名称为: /job:localhost/replica:0/task:0/device:CPU:0
因为 tf.ones(),tf.ones_like()与 tf.zeros(),tf.zeros_like()的用法相似,因此下面只演示前者的使用方法。
创建一个值为 0 的常量。
tf.zeros(shape, dtype=tf.float32, name=None):
l shape:张量形状;
l dtype:类型;
l name:名称。
代码:
zeros_b = tf.zeros(shape=[2, 3], dtype=tf.int32) # 创建 2x3 矩阵,元素值均为 0
根据输入张量创建一个值为 0 的张量,形状和输入张量相同。
tf.zeros_like(input_tensor, dtype=None, name=None, optimize=True):
l input_tensor:张量;
l dtype:类型;
l name:名称;
l optimize:优化。
代码:
zeros_like_c = tf.zeros_like(const_a)
# 查看生成数据
zeros_like_c.numpy()
输出:
array([[0., 0.],
[0., 0.]], dtype=float32)
创建一个张量,用一个具体值充满张量。
tf.fill(dims, value, name=None):
代码:
fill_d = tf.fill([3, 3], 8) # 2x3 矩阵,元素值均为为 8
# 查看数据
fill_d.numpy()
输出:
array([[8, 8, 8],
[8, 8, 8],
[8, 8, 8]], dtype=int32)
用于产生具体分布的张量。该模块中常用的方法包括:tf.random.uniform(),tf.random.normal()和 tf.random.shuffle()等。下面演示 tf.random.normal()的用法。
创建一个符合正态分布的张量。
tf.random.normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32,seed=None, name=None):
代码:
random_e = tf.random.normal([5, 5], mean=0, stddev=1.0, seed=1)
# 查看创建数据
random_e.numpy()
输出:
array([[-0.8113182 , 1.4845988 , 0.06532937, -2.4427042 , 0.0992484 ],
[ 0.5912243 , 0.59282297, -2.1229296 , -0.72289723, -0.05627038],
[ 0.6435448 , -0.26432407, 1.8566332 , 0.5678417 , -0.3828359 ],
[-1.4853433 , 1.2617711 , -0.02530608, -0.2646297 , 1.5328138 ],
[-1.7429771 , -0.43789294, -0.56601 , 0.32066926, 1.132831 ]],
dtype=float32)
将给定制转换为张量。可利用这个函数将 python 的数据类型转换成 TensorFlow 可用的 tensor 数据类型。
tf.convert_to_tensor(value,dtype=None,dtype_hint=None,name=None):
代码:
# 创建一个列表
list_f = [1, 2, 3, 4, 5, 6]
# 查看数据类型
type(list_f)
输出:
list
代码:
tensor_f = tf.convert_to_tensor(list_f, dtype=tf.float32)
tensor_f
输出:
<tf.Tensor: id=16, shape=(6,), dtype=float32, numpy=array([1., 2., 3., 4., 5., 6.], dtype=float32)>
TensorFlow 中,变量通过 tf.Variable 类进行操作。tf.Variable 表示张量,其值可以通过在其上运行算术运算更改。可读取和修改变量值。
代码:
# 创建变量,只需提供初始值
var_1 = tf.Variable(tf.ones([2, 3]))
var_1
输出:
<tf.Variable 'Variable:0' shape=(2, 3) dtype=float32, numpy=
array([[1., 1., 1.],
[1., 1., 1.]], dtype=float32)>
代码:
# 变量数值读取
print("变量 var_1 的数值:", var_1.read_value())
# 变量赋值
var_value_1 = [[1, 2, 3], [4, 5, 6]]
var_1.assign(var_value_1)
print("变量 var_1 赋值后的数值:", var_1.read_value())
输出:
变量 var_1 的数值: tf.Tensor(
[[1. 1. 1.]
[1. 1. 1.]], shape=(2, 3), dtype=float32)
变量 var_1 赋值后的数值: tf.Tensor(
[[1. 2. 3.]
[4. 5. 6.]], shape=(2, 3), dtype=float32)
代码:
# 变量加法
var_1.assign_add(tf.ones([2, 3]))
var_1
输出:
<tf.Variable 'Variable:0' shape=(2, 3) dtype=float32, numpy=
array([[2., 3., 4.],
[5., 6., 7.]], dtype=float32)>
切片的方式主要有:
代码:
# 创建一个 4 维tensor。tensor 包含 4 张图片,每张图片的大小为 100*100*3
tensor_h = tf.random.normal([4, 100, 100, 3])
tensor_h
输出:
<tf.Tensor: id=40, shape=(4, 100, 100, 3), dtype=float32, numpy=
array([[[[-1.2748867 , 0.61186457, -1.3725697 ],
[-0.32428452, 0.2527103 , 0.69562674],
[ 0.55394286, 1.0515776 , 1.6650721 ],
...,
[ 0.5942018 , -0.3625194 , 1.3041826 ],
[ 0.81127596, 3.019384 , 0.22381118],
[ 0.9802666 , 0.52870286, 1.8634393 ]],
[[ 0.05032753, -0.45593497, 1.006573 ],
[-0.97207814, 0.65104896, -0.5570135 ],
[-0.90569866, 0.1540982 , -0.04971581],
...,
# 取出第一张图片
tensor_h[0, :, :, :]
输出:
<tf.Tensor: id=44, shape=(100, 100, 3), dtype=float32, numpy=
array([[[-1.2748867 , 0.61186457, -1.3725697 ],
[-0.32428452, 0.2527103 , 0.69562674],
[ 0.55394286, 1.0515776 , 1.6650721 ],
...,
[ 0.5942018 , -0.3625194 , 1.3041826 ],
[ 0.81127596, 3.019384 , 0.22381118],
[ 0.9802666 , 0.52870286, 1.8634393 ]],
# 每两张图片取出一张的切片
tensor_h[::2, ...]
输出:
<tf.Tensor: id=48, shape=(2, 100, 100, 3), dtype=float32, numpy=
array([[[[-1.2748867 , 0.61186457, -1.3725697 ],
[-0.32428452, 0.2527103 , 0.69562674],
[ 0.55394286, 1.0515776 , 1.6650721 ],
...,
[ 0.5942018 , -0.3625194 , 1.3041826 ],
[ 0.81127596, 3.019384 , 0.22381118],
[ 0.9802666 , 0.52870286, 1.8634393 ]],
#倒序切片
tensor_h[::-1]
输出:
<tf.Tensor: id=52, shape=(4, 100, 100, 3), dtype=float32, numpy=
array([[[[ 0.5161808 , 0.23140958, -0.43209437],
[ 0.00776087, 1.3823358 , 0.6929437 ],
[ 1.5621277 , -0.04508307, -0.1968164 ],
...,
[-1.2630497 , -1.0238761 , -1.1867431 ],
[-0.24941015, -0.11402028, 0.27717665],
[ 0.8502298 , -0.30275303, -0.85351145]],
索引的基本格式:a[d1] [d2] [d3]
代码:
# 取出第一张图片第二个通道中在[20,40]位置的像素点
tensor_h[0][19][39][1]
输出:
<tf.Tensor: id=68, shape=(), dtype=float32, numpy=-1.614223>
在某一维度进行索引。
tf.gather(params, indices,axis=None):
代码:
# 取出tensor_h([4,100,100,3])中,第 1,2,4 张图像。
indices = [0, 1, 3]
tf.gather(tensor_h, axis=0, indices=indices)
输出:
<tf.Tensor: id=83, shape=(3, 100, 100, 3), dtype=float32, numpy=
array([[[[-0.01252888, -0.46393958, 0.60526013],
[-0.01322627, -0.24589129, -1.7498221 ],
[ 0.15702602, -0.84582156, -0.01484271],
...,
[-1.4868114 , 2.6454847 , 0.84849346],
[-0.15565126, 0.5482018 , -0.96524566],
[ 1.1514304 , -0.07815795, 1.7596667 ]],
tf.gather_nd 允许在多维上进行索引:
tf.gather_nd(params,indices):
代码:
# 取出tensot_h([4,100,100,3])中,第一张图像第一个维度中[1,1]的像素点;第二张图片第一像素点中[2,2]的像素点
indices = [[0, 1, 1, 0], [1, 2, 2, 0]]
tf.gather_nd(tensor_h, indices=indices)
输出:
<tf.Tensor: id=85, shape=(2,), dtype=float32, numpy=array([-0.38638964, -1.1200645 ], dtype=float32)>
代码:
const_d_1 = tf.constant([[1, 2, 3, 4]], shape=[2, 2], dtype=tf.float32) # 查看维度常用的三种方式
print(const_d_1.shape)
print(const_d_1.get_shape())
print(tf.shape(const_d_1)) # 输出为张量,其数值表示的是所查看张量维度大小
输出:
(2, 2)
(2, 2)
tf.Tensor([2 2], shape=(2,), dtype=int32)
可以看出.shape 和.get_shape()都是返回 TensorShape 类型对象,而 tf.shape(x)返回的是 Tensor 类型对象。
tf.reshape(tensor,shape,name=None):
代码:
reshape_1 = tf.constant([[1, 2, 3], [4, 5, 6]])
print(reshape_1)
tf.reshape(reshape_1, (3, 2))
输出:
tf.Tensor(
[[1 2 3]
[4 5 6]], shape=(2, 3), dtype=int32)
<tf.Tensor: id=92, shape=(3, 2), dtype=int32, numpy=
array([[1, 2],
[3, 4],
[5, 6]], dtype=int32)>
tf.expand_dims(input,axis,name=None):
代码:
# 生成一个大小为 100*100*3 的张量来表示一张尺寸为 100*100 的三通道彩色图片
expand_sample_1 = tf.random.normal([100, 100, 3], seed=1)
print("原始数据尺寸:", expand_sample_1.shape)
print("在第一个维度前增加一个维度(axis=0):", tf.expand_dims(expand_sample_1, axis=0).shape)
print("在第二个维度前增加一个维度(axis=1):", tf.expand_dims(expand_sample_1, axis=1).shape)
print("在最后一个维度后增加一个维度(axis=-1):", tf.expand_dims(expand_sample_1, axis=-1).shape)
输出:
原始数据尺寸: (100, 100, 3)
在第一个维度前增加一个维度(axis=0): (1, 100, 100, 3)
在第二个维度前增加一个维度(axis=1): (100, 1, 100, 3)
在最后一个维度后增加一个维度(axis=-1): (100, 100, 3, 1)
tf.squeeze(input,axis=None,name=None):
代码:
# 生成一个大小为 100*100*3 的张量来表示一张尺寸为 100*100 的三通道彩色图片
squeeze_sample_1 = tf.random.normal([1, 100, 100, 3])
print("原始数据尺寸:", squeeze_sample_1.shape)
squeezed_sample_1 = tf.squeeze(expand_sample_1)
print("维度压缩后的数据尺寸:", squeezed_sample_1.shape)
输出:
原始数据尺寸: (1, 100, 100, 3)
维度压缩后的数据尺寸: (100, 100, 3)
tf.transpose(a,perm=None,conjugate=False,name='transpose'):
# 低维的转置问题比较简单,输入需转置张量调用 tf.transpose
trans_sample_1 = tf.constant([1, 2, 3, 4, 5, 6], shape=[2, 3])
print("原始数据尺寸:", trans_sample_1.shape)
transposed_sample_1 = tf.transpose(trans_sample_1)
print("转置后数据尺寸:", transposed_sample_1.shape)
输出:
原始数据尺寸: (2, 3)
转置后数据尺寸: (3, 2)
代码:
'''
高维数据转置需要用到 perm 参数,perm 代表输入张量的维度排列。
对于一个三维张量来说,其原始的维度排列为[0, 1, 2](perm)分别代表高维数据的长宽高。通过改变perm
中数值的排列,可以对数据的对应维度进行转置
'''
# 生成一个大小为$*100*200*3 的张量来表示 4 张尺寸为 100*200 的三通道彩色图片
trans_sample_2 = tf.random.normal([4, 100, 200, 3])
print("原始数据尺寸:", trans_sample_2.shape)
# 对 4 张图像的长宽进行对调。原始 perm 为[0,1,2,3],现变为[0,2,1,3]
transposed_sample_2 = tf.transpose(trans_sample_2, [0, 2, 1, 3])
print("转置后数据尺寸:", transposed_sample_2.shape)
输出:
原始数据尺寸: (4, 100, 200, 3)
转置后数据尺寸: (4, 200, 100, 3)
利用把 broadcast_to 可以将小维度推广到大维度。
tf.broadcast_to(input,shape,name=None):
broadcast_sample_1 = tf.constant([1, 2, 3, 4, 5, 6])
print("原始数据:", broadcast_sample_1.numpy())
broadcasted_sample_1 = tf.broadcast_to(broadcast_sample_1, shape=[4, 6])
print("广播后数据:", broadcasted_sample_1.numpy())
输出:
原始数据: [1 2 3 4 5 6]
广播后数据: [[1 2 3 4 5 6]
[1 2 3 4 5 6]
[1 2 3 4 5 6]
[1 2 3 4 5 6]]
代码:
# 运算时,当两个数组的形状不同时,与 numpyy 一样,TensorFlow 将自动触发广播机制。
a = tf.constant([[0, 0, 0],
[10, 10, 10],
[20, 20, 20],
[30, 30, 30]])
b = tf.constant([1, 2, 3])
print(a + b)
输出:
tf.Tensor(
[[ 1 2 3]
[11 12 13]
[21 22 23]
[31 32 33]], shape=(4, 3), dtype=int32)
算术运算主要包括了:加(tf.add)、减(tf.subtract)、乘(tf.multiply)、除(tf.divide)、取对数
(tf.math.log)和指数(tf.pow)等。 因为调用比较简单,下面只演示一个加法例子。
a = tf.constant([[3, 5], [4, 8]])
b = tf.constant([[1, 6], [2, 9]])
print(tf.add(a, b))
输出:
tf.Tensor(
[[ 4 11]
[ 6 17]], shape=(2, 2), dtype=int32)
矩阵乘法运算的实现通过调用 tf.matmul。
代码:
tf.matmul(a, b)
输出:
<tf.Tensor: id=134, shape=(2, 2), dtype=int32, numpy=
array([[13, 63],
[20, 96]], dtype=int32)>
张量的数据统计主要包括:
下面演示 tf.argmax()的用法:返回最大值所在的下标
代码:
argmax_sample_1 = tf.constant([[1, 3, 2], [2, 5, 8], [7, 5, 9]])
print("输入张量:", argmax_sample_1.numpy())
max_sample_1 = tf.argmax(argmax_sample_1, axis=0)
max_sample_2 = tf.argmax(argmax_sample_1, axis=1)
print("按列寻找最大值的位置:", max_sample_1.numpy())
print("按行寻找最大值的位置:", max_sample_2.numpy())
输出:
输入张量: [[1 3 2]
[2 5 8]
[7 5 9]]
按列寻找最大值的位置: [2 1 2]
按行寻找最大值的位置: [1 2 2]
TensorFlow 中,tf.reduce_*一系列操作等都造成张量维度的减少。这一系列操作都可以对一个张量在维度上的元素进行操作,如按行求平均,求取张量中所有元素的乘积等。
常用的包括:tf.reduce_sum(加法)、tf.reduce_prod(乘法)、tf.reduce_min(最小)、 tf.reduce_max(最大)、tf.reduce_mean(均值)、tf.reduce_all(逻辑和)、tf.reduce_any
(逻辑或)和 tf.reduce_logsumexp(log(sum(exp)))操作)等。
这些操作的使用方法都相似,下面只演示 tf.reduce_sum 的操作案例。计算一个张量的各个维度上元素的总和
tf.reduce_sum(input_tensor, axis=None, keepdims=False,name=None):
reduce_sample_1 = tf.constant([1, 2, 3, 4, 5, 6], shape=[2, 3])
print("原始数据", reduce_sample_1.numpy())
print("计算张量中所有元素的和(axis=None):", tf.reduce_sum(reduce_sample_1, axis=None).numpy())
print("按列计算,分别计算各列的和(axis=0):", tf.reduce_sum(reduce_sample_1, axis=0).numpy())
print("按行计算,分别计算各列的和(axis=1):", tf.reduce_sum(reduce_sample_1, axis=1).numpy())
输出:
原始数据 [[1 2 3]
[4 5 6]]
计算张量中所有元素的和(axis=None): 21
按列计算,分别计算各列的和(axis=0): [5 7 9]
按行计算,分别计算各列的和(axis=1): [ 6 15]
TensorFlow 中,张量拼接的操作主要包括:
tf.concat(values, axis, name='concat'):
concat_sample_1 = tf.random.normal([4, 100, 100, 3])
concat_sample_2 = tf.random.normal([40, 100, 100, 3])
print("原始数据的尺寸分别为:", concat_sample_1.shape, concat_sample_2.shape)
concated_sample_1 = tf.concat([concat_sample_1, concat_sample_2], axis=0)
print("拼接后数据的尺寸:", concated_sample_1.shape)
输出:
原始数据的尺寸分别为: (4, 100, 100, 3) (40, 100, 100, 3)
拼接后数据的尺寸: (44, 100, 100, 3)
在原来矩阵基础上增加了一个维度,也是同样的道理,axis 决定维度增加的位置。
tf.stack(values, axis=0, name='stack'):
stack_sample_1 = tf.random.normal([100, 100, 3])
stack_sample_2 = tf.random.normal([100, 100, 3])
print("原始数据的尺寸分别为:", stack_sample_1.shape, stack_sample_2.shape)
# 拼接后维度增加。axis=0,则在第一个维度前增加维度。
stacked_sample_1 = tf.stack([stack_sample_1, stack_sample_2], axis=0)
print("拼接后数据的尺寸:", stacked_sample_1.shape)
输出:
原始数据的尺寸分别为: (100, 100, 3) (100, 100, 3)
拼接后数据的尺寸: (2, 100, 100, 3)
TensorFlow 中,张量分割的操作主要包括:
与 tf.unstack()相比,tf.split()更佳灵活。
tf.unstack(value,num=None,axis=0,name='unstack'):
# 按照第一个维度对数据进行分解,分解后的数据以列表形式输出。
tf.unstack(stacked_sample_1, axis=0)
输出:
[<tf.Tensor: id=176, shape=(100, 100, 3), dtype=float32, numpy=
array([[[-0.6100131 , 1.1552308 , 1.9557871 ],
[ 0.7781739 , -0.97797275, 1.1395042 ],
[ 1.487099 , -2.3994513 , 0.6791377 ],
...,
[ 0.00845545, -0.7138963 , -0.40552953],
[-0.38446733, 0.01316335, -0.6392299 ],
[-0.523189 , -1.0822618 , -0.7537402 ]],
tf.split(value, num_or_size_splits, axis=0):
如果 num_or_size_splits 传入的是一个整数,那直接在 axis=D 这个维度上把张量平均切分成几个小张量。
如果 num_or_size_splits 传入的是一个向量,则在 axis=D 这个维度上把张量按照向量的元素值切分成几个小张量。
import numpy as np
split_sample_1 = tf.random.normal([10, 100, 100, 3])
print("原始数据的尺寸为:", split_sample_1.shape)
splited_sample_1 = tf.split(split_sample_1, num_or_size_splits=5, axis=0)
print("当m_or_size_splits=10,分割后数据的尺寸为:", np.shape(splited_sample_1))
splited_sample_2 = tf.split(split_sample_1, num_or_size_splits=[3, 5, 2], axis=0)
print("当num_or_size_splits=[3,5,2],分割后数据的尺寸分别为:",
np.shape(splited_sample_2[0]), np.shape(splited_sample_2[1]), np.shape(splited_sample_2[2]))
*输出:
原始数据的尺寸为: (10, 100, 100, 3)
当 m_or_size_splits=10,分割后数据的尺寸为: (5, 2, 100, 100, 3)
当 num_or_size_splits=[3,5,2],分割后数据的尺寸分别为: (3, 100, 100, 3) (5, 100, 100, 3) (2, 100, 100, 3)
TensorFlow 中,张量排序的操作主要包括:
代码:
sort_sample_1 = tf.random.shuffle(tf.range(10))
print("输入张量:", sort_sample_1.numpy())
sorted_sample_1 = tf.sort(sort_sample_1, direction="ASCENDING")
print("生序排列后的张量:", sorted_sample_1.numpy())
sorted_sample_2 = tf.argsort(sort_sample_1, direction="ASCENDING")
print("生序排列后,元素的索引:", sorted_sample_2.numpy())
输出:
输入张量: [4 3 5 0 6 2 8 1 7 9]
生序排列后的张量: [0 1 2 3 4 5 6 7 8 9]
生序排列后,元素的索引: [3 7 5 1 0 2 4 8 6 9]
tf.nn.top_k(input,K,sorted=TRUE):
l input:输入张量;
l K:需要输出的前 k 个值及其索引。
l sorted: sorted=TRUE 表示升序排列;sorted=FALSE 表示降序排列。
返回两个张量:
values, index = tf.nn.top_k(sort_sample_1, 5)
print("输入张量:", sort_sample_1.numpy())
print("升序排列后的前 5 个数值:", values.numpy())
print("升序排列后的前 5 个数值的索引:", index.numpy())
输出:
输入张量: [4 3 5 0 6 2 8 1 7 9]
升序排列后的前 5 个数值: [9 8 7 6 5]
升序排列后的前 5 个数值的索引: [9 6 8 4 2]
TensorFlow 的 Eager Execution 模式是一种命令式编程(imperative programming),这和原生 Python 是一致的,当你执行某个操作时,可以立即返回结果的。
TensorFlow1.0 一直是采用 Graph 模式,即先构建一个计算图,然后需要开启 Session,喂进实际的数据才真正执行得到结果。
Eager Execution 模式下,我们可以更容易 debug 代码,但是代码的执行效率更低。
下面我们在 Eager Execution 和 Graph 模式下,用 TensorFlow 实现简单的乘法,来对比两个模式的区别。
x = tf.ones((2, 2), dtype=tf.dtypes.float32)
y = tf.constant([[1, 2],
[3, 4]], dtype=tf.dtypes.float32)
z = tf.matmul(x, y)
print(z)
输出:
tf.Tensor(
[[4. 6.]
[4. 6.]], shape=(2, 2), dtype=float32)
# 在 TensorFlow 2 版本中使用 1.X 版本的语法;可以使用 2.0 中的 v1 兼容包来沿用 1.x 代码,并在代码中关闭 eager运算。
import tensorflow.compat.v1 as tf
tf.disable_eager_execution()
# 创建graph,定义计算图
a = tf.ones((2, 2), dtype=tf.dtypes.float32)
b = tf.constant([[1, 2],
[3, 4]], dtype=tf.dtypes.float32)
c = tf.matmul(a, b)
# 开启绘画,进行运算后,才能取出数据。
with tf.Session() as sess:
print(sess.run(c))
输出:
[[4. 6.]
[4. 6.]]
首先重启一下 kernel,使得 TensorFlow 恢复到 2.0 版本并打开 eager execution 模式。 Eager Execution 模式的另一个优点是可以使用 Python 原生功能,比如下面的条件判断:
import tensorflow as tf
import numpy as np
thre_1 = tf.random.uniform([], 0, 1)
x = tf.reshape(tf.range(0, 4), [2, 2])
print(thre_1)
if thre_1.numpy() > 0.5:
y = tf.matmul(x, x)
else:
y = tf.add(x, x)
输出:
tf.Tensor(0.8043928, shape=(), dtype=float32)
这种动态控制流主要得益于 eager 执行得到 Tensor 可以取出 numpy 值,这避免了使用 Graph 模式下的 tf.cond 和 tf.while 等算子。
当使用 tf.function 装饰器注释函数时,可以像调用任何其他函数一样调用它。它将被编译成图,这意味着可以获得更高效地在在 GPU 或 TPU 上运行。此时函数变成了一个 TensorFlow 中的 operation。我们可以直接调用函数,输出返回值,但是函数内部是在 graph 模式下执行的,无法直接查看中间变量数值
@tf.function
def simple_nn_layer(w, x, b):
print(b)
return tf.nn.relu(tf.matmul(w, x) + b)
w = tf.random.uniform((3, 3))
x = tf.random.uniform((3, 3))
b = tf.constant(0.5, dtype='float32')
simple_nn_layer(w, x, b)
输出:
Tensor("b:0", shape=(), dtype=float32)
<tf.Tensor: id=40, shape=(3, 3), dtype=float32, numpy=
array([[0.5868423, 0.6898268, 0.8843413],
[0.8371662, 0.847257 , 1.1481965],
[1.017222 , 1.1640502, 1.46533 ]], dtype=float32)>
通过输出结果可知,无法直接查看函数内部 b 的数值,而返回值可以通过.numpy()查看。通过相同的操作(执行一层 lstm 计算),比较 graph 和 eager execution 模式的性能。
# timeit 测量小段代码的执行时间
import timeit
# 创建一个卷积层。
CNN_cell = tf.keras.layers.Conv2D(filters=100, kernel_size=2, strides=(1, 1))
# 利用@tf.function,将操作转化为 graph。 @tf.function
def CNN_fn(image):
return CNN_cell(image)
image = tf.zeros([100, 200, 200, 3])
# 比较两者的执行时间CNN_cell(image) CNN_fn(image)
# 调用timeit.timeit,测量代码执行 10 次的时间
print("eager execution 模式下做一层 CNN 卷积层运算的时间:", timeit.timeit(lambda: CNN_cell(image), number=10))
print("graph 模式下做一层 CNN 卷积层运算的时间:", timeit.timeit(lambda: CNN_fn(image), number=10))
输出:
eager execution 模式下做一层 CNN 卷积层运算的时间: 53.275061096
graph 模式下做一层 CNN 卷积层运算的时间: 47.896033531
通过比较,我们可以发现 graph 模式下代码执行效率要高出许多。因此我们以后,可以多尝试用@tf.function 功能,提高代码运行效率。
本节将为大家介绍 TensorFlow 2 常用模块,主要包括:
l tf.data:实现对数据集的操作;
包括读取从内存中直接读取数据集、读取 CSV 文件、读取 tfrecord 文件和数据增强等。
l tf.image:实现对图像处理的操作;
包括图像亮度变换、饱和度变换、图像尺寸变换、图像旋转和边缘检测等操作。
l tf.gfile:实现对文件的操作;
包括对文件的读写操作、文件重命名和文件夹操作等。
l tf.keras:用于构建和训练深度学习模型的高阶 API;
l tf.distributions 等等。
最常见的模型构建方法是层的堆叠,我们通常会使用 tf.keras.Sequential。
代码:
import tensorflow.keras.layers as layers
import tensorflow as tf
model = tf.keras.Sequential()
model.add(layers.Dense(32, activation='relu'))
model.add(layers.Dense(32, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))
函数式模型主要利用 tf.keras.Input 和 tf.keras.Model 构建,比 tf.keras.Sequential 模型要复杂,但是效果很好,可以同时/分阶段输入变量,分阶段输出数据; 你的模型需要多于一个的输出,那么需要选择函数式模型。
模型堆叠(.Sequential)vs 函数式模型(Model):
tf.keras.Sequential 模型是层的简单堆叠,无法表示任意模型。使用 Keras 的函数式模型可以构建复杂的模型拓扑,例如:
# 以上一层的输出作为下一层的输入
x = tf.keras.Input(shape=(32,))
h1 = layers.Dense(32, activation='relu')(x)
h2 = layers.Dense(32, activation='relu')(h1)
y = layers.Dense(10, activation='softmax')(h2)
model_sample_2 = tf.keras.models.Model(x, y)
# 打印模型信息
model_sample_2.summary()
输出:
Model: "model"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_1 (InputLayer) [(None, 32)] 0
_________________________________________________________________
dense_3 (Dense) (None, 32) 1056
_________________________________________________________________
dense_4 (Dense) (None, 32) 1056
_________________________________________________________________
dense_5 (Dense) (None, 10) 330
=================================================================
Total params: 2,442
Trainable params: 2,442
Non-trainable params: 0
_________________________________________________________________
tf.keras.layers 模块的主要作用为配置神经网络层。其中常用的类包括:
下面主要讲解 tf.keras.layers.Dense、 tf.keras.layers.Conv2D、 tf.keras.layers.MaxPooling2D/AveragePooling2D 和 tf.keras.layers.LSTM/tf.keras.layers.LSTMCell。
tf.keras.layers 中主要的网络配置参数如下:
tf.keras.layers.Dense 可配置的参数,主要有:
# 创建包含 32 个神经元的全连接层,其中的激活函数设置为 sigmoid。
# activation 参数可以是函数名称字符串,如'sigmoid';也可以是函数对象,如 tf.sigmoid。
layers.Dense(32, activation='sigmoid')
layers.Dense(32, activation=tf.sigmoid)
# 设置kernel_initializer 参数
layers.Dense(32, kernel_initializer=tf.keras.initializers.he_normal)
# 设置kernel_regularizer 为L2 正则
layers.Dense(32, kernel_regularizer=tf.keras.regularizers.l2(0.01))
输出:
<tensorflow.python.keras.layers.core.Dense at 0x7fd1299c51d0>
tf.keras.layers.Conv2D 可配置的参数,主要有:
l filters:卷积核的数目(即输出的维度);
l kernel_size:卷积核的宽度和长度;
l strides:卷积的步长。
l padding:补 0 策略。
padding=“valid”代表只进行有效的卷积,即对边界数据不处理。padding=“same”代表保留边界处的卷积结果,通常会导致输出 shape 与输入 shape 相同;
l activation:激活函数;
l data_format:数据格式,为“channels_first”或“channels_last”之一。以 128x128 的 RGB 图像为例,“channels_first”应将数据组织为(3,128,128),而“channels_last”应将数据组织为(128,128,3)。该参数的默认值是~/.keras/keras.json 中设置的值,若从未设置过,则为 “channels_last”。
l 其他参数还包括:use_bias;kernel_initializer;bias_initializer;kernel_regularizer; bias_regularizer;activity_regularizer;kernel_constraints;bias_constraints。
layers.Conv2D(64, [1, 1], 2, padding='same', activation="relu")
输出:
<tensorflow.python.keras.layers.convolutional.Conv2D at 0x7fd12991b410>
tf.keras.layers.MaxPooling2D/AveragePooling2D 可配置的参数,主要有:
layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 1))
输出:
<tensorflow.python.keras.layers.pooling.MaxPooling2D at 0x7fd1299d7ed0>
tf.keras.layers.LSTM/tf.keras.layers.LSTMCell 可配置的参数,主要有:
import numpy as np
inputs = tf.keras.Input(shape=(3, 1))
lstm = layers.LSTM(1, return_sequences=True)(inputs)
model_lstm_1 = tf.keras.models.Model(inputs=inputs, outputs=lstm)
inputs = tf.keras.Input(shape=(3, 1))
lstm = layers.LSTM(1, return_sequences=False)(inputs)
model_lstm_2 = tf.keras.models.Model(inputs=inputs, outputs=lstm)
# t1, t2, t3 序列
data = [[[0.1],
[0.2],
[0.3]]]
print(data)
print("当 return_sequences=True 时的输出", model_lstm_1.predict(data))
print("当 return_sequences=False 时的输出", model_lstm_2.predict(data))
**输出:
[[[0.1], [0.2], [0.3]]]
当 return_sequences=True 时的输出 [[[-0.0106758 ] [-0.02711176]
[-0.04583194]]]
当 return_sequences=False 时的输出 [[0.05914127]]
LSTMcell 是 LSTM 层的实现单元。
# LSTM
tf.keras.layers.LSTM(16, return_sequences=True)
# LSTMCell
x = tf.keras.Input((None, 3))
y = layers.RNN(layers.LSTMCell(16))(x)
model_lstm_3 = tf.keras.Model(x, y)
构建好模型后,通过调用 compile 配置该模型的学习流程:
model = tf.keras.Sequential()
model.add(layers.Dense(10, activation='softmax'))
# 确定优化器(optimizer)、损失函数(loss)、模型评估方法(metrics)
model.compile(optimizer=tf.keras.optimizers.Adam(0.001),
loss=tf.keras.losses.categorical_crossentropy, metrics=[tf.keras.metrics.categorical_accuracy])
fit(x=None, y=None, batch_size=None, epochs=1, verbose=1, callbacks=None, validation_split=0.0, validation_data=None, shuffle=True, class_weight=None, sample_weight=None, initial_epoch=0, steps_per_epoch=None, validation_steps=None):
import numpy as np
train_x = np.random.random((1000, 36))
train_y = np.random.random((1000, 10))
val_x = np.random.random((200, 36))
val_y = np.random.random((200, 10))
model.fit(train_x, train_y, epochs=10, batch_size=100, validation_data=(val_x, val_y))
输出:
Train on 1000 samples, validate on 200 samples
Epoch 1/10
1000/1000 [==============================] - 1s 969us/sample - loss: 12.6233 - categorical_accuracy: 0.1030 - val_loss: 12.8527 - val_categorical_accuracy: 0.1050
Epoch 2/10
1000/1000 [==============================] - 0s 108us/sample - loss: 12.6198 - categorical_accuracy: 0.1030 - val_loss: 12.8505 - val_categorical_accuracy: 0.1050
Epoch 3/10
1000/1000 [==============================] - 0s 91us/sample - loss: 12.6178 - categorical_accuracy: 0.1030 - val_loss: 12.8481 - val_categorical_accuracy: 0.1050
Epoch 4/10
1000/1000 [==============================] - 0s 85us/sample - loss: 12.6146 - categorical_accuracy: 0.1030 - val_loss: 12.8449 - val_categorical_accuracy: 0.1050
Epoch 5/10
1000/1000 [==============================] - 0s 87us/sample - loss: 12.6109 - categorical_accuracy: 0.1030 - val_loss: 12.8407 - val_categorical_accuracy: 0.1050
Epoch 6/10
1000/1000 [==============================] - 0s 376us/sample - loss: 12.6105 - categorical_accuracy: 0.1030 - val_loss: 12.8426 - val_categorical_accuracy: 0.1050
Epoch 7/10
1000/1000 [==============================] - 0s 93us/sample - loss: 12.6102 - categorical_accuracy: 0.1030 - val_loss: 12.8418 - val_categorical_accuracy: 0.1050
Epoch 8/10
1000/1000 [==============================] - 0s 81us/sample - loss: 12.6101 - categorical_accuracy: 0.1030 - val_loss: 12.8417 - val_categorical_accuracy: 0.1050
Epoch 9/10
1000/1000 [==============================] - 0s 84us/sample - loss: 12.6093 - categorical_accuracy: 0.1030 - val_loss: 12.8402 - val_categorical_accuracy: 0.1050
Epoch 10/10
1000/1000 [==============================] - 0s 87us/sample - loss: 12.6074 - categorical_accuracy: 0.1030 - val_loss: 12.8387 - val_categorical_accuracy: 0.1050
<tensorflow.python.keras.callbacks.History at 0x7ff6013f2750>
对于大型数据集可以使用 tf.data 构建训练输入。
dataset = tf.data.Dataset.from_tensor_slices((train_x, train_y))
dataset = dataset.batch(32)
dataset = dataset.repeat()
val_dataset = tf.data.Dataset.from_tensor_slices((val_x, val_y))
val_dataset = val_dataset.batch(32)
val_dataset = val_dataset.repeat()
model.fit(dataset, epochs=10, steps_per_epoch=30, validation_data=val_dataset, validation_steps=3)
输出:
Train for 30 steps, validate for 3 steps
Epoch 1/10
30/30 [==============================] - 1s 20ms/step - loss: 12.5710 - categorical_accuracy: 0.1031 - val_loss: 12.6103 - val_categorical_accuracy: 0.0938
Epoch 2/10
30/30 [==============================] - 0s 4ms/step - loss: 12.6300 - categorical_accuracy: 0.1026 - val_loss: 12.6063 - val_categorical_accuracy: 0.0938
Epoch 3/10
30/30 [==============================] - 0s 3ms/step - loss: 12.5923 - categorical_accuracy: 0.0972 - val_loss: 12.5991 - val_categorical_accuracy: 0.0938
Epoch 4/10
30/30 [==============================] - 0s 3ms/step - loss: 12.5959 - categorical_accuracy: 0.1015 - val_loss: 12.5924 - val_categorical_accuracy: 0.0938
Epoch 5/10
30/30 [==============================] - 0s 3ms/step - loss: 12.5987 - categorical_accuracy: 0.1015 - val_loss: 12.5859 - val_categorical_accuracy: 0.0938
Epoch 6/10
30/30 [==============================] - 0s 4ms/step - loss: 12.5951 - categorical_accuracy: 0.1015 - val_loss: 12.5804 - val_categorical_accuracy: 0.0938
Epoch 7/10
30/30 [==============================] - 0s 3ms/step - loss: 12.5632 - categorical_accuracy: 0.1015 - val_loss: 12.5750 - val_categorical_accuracy: 0.0938
Epoch 8/10
30/30 [==============================] - 0s 4ms/step - loss: 12.5913 - categorical_accuracy: 0.1036 - val_loss: 12.5709 - val_categorical_accuracy: 0.0938
Epoch 9/10
30/30 [==============================] - 0s 4ms/step - loss: 12.5877 - categorical_accuracy: 0.1047 - val_loss: 12.5667 - val_categorical_accuracy: 0.0938
Epoch 10/10
30/30 [==============================] - 0s 3ms/step - loss: 12.6003 - categorical_accuracy: 0.1036 - val_loss: 12.5641 - val_categorical_accuracy: 0.0938
<tensorflow.python.keras.callbacks.History at 0x7ff60196a490>
回调函数是传递给模型以自定义和扩展其在训练期间的行为的对象。我们可以编写自己的自定义回调,或使用tf.keras.callbacks 中的内置函数,常用内置回调函数如下:
# 超参数设置
Epochs = 10
# 定义一个学习率动态设置函数
def lr_Scheduler(epoch):
if epoch > 0.9 * Epochs:
lr = 0.0001
elif epoch > 0.5 * Epochs:
lr = 0.001
elif epoch > 0.25 * Epochs:
lr = 0.01
else:
lr = 0.1
print(lr)
return lr
callbacks = [
# 早 停 :
tf.keras.callbacks.EarlyStopping(
# 不再提升的关注指标
monitor='val_loss',
# 不再提升的阈值
min_delta=1e-2,
# 不再提升的轮次
patience=2),
# 定 期 保 存 模 型 :
tf.keras.callbacks.ModelCheckpoint(
# 模型路径
filepath='testmodel_{epoch}.h5',
# 是否保存最佳模型
save_best_only=True,
# 监控指标
monitor='val_loss'),
# 动态更改学习率
tf.keras.callbacks.LearningRateScheduler(lr_Scheduler),
# 使用 TensorBoard
tf.keras.callbacks.TensorBoard(log_dir='./logs')]
model.fit(train_x, train_y, batch_size=16, epochs=Epochs,
callbacks=callbacks, validation_data = (val_x, val_y))
输出:
Train on 1000 samples, validate on 200 samples
0.1
Epoch 1/10
1000/1000 [==============================] - 0s 334us/sample - loss: 12.6352 - categorical_accuracy: 0.1080 - val_loss: 12.1066 - val_categorical_accuracy: 0.1150
0.1
Epoch 2/10
1000/1000 [==============================] - 0s 234us/sample - loss: 12.6656 - categorical_accuracy: 0.0820 - val_loss: 12.2066 - val_categorical_accuracy: 0.0950
0.1
Epoch 3/10
1000/1000 [==============================] - 0s 215us/sample - loss: 12.5260 - categorical_accuracy: 0.0920 - val_loss: 12.1053 - val_categorical_accuracy: 0.1100
<tensorflow.python.keras.callbacks.History at 0x7f7fe15e2690>
评估和预测函数:tf.keras.Model.evaluate 和 tf.keras.Model.predict 方法。
# 模型评估
test_x = np.random.random((1000, 36))
test_y = np.random.random((1000, 10))
model.evaluate(test_x, test_y, batch_size=32)
输出:
1000/1000 [==============================] - 0s 45us/sample - loss: 12.2881 -
categorical_accuracy: 0.0770
[12.288104843139648, 0.077]
# 模型预测
pre_x = np.random.random((10, 36))
result = model.predict(test_x, )
print(result)
输出:
[[0.0460458 0.08133552 0.27336136 ... 0.05372529 0.15784709 0.06589693]
[0.07072002 0.07391042 0.2307739 ... 0.06366149 0.08080809 0.12174617]
[0.07114598 0.07659262 0.31846598 ... 0.03336082 0.05275346 0.05002784]
...
[0.03928034 0.07010722 0.46011814 ... 0.03338903 0.04595451 0.07160679]
[0.04240561 0.04399079 0.4240673 ... 0.04155495 0.0740051 0.06719721]
[0.05728028 0.03738481 0.3684643 ... 0.05326979 0.09295851 0.09666349]]
代码:
import numpy as np # 模型保存
model.save('model/the_save_model.h5')
# 导入模型
new_model = tf.keras.models.load_model('model/the_save_model.h5')
new_prediction = new_model.predict(test_x)
# np.testing.assert_allclose: 判断两个对象的近似程度是否超出了指定的容差限。若是,则抛出异常。:
# atol:指定的容差限
np.testing.assert_allclose(result, new_prediction, atol=1e-6) # 预测结果一样
模型保存后可以在对应的文件夹中找到对应的权重文件。
若权重名后有.h5 或.keras 后缀,则保存为 HDF5 格式文件,否则默认为 TensorFlow Checkpoint格式文件。
代码:
model.save_weights('model/model_weights')
model.save_weights('model/model_weights.h5')
# 权重加载
model.load_weights('model/model_weights')
model.load_weights('model/model_weights.h5')
手写数字识别是常见的图像识别任务,计算机通过手写体图片来识别图片中的字,与印刷字体不同的是,不同人的手写体风格迥异,大小不一,造成了计算机对手写识别任务的困难,此项目通过应用深度学习和 tensorflow 工具对 MNIST 手写数据集进行训练并建模。
1). MNIST 数据集来自美国国家标准与技术研究所(National Institute of Standards and Technology ,简称 NIST);
2). 该数据集由来自 250 个不同人手写的数字构成,其中 50%是高中学生,50%来自人口普查局的工组人员;
3). 数据集可在 http://yann.lecun.com/exdb/mnist/ 获取, 它包含了四个部分:
- Training set images: train-images-idx3-ubyte.gz (9.9 MB, 解压后 47 MB, 包含 60,000 个样本)
- Training set labels: train-labels-idx1-ubyte.gz (29 KB, 解压后 60 KB, 包含 60,000 个标签)
- Test set images: t10k-images-idx3-ubyte.gz (1.6 MB, 解压后 7.8 MB, 包含 10,000个样本)
- Test set labels: t10k-labels-idx1-ubyte.gz (5KB, 解压后 10 KB, 包含 10,000 个标签) 4). mnist 是一个入门级的计算机视觉数据集,它包含各种手写数字图片:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oj84g3QQ-1594520426345)(images/image-20200707142939032.png)]
它也包含每一张图片对应的标签,告诉我们这个是数字几,比如说,上面这四张图片的标签分别是5,0,4,1。
从 tensorflow 直接读取数据集,联网下载解压;
代码:
import os
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, optimizers, datasets
from matplotlib import pyplot as plt
import numpy as np
# 省略掉一些无关信息的打印
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
# 防止libiomp5.dylib 报错
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
(x_train_raw, y_train_raw), (x_test_raw, y_test_raw) = datasets.mnist.load_data()
print(y_train_raw[0])
print(x_train_raw.shape, y_train_raw.shape)
print(x_test_raw.shape, y_test_raw.shape)
# 将分类标签变为 onehot 编码
num_classes = 10
y_train = keras.utils.to_categorical(y_train_raw, num_classes)
y_test = keras.utils.to_categorical(y_test_raw, num_classes)
print(y_train[0])
输出:
5
(60000, 28, 28) (60000,)
(10000, 28, 28) (10000,)
[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
在 mnist 数据集中,images 是一个形状为[60000,28,28]的张量,第一个维度数字用来索引图片,第二、三个维度数字用来索引每张图片中的像素点。在此张量里的每一个元素,都表示某张图片里的某个像素的强度值,介于 0,255 之间。
标签数据是"one-hot vectors",一个 one-hot 向量除了某一位数字是 1 之外,其余各维度数字都是 0,如标签 1 可以表示为([0,1,0,0,0,0,0,0,0,0,0]),因此, labels 是一个 [60000, 10] 的数字矩阵。
绘制前 9 张图片
plt.figure()
for i in range(9):
plt.subplot(3, 3, i + 1)
plt.imshow(x_train_raw[i])
# plt.ylabel(y[i].numpy())
plt.axis('off')
plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yrz6Fq5r-1594520426347)(images/number.png)]
数据处理,因为我们构建的是全连接网络所以输出应该是向量的形式,而非现在图像的矩阵形式。因此 我们需要把图像整理成向量。
# 将 28*28 的图像展开成 784*1 的向量
x_train = x_train_raw.reshape(60000, 784)
x_test = x_test_raw.reshape(10000, 784)
现在像素点的动态范围为 0 到 255。处理图形像素值时,我们通常会把图像像素点归一化到 0 到 1 的范围内。
# 将图像像素值归一化
x_train = x_train.astype('float32') / 255
x_test = x_test.astype('float32') / 255
# 创建模型。模型包括 3 个全连接层和两个 RELU 激活函数
model = keras.Sequential([
layers.Dense(512, activation='relu', input_dim=784),
layers.Dense(256, activation='relu'),
layers.Dense(124, activation='relu'),
layers.Dense(num_classes, activation='softmax')])
model.summary()
输出:
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense (Dense) (None, 512) 401920
_________________________________________________________________
dense_1 (Dense) (None, 256) 131328
_________________________________________________________________
dense_2 (Dense) (None, 124) 31868
_________________________________________________________________
dense_3 (Dense) (None, 10) 1250
=================================================================
Total params: 566,366
Trainable params: 566,366
Non-trainable params: 0
其中 layer.Dense()表示全连接层,activation 参数表示使用的激活函数。
Optimizer = optimizers.Adam(0.001)
model.compile(loss=keras.losses.categorical_crossentropy,
optimizer=Optimizer, metrics=['accuracy'])
以上定义了模型的损失函数为“交叉熵”,优化算法为“Adam”梯度下降方法。
# 使用 fit 方法使模型对训练数据拟合
model.fit(x_train, y_train,
batch_size=128, epochs=10, verbose=1)
输出:
Train on 60000 samples
Epoch 1/10
60000/60000 [==============================] - 8s 132us/sample - loss: 0.2395 - accuracy: 0.9289
Epoch 2/10
60000/60000 [==============================] - 9s 155us/sample - loss: 0.0855 - accuracy: 0.9736
Epoch 3/10
60000/60000 [==============================] - 9s 149us/sample - loss: 0.0564 - accuracy: 0.9820
Epoch 4/10
60000/60000 [==============================] - 8s 129us/sample - loss: 0.0402 - accuracy: 0.9872
Epoch 5/10
60000/60000 [==============================] - 9s 142us/sample - loss: 0.0324 - accuracy: 0.9888
Epoch 6/10
60000/60000 [==============================] - 8s 138us/sample - loss: 0.0263 - accuracy: 0.9911
Epoch 7/10
60000/60000 [==============================] - 7s 120us/sample - loss: 0.0175 - accuracy: 0.9944
Epoch 8/10
60000/60000 [==============================] - 7s 121us/sample - loss: 0.0183 - accuracy: 0.9942
Epoch 9/10
60000/60000 [==============================] - 9s 153us/sample - loss: 0.0174 - accuracy: 0.9942
Epoch 10/10
60000/60000 [==============================] - 7s 120us/sample - loss: 0.0161 - accuracy: 0.9944
其中 epoch 表示批次,表示将全量的数据迭代 10 次。
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
输出:
Test loss: 0.08009832046656702
Test accuracy: 0.9811
经过评估,模型准确率为 0.87,迭代了 10 次训练。
model.save('model/final_DNN_model.h5')
之前用传统方法构建 CNN 网络,可以更清楚的了解内部的网络结构,但是代码量比较多,所以我们尝试用高级 API 构建网络,以简化构建网络的过程。
代码:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, optimizers, datasets
import numpy as np
import os
# 省略掉一些无关信息的打印
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
# 防止libiomp5.dylib 报错
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
(x_train_raw, y_train_raw), (x_test_raw, y_test_raw) = datasets.mnist.load_data()
# 将分类标签变为 onehot 编码
num_classes = 10
y_train = keras.utils.to_categorical(y_train_raw, num_classes)
y_test = keras.utils.to_categorical(y_test_raw, num_classes)
print(y_train[0])
# 将 28*28 的图像展开成 784*1 的向量
x_train = x_train_raw.reshape(60000, 784)
x_test = x_test_raw.reshape(10000, 784)
# 将图像像素值归一化
x_train = x_train.astype('float32') / 255
x_test = x_test.astype('float32') / 255
model = keras.Sequential() # 创建网络序列 ## 添加第一层卷积层和池化层
model.add(keras.layers.Conv2D(filters=32, kernel_size=5, strides=(1, 1),
padding='same', activation=tf.nn.relu, input_shape=(28, 28, 1)))
model.add(keras.layers.MaxPool2D(pool_size=(2, 2), strides=(2, 2), padding='valid'))
## 添加第二层卷积层和池化层
model.add(keras.layers.Conv2D(filters=64, kernel_size=3, strides=(1, 1), padding='same', activation=tf.nn.relu))
model.add(keras.layers.MaxPool2D(pool_size=(2, 2), strides=(2, 2), padding='valid'))
# 添加dropout 层 以减少过拟合
model.add(keras.layers.Dropout(0.25))
model.add(keras.layers.Flatten())
# 添加两层全连接层
model.add(keras.layers.Dense(units=128, activation=tf.nn.relu))
model.add(keras.layers.Dropout(0.5))
model.add(keras.layers.Dense(units=10, activation=tf.nn.softmax))
以上网络中,我们利用 keras.layers 添加了两个卷积池化层,之后又添加了 dropout 层,防止过拟合,最后添加了两层全连接层。
# 将数据扩充维度,以适应 CNN 模型
X_train = x_train.reshape(60000, 28, 28, 1)
X_test = x_test.reshape(10000, 28, 28, 1)
model.compile(optimizer=tf.optimizers.Adam(), loss="categorical_crossentropy", metrics=['accuracy'])
model.fit(x=X_train, y=y_train, epochs=5, batch_size=128)
输出:
[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
Epoch 1/5
469/469 [==============================] - 49s 105ms/step - loss: 0.2675 - accuracy: 0.9175
Epoch 2/5
469/469 [==============================] - 57s 121ms/step - loss: 0.0878 - accuracy: 0.9736
Epoch 3/5
469/469 [==============================] - 55s 117ms/step - loss: 0.0686 - accuracy: 0.9796
Epoch 4/5
469/469 [==============================] - 47s 101ms/step - loss: 0.0552 - accuracy: 0.9839
Epoch 5/5
469/469 [==============================] - 45s 96ms/step - loss: 0.0474 - accuracy: 0.9854
在训练时,网络训练数据只迭代了 5 次,可以再增加网络迭代次数,自行尝试看效果如何。
test_loss, test_acc = model.evaluate(x=X_test, y= datasets.mnist.test.labels)
print("Test Accuracy %.2f" % test_acc)
输出:
313/313 [==============================] - 2s 6ms/step - loss: 0.0235 - accuracy: 0.9925
Test Accuracy 0.99
最终结果也达到了 99%的准确率。
model.save('model/final_DNN_model.h5')
from tensorflow.keras.models import load_model
new_model = load_model('model/final_CNN_model.h5')
new_model.summary()
输出:
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 28, 28, 32) 832
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 14, 14, 32) 0
_________________________________________________________________
conv2d_1 (Conv2D) (None, 14, 14, 64) 18496
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 7, 7, 64) 0
_________________________________________________________________
dropout (Dropout) (None, 7, 7, 64) 0
_________________________________________________________________
flatten (Flatten) (None, 3136) 0
_________________________________________________________________
dense (Dense) (None, 128) 401536
_________________________________________________________________
dropout_1 (Dropout) (None, 128) 0
_________________________________________________________________
dense_1 (Dense) (None, 10) 1290
=================================================================
Total params: 422,154
Trainable params: 422,154
Non-trainable params: 0
_________________________________________________________________
用将预测结果可视化
# 测试集输出结果可视化
import matplotlib.pyplot as plt
% matplotlib inline
def res_Visual(n):
final_opt_a = new_model.predict_classes(X_test[0:n]) # 通过模型预测测试集
fig, ax = plt.subplots(nrows=int(n / 5), ncols=5)
ax = ax.flatten()
print('前{}张图片预测结果为:'.format(n))
for i in range(n):
print(final_opt_a[i], end=',')
if int((i + 1) % 5) == 0:
print('\t')
# 图片可视化展示
img = X_test[i].reshape((28, 28)) # 读取每行数据,格式为 Ndarry
plt.axis("off")
ax[i].imshow(img, cmap='Greys', interpolation='nearest') # 可视化
ax[i].axis("off")
print('测试集前{}张图片为:'.format(n))
res_Visual(20)
输出:
前20张图片预测结果为:
7,2,1,0,4,
1,4,9,5,9,
0,6,9,0,1,
5,9,7,8,4,
测试集前20张图片为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qtaSs7IX-1594520426349)(images/image-20200707183818758.png)]