テンソルは多次元オブジェクトであり、TensorflowやPyTorchなどの深層学習フレームワークに不可欠なデータ表現ブロックである。
スカラーは0次元、ベクトルは1次元、行列は2次元、テンソルは3次元以上である。
実際には、便宜上スカラーやベクトル、行列をテンソルと呼ぶこともよくあります。
注意:テンソルはNumpyの配列のように任意のn次元配列にすることができる。
多くのフレームワークがNumpyの配列を扱うためのサポートを持っており、それらの多くはNumpyの上に構築されているので、統合は自然かつ効率的である。
しかし、torch.Tensor
はNumpy配列よりも多くの組み込み機能を持ち、これらの機能はDeep Learningアプリケーション向け(GPUアクセラレーションなど)なので、PyTorchで作業する際には通常のNumpy配列よりも torch.Tensor
インスタンスを優先するのは理にかなっていると言えます。
さらに、torch.Tensor
は非常にNumpyに似たAPIを持っているので、経験がある人なら直感的に使えるでしょう。
このガイドでは、Numpyの配列とPyTorchのTensorを変換する方法を学びます。
Numpyの配列をPyTorchのTensorに変換する
Numpyの配列をPyTorchのテンソルに変換するには、 from_numpy()
関数を使うか、Numpyの配列を torch.Tensor()
コンストラクタに渡すか、 tensor()
関数を使うかの2種類のアプローチがあります。
import torch
import numpy as np
np_array = np.array([5, 7, 1, 2, 4, 4])
# Convert Numpy array to torch.Tensor
tensor_a = torch.from_numpy(np_array)
tensor_b = torch.Tensor(np_array)
tensor_c = torch.tensor(np_array)
さて、何が違うのでしょうか?from_numpy()と
tensor()の関数は
dtypeを意識しています! Numpyで整数の配列を作成したので、当然ながら要素の
dtypeは
int32` となります。
print(np_array.dtype)
# dtype('int32')
もし2つのテンソルをプリントアウトするとしたら
print(f'tensor_a: {tensor_a}
tensor_b: {tensor_b}
tensor_c: {tensor_c}')
tensor_aと
tensor_cは
np_arrayのデータ型を保持したまま、PyTorch のバリアント (
torch.int32) にキャストされ、
tensor_b` は自動的に float に割り当てられる。
tensor_a: tensor([5, 7, 1, 2, 4, 4], dtype=torch.int32)
tensor_b: tensor([5., 7., 1., 2., 4., 4.])
tensor_c: tensor([5, 7, 1, 2, 4, 4], dtype=torch.int32)
これは、 dtype
フィールドをチェックすることでも確認できる。
print(tensor_a.dtype) # torch.int32
print(tensor_b.dtype) # torch.float32
print(tensor_c.dtype) # torch.int32
dtypeを使ったNumpy配列からPyTorchのTensorへの変換
これらのアプローチは、テンソルを作成する際に明示的に dtype
を設定できるかどうかという点でも異なる。
from_numpy()と
Tensor()は
dtypeの引数を受け付けないが、
tensor()` は受け付ける。
# Retains Numpy dtype
tensor_a = torch.from_numpy(np_array)
# Creates tensor with float32 dtype
tensor_b = torch.Tensor(np_array)
# Retains Numpy dtype OR creates tensor with specified dtype
tensor_c = torch.tensor(np_array, dtype=torch.int32)
print(tensor_a.dtype) # torch.int32
print(tensor_b.dtype) # torch.float32
print(tensor_c.dtype) # torch.int32
もちろん、全く同じ構文で簡単にキャストすることができますし、生成後に dtype
を設定することもできますので、 dtype
引数を受け付けることは制限事項ではなく、むしろ便利なことです。
tensor_a = tensor_a.float()
tensor_b = tensor_b.float()
tensor_c = tensor_c.float()
print(tensor_a.dtype) # torch.float32
print(tensor_b.dtype) # torch.float32
print(tensor_c.dtype) # torch.float32
PyTorchのTensorをNumpyのArrayに変換する
テンソルは最終的にNumpyの配列の上に構築されるので、PyTorchのテンソルをNumpyの配列に変換するのは簡単で、私たちがしなければならないのは基礎となるデータ構造を「公開」するだけです。
PyTorchはハードウェアに応じてデータの計算を最適化することができるので、いくつかの注意点がありますが。
tensor = torch.tensor([1, 2, 3, 4, 5])
np_a = tensor.numpy()
np_b = tensor.detach().numpy()
np_c = tensor.detach().cpu().numpy()
となります。
CPU PyTorch Tensor – CPU Numpy Array
もしテンソルがCPU上にあり、新しいNumpy配列もそこにあるのなら、データ構造を公開するだけで良い。
np_a = tensor.numpy()
# array([1, 2, 3, 4, 5], dtype=int64)
これは非常にうまくいき、きれいなNumpy配列を手に入れることができます。
CPU PyTorch Tensor with Gradients – CPU Numpy Array
しかし、もしテンソルが勾配を計算する必要がある場合(つまり requires_grad
引数が True
に設定されている)、この方法はもううまくいきません。
この場合、テンソルから配列を切り離す必要があり、切り離すことで勾配を除去することになります。
tensor = torch.tensor([1, 2, 3, 4, 5], dtype=torch.float32, requires_grad=True)
np_a = tensor.numpy()
# RuntimeError: Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
np_b = tensor.detach().numpy()
# array([1., 2., 3., 4., 5.], dtype=float32)
GPU PyTorch Tensor – CPU Numpy Array
最後に – GPUでテンソルを作成した場合、通常のNumpy配列はGPUアクセラレーションをサポートしないことを覚えておくとよいでしょう。
それはCPU上に存在します。
テンソルをCPUに転送し、データ構造をデタッチ/エクスポーズする必要があります。
注意: これは to('cpu')
または cpu()
関数によって行うことができます – これらは機能的に等価です。
これは明示的に行う必要があります。
なぜなら、もし自動的に行われた場合、CPUとCUDAのテンソルから配列への変換は、ボンネットの中で異なってしまい、将来的に予期せぬバグにつながる可能性があるからです。
PyTorchはかなり明示的なので、このような自動変換は意図的に避けました。
# Create tensor on the GPU
tensor = torch.tensor([1, 2, 3, 4, 5], dtype=torch.float32, requires_grad=True).cuda()
np_b = tensor.detach().numpy()
# TypeError: can't convert cuda:0 device type tensor to numpy. Use Tensor.cpu() to copy the tensor to host memory first.
np_c = tensor.detach().cpu().numpy()
# array([1., 2., 3., 4., 5.], dtype=float32)
注意:CPUに転送する前にグラデーションを除去するために、cpu()
の前に detach()
を呼び出すことを強くお勧めします。
グラデーションは detach()
を呼び出した後はどうせ重要ではありませんから、どの時点でもそれをコピーすることは完全に冗長で非効率的です。
できるだけ早く「無駄を省く」方が良いのです。
一般的に言って、この方法は最も安全である。
どのような種類のテンソルであっても、失敗することはない。
CPUのテンソルがあって、それをCPUに送っても何も起こらない。
勾配をもたないテンソルなら、それをデタッチしても何も起こらない。
一方、例外が発生する。
結論
このガイドでは – Numpy配列をPyTorchテンソルに変換する方法に入る前に、PyTorchテンソルが何であるかを見てきました。
最後に、PyTorchのテンソルがどのようにNumpyの配列を公開し、どのような場合に追加の転送と刈り込みを行わなければならないかを探りました。