基于DenseNet的图像识别

简介: 在本文中,我们提出了一种架构,将这种见解提炼成一个简单的连接模式:为了确保网络中各层之间的最大信息流,我们**将所有层(具有匹配的特征图大小)直接相互连接**。为了保持前馈特性,**每一层都从所有前面的层获得额外的输入,并将其自己的特征图传递给所有后续层**。图 1 示意性地说明了这种布局。至关重要的是,与 ResNets 相比,我们在将特征传递到层之前从**不通过求和来组合特征**。相反,我们**通过连接(Concatenate操作)它们来组合特征

1、DenseNet简介

  DenseNet的文章我以前写过,原理篇看这里:DenseNet:Densely Connected Convolutional Networks--CVPR2017最佳论文奖

image-20220823211920935

  在本文中,我们提出了一种架构,将这种见解提炼成一个简单的连接模式:为了确保网络中各层之间的最大信息流,我们将所有层(具有匹配的特征图大小)直接相互连接。为了保持前馈特性,每一层都从所有前面的层获得额外的输入,并将其自己的特征图传递给所有后续层。图 1 示意性地说明了这种布局。至关重要的是,与 ResNets 相比,我们在将特征传递到层之前从不通过求和来组合特征。相反,我们通过连接(Concatenate操作)它们来组合特征。

  因此,第 ℓ 层有 ℓ 输入,由所有先前卷积块的特征图组成。它自己的特征图被传递到所有 L−ℓ 后续层。这在 L 层网络中引入了 L(L+1)2 个连接,而不是传统架构中的仅 L 个连接。由于其密集的连接模式,我们将我们的方法称为密集卷积网络(DenseNet)。

2、网络结构

image-20220823214539490

表 1:ImageNet 的 DenseNet 架构。前 3 个网络的增长率为 k = 32,对于 DenseNet-161,k = 48。请注意,==表中显示的每个“conv”层都对应于序列 BN-ReLU-Conv==。

3、花朵数据集

训练集1088张图片,17个类别 测试集272张图片,17个类别。 每个文件夹对应一个花朵类别。

image-20220914213832131

image-20220914213925038

4、DenseNet花朵识别

import tensorflow as tf
import numpy as np
from tensorflow.keras import layers
from tensorflow.keras.models import Model
from tensorflow.keras.applications import DenseNet121
import math
import os
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Input, Dense, Dropout, Conv2D, MaxPool2D, Flatten, GlobalAvgPool2D, \
    BatchNormalization, Activation, Add, ZeroPadding2D, Multiply,Conv1D,GlobalAveragePooling2D,Reshape,multiply,ReLU,MaxPooling2D,Concatenate,\
    AveragePooling2D
from tensorflow.keras.optimizers import Adam
import matplotlib.pyplot as plt
from tensorflow.keras.callbacks import LearningRateScheduler
from tensorflow.keras.models import Model

4.1 Dense Block

  Dense Block的每一个密集层:$BN+ReLU+1*1Conv+BN+ReLU+3*3Conv$

  每一层产生k个特征图,原论文中让每个1*1个卷积产生4k个特征图

  growth_rate:网络的增长率,原论文中超参数名称为k

  论文中对内核大小为3*3的卷积层,输入的每一边都用一个像素补零,以保持特征图大小固定。(代码中设置padding='same'即可)

# Bottleneck layers
# BN+ReLU+1*1Conv+BN+ReLU+3*3Conv
# 每一层产生k个特征图,原论文中让每个1*1个卷积产生4k个特征图
# growth_rate:网络的增长率,原论文中超参数名称为k
def conv_block(x,growth_rate,name):
    x1=BatchNormalization(name=name+'_0_bn')(x)
    x1=ReLU(name=name+'_0_relu')(x1)
    x1=Conv2D(filters=4*growth_rate,
              kernel_size=(1,1),
              use_bias=False,
              name=name+'_1_conv')(x1)
    x1=BatchNormalization(name=name+'_1_bn')(x1)
    x1=ReLU(name=name+'_1_relu')(x1)
    # 论文原话:对内核大小为3*3的卷积层,输入的每一边都用一个像素补零,以保持特征图大小固定。
    x1=Conv2D(growth_rate,
              kernel_size=(3,3),
              padding='same',
              use_bias=False,
              name=name+'_2_conv')(x1)
    # 将前面所有层的特征堆叠后传到下一层
    out=Concatenate(axis=-1,name=name+'_concat')([x,x1])
    return out

'''
x: input tensor.
    blocks: integer, the number of building blocks.
    name: string, block label.
'''
# DenseNet
def dense_block(x,blocks,name):
    for i in range(blocks):
        x=conv_block(x,32,name=name+'_block'+str(i+1))
    return x

4.2 Transition Layer(过渡层)

  过渡层:$BN+1*1Conv+2*2AvgPool$

  reduction:压缩因子,原论文中名称为$\theta$,设置为0.5。

# 论文原话:使用1*1卷积和2*2平均池化作为两个连续密集块之间的过渡层
# 过渡层:BN+1*1Conv+2*2AvgPool
# reduction:压缩因子,原论文中设置为0.5
def transition_block(x,reduction,name):
    x=BatchNormalization(name=name+'_bn')(x)
    x=ReLU(name=name+'_relu')(x)
    x=Conv2D(filters=int(x.shape[-1]*reduction),
             kernel_size=(1,1),
             use_bias=False,
             name=name+'_conv')(x)
    x=AveragePooling2D(pool_size=(2,2),strides=2,name=name+'_pool')(x)
    return x

4.3 DensNet121网络骨干

DenseNet121(k=32):blocks=[6,12,24,16]
DenseNet169(k=32):blocks=[6,12,32,32]
DenseNet201(k=32):blocks=[6,12,48,32]
DenseNet161(k=48):blocks=[6,12,36,24]
def DenseNet(blocks,include_top=True,weights='imagenet',intput_shape=None,classes=1000):
    img_input=Input(shape=intput_shape)
    x=ZeroPadding2D(padding=((3,3),(3,3)))(img_input)
    # 论文原话:初始卷积层包含2k个大小为7*7的卷积,步长为2(k=32)
    # 其实这里完全可以去掉这两个ZeroPadding2D,直接给这里的卷积和池化设置padding='same'即可,
    # 但是我看谷歌的源码中没用same padding,所以这里我也没用。
    x=Conv2D(filters=64,kernel_size=(7,7),strides=2,use_bias=False,name='conv1/conv')(x)
    x=BatchNormalization(name='conv1/bn')(x)
    x=ReLU(name='conv1/relu')(x)
    x=ZeroPadding2D(padding=((1,1),(1,1)))(x)
    x=MaxPooling2D(pool_size=(3,3),strides=2,name='pool1')(x)

    x=dense_block(x,blocks[0],name='conv2')
    x=transition_block(x,0.5,name='pool2')

    x=dense_block(x,blocks[1],name='conv3')
    x=transition_block(x,0.5,name='pool3')

    x=dense_block(x,blocks[2],name='conv4')
    x=transition_block(x,0.5,name='pool4')

    x=dense_block(x,blocks[3],name='conv5')

    x=BatchNormalization(name='bn')(x)
    x=ReLU(name='relu')(x)
    if include_top:
        x=GlobalAveragePooling2D(name='global_avg_pool')(x)
        x=Dense(classes,activation='softmax')(x)

    model=Model(img_input,x)
    return model
densenet121=DenseNet(blocks=[6,12,24,16],include_top=False,intput_shape=(224,224,3))
densenet121.summary()

image-20220914214441979

# 类别数
num_classes = 17
# 批次大小
batch_size = 32
# 周期数
epochs=100
# 图片大小
image_size = 224

model=tf.keras.Sequential([
    densenet121,
    layers.GlobalAveragePooling2D(),
    layers.Dense(num_classes,activation='softmax')
])
model.summary()

image-20220914214510977

这里仿照源码搭建的,不太优雅,其实可以一次性直接搭建好的,这里懒得改了。

4.4 数据增强

# 训练集数据进行数据增强
train_datagen = ImageDataGenerator(
    rotation_range=20,  # 随机旋转度数
    width_shift_range=0.1,  # 随机水平平移
    height_shift_range=0.1,  # 随机竖直平移
    rescale=1 / 255,  # 数据归一化
    shear_range=10,  # 随机错切变换
    zoom_range=0.1,  # 随机放大
    horizontal_flip=True,  # 水平翻转
    brightness_range=(0.7, 1.3),  # 亮度变化
    fill_mode='nearest',  # 填充方式
)
# 测试集数据只需要归一化就可以
test_datagen = ImageDataGenerator(
    rescale=1 / 255,  # 数据归一化
)

# 训练集数据生成器,可以在训练时自动产生数据进行训练
# 从'data/train'获得训练集数据
# 获得数据后会把图片resize为image_size×image_size的大小
# generator每次会产生batch_size个数据
train_generator = train_datagen.flow_from_directory(
    '../data/花朵分类(17)/train',
    target_size=(image_size, image_size), # 调整图像尺寸
    batch_size=batch_size,
)

# 测试集数据生成器
test_generator = test_datagen.flow_from_directory(
    '../data/花朵分类(17)/test',
    target_size=(image_size, image_size),
    batch_size=batch_size,
)
# 字典的键为17个文件夹的名字,值为对应的分类编号
print(train_generator.class_indices)

image-20220914214622027

4.5 callback

# 学习率调节函数,逐渐减小学习率
def adjust_learning_rate(epoch):
    # 前40周期
    if epoch<=40:
        lr = 1e-4
    # 前40到80周期
    elif 40 < epoch <= 80:
        lr = 1e-5
    # 80到100周期
    else:
        lr = 1e-6
    return lr

# 定义优化器
adam = Adam(lr=1e-4)

# 读取模型
checkpoint_save_path = "./checkpoint/DenseNet121(不使用预训练)-花朵分类(17)-.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)

# 定义学习率衰减策略
callbacks = []
callbacks.append(LearningRateScheduler(adjust_learning_rate))
callbacks.append(cp_callback)

4.6 模型训练

# 定义优化器,loss function,训练过程中计算准确率
model.compile(optimizer=adam,loss='categorical_crossentropy',metrics=['accuracy'])

# Tensorflow2.1版本(包括2.1)之后可以直接使用fit训练模型
history = model.fit(x=train_generator,epochs=epochs,validation_data=test_generator,callbacks=callbacks)

image-20220914214742021

这里没有使用预训练,所以效果没有官方的好。

4.7 acc和loss可视化

# 画出训练集准确率曲线图
plt.plot(np.arange(epochs),history.history['accuracy'],c='b',label='train_accuracy')
# 画出验证集准确率曲线图
plt.plot(np.arange(epochs),history.history['val_accuracy'],c='y',label='val_accuracy')
# 图例
plt.legend()
# x坐标描述
plt.xlabel('epochs')
# y坐标描述
plt.ylabel('accuracy')
# 显示图像
plt.show()

image-20220914214823267

# 画出训练集loss曲线图
plt.plot(np.arange(epochs),history.history['loss'],c='b',label='train_loss')
# 画出验证集loss曲线图
plt.plot(np.arange(epochs),history.history['val_loss'],c='y',label='val_loss')
# 图例
plt.legend()
# x坐标描述
plt.xlabel('epochs')
# y坐标描述
plt.ylabel('loss')
# 显示图像
plt.show()

image-20220914214839705

4.8 与预训练的DenseNet结果对比

这里直接利用迁移学习,将在ImageNet模型上预训练的DenseNet模型用来训练,结果如下:

image-20220914215005295

acc:

image-20220914215014394

loss:

image-20220914215029243

  可以看到,有没有预训练对结果的影响很大,而且预训练的权重都是别人经过大量实验得出的,所以后面训练模型的时候尽量用迁移学习方法去做,自己从头训练的话太慢了,效果还不行。
目录
相关文章
|
Windows
mathtype7产品激活密钥最新
MathType是强大的数学公式编辑器,MathType公式编辑器可以说是专门为理科生准备的软件,它可以帮助用户快速的在各种文档中插入符号和公式,不论是简单的公式和符号,还是复杂的都可以非常轻松的输入,并且在与office文档结合使用时,表现的非常完美,是非常好的一款软件,与常见的文字处理软件和演示程序配合使用,能够在各种文档中加入复杂的数学公式和符号,可用在编辑数学试卷、书籍、报刊、论文、幻灯演示等方面,是编辑数学资料的得力工具。
65820 0
|
5月前
|
机器学习/深度学习 人工智能 供应链
告别脆弱的单体应用,用多智能体网络构建稳定的生产力工具
多智能体系统(MAS)代表AI从“单点智能”向“协作智能”的范式跃迁:通过角色化智能体分工协作,天然适配供应链、金融、交通等复杂分布式问题;依托CrewAI等成熟框架,实现任务分解、通信协调与容错降级,兼顾鲁棒性与可解释性。
373 1
|
6月前
|
NoSQL 测试技术 Redis
构建一个可自我改进的多 Agent RAG 系统:架构、评估,以及带人工审核的 Prompt 反馈闭环
本文提出一种可审计、可改进的多Agent RAG系统:通过Orchestrator动态编排分解、检索、批判与合成Agent,结合Token预算管控、溯源式答案生成及SSE实时可观测性;首创Prompt自我改进闭环——自动定位薄弱维度、生成结构化改写、并经回归检测+人工审批上线,兼顾LLM系统质量与工程严谨性。
442 2
构建一个可自我改进的多 Agent RAG 系统:架构、评估,以及带人工审核的 Prompt 反馈闭环
|
人工智能 测试技术 vr&ar
DataDoP :一个大规模多模态数据集,包含 29K 真实世界的自由运动相机轨迹、深度图和详细的动作描述,目的支持艺术化相机轨迹生成。
2025-04-10 ,由浙江大学、上海人工智能实验室、斯坦福大学、香港中文大学和南洋理工大学联合创建了 DataDoP 数据集。该数据集包含 29K 真实世界的自由运动相机轨迹、深度图和详细的动作描述,目的为艺术化相机轨迹生成提供高质量的训练数据,推动基于学习的电影摄影技术发展。 一、研究背景 在视频制作中,相机轨迹设计是传达导演意图和增强视觉叙事的关键工具。传统方法依赖于几何优化或手工设计的程序系统,而近年来的基于学习的方法则继承了结构偏差或缺乏文本对齐,限制了创意合成。 目前遇到困难和挑战: 1、传统方法的局限性:传统方法依赖于几何建模或成本函数工程,限制了创意合成。 2、现有数据集的
|
iOS开发 MacOS Windows
电脑怎么截图?截屏电脑快捷键ctrl加什么?
截图是我们日常使用电脑过程中非常常见的操作之一。无论是想保存有用的信息、分享有趣的内容,还是记录某个错误信息,截图都是一个简单而有效的方式。但是,不同的操作系统和需求会决定使用不同的方法来截图。接下来,我们将详细介绍几种在Windows和Mac电脑上常见的截图方法,帮助您快速掌握这一技能。
电脑怎么截图?截屏电脑快捷键ctrl加什么?
Vue3标签页(Tabs)
该组件提供可自定义的标签页功能,支持居中显示、不同尺寸与样式,并能设置当前激活选项。其主要属性包括标签页数组、居中显示、尺寸、样式、间隙及激活键值。示例展示了基本使用、卡片式标签页、禁用选项、左右滑动等功能。组件基于Vue3开发,并利用`useResizeObserver`等技术实现。[在线预览](https://themusecatcher.github.io/vue-amazing-ui/guide/components/tabs.html)。
1435 0
Vue3标签页(Tabs)
|
算法 JavaScript 前端开发
深入了解Vue2和Vue3的Diff算法差异!
总的来说,Vue3在Diff算法上的优化体现了更智能的静态内容处理、更高效的动态内容更新以及更灵活的内部结构。这些优化使得Vue3在运行时性能上有了显著的提升,尤其是在大型应用和复杂界面的场景下。通过不断地技术迭代和优化,Vue3为开发者提供了更高效、更易用的前端开发体验。
1592 6
|
数据采集 存储 NoSQL
提高爬虫性能的 5 个关键技巧:从并发到异步执行
本文介绍了提高网络爬虫性能的五个关键技巧:并发请求、异步执行、使用代理IP、限制请求频率与休眠时间、优化数据提取与存储。结合拼多多的实际案例,展示了如何通过这些技术优化爬虫效率,确保数据采集的高效性和稳定性。
1240 0
|
算法 Java 计算机视觉
【Qt&OpenCV 直方图均衡 CLAHE】
【Qt&OpenCV 直方图均衡 CLAHE】
504 0
|
弹性计算 虚拟化 异构计算
阿里云GPU服务器NVIDIA A100 GPU卡租用价格表
阿里云GPU服务器NVIDIA A100 GPU卡租用价格表,阿里云GPU服务器租用价格表包括包年包月价格、一个小时收费以及学生GPU服务器租用费用,阿里云GPU计算卡包括NVIDIA V100计算卡、T4计算卡、A10计算卡和A100计算卡,GPU云服务器gn6i可享受3折优惠,阿里云百科分享阿里云GPU服务器租用价格表、GPU一个小时多少钱以及学生GPU服务器收费价格表
23754 0
阿里云GPU服务器NVIDIA A100 GPU卡租用价格表

热门文章

最新文章