【機械学習】データは目で確認した方がいいと思った出来事

[前回](https://qiita.com/nekot0/items/2a733d89cee8f54d486d)に引き続き、終了済の[Kaggleの画像コンペ](https://www.kaggle.com/competitions/siim-covid19-detection/overview)を使って機械学習モデルの実装を進めている。度重なるエラーを乗り越え、とりあえず1エポック回ればいいだろうと学習を回し始めた。プログラムは機嫌よく学習を進めていたのだが、50％ほど進んだところでまたエラーに遭遇し、私は絶望した。

```
ValueError: Caught ValueError in DataLoader worker process 1.
Original Traceback (most recent call last):
  File "/opt/conda/lib/python3.7/site-packages/torch/utils/data/_utils/worker.py", line 302, in _worker_loop
    data = fetcher.fetch(index)
  File "/opt/conda/lib/python3.7/site-packages/torch/utils/data/_utils/fetch.py", line 58, in fetch
    data = [self.dataset[idx] for idx in possibly_batched_index]
  File "/opt/conda/lib/python3.7/site-packages/torch/utils/data/_utils/fetch.py", line 58, in <listcomp>
    data = [self.dataset[idx] for idx in possibly_batched_index]
  File "/tmp/ipykernel_28/3482151344.py", line 46, in __getitem__
    labels = labels
  File "/opt/conda/lib/python3.7/site-packages/albumentations/core/composition.py", line 202, in __call__
    p.preprocess(data)
  File "/opt/conda/lib/python3.7/site-packages/albumentations/core/utils.py", line 83, in preprocess
    data[data_name] = self.check_and_convert(data[data_name], rows, cols, direction="to")
  File "/opt/conda/lib/python3.7/site-packages/albumentations/core/utils.py", line 91, in check_and_convert
    return self.convert_to_albumentations(data, rows, cols)
  File "/opt/conda/lib/python3.7/site-packages/albumentations/core/bbox_utils.py", line 124, in convert_to_albumentations
    return convert_bboxes_to_albumentations(data, self.params.format, rows, cols, check_validity=True)
  File "/opt/conda/lib/python3.7/site-packages/albumentations/core/bbox_utils.py", line 390, in convert_bboxes_to_albumentations
    return [convert_bbox_to_albumentations(bbox, source_format, rows, cols, check_validity) for bbox in bboxes]
  File "/opt/conda/lib/python3.7/site-packages/albumentations/core/bbox_utils.py", line 390, in <listcomp>
    return [convert_bbox_to_albumentations(bbox, source_format, rows, cols, check_validity) for bbox in bboxes]
  File "/opt/conda/lib/python3.7/site-packages/albumentations/core/bbox_utils.py", line 334, in convert_bbox_to_albumentations
    check_bbox(bbox)
  File "/opt/conda/lib/python3.7/site-packages/albumentations/core/bbox_utils.py", line 417, in check_bbox
    raise ValueError(f"Expected {name} for bbox {bbox} to be in the range [0.0, 1.0], got {value}.")
ValueError: Expected x_min for bbox (-0.0009398496240601503, 0.46129587155963303, 0.32471804511278196, 0.9730504587155964, array([1])) to be in the range [0.0, 1.0], got -0.0009398496240601503.
```


だがしかし経験というものは生きるもののようだ。今回はエラーメッセージを見て、どのあたりに原因があるかを推測することができた。エラーメッセージによると、バウンディングボックスの各要素が[0.0, 1.0]の間に収まっていないといけないはずが、範囲を超えているらしい。範囲が[0.0, 1.0]ということは、スケーリングに関係しているのだろう。つまり、入力画像のサイズを変更するときに同時にバウンディングボックスのサイズ変更をしており、そのスケーリング比率に異常を起こしている、ということだろう。

しかしエラーメッセージによると、そのスケーリング比率がマイナスに設定されているらしい。比率がマイナスとはどういうことだろうか？

データを直接確認したいが、エラーの情報からだけではどのデータに対して発生したものかがわからない。こうなったら徹底的にやってやろう。ということで書いたコードがこちら。

```python
dataset = CTDataset(train_root_path, train_image_list)
for i in range(len(train_image_list)):
    print(i)
    image, target = dataset.__getitem__(i)
```

とにかくdatasetのインデックスを表示させて、データを取得するだけのもの。データを取得する前にインデックスを表示させているので、エラーが生じる直前のインデックス番号が問題のデータというわけだ。

とにかくこれを回してみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514778/d977e95d-eec1-0cbb-6e48-5f562367a34e.png)

で、ついに問題のデータを特定した。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514778/311bbdc2-8fb3-d302-e1d5-fa37991bcfc4.png)

そしてデータを確認してみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514778/6bbe5ece-a798-6b00-6be3-d2302b5eb175.png)

なるほど。つまり、元データのバウンディングボックスの情報は画像の外の範囲を示している、しかしAlbumentationsで扱える範囲は画像の範囲内のみなので、範囲外を示すデータに対してはエラーを返す。であれば、バウンディングボックス情報を取得する段階で、座標が負の値をとるものを排除しておけばエラーは回避できそうだ。

```python
x = max(0, int(round(bounding_box['x'])))
```

上記の修正後に現在学習を回しているところだが、1エポック目の50%を超えたのでおそらく問題なく終わるのだろうと思う。

今回学んだのは、時間のかかるプログラムを回す前には、元データを確認しておいた方がよいということだ。コードを書く際にあらゆるデータの値を想定し、書いた範囲でコードがデータを処理できるよう、データを除外、または場合分けを考えておくことで、後になってからのエラーを回避することができる。やはりデータはしっかり目で確認した方がいいと思った。