PyTorchでTensor要素を散布的に追加する:scatter_add関数と代替方法を徹底解説


この関数は、以下の引数を取ります。

  • src (Tensor): 追加する要素の値
  • index (LongTensor): 追加する要素のインデックス
  • dim (int): 要素を追加する次元
  • input (Tensor): 入力テンソル

scatter_add 関数は、以下の出力を返します。

  • Tensor: 入力テンソルと同じ形状の出力テンソル

以下の例では、scatter_add 関数を使用して、1 次元テンソルに値を追加する方法を示します。

import torch

# 入力テンソルを作成する
input = torch.tensor([1, 2, 3, 4, 5])

# 追加する要素のインデックスを作成する
index = torch.tensor([2, 4])

# 追加する要素の値を作成する
src = torch.tensor([3, 5])

# scatter_add 関数を実行する
output = input.scatter_add(0, index, src)

# 出力テンソルを出力する
print(output)

このコードを実行すると、以下の出力が得られます。

tensor([1, 2, 5, 4, 8])

上記の例では、index テンソルによって、input テンソルの 2 番目の要素と 4 番目の要素が選択されます。src テンソルによって、これらの要素にそれぞれ 3 と 5 が追加されます。

scatter_add 関数の詳細

scatter_add 関数は、以下のオプション引数もサポートしています。

  • dim_size (int, optional): dim 次元の出力テンソルのサイズ。指定されていない場合は、input テンソルのサイズが使用されます。
  • out (Tensor, optional): 出力テンソル。指定されていない場合は、新しいテンソルが作成されます。

scatter_add 関数の使用例

scatter_add 関数は、以下のタスクに使用できます。

  • 集計データの更新
  • ヒストグラムの更新
  • スパーステンソルの更新


1 次元テンソルへの要素の追加

この例では、1 次元テンソルに値を追加する方法を示します。

import torch

# 入力テンソルを作成する
input = torch.tensor([1, 2, 3, 4, 5])

# 追加する要素のインデックスを作成する
index = torch.tensor([2, 4])

# 追加する要素の値を作成する
src = torch.tensor([3, 5])

# scatter_add 関数を実行する
output = input.scatter_add(0, index, src)

# 出力テンソルを出力する
print(output)
tensor([1, 2, 5, 4, 8])

2 次元テンソルへの要素の追加

import torch

# 入力テンソルを作成する
input = torch.tensor([[1, 2, 3],
                       [4, 5, 6],
                       [7, 8, 9]])

# 追加する要素のインデックスを作成する
index = torch.tensor([[1, 0],
                       [2, 2]])

# 追加する要素の値を作成する
src = torch.tensor([10, 20, 30])

# scatter_add 関数を実行する
output = input.scatter_add(1, index, src)

# 出力テンソルを出力する
print(output)
tensor([[1, 12, 3],
       [4, 5, 6],
       [7, 8, 30]])

スパーステンソルの更新

この例では、スパーステンソルを更新する方法を示します。

import torch
from scipy.sparse import coo_matrix

# スパーステンソルを作成する
data = [1, 2, 3]
rows = [0, 1, 2]
cols = [0, 2, 1]
sp_tensor = coo_matrix((data, (rows, cols)), shape=(3, 3))

# Tensorに変換する
tensor = torch.from_numpy(sp_tensor.toarray())

# 追加する要素のインデックスを作成する
index = torch.tensor([1, 2])

# 追加する要素の値を作成する
src = torch.tensor([10, 20])

# scatter_add 関数を実行する
output = tensor.scatter_add(1, index, src)

# スパーステンソルに変換する
sp_output = sp_tensor.from_scipy_sparse_matrix(output)

# スパーステンソルを出力する
print(sp_output)
(0, 0)	1.0
(0, 2)	10.0
(1, 0)	2.0
(1, 1)	0.0
(1, 2)	3.0
(2, 0)	3.0
(2, 1)	0.0
(2, 2)	20.0

この例では、ヒストグラムを更新する方法を示します。

import torch

# ヒストグラムを作成する
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
hist, _ = torch.histogram(torch.tensor(data))

# 更新するビンインデックスを作成する
index = torch.tensor([3, 5])

# 更新値を作成する
values = torch.tensor([5, 2])

# scatter_add 関数を実行する
hist = hist.scatter_add(0, index, values)

# ヒストグラムを出力する
print(hist)
tensor([0, 0, 0, 5, 2, 3, 0, 0, 0, 1])


手動ループ

最も基本的な代替手段は、手動ループを使用して要素を散布的に追加することです。 これは単純ですが、時間がかかり、コードが冗長になる可能性があります。

import torch

def scatter_add_manual(input, dim, index, src):
    output = input.clone()
    for i, j, v in zip(index, index, src):
        output[i, j] += v
    return output

# 入力テンソルを作成する
input = torch.tensor([1, 2, 3, 4, 5])

# 追加する要素のインデックスを作成する
index = torch.tensor([2, 4])

# 追加する要素の値を作成する
src = torch.tensor([3, 5])

# 手動ループでscatter_addを実行する
output = scatter_add_manual(input, 0, index, src)

# 出力テンソルを出力する
print(output)
tensor([1, 2, 5, 4, 8])

torch.index_add

torch.index_add 関数は、指定されたインデックス位置の要素にテンソルを追加します。 これは scatter_add よりも汎用性が高く、テンソルだけでなくスカラー値も追加できます。

import torch

# 入力テンソルを作成する
input = torch.tensor([1, 2, 3, 4, 5])

# 追加する要素のインデックスを作成する
index = torch.tensor([2, 4])

# 追加する要素の値を作成する
src = torch.tensor([3, 5])

# index_add 関数を実行する
output = input.index_add(0, index, src)

# 出力テンソルを出力する
print(output)
tensor([1, 2, 5, 4, 8])

torch.bincount

torch.bincount 関数は、ヒストグラムを計算するために使用できます。 これは、ヒストグラムを更新する必要がある場合に便利です。

import torch

# ヒストグラムを作成する
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
counts = torch.bincount(torch.tensor(data))

# 更新するビンインデックスを作成する
index = torch.tensor([3, 5])

# 更新値を作成する
values = torch.tensor([5, 2])

# countsを散布的に追加する
counts = counts.scatter_add_(0, index, values)

# ヒストグラムを出力する
print(counts)
tensor([0, 0, 0, 5, 2, 3, 0, 0, 0, 1])

NumPy

PyTorch テンソルを NumPy 配列に変換し、NumPy 関数を使用して要素を散布的に追加してから、PyTorch テンソルに戻すこともできます。 これは、特に小さなテンソルを扱う場合に役立ちます。

import torch
import numpy as np

# 入力テンソルを作成する
input = torch.tensor([1, 2, 3, 4, 5])

# 追加する要素のインデックスを作成する
index = torch.tensor([2, 4]).numpy()

# 追加する要素の値を作成する
src = torch.tensor([3, 5]).numpy()

# NumPy配列に変換する
input_array = input.numpy()

# NumPyでscatter_addを実行する
input_array[index] += src

# PyTorch テンソルに戻す
output = torch.from_numpy(input_array)

# 出力テンソルを出力する
print(output)
tensor([1, 2, 5, 4, 8])