Die Version 2020 von 100 Klopfen der Sprachverarbeitung, die als Sammlung von Problemen der Verarbeitung natürlicher Sprache bekannt ist, wurde veröffentlicht. Dieser Artikel fasst die Ergebnisse der Lösung von "Kapitel 9: RNN, CNN" aus den folgenden Kapiteln 1 bis 10 zusammen.
Für die Antwort wird Google Colaboratory verwendet. Ausführliche Informationen zum Einrichten und Verwenden von Google Colaboratory finden Sie in diesem Artikel. ** Da in diesem Kapitel die GPU verwendet wird, ändern Sie den Hardwarebeschleuniger von "Laufzeit" -> "Laufzeittyp ändern" in "GPU" und speichern Sie ihn im Voraus. ** ** ** Das Notizbuch mit den Ausführungsergebnissen der folgenden Antworten ist unter [github] verfügbar (https://github.com/hana-mame/nlp100/blob/master/chapter09.ipynb).
Ich möchte den Wörtern in den in Frage 51 erstellten Trainingsdaten eine eindeutige ID-Nummer geben. Das Wort, das in den Trainingsdaten am häufigsten vorkommt, ist "1", das Wort, das als zweites erscheint, ist "2" und so weiter, und das Wort, das mehr als einmal in den Trainingsdaten vorkommt, ist ID. Gib ihm eine Nummer. Implementieren Sie dann eine Funktion, die eine Zeichenfolge mit ID-Nummern für eine bestimmte Wortzeichenfolge zurückgibt. Alle ID-Nummern von Wörtern, die weniger als zweimal vorkommen, sollten jedoch "0" sein.
Lesen Sie die angegebenen Daten nach dem Herunterladen zunächst als Datenrahmen. Anschließend wird es in Trainingsdaten, Verifizierungsdaten und Bewertungsdaten unterteilt und gespeichert. Bis zu diesem Punkt ist der Prozess genau der gleiche wie bei Problem 50 in Kapitel 6, sodass die dort erstellten Daten problemlos gelesen werden können.
#Daten herunterladen
!wget https://archive.ics.uci.edu/ml/machine-learning-databases/00359/NewsAggregatorDataset.zip
!unzip NewsAggregatorDataset.zip
#Doppelte Anführungszeichen durch einfache Anführungszeichen ersetzt, um Fehler beim Lesen zu vermeiden
!sed -e 's/"/'\''/g' ./newsCorpora.csv > ./newsCorpora_re.csv
import pandas as pd
from sklearn.model_selection import train_test_split
#Daten lesen
df = pd.read_csv('./newsCorpora_re.csv', header=None, sep='\t', names=['ID', 'TITLE', 'URL', 'PUBLISHER', 'CATEGORY', 'STORY', 'HOSTNAME', 'TIMESTAMP'])
#Datenextraktion
df = df.loc[df['PUBLISHER'].isin(['Reuters', 'Huffington Post', 'Businessweek', 'Contactmusic.com', 'Daily Mail']), ['TITLE', 'CATEGORY']]
#Datenaufteilung
train, valid_test = train_test_split(df, test_size=0.2, shuffle=True, random_state=123, stratify=df['CATEGORY'])
valid, test = train_test_split(valid_test, test_size=0.5, shuffle=True, random_state=123, stratify=valid_test['CATEGORY'])
#Bestätigung der Anzahl der Fälle
print('[Lerndaten]')
print(train['CATEGORY'].value_counts())
print('[Verifizierungsdaten]')
print(valid['CATEGORY'].value_counts())
print('[Bewertungsdaten]')
print(test['CATEGORY'].value_counts())
Ausgabe
[Lerndaten]
b 4501
e 4235
t 1220
m 728
Name: CATEGORY, dtype: int64
[Verifizierungsdaten]
b 563
e 529
t 153
m 91
Name: CATEGORY, dtype: int64
[Bewertungsdaten]
b 563
e 530
t 152
m 91
Name: CATEGORY, dtype: int64
Erstellen Sie als Nächstes ein Wörterbuch mit Wörtern. Die Wörter in den Lerndaten werden gezählt und das Frequenzranking (ID) wird unter Verwendung desjenigen registriert, das mehr als einmal als Schlüssel erscheint.
from collections import defaultdict
import string
#Worthäufigkeitsaggregation
d = defaultdict(int)
table = str.maketrans(string.punctuation, ' '*len(string.punctuation)) #Eine Tabelle, die Symbole durch Leerzeichen ersetzt
for text in train['TITLE']:
for word in text.translate(table).split():
d[word] += 1
d = sorted(d.items(), key=lambda x:x[1], reverse=True)
#Erstellen eines Wort-ID-Wörterbuchs
word2id = {word: i + 1 for i, (word, cnt) in enumerate(d) if cnt > 1} #Registrieren Sie Wörter, die mehrmals vorkommen
print(f'Anzahl der IDs: {len(set(word2id.values()))}\n')
print('---Top 20 Wörter in der Häufigkeit---')
for key in list(word2id)[:20]:
print(f'{key}: {word2id[key]}')
Ausgabe
Anzahl der IDs: 9405
---Top 20 Wörter in der Häufigkeit---
to: 1
s: 2
in: 3
on: 4
UPDATE: 5
as: 6
US: 7
for: 8
The: 9
of: 10
1: 11
To: 12
2: 13
the: 14
and: 15
In: 16
Of: 17
a: 18
at: 19
A: 20
Definieren Sie abschließend eine Funktion, die ein Wörterbuch verwendet, um eine bestimmte Wortzeichenfolge in eine Zeichenfolge mit ID-Nummern umzuwandeln. Befolgen Sie zu diesem Zeitpunkt die Anweisungen im Fragensatz und geben Sie `` `0``` für Wörter zurück, die nicht im Wörterbuch enthalten sind.
def tokenizer(text, word2id=word2id, unk=0):
"""Teilen Sie den eingegebenen Text durch Leerzeichen und konvertieren Sie ihn in eine ID-Spalte(Wenn es nicht im Wörterbuch enthalten ist, legen Sie die von unk angegebene Nummer fest)"""
table = str.maketrans(string.punctuation, ' '*len(string.punctuation))
return [word2id.get(word, unk) for word in text.translate(table).split()]
Überprüfen Sie den zweiten Satz.
#Bestätigung
text = train.iloc[1, train.columns.get_loc('TITLE')]
print(f'Text: {text}')
print(f'ID-Spalte: {tokenizer(text)}')
Ausgabe
Text: Amazon Plans to Fight FTC Over Mobile-App Purchases
ID-Spalte: [169, 539, 1, 683, 1237, 82, 279, 1898, 4199]
Es gibt eine Wortzeichenfolge $ \ boldsymbol {x} = (x_1, x_2, \ dots, x_T) $, die durch eine ID-Nummer dargestellt wird. $ T $ ist jedoch die Länge der Wortzeichenfolge, und $ x_t \ in \ mathbb {R} ^ {V} $ ist die One-Hot-Notation der Wort-ID-Nummer ($ V $ ist die Gesamtzahl der Wörter). Implementieren Sie unter Verwendung eines wiederkehrenden neuronalen Netzwerks (RNN) die folgende Gleichung als Modell für die Vorhersage der Kategorie $ y $ aus der Wortfolge $ \ boldsymbol {x} $.
\overrightarrow h_0 = 0,\ \overrightarrow h_t = {\rm \overrightarrow{RNN}}(\mathrm{emb}(x_t), \overrightarrow h_{t-1}), \ y = {\rm softmax}(W^{(yh)} \overrightarrow h_T + b^{(y)})
> $ \ Mathrm {emb} (x) \ in \ mathbb {R} ^ {d_w} $ ist jedoch eine Worteinbettung (eine Funktion, die ein Wort von einer One-Hot-Notation in einen Wortvektor konvertiert), $ \ overridearrow h_t \ in \ mathbb {R} ^ {d_h} $ ist der verborgene Zustandsvektor zum Zeitpunkt $ t $, $ {\ rm \ overrightarrow {RNN}} (x, h) $ stammt aus der Eingabe $ x $ und dem verborgenen Zustand $ h $ zum vorherigen Zeitpunkt Die RNN-Einheit, die den nächsten Zustand berechnet, $ W ^ {(yh)} \ in \ mathbb {R} ^ {L \ times d_h} $, ist die Matrix zur Vorhersage der Kategorie aus dem verborgenen Zustandsvektor $ b ^ {(y) )} \ in \ mathbb {R} ^ {L} $ ist ein Bias-Term ($ d_w, d_h, L $ sind die Anzahl der Worteinbettungsdimensionen, die Anzahl der versteckten Zustandsvektordimensionen bzw. die Anzahl der Beschriftungen). Die RNN-Einheit $ {\ rm \ overrightarrow {RNN}} (x, h) $ kann verschiedene Konfigurationen haben, und die folgende Gleichung ist ein typisches Beispiel.
>```math
{\rm \overrightarrow{RNN}}(x,h) = g(W^{(hx)} x + W^{(hh)}h + b^{(h)})
Allerdings ist $ W ^ {(hx)} \ in \ mathbb {R} ^ {d_h \ times d_w}, W ^ {(hh)} \ in \ mathbb {R} ^ {d_h \ times d_h}, b ^ {(h)} \ in \ mathbb {R} ^ {d_h} $ ist der Parameter der RNN-Einheit und $ g $ ist die Aktivierungsfunktion (z. B. $ \ tanh $ und ReLU).
Beachten Sie, dass dieses Problem die Parameter nicht trainiert, sondern nur die Berechnung von $ y $ mit den zufällig initialisierten Parametern erfordert. Hyperparameter wie die Anzahl der Dimensionen sollten auf geeignete Werte wie $ d_w = 300, d_h = 50 $ eingestellt werden (dasselbe gilt für die folgenden Probleme).
Bevor wir auf die Antwort eingehen, wollen wir den Ablauf der Verarbeitung natürlicher Sprache mithilfe eines neuronalen Netzes organisieren, insbesondere bei der Textklassifizierung. Die Textklassifizierung unter Verwendung eines neuronalen Netzes besteht hauptsächlich aus den folgenden vier Schritten.
Für jeden Prozess können verschiedene Methoden in Betracht gezogen werden, z. B. in Kapitel 8
Ich habe den Ablauf von Nr. 4 implementiert und die Parameter von Nr. 4 gelernt (beim Targeting japanischer Dokumente in Nr. 1 Kapitel 4. E06014b146a18e97ca59) morphologische Analyse erforderlich).
Auf der anderen Seite, in diesem Kapitel,
Dann lernen wir die Parameter des Netzwerks kennen, das die Nummern 2 bis 4 verbindet. Wie im Problem dieses Kapitels ist es häufig der Fall, dass die geteilten Token der Einfachheit halber in die entsprechenden IDs konvertiert werden, sie sind jedoch als Prozess in Nr. 1 enthalten.
Lassen Sie uns nun das Netzwerk dieser Frage sofort implementieren.
Verwenden Sie `nn.Embedding``` für die eingebettete Ebene. Bei gegebener Wort-ID wird diese Ebene in einen One-Hot-Vektor und dann in einen Vektor der angegebenen Größe (
`emb_size```) konvertiert.
Der folgende RNN-Teil kann durch den Prozess des rekursiven Durchlaufens der vollständig verbundenen Schicht realisiert werden, kann jedoch einfach unter Verwendung von "nn.RNN" geschrieben werden.
Schließen Sie zum Schluss die vollständig verbundenen Ebenen an und Sie sind fertig.
import torch
from torch import nn
class RNN(nn.Module):
def __init__(self, vocab_size, emb_size, padding_idx, output_size, hidden_size):
super().__init__()
self.hidden_size = hidden_size
self.emb = nn.Embedding(vocab_size, emb_size, padding_idx=padding_idx)
self.rnn = nn.RNN(emb_size, hidden_size, nonlinearity='tanh', batch_first=True)
self.fc = nn.Linear(hidden_size, output_size)
def forward(self, x):
self.batch_size = x.size()[0]
hidden = self.init_hidden() #Erstellen Sie einen Nullvektor für h0
emb = self.emb(x)
# emb.size() = (batch_size, seq_len, emb_size)
out, hidden = self.rnn(emb, hidden)
# out.size() = (batch_size, seq_len, hidden_size)
out = self.fc(out[:, -1, :])
# out.size() = (batch_size, output_size)
return out
def init_hidden(self):
hidden = torch.zeros(1, self.batch_size, self.hidden_size)
return hidden
Definieren Sie als Nächstes die Klasse, die das `Dataset``` erstellt, wie im vorherigen Kapitel. Dieses Mal erhalten wir den Text und die Beschriftung, konvertieren den Text in eine ID mit dem angegebenen
`Tokenizer``` und geben jedem eine Funktion zur Ausgabe im Tensortyp.
from torch.utils.data import Dataset
class CreateDataset(Dataset):
def __init__(self, X, y, tokenizer):
self.X = X
self.y = y
self.tokenizer = tokenizer
def __len__(self): # len(Dataset)Geben Sie den Wert an, mit dem zurückgegeben werden soll
return len(self.y)
def __getitem__(self, index): # Dataset[index]Geben Sie den Wert an, mit dem zurückgegeben werden soll
text = self.X[index]
inputs = self.tokenizer(text)
return {
'inputs': torch.tensor(inputs, dtype=torch.int64),
'labels': torch.tensor(self.y[index], dtype=torch.int64)
}
Erstellen Sie einen `` `Datensatz``` mit den oben genannten. Geben Sie für "Tokenizer" die in der vorherigen Frage definierte Funktion an.
#Erstellen eines Beschriftungsvektors
category_dict = {'b': 0, 't': 1, 'e':2, 'm':3}
y_train = train['CATEGORY'].map(lambda x: category_dict[x]).values
y_valid = valid['CATEGORY'].map(lambda x: category_dict[x]).values
y_test = test['CATEGORY'].map(lambda x: category_dict[x]).values
#Erstellen eines Datensatzes
dataset_train = CreateDataset(train['TITLE'], y_train, tokenizer)
dataset_valid = CreateDataset(valid['TITLE'], y_valid, tokenizer)
dataset_test = CreateDataset(test['TITLE'], y_test, tokenizer)
print(f'len(Dataset)Ausgabe von: {len(dataset_train)}')
print('Dataset[index]Ausgabe von:')
for var in dataset_train[1]:
print(f' {var}: {dataset_train[1][var]}')
Ausgabe
len(Dataset)Ausgabe von: 10684
Dataset[index]Ausgabe von:
inputs: tensor([ 169, 539, 1, 683, 1237, 82, 279, 1898, 4199])
labels: 1
Da wir in dieser Frage nicht lernen werden, geben Sie dem Modell `Eingaben``` von`
Dataset``` und überprüfen Sie die Ausgabe so, wie sie nach Softmax ist.
#Parametereinstellungen
VOCAB_SIZE = len(set(word2id.values())) + 1 #Anzahl der Wörterbuch-IDs+Polsterungs-ID
EMB_SIZE = 300
PADDING_IDX = len(set(word2id.values()))
OUTPUT_SIZE = 4
HIDDEN_SIZE = 50
#Modelldefinition
model = RNN(VOCAB_SIZE, EMB_SIZE, PADDING_IDX, OUTPUT_SIZE, HIDDEN_SIZE)
#Holen Sie sich die ersten 10 vorhergesagten Werte
for i in range(10):
X = dataset_train[i]['inputs']
print(torch.softmax(model(X.unsqueeze(0)), dim=-1))
Ausgabe
tensor([[0.2667, 0.2074, 0.2974, 0.2285]], grad_fn=<SoftmaxBackward>)
tensor([[0.1660, 0.3465, 0.2154, 0.2720]], grad_fn=<SoftmaxBackward>)
tensor([[0.2133, 0.2987, 0.3097, 0.1783]], grad_fn=<SoftmaxBackward>)
tensor([[0.2512, 0.4107, 0.1825, 0.1556]], grad_fn=<SoftmaxBackward>)
tensor([[0.2784, 0.1307, 0.3715, 0.2194]], grad_fn=<SoftmaxBackward>)
tensor([[0.2625, 0.1569, 0.2339, 0.3466]], grad_fn=<SoftmaxBackward>)
tensor([[0.1331, 0.5129, 0.2220, 0.1319]], grad_fn=<SoftmaxBackward>)
tensor([[0.2404, 0.1314, 0.2023, 0.4260]], grad_fn=<SoftmaxBackward>)
tensor([[0.1162, 0.4576, 0.2588, 0.1674]], grad_fn=<SoftmaxBackward>)
tensor([[0.4685, 0.1414, 0.2633, 0.1268]], grad_fn=<SoftmaxBackward>)
Lernen Sie das in Aufgabe 81 konstruierte Modell mit der SGD-Methode (Stochastic Gradient Descent). Trainieren Sie das Modell, während Sie den Verlust und die richtige Antwortrate in den Trainingsdaten sowie den Verlust und die richtige Antwortrate in den Bewertungsdaten anzeigen, und beenden Sie den Vorgang mit einem geeigneten Standard (z. B. 10 Epochen).
Wie im vorherigen Kapitel definiert dies auch eine Reihe von Verarbeitungen zum Lernen als `` `train_model``` Funktion.
from torch.utils.data import DataLoader
import time
from torch import optim
def calculate_loss_and_accuracy(model, dataset, device=None, criterion=None):
"""Berechnen Sie den Verlust / die richtige Antwortrate"""
dataloader = DataLoader(dataset, batch_size=1, shuffle=False)
loss = 0.0
total = 0
correct = 0
with torch.no_grad():
for data in dataloader:
#Gerätespezifikation
inputs = data['inputs'].to(device)
labels = data['labels'].to(device)
#Vorwärtsausbreitung
outputs = model(inputs)
#Verlustberechnung
if criterion != None:
loss += criterion(outputs, labels).item()
#Richtige Berechnung der Antwortrate
pred = torch.argmax(outputs, dim=-1)
total += len(inputs)
correct += (pred == labels).sum().item()
return loss / len(dataset), correct / total
def train_model(dataset_train, dataset_valid, batch_size, model, criterion, optimizer, num_epochs, collate_fn=None, device=None):
"""Führt ein Modelltraining durch und gibt ein Protokoll mit Verlust / korrekter Antwortrate zurück"""
#Gerätespezifikation
model.to(device)
#Erstellen eines Datenladers
dataloader_train = DataLoader(dataset_train, batch_size=batch_size, shuffle=True, collate_fn=collate_fn)
dataloader_valid = DataLoader(dataset_valid, batch_size=1, shuffle=False)
#Scheduler-Einstellungen
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, num_epochs, eta_min=1e-5, last_epoch=-1)
#Lernen
log_train = []
log_valid = []
for epoch in range(num_epochs):
#Startzeit aufzeichnen
s_time = time.time()
#Auf Trainingsmodus einstellen
model.train()
for data in dataloader_train:
#Gradient auf Null initialisieren
optimizer.zero_grad()
#Vorwärtsausbreitung+Fehler bei der Weitergabe+Gewichtsaktualisierung
inputs = data['inputs'].to(device)
labels = data['labels'].to(device)
outputs = model.forward(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
#In den Auswertemodus versetzen
model.eval()
#Verlustberechnung und korrekte Rücklaufquote
loss_train, acc_train = calculate_loss_and_accuracy(model, dataset_train, device, criterion=criterion)
loss_valid, acc_valid = calculate_loss_and_accuracy(model, dataset_valid, device, criterion=criterion)
log_train.append([loss_train, acc_train])
log_valid.append([loss_valid, acc_valid])
#Checkpoint speichern
torch.save({'epoch': epoch, 'model_state_dict': model.state_dict(), 'optimizer_state_dict': optimizer.state_dict()}, f'checkpoint{epoch + 1}.pt')
#Endzeit aufzeichnen
e_time = time.time()
#Ausgabeprotokoll
print(f'epoch: {epoch + 1}, loss_train: {loss_train:.4f}, accuracy_train: {acc_train:.4f}, loss_valid: {loss_valid:.4f}, accuracy_valid: {acc_valid:.4f}, {(e_time - s_time):.4f}sec')
#Das Lernen endet, wenn der Verlust von Verifizierungsdaten in drei aufeinander folgenden Epochen nicht abnimmt
if epoch > 2 and log_valid[epoch - 3][0] <= log_valid[epoch - 2][0] <= log_valid[epoch - 1][0] <= log_valid[epoch][0]:
break
#Machen Sie mit dem Scheduler einen Schritt
scheduler.step()
return {'train': log_train, 'valid': log_valid}
Definieren Sie außerdem eine Funktion zur Visualisierung des Protokolls.
import numpy as np
from matplotlib import pyplot as plt
def visualize_logs(log):
fig, ax = plt.subplots(1, 2, figsize=(15, 5))
ax[0].plot(np.array(log['train']).T[0], label='train')
ax[0].plot(np.array(log['valid']).T[0], label='valid')
ax[0].set_xlabel('epoch')
ax[0].set_ylabel('loss')
ax[0].legend()
ax[1].plot(np.array(log['train']).T[1], label='train')
ax[1].plot(np.array(log['valid']).T[1], label='valid')
ax[1].set_xlabel('epoch')
ax[1].set_ylabel('accuracy')
ax[1].legend()
plt.show()
Stellen Sie die Parameter ein und trainieren Sie das Modell.
#Parametereinstellungen
VOCAB_SIZE = len(set(word2id.values())) + 1
EMB_SIZE = 300
PADDING_IDX = len(set(word2id.values()))
OUTPUT_SIZE = 4
HIDDEN_SIZE = 50
LEARNING_RATE = 1e-3
BATCH_SIZE = 1
NUM_EPOCHS = 10
#Modelldefinition
model = RNN(VOCAB_SIZE, EMB_SIZE, PADDING_IDX, OUTPUT_SIZE, HIDDEN_SIZE)
#Definition der Verlustfunktion
criterion = nn.CrossEntropyLoss()
#Optimierungsdefinition
optimizer = torch.optim.SGD(model.parameters(), lr=LEARNING_RATE)
#Modelllernen
log = train_model(dataset_train, dataset_valid, BATCH_SIZE, model, criterion, optimizer, NUM_EPOCHS)
Ausgabe
epoch: 1, loss_train: 1.0954, accuracy_train: 0.5356, loss_valid: 1.1334, accuracy_valid: 0.5015, 86.4033sec
epoch: 2, loss_train: 1.0040, accuracy_train: 0.6019, loss_valid: 1.0770, accuracy_valid: 0.5516, 85.2816sec
epoch: 3, loss_train: 0.8813, accuracy_train: 0.6689, loss_valid: 0.9793, accuracy_valid: 0.6287, 78.9026sec
epoch: 4, loss_train: 0.7384, accuracy_train: 0.7364, loss_valid: 0.8498, accuracy_valid: 0.7058, 78.4496sec
epoch: 5, loss_train: 0.6427, accuracy_train: 0.7696, loss_valid: 0.7878, accuracy_valid: 0.7253, 83.4453sec
epoch: 6, loss_train: 0.5730, accuracy_train: 0.7942, loss_valid: 0.7378, accuracy_valid: 0.7470, 79.6968sec
epoch: 7, loss_train: 0.5221, accuracy_train: 0.8064, loss_valid: 0.7058, accuracy_valid: 0.7530, 79.7377sec
epoch: 8, loss_train: 0.4924, accuracy_train: 0.8173, loss_valid: 0.7017, accuracy_valid: 0.7605, 78.2168sec
epoch: 9, loss_train: 0.4800, accuracy_train: 0.8234, loss_valid: 0.7014, accuracy_valid: 0.7575, 77.8689sec
epoch: 10, loss_train: 0.4706, accuracy_train: 0.8253, loss_valid: 0.6889, accuracy_valid: 0.7650, 79.4202sec
#Protokollvisualisierung
visualize_logs(log)
#Berechnung der richtigen Antwortrate
_, acc_train = calculate_loss_and_accuracy(model, dataset_train)
_, acc_test = calculate_loss_and_accuracy(model, dataset_test)
print(f'Richtige Antwortrate (Lerndaten):{acc_train:.3f}')
print(f'Richtige Antwortrate (Bewertungsdaten):{acc_test:.3f}')
Ausgabe
Richtige Antwortrate (Lerndaten): 0.825
Richtige Antwortrate (Bewertungsdaten): 0.773
Ändern Sie den Code von Problem 82 so, dass Sie lernen können, indem Sie den Verlust / Gradienten für jeden $ B $ -Fall berechnen (wählen Sie den Wert von $ B $ entsprechend aus). Führen Sie das Lernen auch auf der GPU aus.
Derzeit ist die Serienlänge für jeden Satz unterschiedlich, es ist jedoch erforderlich, die Serienlänge auszurichten, um sie als Mini-Batch zusammenzustellen.
Daher definieren wir eine neue `Padsequence``` -Klasse, die die Funktion hat, entsprechend der maximalen Sequenzlänge mehrerer Anweisungen aufzufüllen. Indem Sie dies dem Argument
collate_fn``` von
`` Dataloader``` geben, ist es möglich, den Prozess des Ausrichtens der Serienlänge jedes Mal zu realisieren, wenn ein Mini-Batch herausgenommen wird.
class Padsequence():
"""Polsterung mit maximaler Serienlänge jedes Mal, wenn eine Mini-Charge aus Dataloader entnommen wird"""
def __init__(self, padding_idx):
self.padding_idx = padding_idx
def __call__(self, batch):
sorted_batch = sorted(batch, key=lambda x: x['inputs'].shape[0], reverse=True)
sequences = [x['inputs'] for x in sorted_batch]
sequences_padded = torch.nn.utils.rnn.pad_sequence(sequences, batch_first=True, padding_value=self.padding_idx)
labels = torch.LongTensor([x['labels'] for x in sorted_batch])
return {'inputs': sequences_padded, 'labels': labels}
#Parametereinstellungen
VOCAB_SIZE = len(set(word2id.values())) + 1
EMB_SIZE = 300
PADDING_IDX = len(set(word2id.values()))
OUTPUT_SIZE = 4
HIDDEN_SIZE = 50
LEARNING_RATE = 5e-2
BATCH_SIZE = 32
NUM_EPOCHS = 10
#Modelldefinition
model = RNN(VOCAB_SIZE, EMB_SIZE, PADDING_IDX, OUTPUT_SIZE, HIDDEN_SIZE)
#Definition der Verlustfunktion
criterion = nn.CrossEntropyLoss()
#Optimierungsdefinition
optimizer = torch.optim.SGD(model.parameters(), lr=LEARNING_RATE)
#Gerätespezifikation
device = torch.device('cuda')
#Modelllernen
log = train_model(dataset_train, dataset_valid, BATCH_SIZE, model, criterion, optimizer, NUM_EPOCHS, collate_fn=Padsequence(PADDING_IDX), device=device)
Ausgabe
epoch: 1, loss_train: 1.2605, accuracy_train: 0.3890, loss_valid: 1.2479, accuracy_valid: 0.4162, 12.1096sec
epoch: 2, loss_train: 1.2492, accuracy_train: 0.4246, loss_valid: 1.2541, accuracy_valid: 0.4424, 12.0607sec
epoch: 3, loss_train: 1.2034, accuracy_train: 0.4795, loss_valid: 1.2220, accuracy_valid: 0.4686, 11.8881sec
epoch: 4, loss_train: 1.1325, accuracy_train: 0.5392, loss_valid: 1.1542, accuracy_valid: 0.5210, 12.2269sec
epoch: 5, loss_train: 1.0543, accuracy_train: 0.6214, loss_valid: 1.0623, accuracy_valid: 0.6175, 11.8767sec
epoch: 6, loss_train: 1.0381, accuracy_train: 0.6316, loss_valid: 1.0556, accuracy_valid: 0.6145, 11.9757sec
epoch: 7, loss_train: 1.0546, accuracy_train: 0.6165, loss_valid: 1.0806, accuracy_valid: 0.5913, 12.0352sec
epoch: 8, loss_train: 0.9924, accuracy_train: 0.6689, loss_valid: 1.0150, accuracy_valid: 0.6587, 11.9090sec
epoch: 9, loss_train: 1.0123, accuracy_train: 0.6517, loss_valid: 1.0482, accuracy_valid: 0.6310, 12.0953sec
epoch: 10, loss_train: 1.0036, accuracy_train: 0.6623, loss_valid: 1.0319, accuracy_valid: 0.6504, 11.9331sec
#Protokollvisualisierung
visualize_logs(log)
#Berechnung der richtigen Antwortrate
_, acc_train = calculate_loss_and_accuracy(model, dataset_train, device)
_, acc_test = calculate_loss_and_accuracy(model, dataset_test, device)
print(f'Richtige Antwortrate (Lerndaten):{acc_train:.3f}')
print(f'Richtige Antwortrate (Bewertungsdaten):{acc_test:.3f}')
Ausgabe
Richtige Antwortrate (Lerndaten): 0.662
Richtige Antwortrate (Bewertungsdaten): 0.649
Initialisieren und lernen Sie das Wort, in das $ emb (x) $ eingebettet ist, mit einem vorgelernten Wortvektor (z. B. einem gelernten Wortvektor im Google News-Datensatz (ca. 100 Milliarden Wörter)).
Laden Sie den vorgelernten Wortvektor wie im vorherigen Kapitel herunter.
#Laden Sie den gelernten Wortvektor herunter
FILE_ID = "0B7XkCwpI5KDYNlNUTTlSS21pQmM"
FILE_NAME = "GoogleNews-vectors-negative300.bin.gz"
!wget --load-cookies /tmp/cookies.txt "https://docs.google.com/uc?export=download&confirm=$(wget --quiet --save-cookies /tmp/cookies.txt --keep-session-cookies --no-check-certificate 'https://docs.google.com/uc?export=download&id=$FILE_ID' -O- | sed -rn 's/.*confirm=([0-9A-Za-z_]+).*/\1\n/p')&id=$FILE_ID" -O $FILE_NAME && rm -rf /tmp/cookies.txt
Wenn Sie einen vorgelernten Wortvektor als Modell verwenden, gibt es zwei Methoden: Eine besteht darin, alle Wörter zu verwenden (das Wörterbuch zu ersetzen), und die andere darin, das Wörterbuch der vorliegenden Daten so zu verwenden, wie es ist, und es nur als Anfangswert dieser Wortvektoren zu verwenden. Es gibt. Dieses Mal wird das letztere Verfahren angewendet und der Wortvektor, der dem bereits erstellten Wörterbuch entspricht, wird extrahiert.
from gensim.models import KeyedVectors
#Geladenes trainiertes Modell laden
model = KeyedVectors.load_word2vec_format('./GoogleNews-vectors-negative300.bin.gz', binary=True)
#Holen Sie sich gelernt Wortvektor
VOCAB_SIZE = len(set(word2id.values())) + 1
EMB_SIZE = 300
weights = np.zeros((VOCAB_SIZE, EMB_SIZE))
words_in_pretrained = 0
for i, word in enumerate(word2id.keys()):
try:
weights[i] = model[word]
words_in_pretrained += 1
except KeyError:
weights[i] = np.random.normal(scale=0.4, size=(EMB_SIZE,))
weights = torch.from_numpy(weights.astype((np.float32)))
print(f'Anzahl der im gelernten Vektor verwendeten Wörter: {words_in_pretrained} / {VOCAB_SIZE}')
print(weights.size())
Ausgabe
Anzahl der im gelernten Vektor verwendeten Wörter: 9174 / 9406
torch.Size([9406, 300])
Ändern Sie dies so, dass der Anfangswert für die eingebettete Schicht des Netzwerks festgelegt werden kann. Fügen Sie außerdem Einstellungen für bidirektional und mehrschichtig für das nächste Problem hinzu.
class RNN(nn.Module):
def __init__(self, vocab_size, emb_size, padding_idx, output_size, hidden_size, num_layers, emb_weights=None, bidirectional=False):
super().__init__()
self.hidden_size = hidden_size
self.num_layers = num_layers
self.num_directions = bidirectional + 1 #Unidirektional: 1, bidirektional: 2
if emb_weights != None: #Geben Sie das Gewicht der eingebetteten Ebene an, falls angegeben_Mit Gewichten initialisieren
self.emb = nn.Embedding.from_pretrained(emb_weights, padding_idx=padding_idx)
else:
self.emb = nn.Embedding(vocab_size, emb_size, padding_idx=padding_idx)
self.rnn = nn.RNN(emb_size, hidden_size, num_layers, nonlinearity='tanh', bidirectional=bidirectional, batch_first=True)
self.fc = nn.Linear(hidden_size * self.num_directions, output_size)
def forward(self, x):
self.batch_size = x.size()[0]
hidden = self.init_hidden() #Erstellen Sie einen Nullvektor für h0
emb = self.emb(x)
# emb.size() = (batch_size, seq_len, emb_size)
out, hidden = self.rnn(emb, hidden)
# out.size() = (batch_size, seq_len, hidden_size * num_directions)
out = self.fc(out[:, -1, :])
# out.size() = (batch_size, output_size)
return out
def init_hidden(self):
hidden = torch.zeros(self.num_layers * self.num_directions, self.batch_size, self.hidden_size)
return hidden
Lernen Sie, indem Sie den Anfangswert der eingebetteten Ebene angeben.
#Parametereinstellungen
VOCAB_SIZE = len(set(word2id.values())) + 1
EMB_SIZE = 300
PADDING_IDX = len(set(word2id.values()))
OUTPUT_SIZE = 4
HIDDEN_SIZE = 50
NUM_LAYERS = 1
LEARNING_RATE = 5e-2
BATCH_SIZE = 32
NUM_EPOCHS = 10
#Modelldefinition
model = RNN(VOCAB_SIZE, EMB_SIZE, PADDING_IDX, OUTPUT_SIZE, HIDDEN_SIZE, NUM_LAYERS, emb_weights=weights)
#Definition der Verlustfunktion
criterion = nn.CrossEntropyLoss()
#Optimierungsdefinition
optimizer = torch.optim.SGD(model.parameters(), lr=LEARNING_RATE)
#Gerätespezifikation
device = torch.device('cuda')
#Modelllernen
log = train_model(dataset_train, dataset_valid, BATCH_SIZE, model, criterion, optimizer, NUM_EPOCHS, collate_fn=Padsequence(PADDING_IDX), device=device)
Ausgabe
epoch: 1, loss_train: 1.1655, accuracy_train: 0.4270, loss_valid: 1.1839, accuracy_valid: 0.4244, 9.7483sec
epoch: 2, loss_train: 1.1555, accuracy_train: 0.4635, loss_valid: 1.1404, accuracy_valid: 0.4865, 9.7553sec
epoch: 3, loss_train: 1.0189, accuracy_train: 0.6263, loss_valid: 1.0551, accuracy_valid: 0.6085, 10.0445sec
epoch: 4, loss_train: 1.0377, accuracy_train: 0.6221, loss_valid: 1.0947, accuracy_valid: 0.5951, 10.1138sec
epoch: 5, loss_train: 1.0392, accuracy_train: 0.6082, loss_valid: 1.0776, accuracy_valid: 0.5921, 9.8540sec
epoch: 6, loss_train: 1.0447, accuracy_train: 0.6087, loss_valid: 1.1020, accuracy_valid: 0.5793, 9.8598sec
epoch: 7, loss_train: 0.9999, accuracy_train: 0.6270, loss_valid: 1.0519, accuracy_valid: 0.6108, 9.7565sec
epoch: 8, loss_train: 0.9539, accuracy_train: 0.6557, loss_valid: 1.0092, accuracy_valid: 0.6385, 9.7457sec
epoch: 9, loss_train: 0.9287, accuracy_train: 0.6674, loss_valid: 0.9806, accuracy_valid: 0.6430, 9.6464sec
epoch: 10, loss_train: 0.9456, accuracy_train: 0.6593, loss_valid: 1.0029, accuracy_valid: 0.6377, 9.6835sec
#Protokollvisualisierung
visualize_logs(log)
#Berechnung der richtigen Antwortrate
_, acc_train = calculate_loss_and_accuracy(model, dataset_train, device)
_, acc_test = calculate_loss_and_accuracy(model, dataset_test, device)
print(f'Richtige Antwortrate (Lerndaten):{acc_train:.3f}')
print(f'Richtige Antwortrate (Bewertungsdaten):{acc_test:.3f}')
Ausgabe
Richtige Antwortrate (Lerndaten): 0.659
Richtige Antwortrate (Bewertungsdaten): 0.645
Codieren Sie den Eingabetext mit Vorwärts- und Rückwärts-RNNs und trainieren Sie das Modell.
\overleftarrow h_{T+1} = 0, \ \overleftarrow h_t = {\rm \overleftarrow{RNN}}(\mathrm{emb}(x_t), \overleftarrow h_{t+1}), \ y = {\rm softmax}(W^{(yh)} [\overrightarrow h_T; \overleftarrow h_1] + b^{(y)})
> $ \ Overrightarrow h_t \ in \ mathbb {R} ^ {d_h}, \ overleftarrow h_t \ in \ mathbb {R} ^ {d_h} $ sind jedoch die Zeiten $ t, die von den Vorwärts- bzw. Rückwärts-RNNs erhalten werden. Der verborgene Zustandsvektor von $, $ {\ rm \ overleftarrow {RNN}} (x, h) $ ist die RNN-Einheit, die den vorherigen Zustand aus der Eingabe $ x $ und dem verborgenen Zustand $ h $ des nächsten Mal berechnet, $ W ^ {( yh)} \ in \ mathbb {R} ^ {L \ times 2d_h} $ ist eine Matrix zur Vorhersage von Kategorien aus versteckten Zustandsvektoren, $ b ^ {(y)} \ in \ mathbb {R} ^ {L} $ Ist ein Bias-Begriff. Außerdem repräsentiert $ [a; b] $ die Verkettung der Vektoren $ a $ und $ b $.
> Experimentieren Sie außerdem mit mehrschichtigen bidirektionalen RNNs.
Setzen Sie `` `bidirektional```, ein Argument, das die Bidirektionalität angibt, auf` `` True``` und setzen Sie `` `NUM_LAYERS``` auf` `` 2```, um das Lernen auszuführen. ..
```python
#Parametereinstellungen
VOCAB_SIZE = len(set(word2id.values())) + 1
EMB_SIZE = 300
PADDING_IDX = len(set(word2id.values()))
OUTPUT_SIZE = 4
HIDDEN_SIZE = 50
NUM_LAYERS = 2
LEARNING_RATE = 5e-2
BATCH_SIZE = 32
NUM_EPOCHS = 10
#Modelldefinition
model = RNN(VOCAB_SIZE, EMB_SIZE, PADDING_IDX, OUTPUT_SIZE, HIDDEN_SIZE, NUM_LAYERS, emb_weights=weights, bidirectional=True)
#Definition der Verlustfunktion
criterion = nn.CrossEntropyLoss()
#Optimierungsdefinition
optimizer = torch.optim.SGD(model.parameters(), lr=LEARNING_RATE)
#Gerätespezifikation
device = torch.device('cuda')
#Modelllernen
log = train_model(dataset_train, dataset_valid, BATCH_SIZE, model, criterion, optimizer, NUM_EPOCHS, collate_fn=Padsequence(PADDING_IDX), device=device)
Ausgabe
epoch: 1, loss_train: 1.1731, accuracy_train: 0.4307, loss_valid: 1.1915, accuracy_valid: 0.4274, 19.3181sec
epoch: 2, loss_train: 1.0395, accuracy_train: 0.6116, loss_valid: 1.0555, accuracy_valid: 0.5996, 18.8118sec
epoch: 3, loss_train: 1.0529, accuracy_train: 0.5899, loss_valid: 1.0832, accuracy_valid: 0.5696, 18.9088sec
epoch: 4, loss_train: 0.9831, accuracy_train: 0.6351, loss_valid: 1.0144, accuracy_valid: 0.6235, 18.8913sec
epoch: 5, loss_train: 1.0622, accuracy_train: 0.5797, loss_valid: 1.1142, accuracy_valid: 0.5487, 19.0636sec
epoch: 6, loss_train: 1.0463, accuracy_train: 0.5741, loss_valid: 1.0972, accuracy_valid: 0.5367, 19.0612sec
epoch: 7, loss_train: 1.0056, accuracy_train: 0.6102, loss_valid: 1.0485, accuracy_valid: 0.5898, 19.0420sec
epoch: 8, loss_train: 0.9724, accuracy_train: 0.6294, loss_valid: 1.0278, accuracy_valid: 0.6093, 19.3077sec
epoch: 9, loss_train: 0.9469, accuracy_train: 0.6371, loss_valid: 0.9943, accuracy_valid: 0.6160, 19.2803sec
epoch: 10, loss_train: 0.9343, accuracy_train: 0.6451, loss_valid: 0.9867, accuracy_valid: 0.6235, 19.0755sec
#Protokollvisualisierung
visualize_logs(log)
#Berechnung der richtigen Antwortrate
_, acc_train = calculate_loss_and_accuracy(model, dataset_train, device)
_, acc_test = calculate_loss_and_accuracy(model, dataset_test, device)
print(f'Richtige Antwortrate (Lerndaten):{acc_train:.3f}')
print(f'Richtige Antwortrate (Bewertungsdaten):{acc_test:.3f}')
Ausgabe
Richtige Antwortrate (Lerndaten): 0.645
Richtige Antwortrate (Bewertungsdaten): 0.634
Es gibt eine Wortfolge $ \ boldsymbol x = (x_1, x_2, \ dots, x_T) $, die durch eine ID-Nummer dargestellt wird. $ T $ ist jedoch die Länge der Wortzeichenfolge, und $ x_t \ in \ mathbb {R} ^ {V} $ ist die One-Hot-Notation der Wort-ID-Nummer ($ V $ ist die Gesamtzahl der Wörter). Implementieren Sie mithilfe des Convolutional Neural Network (CNN) ein Modell, das die Kategorie $ y $ aus der Wortzeichenfolge $ \ boldsymbol x $ vorhersagt.
Die Konfiguration des Faltungsnetzwerks ist jedoch wie folgt.
- Anzahl der Dimensionen zum Einbetten von Wörtern: $ d_w $
p_t = g(W^{(px)} [\mathrm{emb}(x_{t-1}); \mathrm{emb}(x_t); \mathrm{emb}(x_{t+1})] + b^{(p)}) $]
> $ W ^ {(px)} \ in \ mathbb {R} ^ {d_h \ times 3d_w}, b ^ {(p)} \ in \ mathbb {R} ^ {d_h} $ ist jedoch ein CNN-Parameter. $ g $ ist eine Aktivierungsfunktion (z. B. $ \ tanh $ oder ReLU) und $ [a; b; c] $ ist eine Verkettung von Vektoren $ a, b, c $. Die Anzahl der Spalten in der Matrix $ W ^ {(px)} $ beträgt $ 3d_w $, da die lineare Konvertierung für die verketteten Worteinbettungen von drei Token durchgeführt wird.
Beim Maximalwertpooling wird zu jeder Zeit der Maximalwert für jede Dimension des Merkmalsvektors verwendet, und der Merkmalsvektor $ c \ in \ mathbb {R} ^ {d_h} $ des Eingabedokuments wird erhalten. Wenn $ c [i] $ den Wert der $ i $ -ten Dimension des Vektors $ c $ darstellt, wird das Maximalwert-Pooling durch die folgende Gleichung ausgedrückt.
>```math
c[i] = \max_{1 \leq t \leq T} p_t[i]
Schließlich enthält der Eingabedokument-Merkmalsvektor $ c $ die Matrix $ W ^ {(yc)} \ in \ mathbb {R} ^ {L \ times d_h} $ und den Bias-Term $ b ^ {(y)} \ in Wenden Sie die lineare Transformation mit \ mathbb {R} ^ {L} $ und der Softmax-Funktion an, um die Kategorie $ y $ vorherzusagen.
y = {\rm softmax}(W^{(yc)} c + b^{(y)})
> Beachten Sie, dass dieses Problem das Modell nicht trainiert, sondern nur die Berechnung von $ y $ mit einer zufällig initialisierten Gewichtsmatrix erfordert.
Implementieren Sie das angegebene Netzwerk.
Berechnen Sie nach der eingebetteten Schicht die Faltung mit `` `nn.Conv2d```. Der Maximalwert wird in der Serienlängenrichtung mit `` `max_pool``` erfasst, und die Vektoren werden in diesem Teil in Satzeinheiten aggregiert.
```python
from torch.nn import functional as F
class CNN(nn.Module):
def __init__(self, vocab_size, emb_size, padding_idx, output_size, out_channels, kernel_heights, stride, padding, emb_weights=None):
super().__init__()
if emb_weights != None: #Geben Sie das Gewicht der eingebetteten Ebene an, falls angegeben_Mit Gewichten initialisieren
self.emb = nn.Embedding.from_pretrained(emb_weights, padding_idx=padding_idx)
else:
self.emb = nn.Embedding(vocab_size, emb_size, padding_idx=padding_idx)
self.conv = nn.Conv2d(1, out_channels, (kernel_heights, emb_size), stride, (padding, 0))
self.drop = nn.Dropout(0.3)
self.fc = nn.Linear(out_channels, output_size)
def forward(self, x):
# x.size() = (batch_size, seq_len)
emb = self.emb(x).unsqueeze(1)
# emb.size() = (batch_size, 1, seq_len, emb_size)
conv = self.conv(emb)
# conv.size() = (batch_size, out_channels, seq_len, 1)
act = F.relu(conv.squeeze(3))
# act.size() = (batch_size, out_channels, seq_len)
max_pool = F.max_pool1d(act, act.size()[2])
# max_pool.size() = (batch_size, out_channels, 1) -> seq_Holen Sie sich den Maximalwert in Len-Richtung
out = self.fc(self.drop(max_pool.squeeze(2)))
# out.size() = (batch_size, output_size)
return out
#Parametereinstellungen
VOCAB_SIZE = len(set(word2id.values())) + 1
EMB_SIZE = 300
PADDING_IDX = len(set(word2id.values()))
OUTPUT_SIZE = 4
OUT_CHANNELS = 100
KERNEL_HEIGHTS = 3
STRIDE = 1
PADDING = 1
#Modelldefinition
model = CNN(VOCAB_SIZE, EMB_SIZE, PADDING_IDX, OUTPUT_SIZE, OUT_CHANNELS, KERNEL_HEIGHTS, STRIDE, PADDING, emb_weights=weights)
#Holen Sie sich die ersten 10 vorhergesagten Werte
for i in range(10):
X = dataset_train[i]['inputs']
print(torch.softmax(model(X.unsqueeze(0)), dim=-1))
Ausgabe
tensor([[0.2607, 0.2267, 0.2121, 0.3006]], grad_fn=<SoftmaxBackward>)
tensor([[0.2349, 0.2660, 0.2462, 0.2529]], grad_fn=<SoftmaxBackward>)
tensor([[0.2305, 0.2649, 0.2099, 0.2948]], grad_fn=<SoftmaxBackward>)
tensor([[0.2569, 0.2409, 0.2418, 0.2604]], grad_fn=<SoftmaxBackward>)
tensor([[0.2610, 0.2149, 0.2355, 0.2886]], grad_fn=<SoftmaxBackward>)
tensor([[0.2627, 0.2363, 0.2388, 0.2622]], grad_fn=<SoftmaxBackward>)
tensor([[0.2694, 0.2434, 0.2224, 0.2648]], grad_fn=<SoftmaxBackward>)
tensor([[0.2423, 0.2465, 0.2365, 0.2747]], grad_fn=<SoftmaxBackward>)
tensor([[0.2591, 0.2695, 0.2468, 0.2246]], grad_fn=<SoftmaxBackward>)
tensor([[0.2794, 0.2465, 0.2234, 0.2507]], grad_fn=<SoftmaxBackward>)
Lernen Sie das in Aufgabe 86 konstruierte Modell mit der SGD-Methode (Stochastic Gradient Descent). Trainieren Sie das Modell, während Sie den Verlust und die richtige Antwortrate in den Trainingsdaten sowie den Verlust und die richtige Antwortrate in den Bewertungsdaten anzeigen, und beenden Sie den Vorgang mit einem geeigneten Standard (z. B. 10 Epochen).
#Parametereinstellungen
VOCAB_SIZE = len(set(word2id.values())) + 1
EMB_SIZE = 300
PADDING_IDX = len(set(word2id.values()))
OUTPUT_SIZE = 4
OUT_CHANNELS = 100
KERNEL_HEIGHTS = 3
STRIDE = 1
PADDING = 1
LEARNING_RATE = 5e-2
BATCH_SIZE = 64
NUM_EPOCHS = 10
#Modelldefinition
model = CNN(VOCAB_SIZE, EMB_SIZE, PADDING_IDX, OUTPUT_SIZE, OUT_CHANNELS, KERNEL_HEIGHTS, STRIDE, PADDING, emb_weights=weights)
#Definition der Verlustfunktion
criterion = nn.CrossEntropyLoss()
#Optimierungsdefinition
optimizer = torch.optim.SGD(model.parameters(), lr=LEARNING_RATE)
#Gerätespezifikation
device = torch.device('cuda')
#Modelllernen
log = train_model(dataset_train, dataset_valid, BATCH_SIZE, model, criterion, optimizer, NUM_EPOCHS, collate_fn=Padsequence(PADDING_IDX), device=device)
Ausgabe
epoch: 1, loss_train: 1.0671, accuracy_train: 0.5543, loss_valid: 1.0744, accuracy_valid: 0.5726, 12.9214sec
epoch: 2, loss_train: 0.9891, accuracy_train: 0.6594, loss_valid: 1.0148, accuracy_valid: 0.6452, 12.6483sec
epoch: 3, loss_train: 0.9098, accuracy_train: 0.6928, loss_valid: 0.9470, accuracy_valid: 0.6729, 12.7305sec
epoch: 4, loss_train: 0.8481, accuracy_train: 0.7139, loss_valid: 0.8956, accuracy_valid: 0.7028, 12.7967sec
epoch: 5, loss_train: 0.8055, accuracy_train: 0.7250, loss_valid: 0.8634, accuracy_valid: 0.7096, 12.6543sec
epoch: 6, loss_train: 0.7728, accuracy_train: 0.7361, loss_valid: 0.8425, accuracy_valid: 0.7141, 12.7423sec
epoch: 7, loss_train: 0.7527, accuracy_train: 0.7396, loss_valid: 0.8307, accuracy_valid: 0.7216, 12.6718sec
epoch: 8, loss_train: 0.7403, accuracy_train: 0.7432, loss_valid: 0.8227, accuracy_valid: 0.7246, 12.5854sec
epoch: 9, loss_train: 0.7346, accuracy_train: 0.7447, loss_valid: 0.8177, accuracy_valid: 0.7216, 12.4846sec
epoch: 10, loss_train: 0.7331, accuracy_train: 0.7448, loss_valid: 0.8167, accuracy_valid: 0.7231, 12.7443sec
#Protokollvisualisierung
visualize_logs(log)
#Berechnung der richtigen Antwortrate
_, acc_train = calculate_loss_and_accuracy(model, dataset_train, device)
_, acc_test = calculate_loss_and_accuracy(model, dataset_test, device)
print(f'Richtige Antwortrate (Lerndaten):{acc_train:.3f}')
print(f'Richtige Antwortrate (Bewertungsdaten):{acc_test:.3f}')
Ausgabe
Richtige Antwortrate (Lerndaten): 0.745
Richtige Antwortrate (Bewertungsdaten): 0.719
Erstellen Sie einen Hochleistungskategorie-Klassifikator, indem Sie den Code in Frage 85 und Frage 87 ändern und die Form und die Hyperparameter des neuronalen Netzwerks anpassen.
Dieses Mal werde ich ein Netzwerk ausprobieren, das das in Convolutional Neural Networks for Satzklassifizierung vorgeschlagene TextCNN vereinfacht. In der vorherigen Frage hat CNN nur Filter mit einer Breite von 3 gelernt, aber dieses Netzwerk verwendet Filter mit drei Breiten von 2, 3 und 4.
from torch.nn import functional as F
class textCNN(nn.Module):
def __init__(self, vocab_size, emb_size, padding_idx, output_size, out_channels, conv_params, drop_rate, emb_weights=None):
super().__init__()
if emb_weights != None: #Geben Sie das Gewicht der eingebetteten Ebene an, falls angegeben_Mit Gewichten initialisieren
self.emb = nn.Embedding.from_pretrained(emb_weights, padding_idx=padding_idx)
else:
self.emb = nn.Embedding(vocab_size, emb_size, padding_idx=padding_idx)
self.convs = nn.ModuleList([nn.Conv2d(1, out_channels, (kernel_height, emb_size), padding=(padding, 0)) for kernel_height, padding in conv_params])
self.drop = nn.Dropout(drop_rate)
self.fc = nn.Linear(len(conv_params) * out_channels, output_size)
def forward(self, x):
# x.size() = (batch_size, seq_len)
emb = self.emb(x).unsqueeze(1)
# emb.size() = (batch_size, 1, seq_len, emb_size)
conv = [F.relu(conv(emb)).squeeze(3) for i, conv in enumerate(self.convs)]
# conv[i].size() = (batch_size, out_channels, seq_len + padding * 2 - kernel_height + 1)
max_pool = [F.max_pool1d(i, i.size(2)) for i in conv]
# max_pool[i].size() = (batch_size, out_channels, 1) -> seq_Holen Sie sich den Maximalwert in Len-Richtung
max_pool_cat = torch.cat(max_pool, 1)
# max_pool_cat.size() = (batch_size, len(conv_params) * out_channels, 1) ->Kombinieren Sie die Ergebnisse nach Filter
out = self.fc(self.drop(max_pool_cat.squeeze(2)))
# out.size() = (batch_size, output_size)
return out
Verwenden Sie auch optuna für die Parameteroptimierung wie in Kapitel 6.
!pip install optuna
import optuna
def objective(trial):
#Parametersatz, der eingestellt werden soll
emb_size = int(trial.suggest_discrete_uniform('emb_size', 100, 400, 100))
out_channels = int(trial.suggest_discrete_uniform('out_channels', 50, 200, 50))
drop_rate = trial.suggest_discrete_uniform('drop_rate', 0.0, 0.5, 0.1)
learning_rate = trial.suggest_loguniform('learning_rate', 5e-4, 5e-2)
momentum = trial.suggest_discrete_uniform('momentum', 0.5, 0.9, 0.1)
batch_size = int(trial.suggest_discrete_uniform('batch_size', 16, 128, 16))
#Feste Parametereinstellungen
VOCAB_SIZE = len(set(word2id.values())) + 1
PADDING_IDX = len(set(word2id.values()))
OUTPUT_SIZE = 4
CONV_PARAMS = [[2, 0], [3, 1], [4, 2]]
NUM_EPOCHS = 30
#Modelldefinition
model = textCNN(VOCAB_SIZE, EMB_SIZE, PADDING_IDX, OUTPUT_SIZE, out_channels, CONV_PARAMS, drop_rate, emb_weights=weights)
#Definition der Verlustfunktion
criterion = nn.CrossEntropyLoss()
#Optimierungsdefinition
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, momentum=momentum)
#Gerätespezifikation
device = torch.device('cuda')
#Modelllernen
log = train_model(dataset_train, dataset_valid, batch_size, model, criterion, optimizer, NUM_EPOCHS, collate_fn=Padsequence(PADDING_IDX), device=device)
#Verlustberechnung
loss_valid, _ = calculate_loss_and_accuracy(model, dataset_valid, device, criterion=criterion)
return loss_valid
Führt eine Parametersuche durch.
#Optimierung
study = optuna.create_study()
study.optimize(objective, timeout=7200)
#Ergebnisse anzeigen
print('Best trial:')
trial = study.best_trial
print(' Value: {:.3f}'.format(trial.value))
print(' Params: ')
for key, value in trial.params.items():
print(' {}: {}'.format(key, value))
Ausgabe
Best trial:
Value: 0.469
Params:
emb_size: 300.0
out_channels: 100.0
drop_rate: 0.4
learning_rate: 0.013345934577557608
momentum: 0.8
batch_size: 32.0
Trainieren Sie das Modell mit den gesuchten Parametern.
#Parametereinstellungen
VOCAB_SIZE = len(set(word2id.values())) + 1
EMB_SIZE = int(trial.params['emb_size'])
PADDING_IDX = len(set(word2id.values()))
OUTPUT_SIZE = 4
OUT_CHANNELS = int(trial.params['out_channels'])
CONV_PARAMS = [[2, 0], [3, 1], [4, 2]]
DROP_RATE = trial.params['drop_rate']
LEARNING_RATE = trial.params['learning_rate']
BATCH_SIZE = int(trial.params['batch_size'])
NUM_EPOCHS = 30
#Modelldefinition
model = textCNN(VOCAB_SIZE, EMB_SIZE, PADDING_IDX, OUTPUT_SIZE, OUT_CHANNELS, CONV_PARAMS, DROP_RATE, emb_weights=weights)
print(model)
#Definition der Verlustfunktion
criterion = nn.CrossEntropyLoss()
#Optimierungsdefinition
optimizer = torch.optim.SGD(model.parameters(), lr=LEARNING_RATE, momentum=0.9)
#Gerätespezifikation
device = torch.device('cuda')
#Modelllernen
log = train_model(dataset_train, dataset_valid, BATCH_SIZE, model, criterion, optimizer, NUM_EPOCHS, collate_fn=Padsequence(PADDING_IDX), device=device)
Ausgabe
textCNN(
(emb): Embedding(9406, 300, padding_idx=9405)
(convs): ModuleList(
(0): Conv2d(1, 100, kernel_size=(2, 300), stride=(1, 1))
(1): Conv2d(1, 100, kernel_size=(3, 300), stride=(1, 1), padding=(1, 0))
(2): Conv2d(1, 100, kernel_size=(4, 300), stride=(1, 1), padding=(2, 0))
)
(drop): Dropout(p=0.4, inplace=False)
(fc): Linear(in_features=300, out_features=4, bias=True)
)
epoch: 1, loss_train: 0.7908, accuracy_train: 0.7239, loss_valid: 0.8660, accuracy_valid: 0.6901, 12.2279sec
epoch: 2, loss_train: 0.5800, accuracy_train: 0.7944, loss_valid: 0.7384, accuracy_valid: 0.7485, 12.1637sec
epoch: 3, loss_train: 0.3951, accuracy_train: 0.8738, loss_valid: 0.6189, accuracy_valid: 0.7919, 12.1612sec
epoch: 4, loss_train: 0.2713, accuracy_train: 0.9217, loss_valid: 0.5499, accuracy_valid: 0.8136, 12.1877sec
epoch: 5, loss_train: 0.1913, accuracy_train: 0.9593, loss_valid: 0.5176, accuracy_valid: 0.8293, 12.1722sec
epoch: 6, loss_train: 0.1322, accuracy_train: 0.9749, loss_valid: 0.5042, accuracy_valid: 0.8234, 12.4483sec
epoch: 7, loss_train: 0.1033, accuracy_train: 0.9807, loss_valid: 0.4922, accuracy_valid: 0.8323, 12.1556sec
epoch: 8, loss_train: 0.0723, accuracy_train: 0.9943, loss_valid: 0.4900, accuracy_valid: 0.8308, 12.0309sec
epoch: 9, loss_train: 0.0537, accuracy_train: 0.9966, loss_valid: 0.4903, accuracy_valid: 0.8346, 11.9471sec
epoch: 10, loss_train: 0.0414, accuracy_train: 0.9966, loss_valid: 0.4801, accuracy_valid: 0.8421, 11.9275sec
epoch: 11, loss_train: 0.0366, accuracy_train: 0.9978, loss_valid: 0.4943, accuracy_valid: 0.8406, 11.9691sec
epoch: 12, loss_train: 0.0292, accuracy_train: 0.9983, loss_valid: 0.4839, accuracy_valid: 0.8436, 11.9665sec
epoch: 13, loss_train: 0.0271, accuracy_train: 0.9982, loss_valid: 0.5042, accuracy_valid: 0.8421, 11.9634sec
epoch: 14, loss_train: 0.0222, accuracy_train: 0.9986, loss_valid: 0.4912, accuracy_valid: 0.8458, 11.9298sec
epoch: 15, loss_train: 0.0194, accuracy_train: 0.9988, loss_valid: 0.4925, accuracy_valid: 0.8436, 11.9375sec
epoch: 16, loss_train: 0.0176, accuracy_train: 0.9988, loss_valid: 0.5074, accuracy_valid: 0.8451, 11.9333sec
epoch: 17, loss_train: 0.0163, accuracy_train: 0.9991, loss_valid: 0.5124, accuracy_valid: 0.8436, 11.9137sec
#Protokollvisualisierung
visualize_logs(log)
#Berechnung der richtigen Antwortrate
_, acc_train = calculate_loss_and_accuracy(model, dataset_train, device)
_, acc_test = calculate_loss_and_accuracy(model, dataset_test, device)
print(f'Richtige Antwortrate (Lerndaten):{acc_train:.3f}')
print(f'Richtige Antwortrate (Bewertungsdaten):{acc_test:.3f}')
Ausgabe
Richtige Antwortrate (Lerndaten): 0.999
Richtige Antwortrate (Bewertungsdaten): 0.851
Erstellen Sie ein Modell, das die Überschriften von Nachrichtenartikeln in Kategorien unterteilt, beginnend mit einem vorab trainierten Sprachmodell (z. B. BERT).
[PyTorch] Einführung in die Dokumentklassifizierung mit BERT ist in einem anderen Artikel ausgeschnitten. Hier wird nur das Ergebnis der korrekten Antwortrate angezeigt.
Richtige Antwortrate (Lerndaten): 0.993
Richtige Antwortrate (Bewertungsdaten): 0.948
Sprachverarbeitung 100 Klopfen sind so konzipiert, dass Sie nicht nur die Verarbeitung natürlicher Sprache selbst lernen können, sondern auch die grundlegende Datenverarbeitung und das allgemeine maschinelle Lernen. Sogar diejenigen, die maschinelles Lernen in Online-Kursen studieren, können sehr gute Ergebnisse erzielen. Probieren Sie es also bitte aus.
Zur Antwort auf alle 100 Fragen
Recommended Posts