web-dev-qa-db-ja.com

PyTorchでバッチ入力LSTMネットワークを正しく実装する方法は?

PyTorchのこの release は、リカレントニューラルネットワークの可変長入力にPackedSequenceを提供するようです。ただし、正しく使用するのは少し難しいことがわかりました。

pad_packed_sequenceを使用してpack_padded_sequenceによって供給されたRNNレイヤーの出力を回復すると、T x B x Nテンソルoutputsが得られました。ここで、Tは最大時間ですステップ、Bはバッチサイズ、Nは非表示サイズです。バッチ内の短いシーケンスの場合、後続の出力はすべてゼロになることがわかりました。

ここに私の質問があります。

  1. すべてのシーケンスの最後の出力が必要な単一の出力タスクの場合、このテンソルには短いシーケンスの多くのゼロが含まれるため、単純なoutputs[-1]は間違った結果を返します。すべてのシーケンスの個々の最後の出力を取得するには、シーケンスの長さでインデックスを作成する必要があります。それを行う簡単な方法はありますか?
  2. 複数の出力タスク(例:seq2seq)の場合、通常、線形レイヤーN x Oを追加し、バッチ出力T x B x OTB x Oに再形成し、真のターゲットとのクロスエントロピー損失を計算しますTB(通常、言語モデルの整数)。この場合、バッチ出力のこれらのゼロは重要ですか?
14
stackunderflow

質問1-最終タイムステップ

これは、最後のタイムステップの出力を取得するために使用するコードです。もっと簡単な解決策があるかどうかはわかりません。もしそうなら、私はそれを知りたいです。この ディスカッション に従い、last_timestepメソッドの相対コードスニペットを取得しました。これは私の楽しみです。

class BaselineRNN(nn.Module):
    def __init__(self, **kwargs):
        ...

    def last_timestep(self, unpacked, lengths):
        # Index of the last output for each sequence.
        idx = (lengths - 1).view(-1, 1).expand(unpacked.size(0),
                                               unpacked.size(2)).unsqueeze(1)
        return unpacked.gather(1, idx).squeeze()

    def forward(self, x, lengths):
        embs = self.embedding(x)

        # pack the batch
        packed = pack_padded_sequence(embs, list(lengths.data),
                                      batch_first=True)

        out_packed, (h, c) = self.rnn(packed)

        out_unpacked, _ = pad_packed_sequence(out_packed, batch_first=True)

        # get the outputs from the last *non-masked* timestep for each sentence
        last_outputs = self.last_timestep(out_unpacked, lengths)

        # project to the classes using a linear layer
        logits = self.linear(last_outputs)

        return logits

質問2-マスクされたクロスエントロピー損失

はい、デフォルトではゼロが埋め込まれたタイムステップ(ターゲット)が重要です。ただし、それらをマスクするのは非常に簡単です。使用するPyTorchのバージョンに応じて、2つのオプションがあります。

  1. PyTorch 0.2.0 :pytorchは、ignore_index引数を使用して、 CrossEntropyLoss で直接マスキングをサポートします。たとえば、言語モデリングまたはseq2seqで、ゼロパディングを追加する場合、次のようにゼロパディングワード(ターゲット)をマスクします。

    loss_function = nn.CrossEntropyLoss(ignore_index = 0)

  2. PyTorch 0.1.12 以前:PyTorchの古いバージョンでは、マスキングはサポートされていなかったため、独自の回避策を実装する必要がありました。私が使用した解決策は、 jihunchoi による masked_cross_entropy.py でした。この ディスカッション にも興味があるかもしれません。

8

数日前、私は このメソッド を見つけました。これは、インデックス付けを使用して、ワンライナーで同じタスクを実行します。

最初にデータセットバッチ(_[batch size, sequence length, features]_)があるので、私にとっては:

_unpacked_out = unpacked_out[np.arange(unpacked_out.shape[0]), lengths - 1, :]
_

ここで、_unpacked_out_は_torch.nn.utils.rnn.pad_packed_sequence_の出力です。

クリストスバジオティスが上記で使用しているlast_timestep()メソッドに似ている ここで説明 メソッドと比較しました(また here を推奨)。私の場合、結果は同じです。

0
n8henrie