行列のすべての列に少なくとも「e」個の非ゼロ要素があることを確認してください

行列の各列に少なくとも 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