[PYTHON] [PyTorch] Japanische Satzgenerierung mit Transformer

Einführung

In diesem Artikel möchten wir ein Modell erstellen, das automatisch einen Roman generiert, indem ein beliebiger Titel mit einem Paar aus neuartigem Titel und Text eingegeben wird.

Vorbereitungen

Google Colaboratory wird als Analyseinfrastruktur verwendet. Google Colaboratory ist eine von Google bereitgestellte Cloud-Notebook-Umgebung, die jeder mit einem Google-Konto kostenlos nutzen kann. Die für die Datenanalyse erforderliche Bibliothek ist nicht nur im Voraus vorbereitet, sondern kann auch mit der GPU verwendet werden. Es wird daher dringend empfohlen, wenn Sie etwas auf Ihrem Notebook-PC ausprobieren möchten. Weitere Informationen zum Einrichten von Google Colaboratory finden Sie in diesem Artikel.

Öffnen Sie nach dem Setup das Notizbuch für diese Analyse und führen Sie den folgenden Befehl aus, um die Bibliothek zu installieren, die nicht im Voraus installiert wurde.

!pip install PyDrive
!pip install janome
!pip install mojimoji

Daten bekommen

Die neuen Daten stammen von Github von Aozora Bunko. Kopieren Sie zunächst das Ziel-Repository in Ihr eigenes Google Drive. Sie können auch die folgenden Befehle in Google Colaboratory ausführen.

!git clone --branch master --depth 1 https://github.com/aozorabunko/aozorabunko.git "drive/My Drive/Beliebiges Verzeichnis"

Extrahieren Sie anschließend die für die Modellkonstruktion verwendeten Daten aus den kopierten Dateien und formatieren Sie sie. Dieses Mal werde ich einen Roman verwenden, dessen Text weniger als 3.000 Zeichen enthält. Außerdem bezieht sich "ID auf Laufwerk" im Code auf die Zeichenfolge unter "Ordner", die in der URL des Zielverzeichnisses enthalten ist.

#----------------------
#Holen Sie sich eine Liste der Zieldateien
#----------------------
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
from google.colab import auth
from oauth2client.client import GoogleCredentials
import pandas as pd

#Ermöglichen Sie den Zugriff auf Google Drive
auth.authenticate_user()
gauth = GoogleAuth()
gauth.credentials = GoogleCredentials.get_application_default()
drive = GoogleDrive(gauth)

#Holen Sie sich den Titel aller Werke und ID auf Laufwerk
def get_list_file_recursively(parent_id, l=None):
    if l is None:
        l = []

    file_list = drive.ListFile({'q': '"{}" in parents and trashed = false'.format(parent_id)}).GetList()
    l += [f for f in file_list if f['mimeType'] != 'application/vnd.google-apps.folder']

    for f in file_list:
        if f['mimeType'] == 'application/vnd.google-apps.folder':
            get_list_file_recursively(f['id'], l)

    return l

listed = []
for f in get_list_file_recursively('Die ID auf dem Laufwerk des Verzeichnisses der obersten Ebene des kopierten Repositorys'):
  print(f['title'])
  if 'html' in f['title'] and 'card' not in f['title']:
    list = [f['title'], f['id']]
    listed.append(list)
listed = pd.DataFrame(listed)

#----------------------
#Erwerb von Titel / Text
#----------------------
from bs4 import BeautifulSoup

#Lesen Sie die HTML-Datei der Liste
Stories = []
for i in range(0, len(listed)):
    if i % 100 == 0:
        print('{} / {}'.format(i, len(listed)))

    #Identifizieren Sie Dateien anhand der Listen-ID
    file_data = drive.CreateFile({'id': listed.iloc[i, 2]})
    file_data.GetContentFile(listed.iloc[i, 1])
    with open(listed.iloc[i, 1], 'rb') as html:
        soup = BeautifulSoup(html, 'lxml')

    #Titel / Text abrufen
    title = soup.find("h1", class_='title')
    main_text = soup.find("div", class_='main_text')

    #Wenn der Titel oder Text fehlt, überspringen Sie ihn
    if title == None or main_text == None:
        continue

    #Rubin entfernen
    for yomigana in main_text.find_all(["rp", "h4", "rt"]):
        yomigana.decompose()

    #Formatieren und in Zeichenfolge konvertieren
    title = [line.strip() for line in 
    title.text.strip().splitlines()]
    main_text = [line.strip() for line in 
    main_text.text.strip().splitlines()]
    title = ''.join(title)
    text=''.join(main_text)

    #Der Text ist 3,Eingrenzen auf Arbeiten innerhalb von 000 Zeichen
    if len(text) <= 3000:
        Stories.append([title, text])

#Als CSV speichern
Stories = pd.DataFrame(Stories)
Stories.to_csv('drive/My Drive/Stories.csv', index=False, header=False)

Speichern Sie schließlich eine zufällige Aufteilung von 80% der Daten für das Training und 20% für das Testen.

#----------------------
#Datenaufteilung
#----------------------
from sklearn.model_selection import KFold
kf = KFold(n_splits=5, shuffle=True, random_state=12345)
tr_idx, te_idx = list(kf.split(df))[0]

train = df.iloc[tr_idx, :]
test = df.iloc[te_idx, :]
train.to_csv('drive/My Drive/train.csv', index=False, header=False)
test.to_csv('drive/My Drive/test.csv', index=False, header=False)

Vorbereitung der Trainingsdaten

Verwenden Sie torchtext, um die Daten zu lesen. Eine grundlegende Erklärung zu torchtext finden Sie in diesem Artikel.

Definition der Vorverarbeitung

Definieren Sie zunächst die Vorverarbeitungsfunktion, die beim Lesen von Daten mit torchtext angewendet werden soll. Verwenden Sie Janome für die morphologische Analyse.

#----------------------
#Definition der Vorverarbeitung
#----------------------
from torchtext import data
from janome.tokenizer import Tokenizer
import re
import mojimoji

#Definition der Zeichenfolgenverarbeitung
def preprocessing(text):
    #Entfernen Sie Zeilenumbrüche, Leerzeichen halber Breite und Leerzeichen voller Breite
    text = re.sub('\r', '', text)
    text = re.sub('\n', '', text)
    text = re.sub(' ', '', text)
    text = re.sub(' ', '', text)
    #Einheitliche "0" für Zahlenzeichen
    text = re.sub(r'[0-9 0-9]', '0', text) 
    #Vollwinkel
    text = mojimoji.han_to_zen(text)
    return text

#Tokenizer-Definition
j_t = Tokenizer()
def tokenizer(text):
    return [tok for tok in j_t.tokenize(text, wakati=True)]

#String-Verarbeitung+ Tokenizer
def tokenizer_with_preprocessing(text):
    text = preprocessing(text)
    text = tokenizer(text)
    return text

Torchtext-Einstellungen

Als nächstes stellen Sie die Lesemethode mit torchtext ein.

#----------------------
#Felddefinition
#----------------------
TEXT = data.Field(
    sequential=True, 
    init_token='<sos>', 
    eos_token='<eos>', 
    tokenize=tokenizer_with_preprocessing, 
    lower=True, 
    use_vocab=True, 
    batch_first=True
)

Daten lesen

Lesen Sie die zum Lernen und Testen unterteilte CSV-Datei und erstellen Sie ein Vokabularwörterbuch.

#----------------------
#Daten lesen
#----------------------
train_ds, test_ds = data.TabularDataset.splits(
    path='drive/My Drive',
    train='train.csv',
    test='test.csv',
    format='csv',
    skip_header=False,
    fields=[('title', TEXT), ('text', TEXT)]
)

#Bestätigung
train_ds[0].__dict__.keys()
test_ds[0].__dict__.keys()
for i in range(0, 10):
    print(vars(train_ds[i]))
    print(vars(test_ds[i]))

#Erstellen Sie ein Wörterbuch
TEXT.build_vocab(train_ds, test_ds, min_freq=2)

#Wortzahl
print(TEXT.vocab.freqs)
print('Anzahl der Vokabeln:{}'.format(len(TEXT.vocab)))

#Iterator erstellen
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 
# -->Wählen Sie die GPU vorab unter "Laufzeittyp ändern" aus.
train_iter = data.Iterator(train_ds, batch_size=16, shuffle=True, device=device)
test_iter = data.Iterator(test_ds, batch_size=16, shuffle=False, device=device)

#Bestätigung
batch = next(iter(train_iter))
print(batch.title)
print(batch.text)

batch = next(iter(test_iter))
print(batch.title)
print(batch.text)

Modellbau

Netzwerkdefinition

Transformator implementieren. Dieser Artikel erklärt nicht, wie Transformer funktioniert, aber auf Japanisch ist es dieser Artikel und auf Englisch ist es [dieser Artikel](http: // jalammar). .github.io / Illustrated-Transformator /) ist sehr einfach zu verstehen. Informationen zur Implementierung finden Sie auch unter hier. Es war. Die Bedeutung der einzelnen Prozesse wird ebenfalls ausführlich erläutert. Lesen Sie sie daher bitte, bevor Sie es versuchen.

Definieren Sie zunächst einen Encoder, der den Titel des Romans vektorisiert.

import torch
from torch import nn

class Encoder(nn.Module):
    def __init__(self, 
                 input_dim, 
                 hid_dim, 
                 n_layers, 
                 n_heads, 
                 pf_dim,
                 dropout, 
                 device,
                 max_length=100):
        super().__init__()

        self.device = device
        
        self.tok_embedding = nn.Embedding(input_dim, hid_dim)
        self.pos_embedding = nn.Embedding(max_length, hid_dim)
        
        self.layers = nn.ModuleList([EncoderLayer(hid_dim, 
                                                  n_heads, 
                                                  pf_dim,
                                                  dropout, 
                                                  device) 
                                     for _ in range(n_layers)])
        
        self.dropout = nn.Dropout(dropout)
        
        self.scale = torch.sqrt(torch.FloatTensor([hid_dim])).to(device)
        
    def forward(self, src, src_mask):
        
        #src = [batch size, src len]
        #src_mask = [batch size, src len]
        
        batch_size = src.shape[0]
        src_len = src.shape[1]
        
        pos = torch.arange(0, src_len).unsqueeze(0).repeat(batch_size, 1).to(self.device)
        
        #pos = [batch size, src len]
        
        src = self.dropout((self.tok_embedding(src) * self.scale) + self.pos_embedding(pos))
        
        #src = [batch size, src len, hid dim]
        
        for layer in self.layers:
            src = layer(src, src_mask)
            
        #src = [batch size, src len, hid dim]
            
        return src


class EncoderLayer(nn.Module):
    def __init__(self, 
                 hid_dim, 
                 n_heads, 
                 pf_dim,  
                 dropout, 
                 device):
        super().__init__()
        
        self.layer_norm = nn.LayerNorm(hid_dim)
        self.self_attention = MultiHeadAttentionLayer(hid_dim, n_heads, dropout, device)
        self.positionwise_feedforward = PositionwiseFeedforwardLayer(hid_dim, pf_dim, dropout)
        self.dropout = nn.Dropout(dropout)
        
    def forward(self, src, src_mask):
        
        #src = [batch size, src len, hid dim]
        #src_mask = [batch size, src len]
                
        #self attention
        _src, _ = self.self_attention(src, src, src, src_mask)
        
        #dropout, residual connection and layer norm
        src = self.layer_norm(src + self.dropout(_src))
        
        #src = [batch size, src len, hid dim]
        
        #positionwise feedforward
        _src = self.positionwise_feedforward(src)
        
        #dropout, residual and layer norm
        src = self.layer_norm(src + self.dropout(_src))
        
        #src = [batch size, src len, hid dim]
        
        return src


class MultiHeadAttentionLayer(nn.Module):
    def __init__(self, hid_dim, n_heads, dropout, device):
        super().__init__()
        
        assert hid_dim % n_heads == 0
        
        self.hid_dim = hid_dim
        self.n_heads = n_heads
        self.head_dim = hid_dim // n_heads
        
        self.fc_q = nn.Linear(hid_dim, hid_dim)
        self.fc_k = nn.Linear(hid_dim, hid_dim)
        self.fc_v = nn.Linear(hid_dim, hid_dim)
        
        self.fc_o = nn.Linear(hid_dim, hid_dim)
        
        self.dropout = nn.Dropout(dropout)
        
        self.scale = torch.sqrt(torch.FloatTensor([self.head_dim])).to(device)
        
    def forward(self, query, key, value, mask = None):
        
        batch_size = query.shape[0]
        
        #query = [batch size, query len, hid dim]
        #key = [batch size, key len, hid dim]
        #value = [batch size, value len, hid dim]
                
        Q = self.fc_q(query)
        K = self.fc_k(key)
        V = self.fc_v(value)
        
        #Q = [batch size, query len, hid dim]
        #K = [batch size, key len, hid dim]
        #V = [batch size, value len, hid dim]
                
        Q = Q.view(batch_size, -1, self.n_heads, self.head_dim).permute(0, 2, 1, 3)
        K = K.view(batch_size, -1, self.n_heads, self.head_dim).permute(0, 2, 1, 3)
        V = V.view(batch_size, -1, self.n_heads, self.head_dim).permute(0, 2, 1, 3)
        
        #Q = [batch size, n heads, query len, head dim]
        #K = [batch size, n heads, key len, head dim]
        #V = [batch size, n heads, value len, head dim]
                
        energy = torch.matmul(Q, K.permute(0, 1, 3, 2)) / self.scale
        
        #energy = [batch size, n heads, seq len, seq len]
        
        if mask is not None:
            energy = energy.masked_fill(mask == 0, -1e10)
        
        attention = torch.softmax(energy, dim = -1)
                
        #attention = [batch size, n heads, query len, key len]
        
        x = torch.matmul(self.dropout(attention), V)
        
        #x = [batch size, n heads, seq len, head dim]
        
        x = x.permute(0, 2, 1, 3).contiguous()
        
        #x = [batch size, seq len, n heads, head dim]
        
        x = x.view(batch_size, -1, self.hid_dim)
        
        #x = [batch size, seq len, hid dim]
        
        x = self.fc_o(x)
        
        #x = [batch size, seq len, hid dim]
        
        return x, attention


class PositionwiseFeedforwardLayer(nn.Module):
    def __init__(self, hid_dim, pf_dim, dropout):
        super().__init__()
        
        self.fc_1 = nn.Linear(hid_dim, pf_dim)
        self.fc_2 = nn.Linear(pf_dim, hid_dim)
        
        self.dropout = nn.Dropout(dropout)
        
    def forward(self, x):
        
        #x = [batch size, seq len, hid dim]
        
        x = self.dropout(torch.relu(self.fc_1(x)))
        
        #x = [batch size, seq len, pf dim]
        
        x = self.fc_2(x)
        
        #x = [batch size, seq len, hid dim]
        
        return x

Definieren Sie als Nächstes einen Decoder, der den Titelvektor verwendet und den Hauptteil des Romans generiert.

class Decoder(nn.Module):
    def __init__(self, 
                 output_dim, 
                 hid_dim, 
                 n_layers, 
                 n_heads, 
                 pf_dim, 
                 dropout, 
                 device,
                 max_length=1000):
        super().__init__()
        
        self.device = device
        
        self.tok_embedding = nn.Embedding(output_dim, hid_dim)
        self.pos_embedding = nn.Embedding(max_length, hid_dim)
        
        self.layers = nn.ModuleList([DecoderLayer(hid_dim, 
                                                  n_heads, 
                                                  pf_dim, 
                                                  dropout, 
                                                  device)
                                     for _ in range(n_layers)])
        
        self.fc_out = nn.Linear(hid_dim, output_dim)
        
        self.dropout = nn.Dropout(dropout)
        
        self.scale = torch.sqrt(torch.FloatTensor([hid_dim])).to(device)
        
    def forward(self, trg, enc_src, trg_mask, src_mask):
        
        #trg = [batch size, trg len]
        #enc_src = [batch size, src len, hid dim]
        #trg_mask = [batch size, trg len]
        #src_mask = [batch size, src len]
                
        batch_size = trg.shape[0]
        trg_len = trg.shape[1]
        
        pos = torch.arange(0, trg_len).unsqueeze(0).repeat(batch_size, 1).to(self.device)
                            
        #pos = [batch size, trg len]
            
        trg = self.dropout((self.tok_embedding(trg) * self.scale) + self.pos_embedding(pos))
                
        #trg = [batch size, trg len, hid dim]
        
        for layer in self.layers:
            trg, attention = layer(trg, enc_src, trg_mask, src_mask)
        
        #trg = [batch size, trg len, hid dim]
        #attention = [batch size, n heads, trg len, src len]
        
        output = self.fc_out(trg)
        
        #output = [batch size, trg len, output dim]
            
        return output, attention


class DecoderLayer(nn.Module):
    def __init__(self, 
                 hid_dim, 
                 n_heads, 
                 pf_dim, 
                 dropout, 
                 device):
        super().__init__()
        
        self.layer_norm = nn.LayerNorm(hid_dim)
        self.self_attention = MultiHeadAttentionLayer(hid_dim, n_heads, dropout, device)
        self.encoder_attention = MultiHeadAttentionLayer(hid_dim, n_heads, dropout, device)
        self.positionwise_feedforward = PositionwiseFeedforwardLayer(hid_dim, pf_dim, dropout)
        self.dropout = nn.Dropout(dropout)
        
    def forward(self, trg, enc_src, trg_mask, src_mask):
        
        #trg = [batch size, trg len, hid dim]
        #enc_src = [batch size, src len, hid dim]
        #trg_mask = [batch size, trg len]
        #src_mask = [batch size, src len]
        
        #self attention
        _trg, _ = self.self_attention(trg, trg, trg, trg_mask)
        
        #dropout, residual connection and layer norm
        trg = self.layer_norm(trg + self.dropout(_trg))
            
        #trg = [batch size, trg len, hid dim]
            
        #encoder attention
        _trg, attention = self.encoder_attention(trg, enc_src, enc_src, src_mask)
        
        #dropout, residual connection and layer norm
        trg = self.layer_norm(trg + self.dropout(_trg))
                    
        #trg = [batch size, trg len, hid dim]
        
        #positionwise feedforward
        _trg = self.positionwise_feedforward(trg)
        
        #dropout, residual and layer norm
        trg = self.layer_norm(trg + self.dropout(_trg))
        
        #trg = [batch size, trg len, hid dim]
        #attention = [batch size, n heads, trg len, src len]
        
        return trg, attention

Schließen Sie zum Schluss den Encoder und den Decoder an, um den Transformator fertigzustellen.

class Seq2Seq(nn.Module):
    def __init__(self, 
                 encoder, 
                 decoder, 
                 src_pad_idx, 
                 trg_pad_idx, 
                 device):
        super().__init__()
        
        self.encoder = encoder
        self.decoder = decoder
        self.src_pad_idx = src_pad_idx
        self.trg_pad_idx = trg_pad_idx
        self.device = device
        
    def make_src_mask(self, src):
        
        #src = [batch size, src len]
        
        src_mask = (src != self.src_pad_idx).unsqueeze(1).unsqueeze(2)

        #src_mask = [batch size, 1, 1, src len]

        return src_mask
    
    def make_trg_mask(self, trg):
        
        #trg = [batch size, trg len]
        
        trg_pad_mask = (trg != self.trg_pad_idx).unsqueeze(1).unsqueeze(3)
        
        #trg_pad_mask = [batch size, 1, trg len, 1]
        
        trg_len = trg.shape[1]
        
        trg_sub_mask = torch.tril(torch.ones((trg_len, trg_len), device = self.device)).bool()
        
        #trg_sub_mask = [trg len, trg len]
            
        trg_mask = trg_pad_mask & trg_sub_mask
        
        #trg_mask = [batch size, 1, trg len, trg len]
        
        return trg_mask

    def forward(self, src, trg):
        
        #src = [batch size, src len]
        #trg = [batch size, trg len]
                
        src_mask = self.make_src_mask(src)
        trg_mask = self.make_trg_mask(trg)
        
        #src_mask = [batch size, 1, 1, src len]
        #trg_mask = [batch size, 1, trg len, trg len]
        
        enc_src = self.encoder(src, src_mask)
        
        #enc_src = [batch size, src len, hid dim]
                
        output, attention = self.decoder(trg, enc_src, trg_mask, src_mask)
        
        #output = [batch size, trg len, output dim]
        #attention = [batch size, n heads, trg len, src len]
        
        return output, attention

Obwohl diesmal nicht durchgeführt, ist es möglich, das Aufmerksamkeitsgewicht nach dem Lernen unter Verwendung der Aufmerksamkeit des Rückgabewerts zu visualisieren.

Modelllernen

Lerne das Modell. Im Referenzskript wird die Epoche gestoppt, indem die Genauigkeit der Validierungsdaten betrachtet wird. In diesem Versuch nahm die Genauigkeit der Validierungsdaten jedoch mit fortschreitendem Lernen ab der ersten Epoche ab, sodass eine Überanpassung ignoriert wurde und das Modell nach der letzten Epoche Wird angenommen.

#----------------------
#Vorbereitung zum Lernen
#----------------------
#Parametereinstellungen
INPUT_DIM = len(TEXT.vocab)
OUTPUT_DIM = len(TEXT.vocab)
HID_DIM = 256
ENC_LAYERS = 3
DEC_LAYERS = 3
ENC_HEADS = 8
DEC_HEADS = 8
ENC_PF_DIM = 512
DEC_PF_DIM = 512
ENC_DROPOUT = 0.1
DEC_DROPOUT = 0.1

#Encoder-Initialisierung
enc = Encoder(INPUT_DIM, 
              HID_DIM, 
              ENC_LAYERS, 
              ENC_HEADS, 
              ENC_PF_DIM, 
              ENC_DROPOUT, 
              device)

#Decoder-Initialisierung
dec = Decoder(OUTPUT_DIM, 
              HID_DIM, 
              DEC_LAYERS, 
              DEC_HEADS, 
              DEC_PF_DIM, 
              DEC_DROPOUT, 
              device)

#Angabe der ID für das Auffüllen
PAD_IDX = TEXT.vocab.stoi[TEXT.pad_token]

#Modellinitialisierung
model = Seq2Seq(enc, dec, PAD_IDX, PAD_IDX, device).to(device)

#Gewichtsinitialisierung
def initialize_weights(m):
    if hasattr(m, 'weight') and m.weight.dim() > 1:
        nn.init.xavier_uniform_(m.weight.data)
model.apply(initialize_weights)

#Optimierungseinstellungen
LEARNING_RATE = 0.0005
optimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)

#Einstellungen der Verlustfunktion
criterion = nn.CrossEntropyLoss(ignore_index=PAD_IDX)

#Definition der Lernfunktion
def train(model, iterator, optimizer, criterion, clip):
    
    model.train()
    
    epoch_loss = 0
    
    for i, batch in enumerate(iterator):
        
        src = batch.title
        trg = batch.text
        
        optimizer.zero_grad()
        
        output, _ = model(src, trg[:,:-1])
                
        #output = [batch size, trg len - 1, output dim]
        #trg = [batch size, trg len]
            
        output_dim = output.shape[-1]
            
        output = output.contiguous().view(-1, output_dim)
        trg = trg[:,1:].contiguous().view(-1)
                
        #output = [batch size * trg len - 1, output dim]
        #trg = [batch size * trg len - 1]
            
        loss = criterion(output, trg)
        
        loss.backward()
        
        torch.nn.utils.clip_grad_norm_(model.parameters(), clip)
        
        optimizer.step()
        
        epoch_loss += loss.item()
        
    return epoch_loss / len(iterator)

#Definition der Bewertungsfunktion
def evaluate(model, iterator, criterion):
    
    model.eval()
    
    epoch_loss = 0
    
    with torch.no_grad():
    
        for i, batch in enumerate(iterator):

            src = batch.title
            trg = batch.text

            output, _ = model(src, trg[:,:-1])
            
            #output = [batch size, trg len - 1, output dim]
            #trg = [batch size, trg len]
            
            output_dim = output.shape[-1]
            
            output = output.contiguous().view(-1, output_dim)
            trg = trg[:,1:].contiguous().view(-1)
            
            #output = [batch size * trg len - 1, output dim]
            #trg = [batch size * trg len - 1]
            
            loss = criterion(output, trg)

            epoch_loss += loss.item()
        
    return epoch_loss / len(iterator)

#Definition der Funktion zur Messung der Bearbeitungszeit
def epoch_time(start_time, end_time):
    elapsed_time = end_time - start_time
    elapsed_mins = int(elapsed_time / 60)
    elapsed_secs = int(elapsed_time - (elapsed_mins * 60))
    return elapsed_mins, elapsed_secs

#Definition der Satzgenerierungsfunktion
def translate_sentence(sentence, src_field, trg_field, model, device, max_len=1000):
    
    model.eval()
        
    tokens = [token.lower() for token in sentence]

    tokens = [src_field.init_token] + tokens + [src_field.eos_token]
        
    src_indexes = [src_field.vocab.stoi[token] for token in tokens]

    src_tensor = torch.LongTensor(src_indexes).unsqueeze(0).to(device)
    
    src_mask = model.make_src_mask(src_tensor)
    
    with torch.no_grad():
        enc_src = model.encoder(src_tensor, src_mask)

    trg_indexes = [trg_field.vocab.stoi[trg_field.init_token]]

    for i in range(max_len):

        trg_tensor = torch.LongTensor(trg_indexes).unsqueeze(0).to(device)

        trg_mask = model.make_trg_mask(trg_tensor)
        
        with torch.no_grad():
            output, attention = model.decoder(trg_tensor, enc_src, trg_mask, src_mask)
        
        pred_token = output.argmax(2)[:,-1].item()
        
        trg_indexes.append(pred_token)

        if pred_token == trg_field.vocab.stoi[trg_field.eos_token]:
            break
    
    trg_tokens = [trg_field.vocab.itos[i] for i in trg_indexes]
    
    return trg_tokens[1:], attention


#----------------------
#Modelllernen
#----------------------
import time
import math

N_EPOCHS = 100
CLIP = 1

#Holen Sie sich 1 Beispielarbeit
example_idx = 8
src_sample = vars(train_ds.examples[example_idx])['title']
trg_sample = vars(train_ds.examples[example_idx])['text']

#Titel und Text anzeigen
print(f'src = {src_sample}')
print(f'trg = {trg_sample}')

best_valid_loss = float('inf')

for epoch in range(N_EPOCHS):
    
    start_time = time.time()
    
    train_loss = train(model, train_iter, optimizer, criterion, CLIP)
    valid_loss = evaluate(model, test_iter, criterion)
    
    end_time = time.time()
    
    epoch_mins, epoch_secs = epoch_time(start_time, end_time)
    
    #if valid_loss < best_valid_loss:
    #    best_valid_loss = valid_loss
    #    torch.save(model.state_dict(), 'drive/My Drive/trained_model.pt')
    #Im Referenzskript wird die Genauigkeit der Validierungsdaten als Index verwendet. In diesem Versuch nahm die Genauigkeit der Validierungsdaten jedoch mit fortschreitendem Lernen ab, sodass eine Überanpassung ignoriert und das Modell nach der letzten Epoche übernommen wurde (unter Berücksichtigung des Endes des Prozesses). Speichern nach jeder Epoche)
    torch.save(model.state_dict(), 'drive/My Drive/trained_model.pt')

    #Zeigen Sie die Genauigkeit der Trainings- / Validierungsdaten für jede Epoche an
    print(f'Epoch: {epoch+1:02} | Time: {epoch_mins}m {epoch_secs}s')
    print(f'\tTrain Loss: {train_loss:.3f} | Train PPL: {math.exp(train_loss):7.3f}')
    print(f'\t Val. Loss: {valid_loss:.3f} |  Val. PPL: {math.exp(valid_loss):7.3f}')

    #Zeigen Sie das Ergebnis der Generierung des Textes aus dem Titel einer Beispielarbeit für jeweils 10 Epochen an
    if epoch % 10 == 0:
      translation, attention = translate_sentence(src_sample, TEXT, TEXT, model, device)
      print(f'predicted trg = {translation}')

Prüfung

Sie können einen Roman erstellen, indem Sie unten Ihren Lieblingstitel eingeben.

translation, attention = translate_sentence(['Beliebiger Titel'], TEXT, TEXT, model, device)
print(f'predicted trg = {translation}')

Ich habe es versucht. Die oberen beiden sind Titel, die in den Trainingsdaten enthalten sind, und die unteren beiden sind Titel, die nicht enthalten sind (um genau zu sein, sie sind im Wörterbuch enthalten (= zweimal im Titel oder Hauptteil der Trainingsdaten oder Testdaten). Von den Wortkombinationen (die oben erscheinen) diejenigen, die nicht die Titel von Lerndaten sind).

Titel eingeben Generierter Text
Erinnerungen Als ich in meinen Zwanzigern war, traf ich Herrn Ogai fünf oder sechs Mal. Dann ein paar Mal vom Armeeministerium(unk)Ich habe es gesehen, als ich die Kalibrierung an die Station geliefert habe, aber es war einfach,(unk)Ich habe nicht genug Material, um die Geschichte zu erzählen. Zu dieser Zeit waren Hiroshi Yosano, Nagae Ikuta und Kafu Nagai die Schüler von Herrn Kogai, und ich war wie der Enkel des Lehrers. Aus diesem Grund habe ich keine direkte Beziehung zum Lehrer, aber ich respektiere ihn für seine literarische Arbeit. Es scheint, dass der Lehrer immer darauf geachtet hat, dass sich die andere Person nicht verkrampft fühlt und unter einer scheinbar schwierigen Person leidet, aber es war hier ziemlich eng. Apropos Gedanken, eines Tages, um die Veröffentlichung einer Zeitschrift namens "We" zu feiern ... (unten weggelassen)
Fußabdrücke ずつと昔のこと一匹の狐が河岸の粘土層を走つていつたそれから何万年かたつたあとにその粘土層が化石となつてFußabdrückeが残つたそのFußabdrückeを見ると、むかし狐が何を考えて走つていつたのかがわかる
Haar Ah, du bist emotional, aber ich bin melancholisch, Segawas 螢,(unk)Wenn Sie das Licht von Fufunabata betrachten, können Sie die Augen von Yufugures Jungfrau sehen.(unk), Kuchi(unk)
Musik (unk)Ist(unk)Ist(unk)太陽が落ちて太陽の世界が始つた[#「始つた」Ist底本でIst「始まつた」](unk)Ist戸袋(unk)Ist(unk)Die Sonne ist aufgegangen und die Nachtwelt hat begonnen(unk)Ist妖怪下痢Ist(unk)Higurashi zeichnete einen Durchmesser und die Welt von Dada begann (es(unk)Schau es dir an und Christus beeindruckt es

Für Trainingsdaten scheint es, dass der Text aus dem Titel mit hoher Genauigkeit reproduziert werden kann. Andererseits sind Titel, die nicht in den Trainingsdaten enthalten sind, zu bedeutungslosen Sätzen geworden.

abschließend

Es wurde bestätigt, dass der Text mit Transformer aus dem Titel des Romans generiert werden kann. Ich konnte jedoch keinen richtigen Titel für den Titel generieren, der nicht in den Trainingsdaten enthalten ist. Daher möchte ich beim nächsten Mal die Validierungsdaten verwenden, die ich dieses Mal aufgegeben habe, und ein allgemeineres Modell anstreben.

Referenz

Recommended Posts

[PyTorch] Japanische Satzgenerierung mit Transformer
[PyTorch] Einführung in die Klassifizierung japanischer Dokumente mit BERT
[Python3] Automatische Texterzeugung mit janome und markovify
07. Anweisungsgenerierung nach Vorlage
Ich habe den Code für die japanische Satzgenerierung mit DeZero geschrieben
Japanische morphologische Analyse mit Janome
Satzerzeugung mit GRU (Keras)
[PyTorch] Tutorial (japanische Version) ② ~ AUTOGRAD ~
Hinweise zur Optimierung mit Pytorch
Stärkste Pokemon-Generation mit LSTM