菅間修正済み 2025/05/15
【原題】TENSORS
【原著】 Suraj Subramanian、Seth Juarez 、Cassie Breviu 、Dmitry Soshnikov、Ari Bornstein
【元URL】https://pytorch.org/tutorials/beginner/basics/tensorqs_tutorial.html
【翻訳】電通国際情報サービスISID AIトランスフォーメーションセンター 小川 雄太郎
【日付】2021年03月20日
【チュトーリアル概要】
本チュートリアルでは、PyTorchの基本データ型である、Tensor(テンソル)について解説を行います。
テンソルはNumPyのndarraysに似ていますが、違いとしてGPUや他のハードウェアアクセラレータ上で動作させることができます。
テンソルとNumPyの配列は基本的には同じメモリを共有することができるため、2つの型間での変換時にはデータをコピーする必要がありません。
テンソルはその他に、自動微分に最適化されています(この点については、後ほど5. 自動微分にて、詳しく説明します)。
NumPyのndarraysに慣れている人は、Tensor APIをすぐに使いこなせると思います。
そうでない場合には、本チュートリアルを通してぜひ習得してください。
import torch
import numpy as np
データから直接テンソルに変換
データから直接テンソルを作ることができます。
その際、データ型は自動的に推測されます。
data = [[1, 2],[3, 4]]
x_data = torch.tensor(data)
NumPy arrayからテンソルに変換
テンソルとNumpy arraysは相互に変換可能です。
np_array = np.array(data)
x_np = torch.from_numpy(np_array)
他のテンソルから作成
他のテンソルから新しいテンソルを作成する場合、明示的に上書きされない限り、引数のテンソルのプロパティ(形状、データ型)を保持します。
x_ones = torch.ones_like(x_data) # x_dataの特性(プロパティ)を維持
print(f"Ones Tensor: \n {x_ones} \n")
x_rand = torch.rand_like(x_data, dtype=torch.float) # x_dataのdatatypeを上書き更新
print(f"Random Tensor: \n {x_rand} \n")
Ones Tensor:
tensor([[1, 1],
[1, 1]])
Random Tensor:
tensor([[0.1634, 0.9357],
[0.3695, 0.1511]])
ランダム値や定数のテンソルの作成
shapeは、テンソルの次元を示すタプルです。
以下の例では、shapeからテンソルのサイズを決めています。
shape = (2,3,)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)
print(f"Random Tensor: \n {rand_tensor} \n")
print(f"Ones Tensor: \n {ones_tensor} \n")
print(f"Zeros Tensor: \n {zeros_tensor}")
Random Tensor:
tensor([[0.8638, 0.0139, 0.6148],
[0.6302, 0.3649, 0.0683]])
Ones Tensor:
tensor([[1., 1., 1.],
[1., 1., 1.]])
Zeros Tensor:
tensor([[0., 0., 0.],
[0., 0., 0.]])
テンソルは属性変数として、その形状、データの型、保存されているデバイスを保持しています。
tensor = torch.rand(3,4)
print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")
Shape of tensor: torch.Size([3, 4]) Datatype of tensor: torch.float32 Device tensor is stored on: cpu
各操作はGPU上で実行可能です(一般的にCPUの場合より高速です)。
Google Colaboratoryを使用している場合は、GPUを使用できるように設定してください。
(Runtime > Change runtime type > GPU)
デフォルトではテンソルはCPU上で作られます。
そのため、明示的に、.toメソッドを使用して、テンソルをGPU上へと移動させます。
大きなテンソルをデバイス間でコピーすると、時間とメモリの面でコストがかかる点にご注意ください。
# GPUが使用可能であれば、GPU上にテンソルを移動させる
if torch.cuda.is_available():
tensor = tensor.to('cuda')
テンソル操作の中からいくつかを試してみましょう。
NumPy APIに慣れていれば、Tensor APIも簡単に使えるようになると思います。
numpy-likeなindexingとslicing:
tensor = torch.ones(4, 4)
print('First row: ',tensor[0])
print('First column: ', tensor[:, 0])
print('Last column:', tensor[..., -1])
tensor[:,1] = 0
print(tensor)
First row: tensor([1., 1., 1., 1.])
First column: tensor([1., 1., 1., 1.])
Last column: tensor([1., 1., 1., 1.])
tensor([[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.]])
テンソルの結合
torch.catを使用することで、テンソルを特定の次元に沿って結合させることができます(詳細はこちらをご覧ください)。
torch.catとは微妙に異なるテンソル結合演算であるtorch.stackも確認しておいてください。
t1 = torch.cat([tensor, tensor, tensor], dim=1)
print(t1)
tensor([[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.]])
算術演算
# 2つのテンソル行列のかけ算です。 y1, y2, y3 は同じ結果になります。
y1 = tensor @ tensor.T
y2 = tensor.matmul(tensor.T)
y3 = torch.rand_like(tensor)
torch.matmul(tensor, tensor.T, out=y3)
# こちらは、要素ごとの積を求めます。 z1, z2, z3 は同じ値になります。
z1 = tensor * tensor
z2 = tensor.mul(tensor)
z3 = torch.rand_like(tensor)
torch.mul(tensor, tensor, out=z3)
tensor([[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.]])
1要素のテンソル
1要素のテンソル(テンソルの全要素を足し算する等をした結果生まれます)を扱う場合には、.item()を使用することでPythonの数値型変数に変換できます。
agg = tensor.sum()
agg_item = agg.item()
print(agg_item, type(agg_item))
12.0 <class 'float'>
インプレース操作
演算結果をオペランドに格納する演算をインプレースと呼びます。
メソッドの最後、接尾辞として操作名に、 _ が付きます。
例えば、x.copy_(y), x.t_()であり、xの内容そのものを更新します。
print(tensor, "\n")
tensor.add_(5)
print(tensor)
tensor([[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.]])
tensor([[6., 5., 6., 6.],
[6., 5., 6., 6.],
[6., 5., 6., 6.],
[6., 5., 6., 6.]])
【注意】
インプレース操作はメモリを節約できますが、演算履歴が失われてしまうため、微分を計算する際には問題となります。
そのため、そのような微分を求める場面ではインプレース操作の使用は推奨されていません。
CPU上のテンソルとNumpy arraysは同じメモリを共有することができ、相互変換が容易です。
Tensor to NumPy array
t = torch.ones(5)
print(f"t: {t}")
n = t.numpy()
print(f"n: {n}")
t: tensor([1., 1., 1., 1., 1.]) n: [1. 1. 1. 1. 1.]
この際、テンソルが変化すると、Numpy側も変化します。
t.add_(1)
print(f"t: {t}")
print(f"n: {n}")
t: tensor([2., 2., 2., 2., 2.]) n: [2. 2. 2. 2. 2.]
NumPy array to Tensor
n = np.ones(5)
t = torch.from_numpy(n)
NumPy arrayの変化はテンソル側にも反映されます。
np.add(n, 1, out=n)
print(f"t: {t}")
print(f"n: {n}")
t: tensor([2., 2., 2., 2., 2.], dtype=torch.float64) n: [2. 2. 2. 2. 2.]
以上。