自然言語処理

自然言語関係のメモ

265 views

LSTMを使った文章の要約について

LSTMを使用した文書要約は、その名の通り、長いテキストや文書を短い要約に変換するタスクです。このアプローチは主に2つのカテゴリに分けられます: 抽出型要約と生成型要約。

  1. 抽出型要約:

    • 原文からの文やフレーズを選択して要約を作成します。
    • LSTMやその他のRNNが、各文やフレーズが要約に適しているかどうかを判断するためのスコアを予測するモデルとして使用されることがあります。
  2. 生成型要約:

    • 原文の内容を基にして、新しい文を生成して要約を作成します。
    • これはseq2seqモデル(エンコーダとデコーダの組み合わせ)を使用して行われることが多いです。エンコーダは原文を固定長のベクトルにエンコードし、デコーダはそのベクトルを使用して要約を生成します。

LSTMを使用した生成型文書要約の基本的な手順は以下のとおりです:

  1. エンコーダの設計:

    • エンコーダは、文書の各単語をエンコードし、隠れ状態を出力します。
    • 通常、BiLSTM(双方向LSTM)がエンコーダとして使用されることが多いです。
  2. デコーダの設計:

    • デコーダは、エンコーダの出力と前のタイムステップの隠れ状態を基に、次の単語を予測します。
    • ここでもLSTMが使用されます。
  3. Attention Mechanismの導入:

    • Attentionメカニズムは、デコーダがエンコーダの出力の重要な部分に「注意」を払うことを可能にします。
    • これにより、デコーダは要約生成の各ステップで、原文の関連する部分を考慮することができます。
  4. モデルの訓練:

    • 原文とその対応する参照要約のペアを用いてモデルを訓練します。
    • 損失関数として、通常、クロスエントロピー損失が使用されます。
  5. 予測とビームサーチ:

    • 生成時には、最も確率の高い単語を選択する代わりに、ビームサーチという手法を使用して、複数の要約候補を考慮することが一般的です。

LSTMに基づく生成型の文書要約は、実際のアプリケーションにおいて良好な結果をもたらすことが多いですが、近年の研究では、TransformerアーキテクチャやBERT、GPTなどのモデルが、さらに高い性能を達成するための方法として注目を浴びています。

LSTMを使ったseq2seqのサンプルプログラム

import torch
import torch.nn as nn
import torch.optim as optim
from janome.tokenizer import Tokenizer
from collections import defaultdict

# 前処理
tokenizer = Tokenizer()

def tokenize(text):
    return [tok for tok in tokenizer.tokenize(text, wakati=True)]

def build_vocab(data):
    word_freq = defaultdict(int)
    for sentence in data:
        for word in sentence:
            word_freq[word] += 1

    vocab = sorted(word_freq, key=word_freq.get, reverse=True)
    vocab = ['<unk>', '<pad>', '<sos>', '<eos>'] + vocab
    word2id = {word: i for i, word in enumerate(vocab)}
    return vocab, word2id

# モデル定義
class Encoder(nn.Module):
    def __init__(self, input_dim, emb_dim, hidden_dim):
        super(Encoder, self).__init__()
        self.embedding = nn.Embedding(input_dim, emb_dim)
        self.rnn = nn.LSTM(emb_dim, hidden_dim)

    def forward(self, src):
        embedded = self.embedding(src)
        outputs, (hidden, cell) = self.rnn(embedded)
        return hidden, cell

class Decoder(nn.Module):
    def __init__(self, output_dim, emb_dim, hidden_dim):
        super(Decoder, self).__init__()
        self.embedding = nn.Embedding(output_dim, emb_dim)
        self.rnn = nn.LSTM(emb_dim, hidden_dim)
        self.out = nn.Linear(hidden_dim, output_dim)

    def forward(self, input, hidden, cell):
        input = input.unsqueeze(0)
        embedded = self.embedding(input)
        output, (hidden, cell) = self.rnn(embedded, (hidden, cell))
        prediction = self.out(output.squeeze(0))
        return prediction, hidden, cell

# トレーニングルーチン
def train(encoder, decoder, iterator, optimizer, criterion, device):
    encoder.train()
    decoder.train()

    total_loss = 0

    for src, tgt in iterator:
        src, tgt = torch.tensor(src).to(device), torch.tensor(tgt).to(device)
        optimizer.zero_grad()

        hidden, cell = encoder(src)

        tgt_len = tgt.shape[0]
        batch_size = tgt.shape[1]
        outputs = torch.zeros(tgt_len, batch_size, OUTPUT_DIM).to(device)

        input = tgt[0,:]

        for t in range(1, tgt_len):
            output, hidden, cell = decoder(input, hidden, cell)
            outputs[t] = output
            top1 = output.argmax(1)
            input = tgt[t] if random.random() > teacher_forcing_ratio else top1

        loss = criterion(outputs[1:].view(-1, OUTPUT_DIM), tgt[1:].view(-1))
        loss.backward()
        optimizer.step()
        total_loss += loss.item()

    return total_loss / len(iterator)

# データ
src_data = ['これはテスト文です。', 'seq2seqは面白いモデルです。']
tgt_data = ['<sos> テスト文 <eos>', '<sos> seq2seqモデル <eos>']

src_tokenized = [tokenize(text) for text in src_data]
tgt_tokenized = [tokenize(text) for text in tgt_data]

vocab, word2id = build_vocab(src_tokenized + tgt_tokenized)
id2word = {i: word for word, i in word2id.items()}

# ID変換
src_id_data = [[word2id.get(word, 0) for word in sentence] for sentence in src_tokenized]
tgt_id_data = [[word2id.get(word, 0) for word in sentence] for sentence in tgt_tokenized]

# パラメータ
INPUT_DIM = len(vocab)
OUTPUT_DIM = len(vocab)
ENC_EMB_DIM = 256
DEC_EMB_DIM = 256
HID_DIM = 512
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
teacher_forcing_ratio = 0.5

encoder = Encoder(INPUT_DIM, ENC_EMB_DIM, HID_DIM).to(device)
decoder = Decoder(OUTPUT_DIM, DEC_EMB_DIM, HID_DIM).to(device)

optimizer = optim.Adam(list(encoder.parameters()) + list(decoder.parameters()))
criterion = nn.CrossEntropyLoss(ignore_index=word2id['<pad>'])

# トレーニング
N_EPOCHS = 100
for epoch in range(N_EPOCHS):
    iterator = list(zip(src_id_data, tgt_id_data))
    loss = train(encoder, decoder, iterator, optimizer, criterion, device)
    print(f"Epoch: {epoch+1:02}, Loss: {loss:.4f}")

Page 5 of 6.

前のページ 次のページ



[添付ファイル]


お問い合わせ

プロフィール

すぺぺぺ

自己紹介

本サイトの作成者。
プログラムは趣味と勉強を兼ねて、のんびり本サイトを作っています。
フレームワークはdjango。
ChatGPTで自動プログラム作成に取り組み中。

サイト/ブログ

https://www.osumoi-stdio.com/novel/

ツイッター

@darkimpact0626