网络层:
(1)常用网络层:keras提供了非常多的常用网络层类,用tf.keras.layers.xxx()实现,但深度学习可以使用的网络层远远不止这些。
(2)自定义网络层:需要创建自定义网络层类,这个类继承自tf.keras.layers.Layer基类;
网络(模型):
(1)通常网络构建:利用tf.keras.Sequential()网络容器,将网路层按顺序加入容器,包装成网络/模型,这种方法适用于数据按序从第一层传播到第二层,再从第二层传播到第三层。。。
(2)自定义网络构建:需要创建自定义网络类,这个类继承自tf.keras.Model基类。这样建立的自定义类能够方便的利用Layer/Model基类提供的参数管理等功能,并能够处理构建复杂逻辑的网络模型。
(1)创建自定义网络层类,这个类继承tf.keras.layers.Layer
(2)自定义网络层类:需要实现初始化__init__方法、前向传播逻辑call方法。
import tensorflow as tf
#创建网络层类,继承自Layer基类:需要实现两个操作(1)初始化————init————(2)前向传播call
class MyDense(tf.keras.layers.Layer):
'''1)初始化:'''
def __init__(self,inp_dim,oup_dim):
super(MyDense,self).__init__()
'''创建权值张量并添加到类管理列表中,设置需要优化'''
'''1.self.add_variable创建类成员,而变量名name由tensorflow内部维护'''
self.kernel=self.add_variable('w',[inp_dim,oup_dim],trainable=True)#加入参数列表
'''2.通过tf.Variable创建的类成员也会自动加入类参数列表'''
#self.kernel=tf.Variable(tf.random.normal([inp_dim,oup_dim]),trainable=True)
'''2)前向运算:'''
def call(self, input, training=None):
'''
:param input:
:param training: 为True时执行训练模式,为False时执行测试模式
:return: 网络输出
'''
#实现自定义类的前向传播逻辑
out=input@self.kernel
out=tf.nn.relu(out)
return out
#实例化网络层
net=MyDense(4,3)
#查看自定义层的参数列表
print(net.variables)#查看全部参数列表
# [
# array([[ 0.2753241 , -0.11746955, 0.8893913 ],
# [ 0.53470445, 0.7483046 , -0.51223654],
# [ 0.791355 , 0.89082956, -0.5275161 ],
# [-0.8661521 , -0.04753697, -0.48771688]], dtype=float32)>]
print(net.trainable_variables)#查看可训练参数的列表
# [
# array([[ 0.2753241 , -0.11746955, 0.8893913 ],
# [ 0.53470445, 0.7483046 , -0.51223654],
# [ 0.791355 , 0.89082956, -0.5275161 ],
# [-0.8661521 , -0.04753697, -0.48771688]], dtype=float32)>]
在上述自定义网络层后,我们利用自定义的网络层来实现自定义网络(模型)。
(1)创建自定义网络类,这个类继承tf.keras.Model
(2)自定义网络类:需要实现初始化__init__方法、前向传播逻辑call方法。
import tensorflow as tf
'''自定义网络层:'''
#自定义网络层类,
class MyDense(tf.keras.layers.Layer):
def __init__(self,inp_dim,oup_dim):
super(MyDense,self).__init__()
self.kernel=self.add_variable('w',[inp_dim,oup_dim],trainable=True)#加入参数列表
#self.kernel=tf.Variable(tf.random.normal([inp_dim,oup_dim]),trainable=True)
def call(self, input, training=None):
#实现自定义类的前向传播逻辑
out=input@self.kernel
out=tf.nn.relu(out)
return out
'''1.常用网络(模型)的构建:'''
#通过网络层容器sequential封装网络模型
network=tf.keras.Sequential([
MyDense(784,256),
MyDense(256,128),
MyDense(128,64),
MyDense(64,32),
MyDense(32,10)
])
network.build(input_shape=(None,28*28))
'''
sequential适用于数据按序从第一层传播到第二层,再从第二层传播到第三层,,,,。
对于复杂的网络结构,例如第三层的输入不仅仅是第二层的输出,还有第二层的输出以及第一层的输出
'''
'''2.自定义网络(模型)的构建:'''
class MyModel(tf.keras.Model):
#自定义网络类
def __init__(self):
super(MyModel,self).__init__()
#完成网络模型内部需要的网络层的构建
self.fc1=MyDense(28*28,256)
self.fc2 = MyDense(256, 128)
self.fc3 = MyDense(128, 64)
self.fc4 = MyDense(64, 32)
self.fc5 = MyDense(32, 10)
'''自定义定义网络模型前向运算逻辑'''
def call(self, inputs, training=None):
#自定义前向传播逻辑
x=self.fc1(inputs)
x=self.fc2(x)
x = self.fc3(x)
x = self.fc4(x)
x = self.fc5(x)
return x
常用网络模型,如resnet、vgg等不需要手动创建网络,可以直接从keras.application子模块中通过一行代码即可创建并使用这些经典模型,同时还可以通过设置参数weight参数加载预训练的网络参数。
通过设置pre_net.trainable选择冻结预训练模型的部分参数,只需要训练其他网络层
通常加载预训练模型后,去除最后一层作为新任务的特征提取子网络,并根据自己的任务追加一个全连接分类层,从而在预训练网络的基础上快速高效的学习新任务。
语法:tf.keras.applications.xxx(weight=‘数据集’。。。),加载预训练的模型及参数
import tensorflow as tf
'''(1)加载预模型,并去掉最后一层'''
#加载在ImageNet上训练后的resnet50网络模型,并去掉最后一层
resnet50=tf.keras.applications.ResNet50(weights='imagenet',include_top=False)
resnet50.summary()
#测试网络输出
x=tf.random.normal([4,224,224,3])
out=resnet50(x)#获得网络输出
print(out.shape)#(4, 7, 7, 2048)
'''(2)在预训练模型的基础上,设计自己的模型'''
#新建池化层
global_average_layer=tf.keras.layers.GlobalAveragePooling2D()
x=tf.random.normal([4,7,7,2048])#利用上层即去掉最后一层的预训练模型的输出,作为本层输入,测试其输出
out=global_average_layer(x)
print(out.shape)#(4, 2048)
#新建全连接层
fc=tf.keras.layers.Dense(100)
x=tf.random.normal([4,2048])#利用上层即全局平均池化层的输出,作为本层输入,测试其输出
out=fc(x)
print(out.shape)#(4, 100)
'''重新包装模型,形成自定义的模型'''
'''可以通过设置'''
model=tf.keras.Sequential([
resnet50,#预训练模型
global_average_layer,#添加的新的全局平均池化层
fc#添加的全连接层
])
'''可以通过设置resnet50.trainable选择冻结resnet50的部分参数,只需要训练其他网络层'''
model.summary()
# Model: "sequential"
# _________________________________________________________________
# Layer (type) Output Shape Param #
# =================================================================
# resnet50 (Model) (None, None, None, 2048) 23587712
# _________________________________________________________________
# global_average_pooling2d (Gl (None, 2048) 0
# _________________________________________________________________
# dense (Dense) (None, 100) 204900
# =================================================================
# Total params: 23,792,612
# Trainable params: 23,739,492
# Non-trainable params: 53,120
# _________________________________________________________________
在网络训练过程中通常需要统计准确率、召回率等测量指标,除了可以通过手动计算的方式获取这些统计数据,keras提供了一些常用的测量工具,位于tf.keras.metrics模块中,专门用于统计训练过程中常用的指标。
keras的测量工具的使用方法一般有四个主要步骤:新建测量器,写入数据,读取统计数据、清零测量器。
在tf.keras.metrics模块中,提供了较多的常用的测量器类:
(1)统计平均值的Mean类:tf.keras.metrics.Mean()
(2)统计准确率的Accuracy类:tf.keras.metrics.Accuracy()
(3)统计余弦相似度CosineSimilarity类:tf.keras.metrics.CosineSimilarity()
(4)等等
import tensorflow as tf
#(1)新建测量器:平均测量器,适合loss数据
loss_meter=tf.keras.metrics.Mean()
建立测量器之后,通过测量器的updata_state函数可以写入新的数据,测量器会根据自身逻辑记录并处理采样数据。
语法:my_metrics.update_state()
import tensorflow as tf
#(1)新建测量器:平均测量器,适合loss数据
loss_meter=tf.keras.metrics.Mean()
#(2)写入数据:记录采样的数据,通过float()函数将张量转换为普通数值
loss_meter.update_state(float(loss))
通过调用测量器的result()函数,来获取统计值
语法:my_metrics.result()
import tensorflow as tf
#(1)新建测量器:平均测量器,适合loss数据
loss_meter=tf.keras.metrics.Mean()
#(2)写入数据:记录采样的数据,通过float()函数将张量转换为普通数值
loss_meter.update_state(float(loss))
#(3)读取统计信息:打印统计期间的平均损失
loss_meter.result()
print(step,'loss',loss_meter.result())
由于测量器会统计所有历史记录数据,因此在启动新一轮统计时,有必要清除历史状态。通过reset_state()可以实现
语法:my_metrics.result()
#正确测量工具代码下文准确率统计实战进行展示。本案例,只是描述测量工具的使用步骤,缺少部分信息,并不能运行。
import tensorflow as tf
#(1)新建测量器:平均测量器,适合loss数据
loss_meter=tf.keras.metrics.Mean()
#(2)写入数据:记录采样的数据,通过float()函数将张量转换为普通数值
loss_meter.update_state(float(loss))
#(3)读取统计信息:打印统计期间的平均损失
loss_meter.result()
print(step,'loss',loss_meter.result())
#(4)清除状态:每训练100轮清零一次,进行重新统计
if step % 100==0:
print(step,'loss',loss_meter.result())
loss_meter.reset_states()#清零统计信息,并进行重新统计
import tensorflow as tf
from tensorflow.keras import datasets, layers, optimizers, Sequential, metrics
'''(1)数据集加载与预处理'''
(x, y), (x_val, y_val) = datasets.mnist.load_data()#加载数据
db = tf.data.Dataset.from_tensor_slices((x,y))#将加载的数据转换为Dataset对象,方便使用tensorflow操作
def preprocess(x, y):
x = tf.cast(x, dtype=tf.float32) / 255.
y = tf.cast(y, dtype=tf.int32)
return x,y
batchsz = 128
db = db.map(preprocess).shuffle(60000).batch(batchsz).repeat(10)#数据处理
print(type(db))
ds_val = tf.data.Dataset.from_tensor_slices((x_val, y_val))
ds_val = ds_val.map(preprocess).batch(batchsz)
'''(2)构建网络'''
network = Sequential([layers.Dense(256, activation='relu'),
layers.Dense(128, activation='relu'),
layers.Dense(64, activation='relu'),
layers.Dense(32, activation='relu'),
layers.Dense(10)])
network.build(input_shape=(None, 28*28))
'''(3)设置优化器'''
optimizer = optimizers.Adam(lr=0.01)
'''(4)新建测量器:精度测量器和平均值测量器'''
acc_meter = metrics.Accuracy()
loss_meter = metrics.Mean()
'''(5)按批,训练'''
for step, (x,y) in enumerate(db):#批训练
with tf.GradientTape() as tape:
# [b, 28, 28] => [b, 784]
x = tf.reshape(x, (-1, 28*28))
out = network(x)#网络预测值
y_onehot = tf.one_hot(y, depth=10) #真实标签值
loss = tf.reduce_mean(tf.losses.categorical_crossentropy(y_onehot, out, from_logits=True))#批量的损失的均值
'''测量器读入数据'''
loss_meter.update_state(loss)
grads = tape.gradient(loss, network.trainable_variables)#根据损失,计算网络参数的梯度
optimizer.apply_gradients(zip(grads, network.trainable_variables))#网络参数更新
if step % 100 == 0:#每训练100批打印一次损失均值,并重置测量器的数据
print(step, 'loss:', loss_meter.result().numpy())
loss_meter.reset_states()
if step % 500 == 0:#每训练500批次,通过验证集计算模型的精度并打印
total, total_correct = 0., 0
acc_meter.reset_states()
for step, (x, y) in enumerate(ds_val): #使用验证集对训练的网络进行精度验证
x = tf.reshape(x, (-1, 28*28))
out = network(x)
pred = tf.argmax(out, axis=1) #网络预测
pred = tf.cast(pred, dtype=tf.int32)
correct = tf.equal(pred, y)#统计本批次正确预测的个数
total_correct += tf.reduce_sum(tf.cast(correct, dtype=tf.int32)).numpy()#所有批次正确预测的个数
total += x.shape[0]
'''精度测量器读入数据'''
acc_meter.update_state(y, pred)
print(step, 'Evaluate Acc:', total_correct/total, acc_meter.result().numpy())