Pytoch mark-rnn実現詳細共有
Data Loader
Datasetが需要を満たすことができません。touch.utils.data.Datasetをカスタマイズする必要があります。init_,_getitem_,_len_,そうでないと、DataLoaderがカスタムDatasetを導入すると、上記の関数が足りなくなります。NotImplementendErrエラーが発生します。
Numpy放送機構:
すべての入力配列をその中のshapeの一番長い配列に揃えてください。shapeの中で足りない部分は全部前に1を追加して補完します。
出力配列のshapeは入力配列shapeの各軸の最大値です。
入力配列のある軸と出力配列の対応軸の長さが同じか、またはその長さが1の場合、この配列は計算に使用できます。そうでないとエラーが発生します。
入力配列の軸の長さが1の場合、この軸に沿って演算するときは、この軸の最初のセットの値を使います。
CUDAのpytouchにおける拡張:
toch.utils.ffiにcreateを使用します。extension拡張:
1 Resnet構造
Resnetは一般的に5つのボリューム(conv)層に分かれています。各階は一つのステージです。これらのブロックの数は、それぞれのstageの中で同じ数のブロックで構成されています。countは、最初のstageは他のいくつかのstageとは全く違っていても、単独のブロックから構成されていると見なしても良いので、ブロックが積み重ねられて構成される第二層から第5層まで(つまりstage 2-stage 5またはconv 2-conv 5)、それぞれindex 1-index 4と定義されています。図はress netの基本構造である。
以下のコードはブロックの数を制御することによって、異なるResenet(Reset 50などを含む)を構築します。
2.1 Bottleneck構造
Resnetでは、第一層巻の下層はブロックとして見られ、第二層から第五層まではそれぞれBottleneckと呼ばれるブロックが二層に積み重なっていると述べました。第一層はstemブロックとして見られます。この中のBottleneckの構造は以下の通りである。
markrcnn benchmarkで上記の構造を構築するコードは以下の通りです。
Resnetの最初の層はStem構造と見なされます。その構造のコードは以下の通りです。
maskrcenn benchmarkでは、上記の2つのblock構造のデリバティブとパッケージ、BottleneckとStemは、それぞれBatch NormalzationとGroup Normalizationのパッケージクラスを派生しています。
3.1 Resnet構造
以上の基礎の上で、私達は以上の構造の上で更に本当のResnetを構築することができます。
ヘッドは、私が理解している限り、ある機能のネットワーク構造を完成させることです。Resnet headとは、Bottleneckブロックを使って異なるResnetを構成する機能ネットワーク構造を積み重ねることです。内部構造が似ていて、ある機能を完成させることです。ここではあまり紹介しません。上のResnetの構造ですから。
Datasetが需要を満たすことができません。touch.utils.data.Datasetをカスタマイズする必要があります。init_,_getitem_,_len_,そうでないと、DataLoaderがカスタムDatasetを導入すると、上記の関数が足りなくなります。NotImplementendErrエラーが発生します。
Numpy放送機構:
すべての入力配列をその中のshapeの一番長い配列に揃えてください。shapeの中で足りない部分は全部前に1を追加して補完します。
出力配列のshapeは入力配列shapeの各軸の最大値です。
入力配列のある軸と出力配列の対応軸の長さが同じか、またはその長さが1の場合、この配列は計算に使用できます。そうでないとエラーが発生します。
入力配列の軸の長さが1の場合、この軸に沿って演算するときは、この軸の最初のセットの値を使います。
CUDAのpytouchにおける拡張:
toch.utils.ffiにcreateを使用します。extension拡張:
def create_extension(name, headers, sources, verbose=True, with_cuda=False,
package=False, relative_to='.', **kwargs):
"""Creates and configures a cffi.FFI object, that builds PyTorch extension.
Arguments:
name (str): package name. Can be a nested module e.g. ``.ext.my_lib``.
headers (str or List[str]): list of headers, that contain only exported
functions
sources (List[str]): list of sources to compile.
verbose (bool, optional): if set to ``False``, no output will be printed
(default: True).
with_cuda (bool, optional): set to ``True`` to compile with CUDA headers
(default: False)
package (bool, optional): set to ``True`` to build in package mode (for modules
meant to be installed as pip packages) (default: False).
relative_to (str, optional): path of the build file. Required when
``package is True``. It's best to use ``__file__`` for this argument.
kwargs: additional arguments that are passed to ffi to declare the
extension. See `Extension API reference`_ for details.
.. _`Extension API reference`: https://docs.python.org/3/distutils/apiref.html#distutils.core.Extension
"""
base_path = os.path.abspath(os.path.dirname(relative_to))
name_suffix, target_dir = _create_module_dir(base_path, name)
if not package:
cffi_wrapper_name = '_' + name_suffix
else:
cffi_wrapper_name = (name.rpartition('.')[0] +
'.{0}._{0}'.format(name_suffix))
wrapper_source, include_dirs = _setup_wrapper(with_cuda)
include_dirs.extend(kwargs.pop('include_dirs', []))
if os.sys.platform == 'win32':
library_dirs = glob.glob(os.getenv('CUDA_PATH', '') + '/lib/x64')
library_dirs += glob.glob(os.getenv('NVTOOLSEXT_PATH', '') + '/lib/x64')
here = os.path.abspath(os.path.dirname(__file__))
lib_dir = os.path.join(here, '..', '..', 'lib')
library_dirs.append(os.path.join(lib_dir))
else:
library_dirs = []
library_dirs.extend(kwargs.pop('library_dirs', []))
if isinstance(headers, str):
headers = [headers]
all_headers_source = ''
for header in headers:
with open(os.path.join(base_path, header), 'r') as f:
all_headers_source += f.read() + '
'
ffi = cffi.FFI()
sources = [os.path.join(base_path, src) for src in sources]
# NB: TH headers are C99 now
kwargs['extra_compile_args'] = ['-std=c99'] + kwargs.get('extra_compile_args', [])
ffi.set_source(cffi_wrapper_name, wrapper_source + all_headers_source,
sources=sources,
include_dirs=include_dirs,
library_dirs=library_dirs, **kwargs)
ffi.cdef(_typedefs + all_headers_source)
_make_python_wrapper(name_suffix, '_' + name_suffix, target_dir)
def build():
_build_extension(ffi, cffi_wrapper_name, target_dir, verbose)
ffi.build = build
return ffi
補足知識:markrcenn-benchmarkコード詳細解のress net.py1 Resnet構造
Resnetは一般的に5つのボリューム(conv)層に分かれています。各階は一つのステージです。これらのブロックの数は、それぞれのstageの中で同じ数のブロックで構成されています。countは、最初のstageは他のいくつかのstageとは全く違っていても、単独のブロックから構成されていると見なしても良いので、ブロックが積み重ねられて構成される第二層から第5層まで(つまりstage 2-stage 5またはconv 2-conv 5)、それぞれindex 1-index 4と定義されています。図はress netの基本構造である。
以下のコードはブロックの数を制御することによって、異なるResenet(Reset 50などを含む)を構築します。
# -----------------------------------------------------------------------------
# Standard ResNet models
# -----------------------------------------------------------------------------
# ResNet-50 ( )
# ResNet 5 , , , index 。 block_count
ResNet50StagesTo5 = tuple(
StageSpec(index=i, block_count=c, return_features=r)
for (i, c, r) in ((1, 3, False), (2, 4, False), (3, 6, False), (4, 3, True))
)
# ResNet-50 up to stage 4 (excludes stage 5)
ResNet50StagesTo4 = tuple(
StageSpec(index=i, block_count=c, return_features=r)
for (i, c, r) in ((1, 3, False), (2, 4, False), (3, 6, True))
)
# ResNet-101 (including all stages)
ResNet101StagesTo5 = tuple(
StageSpec(index=i, block_count=c, return_features=r)
for (i, c, r) in ((1, 3, False), (2, 4, False), (3, 23, False), (4, 3, True))
)
# ResNet-101 up to stage 4 (excludes stage 5)
ResNet101StagesTo4 = tuple(
StageSpec(index=i, block_count=c, return_features=r)
for (i, c, r) in ((1, 3, False), (2, 4, False), (3, 23, True))
)
# ResNet-50-FPN (including all stages)
ResNet50FPNStagesTo5 = tuple(
StageSpec(index=i, block_count=c, return_features=r)
for (i, c, r) in ((1, 3, True), (2, 4, True), (3, 6, True), (4, 3, True))
)
# ResNet-101-FPN (including all stages)
ResNet101FPNStagesTo5 = tuple(
StageSpec(index=i, block_count=c, return_features=r)
for (i, c, r) in ((1, 3, True), (2, 4, True), (3, 23, True), (4, 3, True))
)
# ResNet-152-FPN (including all stages)
ResNet152FPNStagesTo5 = tuple(
StageSpec(index=i, block_count=c, return_features=r)
for (i, c, r) in ((1, 3, True), (2, 8, True), (3, 36, True), (4, 3, True))
)
以上のような組み合わせによって、markrcnn benchmarkは異なるbackboneを構築できます。
def _make_stage(
transformation_module,
in_channels,
bottleneck_channels,
out_channels,
block_count,
num_groups,
stride_in_1x1,
first_stride,
dilation=1,
dcn_config={}
):
blocks = []
stride = first_stride
# ,
for _ in range(block_count):
blocks.append(
transformation_module(
in_channels,
bottleneck_channels,
out_channels,
num_groups,
stride_in_1x1,
stride,
dilation=dilation,
dcn_config=dcn_config
)
)
stride = 1
in_channels = out_channels
return nn.Sequential(*blocks)
これらのいくつかの異なるbackboneはその後、同じオブジェクトに統合されて呼び出されやすくなります。
_STAGE_SPECS = Registry({
"R-50-C4": ResNet50StagesTo4,
"R-50-C5": ResNet50StagesTo5,
"R-101-C4": ResNet101StagesTo4,
"R-101-C5": ResNet101StagesTo5,
"R-50-FPN": ResNet50FPNStagesTo5,
"R-50-FPN-RETINANET": ResNet50FPNStagesTo5,
"R-101-FPN": ResNet101FPNStagesTo5,
"R-101-FPN-RETINANET": ResNet101FPNStagesTo5,
"R-152-FPN": ResNet152FPNStagesTo5,
})
2ブロック構造2.1 Bottleneck構造
Resnetでは、第一層巻の下層はブロックとして見られ、第二層から第五層まではそれぞれBottleneckと呼ばれるブロックが二層に積み重なっていると述べました。第一層はstemブロックとして見られます。この中のBottleneckの構造は以下の通りである。
markrcnn benchmarkで上記の構造を構築するコードは以下の通りです。
class Bottleneck(nn.Module):
def __init__(
self,
in_channels,
bottleneck_channels,
out_channels,
num_groups,
stride_in_1x1,
stride,
dilation,
norm_func,
dcn_config
):
super(Bottleneck, self).__init__()
#
self.downsample = None
if in_channels != out_channels:
# 1 ,
down_stride = stride if dilation == 1 else 1
self.downsample = nn.Sequential(
Conv2d(
in_channels, out_channels,
kernel_size=1, stride=down_stride, bias=False
),
norm_func(out_channels),
)
for modules in [self.downsample,]:
for l in modules.modules():
if isinstance(l, Conv2d):
nn.init.kaiming_uniform_(l.weight, a=1)
if dilation > 1:
stride = 1 # reset to be 1
# The original MSRA ResNet models have stride in the first 1x1 conv
# The subsequent fb.torch.resnet and Caffe2 ResNe[X]t implementations have
# stride in the 3x3 conv
#
stride_1x1, stride_3x3 = (stride, 1) if stride_in_1x1 else (1, stride)
# ,
# 1
self.conv1 = Conv2d(
in_channels,
bottleneck_channels,
kernel_size=1,
stride=stride_1x1,
bias=False,
)
self.bn1 = norm_func(bottleneck_channels)
# TODO: specify init for the above
with_dcn = dcn_config.get("stage_with_dcn", False)
if with_dcn:
# dcn
deformable_groups = dcn_config.get("deformable_groups", 1)
with_modulated_dcn = dcn_config.get("with_modulated_dcn", False)
self.conv2 = DFConv2d(
bottleneck_channels,
bottleneck_channels,
defrost=with_modulated_dcn,
kernel_size=3,
stride=stride_3x3,
groups=num_groups,
dilation=dilation,
deformable_groups=deformable_groups,
bias=False
)
else:
# 3
self.conv2 = Conv2d(
bottleneck_channels,
bottleneck_channels,
kernel_size=3,
stride=stride_3x3,
padding=dilation,
bias=False,
groups=num_groups,
dilation=dilation
)
nn.init.kaiming_uniform_(self.conv2.weight, a=1)
self.bn2 = norm_func(bottleneck_channels)
self.conv3 = Conv2d(
bottleneck_channels, out_channels, kernel_size=1, bias=False
)
self.bn3 = norm_func(out_channels)
for l in [self.conv1, self.conv3,]:
nn.init.kaiming_uniform_(l.weight, a=1)
def forward(self, x):
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = F.relu_(out)
out = self.conv2(out)
out = self.bn2(out)
out = F.relu_(out)
out0 = self.conv3(out)
out = self.bn3(out0)
if self.downsample is not None:
identity = self.downsample(x)
out += identity
out = F.relu_(out)
return out
2.2 Stem構造Resnetの最初の層はStem構造と見なされます。その構造のコードは以下の通りです。
class BaseStem(nn.Module):
def __init__(self, cfg, norm_func):
super(BaseStem, self).__init__()
# backbone ,
out_channels = cfg.MODEL.RESNETS.STEM_OUT_CHANNELS
# , , , Resnet
self.conv1 = Conv2d(
3, out_channels, kernel_size=7, stride=2, padding=3, bias=False
)
self.bn1 = norm_func(out_channels)
for l in [self.conv1,]:
nn.init.kaiming_uniform_(l.weight, a=1)
def forward(self, x):
x = self.conv1(x)
x = self.bn1(x)
x = F.relu_(x)
x = F.max_pool2d(x, kernel_size=3, stride=2, padding=1)
return x
2.3両構造のデリバティブとパッケージmaskrcenn benchmarkでは、上記の2つのblock構造のデリバティブとパッケージ、BottleneckとStemは、それぞれBatch NormalzationとGroup Normalizationのパッケージクラスを派生しています。
class BottleneckWithFixedBatchNorm(Bottleneck):
def __init__(
self,
in_channels,
bottleneck_channels,
out_channels,
num_groups=1,
stride_in_1x1=True,
stride=1,
dilation=1,
dcn_config={}
):
super(BottleneckWithFixedBatchNorm, self).__init__(
in_channels=in_channels,
bottleneck_channels=bottleneck_channels,
out_channels=out_channels,
num_groups=num_groups,
stride_in_1x1=stride_in_1x1,
stride=stride,
dilation=dilation,
norm_func=FrozenBatchNorm2d,
dcn_config=dcn_config
)
class StemWithFixedBatchNorm(BaseStem):
def __init__(self, cfg):
super(StemWithFixedBatchNorm, self).__init__(
cfg, norm_func=FrozenBatchNorm2d
)
class BottleneckWithGN(Bottleneck):
def __init__(
self,
in_channels,
bottleneck_channels,
out_channels,
num_groups=1,
stride_in_1x1=True,
stride=1,
dilation=1,
dcn_config={}
):
super(BottleneckWithGN, self).__init__(
in_channels=in_channels,
bottleneck_channels=bottleneck_channels,
out_channels=out_channels,
num_groups=num_groups,
stride_in_1x1=stride_in_1x1,
stride=stride,
dilation=dilation,
norm_func=group_norm,
dcn_config=dcn_config
)
class StemWithGN(BaseStem):
def __init__(self, cfg):
super(StemWithGN, self).__init__(cfg, norm_func=group_norm)
_TRANSFORMATION_MODULES = Registry({
"BottleneckWithFixedBatchNorm": BottleneckWithFixedBatchNorm,
"BottleneckWithGN": BottleneckWithGN,
})
次に、これらの2つの構造はBNとGNに関する4つの派生種をカプセル化して、呼び出しを容易にする。パッケージは:
_TRANSFORMATION_MODULES = Registry({
"BottleneckWithFixedBatchNorm": BottleneckWithFixedBatchNorm,
"BottleneckWithGN": BottleneckWithGN,
})
_STEM_MODULES = Registry({
"StemWithFixedBatchNorm": StemWithFixedBatchNorm,
"StemWithGN": StemWithGN,
})
3 Resnet全体構造3.1 Resnet構造
以上の基礎の上で、私達は以上の構造の上で更に本当のResnetを構築することができます。
class ResNet(nn.Module):
def __init__(self, cfg):
super(ResNet, self).__init__()
# If we want to use the cfg in forward(), then we should make a copy
# of it and store it for later use:
# self.cfg = cfg.clone()
# Translate string names to implementations
# conv , , stem
stem_module = _STEM_MODULES[cfg.MODEL.RESNETS.STEM_FUNC]
# backbone
stage_specs = _STAGE_SPECS[cfg.MODEL.BACKBONE.CONV_BODY]
# bottleneck , backbone
transformation_module = _TRANSFORMATION_MODULES[cfg.MODEL.RESNETS.TRANS_FUNC]
# Construct the stem module
self.stem = stem_module(cfg)
# Constuct the specified ResNet stages
# group normalization
num_groups = cfg.MODEL.RESNETS.NUM_GROUPS
#
width_per_group = cfg.MODEL.RESNETS.WIDTH_PER_GROUP
# stem , ,
in_channels = cfg.MODEL.RESNETS.STEM_OUT_CHANNELS
# group backbone
stage2_bottleneck_channels = num_groups * width_per_group
#
stage2_out_channels = cfg.MODEL.RESNETS.RES2_OUT_CHANNELS
self.stages = []
self.return_features = {}
for stage_spec in stage_specs:
name = "layer" + str(stage_spec.index)
# stage2 , 2
stage2_relative_factor = 2 ** (stage_spec.index - 1)
bottleneck_channels = stage2_bottleneck_channels * stage2_relative_factor
out_channels = stage2_out_channels * stage2_relative_factor
stage_with_dcn = cfg.MODEL.RESNETS.STAGE_WITH_DCN[stage_spec.index -1]
#
module = _make_stage(
transformation_module,
in_channels,
bottleneck_channels,
out_channels,
stage_spec.block_count,
num_groups,
cfg.MODEL.RESNETS.STRIDE_IN_1X1,
first_stride=int(stage_spec.index > 1) + 1,
dcn_config={
"stage_with_dcn": stage_with_dcn,
"with_modulated_dcn": cfg.MODEL.RESNETS.WITH_MODULATED_DCN,
"deformable_groups": cfg.MODEL.RESNETS.DEFORMABLE_GROUPS,
}
)
in_channels = out_channels
self.add_module(name, module)
self.stages.append(name)
self.return_features[name] = stage_spec.return_features
# Optionally freeze (requires_grad=False) parts of the backbone
self._freeze_backbone(cfg.MODEL.BACKBONE.FREEZE_CONV_BODY_AT)
#
def _freeze_backbone(self, freeze_at):
if freeze_at < 0:
return
for stage_index in range(freeze_at):
if stage_index == 0:
m = self.stem # stage 0 is the stem
else:
m = getattr(self, "layer" + str(stage_index))
for p in m.parameters():
p.requires_grad = False
def forward(self, x):
outputs = []
x = self.stem(x)
for stage_name in self.stages:
x = getattr(self, stage_name)(x)
if self.return_features[stage_name]:
outputs.append(x)
return outputs
3.2 Resnet head構造ヘッドは、私が理解している限り、ある機能のネットワーク構造を完成させることです。Resnet headとは、Bottleneckブロックを使って異なるResnetを構成する機能ネットワーク構造を積み重ねることです。内部構造が似ていて、ある機能を完成させることです。ここではあまり紹介しません。上のResnetの構造ですから。
class ResNetHead(nn.Module):
def __init__(
self,
block_module,
stages,
num_groups=1,
width_per_group=64,
stride_in_1x1=True,
stride_init=None,
res2_out_channels=256,
dilation=1,
dcn_config={}
):
super(ResNetHead, self).__init__()
stage2_relative_factor = 2 ** (stages[0].index - 1)
stage2_bottleneck_channels = num_groups * width_per_group
out_channels = res2_out_channels * stage2_relative_factor
in_channels = out_channels // 2
bottleneck_channels = stage2_bottleneck_channels * stage2_relative_factor
block_module = _TRANSFORMATION_MODULES[block_module]
self.stages = []
stride = stride_init
for stage in stages:
name = "layer" + str(stage.index)
if not stride:
stride = int(stage.index > 1) + 1
module = _make_stage(
block_module,
in_channels,
bottleneck_channels,
out_channels,
stage.block_count,
num_groups,
stride_in_1x1,
first_stride=stride,
dilation=dilation,
dcn_config=dcn_config
)
stride = None
self.add_module(name, module)
self.stages.append(name)
self.out_channels = out_channels
def forward(self, x):
for stage in self.stages:
x = getattr(self, stage)(x)
return x
以上のPytouch mark-rnnの詳細を共有するということは、小編集が皆さんに提供したすべての内容です。参考にしていただければと思います。どうぞよろしくお願いします。