はじめに
この記事では,物体検出タスクなどでよく用いられるDetectron2でよく生じるエラーについて解説していこうと思います.同じエラーが出たという人はぜひこの記事を参考にしてみてください.
ImportError: cannotimport name '_C' from detectron2(最初の注意書きをしっかり読んでから実践してください)
時々,githubからクローンしてきたリポジトリをVSCode上などのエディタ上で実行しようとすると,
ImportError: cannotimport name '_C' from detectron2
というエラーに出くわすことがあります.
このエラーはクローンしてきたgithubリポジトリに含まれているdetectron2をそのまま使用している時に生じることが多いです.
※注意
大体の場合githubのリポジトリをしっかり読んでbuildを行えば,このエラーは解決される(orそもそも出ない)はずです.まずは,モデルを実行する前にgithubのREADME.mdをよく読んで動かすための準備をしっかりして,それでもダメな場合のみ下記のエラー解決法を実践するようにしましょう.面倒くさがって最初からこの方法をやってしまうと,モデルのプログラムと整合性が取れない場合があり,余計におかしくなる場合があります.
例えば,あるモデルのリポジトリをgithubからクローンしてくると,
cloned_repo/
│
├── modeling/
│ ├── __init__.py
│ ├── meta_arch.py
│ └── fcos.py
│
├── utils/
│ ├── __init__.py
│ └── registry.py
│
├── detectron2/ #クローンしてきたリポジトリに元から含まれていたもの
│ ├── .cicleci
│ ├── __init__.py
│ └── registry.py
│
│
├── train.py
├── test.py
└── ・・・
上記のように,元からDetectron2が含まれていることが多いです.しかし,これをそのまま使用すると,現在説明しているImportErrorが生じることがあリます.
解決策としては,クローンしてきたgithubリポジトリに含まれているdetectron2フォルダは削除するか名前を変更して,公式のdetectron2を改めて対象ディレクトリにクローンして,公式のdetectron2をモジュールとして参照するようにpip installを行えば良いです.参照モードにするには,pip installする際に-eコマンドを以下のようにつけます.
python -m pip install -e path/to/detectron2
これで公式のdetectron2を参照するようになります.これでもエラーが解消されない場合は他の対処法を考える必要があります.
ImportError: cannot import name 'CityscapesEvaluator' from 'detectron2.evaluation'
上記のエラーを解決すると,公式のdetectron2とgithubのリポジトリで作成されたフォルダやファイルが混合したようなディレクトリ構造になります.detectron2の公式のリポジトリは常にアップデートされて最新のものになっていますが,githubのリポジトリが作成された年数によっては最新のdetectron2を利用していない場合もあり,作成されたファイルもその公式のアップデートにそぐわない記述になっていることもしばしばあります.
ここで解説するImportErrorもそのうちの一つです.
公式のdetectron2では,現在(2024年4月現在)evaluationの__init__.pyは次のように定義されています.
# Copyright (c) Facebook, Inc. and its affiliates.
from .cityscapes_evaluation import CityscapesInstanceEvaluator, CityscapesSemSegEvaluator
from .coco_evaluation import COCOEvaluator
from .rotated_coco_evaluation import RotatedCOCOEvaluator
from .evaluator import DatasetEvaluator, DatasetEvaluators, inference_context, inference_on_dataset
from .lvis_evaluation import LVISEvaluator
from .panoptic_evaluation import COCOPanopticEvaluator
from .pascal_voc_evaluation import PascalVOCDetectionEvaluator
from .sem_seg_evaluation import SemSegEvaluator
from .testing import print_csv_format, verify_results
__all__ = [k for k in globals().keys() if not k.startswith("_")]
この__init__.pyによって,evaculation/以下の各ファイルのクラスがモジュールとしてimportできるようになるのですが,少し前までは,'CityscapesInstanceEvaluator'の部分は'CityscapesEvaluator'となっていました.
そのため,少し前に作成されたgithubリポジトリの学習などを行うためのファイルには,
from detectron2.evaluation import (
CityscapesEvaluator,
COCOEvaluator,
COCOPanopticEvaluator,
DatasetEvaluators,
LVISEvaluator,
PascalVOCDetectionEvaluator,
SemSegEvaluator,
verify_results,
)
と書かれていることが多いです.
最新のdetectron2をライブラリとして参照する設定にしていると,CityscapesEvaluatorは当然 最新のdetectron2の__init__.pyには定義されていないので,このようなImportErrorが出るわけです.
解決方法は単純で,単に上記の'CityscapesEvaluator'を下記のように'CityscapesInstanceEvaluator'に変更するだけです.
from detectron2.evaluation import (
CityscapesInstanceEvaluator, #ここを変更する
COCOEvaluator,
COCOPanopticEvaluator,
DatasetEvaluators,
LVISEvaluator,
PascalVOCDetectionEvaluator,
SemSegEvaluator,
verify_results,
)
このようにすれば,エラーは解決されるはずです.このようなエラーが発生したときは,そのフォルダの __init__.py
まで行って, モジュールがどのように定義されているか確認しに行くのも1つの手かもしれません.
KeyError:'Non-existent config key:XXX'
このエラーは実行時にconfigオブジェクトにXXX(存在しないキーの名前がここに入ります)
というキーが見つからないことを意味しています.使用しているyamlファイルにXXX
というキーが含まれていなかった場合,書き足すか別のyamlファイルを使用すればいいのですが,yamlファイルにそのような記述があってもこのエラーが出ることがあります.
その原因と対処法を今から説明します.
detectron2を含むリポジトリをgithubからクローンしてきて,中身を見
るとこのようなコードをよく見かけます.
def setup(args):
"""
Create configs and perform basic setups.
"""
cfg = get_cfg()
cfg.merge_from_file(args.config_file)
cfg.merge_from_list(args.opts)
cfg.freeze()
default_setup(cfg, args)
return cfg
- これは,簡単に言えばconfigファイルのセットアップを行う関数で,main関数の最初に呼び出されることが多いです.
- この関数では
get_cfg()
関数を用いて,cfgオブジェクトにデフォルトのconfig(ここでは_C)を代入します. - defaultのconfigが何になっているかは,大体の場合,
get_cfg()
の定義に移動(⌘+クリック,WindowsではおそらくCtrl+クリック)すれば確認することができます. - その後,タスクのconfigファイルとして指定したyamlファイルの中身をデフォルトのconfigファイルに
merge_from_file()
関数を使って上書きします. - この際に,defaultのconfigである_Cにないようなconfig keyを持つconfigファイルを上書きしようとしても,_Cにそのconfig keyがない限り,上書きされることはなく, cfgオブジェクトもそのconfig keyを保持することができません.
- つまり_C(defaultのconfig)に含まれていないconfig keyを,上書き(マージ)によってcfgオブジェクトに持たせることは,この上記の関数記述では不可能なのです.
- そのため,上記のような関数のまま実行をしようとすると,yamlファイルを_Cにマージして追加したはずの
XXX
というキーは追加されていないという現象が発生して,yamlファイルで指定したはずのXXX
というキーが実は存在しないというエラーが発生してしまうのです.
これを解決するために,set_new_allowed(True)
という関数が存在します.
def setup(args):
"""
Create configs and perform basic setups.
"""
cfg = get_cfg()
cfg.set_new_allowed(True) #ここに追加します
cfg.merge_from_file(args.config_file)
cfg.merge_from_list(args.opts)
cfg.freeze()
default_setup(cfg, args)
return cfg
この関数を追加することで,マージする際にマージ先に存在しないconfig keyでもconfigオブジェクトに保持させることができます.
このようにすることで,cfgオブジェクトに保持させたいconfig keyを確実に反映させることができて,ここで説明しているエラーも出なくなります.
もし,それでもダメな場合は他の方法を考える必要があります.
KeyError: "No object named 'XXX' found in 'META_ARCH' registry!"
- このエラーは,
META_ARCH
というレジストリの中にXXX
という名前のオブジェクトが登録されていない時に生じるエラーです. - 深層学習におけるレジストリとは,特定のタスクや機能のためにクラスや関数などのオブジェクトを登録して,必要な時に取り出すことができるシステムです.
- メタアーキテクチャ(Meta Architecture)は,深層学習モデルの設計の中核をなす構造や戦略のことを指し,モデルの基本的な構成要素や全体的なアプローチを定義します.
- detectron2でもいろいろなメタアーキテクチャが利用されているわけですが,detectron2のディレクトリの
modeling/meta_arch/build.py
に次のようなコードがあります.
def build_model(cfg):
"""
Build the whole model architecture, defined by ``cfg.MODEL.META_ARCHITECTURE``.
Note that it does not load any weights from ``cfg``.
"""
meta_arch = cfg.MODEL.META_ARCHITECTURE
cfg.set_new_allowed(True)
cfg.freeze()
model = META_ARCH_REGISTRY.get(meta_arch)(cfg)
model.to(torch.device(cfg.MODEL.DEVICE))
_log_api_usage("modeling.meta_arch." + meta_arch)
return model
このコードは,単純にモデルをビルドするための関数ですが,その中の
model = META_ARCH_REGISTRY.get(meta_arch)(cfg)
というコードで,configオブジェクトで指定したモデルをMETA_ARCH_REGISTRY
から取得してくる際に,そのモデルがMETA_ARCH_REGISTRY内に登録されていないと,ここで説明しているエラーが発生します.
そのための解決策として,まずMETA_ARCH_REGISTRY
にどのモデルが登録されているかを把握する必要があります.
エラーが出る直前などでチェックポイントを設定してからデバックを行って,デバックコンソールで
> META_ARCH_REGISTRY
と入力して,エンターを押すと
このように,レジストリに登録されている一覧を取得することができます.ここに,'XXX'という名前のオブジェクトを登録する必要があります.
どのように登録するかは,一覧を取得した時に登録されているモデルのクラスを作成しているファイルを確認すればすぐわかるようになっています.
modeling/meta_arch
内に様々なモデルを定義したファイルが置いてありますが,レジストリ内に登録してあるモデルのファイルを確認すると,class宣言の1行上に次のような記述があるはずです.
@META_ARCH_REGISTRY.register()
class GeneralizedRCNN(nn.Module):
・・・
このようにデコレータ(既存の関数やクラスに機能を追加するための特別な構文)を用いて,META_ARCH
にそのモデルを登録することができます.この記述を追加したいモデルのクラスの1行上に加えるとエラーは解消されるはずです.
TypeError (note: full exception trace is shown but execution is paused at: _run_module_as_main) FCOS._init_() takes 1 positional argument but 2 were given
公式のdetectron2ではモデルとしてFCOS
というものが定義されています.
今回では,FCOS
というモデルで一例を話しますが,他のモデルにも使用できるノウハウなのでぜひ参考にしてください.
meta_arch = cfg.MODEL.META_ARCHITECTURE
model = META_ARCH_REGISTRY.get(meta_arch)(cfg)
githubのリポジトリを見てみるとこのように,modelを定義しているコードがあります.
これは簡単に言えばMETA_ARCH_REGISTRY
の中でconfigで定義したモデルを,model
という変数名で使用できるようにしているのですが,実際にデバックなどを行なって確認してみるとMETA_ARCH_REGISTRY.get(meta_arch)
の値は,configでMETA_ARCHTECTURE
にFCOS
を使用しているため,実質FCOS(cfg)
と記述したのと同じであるということになります.
つまり,FCOS
といううクラスにconfigオブジェクトの値が渡されるようになっているのです.
ここで公式のdetectron2/modeling/meta_arch/fcos.py
を見てみると下記のような記述があります.
class FCOS(DenseDetector):
"""
Implement FCOS in :paper:`fcos`.
"""
def __init__(
self,
*,
backbone: Backbone,
head: nn.Module,
head_in_features: Optional[List[str]] = None,
box2box_transform=None,
num_classes,
center_sampling_radius: float = 1.5,
focal_loss_alpha=0.25,
focal_loss_gamma=2.0,
test_score_thresh=0.2,
test_topk_candidates=1000,
test_nms_thresh=0.6,
max_detections_per_image=100,
pixel_mean,
pixel_std,
):
この__init__
の部分で色々値を受け取って処理を進めていくのですが,この__init__
に値を渡すときにこのままだとエラーが出てしまいます.
このエラーを説明するためにpositional argument
とkeyward argument
について理解しないといけません.pythonでは,関数やメソッドに引数を与える方法としてその二つがあります.その二つの違いがわからない場合は他にわかりやすく説明しているサイトがたくさんあるのでそちらを参考にして理解しましょう.そもそもこのdef __init__
ではpositional argument
がkeyward argument
よりも後ろにあるのでエラーが出るのは自明ですが,今回のエラーはおそらくそれが原因ではありません.
関数の仮引数を設定するときにpythonでは*
という文字を置くと,それ以降の引数はkeyward argument
でないといけません.つまり,*
以降の仮引数では関数が呼ばれる際に引数としてkeyward argument
が期待されているのです.
つまり,位置引数として存在できるのは
ここでの例では,model = META_ARCH_REGISTRY.get(meta_arch)(cfg)
としてFCOS
にconfigオブジェクトの値を渡していますが,このconfigオブジェクトが正常に期待通り引数に値を渡してくれるとも限りません.
__init__
の中に直接必要な情報を書き込んで引数を全てkeyward argument
にしてエラーを回避する方法をありますが,今回はあえて遠回りとも言える方法も紹介します.
それは,configオブジェクトを受け取り,それを辞書型で返す関数を作成することです.detectron2では,必要な情報を求めてそれを辞書型でreturnする関数が多々存在しますので,そのような関数に慣れておくのも悪くはないでしょう.
具体的には,下記のようにします.
def from_config(cfg):
backbone = build_backbone(cfg)
head = "RetinaNet"
head_in_features = cfg.MODEL.RETINANET.IN_FEATURES
num_classes = cfg.MODEL.RETINANET.NUM_CLASSES
pixel_mean = cfg.MODEL.PIXEL_MEAN
pixel_std = cfg.MODEL.PIXEL_STD
return {
"backbone": backbone,
"head": head,
"head_in_features": head_in_features,
"num_classes": num_classes,
"pixel_mean": pixel_mean,
"pixel_std": pixel_std,
}
そしてこの関数を使って,下記のようにFCOSクラスに値を渡すことができます.
meta_arch = cfg.MODEL.META_ARCHITECTURE
cfg_dict = from_config(cfg)
model = META_ARCH_REGISTRY.get(meta_arch)(**cfg_dict)
このようにすれば,問題なくモデルに値を渡すことができるはずです.
まとめ
今回はdetectron2でよく生じるエラーについて解説してみました.
今回の解説では私の環境での話になりますのであくまで参考程度に試してみていただけると幸いです.
他にもエラーの内容を検索してみると詳しく解説しているサイトも多くあるので,エラーの根本的な原因を理解して修正していくことが大切です.