【Pytorch】 detectron2 teardown reports
detectron2 teardown reports
detectron2 teardown reports
분류 : ConNet
re-parameterization technique
를 사용했다.favorable accuracy-speed trade-off
를 가진 모델이다.PS. 여기서, full precision(fp32): half precision이 fp16(floating point)을 Training 과정에서 효율적으로 사용하고 fp32도 중간중간에 사용하는 것이라면, full precision은 항상 fp32만 사용하는 것.
complicated ConvNets
: Inception, ResNet, DensNet, EfficientNetdrawbacks
complicated multi-branch designs
(ex, residual addition, branch-concatenation in Inception): 구현하기가 어렵다. Inference하는데 느리다. 메모리 사용을 더 많이 한다.without any branch
모델이다. 하지만 multi-branch
들과 유사한 성능을 내기에는 매우 challenging했다.avoids the gradient vanishing problem
을 위해서 residual Block
을 사용한다. 하지만! 이 모듈은 Inference 에서 필요가 없다.하기 위해서
re-parameterization` 사용했다. 전체 구조를 미리 대강 살펴보면 아래와 같다.아래에 4.2 내용 참조
Winograd Algorithm
을 사용하면 3x3 conv연산을 더욱 가속할 수 있다. Original 3x3 conv연산보다 the amount of multiplications (MULs)
의 양이 4/9로 줄어든다. 우리는 이 알고리즘을 통한 연산을 딥러닝 프레임워크에서 default로 사용하고 있다.Tera FLoating-point Operations Per Second
GPU 제작사에서 사용하는 지표로써 actual running time and computational density의 정도를 의미한다. 높을수록 좋다.time-consuming
이 필요하기 때문이다.)computational density
가 대략 15배 차이 난다는 것을 의미한다.the memory access cost (MAC)
(2) ` degree of parallelism ` 때문이다.MAC
는 다른 연산에 비해서 높은 time-consuming
이 필요하고, degree of parallelism
이 높으면 빠른 연산이 가능하다.MAC
가 거의 없고, parallelism
이 매우 높다.NASNET-A(the number of individual conv or pooling operations in one building block)
: few (paralleism을 사용한) large operators
이 아닌 multiple small operators
을 사용하는 정도를 의미한다. 높을 수록 degree of parallelism
이 안 좋은 것이다.multi-branch topology
들은 memory-inefficient 하다. 예시는 아래의 그림에 있다.multi-branch topology
에는 몇가지 제약이 있다.channel pruning
to remove some unimportant channels 사용하지 못한다. (channel pruning)Residual block
에 의해서 implicit (sum) ensemble model
이라고 할 수 있다. n개의 block은 an ensemble of 2^n models
이라고 표현가능하다. RepVGG는 an ensemble of 3^n models
모델을 만들었다. max pooling
를 사용하지만, RepVGG에서는 최소한의 Operator만을 사용하기 위해서 사용하지 않는다.Global average pooling -> fully connected layer
guidelines
을 따라서 아래와 같은 Architecture models을 만들었다. dilated conv
를 적용한다. RepVGG에서도 dilated conv
구현하였지만, 속도가 많이 줄어들었다. (5*5로 padding한 conv를 사용해야하기 때문?)dilated conv
를 적용한 것이 fast 모델이다.kernel_value = np.zeros((self.in_channels, input_dim, 3, 3), dtype=np.float32)
에서 self.in_channels
가 self.out_channels
이 아닌 이유는, Skip_connection 연산은 input, output channel이 같기 때문이다.kernel_value[i, i % input_dim, 1, 1] = 1
코드가 위의 나의 그림 중 노란색으로 수정한 코드부분이다.In this short guide, you’ll see how to Visualize torch.tensor or numpy.ndarray
how to Visualize torch.tensor or numpy.ndarray
# from via import via; via(x)
import numpy as np
import torch
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
def via(arr, save_txt:bool = True, size:tuple = (20,20),
out:str = 'array_out.txt', Normalize:bool = False):
dim = arr.ndim
if isinstance(arr, np.ndarray):
# (#Images, #Chennels, #Row, #Column)
if dim == 4:
arr = arr.transpose(3,2,0,1)
if dim == 3:
arr = arr.transpose(2,0,1)
if isinstance(arr, torch.Tensor):
arr = arr.numpy()
fig = plt.figure(figsize=size)
if save_txt:
with open(out, 'w') as outfile:
outfile.write('# Array shape: {0}\n'.format(arr.shape))
if dim == 1 or dim == 2:
np.savetxt(outfile, arr, fmt='%-7.3f')
elif dim == 3:
for i, arr2d in enumerate(arr):
outfile.write('# {0}-th channel\n'.format(i))
np.savetxt(outfile, arr2d, fmt='%-7.3f')
elif dim == 4:
for j, arr3d in enumerate(arr):
outfile.write('\n\n# {0}-th Image\n'.format(j))
for i, arr2d in enumerate(arr3d):
outfile.write('# {0}-th channel\n'.format(i))
np.savetxt(outfile, arr2d, fmt='%-7.3f')
else:
print("Out of dimension!")
if Normalize:
arr -= np.min(arr)
arr /= max(np.max(arr),10e-7)
if dim == 1 or dim == 2:
if dim==1: arr = arr.reshape((1,-1))
fig.suptitle('Array shape: {0}\n'.format(arr.shape), fontsize=30)
plt.imshow(arr, cmap='jet')
plt.colorbar()
plt.savefig('array_out.png')
elif dim == 3:
x_n = int(np.ceil(np.sqrt(arr.shape[0])))
fig.suptitle('Array shape: {0}\n'.format(arr.shape), fontsize=30)
for i, arr2d in enumerate(arr):
ax = fig.add_subplot(x_n,x_n,i+1)
im = ax.imshow(arr2d, cmap='jet')
plt.colorbar(im)
ax.set_title('{0}-channel'.format(i))
fig.savefig('array_out.png')
elif dim == 4:
img_n = arr.shape[0]
x_n = int(np.ceil(np.sqrt(arr.shape[1])))
outer = gridspec.GridSpec(img_n, 1)
fig.suptitle('Array shape: {0}\n'.format(arr.shape), fontsize=30)
for j, arr3d in enumerate(arr):
inner = gridspec.GridSpecFromSubplotSpec(x_n, x_n, subplot_spec=outer[j],wspace=0.1,hspace=0.3)
for i, arr2d in enumerate(arr3d):
ax = plt.subplot(inner[i])
im = ax.imshow(arr2d, cmap='jet')
plt.colorbar(im)
ax.set_title('{0}-Image {1}-channel'.format(j,i))
fig.suptitle('Array shape: {0}\n'.format(arr.shape), fontsize=30)
fig.savefig('array_out.png')
else:
print("Out of dimension!")
arr = torch.rand(2,28,35)
via(arr, size=(20,20))
from pythonfile import Visualarr; Visualarr(x)
“The effect of initialization and architecture”, NIPS 2018
논문 내용을 사용했다.Distillation token : Class token과 같은 역할을 한다. 하지만, the (hard) label predicted by the teacher
만으로 학습된다. (Its target objective is given by the distillation component of the loss) = 아래 loss함수의 (Fun) + (Fun)에서 오른쪽 항 만을 사용해서 학습시킨다.
Distillation token으로 만들어 지는 Distillation Embeding의 결과도 Classfication Prediction 결과가 나와야 하는데, Teacher모델이 예측하는 결과가 나오면 된다. (이미지 자체 Label에 대해서는 학습되지 않는다.)
Output에 대해서 entropy를 계산한 feature map
을 이용해서 Adversarial learning을 적용한다. 이로써, Target 예측결과도 Source 예측 결과처럼 적은 Entropy를 갖도록 유도한다.weighted self-information space
)을 이용해서 Adversarial learning을 적용한다.weighted self-information space=I_x
: Shape (C x H x W). Mu
는 얼마나 유사하게 만들것 인가? 를 나타내는 강도 값이다. 0=약하게, 1=강하게. the class-prior vector p_s
: 각 클래스당 픽셀의 갯수의 L_1 histogram 이다.
Bipartite track query matching
에 대한 부분
Deeplab V2를 사용했다고 한다. V2의 architecture 그림은 없으니 아래의 V3 architecture 그림 참조
AdaptSegNet 전체 Architecture
코드를 통해 배운, 가장 정확한 학습과정 그림은 해당 나의 포스트 참조.
Ubuntu re-install & mmclassification teardown reports
Docker hub naming (참고 사이트)
pytorch-1.5.1-cuda10.1-runtime
에서는 cat /usr/local/cuda/version.txt
가 동작하지 않는다. 그럼에도 불구하고 예전에 detr을 cuda로 돌렸었다. mmclf에서는 cuda문제가 발생했으므로 속편하게 pytorch-1.5.1-cuda10.1-devel
사용해야겠다.docker run –shm-size
docker run
cat /usr/local/cuda/version.txt
가 동작하지 않는다. 맘편하게 devel사용한다.$ sudo docker run -d -it \
--gpus all \
--restart always \
-p 8000:8080 \
--name "mmcf" \
--shm-size 2G \
-v ./home/junha/docker/mmclf/mmclassification \ # 가능하면 무조건 절대경로 사용할 것
-v /hdd1T:/dataset \
pytorch/pytorch:1.5.1-cuda10.1-cudnn7-devel
$ sudo docker run -d -it \
--gpus all \
--restart always \
-p 8080:8080 \
--name "mmcl" \
--shm-size 2G \
-v /home/junha/docker:/workspace \
-v /hdd1T:/dataset \
sb020518/mmcl:0.1
mmcv installation
$ apt-get update
$ apt-get install ffmpeg libsm6 libxext6 -y
$ pip install mmcv-full==1.3.0 -f https://download.openmmlab.com/mmcv/dist/cu101/torch1.5.0/index.html
$ git clone https://github.com/open-mmlab/mmclassification.git
$ cd mmclassification
$ pip install -e . # or "python setup.py develop"
이런 식으로 install하면, 이 공간의 mmcls 파일을 수정하면, import mmcls.util.collect_env
이것을 새로운 py파일에 적든, 터미널에서 python을 실행해서 적든, 모두 수정된 코드 내용이 적용 된다.
mmclasification install Test
python demo/image_demo.py \
/workspace/checkpoints/dog.jpg \
/workspace/configs/resnet/resnet50_b32x8_imagenet.py \
/workspace/checkpoints/resnet50_batch256_imagenet_20200708-cfb998bf.pth
[코드에서 다 봐야하는 것] (순서대로 보지 말고. 코드 따라가면서 눈에 보이는 것 부터 부셔버리기)
config 파일 분석하기 (아래 팁 참조)
configs/resnet/resnet50_b32x8_imagenet.py
에는 또 다른 config 파일의 path만 기록 되어 있다.print(f'Config:\n{cfg.pretty_text}')
test.py이든 train.py이든 핵심 호출법은 아래와 같다.
model = build_classifier(cfg.model)
dataset = build_dataset(cfg.data.test)
, datasets = [build_dataset(cfg.data.train)]
tools/test.py
, tools/dist_test.sh
dist_test.sh
는 결국 tools/test.py
실행한다.$ python tools/test.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}]
이거로 먼저 debuging하고 그 다음에 dist_test.sh 파일을 이용해서 multi-gpu testing을 진행하자.$ python tools/test.py configs/resnet/resnet50_b32x8_imagenet.py checkpoints/resnet50_batch256_imagenet_20200708-cfb998bf.pth --out result/imagenet-val.json
$ python tools/train.py configs/resnet/resnet50_b32x8_imagenet.py --load-from checkpoints/resnet50_batch256_imagenet_20200708-cfb998bf.pth
tools/test.py
+ "args" : ["configs/resnet/resnet50_b32x8_imagenet.py", "checkpoints/resnet50_batch256_imagenet_20200708-cfb998bf.pth "]
, or "args" : ["configs/resnet/resnet50_b32x8_imagenet.py", "checkpoints/resnet50_batch256_imagenet_20200708-cfb998bf.pth", "--metrics", "mAP", "CP", "CR"]
python tools/test.py configs/resnet/resnet50_b32x8_imagenet.py checkpoints/resnet50_batch256_imagenet_20200708-cfb998bf.pth --out ./result/imagenet-val-resnet50.json --metrics "accuracy" "precision" "recall" "f1_score" "support"
build_dataset/classifier
호출 순서
# 아래로 내려갈 수록 define으로 들어가는 것
## 1. dataset
from mmcls.datasets import build_dataloader, build_dataset
from mmcv.utils import Registry, build_from_cfg
return <class 'mmcls.datasets.imagesnet.ImagesNet'> # cfg.dataset_type 에 적힌 class
# return이 완벽하게 되기 이전에 __init__ 가 실행되면서, 맴버 변수가 채워진다. 이때 pipeline(augmentation)이 정의된다.
## 2. classifier
from mmcls.models import build_classifier
from mmcv.utils import build_from_cfg
return <class 'mmcls.models.classifiers.image.ImageClassifier'> # cfg.model.type 에 적힌 class
# return이 완벽하게 되기 이전에 __init__ 가 실행되면서, 맴버 변수가 채워진다. 이떄 backbone, neck, head가 정의된다.
model.eval() 하고, 테드스 결과 뽑아내기
# tools/test.py
model = MMDataParallel(model, device_ids=[0])
outputs = single_gpu_test(model, data_loader)
<class 'mmcv.paralled.MMDataParalled' object>
로 바뀐다.<class 'torch.utils.data.dataloader.DataLoader' object>
result 결과 뽑기
# from mmcls.apis import single_gpu_test
for i, data in enumerate(data_loader):
with torch.no_grad():
result = model(return_loss=False, **data)
"""
type(result) = list
len(result) = 32 = batch size = cfg.data.samples_per_gpu
type(result[0]) = numpy.ndarray
result[0].shape = (1000,)
"""
argparse, config 읽고, 에러 미리 확인.
build_
수행하기
model = build_classifier(cfg.model)
datasets = [build_dataset(cfg.data.train)]
# cpu worker 처리만 해주고
train_model(
model,
datasets,
cfg,
distributed=distributed,
validate=(not args.no_validate),
timestamp=timestamp,
meta=meta)
from mmcls.apis import train_model
# /mmcls/apis/train_model
"""
1. dataloader = `<class 'torch.utils.data.dataloader.DataLoader' object>
2. model = `<class 'mmcv.paralled.MMDataParalled' object>
"""
runner = build_runner(
cfg.runner,
default_args=dict(
model=model,
batch_processor=None,
optimizer=optimizer,
work_dir=cfg.work_dir,
logger=logger,
meta=meta))
runner.run(data_loaders, cfg.workflow)
<mmcv.runner.epoch_based_runner.EpochBasedRunner>
(Github link)from mmcv.runner import epoch_based_runner
# /mmcls/runner/epoch_based_runner
class EpochBasedRunner(BaseRunner):
def run(self, data_loaders, workflow, max_epochs=None, **kwargs): # kwargs = {None}, self.model 이미 저장되어있음
epoch_runner = getattr(self, mode)
epoch_runner(data_loaders[i], **kwargs)
# == self.train(data_loaders[i], **kwargs)
def train()
for i, data_batch in enumerate(self.data_loader):
self.run_iter(data_batch, train_mode=True)
def run_iter(self, data_batch, train_mode, **kwargs):
outputs = self.model.train_step(data_batch, self.optimizer, **kwargs)
"""
여기서 model은
<class 'mmcls.models.classifiers.image.ImageClassifier' object> 이자
<class 'mmcv.paralled.MMDataParalled' object> 이다.
따라서 아래의 파일에 들어가면 ""train_step"" 함수 찾기 가능!
"""
mmclassification/mmcls/models/classifiers/base.py
# mmclassification/mmcls/models/classifiers/base.py
class BaseClassifier(nn.Module, metaclass=ABCMeta):
def train_step(self, data, optimizer):
losses = self(**data) #== BaseClassifier.fowerd 실행!
outputs = dict(loss=loss, log_vars=log_vars, num_samples=len(data['img'].data))
def forward(self, img, return_loss=True, **kwargs):
if return_loss:
return self.forward_train(img, **kwargs)
def forward_train(self, imgs, **kwargs):
pass
# 즉! torch.nn.Module.forward 그대로 실행된다.
# test.py에서 디버깅 하면서, cfg파일 읽어보기
print(f'Config:\n{cfg.pretty_text}')
########## 예를 들어서 이 파일을 봐보자. configs/resnet/resnet50_b32x8_imagenet.py ##########
_base_ = [
'../_base_/models/resnet50.py', '../_base_/datasets/imagenet_bs32.py',
'../_base_/schedules/imagenet_bs256.py', '../_base_/default_runtime.py'
]
########## '../_base_/models/resnet50.py' ##########
model = dict(
type='ImageClassifier',
backbone=dict(
type='ResNeSt',
depth=50,
num_stages=4,
out_indices=(3, ),
style='pytorch'),
neck=dict(type='GlobalAveragePooling'),
head=dict(
type='LinearClsHead',
num_classes=1000,
in_channels=2048,
loss=dict(type='CrossEntropyLoss', loss_weight=1.0),
topk=(1, 5),
))
########## '../_base_/datasets/imagenet_bs32.py' ##########
dataset_type = 'ImageNet'
img_norm_cfg = dict(
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
# 원하는 Transformer(data agumentation)은 아래와 같이 추가하면 된다.
train_pipeline = [
dict(type='LoadImageFromFile'),
dict(type='RandomResizedCrop', size=224),
dict(type='RandomFlip', flip_prob=0.5, direction='horizontal'),
dict(type='Normalize', **img_norm_cfg),
dict(type='ImageToTensor', keys=['img']),
dict(type='ToTensor', keys=['gt_label']),
dict(type='Collect', keys=['img', 'gt_label'])
]
test_pipeline = [
dict(type='LoadImageFromFile'),
dict(type='Resize', size=(256, -1)),
dict(type='CenterCrop', crop_size=224),
dict(type='Normalize', **img_norm_cfg),
dict(type='ImageToTensor', keys=['img']),
dict(type='Collect', keys=['img'])
]
# 위에 내용은 이 아래를 정의하기 위해서 사용된다. cfg.data.test, cfg.data.val 이 사용된다.
data = dict(
samples_per_gpu=32,
workers_per_gpu=2,
train=dict(
type=dataset_type,
data_prefix='data/imagenet/train',
pipeline=train_pipeline),
val=dict(
type=dataset_type,
data_prefix='data/imagenet/val',
ann_file='data/imagenet/meta/val.txt',
pipeline=test_pipeline),
test=dict(
# replace `data/val` with `data/test` for standard test
type=dataset_type,
data_prefix='data/imagenet/val',
ann_file='data/imagenet/meta/val.txt',
pipeline=test_pipeline))
evaluation = dict(interval=1, metric='accuracy')
########## '../_base_/schedules/imagenet_bs256.py' ##########
optimizer = dict(type='SGD', lr=0.1, momentum=0.9, weight_decay=0.0001)
optimizer_config = dict(grad_clip=None)
# learning policy
lr_config = dict(policy='step', step=[30, 60, 90])
runner = dict(type='EpochBasedRunner', max_epochs=100)
########## '../_base_/default_runtime.py' ##########
# checkpoint saving
# 1번 epoch씩 모델 파라미터 저장
checkpoint_config = dict(interval=1)
# yapf:disable
# 100번 iteration에 한번씩 log 출력
log_config = dict(
interval=100,
hooks=[
dict(type='TextLoggerHook'),
# dict(type='TensorboardLoggerHook')
])
# yapf:enable
dist_params = dict(backend='nccl')
log_level = 'INFO'
load_from = None
resume_from = None
workflow = [('train', 1)] # [('train', 2), ('val', 1)] means running 2 epochs for training and 1 epoch for validation, iteratively.
mmcv.utils.Registry 사실 맴버 변수는 2개 뿐이다,
self._name = ‘backbone’
self._module_dict = 아래 그림 4번처럼 모든 backbone을 다 알고 있다!! 어떻게 알지??
아래 그림 2번과 같이 정의된 class를 모두 담고 있는 것이다.
@BACKCONES.register_modules()* 은 언제 실행되는 것 일까? 초반에 import할때 모두 실행된다. 여기*에다가 Breakpoint 걸고 디버깅한 결과는 아래와 같다.
# test.py
import mmcls.models
import mmcls.models.__init__
import mmcls.models.backbone.__init__
from .regnet import RegNet
How to load part of pre trained model?
pretrained_dict = ...
model_dict = model.state_dict()
# 1. filter out unnecessary keys
pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict}
# 2. overwrite entries in the existing state dict
model_dict.update(pretrained_dict)
# 3. load the new state dict
model.load_state_dict(pretrained_dict)
tools/test.py
의 가장 마지막이, single_gpu_test
함수에 들어가는 것이었다.mmcls/models/backbones/ResNet.py
에다가 breakpoint 걸어서 디버깅 해본 결과, 아래와 같은 코드 흐름을 확인할 수 있었다.# mmcv.runner.epoch_based_runner.py
# ** 아래의 2 코드는 같은 말이다. ** if mode == str('train') **
epoch_runner = getattr(self, mode)
epoch_runner = self.train()
EpochBasedRunner
을 확인해보면 train 맴버함수
(Github link)를 가지고 있는 것을 알 수 있다.