行列の各列に少なくとも e
個のゼロ以外の要素が含まれていることを確認し、ゼロ値の要素を値 y でランダムに置き換えない各列について確認したいと思います
列に e
以外の要素が含まれるまで。一部の列に 0、1、または 2 つの要素がある次の行列を考えてみましょう。操作後、各列には値 y
の少なくとも e
個の要素が必要です。
前
tensor([[0, 7, 0, 0],
[0, 0, 0, 0],
[0, 1, 0, 4]], dtype=torch.int32)
後、e = 2
tensor([[y, 7, 0, y],
[y, 0, y, 0],
[0, 1, y, 4]], dtype=torch.int32)
私は非常に遅くて単純なループベースのソリューションを持っています:
def scatter_elements(x, e, y):
for i in range(x.shape[1]):
col = x.data[:, i]
num_connections = col.count_nonzero()
to_add = torch.clip(e - 接続数, 0, なし)
インデックス = torch.where(col == 0)[0]
perm = torch.randperm(indices.shape[0])[:to_add]
col.data[インデックス[perm]] = y
ループなしでこれを行うことは可能ですか? torch.scatter
を使用して index
配列を最初に生成することを考えましたが、追加する要素の数が列ごとに異なるため、簡単な使用方法がわかりませんそれ。提案やヒントは大歓迎です!
編集: コメントに基づいてインデックスを入れ替え、タイトルと説明を更新しました。
各列に少なくとも e
要素があり、正確に e
要素がないことのみを気にする場合は、ループなしで実行できます。重要なのは、この場合、ゼロ以外のすべての値を置き換えて配列を作成し、この配列から列ごとに e 値をサンプリングできることです。
便宜上、x.shape = [a,b]
とします
replace
を作成します (つまり、すべての 0 が y
に置き換えられます)。x
と同じサイズのランダムな配列を作成します。torch.topk
を使用して、列ごとに k
個の最大乱数を取得します。これは、各列の k
ランダム インデックスを取得するために使用されます (あなたの場合は k
= e
)。 x
が非負の整数である場合、topk
操作の前にランダム配列に x
を追加して、既存の非ゼロの整数を確保できます。要素が最初に選択されます。これにより、e
個を超える接続が追加されないようにします。replace
の値に置き換えます。def scatter_elements(x,e,y):
x = x.float()
# 1. replace は x と同じ形状で、すべての 0 が y に置き換えられます
replace = torch.where(x > 0 , x, torch.ones(x.shape)*y)
# 2-3。列ごとにランダムなインデックスを取得する
randn = torch.rand(x.shape)
if True: # 変更自体が列に e 個を超える要素をゼロ以外の値に割り当てたくない場合は True
randn += x # x が非負の整数であると仮定します
ind = torch.topk(randn,e,dim = 0)[1] # 最初の戻り値は値、2 番目の戻り値はインデックスです
# 2 番目のインデックスを作成して、ind の各インデックスが対応する列を示します
col_ind = torch.arange(x.shape[1]).unsqueeze(0).expand(ind.shape)
# 4. ind と col_ind で x にインデックスを付け、replace でこれらの値を対応する値に設定します。
ind = ind.reshape(-1) # 行のインデックスに使用できるように 1D 配列にフラット化します
col_ind = col_ind.reshape(-1) # 列のインデックスに使用できるように 1D 配列にフラット化します
x[ind,col_ind] = replace[ind,col_ind]
×を返す
私の限られたタイミング テストでは、ベクトル化されたソリューションは、元のループ ソリューションよりも約 5 ~ 6 倍高速でした。
元のループベースのソリューションをさらに実験した結果、すでにベクトル化されたバージョンにかなり近づいていることに気付きました:
def scatter_elements_vec(x, e, y):
rand = torch.rand_like(x, dtype=torch.float)
rand[x != 0] = torch.inf
num_connections = x.count_nonzero(0)
to_add = torch.clip(e - 接続数, 0, なし)
mask = torch.argsort(rand, 0) < to_add
x[マスク] = y