【TensorFlow2.0】自定义网络层、自定义网络、内置预训练模型、测量工具

文章目录

  • 一、自定义网络/层
    • 1.1 自定义网络层
    • 1.2 自定义网络
  • 二、内置预训练模型
    • 2.1 加载模型
  • 三、测量工具
    • 3.1 新建测量器
    • 3.2 写入数据
    • 3.3 读取统计信息
    • 3.4 清除状态
    • 3.5 准确率统计实战

一、自定义网络/层

  网络层:
  (1)常用网络层:keras提供了非常多的常用网络层类,用tf.keras.layers.xxx()实现,但深度学习可以使用的网络层远远不止这些。
  (2)自定义网络层:需要创建自定义网络层类,这个类继承自tf.keras.layers.Layer基类;
  网络(模型):
  (1)通常网络构建:利用tf.keras.Sequential()网络容器,将网路层按顺序加入容器,包装成网络/模型,这种方法适用于数据按序从第一层传播到第二层,再从第二层传播到第三层。。。
  (2)自定义网络构建:需要创建自定义网络类,这个类继承自tf.keras.Model基类。这样建立的自定义类能够方便的利用Layer/Model基类提供的参数管理等功能,并能够处理构建复杂逻辑的网络模型。

1.1 自定义网络层

  (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.2 自定义网络

  在上述自定义网络层后,我们利用自定义的网络层来实现自定义网络(模型)。
  (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选择冻结预训练模型的部分参数,只需要训练其他网络层

2.1 加载模型

  通常加载预训练模型后,去除最后一层作为新任务的特征提取子网络,并根据自己的任务追加一个全连接分类层,从而在预训练网络的基础上快速高效的学习新任务。
  语法: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的测量工具的使用方法一般有四个主要步骤:新建测量器,写入数据,读取统计数据、清零测量器。

3.1 新建测量器

  在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()

3.2 写入数据

  建立测量器之后,通过测量器的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))

3.3 读取统计信息

  通过调用测量器的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())

3.4 清除状态

  由于测量器会统计所有历史记录数据,因此在启动新一轮统计时,有必要清除历史状态。通过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()#清零统计信息,并进行重新统计

3.5 准确率统计实战

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())

你可能感兴趣的:(TensorFlow)