改进的注意力多尺度特征融合卷积神经网络
本文改进注意力多尺度特征融合卷积神经网络,加入基于style的重新校准模块(SRM),通过样式池提取特征图通道样式信息,经通道无关的style集成估计权重,增强CNN表示能力且参数少。用Caltech101的16类数据集,对比VGG19、ResNet50等模型,改进模型性能提升较明显。
① 项目背景
本文改进了注意力多尺度特征融合卷积神经网络,加入了一种基于style的重新校准模块(SRM),可以通过利用其style自适应地重新校准中间特征图。 SRM首先通过样式池从特征图的每个通道中提取样式信息,然后通过与通道无关的style集成来估计每个通道的重新校准权重。通过将各个style的相对重要性纳入特征图,SRM有效地增强了CNN的表示能力。重点是轻量级,引入的参数非常少,同时效果还不错。
论文地址:https://arxiv.org/pdf/1903.10829.pdf
② 数据准备
2.1 解压缩数据集
我们将网上获取的数据集以压缩包的方式上传到aistudio数据集中,并加载到我们的项目内。
在使用之前我们进行数据集压缩包的一个解压。
In [1]
!unzip -oq /home/aistudio/data/data69664/Images.zip -d work/dataset
In [2]
import paddleimport numpy as npfrom typing import Callable#参数配置config_parameters = { "class_dim": 16, #分类数
"target_path":"/home/aistudio/work/",
'train_image_dir': '/home/aistudio/work/trainImages', 'eval_image_dir': '/home/aistudio/work/evalImages', 'epochs':100, 'batch_size': 32, 'lr': 0.01}
2.2 划分数据集
接下来我们使用标注好的文件进行数据集类的定义,方便后续模型训练使用。
In [3]
import osimport shutil
train_dir = config_parameters['train_image_dir']
eval_dir = config_parameters['eval_image_dir']
paths = os.listdir('work/dataset/Images')if not os.path.exists(train_dir):
os.mkdir(train_dir)if not os.path.exists(eval_dir):
os.mkdir(eval_dir)for path in paths:
imgs_dir = os.listdir(os.path.join('work/dataset/Images', path))
target_train_dir = os.path.join(train_dir,path)
target_eval_dir = os.path.join(eval_dir,path) if not os.path.exists(target_train_dir):
os.mkdir(target_train_dir) if not os.path.exists(target_eval_dir):
os.mkdir(target_eval_dir) for i in range(len(imgs_dir)): if ' ' in imgs_dir[i]:
new_name = imgs_dir[i].replace(' ', '_') else:
new_name = imgs_dir[i]
target_train_path = os.path.join(target_train_dir, new_name)
target_eval_path = os.path.join(target_eval_dir, new_name)
if i % 5 == 0:
shutil.copyfile(os.path.join(os.path.join('work/dataset/Images', path), imgs_dir[i]), target_eval_path) else:
shutil.copyfile(os.path.join(os.path.join('work/dataset/Images', path), imgs_dir[i]), target_train_path)print('finished train val split!')
finished train val split!
2.3 数据集定义与数据集展示
2.3.1 数据集展示
我们先看一下解压缩后的数据集长成什么样子,对比分析经典模型在Caltech101抽取16类mini版数据集上的效果
In [5]
import osimport randomfrom matplotlib import pyplot as pltfrom PIL import Image
imgs = []
paths = os.listdir('work/dataset/Images')for path in paths:
img_path = os.path.join('work/dataset/Images', path) if os.path.isdir(img_path):
img_paths = os.listdir(img_path)
img = Image.open(os.path.join(img_path, random.choice(img_paths)))
imgs.append((img, path))
f, ax = plt.subplots(4, 4, figsize=(12,12))for i, img in enumerate(imgs[:16]):
ax[i//4, i%4].imshow(img[0])
ax[i//4, i%4].axis('off')
ax[i//4, i%4].set_title('label: %s' % img[1])
plt.show()
<Figure size 864x864 with 16 Axes>
2.3.2 导入数据集的定义实现
In [6]
#数据集的定义class Dataset(paddle.io.Dataset):
"""
步骤一:继承paddle.io.Dataset类
"""
def __init__(self, transforms: Callable, mode: str ='train'):
"""
步骤二:实现构造函数,定义数据读取方式
"""
super(Dataset, self).__init__()
self.mode = mode
self.transforms = transforms
train_image_dir = config_parameters['train_image_dir']
eval_image_dir = config_parameters['eval_image_dir']
train_data_folder = paddle.vision.DatasetFolder(train_image_dir)
eval_data_folder = paddle.vision.DatasetFolder(eval_image_dir)
if self.mode == 'train':
self.data = train_data_folder elif self.mode == 'eval':
self.data = eval_data_folder def __getitem__(self, index):
"""
步骤三:实现__getitem__方法,定义指定index时如何获取数据,并返回单条数据(训练数据,对应的标签)
"""
data = np.array(self.data[index][0]).astype('float32')
data = self.transforms(data)
label = np.array([self.data[index][1]]).astype('int64')
return data, label
def __len__(self):
"""
步骤四:实现__len__方法,返回数据集总数目
"""
return len(self.data)
In [7]
from paddle.vision import transforms as T#数据增强transform_train =T.Compose([T.Resize((256,256)), #T.RandomVerticalFlip(10),
#T.RandomHorizontalFlip(10),
T.RandomRotation(10),
T.Transpose(),
T.Normalize(mean=[0, 0, 0], # 像素值归一化
std =[255, 255, 255]), # transforms.ToTensor(), # transpose操作 + (img / 255),并且数据结构变为PaddleTensor
T.Normalize(mean=[0.50950350, 0.54632660, 0.57409690],# 减均值 除标准差
std= [0.26059777, 0.26041326, 0.29220656])# 计算过程:output[channel] = (input[channel] - mean[channel]) / std[channel]
])
transform_eval =T.Compose([ T.Resize((256,256)),
T.Transpose(),
T.Normalize(mean=[0, 0, 0], # 像素值归一化
std =[255, 255, 255]), # transforms.ToTensor(), # transpose操作 + (img / 255),并且数据结构变为PaddleTensor
T.Normalize(mean=[0.50950350, 0.54632660, 0.57409690],# 减均值 除标准差
std= [0.26059777, 0.26041326, 0.29220656])# 计算过程:output[channel] = (input[channel] - mean[channel]) / std[channel]
])
2.3.3 实例化数据集类
根据所使用的数据集需求实例化数据集类,并查看总样本量。
In [8]
train_dataset =Dataset(mode='train',transforms=transform_train)
eval_dataset =Dataset(mode='eval', transforms=transform_eval )#数据异步加载train_loader = paddle.io.DataLoader(train_dataset,
places=paddle.CUDAPlace(0),
batch_size=32,
shuffle=True, #num_workers=2,
#use_shared_memory=True
)
eval_loader = paddle.io.DataLoader (eval_dataset,
places=paddle.CUDAPlace(0),
batch_size=32, #num_workers=2,
#use_shared_memory=True
)print('训练集样本量: {},验证集样本量: {}'.format(len(train_loader), len(eval_loader)))
训练集样本量: 45,验证集样本量: 12
③ 模型选择和开发
3.1 对比网络构建
本次我们选取了经典的卷积神经网络resnet50,vgg19,mobilenet_v2来进行实验比较。
In [ ]
network = paddle.vision.models.vgg19(num_classes=16)#模型封装model = paddle.Model(network)#模型可视化model.summary((-1, 3,256 , 256))
In [ ]
network = paddle.vision.models.resnet50(num_classes=16)#模型封装model2 = paddle.Model(network)#模型可视化model2.summary((-1, 3,256 , 256))
3.2 对比网络训练
In [12]
#优化器选择class SaveBestModel(paddle.callbacks.Callback):
def __init__(self, target=0.5, path='work/best_model', verbose=0):
self.target = target
self.epoch = None
self.path = path def on_epoch_end(self, epoch, logs=None):
self.epoch = epoch def on_eval_end(self, logs=None):
if logs.get('acc') > self.target:
self.target = logs.get('acc')
self.model.save(self.path) print('best acc is {} at epoch {}'.format(self.target, self.epoch))
callback_visualdl = paddle.callbacks.VisualDL(log_dir='work/vgg19')
callback_savebestmodel = SaveBestModel(target=0.5, path='work/best_model')
callbacks = [callback_visualdl, callback_savebestmodel]
base_lr = config_parameters['lr']
epochs = config_parameters['epochs']def make_optimizer(parameters=None):
momentum = 0.9
learning_rate= paddle.optimizer.lr.CosineAnnealingDecay(learning_rate=base_lr, T_max=epochs, verbose=False)
weight_decay=paddle.regularizer.L2Decay(0.0001)
optimizer = paddle.optimizer.Momentum(
learning_rate=learning_rate,
momentum=momentum,
weight_decay=weight_decay,
parameters=parameters) return optimizer
optimizer = make_optimizer(model.parameters())
model.prepare(optimizer,
paddle.nn.CrossEntropyLoss(),
paddle.metric.Accuracy())
model.fit(train_loader,
eval_loader,
epochs=100,
batch_size=1, # 是否打乱样本集
callbacks=callbacks,
verbose=1) # 日志展示格式
3.3 改进的注意力多尺度特征融合卷积神经网络SRM-Inception-Net
3.3.1 SRM模块的介绍
SRM首先通过样式池从特征图的每个通道中提取样式信息,然后通过与通道无关的style集成来估计每个通道的重新校准权重。通过将各个style的相对重要性纳入特征图,SRM有效地增强了CNN的表示能力。重点是轻量级,引入的参数非常少,其中Style Pooling是avgpool和stdpool拼接,Style Intergration就是一个自适应加权融合.
图1 SRM模块细节示意图
In [9]
import paddle.nn as nnclass srm_layer(nn.Layer):
def __init__(self, channel):
super(srm_layer, self).__init__()
self.cfc = self.create_parameter(shape=[channel, 2], default_initializer=nn.initializer.Assign(paddle.zeros([channel, 2])))
self.bn = nn.BatchNorm2D(channel)
self.activation = nn.Sigmoid() setattr(self.cfc, 'srm_param', True) setattr(self.bn.weight, 'srm_param', True) setattr(self.bn.bias, 'srm_param', True) def _style_pooling(self, x, eps=1e-5):
N, C, _, _ = x.shape
channel_mean = paddle.mean(paddle.reshape(x, [N, C, -1]), axis=2, keepdim=True)
channel_var = paddle.var(paddle.reshape(x, [N, C, -1]), axis=2, keepdim=True) + eps
channel_std = paddle.sqrt(channel_var)
t = paddle.concat((channel_mean, channel_std), axis=2) return t
def _style_integration(self, t):
z = t*paddle.reshape(self.cfc, [-1, self.cfc.shape[0], self.cfc.shape[1]])
tmp = paddle.sum(z, axis=2)
z = paddle.reshape(tmp, [tmp.shape[0], tmp.shape[1], 1, 1]) # B x C x 1 x 1
z_hat = self.bn(z)
g = self.activation(z_hat) return g def forward(self, x):
# B x C x 2
t = self._style_pooling(x) # B x C x 1 x 1
g = self._style_integration(t) return x * g
3.3.2 注意力多尺度特征融合卷积神经网络的搭建
In [10]
import paddle.nn.functional as F# 构建模型(Inception层)class Inception(paddle.nn.Layer):
def __init__(self, in_channels, c1, c2, c3, c4):
super(Inception, self).__init__() # 路线1,卷积核1x1
self.route1x1_1 = paddle.nn.Conv2D(in_channels, c1, kernel_size=1) # 路线2,卷积层1x1、卷积层3x3
self.route1x1_2 = paddle.nn.Conv2D(in_channels, c2[0], kernel_size=1)
self.route3x3_2 = paddle.nn.Conv2D(c2[0], c2[1], kernel_size=3, padding=1) # 路线3,卷积层1x1、卷积层5x5
self.route1x1_3 = paddle.nn.Conv2D(in_channels, c3[0], kernel_size=1)
self.route5x5_3 = paddle.nn.Conv2D(c3[0], c3[1], kernel_size=5, padding=2) # 路线4,池化层3x3、卷积层1x1
self.route3x3_4 = paddle.nn.MaxPool2D(kernel_size=3, stride=1, padding=1)
self.route1x1_4 = paddle.nn.Conv2D(in_channels, c4, kernel_size=1) def forward(self, x):
route1 = F.relu(self.route1x1_1(x))
route2 = F.relu(self.route3x3_2(F.relu(self.route1x1_2(x))))
route3 = F.relu(self.route5x5_3(F.relu(self.route1x1_3(x))))
route4 = F.relu(self.route1x1_4(self.route3x3_4(x)))
out = [route1, route2, route3, route4] return paddle.concat(out, axis=1) # 在通道维度(axis=1)上进行连接# 构建 BasicConv2d 层def BasicConv2d(in_channels, out_channels, kernel, stride=1, padding=0):
layer = paddle.nn.Sequential(
paddle.nn.Conv2D(in_channels, out_channels, kernel, stride, padding),
paddle.nn.BatchNorm2D(out_channels, epsilon=1e-3),
paddle.nn.ReLU()) return layer# 搭建网络class TowerNet(paddle.nn.Layer):
def __init__(self, in_channel, num_classes):
super(TowerNet, self).__init__()
self.b1 = paddle.nn.Sequential(
BasicConv2d(in_channel, out_channels=64, kernel=3, stride=2, padding=1),
paddle.nn.MaxPool2D(2, 2))
self.b2 = paddle.nn.Sequential(
BasicConv2d(64, 128, kernel=3, padding=1),
paddle.nn.MaxPool2D(2, 2))
self.b3 = paddle.nn.Sequential(
BasicConv2d(128, 256, kernel=3, padding=1),
paddle.nn.MaxPool2D(2, 2),
srm_layer(256))
self.b4 = paddle.nn.Sequential(
BasicConv2d(256, 256, kernel=3, padding=1),
paddle.nn.MaxPool2D(2, 2),
srm_layer(256))
self.b5 = paddle.nn.Sequential(
Inception(256, 64, (64, 128), (16, 32), 32),
paddle.nn.MaxPool2D(2, 2),
srm_layer(256),
Inception(256, 64, (64, 128), (16, 32), 32),
paddle.nn.MaxPool2D(2, 2),
srm_layer(256),
Inception(256, 64, (64, 128), (16, 32), 32))
self.AvgPool2D=paddle.nn.AvgPool2D(2)
self.flatten=paddle.nn.Flatten()
self.b6 = paddle.nn.Linear(256, num_classes) def forward(self, x):
x = self.b1(x)
x = self.b2(x)
x = self.b3(x)
x = self.b4(x)
x = self.b5(x)
x = self.AvgPool2D(x)
x = self.flatten(x)
x = self.b6(x) return x
In [11]
model = paddle.Model(TowerNet(3, config_parameters['class_dim'])) model.summary((-1, 3, 256, 256))
④改进模型的训练和优化器的选择
In [12]
#优化器选择class SaveBestModel(paddle.callbacks.Callback):
def __init__(self, target=0.5, path='work/best_model', verbose=0):
self.target = target
self.epoch = None
self.path = path def on_epoch_end(self, epoch, logs=None):
self.epoch = epoch def on_eval_end(self, logs=None):
if logs.get('acc') > self.target:
self.target = logs.get('acc')
self.model.save(self.path) print('best acc is {} at epoch {}'.format(self.target, self.epoch))
callback_visualdl = paddle.callbacks.VisualDL(log_dir='work/SRM_Inception_Net')
callback_savebestmodel = SaveBestModel(target=0.5, path='work/best_model')
callbacks = [callback_visualdl, callback_savebestmodel]
base_lr = config_parameters['lr']
epochs = config_parameters['epochs']def make_optimizer(parameters=None):
momentum = 0.9
learning_rate= paddle.optimizer.lr.CosineAnnealingDecay(learning_rate=base_lr, T_max=epochs, verbose=False)
weight_decay=paddle.regularizer.L2Decay(0.0002)
optimizer = paddle.optimizer.Momentum(
learning_rate=learning_rate,
momentum=momentum,
weight_decay=weight_decay,
parameters=parameters) return optimizer
optimizer = make_optimizer(model.parameters())
In [13]
model.prepare(optimizer,
paddle.nn.CrossEntropyLoss(),
paddle.metric.Accuracy())
In [14]
model.fit(train_loader,
eval_loader,
epochs=100,
batch_size=1, # 是否打乱样本集
callbacks=callbacks,
verbose=1) # 日志展示格式
⑤模型训练效果展示
绿色曲线为本次改进模型训练曲线,在增加了SRM模块的注意力机制后,性能和其他经典网络有了较大幅度的提升,但相较于SA注意力机制还稍差些。
以上就是改进的注意力多尺度特征融合卷积神经网络的详细内容,更多请关注启程网【www.vszh.cn】AI 人工智能栏目。
如非特殊说明,本站内容均来自于网友自主分享,如有任何问题均请联系我们进行处理!
猜您喜欢
IBM推出全球首个亚1nm芯片技术 从纳米时代推向原子尺度
2026-06-27IBM宣布带来一项重大半导体突破,推出全球首个亚1nm芯片技术,采用了0.7nm的革命性晶体管架构。半导体在计算、家电、通信设备、交通系统和关键基础设施等各个领域都扮演着关键角色,这一成就标志着一个里程碑时刻,特别是行业面临传统芯片缩放的物理极限。 这次IBM将近1000亿个晶体管集成在指甲盖大小的亚1nm芯片上,晶体管密度接近IBM在2021年发布的2nm芯片的两倍。其中得益于一系列结构和材料的...
周淑怡直播玩影游《男神模拟器》 尺度太大了
2026-05-21近日主播周淑怡游玩影游《男神模拟器》,游戏中出现美女被调戏,一言不合脱衣擦背的场景。这让周姐说话都结巴了,小表情也很亮眼。一起来看看视频吧! 视频欣赏: 恋爱模拟游戏《男神模拟器》已在Steam上发售。游戏以时空穿越为核心,玩家将扮演一位穿越回学生时代的青年,在网红“江南第一深情”童锦程的“僚机”辅助下,与七位不同人生阶段的女孩展开纠葛。
现代二游尺度越来越大 《恶魔高校》插画师自愧不如!
2026-04-20曾为经典系列《恶魔高校D×D》打造女主角形象的日本知名画师みやま零,近日分享了一个令人意外的观察。过去十年间,动画角色设计中的露骨尺度已发生翻天覆地的变化,昔日被视为性感代名词的招牌角色莉雅丝·吉蒙里,若与《碧蓝航线》这类抽卡手游中的女角相比,如今竟显得近乎保守。 据美山零所述,夸张的身材比例、少得可怜的布料与极致挑逗的姿态,这种过去被视作特殊"粉丝福利"的手法,如今已彻底成为该类型作品的主流标配...
《光环:战役进化》评级出炉 更成人化突破系列尺度
2026-04-08据澳大利亚分级委员会官网显示,光环系列新作《光环:战役进化》已获得MA15+评级。这是近年来主线《光环》游戏中评级最高的一次,引发了玩家和业界的广泛关注。 根据分级信息,该游戏的消费者建议内容为“强烈的主题与暴力,以及在线互动与聊天”。MA15+评级意味着该游戏被认为适合15岁及以上玩家,更年轻的观众需在成人陪同下游玩。 作为对比,此前推出的《光环:无限》与《光环5:守护者》均获得了比MA15+低...
《幻兽♡帕鲁》恋爱模拟游戏 官方明确尺度无成人内容
2026-04-02继公布全新宣传片后,《幻兽帕鲁》衍生恋爱模拟游戏《幻兽♡帕鲁!~已经不想只做朋友了~》再次引发热议。针对玩家关于游戏是否包含成人内容的猜测,Pocketpair发行与传播负责人John“Bucky”Buckley在官方频道上明确回应:这绝非18禁游戏。 《幻兽♡帕鲁!~已经不想只做朋友了~》最初只是一个愚人节玩笑,但如今已成为正式项目。游戏将舞台设定在“私立帕尔帕戈斯学院”,玩家将扮演一名高中生,...
Edge浏览器大声朗读怎么解决发音生硬_Edge浏览器神经网络语音强制
2026-03-28应启用Edge神经网络语音并安装对应语音包,具体包括:一、在edge://settings/accessibility中选择带(Neural)后缀的语音;二、通过Windows语言选项下载Neural语音;三、通过启动参数强制指定语音ID;四、清理注册表中非Neural语音项。 如果您在使用Edge浏览器的“大声朗读”功能时发现语音发音生硬、缺乏自然语调,可能是由于当前语音引擎未启用神经网络语音(...
PS怎么把模糊不清的图片变高清锐化 PS高反差保留与AI神经网络滤镜无损放大技巧
2026-03-26解决图片模糊问题需分五步:一用高反差保留+叠加强化边缘;二用CameraRaw协同增强纹理与锐化;三用神经滤镜超分辨率2倍无损放大;四用StartAI插件本地AI一键修复;五用嗨格式离线AI放大至200%。 如果您尝试提升一张模糊不清的图片的清晰度与细节表现力,但仅靠基础调色无法恢复结构信息,则问题往往源于图像高频细节丢失或像素级纹理退化。以下是解决此问题的步骤: 本文运行环境:MacBookPr...
尺度拉满!美少女塔防肉鸽《御炮少女》推出拍照模式
2026-03-19今日,塔防射击rougelite游戏《御炮少女》为玩家带来免费更新,新增了玩家期待已久的高自由度拍照模式。 本作讲述了行星异形中和与战术攻击部队的少女独自站在地球存续的最前线,抵抗神秘的外星侵略者的故事。此次更新通过加入专门的拍照模式,让玩家可以用不同背景、画面特效和服装选项,拍摄摆出各种姿势的少女,以此充分展现角色的魅力。 本作在游戏过程中,少女的服装会因激烈的战斗而逐渐破损但在本次更新后,玩家...
