From 16984f5f76c76d5522cf2102c975c5937c2a98f4 Mon Sep 17 00:00:00 2001
From: wqz960 <362379625@qq.com>
Date: Wed, 22 Jul 2020 04:20:55 +0000
Subject: [PATCH] add ResNeSt
---
configs/ResNeSt/ResNeSt50.yaml | 78 ++++
docs/zh_CN/models/ResNeSt_RegNet.md | 14 +
ppcls/modeling/architectures/resnest.py | 453 ++++++++++++++++++++++++
3 files changed, 545 insertions(+)
create mode 100644 configs/ResNeSt/ResNeSt50.yaml
create mode 100644 docs/zh_CN/models/ResNeSt_RegNet.md
create mode 100644 ppcls/modeling/architectures/resnest.py
diff --git a/configs/ResNeSt/ResNeSt50.yaml b/configs/ResNeSt/ResNeSt50.yaml
new file mode 100644
index 00000000..01e056da
--- /dev/null
+++ b/configs/ResNeSt/ResNeSt50.yaml
@@ -0,0 +1,78 @@
+mode: 'train'
+ARCHITECTURE:
+ name: 'ResNeSt50'
+
+pretrained_model: ""
+model_save_dir: "./output/"
+classes_num: 1000
+total_images: 1281167
+save_interval: 1
+validate: True
+valid_interval: 1
+epochs: 300
+topk: 5
+image_shape: [3, 224, 224]
+
+use_mix: True
+ls_epsilon: 0.1
+
+LEARNING_RATE:
+ function: 'CosineWarmup'
+ params:
+ lr: 0.1
+
+OPTIMIZER:
+ function: 'Momentum'
+ params:
+ momentum: 0.9
+ regularizer:
+ function: 'L2'
+ factor: 0.000070
+
+TRAIN:
+ batch_size: 256
+ num_workers: 4
+ file_list: "./dataset/ILSVRC2012/train_list.txt"
+ data_dir: "./dataset/ILSVRC2012/"
+ shuffle_seed: 0
+ transforms:
+ - DecodeImage:
+ to_rgb: True
+ to_np: False
+ channel_first: False
+ - RandCropImage:
+ size: 224
+ - RandFlipImage:
+ flip_code: 1
+ - AutoAugment:
+ - NormalizeImage:
+ scale: 1./255.
+ mean: [0.485, 0.456, 0.406]
+ std: [0.229, 0.224, 0.225]
+ order: ''
+ - ToCHWImage:
+ mix:
+ - CutmixOperator:
+ alpha: 0.2
+
+VALID:
+ batch_size: 64
+ num_workers: 4
+ file_list: "./dataset/ILSVRC2012/val_list.txt"
+ data_dir: "./dataset/ILSVRC2012/"
+ shuffle_seed: 0
+ transforms:
+ - DecodeImage:
+ to_rgb: True
+ to_np: False
+ channel_first: False
+ - ResizeImage:
+ resize_short: 256
+ - CropImage:
+ size: 224
+ - NormalizeImage:
+ scale: 1.0/255.0
+ mean: [0.485, 0.456, 0.406]
+ std: [0.229, 0.224, 0.225]
+ order: ''
+ - ToCHWImage:
diff --git a/docs/zh_CN/models/ResNeSt_RegNet.md b/docs/zh_CN/models/ResNeSt_RegNet.md
new file mode 100644
index 00000000..2b12d739
--- /dev/null
+++ b/docs/zh_CN/models/ResNeSt_RegNet.md
@@ -0,0 +1,14 @@
+# ResNeSt以及RegNet网络
+
+## 概述
+
+ResNeSt系列模型是在2020年提出的,在原有的resnet网络结构上做了改进,通过引入K个Group和在不同Group中加入类似于SEBlock的attention模块,使得精度相比于基础模型ResNet有了大幅度的提高,且参数量和flops与基础的ResNet基本保持一致。
+
+
+## 精度、FLOPS和参数量
+
+| Models | Top1 | Top5 | Reference
top1 | Reference
top5 | FLOPS
(G) | Parameters
(M) |
+|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
+| ResNeSt50 | 0.8102 | 0.9542| 0.8113 | -|5.39 | 27.5 |
+
+
diff --git a/ppcls/modeling/architectures/resnest.py b/ppcls/modeling/architectures/resnest.py
new file mode 100644
index 00000000..d8153782
--- /dev/null
+++ b/ppcls/modeling/architectures/resnest.py
@@ -0,0 +1,453 @@
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import paddle.fluid as fluid
+from paddle.fluid.initializer import MSRA, ConstantInitializer
+from paddle.fluid.param_attr import ParamAttr
+from paddle.fluid.regularizer import L2DecayRegularizer
+import math
+from paddle.fluid.contrib.model_stat import summary
+
+__all__ = ['ResNeSt50', 'ResNeSt101', 'ResNeSt200', 'ResNeSt269',
+ 'ResNeSt50_fast_1s1x64d', 'ResNeSt50_fast_2s1x64d',
+ 'ResNeSt50_fast_4s1x64d', 'ResNeSt50_fast_1s2x40d',
+ 'ResNeSt50_fast_2s2x40d', 'ResNeSt50_fast_2s2x40d',
+ 'ResNeSt50_fast_4s2x40d', 'ResNeSt50_fast_1s4x24d']
+
+class ResNeSt():
+ def __init__(self, layers, radix=1, groups=1, bottleneck_width=64, dilated=False,
+ dilation=1, deep_stem=False, stem_width=64, avg_down=False,
+ rectify_avg=False, avd=False, avd_first=False, final_drop=0.0,
+ last_gamma=False, bn_decay=0.0):
+ self.cardinality = groups
+ self.bottleneck_width = bottleneck_width
+ # ResNet-D params
+ self.inplanes = stem_width*2 if deep_stem else 64
+ self.avg_down = avg_down
+ self.last_gamma = last_gamma
+ # ResNeSt params
+ self.radix = radix
+ self.avd = avd
+ self.avd_first = avd_first
+
+ self.deep_stem = deep_stem
+ self.stem_width = stem_width
+ self.layers = layers
+ self.final_drop = final_drop
+ self.dilated = dilated
+ self.dilation = dilation
+ self.bn_decay = 0.0 # bn_decay
+
+ self.rectify_avg = rectify_avg
+
+ def net(self, input, class_dim=1000):
+ if self.deep_stem:
+ x = self.conv_bn_layer(x=input,
+ num_filters=self.stem_width,
+ filters_size=3,
+ stride=2,
+ groups=1,
+ act="relu",
+ name="conv1")
+ x = self.conv_bn_layer(x=x,
+ num_filters=self.stem_width,
+ filters_size=3,
+ stride=1,
+ groups=1,
+ act="relu",
+ name="conv2")
+ x = self.conv_bn_layer(x=x,
+ num_filters=self.stem_width*2,
+ filters_size=3,
+ stride=1,
+ groups=1,
+ act="relu",
+ name="conv3")
+ else:
+ x = self.conv_bn_layer(x=input,
+ num_filters=64,
+ filters_size=7,
+ stride=2,
+ act="relu",
+ name="conv1")
+
+ x = fluid.layers.pool2d(input=x,
+ pool_size=3,
+ pool_type="max",
+ pool_stride=2,
+ pool_padding=1)
+
+ x = self.resnest_layer(x=x,
+ planes=64,
+ blocks=self.layers[0],
+ is_first=False,
+ name="layer1")
+ x = self.resnest_layer(x=x,
+ planes=128,
+ blocks=self.layers[1],
+ stride=2,
+ name="layer2")
+ if self.dilated or self.dilation==4:
+ x = self.resnest_layer(x=x,
+ planes=256,
+ blocks=self.layers[2],
+ stride=1,
+ dilation=2,
+ name="layer3")
+ x = self.resnest_layer(x=x,
+ planes=512,
+ blocks=self.layers[3],
+ stride=1,
+ dilation=4,
+ name="layer4")
+ elif self.dilation==2:
+ x = self.resnest_layer(x=x,
+ planes=256,
+ blocks=self.layers[2],
+ stride=2,
+ dilation=1,
+ name="layer3")
+ x = self.resnest_layer(x=x,
+ planes=512,
+ blocks=self.layers[3],
+ stride=1,
+ dilation=2,
+ name="layer4")
+ else:
+ x = self.resnest_layer(x=x,
+ planes=256,
+ blocks=self.layers[2],
+ stride=2,
+ name="layer3")
+ x = self.resnest_layer(x=x,
+ planes=512,
+ blocks=self.layers[3],
+ stride=2,
+ name="layer4")
+ x = fluid.layers.pool2d(input=x, pool_type="avg", global_pooling=True, name="global_avg")
+ x = fluid.layers.dropout(x=x, dropout_prob=self.final_drop, name="final_drop")
+ stdv=1.0/math.sqrt(x.shape[1]*1.0)
+ x = fluid.layers.fc(input=x, size=class_dim,
+ param_attr=fluid.param_attr.ParamAttr(name="fc_weights",
+ initializer=fluid.initializer.Uniform(-stdv, stdv)),
+ bias_attr=ParamAttr(name="fc_offset"))
+ return x
+
+ def conv_bn_layer(self,
+ x,
+ num_filters,
+ filters_size,
+ stride=1,
+ groups=1,
+ act=None,
+ name=None):
+ x = fluid.layers.conv2d(input=x,
+ num_filters=num_filters,
+ filter_size=filters_size,
+ stride=stride,
+ padding=(filters_size-1)//2,
+ groups=groups,
+ act=None,
+ param_attr=ParamAttr(initializer=MSRA(), name=name+"_weight"),
+ bias_attr=False)
+ x = fluid.layers.batch_norm(input=x,
+ act=act,
+ param_attr=ParamAttr(name=name+"_scale",
+ regularizer=L2DecayRegularizer(regularization_coeff=self.bn_decay)),
+ bias_attr=ParamAttr(name=name+"_offset",
+ regularizer=L2DecayRegularizer(regularization_coeff=self.bn_decay)),
+ moving_mean_name=name+"_mean",
+ moving_variance_name=name+"_variance")
+ return x
+
+ def rsoftmax(self, x, radix, cardinality, name=None):
+ batch, r, h, w = x.shape
+ if radix > 1:
+ x = fluid.layers.reshape(x=x,
+ shape=[0, cardinality, radix, int(r*h*w/cardinality/radix)])
+ x = fluid.layers.transpose(x=x, perm=[0, 2, 1, 3])
+ x = fluid.layers.softmax(input=x, axis=1)
+ x = fluid.layers.reshape(x=x,
+ shape=[0, r*h*w])
+ else:
+ x = fluid.layers.sigmoid(x=x)
+ return x
+
+ def splat_conv(self, x, in_channels, channels, kernel_size, stride=1, padding=0,
+ dilation=1, groups=1, bias=True, radix=2, reduction_factor=4,
+ rectify_avg=False, name=None):
+ x = self.conv_bn_layer(x=x,
+ num_filters=channels*radix,
+ filters_size=kernel_size,
+ stride=stride,
+ groups=groups*radix,
+ act="relu",
+ name=name+"_splat1")
+
+ batch, rchannel = x.shape[:2]
+ if radix>1:
+ splited = fluid.layers.split(input=x, num_or_sections=radix, dim=1)
+ gap = fluid.layers.sum(x=splited)
+ else:
+ gap = x
+ gap = fluid.layers.pool2d(input=gap, pool_type="avg", global_pooling=True)
+ inter_channels = int(max(in_channels*radix//reduction_factor, 32))
+ gap = self.conv_bn_layer(x=gap,
+ num_filters=inter_channels,
+ filters_size=1,
+ groups=groups,
+ act="relu",
+ name=name+"_splat2")
+
+ atten = fluid.layers.conv2d(input=gap,
+ num_filters=channels*radix,
+ filter_size=1,
+ stride=1,
+ padding=0,
+ groups=groups,
+ act=None,
+ param_attr=ParamAttr(name=name+"_splat_weights", initializer=MSRA()),
+ bias_attr=False)
+ atten = self.rsoftmax(x=atten,
+ radix=radix,
+ cardinality=groups,
+ name=name+"_rsoftmax")
+ atten = fluid.layers.reshape(x=atten, shape=[-1, atten.shape[1], 1, 1])
+
+ if radix > 1:
+ attens = fluid.layers.split(input=atten, num_or_sections=radix, dim=1)
+ out = fluid.layers.sum([fluid.layers.elementwise_mul(x=att, y=split) for (att, split) in zip(attens, splited)])
+ else:
+ out = fluid.layers.elementwise_mul(atten, x)
+ return out
+
+
+ def bottleneck(self, x, inplanes, planes, stride=1, radix=1, cardinality=1, bottleneck_width=64,
+ avd=False, avd_first=False, dilation=1, is_first=False, rectify_avg=False,
+ last_gamma=False, name=None):
+
+ short = x
+
+ group_width = int(planes * (bottleneck_width/64.)) * cardinality
+ x = self.conv_bn_layer(x=x,
+ num_filters=group_width,
+ filters_size=1,
+ stride=1,
+ groups=1,
+ act="relu",
+ name=name+"_conv1")
+ if avd and avd_first and (stride>1 or is_first):
+ x = fluid.layers.pool2d(input=x,
+ pool_size=3,
+ pool_type="avg",
+ pool_stride=stride,
+ pool_padding=1)
+ if radix >= 1:
+ x = self.splat_conv(x=x,
+ in_channels=group_width,
+ channels=group_width,
+ kernel_size=3,
+ stride=1,
+ padding=dilation,
+ dilation=dilation,
+ groups=cardinality,
+ bias=False,
+ radix=radix,
+ rectify_avg=rectify_avg,
+ name=name+"_splatconv")
+ else:
+ x = self.conv_bn_layer(x=x,
+ num_filters=group_width,
+ filters_size=3,
+ stride=1,
+ padding=dilation,
+ dilation=dialtion,
+ groups=cardinality,
+ act="relu",
+ name=name+"_conv2")
+
+ if avd and avd_first==False and (stride>1 or is_first):
+ x = fluid.layers.pool2d(input=x,
+ pool_size=3,
+ pool_type="avg",
+ pool_stride=stride,
+ pool_padding=1)
+ x = self.conv_bn_layer(x=x,
+ num_filters=planes*4,
+ filters_size=1,
+ stride=1,
+ groups=1,
+ act=None,
+ name=name+"_conv3")
+
+ if stride!=1 or self.inplanes != planes * 4:
+ if self.avg_down:
+ if dilation==1:
+ short = fluid.layers.pool2d(input=short,
+ pool_size=stride,
+ pool_type="avg",
+ pool_stride=stride,
+ ceil_mode=True)
+ else:
+ short = fluid.layers.pool2d(input=short,
+ pool_size=1,
+ pool_type="avg",
+ pool_stride=1,
+ ceil_mode=True)
+ short = fluid.layers.conv2d(input=short,
+ num_filters=planes*4,
+ filter_size=1,
+ stride=1,
+ padding=0,
+ groups=1,
+ act=None,
+ param_attr=ParamAttr(name=name+"_weights", initializer=MSRA()),
+ bias_attr=False)
+ else:
+ short = fluid.layers.conv2d(input=short,
+ num_filters=planes*4,
+ filter_size=1,
+ stride=stride,
+ param_attr=ParamAttr(name=name+"_shortcut_weights", initializer=MSRA()),
+ bias_attr=False)
+
+ short = fluid.layers.batch_norm(input=short,
+ act=None,
+ param_attr=ParamAttr(name=name+"_shortcut_scale",
+ regularizer=L2DecayRegularizer(regularization_coeff=self.bn_decay)),
+ bias_attr=ParamAttr(name=name+"_shortcut_offset",
+ regularizer=L2DecayRegularizer(regularization_coeff=self.bn_decay)),
+ moving_mean_name=name+"_shortcut_mean",
+ moving_variance_name=name+"_shortcut_variance")
+
+ return fluid.layers.elementwise_add(x=short, y=x, act="relu")
+
+ def resnest_layer(self,
+ x,
+ planes,
+ blocks,
+ stride=1,
+ dilation=1,
+ is_first=True,
+ name=None):
+ if dilation==1 or dilation==2:
+ x = self.bottleneck(x=x,
+ inplanes=self.inplanes,
+ planes=planes,
+ stride=stride,
+ radix=self.radix,
+ cardinality=self.cardinality,
+ bottleneck_width=self.bottleneck_width,
+ avd=self.avd,
+ avd_first=self.avd_first,
+ dilation=1,
+ is_first=is_first,
+ rectify_avg=self.rectify_avg,
+ last_gamma=self.last_gamma,
+ name=name+"_bottleneck_0")
+ elif dilation==4:
+ x = self.bottleneck(x=x,
+ inplanes=self.inplanes,
+ planes=planes,
+ stride=stride,
+ radix=self.radix,
+ cardinality=self.cardinality,
+ bottleneck_width=self.bottleneck_width,
+ avd=self.avd,
+ avd_first=self.avd_first,
+ dilation=2,
+ is_first=is_first,
+ rectify_avg=self.rectify_avg,
+ last_gamma=self.last_gamma,
+ name=name+"_bottleneck_0")
+ else:
+ raise RuntimeError("=>unknown dilation size")
+
+ self.inplanes = planes*4
+ for i in range(1, blocks):
+ name = name+"_bottleneck_"+str(i)
+ x = self.bottleneck(x=x,
+ inplanes=self.inplanes,
+ planes=planes,
+ radix=self.radix,
+ cardinality=self.cardinality,
+ bottleneck_width=self.bottleneck_width,
+ avd=self.avd,
+ avd_first=self.avd_first,
+ dilation=dilation,
+ rectify_avg=self.rectify_avg,
+ last_gamma=self.last_gamma,
+ name=name)
+ return x
+
+
+def ResNeSt50(**args):
+ model = ResNeSt(layers=[3,4,6,3], radix=2, groups=1, bottleneck_width=64,
+ deep_stem=True, stem_width=32, avg_down=True,
+ avd=True, avd_first=False, final_drop=0.0, **args)
+ return model
+
+
+def ResNeSt101(**args):
+ model = ResNeSt(layers=[3,4,23,3], radix=2, groups=1, bottleneck_width=64,
+ deep_stem=True, stem_width=64, avg_down=True,
+ avd=True, avd_first=False, final_drop=0.0, **args)
+ return model
+
+
+def ResNeSt200(**args):
+ model = ResNeSt(layers=[3,24,36,3], radix=2, groups=1, bottleneck_width=64,
+ deep_stem=True, stem_width=64, avg_down=True,
+ avd=True, avd_first=False, final_drop=0.2, **args)
+ return model
+
+def ResNeSt269(**args):
+ model = ResNeSt(layers=[3,30,48,8], radix=2, groups=1, bottleneck_width=64,
+ deep_stem=True, stem_width=64, avg_down=True,
+ avd=True, avd_first=False, final_drop=0.2, **args)
+ return model
+
+def ResNeSt50_fast_1s1x64d(**args):
+ model = ResNeSt(layers=[3,4,6,3], radix=1, groups=1, bottleneck_width=64,
+ deep_stem=True, stem_width=32, avg_down=True,
+ avd=True, avd_first=True, final_drop=0.0, **args)
+ return model
+
+def ResNeSt50_fast_2s1x64d(**args):
+ model = ResNeSt(layers=[3,4,6,3], radix=2, groups=1, bottleneck_width=64,
+ deep_stem=True, stem_width=32, avg_down=True,
+ avd=True, avd_first=True, final_drop=0.0, **args)
+ return model
+
+def ResNeSt50_fast_4s1x64d(**args):
+ model = ResNeSt(layers=[3,4,6,3], radix=2, groups=1, bottleneck_width=64,
+ deep_stem=True, stem_width=32, avg_down=True,
+ avd=True, avd_first=True, final_drop=0.0, **args)
+ return model
+
+def ResNeSt50_fast_1s2x40d(**args):
+ model = ResNeSt(layers=[3,4,6,3], radix=1, groups=2, bottleneck_width=40,
+ deep_stem=True, stem_width=32, avg_down=True,
+ avd=True, avd_first=True, final_drop=0.0, **args)
+ return model
+
+def ResNeSt50_fast_2s2x40d(**args):
+ model = ResNeSt(layers=[3,4,6,3], radix=2, groups=2, bottleneck_width=40,
+ deep_stem=True, stem_width=32, avg_down=True,
+ avd=True, avd_first=True, final_drop=0.0, **args)
+ return model
+
+def ResNeSt50_fast_4s2x40d(**args):
+ model = ResNeSt(layers=[3,4,6,3], radix=4, groups=2, bottleneck_width=40,
+ deep_stem=True, stem_width=32, avg_down=True,
+ avd=True, avd_first=True, final_drop=0.0, **args)
+ return model
+
+def ResNeSt50_fast_1s4x24d(**args):
+ model = ResNeSt(layers=[3,4,6,3], radix=1, groups=4, bottleneck_width=24,
+ deep_stem=True, stem_width=32, avg_down=True,
+ avd=True, avd_first=True, final_drop=0.0, **args)
+ return model
+
+
--
GitLab