pytorchはfancy indexをサポートしていないということでしたので
サポートしています。
遅いのは、一旦 numpy に戻した上に、インデックスが tolist() で Python のリストにして処理しているからだと思います。
numpy でできることは、基本的に Pytorch でできるので、例えば arange() なども numpy.arange()
ではなく、`torch.arange(..., device="cuda") のように最初から GPU 上に作りましょう。
Pytorch - numpy、Pytroch の関数対応表 - pystyle
サンプルコード
Jupyter Notebook の %timeit で一回あたりの時間を以下の GPU で試してみました。
- GTX 1080: 1.23 ms
- GTX 2080: 700 µs
- Core i7-6700K @ 4.00GHz (index が numpy): 27.5 ms
- Core i7-6700K @ 4.00GHz (index が list): 442 ms
インデックスにリストを使うなど配列処理に Python のオブジェクトが混じってしまうと、Cで最適化された numpy のコードではなく、Python のコードを実行することになるので、かなり遅くなります。
おおよそ
- GPU は CPU より数十倍早い
- C言語は Python より100倍ぐらい早い
Python - コードの実行時間を計測する方法 - pystyle
Pytorch の計測コード
python
1import torch
2
3N = 7000
4M = 1500000
5
6A = torch.randn(N, N, device="cuda")
7I = torch.randint(high=N, size=(M,), device="cuda")
8J = torch.randint(high=N, size=(M,), device="cuda")
9
10print(A.shape, I.shape, J.shape)
11# torch.Size([7000, 7000]) torch.Size([1500000]) torch.Size([1500000])
12
13%timeit A[I, J]
14# GTX 1080: 1.23 ms ± 154 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
15# GTX 2080: 700 µs ± 68.1 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
CPU (index が numpy) の計測コード
import numpy as np
N = 7000
M = 1500000
A = np.random.randn(N, N)
I = np.random.randint(0, N, size=M)
J = np.random.randint(0, N, size=M)
print(A.shape, I.shape, J.shape)
# torch.Size([7000, 7000]) torch.Size([1500000]) torch.Size([1500000])
%timeit -n100 A[I, J]
# 27.5 ms ± 57.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
CPU (index が list) の計測コード
python
1import numpy as np
2
3N = 7000
4M = 1500000
5
6A = np.random.randn(N, N)
7I = np.random.randint(0, N, size=M).tolist()
8J = np.random.randint(0, N, size=M).tolist()
9
10print(A.shape, len(I), len(J))
11# torch.Size([7000, 7000]) torch.Size([1500000]) torch.Size([1500000])
12
13%timeit -n1 A[I, J]
14# 442 ms ± 2.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/07/22 04:26