広告

PyTorch Tensorを確実にNumpy Arrayに変換する

結論

Tensor.detach().cpu().numpy() とする。
  In [1]: import torch
  In [2]: import numpy as np
  In [3]: a = torch.Tensor([[1, 2], [3, 4]])
  In [4]: a.detach().cpu().numpy()
  Out[5]:
  array([[1., 2.],
         [3., 4.]], dtype=float32)
  
とすれば(ほぼ)確実に変換が成功する。 解説:PyTorch TensorとNumpy Arrayはどちらも多次元配列の ためのデータ型なので自由に変換できていいのでは? と思うが実はTensorには機械学習のための色々な追加機能があり、
これを無効化してやらないとNumPy側で対応していない部分が
残ってしまい変換できないのが問題だ。

失敗例1:テンソルがGPUにある

to("cuda")でテンソルをGPUに移しておいて高速で計算できるようにできるが、 NumPyはGPUの機能がないため変換に失敗(ちなみにNumPyのGPU版のCuPyというのもあるらしい)
    In [7]: a = torch.Tensor([[1, 2], [3, 4]]).to("cuda")
    In [8]: a.numpy()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
 in 
----> 1 a.numpy()

TypeError: can't convert cuda:0 device type tensor to numpy.
Use Tensor.cpu() to copy the tensor to host memory first.
  


失敗例1:テンソルがgradを持っている

Gradとはニューラルネットなどの学習に使うテンソルの勾配の情報である。
これは機械学習でよく使うのでPyTorchではテンソルとセットで持っているようになっているが、 当然NumPyでサポートされていないので変換を妨げる。
detach()でgradを持たないテンソルのコピーを作ることができ、これはNumPyに変換できる。
    In [10]: a = torch.nn.Parameter(torch.Tensor([[1, 2], [3, 4]]))  # Parameterはgradを持っている
    In [11]: a.numpy()
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
 in 
----> 1 a.numpy()

RuntimeError: Can't call numpy() on Tensor that requires grad.
Use tensor.detach().numpy() instead.
  


対策

Gradをなくしてくれるdetach()とcpuにデータを移すcpu()を必ず呼ぶようにする。
これらはもともとgradがなかったりデータがcpuにあっても何もしないため、 呼んでおいて損はない。
    In [12]: a = torch.nn.Parameter(torch.Tensor([[1, 2], [3, 4]])).to("cuda")

  In [13]: a.detach().cpu().numpy()
  Out[13]:  #成功
  array([[1., 2.],
         [3., 4.]], dtype=float32)
  


おすすめ記事

PIL, NumPy, PyTorchのデータ相互変換早見表

NumPyのarray.sizeに相当するのはPytorchのTensor.numel()

Squeeze / unsqueezeの使い方:要素数1の次元を消したり作ったりする