【MMDetection】 config基礎編

## 概要
まだまだ日本語情報が少ない物体検出フレームワーク"MMDetection"について、学んだことを記録していこうと思います。間違い等ありましたらぜひコメントで教えてください。よろしくお願いします。

前回：[既存モデルの学習編](https://qiita.com/bellpond/items/931a2c29a177bbc08888)


今回はMMDetectionでモデルを設計する上で欠かせない config について、その基礎をまとめたいと思います。

## config ファイルとは
MMDetectionでは、モジュールを組み合わせることでモデルを設計します。モデルの設計（と学習方法）は`mmdetecion/configs/*/*.py`に記述し、これをconfigファイルと呼びます。

configのファイル名はモデルの概要が理解できる様に命名されています。
命名規則については[こちら](https://qiita.com/bellpond/items/931a2c29a177bbc08888#config%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%90%8D%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)で触れているのでご確認ください。

## config ファイルの読み込み

configファイルはMMCVライブラリの Config クラスのメソッド`Config.fromfile`を用いて読み込むことで、ひとつの辞書としてロードすることができます。ソースコードは[こちら](https://github.com/open-mmlab/mmcv/blob/master/mmcv/utils/config.py#L65)。

それでは、実際にconfigファイルを作って、読み込みを行ってみます。

### 1. configファイルの作成
```my_config_a.py
a = 10
b = '20'
c = dict(c_1: 1, c_2: 2)
```

### 2. configファイルのロード
インタラクティブモードでpythonを起動して以下のように入力します。

```python
>>> from mmcv import Config
>>>
>>> cfg = Config.fromfile('./my_config_a.py')
>>> cfg
Config (path: ./my_config_a.py): {'a': 10, 'b': '20', 'c': {'c_1': 1, 'c_2': 2}}
```
config ファイルが読み込まれて辞書に変換されていることが確認できます。

## config の記法
上述したように、config ファイルは`Config.fromfile`メソッドでロードすると一つの辞書に変換されます。これはconfig ファイルをオブジェクトとして読み込み、そのアトリビュートを辞書型に変換して返すことで実現しています。

従って、クラスの中にアトリビュートを追加する感覚で書けば大丈夫です。

### 1. keyとvalueの追加
`key = value`という形で1行ごとに追加することができます。

```my_config_a.py
# １行ごとに key = value の形で追加していく
a = 10
b = '20'
c = dict(c_1=1, c_2=2)  # value に辞書を持たせることも可能
```

### 2. configファイルの継承
`_base_ = '[継承するconfigファイルへのパス]'`と最初の行に追加することで、既存のconfigファイルを元に、新しくkeyとvalueを追加したconfigファイルを作成することができます。

#### 2.1. 継承の基本

```my_config_b.py
# my_config_a.py の内容を継承
_base_ = 'my_config_a.py'
c = dict(c_3='config_b')  # 辞書のkey:valueも追加できる
d = 'config_b'
```

```python
>>> from mmcv import Config
>>>
>>> cfg = Config.fromfile('./my_config_b.py')
>>> cfg
Config (path: my_config_b.py): {'a': 10, 'b': '20', 'c': {'c_1': 1, 'c_2': 2, 'c_3': 'config_b'}, 'd': 'config_b'}
```
my_config_a.pyの内容はそのままに、新しく`{'c': {'c_3': 'config_b'}}`と`{'d': 'config_b'}`が追加されていることがわかります。

#### 2.2. 継承したvalueの修正
keyとvalueの追加だけでなく、継承したvalueを修正することもできます。

```my_config_b.py
# my_config_a.py の内容を継承
_base_ = 'my_config_a.py'
a = 30  # a = 10 から修正
d = 'config_b'
```

```python
>>> from mmcv import Config
>>>
>>> cfg = Config.fromfile('./my_config_b.py')
>>> cfg
Config (path: ./my_config_b.py): {'a': 30, 'b': '20', 'c': {'c_1': 1, 'c_2': 2}, 'd': 'config_b'}
```
継承したconfigファイルの内容が修正されていることがわかります。

#### 2.3. 継承した辞書のkeyを削除
さらに、`_delete_=True`を用いることで継承したdictのkeyをリセット（消去）することもできます。

```my_config_b.py
# my_config_a.py の内容を継承
_base_ = 'my_config_a.py'
c = dict(
    _delete_=True,  # 継承した辞書cのkey（c_1, c_2）を削除
    c_3 = 3, c_4 = 4)
d = 'config_b'
```

```python
>>> from mmcv import Config
>>>
>>> cfg = Config.fromfile('./my_config_b.py')
>>> cfg
Config (path: my_config_b.py): {'a': 10, 'b': '20', 'c': {'c_3': 3, 'c_4': 4}, 'd': 'config_b'}
```
継承した辞書cのキーがリセットされていることがわかります。

#### 2.4. 複数のconfigファイルを継承
`_base_`にconfigファイルへのパスをリストとして渡すことで、複数のconfigファイルを継承することができます。
このとき、configファイル間のkeyの重複は許されません。

```my_config_b.py
# my_config_a.py　と重複しないように修正
d = 'config_b'
```

```my_config_c.py
# my_config_a.py, my_config_b.py の内容を継承
_base_ = ['my_config_a.py', 'my_config_b.py']
e = 'config_c'
```

```python
>>> from mmcv import Config
>>>
>>> cfg = Config.fromfile('./my_config_c.py')
>>> cfg
Config (path: my_config_d.py): {'a': 10, 'b': '20', 'c': {'c_1': 1, 'c_2': 2}, 'd': 'config_b', 'e': 'config_c'}
```
ふたつのconfigが継承されていることがわかります。

### 3. valueへのアクセス
ロードしたconfigのvalueへのアクセスは、対応するkeyを名前に持つアトリビュートの呼び出しと同様に行えます。

```python
>>> from mmcv import Config
>>>
>>> cfg = Config.fromfile('./my_config_a.py')
>>> cfg.a
10
>>> cfg.b
'20'
>>> cfg.c
{'c_1': 1, 'c_2': 2}
>>> cfg.c.c_1  # valueに辞書を持つ場合も同様にアクセス可能
1
>>> cfg.c.c_2
2
```


## おまけ
confiｇファイルではいくつかの事前定義された変数が利用可能なので、簡単に紹介します。

- `'{{ fileDirname }}'`：configファイルのディレクトリまでのパス
- `'{{ fileBasename }}'`：configファイル名
- `'{{ fileBasenameNoExtension }}'`：configファイル名から拡張子を除いたもの
- `'{{ fileExtname }}'`：configファイルの拡張子

```my_config_d.py
a = 10
fileBasename = '{{ fileBasenameNoExtension }}'
fileExtname = '{{ fileExtname }}'
```

```python
>>> from mmcv import Config
>>>
>>> cfg = Config.fromfile('./my_config_d.py')
>>> cfg.fileBasename
'my_config_d'
>>> cfg.fileExtname
'.py'
```

# 参考文献
- [公式ドキュメント](https://mmdetection.readthedocs.io/en/latest/index.html)
- [MMCVの公式ドキュメント](https://mmcv.readthedocs.io/en/latest/index.html)