TensorFlow2的ResNet实现,满满干货! 您所在的位置:网站首页 resnest代码 TensorFlow2的ResNet实现,满满干货!

TensorFlow2的ResNet实现,满满干货!

2024-04-22 05:47| 来源: 网络整理| 查看: 265

TensorFlow2的ResNet实现,满满干货!

TensorFlow2

TensorFlow2是2019去年10月由Google发布的TensorFlow升级版。这可以算是TensorFlow的历史性改革,相比1.X版本增加了许多新内容。由于更新了TF的版本,许多小伙伴在原有基于1.X版本的程序就会出现许多警告和报错。由于去年年底课业繁忙,考试也一堆一堆,很遗憾没有在第一时间了解TF2的新内容。

放假在家好好研究了一下TF2,拿几个经典的卷积神经网络——LeNet5、AlexNet、VGG16、GoogLeNet(Inception v1)和ResNet练了练手。

ResNet

先介绍一下ResNet,深度学习和目标检测领域的小伙伴们应该对此都比较熟悉了,即便不知道具体结构和原理,对这个名字也应该听过无数回了。ResNet是一个应用十分广泛的卷积神经网络的特征提取网络,在2016年由大名鼎鼎的何恺明(He-Kaiming)及其团队提出,他曾以第一作者身份拿过2次CVPR最佳论文奖(2009年和2016年),其中2016年CVPR最佳论文就是这个深度残差网络。

残差网络是致力于使用多个有参网络层来学习输入、输出之间的残差函数即 F(x)=H(x)-x , x 为浅层输出, H(x) 为深层输出, F(x) 为夹在它们之间的一个函数变换。当浅层 x 中的特征已经成熟时,如果任何对于特征 x 的改变都会让 loss 变大的话, F(x) 就会自动趋于 0 , x 就可以从恒等映射(identity mapping)路径继续向下传递。这样可以在不增加计算成本的基础上实现一下目的:在前向传递的过程中,当浅层的 x 输出已经足够成熟(optimal),让深层网络后面的层(subsequent layers)能够实现恒等映射的作用。

以下是实现残差学习的一个版块(block)示意图(摘自论文):

程序实现的思路也是通过先构建一个block然后根据ResNet的结构实现整个网络的构建。关于ResNet的有关概念这里不做过多的介绍。

环境

CPU: Intel i5 9400FGPU: GTX 1660RAM: 16GCUDA: 10.1TF Version:2.1

使用的是 Spyder 编辑,因为安装了 Anaconda 其自带的一些编辑器都非常方便,可以直接使用。

数据集

使用了fashion数据集。fashion数据集出自论文:

这是2016年CVPR上的一篇论文。该数据集中包含:

289,222 张服装图片 clothes images;50 个服装类别 clothing categories1,000 个服装属性 clothing attributes。代码实现与结果结果展示

loss和acc曲线:

参数量:

代码# -*- coding: utf-8 -*- """ Created on Tue Apr 14 2020 @author: jiollos """ # 导入包 import tensorflow as tf import os import numpy as np from matplotlib import pyplot as plt from tensorflow.keras.layers import Conv2D, BatchNormalization, Activation, MaxPool2D, Dropout, Flatten, Dense from tensorflow.keras import Model # 设置显示格式 np.set_printoptions(threshold=np.inf) # 导入数据集 fashion = tf.keras.datasets.fashion_mnist (x_train, y_train), (x_test, y_test) = fashion.load_data() x_train, x_test = x_train / 255.0, x_test / 255.0 print("x_train.shape", x_train.shape) # 给数据增加一个维度,使数据和网络结构匹配 x_train = x_train.reshape(x_train.shape[0], 28, 28, 1) x_test = x_test.reshape(x_test.shape[0], 28, 28, 1) print("x_train.shape", x_train.shape) # 构建ResNetBlock的class class ResnetBlock(Model): def __init__(self, filters, strides=1, residual_path=False): super(ResnetBlock, self).__init__() self.filters = filters self.strides = strides self.residual_path = residual_path # 第1个部分 self.c1 = Conv2D(filters, (3, 3), strides=strides, padding='same', use_bias=False) self.b1 = BatchNormalization() self.a1 = Activation('relu') # 第2个部分 self.c2 = Conv2D(filters, (3, 3), strides=1, padding='same', use_bias=False) self.b2 = BatchNormalization() # residual_path为True时,对输入进行下采样,即用1x1的卷积核做卷积操作,保证x能和F(x)维度相同,顺利相加 if residual_path: self.down_c1 = Conv2D(filters, (1, 1), strides=strides, padding='same', use_bias=False) self.down_b1 = BatchNormalization() self.a2 = Activation('relu') def call(self, inputs): # residual等于输入值本身,即residual=x residual = inputs # 将输入通过卷积、BN层、激活层,计算F(x) x = self.c1(inputs) x = self.b1(x) x = self.a1(x) x = self.c2(x) y = self.b2(x) # 如果维度不同则调用代码,否则不执行 if self.residual_path: residual = self.down_c1(inputs) residual = self.down_b1(residual) # 最后输出的是两部分的和,即F(x)+x或F(x)+Wx,再过激活函数 out = self.a2(y + residual) return out class ResNet18(Model): def __init__(self, block_list, initial_filters=64): # block_list表示每个block有几个卷积层 super(ResNet18, self).__init__() self.num_blocks = len(block_list) # 共有几个block self.block_list = block_list self.out_filters = initial_filters # 结构定义 self.c1 = Conv2D(self.out_filters, (3, 3), strides=1, padding='same', use_bias=False) self.b1 = BatchNormalization() self.a1 = Activation('relu') self.blocks = tf.keras.models.Sequential() # 构建ResNet网络结构 for block_id in range(len(block_list)): # 第几个resnet block for layer_id in range(block_list[block_id]): # 第几个卷积层 if block_id != 0 and layer_id == 0: # 对除第一个block以外的每个block的输入进行下采样 block = ResnetBlock(self.out_filters, strides=2, residual_path=True) else: block = ResnetBlock(self.out_filters, residual_path=False) self.blocks.add(block) # 将构建好的block加入resnet self.out_filters *= 2 # 下一个block的卷积核数是上一个block的2倍 self.p1 = tf.keras.layers.GlobalAveragePooling2D() self.f1 = tf.keras.layers.Dense(10, activation='softmax', kernel_regularizer=tf.keras.regularizers.l2()) def call(self, inputs): x = self.c1(inputs) x = self.b1(x) x = self.a1(x) x = self.blocks(x) x = self.p1(x) y = self.f1(x) return y # 运行,一共4个元素,所以block执行4次,每次有2个 model = ResNet18([2, 2, 2, 2]) # 设置优化器等 model.compile(optimizer='adam', loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False), metrics=['sparse_categorical_accuracy']) # 设置断点 checkpoint_save_path = "./checkpoint/ResNet18.ckpt" if os.path.exists(checkpoint_save_path + '.index'): print('-------------load the model-----------------') model.load_weights(checkpoint_save_path) cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path, save_weights_only=True, save_best_only=True) history = model.fit(x_train, y_train, batch_size=128, epochs=10, validation_data=(x_test, y_test), validation_freq=1, callbacks=[cp_callback]) # 显示结果 model.summary() # 保存权重 # print(model.trainable_variables) file = open('./weights.txt', 'w') for v in model.trainable_variables: file.write(str(v.name) + '\n') file.write(str(v.shape) + '\n') file.write(str(v.numpy()) + '\n') file.close() # 显示训练集和验证集的acc和loss曲线 acc = history.history['sparse_categorical_accuracy'] val_acc = history.history['val_sparse_categorical_accuracy'] loss = history.history['loss'] val_loss = history.history['val_loss'] plt.plot(acc, label='Training Accuracy') plt.plot(val_acc, label='Validation Accuracy') plt.title('Training and Validation Accuracy') plt.legend() plt.show() plt.plot(loss, label='Training Loss') plt.plot(val_loss, label='Validation Loss') plt.title('Training and Validation Loss') plt.legend() plt.show()联系我如果有什么想法与建议可以在评论区评论,或者与我私信。更多内容请关注我的CSDN账号:@我什么也不会TwT。或者扫描下方二维码:

感谢阅读,如果你喜欢或者觉得对你有所帮助的话,欢迎“喜欢”和“收藏”,也可以分享给你的小伙伴们~



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有