159
社区成员
GENet_Res50是一个基于GEBlock构建于ResNet50之上的卷积神经网络,可以将ImageNet图像分成1000个目标类,准确率达78.47%。
在Resnet50的基础上,作者改进了原本的残差结构,设计了两个操作:gather和excite。gather从局部空间位置上提取特征,excite将gather提取到的特征还原回原来的尺度。这个过程类似于encoder-decoder模型,称为编码-解码模型。这个GEnet跟SEnet一样可以嵌入到任何卷积网络中,以很小的参数和计算复杂度为代价能够提升网络的性能,主要应用在目标检测中。
https://arxiv.org/abs/1810.12348
昇腾处理器(Ascend)
框架:MindSpore
参考资源:
从经验来看,当增加网络层数后,网络可以进行更加复杂的特征模式的提取,所以当模型更深时理论上可以取得更好的结果。但是更深的网络其性能一定会更好吗?实验发现深度网络出现了退化问题:网络深度增加时,网络准确度出现饱和,甚至出现下降。
何凯明博士提出了残差学习来解决退化问题。对于一个堆积层结构,当输入为x时其学习到的特征记为 H(x) ,现在我们希望可以学习到残差F(x)=H(x)-x,这样其实原始的学习特征是F(x)+x。之所以这样是因为残差学习相比原始特征直接学习更容易。当残差为0时,此时堆积层仅仅做了恒等映射,至少网络性能不会下降,实际上残差不会为0,这也会使得堆积层在输入特征基础上学习到新的特征,从而拥有更好的性能。
对于CNN网络来说,其核心计算是卷积算子,其通过卷积核从输入特征图学习到新特征图。从本质上讲,卷积是对一个局部区域进行特征融合,这包括空间上(H和W维度)以及通道间(C维度)的特征融合。对于卷积操作,很大一部分工作是提高感受野,即空间上融合更多特征融合,或者是提取多尺度空间信息。SENet网络的创新点在于关注channel之间的关系,希望模型可以自动学习到不同channel特征的重要程度。为此,SENet提出了Squeeze-and-Excitation (SE)模块。
Squeeze-and-Excitation:又称为特征重标定卷积,或者注意力机制。具体来说,就是通过学习的方式来自动获取到每个特征通道的重要程度,然后依照这个重要程度去提升有用的特征并抑制对当前用处不大的特征。
SE模块很容易嵌入到其它网络中,在其它网络如ResNet和VGG中引入SE模块,都能提升网络的效果。SE模块主要为了提升模型对channel特征的敏感性,这个模块是轻量级的,而且可以应用在现有的网络结构中,只需要增加较少的计算量就可以带来性能的提升。
GENet其实是SENet的改进版,以便于更好地利用特征图空间上下文信息。
Global-Avg-Pooling的方式已被SENet证明是有效的方式,且一系列Bag-of-Visual-words模型也表明:用汇集局部区域所得的局部描述子,来组建成新的表示,这种方法是有效的。 故基于SENet,GENet针对如何从特征图中提取出好的feature context,再用于特征图间重要程度的调控进行了研究。
GENet定义了Gather算子和Excite算子,如图所示:
代码里涉及的pytorch与mindspore算子的对应关系如下:
pytorch | mindspore | 功能 |
---|---|---|
torch.nn | mindspore.nn | 构建神经网络的预定义构建块或计算单元 |
torch.nn.Conv2d; torch.nn.BatchNorm2d ;torch.nn.ReLU | mindspore.nn.Conv2dBnAct | 二维卷积层+归一化函数+ReLU激活函数 |
torch.nn.Conv2d | mindspore.nn.Conv2d | 二维卷积层 |
torch.nn.BatchNorm2d | mindspore.nn.BatchNorm2d | 归一化函数 |
torch.nn.ModuleList | mindspore.nn.CellList | 将Cell保存在List中 |
torch.nn.Sequential | mindspore.nn.SequentialCell | Cell的List将按照它们在构造函数中传递的顺序添加到里面 |
torch.nn.AdaptiveAvgPool2d(1,1) | mindspore.ops.ReduceMean | 平均池化 |
torch.nn.ReLU | mindspore.nn.ReLU | ReLU激活函数 |
torch.nn.Sigmoid | nn.Sigmoid | sigmoid激活函数 |
torch.add | mindspore.ops.Add | 将两个输入张量相加 |
torch.mul | mindspore.ops.Mul | 将两个输入张量相乘 |
torch.nn.Flatten | mindspore.nn.Flatten | 展平张量 |
torch.nn.functional.one_hot | mindspore.ops.OneHot | 把tensor转为one-hot向量形式 |
nn.CrossEntropyLoss | nn.SoftmaxCrossEntropyWithLogits | 交叉熵损失 |
torch.cuda.set_device | mindspore.context.set_context | 设置运行环境的context |
torch.nn.Module.load_state_dict | mindspore.load_checkpoint mindspore.load_param_into_net | 加载模型权重 |
torch.onnx.export;torch.jit.export | mindspore.export | 导出对应的模型文件:pytorch主要是jit和onnx文件;mindspore主要是导出AIR, MINDIR,ONNX文件 |
torch.optim.SGD | mindspore.nn.Momentum | 梯度下降优化器 |
使用的数据集:imagenet 2012
数据集处理主要分为四个步骤:
初始化context
# init context
context.set_context(mode=context.GRAPH_MODE, device_target=target, save_graphs=False)
复制
是否分布式训练,若是,设置分布式训练的一些初始化的参数。
if run_distribute:
context.set_context(device_id=device_id,
enable_auto_mixed_precision=True)
context.set_auto_parallel_context(device_num=device_num, parallel_mode=ParallelMode.DATA_PARALLEL,
gradients_mean=True)
set_algo_parameters(elementwise_op_strategy_follow=True)
context.set_auto_parallel_context(all_reduce_fusion_config=[85, 160])
init()
复制
创建数据集
dataset = create_dataset(dataset_path=local_train_data_url, do_train=True, repeat_num=1,
batch_size=config.batch_size, target=target, distribute=run_distribute)
step_size = dataset.get_dataset_size()
复制
定义网络
mlp = trans_char_to_bool(args_opt.mlp)
extra = trans_char_to_bool(args_opt.extra)
net = net(class_num=config.class_num, extra=extra, mlp=mlp)
复制
初始化权重
如果有预训练权重,直接加载checkpoint;若没有,且是卷积操作,则用HeKaiMing均匀算法初始化数组,并从均匀分布中采集样本;若没有,且是全连接操作,则初始化用截断正态分布,是有界正态分布的。
if args_opt.pre_trained:
param_dict = load_checkpoint(args_opt.pre_trained)
load_param_into_net(net, param_dict)
else:
for _, cell in net.cells_and_names():
if isinstance(cell, nn.Conv2d): cell.weight.set_data(weight_init.initializer(weight_init.HeUniform(), cell.weight.shape, cell.weight.dtype))
if isinstance(cell, nn.Dense):
cell.weight.set_data(weight_init.initializer(weight_init.TruncatedNormal(), cell.weight.shape, cell.weight.dtype))
lr = get_lr(config.lr_init, config.lr_end, config.epoch_size, step_size, config.decay_mode)
lr = Tensor(lr)
复制
定义优化器
采用的是Momentum优化器
decayed_params = []
no_decayed_params = []
for param in net.trainable_params():
if 'beta' not in param.name and 'gamma' not in param.name and 'bias' not in param.name:
decayed_params.append(param)
else:
no_decayed_params.append(param)
group_params = [{'params': decayed_params, 'weight_decay': config.weight_decay},
{'params': no_decayed_params},
{'order_params': net.trainable_params()}]
opt = Momentum(group_params, lr, config.momentum, loss_scale=config.loss_scale)
复制
定义loss和模型
网络采用的是交叉熵损失函数。
if target == "Ascend":
if not config.use_label_smooth:
config.label_smooth_factor = 0.0
loss = CrossEntropySmooth(sparse=True, reduction="mean",
smooth_factor=config.label_smooth_factor,
num_classes=config.class_num)
loss_scale = FixedLossScaleManager(config.loss_scale, drop_overflow_update=False)
model = Model(net, loss_fn=loss, optimizer=opt, loss_scale_manager=loss_scale,
metrics={'acc'}, amp_level="O2", keep_batchnorm_fp32=False)
else:
raise ValueError("Unsupported device target.")
复制
定义callbacks
time_cb = TimeMonitor(data_size=step_size)
loss_cb = LossMonitor()
rank_id = int(os.getenv("RANK_ID"))
cb = [time_cb, loss_cb]
if rank_id == 0:
config_ck = CheckpointConfig(save_checkpoint_steps=config.save_checkpoint_epochs*step_size,
keep_checkpoint_max=config.keep_checkpoint_max)
ckpt_cb = ModelCheckpoint(prefix="GENet", directory=ckpt_save_dir, config=config_ck)
cb += [ckpt_cb]
复制
进行训练
dataset_sink_mode = target != "CPU"
model.train(config.epoch_size, dataset, callbacks=cb,
sink_size=dataset.get_dataset_size(), dataset_sink_mode=dataset_sink_mode)
if device_id == 0 and args_opt.is_modelarts == "True":
mox.file.copy_parallel(ckpt_save_dir, args_opt.train_url)
复制
从pytorch迁移到mindspore中遇到了很多问题,感谢论坛中的各位老师的帮助。 印象较深的主要有一下两点:
AdaptivePooling与AvgPooling
mindspore.ops.AdaptiveAvgPool2D只支持GPU,所以要用mindspore.ops.AvgPool替换
AdaptivePooling与AvgPooling相互转换的时候卷积核和stride应该怎么设置
mindspore.ops.AdaptiveAvgPool2D(output_size)
mindspore.ops.AvgPool(kernel_size=1, strides=1, pad_mode="valid", data_format="NCHW")
需要用output_size换算kernel_size和stride
stride = floor(input_size/output_size) kernel_size = input_size − (output_size−1) * stride
在pynative模式下跑模型,会遇到The pointer[cnode] is null.的问题(跑到一半就报错)
该问题主要是因为在自定义数据集里面使用了Tensor计算
Tensor计算会调用底层的算子进行计算,但是数据处理是多线程并行处理
因此会起多个线程进行计算,但是计算当前不支持多线程执行,因此报错;
自定义数据集中的getitem中尽量不使用MindSpore的Tensor及相关操作,使用numpy