PyTorch入門:テンソル操作完全ガイド

Tensorは、配列や行列に非常によ似た特殊なデータ構造です。PyTorchでは、モデルの入力と出力、およびモデルのパラメータをエンコードするためにtensorを使用します。 TensorはNumPyの配列に似ていますが、tensorはGPUやその他の特殊なハードウェア上で実行して計算を高速化できる点が異なります。ndarrayに慣れているなら、Tensor APIにも慣れるでしょう。そうでない場合、このクイックAPIガイドに従ってください。

import torch
import numpy as np

テンソルの初期化

テンソルはさまざまな方法で初期化できます。以下の例を見てみましょう:

データから直接初期化

テンソルはデータから直接初期化できます。データ型は自動的に推論されます。

sample_data = [[1, 2], [3, 4]]
x_data = torch.tensor(sample_data)

NumPy配列から初期化

テンソルはNumPy配列から作成できます(その逆も可能 - NumPyとの連携を参照)。

np_array = np.array(sample_data)
x_np = torch.from_numpy(np_array)

他のテンソルから初期化:

明示的に上書きしない限り、新しいテンソルは元のテンソルの属性(形状、データ型)を保持します。

x_ones = torch.ones_like(x_data) # x_dataのプロパティを保持
print(f"1で埋められたテンソル: \n {x_ones} \n")
x_rand = torch.rand_like(x_data, dtype=torch.float) # x_dataのデータ型を上書き
print(f"ランダムなテンソル: \n {x_rand} \n")

出力:

1で埋められたテンソル:
 tensor([[1, 1], 
        [1, 1]])
ランダムなテンソル:
 tensor([[0.1234, 0.5678], 
        [0.9012, 0.3456]])

ランダム値または定数値を使用:

shapeはテンソルの次元を表すタプルです。以下の関数では、出力テンソルの次元を決定します。

dimensions = (2, 3,)
rand_tensor = torch.rand(dimensions)
ones_tensor = torch.ones(dimensions)
zeros_tensor = torch.zeros(dimensions)

print(f"ランダムなテンソル: \n {rand_tensor} \n")
print(f"1で埋められたテンソル: \n {ones_tensor} \n")
print(f"0で埋められたテンソル: \n {zeros_tensor} \n")

出力:

ランダムなテンソル:
 tensor([[0.2345, 0.6789, 0.0123],
        [0.4567, 0.8901, 0.3456]])

1で埋められたテンソル:
 tensor([[1., 1., 1.],
        [1., 1., 1.]])

0で埋められたテンソル:
 tensor([[0., 0., 0.],
        [0., 0., 0.]])

テンソルの属性

テンソルの属性は、その形状、データ型、および保存されているデバイスを記述します。

sample_tensor =  torch.rand(3, 4)

print(f"テンソルの形状: {sample_tensor.shape}")
print(f"テンソルのデータ型: {sample_tensor.dtype}")
print(f"テンソルが保存されているデバイス: {sample_tensor.device}")

出力:

テンソルの形状: torch.Size([3, 4])
テンソルのデータ型: torch.float32
テンソルが保存されているデバイス: cpu

テンソルの操作

100以上のテンソル操作があり、転置、インデックス、スライス、数学演算、線形代数、ランダムサンプリングなどが含まれます。より包括的な説明はこちらをご覧ください。 これらの操作のすべてはGPUで実行できます(通常はCPUより高速です)。Colabを使用している場合は、Edit->Notebook SettingでGPUを構成してください。

# 利用可能であれば、テンソルをGPUに移動します
if torch.cuda.is_available():
  sample_tensor = sample_tensor.to('cuda')
  print(f"テンソルが保存されているデバイス: {sample_tensor.device}")

出力:

テンソルが保存されているデバイス: cuda:0

操作リストのいくつかを試してみてください。NumPy APIに慣れている場合、Tensor APIの使用は簡単に感じるでしょう。

NumPyに似た標準的なインデックスとスライス:

matrix = torch.ones(4, 4)
matrix[:, 1] = 0
print(matrix)

出力:

tensor([[1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.]])

テンソルの連結、torch.catを使用して指定された次元に沿って一連のテンソルを連結できます。torch.stackも参照してください。これは別のテンソルの連結操作で、torch.catとは少し異なります。

combined = torch.cat([matrix, matrix, matrix], dim=1)
print(combined)

出力:

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.]])

dim=0の場合、最初の次元に沿って連結(最初の次元の値が増加)、dim=1の場合、2番目の次元に沿って連結(2番目の次元の値が増加)

テンソルの乗算

# 要素ごとの積を計算
print(f"matrix.mul(matrix) \n {matrix.mul(matrix)} \n")
# 代替構文
print(f"matrix * matrix \n {matrix * matrix}")

出力:

matrix.mul(matrix)
 tensor([[1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.]])

matrix * matrix
 tensor([[1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.]])

2つのテンソルの行列積を計算

print(f"matrix.matmul(matrix.T) \n {matrix.matmul(matrix.T)} \n")
# 代替構文:
print(f"matrix @ matrix.T \n {matrix @ matrix.T}")

出力:

matrix.matmul(matrix.T)
 tensor([[3., 3., 3., 3.],
        [3., 3., 3., 3.],
        [3., 3., 3., 3.],
        [3., 3., 3., 3.]])

matrix @ matrix.T
 tensor([[3., 3., 3., 3.],
        [3., 3., 3., 3.],
        [3., 3., 3., 3.],
        [3., 3., 3., 3.]])

In-place操作: テンソル操作の名前の後に「_」を付けると、in-place操作になります。例:x.copy_(y), x.t_()xを変更します。

print(matrix, "\n")
matrix.add_(5)
print(matrix)

出力:

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.]])

注意:In-place操作はメモリを節約できますが、導数を計算すると問題が発生する可能性があります。なぜなら、履歴が即座に失われるからです。そのため、これらの使用は推奨されません。

NumPyとの連携

CPU上のテンソルとNumPy配列は、その基礎となるメモリ位置を共有できます。一方を変更すると、もう一方も変更されます。

テンソルをNumPy配列に変換

tensor_example = torch.ones(5)
print(f"tensor_example: {tensor_example}")
numpy_array = tensor_example.numpy()
print(f"numpy_array: {numpy_array}")

出力:

tensor_example: tensor([1., 1., 1., 1., 1.])
numpy_array: [1. 1. 1. 1. 1.]

テンソルへの変更はNumPy配列にも反映されます

tensor_example.add_(1)
print(f"tensor_example: {tensor_example}")
print(f"numpy_array: {numpy_array}")

出力:

tensor_example: tensor([2., 2., 2., 2., 2.])
numpy_array: [2. 2. 2. 2. 2.]

NumPy配列をテンソルに変換

numpy_data = np.ones(5)
tensor_from_numpy = torch.from_numpy(numpy_data)

NumPyへの変更はテンソルにも反映されます

np.add(numpy_data, 1, out=numpy_data)
print(f"tensor_from_numpy: {tensor_from_numpy}")
print(f"numpy_data: {numpy_data}")

出力:

tensor_from_numpy: tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
numpy_data: [2. 2. 2. 2. 2.]

タグ: PyTorch テンソル ディープラーニング GPU NumPy

8月6日 11:04 投稿