Note to Self by notekunst

ただの備忘録です

古いGPU(CUDA CC=3.0)のためのTorchvisonのビルド(Ubuntu 22.04 on MacBookPro 15" Late2013)

以下の組み合わせで、一部の機能が、一応、動くようになりました。

GPU GeForce GT 750M (Compute Capability 3.0)
Display Driver 418.113 (nvidia-smiによると470.129.06)
CUDA 10.2
cuDNN 7.6.5
pytorch 1.9.1
torchvision 0.10.1
torchaudio 0.9.1

pytorch v1.9.1 に続いて、torchvision v0.10.1 をビルドしました。以下、ビルド作業の記録です。

torchvision v0.10.1 のビルドの手順

v0.10.1 の README.md にビルドの手順が記載されています。
https://github.com/pytorch/vision/tree/v0.10.1
これを参照して、ビルドを進めます。

ソースコードのダウンロード

適当なディレクトリを作成して、ソースコードをダウンロードします。

git clone --depth 1 --recursive https://github.com/pytorch/vision -b v0.10.1

ビルド

v0.10.1 の README.md (https://github.com/pytorch/vision/tree/v0.10.1) を参照しつつ、ビルドを進めます。念のために pytorch のビルドの設定と同じ設定にしました。

$ conda install libpng jpeg
$ export CMAKE_PREFIX_PATH=${CONDA_PREFIX:-"$(dirname $(which conda))/../"}
$ export USE_CUDA=1 USE_CUDNN=1 TORCH_CUDA_ARCH_LIST="3.0" 
$ export MAX_JOBS=2  (任意)
$ python setup.py install

これで、ビルド、インストールが完了しました。

テスト

torchvision のソースコードvision/test/ には、テスト用のスクリプトがあります。torchvision の以下の記事には、テスト方法の説明があります。
https://github.com/pytorch/vision/blob/v0.10.1/CONTRIBUTING.md#unit-tests=

以下のコマンドにより、複数のテストが順番に行われます。

$ pytest vision/test -vvv

試してみたところ、こんな結果になりました。
= 25 failed, 2233 passed, 108 skipped, 6 xfailed, 1545405 warnings in 783.64s (0:13:03) =

以下のファイルは、ファイル名とタイムスタンプとから、先ほどのテストの失敗の記録と思われます。
vision/.pytest_cache/v/cache/lastfailed
ファイルの中を見てみると、データダウンロードに関するエラー以外に、以下のテストのエラーがありました。

  • test_models.py
  • test_ops.py
  • test_transforms.py

個々にテストを行って結果を見てみます。

$ python vision/test/test_models.py

"CUDA out of memory"のエラーが発生していました(これは、仕方ないです・・・)

$ python vision/test/test_ops.py
Ran 66 tests in 229.388s
OK

なぜか、エラーは出ませんでした。

$ python vision/test/test_transforms.py
...
FAIL: test_adjust_gamma (__main__.Tester)

torchvision.transforms.functional.adjust_gamma の計算にエラーがあるようです。
test_transforms.py の test_adjust_gamma を見てみます。

    def test_adjust_gamma(self):
        x_shape = [2, 2, 3]
        x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
        x_np = np.array(x_data, dtype=np.uint8).reshape(x_shape)
        x_pil = Image.fromarray(x_np, mode='RGB')

        # test 0
        y_pil = F.adjust_gamma(x_pil, 1)
        y_np = np.array(y_pil)
        torch.testing.assert_close(y_np, x_np)

        # test 1
        y_pil = F.adjust_gamma(x_pil, 0.5)
        y_np = np.array(y_pil)
        y_ans = [0, 35, 57, 117, 186, 241, 97, 45, 245, 152, 255, 16]
        y_ans = np.array(y_ans, dtype=np.uint8).reshape(x_shape)
        torch.testing.assert_close(y_np, y_ans)

       # test 2
        y_pil = F.adjust_gamma(x_pil, 2)
        y_np = np.array(y_pil)
        y_ans = [0, 0, 0, 11, 71, 201, 5, 0, 215, 31, 255, 0]
        y_ans = np.array(y_ans, dtype=np.uint8).reshape(x_shape)
        torch.testing.assert_close(y_np, y_ans)

定義済の x_data に対応する x_np と、ガンマ補正(ガンマ=1)を行って得られる y_np を比較してます。また、y_np と定義済の y_ans を比較しています。ガンマ=1なので、比較結果は一致するはずです。python 上で計算を追いかけてみます。

>>> x_shape = [2, 2, 3]
>>> x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
>>> x_np = np.array(x_data, dtype=np.uint8).reshape(x_shape)
>>> x_pil = Image.fromarray(x_np, mode='RGB')
>>> y_pil = F.adjust_gamma(x_pil, 1)
>>> y_np = np.array(y_pil)
>>> x_np
array([[[  0,   5,  13],
        [ 54, 135, 226]],
       [[ 37,   8, 234],
        [ 90, 255,   1]]], dtype=uint8)
>>> y_np
array([[[  0,   5,  13],
        [ 54, 136, 227]],
       [[ 37,   8, 235],
        [ 90, 255,   1]]], dtype=uint8)

x_np と y_np との間で、いくつかの要素の値が1ずれてます。
さらに続けます。

>>> y_pil = F.adjust_gamma(x_pil, 0.5)
>>> y_np = np.array(y_pil)
>>> y_ans = [0, 35, 57, 117, 186, 241, 97, 45, 245, 152, 255, 16]
>>> y_ans = np.array(y_ans, dtype=np.uint8).reshape(x_shape)
>>> y_np
array([[[  0,  36,  58],
        [118, 186, 241]],
       [[ 98,  45, 245],
        [152, 255,  16]]], dtype=uint8)
>>> y_ans
array([[[  0,  35,  57],
        [117, 186, 241]],
       [[ 97,  45, 245],
        [152, 255,  16]]], dtype=uint8)

y_np と y_ans との間で、いくつかの要素の値が1ずれてます。
さらに続けます。

>>> y_pil = F.adjust_gamma(x_pil, 2)
>>> y_np = np.array(y_pil)
>>> y_ans = [0, 0, 0, 11, 71, 201, 5, 0, 215, 31, 255, 0]
>>> y_ans = np.array(y_ans, dtype=np.uint8).reshape(x_shape)
>>> y_np
array([[[  0,   0,   1],
        [ 11,  72, 201]],
       [[  5,   0, 216],
        [ 32, 255,   0]]], dtype=uint8)
>>> y_ans
array([[[  0,   0,   0],
        [ 11,  71, 201]],
       [[  5,   0, 215],
        [ 31, 255,   0]]], dtype=uint8)

y_np と y_ans との間で、いくつかの要素の値が1ずれてます。
大間違いはしていませんが、少し残念です。
ソースコードの 関連部分も見ましたが、シンプルに計算してるだけのように見えました(vision/torchvision/transforms/ の functional.py, functional_pil.py, functional_tensor.py)。現時点で、不一致の原因はわかっていません。

いろいろな計算で遊ぶには、十分な気もします。