この記事について
ChainerでTrainerを使おうと思い、lIteratorにデータを渡すためのクラスであるDatasetMixinを継承して自前データセットを作った時に、単純な勘違いでありながら若干苦労をしたので記録として残します。
(追記) この記事内で想定しているChainerのバージョンは1.20です。新しいバージョンでは状況が変化するかもしれません。
遭遇したエラーメッセージ
出力されるエラーメッセージが原因解決につながりづらい(わかりづらい)ものでした。
File "cupy/cuda/device.pyx", line 66, in cupy.cuda.device.Device.__enter__ (cupy/cuda/device.cpp:1621)
File "cupy/cuda/device.pyx", line 81, in cupy.cuda.device.Device.use (cupy/cuda/device.cpp:1862)
File "cupy/cuda/runtime.pyx", line 178, in cupy.cuda.runtime.setDevice (cupy/cuda/runtime.cpp:2702)
File "cupy/cuda/runtime.pyx", line 130, in cupy.cuda.runtime.check_status (cupy/cuda/runtime.cpp:2028)
cupy.cuda.runtime.CUDARuntimeError: cudaErrorInvalidDevice: invalid device ordinal
このメッセージを見た時に自分は「モデルかデータのGPU処理を書き間違えたかな?」と考えました。なのでcuda.cupy周りを必死にいじってみたりしたのですが、解決に至りませんでした。
ドキュメントを注意深く読めば気がついたかもしれませんが、最終的に自分はchainerのソースをみて原因を理解しました。
作成したコードの一部
class MyDataset(chainer.dataset.DatasetMixin):
def __init__(self, path):
label_list = {}
def get_label(l):
num = label_list.get(l, -1)
if num < 0:
label_list[l] = len(label_list)
return label_list[l]
return num
flist = []
llist = []
for root, dirs, files in os.walk(path):
label = os.path.basename(root) # ディレクトリ名をラベルとする
label_num = get_label(label)
for file in files:
flist.append(os.path.join(root, file))
llist.append(label_num)
self.flist = flist
self.llist = llist
def __len__(self):
return len(self.flist)
def get_example(self, i):
fname = self.flist[i]
label = self.llist[i]
img = Image.open(fname)
img = np.asarray(img, dtype=np.float32).transpose(2, 0, 1)
return img, label
GPUを有効化したchainerで、このクラスを使ってIterator, Trainerを使った学習をさせると、GPUの利用の有無にかかわらず先ほどのようなエラーが出ます。
CPU版chainerでは次のようなエラーになりました。
check_cuda_available()
File "/path/to/lib/python3.4/site-packages/chainer/cuda.py", line 83, in check_cuda_available
raise RuntimeError(msg)
RuntimeError: CUDA environment is not correctly set up
(see https://github.com/pfnet/chainer#installation).CuPy is not correctly installed. Please check your environment, uninstall Chainer and reinstall it with `pip install chainer --no-cache-dir -vvvv`.
原因
シンプルな話で、get_exampleが返すラベルの値がnumpyオブジェクトでないことが原因です。
Trainerを使わずにコードを書いている時は、値をVariableにwrapする時点でnumpyオブジェクトになっていないとTypeError("numpy.ndarray or cuda.ndarray are expected.")が出るので気が付きやいのですが、Iterator/Trainerに任せると異なるエラーが出てくるので私はなかなか原因に辿りつけませんでした(型チェックを行うような要望をすべき?)。
対策
この場合ラベル値の入っている配列llistをnumpyオブジェクトにすれば解決です。
@@ -16,7 +16,7 @@
flist.append(os.path.join(root, file))
llist.append(label_num)
self.flist = flist
- self.llist = llist
+ self.llist = np.asarray(llist, dtype=np.int32)
def __len__(self):
return len(self.flist)
最後に
Trainerを使うとファンシーな進捗出力(ProgressBar())が出せてかっこいいのでぜひTrainerを使いましょう。