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 8: Neuronales Netz" aus den folgenden Kapiteln 1 bis 10 zusammen.
Wir verwenden Google Colaboratory für Antworten. 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/chapter08.ipynb).
Implementieren Sie ein Kategorisierungsmodell mit einem neuronalen Netzwerk basierend auf der Kategorisierung der in Kapitel 6 beschriebenen Nachrichtenartikel. Verwenden Sie in diesem Kapitel maschinelle Lernplattformen wie PyTorch, TensorFlow und Chainer.
Ich möchte die in Aufgabe 50 erstellten Trainingsdaten, Verifizierungsdaten und Bewertungsdaten in Matrizen und Vektoren konvertieren. Zum Beispiel möchte ich für Trainingsdaten eine Matrix $ X $ erstellen, in der die Merkmalsvektoren $ \ boldsymbol {x} _i $ aller Fälle $ x_i $ angeordnet sind, und eine Matrix (Vektor) $ Y $, in der die richtigen Antwortbezeichnungen angeordnet sind.
X = \begin{pmatrix} \boldsymbol{x}_1 \ \boldsymbol{x}_2 \ \dots \ \boldsymbol{x}_n \ \end{pmatrix} \in \mathbb{R}^{n \times d}, Y = \begin{pmatrix} y_1 \ y_2 \ \dots \ y_n \ \end{pmatrix} \in \mathbb{N}^{n}
>
> Hier ist $ n $ die Anzahl der Fälle von Trainingsdaten, und $ \ boldsymbol x_i \ in \ mathbb {R} ^ d $ und $ y_i \ in \ mathbb N $ sind $ i \ in \ {1. \ dots, n \} Repräsentiert den Merkmalsvektor und die korrekte Bezeichnung des $ -ten Falls.
> Dieses Mal gibt es vier Kategorien: "Business", "Wissenschaft und Technologie", "Unterhaltung" und "Gesundheit". Wenn $ \ mathbb N_ {<4} $ eine natürliche Zahl (einschließlich $ 0 $) darstellt, die kleiner als $ 4 $ ist, lautet die korrekte Bezeichnung $ y_i $ in jedem Fall $ y_i \ in \ mathbb N_ {<4} $ Kann ausgedrückt werden durch.
> Im Folgenden wird die Anzahl der Etikettentypen durch $ L $ dargestellt ($ L = 4 $ in dieser Klassifizierungsaufgabe).
>
> Der Merkmalsvektor $ \ boldsymbol x_i $ des $ i $ -ten Falls wird durch die folgende Gleichung berechnet.
>
> $$\boldsymbol x_i = \frac{1}{T_i} \sum_{t=1}^{T_i} \mathrm{emb}(w_{i,t})$$
>
> Hier ist der $ i $ -te Fall $ T_i $ (Artikelüberschrift) Wortketten $ (w_ {i, 1}, w_ {i, 2}, \ dots, w_ {i, T_i}) $ , Und $ \ mathrm {emb} (w) \ in \ mathbb {R} ^ d $ ist ein Wortvektor (die Anzahl der Dimensionen ist $ d $), der dem Wort $ w $ entspricht. Das heißt, $ \ boldsymbol x_i $ ist die Artikelüberschrift des $ i $ -ten Falls, ausgedrückt durch den Durchschnitt der Vektoren der in der Überschrift enthaltenen Wörter. Dieses Mal sollte der in Frage 60 heruntergeladene Wortvektor verwendet werden. Da wir einen Wortvektor mit der Dimension $ 300 $ verwendet haben, ist $ d = 300 $.
> Die Bezeichnung $ y_i $ des $ i $ -ten Falls ist wie folgt definiert.
>
>```math
y_i = \begin{cases}
0 & (\mbox{Artikel}\boldsymbol x_i\mbox{If ist in der Kategorie "Business"}) \\
1 & (\mbox{Artikel}\boldsymbol x_i\mbox{Ist in der Kategorie "Wissenschaft und Technologie"}) \\
2 & (\mbox{Artikel}\boldsymbol x_i\mbox{If ist in der Kategorie "Unterhaltung"}) \\
3 & (\mbox{Artikel}\boldsymbol x_i\mbox{If ist in der Kategorie "Gesundheit"}) \\
\end{cases}
Wenn zwischen dem Kategorienamen und der Etikettennummer eine Eins-zu-Eins-Entsprechung besteht, muss die Entsprechung nicht der in der obigen Formel angegebenen entsprechen.
Erstellen Sie anhand der obigen Spezifikationen die folgende Matrix / den folgenden Vektor und speichern Sie sie in einer Datei.
- Trainingsdaten-Feature-Matrix: $ X_ {\ rm train} \ in \ mathbb {R} ^ {N_t \ times d} $
- Trainingsdaten-Beschriftungsvektor: $ Y_ {\ rm train} \ in \ mathbb {N} ^ {N_t} $
- Funktionsmatrix für Validierungsdaten: $ X_ {\ rm valid} \ in \ mathbb {R} ^ {N_v \ times d} $
- Validierungsdaten-Beschriftungsvektor: $ Y_ {\ rm valid} \ in \ mathbb {N} ^ {N_v} $
- Funktionsmatrix für Auswertungsdaten: $ X_ {\ rm test} \ in \ mathbb {R} ^ {N_e \ times d} $
- Bewertungsdaten-Beschriftungsvektor: $ Y_ {\ rm test} \ in \ mathbb {N} ^ {N_e} $
Beachten Sie, dass $ N_t, N_v und N_e $ die Anzahl der Fälle von Trainingsdaten, die Anzahl der Fälle von Verifizierungsdaten bzw. die Anzahl der Fälle von Bewertungsdaten sind.
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 die Verarbeitung genau die gleiche wie bei Problem 50, so dass es kein Problem gibt, die dort erstellten Daten zu lesen.
#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'])
#Datenspeicher
train.to_csv('./train.txt', sep='\t', index=False)
valid.to_csv('./valid.txt', sep='\t', index=False)
test.to_csv('./test.txt', sep='\t', index=False)
#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
Laden Sie als Nächstes den in Frage 60 verwendeten gelernten Wortvektor herunter und laden Sie ihn.
#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
from gensim.models import KeyedVectors
#Gelernten Wortvektor laden
model = KeyedVectors.load_word2vec_format('./GoogleNews-vectors-negative300.bin.gz', binary=True)
Erstellen Sie abschließend einen Feature-Vektor und einen Beschriftungsvektor und speichern Sie diese. Danach wird es in den Tensor-Typ konvertiert, damit es von PyTorch als Eingabe für das neuronale Netz verwendet werden kann.
import string
import torch
def transform_w2v(text):
table = str.maketrans(string.punctuation, ' '*len(string.punctuation))
words = text.translate(table).split() #Nachdem Sie das Symbol durch ein Leerzeichen ersetzt haben, teilen Sie es durch ein Leerzeichen und listen Sie es auf.
vec = [model[word] for word in words if word in model] #Wort für Wort vektorisiert
return torch.tensor(sum(vec) / len(vec)) #Konvertieren Sie den Durchschnittsvektor in Tensortyp und Ausgabe
#Erstellen eines Feature-Vektors
X_train = torch.stack([transform_w2v(text) for text in train['TITLE']])
X_valid = torch.stack([transform_w2v(text) for text in valid['TITLE']])
X_test = torch.stack([transform_w2v(text) for text in test['TITLE']])
print(X_train.size())
print(X_train)
Ausgabe
torch.Size([10684, 300])
tensor([[ 0.0837, 0.0056, 0.0068, ..., 0.0751, 0.0433, -0.0868],
[ 0.0272, 0.0266, -0.0947, ..., -0.1046, -0.0489, -0.0092],
[ 0.0577, -0.0159, -0.0780, ..., -0.0421, 0.1229, 0.0876],
...,
[ 0.0392, -0.0052, 0.0686, ..., -0.0175, 0.0061, -0.0224],
[ 0.0798, 0.1017, 0.1066, ..., -0.0752, 0.0623, 0.1138],
[ 0.1664, 0.0451, 0.0508, ..., -0.0531, -0.0183, -0.0039]])
#Erstellen eines Beschriftungsvektors
category_dict = {'b': 0, 't': 1, 'e':2, 'm':3}
y_train = torch.tensor(train['CATEGORY'].map(lambda x: category_dict[x]).values)
y_valid = torch.tensor(valid['CATEGORY'].map(lambda x: category_dict[x]).values)
y_test = torch.tensor(test['CATEGORY'].map(lambda x: category_dict[x]).values)
print(y_train.size())
print(y_train)
Ausgabe
torch.Size([10684])
tensor([0, 1, 3, ..., 0, 3, 2])
#sparen
torch.save(X_train, 'X_train.pt')
torch.save(X_valid, 'X_valid.pt')
torch.save(X_test, 'X_test.pt')
torch.save(y_train, 'y_train.pt')
torch.save(y_valid, 'y_valid.pt')
torch.save(y_test, 'y_test.pt')
Lesen Sie die in Frage 70 gespeicherte Matrix und führen Sie die folgenden Berechnungen für die Trainingsdaten durch.
\hat{y}1=softmax(x_1W),\\hat{Y}=softmax(X{[1:4]}W)
$ Softmax $ ist jedoch die Softmax-Funktion, $ X_ {[1: 4]} ∈ \ mathbb {R} ^ {4 × d} $ ist der Merkmalsvektor $ x_1 $, $ x_2 $, $ x_3 $, $ x_4 $ Ist eine vertikal angeordnete Matrix.
>```math
X_{[1:4]}=\begin{pmatrix}x_1\\x_2\\x_3\\x_4\end{pmatrix}
Die Matrix $ W \ in \ mathbb {R} ^ {d \ times L} $ ist die Gewichtsmatrix des einschichtigen neuronalen Netzwerks, die mit einem Zufallswert initialisiert werden kann (gelernt in Aufgabe 73 und höher). .. Beachten Sie, dass $ \ hat {\ boldsymbol y_1} \ in \ mathbb {R} ^ L $ ein Vektor ist, der die Wahrscheinlichkeit der Zugehörigkeit zu jeder Kategorie darstellt, wenn der Fall $ x_1 $ durch die verlernte Matrix $ W $ klassifiziert wird. In ähnlicher Weise drückt $ \ hat {Y} \ in \ mathbb {R} ^ {n \ times L} $ die Wahrscheinlichkeit der Zugehörigkeit zu jeder Kategorie als Matrix für die Trainingsdatenbeispiele $ x_1, x_2, x_3, x_4 $ aus. tun.
Definieren Sie zunächst ein einschichtiges neuronales Netzwerk mit dem Namen "SLPNet". Definieren Sie die Ebenen, aus denen das Netzwerk besteht, mit `__init__``` und ordnen Sie die Ebenen, durch die die Eingabedaten weitergeleitet werden, mit der Methode`
forward``` an.
from torch import nn
class SLPNet(nn.Module):
def __init__(self, input_size, output_size):
super().__init__()
self.fc = nn.Linear(input_size, output_size, bias=False)
nn.init.normal_(self.fc.weight, 0.0, 1.0) #Initialisieren Sie Gewichte mit normalen Zufallszahlen
def forward(self, x):
x = self.fc(x)
return x
Anschließend wird das definierte Modell initialisiert und die angegebenen Berechnungen durchgeführt.
model = SLPNet(300, 4) #Initialisierung eines einschichtigen neuronalen Netzwerks
y_hat_1 = torch.softmax(model.forward(X_train[:1]), dim=-1)
print(y_hat_1)
Ausgabe
tensor([[0.4273, 0.0958, 0.2492, 0.2277]], grad_fn=<SoftmaxBackward>)
Y_hat = torch.softmax(model.forward(X_train[:4]), dim=-1)
print(Y_hat)
Ausgabe
tensor([[0.4273, 0.0958, 0.2492, 0.2277],
[0.2445, 0.2431, 0.0197, 0.4927],
[0.7853, 0.1132, 0.0291, 0.0724],
[0.5279, 0.2319, 0.0873, 0.1529]], grad_fn=<SoftmaxBackward>)
Berechnen Sie den Kreuzentropieverlust und den Gradienten für die Matrix $ W $ für den Fall $ x_1 $ und die Fallmenge $ x_1 $, $ x_2 $, $ x_3 $, $ x_4 $ der Trainingsdaten. Für einen bestimmten Fall $ x_i $ wird der Verlust durch die folgende Gleichung berechnet.
Der Kreuzentropieverlust für die Fallmenge ist jedoch der Durchschnitt der Verluste für jeden in der Menge enthaltenen Fall.
Hier verwenden wir den `CrossEntropyLoss``` des`
nn``` Moduls.
Durch Eingabe des Ausgabevektors und des Beschriftungsvektors des Modells kann der durchschnittliche Verlust der obigen Gleichung berechnet werden.
criterion = nn.CrossEntropyLoss()
l_1 = criterion(model.forward(X_train[:1]), y_train[:1]) #Der Eingabevektor ist der Wert vor Softmax
model.zero_grad() #Gradient auf Null initialisieren
l_1.backward() #Berechnen Sie den Gradienten
print(f'Verlust: {l_1:.4f}')
print(f'Steigung:\n{model.fc.weight.grad}')
Ausgabe
Verlust: 2.9706
Steigung:
tensor([[-0.0794, -0.0053, -0.0065, ..., -0.0713, -0.0411, 0.0823],
[ 0.0022, 0.0001, 0.0002, ..., 0.0020, 0.0011, -0.0023],
[ 0.0611, 0.0041, 0.0050, ..., 0.0549, 0.0316, -0.0634],
[ 0.0161, 0.0011, 0.0013, ..., 0.0144, 0.0083, -0.0167]])
l = criterion(model.forward(X_train[:4]), y_train[:4])
model.zero_grad()
l.backward()
print(f'Verlust: {l:.4f}')
print(f'Steigung:\n{model.fc.weight.grad}')
Ausgabe
Verlust: 3.0799
Steigung:
tensor([[-0.0207, 0.0079, -0.0090, ..., -0.0350, -0.0003, 0.0232],
[-0.0055, -0.0063, 0.0225, ..., 0.0252, 0.0166, 0.0039],
[ 0.0325, -0.0089, -0.0215, ..., 0.0084, 0.0122, -0.0030],
[-0.0063, 0.0072, 0.0081, ..., 0.0014, -0.0285, -0.0241]])
Lernen Sie die Matrix $ W $ mit der SGD-Methode (Stochastic Gradient Descent). Das Lernen kann nach einem geeigneten Standard abgeschlossen werden (z. B. "Ende in 100 Epochen").
Bereiten Sie zum Lernen `Dataset``` und`
Dataloadervor.
dataset```Ist ein Typ, der den Merkmalsvektor und den Beschriftungsvektor zusammenhalten kann und den ursprünglichen Tensor mithilfe der folgenden Klasse transformiert.
from torch.utils.data import Dataset
class CreateDataset(Dataset):
def __init__(self, X, y): #Geben Sie die Komponenten des Datasets an
self.X = X
self.y = y
def __len__(self): # len(dataset)Geben Sie den Wert an, mit dem zurückgegeben werden soll
return len(self.y)
def __getitem__(self, idx): # dataset[idx]Geben Sie den Wert an, mit dem zurückgegeben werden soll
if isinstance(idx, torch.Tensor):
idx = idx.tolist()
return [self.X[idx], self.y[idx]]
Erstellen Sie nach der Konvertierung "DataLoader". `Dataloader``` kann in`
Dataset eingegeben werden, und die in der angegebenen Größe (`` `batch_size
) gesammelten Daten können der Reihe nach abgerufen werden. Hier wird `batch_size = 1``` gesetzt, was bedeutet, dass`
Dataloader erstellt wird, um Daten einzeln abzurufen. Beachten Sie, dass `` `Dataloader
in der Reihenfolge mit der
for``` -Anweisung abgerufen werden kann oder der nächste Block mit`` next (iter (Dataloader))
`aufgerufen werden kann.
from torch.utils.data import DataLoader
dataset_train = CreateDataset(X_train, y_train)
dataset_valid = CreateDataset(X_valid, y_valid)
dataset_test = CreateDataset(X_test, y_test)
dataloader_train = DataLoader(dataset_train, batch_size=1, shuffle=True)
dataloader_valid = DataLoader(dataset_valid, batch_size=len(dataset_valid), shuffle=False)
dataloader_test = DataLoader(dataset_test, batch_size=len(dataset_test), shuffle=False)
Nachdem die Daten fertig sind, trainieren wir die Matrix $ W $. Die Definition des Modells und die Definition der Verlustfunktion sind dieselben wie in der vorherigen Frage. Dieses Mal werden wir auch die Gewichte aus dem berechneten Gradienten aktualisieren, sodass wir auch einen Optimierer definieren. Hier wird SGD gemäß den Anweisungen eingestellt. Wenn die Teile fertig sind, wird das Lernen mit der Anzahl der auf 10 eingestellten Epochen ausgeführt.
#Modelldefinition
model = SLPNet(300, 4)
#Definition der Verlustfunktion
criterion = nn.CrossEntropyLoss()
#Optimierungsdefinition
optimizer = torch.optim.SGD(model.parameters(), lr=1e-1)
#Lernen
num_epochs = 10
for epoch in range(num_epochs):
#Auf Trainingsmodus einstellen
model.train()
loss_train = 0.0
for i, (inputs, labels) in enumerate(dataloader_train):
#Gradient auf Null initialisieren
optimizer.zero_grad()
#Vorwärtsausbreitung+Fehler bei der Weitergabe+Gewichtsaktualisierung
outputs = model.forward(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
#Rekordverlust
loss_train += loss.item()
#Berechnung des Batch-Durchschnittsverlusts
loss_train = loss_train / i
#Berechnung des Validierungsdatenverlusts
model.eval()
with torch.no_grad():
inputs, labels = next(iter(dataloader_valid))
outputs = model.forward(inputs)
loss_valid = criterion(outputs, labels)
#Ausgabeprotokoll
print(f'epoch: {epoch + 1}, loss_train: {loss_train:.4f}, loss_valid: {loss_valid:.4f}')
Ausgabe
epoch: 1, loss_train: 0.4745, loss_valid: 0.3637
epoch: 2, loss_train: 0.3173, loss_valid: 0.3306
epoch: 3, loss_train: 0.2884, loss_valid: 0.3208
epoch: 4, loss_train: 0.2716, loss_valid: 0.3150
epoch: 5, loss_train: 0.2615, loss_valid: 0.3141
epoch: 6, loss_train: 0.2519, loss_valid: 0.3092
epoch: 7, loss_train: 0.2474, loss_valid: 0.3114
epoch: 8, loss_train: 0.2431, loss_valid: 0.3072
epoch: 9, loss_train: 0.2393, loss_valid: 0.3096
epoch: 10, loss_train: 0.2359, loss_valid: 0.3219
Im Verlauf der Epoche können wir feststellen, dass der Verlust von Trainingsdaten allmählich abnimmt.
Wenn Sie die Fälle von Trainingsdaten und Bewertungsdaten anhand der in Frage 73 erhaltenen Matrix klassifizieren, ermitteln Sie für jede die richtige Antwortrate.
Definieren Sie mit dem trainierten Modell und `` `Dataloader``` als Eingabe eine Funktion zur Berechnung der Genauigkeitsrate.
def calculate_accuracy(model, loader):
model.eval()
total = 0
correct = 0
with torch.no_grad():
for inputs, labels in loader:
outputs = model(inputs)
pred = torch.argmax(outputs, dim=-1)
total += len(inputs)
correct += (pred == labels).sum().item()
return correct / total
acc_train = calculate_accuracy(model, dataloader_train)
acc_test = calculate_accuracy(model, dataloader_test)
print(f'Richtige Antwortrate (Lerndaten):{acc_train:.3f}')
print(f'Richtige Antwortrate (Bewertungsdaten):{acc_test:.3f}')
Ausgabe
Richtige Antwortrate (Lerndaten): 0.920
Richtige Antwortrate (Bewertungsdaten): 0.891
Durch Ändern des Codes von Frage 73 können jedes Mal, wenn die Parameteraktualisierung jeder Epoche abgeschlossen ist, der Verlust der Trainingsdaten, die korrekte Antwortrate, der Verlust der Verifizierungsdaten und die korrekte Antwortrate in einem Diagramm aufgezeichnet werden, um den Lernfortschritt zu überprüfen. Tu es.
Der Verlust und die korrekte Antwortrate werden aufgezeichnet, indem die Funktion in der vorherigen Frage so geändert wird, dass der Verlust auch berechnet und für jede Epoche angewendet werden kann.
def calculate_loss_and_accuracy(model, criterion, loader):
model.eval()
loss = 0.0
total = 0
correct = 0
with torch.no_grad():
for inputs, labels in loader:
outputs = model(inputs)
loss += criterion(outputs, labels).item()
pred = torch.argmax(outputs, dim=-1)
total += len(inputs)
correct += (pred == labels).sum().item()
return loss / len(loader), correct / total
#Modelldefinition
model = SLPNet(300, 4)
#Definition der Verlustfunktion
criterion = nn.CrossEntropyLoss()
#Optimierungsdefinition
optimizer = torch.optim.SGD(model.parameters(), lr=1e-1)
#Lernen
num_epochs = 30
log_train = []
log_valid = []
for epoch in range(num_epochs):
#Auf Trainingsmodus einstellen
model.train()
for inputs, labels in dataloader_train:
#Gradient auf Null initialisieren
optimizer.zero_grad()
#Vorwärtsausbreitung+Fehler bei der Weitergabe+Gewichtsaktualisierung
outputs = model.forward(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
#Verlustberechnung und korrekte Rücklaufquote
loss_train, acc_train = calculate_loss_and_accuracy(model, criterion, dataloader_train)
loss_valid, acc_valid = calculate_loss_and_accuracy(model, criterion, dataloader_valid)
log_train.append([loss_train, acc_train])
log_valid.append([loss_valid, acc_valid])
#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}')
Ausgabe
epoch: 1, loss_train: 0.3476, accuracy_train: 0.8796, loss_valid: 0.3656, accuracy_valid: 0.8840
epoch: 2, loss_train: 0.2912, accuracy_train: 0.8988, loss_valid: 0.3219, accuracy_valid: 0.8967
・ ・ ・
epoch: 29, loss_train: 0.2102, accuracy_train: 0.9287, loss_valid: 0.3259, accuracy_valid: 0.8930
epoch: 30, loss_train: 0.2119, accuracy_train: 0.9289, loss_valid: 0.3262, accuracy_valid: 0.8945
from matplotlib import pyplot as plt
#Visualisierung
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()
Ändern Sie den Code in Frage 75 und schreiben Sie die Prüfpunkte (Werte der Parameter im Lernprozess (Gewichtsmatrix usw.) und den internen Status des Optimierungsalgorithmus) jedes Mal in eine Datei, wenn die Parameteraktualisierung jeder Epoche abgeschlossen ist.
Auf die Parameter in der Mitte des Lernens kann mit `` model.state_dict ()
zugegriffen werden, und auf den internen Status des Optimierungsalgorithmus kann mit
optimizer.state_dict ()
`zugegriffen werden. Speichern Sie ihn also zusammen mit der Anzahl der Epochen in jeder Epoche. Verarbeitung hinzufügen.
Die Ausgabe ist die gleiche wie bei der vorherigen Frage, daher wird sie weggelassen.
#Modelldefinition
model = SLPNet(300, 4)
#Definition der Verlustfunktion
criterion = nn.CrossEntropyLoss()
#Optimierungsdefinition
optimizer = torch.optim.SGD(model.parameters(), lr=1e-1)
#Lernen
num_epochs = 10
log_train = []
log_valid = []
for epoch in range(num_epochs):
#Auf Trainingsmodus einstellen
model.train()
for inputs, labels in dataloader_train:
#Gradient auf Null initialisieren
optimizer.zero_grad()
#Vorwärtsausbreitung+Fehler bei der Weitergabe+Gewichtsaktualisierung
outputs = model.forward(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
#Verlustberechnung und korrekte Rücklaufquote
loss_train, acc_train = calculate_loss_and_accuracy(model, criterion, dataloader_train)
loss_valid, acc_valid = calculate_loss_and_accuracy(model, criterion, dataloader_valid)
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')
#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}')
Ändern Sie den Code von Problem 76, berechnen Sie den Verlust / Gradienten für jeden $ B $ -Fall und aktualisieren Sie den Wert der Matrix $ W $ (Mini-Batch). Vergleichen Sie die Zeit, die erforderlich ist, um eine Epoche zu lernen, während Sie den Wert von $ B $ in $ 1,2,4,8,… $ ändern.
Da es schwierig ist, alle Prozesse jedes Mal zu schreiben, wenn die Stapelgröße geändert wird, funktionieren die Prozesse nach der Erstellung von `Dataloader``` als`
train_model``` und nehmen einige Parameter einschließlich der Stapelgröße als Argumente. Festlegen als.
import time
def train_model(dataset_train, dataset_valid, batch_size, model, criterion, optimizer, num_epochs):
#Erstellen eines Datenladers
dataloader_train = DataLoader(dataset_train, batch_size=batch_size, shuffle=True)
dataloader_valid = DataLoader(dataset_valid, batch_size=len(dataset_valid), shuffle=False)
#Lernen
log_train = []
log_valid = []
for epoch in range(num_epochs):
#Startzeit aufzeichnen
s_time = time.time()
#Auf Trainingsmodus einstellen
model.train()
for inputs, labels in dataloader_train:
#Gradient auf Null initialisieren
optimizer.zero_grad()
#Vorwärtsausbreitung+Fehler bei der Weitergabe+Gewichtsaktualisierung
outputs = model.forward(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
#Verlustberechnung und korrekte Rücklaufquote
loss_train, acc_train = calculate_loss_and_accuracy(model, criterion, dataloader_train)
loss_valid, acc_valid = calculate_loss_and_accuracy(model, criterion, dataloader_valid)
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')
return {'train': log_train, 'valid': log_valid}
Messen Sie die Verarbeitungszeit, während Sie die Chargengröße ändern.
#Datensatz erstellen
dataset_train = CreateDataset(X_train, y_train)
dataset_valid = CreateDataset(X_valid, y_valid)
#Modelldefinition
model = SLPNet(300, 4)
#Definition der Verlustfunktion
criterion = nn.CrossEntropyLoss()
#Optimierungsdefinition
optimizer = torch.optim.SGD(model.parameters(), lr=1e-1)
#Modelllernen
for batch_size in [2 ** i for i in range(11)]:
print(f'Chargengröße: {batch_size}')
log = train_model(dataset_train, dataset_valid, batch_size, model, criterion, optimizer, 1)
Ausgabe
Chargengröße: 1
epoch: 1, loss_train: 0.3237, accuracy_train: 0.8888, loss_valid: 0.3476, accuracy_valid: 0.8817, 5.4416sec
Chargengröße: 2
epoch: 1, loss_train: 0.2966, accuracy_train: 0.8999, loss_valid: 0.3258, accuracy_valid: 0.8847, 3.0029sec
Chargengröße: 4
epoch: 1, loss_train: 0.2883, accuracy_train: 0.8999, loss_valid: 0.3222, accuracy_valid: 0.8862, 1.5988sec
Chargengröße: 8
epoch: 1, loss_train: 0.2835, accuracy_train: 0.9023, loss_valid: 0.3179, accuracy_valid: 0.8907, 0.8732sec
Chargengröße: 16
epoch: 1, loss_train: 0.2817, accuracy_train: 0.9038, loss_valid: 0.3164, accuracy_valid: 0.8907, 0.5445sec
Chargengröße: 32
epoch: 1, loss_train: 0.2810, accuracy_train: 0.9038, loss_valid: 0.3159, accuracy_valid: 0.8900, 0.3482sec
Chargengröße: 64
epoch: 1, loss_train: 0.2806, accuracy_train: 0.9040, loss_valid: 0.3157, accuracy_valid: 0.8900, 0.2580sec
Chargengröße: 128
epoch: 1, loss_train: 0.2806, accuracy_train: 0.9041, loss_valid: 0.3156, accuracy_valid: 0.8900, 0.1984sec
Chargengröße: 256
epoch: 1, loss_train: 0.2801, accuracy_train: 0.9039, loss_valid: 0.3155, accuracy_valid: 0.8900, 0.1715sec
Chargengröße: 512
epoch: 1, loss_train: 0.2802, accuracy_train: 0.9038, loss_valid: 0.3155, accuracy_valid: 0.8900, 0.2177sec
Chargengröße: 1024
epoch: 1, loss_train: 0.2792, accuracy_train: 0.9038, loss_valid: 0.3155, accuracy_valid: 0.8900, 0.1603sec
Im Allgemeinen können Sie sehen, dass die Berechnungszeit umso kürzer ist, je größer die Chargengröße ist.
Ändern Sie den Code in Frage 77 und führen Sie das Training auf der GPU aus.
Fügen Sie das Argument `device``` hinzu, um die GPU für`
berechne_verlust_und_genauigkeit und `` `train_model
anzugeben.
In jeder Funktion können Sie die GPU verwenden, indem Sie den Prozess zum Senden des Modells und des Eingabetensors an die GPU hinzufügen und `cuda``` für`
device``` angeben.
def calculate_loss_and_accuracy(model, criterion, loader, device):
model.eval()
loss = 0.0
total = 0
correct = 0
with torch.no_grad():
for inputs, labels in loader:
inputs = inputs.to(device)
labels = labels.to(device)
outputs = model(inputs)
loss += criterion(outputs, labels).item()
pred = torch.argmax(outputs, dim=-1)
total += len(inputs)
correct += (pred == labels).sum().item()
return loss / len(loader), correct / total
def train_model(dataset_train, dataset_valid, batch_size, model, criterion, optimizer, num_epochs, device=None):
#An GPU senden
model.to(device)
#Erstellen eines Datenladers
dataloader_train = DataLoader(dataset_train, batch_size=batch_size, shuffle=True)
dataloader_valid = DataLoader(dataset_valid, batch_size=len(dataset_valid), shuffle=False)
#Lernen
log_train = []
log_valid = []
for epoch in range(num_epochs):
#Startzeit aufzeichnen
s_time = time.time()
#Auf Trainingsmodus einstellen
model.train()
for inputs, labels in dataloader_train:
#Gradient auf Null initialisieren
optimizer.zero_grad()
#Vorwärtsausbreitung+Fehler bei der Weitergabe+Gewichtsaktualisierung
inputs = inputs.to(device)
labels = labels.to(device)
outputs = model.forward(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
#Verlustberechnung und korrekte Rücklaufquote
loss_train, acc_train = calculate_loss_and_accuracy(model, criterion, dataloader_train, device)
loss_valid, acc_valid = calculate_loss_and_accuracy(model, criterion, dataloader_valid, device)
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')
return {'train': log_train, 'valid': log_valid}
#Datensatz erstellen
dataset_train = CreateDataset(X_train, y_train)
dataset_valid = CreateDataset(X_valid, y_valid)
#Modelldefinition
model = SLPNet(300, 4)
#Definition der Verlustfunktion
criterion = nn.CrossEntropyLoss()
#Optimierungsdefinition
optimizer = torch.optim.SGD(model.parameters(), lr=1e-1)
#Gerätespezifikation
device = torch.device('cuda')
#Modelllernen
for batch_size in [2 ** i for i in range(11)]:
print(f'Chargengröße: {batch_size}')
log = train_model(dataset_train, dataset_valid, batch_size, model, criterion, optimizer, 1, device=device)
Ausgabe
Chargengröße: 1
epoch: 1, loss_train: 0.3300, accuracy_train: 0.8874, loss_valid: 0.3584, accuracy_valid: 0.8772, 9.0342sec
Chargengröße: 2
epoch: 1, loss_train: 0.3025, accuracy_train: 0.8994, loss_valid: 0.3374, accuracy_valid: 0.8870, 4.6391sec
Chargengröße: 4
epoch: 1, loss_train: 0.2938, accuracy_train: 0.9005, loss_valid: 0.3321, accuracy_valid: 0.8855, 2.4228sec
Chargengröße: 8
epoch: 1, loss_train: 0.2894, accuracy_train: 0.9039, loss_valid: 0.3299, accuracy_valid: 0.8855, 1.2517sec
Chargengröße: 16
epoch: 1, loss_train: 0.2876, accuracy_train: 0.9038, loss_valid: 0.3285, accuracy_valid: 0.8855, 0.7149sec
Chargengröße: 32
epoch: 1, loss_train: 0.2867, accuracy_train: 0.9050, loss_valid: 0.3280, accuracy_valid: 0.8862, 0.4323sec
Chargengröße: 64
epoch: 1, loss_train: 0.2863, accuracy_train: 0.9050, loss_valid: 0.3277, accuracy_valid: 0.8862, 0.2834sec
Chargengröße: 128
epoch: 1, loss_train: 0.2869, accuracy_train: 0.9051, loss_valid: 0.3276, accuracy_valid: 0.8862, 0.2070sec
Chargengröße: 256
epoch: 1, loss_train: 0.2864, accuracy_train: 0.9054, loss_valid: 0.3275, accuracy_valid: 0.8862, 0.1587sec
Chargengröße: 512
epoch: 1, loss_train: 0.2859, accuracy_train: 0.9056, loss_valid: 0.3275, accuracy_valid: 0.8862, 0.2016sec
Chargengröße: 1024
epoch: 1, loss_train: 0.2858, accuracy_train: 0.9056, loss_valid: 0.3275, accuracy_valid: 0.8862, 0.1303sec
Obwohl die Stapelgröße klein ist, scheint die Zeit zum Senden an die GPU für jeden Stapel länger und die Verarbeitungszeit bei Verwendung der CPU kürzer zu sein. Auf der anderen Seite können wir mit zunehmender Stapelgröße feststellen, dass die Verwendung der GPU schneller ist.
Ändern Sie den Code in Problem 78, um einen Hochleistungs-Kategorisierer zu erstellen und gleichzeitig die Form des neuronalen Netzwerks zu ändern, z. B. das Einführen von Bias-Begriffen und das Überlagern.
Definieren Sie ein neues mehrschichtiges neuronales Netzwerk "MLPNet". Dieses Netzwerk besteht aus Eingabeschicht-> Mittelschicht-> Ausgabeschicht, und die Chargennormalisierung wird nach der Mittelschicht durchgeführt. Darüber hinaus führt `` `train_model``` einen neuen Standard für die Beendigung des Lernens ein. Dieses Mal besteht die Regel einfach darin, anzuhalten, wenn der Verlust von Verifizierungsdaten in drei aufeinander folgenden Epochen nicht abnimmt. Darüber hinaus werden wir einen Scheduler hinzufügen, der die Lernrate schrittweise senkt, um die Generalisierungsleistung zu verbessern.
from torch.nn import functional as F
class MLPNet(nn.Module):
def __init__(self, input_size, mid_size, output_size, mid_layers):
super().__init__()
self.mid_layers = mid_layers
self.fc = nn.Linear(input_size, mid_size)
self.fc_mid = nn.Linear(mid_size, mid_size)
self.fc_out = nn.Linear(mid_size, output_size)
self.bn = nn.BatchNorm1d(mid_size)
def forward(self, x):
x = F.relu(self.fc(x))
for _ in range(self.mid_layers):
x = F.relu(self.bn(self.fc_mid(x)))
x = F.relu(self.fc_out(x))
return x
from torch import optim
def calculate_loss_and_accuracy(model, criterion, loader, device):
model.eval()
loss = 0.0
total = 0
correct = 0
with torch.no_grad():
for inputs, labels in loader:
inputs = inputs.to(device)
labels = labels.to(device)
outputs = model(inputs)
loss += criterion(outputs, labels).item()
pred = torch.argmax(outputs, dim=-1)
total += len(inputs)
correct += (pred == labels).sum().item()
return loss / len(loader), correct / total
def train_model(dataset_train, dataset_valid, batch_size, model, criterion, optimizer, num_epochs, device=None):
#An GPU senden
model.to(device)
#Erstellen eines Datenladers
dataloader_train = DataLoader(dataset_train, batch_size=batch_size, shuffle=True)
dataloader_valid = DataLoader(dataset_valid, batch_size=len(dataset_valid), 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 inputs, labels in dataloader_train:
#Gradient auf Null initialisieren
optimizer.zero_grad()
#Vorwärtsausbreitung+Fehler bei der Weitergabe+Gewichtsaktualisierung
inputs = inputs.to(device)
labels = labels.to(device)
outputs = model.forward(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
#Verlustberechnung und korrekte Rücklaufquote
loss_train, acc_train = calculate_loss_and_accuracy(model, criterion, dataloader_train, device)
loss_valid, acc_valid = calculate_loss_and_accuracy(model, criterion, dataloader_valid, device)
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}
#Datensatz erstellen
dataset_train = CreateDataset(X_train, y_train)
dataset_valid = CreateDataset(X_valid, y_valid)
#Modelldefinition
model = MLPNet(300, 200, 4, 1)
#Definition der Verlustfunktion
criterion = nn.CrossEntropyLoss()
#Optimierungsdefinition
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)
#Gerätespezifikation
device = torch.device('cuda')
#Modelllernen
log = train_model(dataset_train, dataset_valid, 64, model, criterion, optimizer, 1000, device)
Ausgabe
epoch: 1, loss_train: 1.1176, accuracy_train: 0.6679, loss_valid: 1.1150, accuracy_valid: 0.6572, 0.4695sec
epoch: 2, loss_train: 0.8050, accuracy_train: 0.7620, loss_valid: 0.8005, accuracy_valid: 0.7687, 0.4521sec
・ ・ ・
epoch: 96, loss_train: 0.1708, accuracy_train: 0.9460, loss_valid: 0.2858, accuracy_valid: 0.9034, 0.4632sec
epoch: 97, loss_train: 0.1702, accuracy_train: 0.9466, loss_valid: 0.2861, accuracy_valid: 0.9034, 0.5373sec
Es wurde in 97 Epochen eingestellt. Visualisieren Sie den Verlust und die Genauigkeitsrate für jede Epoche.
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()
Überprüfen Sie die Genauigkeitsrate der Bewertungsdaten.
def calculate_accuracy(model, loader, device):
model.eval()
total = 0
correct = 0
with torch.no_grad():
for inputs, labels in loader:
inputs = inputs.to(device)
labels = labels.to(device)
outputs = model(inputs)
pred = torch.argmax(outputs, dim=-1)
total += len(inputs)
correct += (pred == labels).sum().item()
return correct / total
#Bestätigung der korrekten Antwortrate
acc_train = calculate_accuracy(model, dataloader_train, device)
acc_test = calculate_accuracy(model, dataloader_test, device)
print(f'Richtige Antwortrate (Lerndaten):{acc_train:.3f}')
print(f'Richtige Antwortrate (Bewertungsdaten):{acc_test:.3f}')
Ausgabe
Richtige Antwortrate (Lerndaten): 0.947
Richtige Antwortrate (Bewertungsdaten): 0.921
In dem einschichtigen neuronalen Netzwerk betrug die Genauigkeitsrate der Bewertungsdaten 0,891, sie wird jedoch durch Verwendung mehrerer Schichten um 3 Punkte verbessert.
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.
Recommended Posts