自然言語関係のメモ
265 views
LSTMを使用した文書要約は、その名の通り、長いテキストや文書を短い要約に変換するタスクです。このアプローチは主に2つのカテゴリに分けられます: 抽出型要約と生成型要約。
抽出型要約:
生成型要約:
LSTMを使用した生成型文書要約の基本的な手順は以下のとおりです:
エンコーダの設計:
デコーダの設計:
Attention Mechanismの導入:
モデルの訓練:
予測とビームサーチ:
LSTMに基づく生成型の文書要約は、実際のアプリケーションにおいて良好な結果をもたらすことが多いですが、近年の研究では、TransformerアーキテクチャやBERT、GPTなどのモデルが、さらに高い性能を達成するための方法として注目を浴びています。
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/