Version 2020 de 100 coups de traitement du langage, qui est célèbre comme une collection de problèmes de traitement du langage naturel, a été publié. Cet article résume les résultats de la résolution du «Chapitre 8: Neural Net» des chapitres 1 à 10 suivants.
Nous utilisons Google Colaboratory pour obtenir des réponses. Pour plus d'informations sur la configuration et l'utilisation de Google Colaboratory, consultez cet article. ** Puisque le GPU est utilisé dans ce chapitre, changez l'accélérateur matériel en "GPU" depuis "Runtime" -> "Changer le type d'exécution" et enregistrez-le à l'avance. ** ** Le cahier contenant les résultats d'exécution des réponses suivantes est disponible sur github.
Mettre en œuvre un modèle de catégorisation avec un réseau de neurones basé sur la catégorisation des articles de presse abordés au chapitre 6. Dans ce chapitre, utilisez des plates-formes d'apprentissage automatique telles que PyTorch, TensorFlow et Chainer.
Je souhaite convertir les données d'entraînement, les données de vérification et les données d'évaluation construites dans le problème 50 en matrices et vecteurs. Par exemple, pour les données d'apprentissage, nous aimerions créer une matrice $ X $ dans laquelle les vecteurs de caractéristiques $ \ boldsymbol {x} _i $ de tous les cas $ x_i $ sont arrangés et une matrice (vecteur) $ Y $ dans laquelle les étiquettes de réponse correctes sont arrangées.
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}
>
> Ici, $ n $ est le nombre de cas de données d'apprentissage, et $ \ boldsymbol x_i \ in \ mathbb {R} ^ d $ et $ y_i \ in \ mathbb N $ sont respectivement $ i \ in \ {1. \ dots, n \} Représente le vecteur de caractéristiques et l'étiquette correcte du $ th cas.
> Cette fois, il y a quatre catégories: «affaires», «science et technologie», «divertissement» et «santé». Si $ \ mathbb N_ {<4} $ représente un nombre naturel (y compris $ 0 $) inférieur à 4 $ $, l'étiquette correcte $ y_i $ dans tous les cas est $ y_i \ in \ mathbb N_ {<4} $ Peut être exprimé par.
> Dans ce qui suit, le nombre de types d'étiquettes est représenté par $ L $ ($ L = 4 $ dans cette tâche de classification).
>
> Le vecteur de caractéristiques $ \ boldsymbol x_i $ du cas $ i $ th est calculé par l'équation suivante.
>
> $$\boldsymbol x_i = \frac{1}{T_i} \sum_{t=1}^{T_i} \mathrm{emb}(w_{i,t})$$
>
> Ici, le cas $ i $ ème est $ T_i $ (titre d'article) chaînes de mots $ (w_ {i, 1}, w_ {i, 2}, \ dots, w_ {i, T_i}) $ \\ mathrm {emb} (w) \ in \ mathbb {R} ^ d $ est un vecteur de mot (le nombre de dimensions est $ d $) correspondant au mot $ w $. Autrement dit, $ \ boldsymbol x_i $ est l'en-tête d'article du $ i $ ème cas exprimé par la moyenne des vecteurs des mots contenus dans l'en-tête. Cette fois, le mot vecteur téléchargé à la question 60 doit être utilisé. Puisque nous avons utilisé un vecteur de mot de dimension 300 $, $ d = 300 $.
> L'étiquette $ y_i $ du cas $ i $ ème est définie comme suit.
>
>```math
y_i = \begin{cases}
0 & (\mbox{article}\boldsymbol x_i\mbox{Si appartient à la catégorie "Entreprise"}) \\
1 & (\mbox{article}\boldsymbol x_i\mbox{Fait partie de la catégorie "Science et technologie"}) \\
2 & (\mbox{article}\boldsymbol x_i\mbox{Si appartient à la catégorie "Divertissement"}) \\
3 & (\mbox{article}\boldsymbol x_i\mbox{Si appartient à la catégorie «santé»}) \\
\end{cases}
S'il existe une correspondance univoque entre le nom de la catégorie et le numéro d'étiquette, la correspondance ne doit pas nécessairement être comme indiqué dans la formule ci-dessus.
En vous basant sur les spécifications ci-dessus, créez la matrice / vecteur suivant et enregistrez-le dans un fichier.
- Matrice des caractéristiques des données d'entraînement: $ X_ {\ rm train} \ in \ mathbb {R} ^ {N_t \ times d} $
- Vecteur d'étiquette de données d'entraînement: $ Y_ {\ rm train} \ in \ mathbb {N} ^ {N_t} $
- Matrice des caractéristiques des données de validation: $ X_ {\ rm valide} \ in \ mathbb {R} ^ {N_v \ times d} $
- Vecteur d'étiquette de données de validation: $ Y_ {\ rm valide} \ in \ mathbb {N} ^ {N_v} $
- Matrice des caractéristiques des données d'évaluation: $ X_ {\ rm test} \ in \ mathbb {R} ^ {N_e \ times d} $
- Vecteur d'étiquette de données d'évaluation: $ Y_ {\ rm test} \ in \ mathbb {N} ^ {N_e} $
Notez que $ N_t, N_v et N_e $ sont respectivement le nombre de cas de données d'apprentissage, le nombre de cas de données de vérification et le nombre de cas de données d'évaluation.
Tout d'abord, après avoir téléchargé les données spécifiées, lisez-les comme une trame de données. Ensuite, il est divisé en données d'entraînement, données de vérification et données d'évaluation et sauvegardé. Jusqu'à ce point, le traitement est exactement le même que le problème 50, il n'y a donc pas de problème pour lire les données qui y sont créées.
#Télécharger les données
!wget https://archive.ics.uci.edu/ml/machine-learning-databases/00359/NewsAggregatorDataset.zip
!unzip NewsAggregatorDataset.zip
#Remplacement des guillemets doubles par des guillemets simples pour éviter les erreurs lors de la lecture
!sed -e 's/"/'\''/g' ./newsCorpora.csv > ./newsCorpora_re.csv
import pandas as pd
from sklearn.model_selection import train_test_split
#Lire les données
df = pd.read_csv('./newsCorpora_re.csv', header=None, sep='\t', names=['ID', 'TITLE', 'URL', 'PUBLISHER', 'CATEGORY', 'STORY', 'HOSTNAME', 'TIMESTAMP'])
#Extraction de données
df = df.loc[df['PUBLISHER'].isin(['Reuters', 'Huffington Post', 'Businessweek', 'Contactmusic.com', 'Daily Mail']), ['TITLE', 'CATEGORY']]
#Répartition des données
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'])
#Stockage de données
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)
#Confirmation du nombre de cas
print('[Données d'apprentissage]')
print(train['CATEGORY'].value_counts())
print('[Données de vérification]')
print(valid['CATEGORY'].value_counts())
print('[Données d'évaluation]')
print(test['CATEGORY'].value_counts())
production
[Données d'apprentissage]
b 4501
e 4235
t 1220
m 728
Name: CATEGORY, dtype: int64
[Données de vérification]
b 563
e 529
t 153
m 91
Name: CATEGORY, dtype: int64
[Données d'évaluation]
b 563
e 530
t 152
m 91
Name: CATEGORY, dtype: int64
Ensuite, téléchargez et chargez le vecteur de mot appris utilisé à la question 60.
#Télécharger le vecteur de mot appris
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
#Charger le vecteur de mot appris
model = KeyedVectors.load_word2vec_format('./GoogleNews-vectors-negative300.bin.gz', binary=True)
Enfin, créez un vecteur de caractéristiques et un vecteur d'étiquette et enregistrez-les. Après cela, il est converti en type Tensor afin qu'il puisse être utilisé comme entrée pour le réseau neuronal par PyTorch.
import string
import torch
def transform_w2v(text):
table = str.maketrans(string.punctuation, ' '*len(string.punctuation))
words = text.translate(table).split() #Après avoir remplacé le symbole par un espace, divisez-le par un espace et listez-le.
vec = [model[word] for word in words if word in model] #Vectorisé mot par mot
return torch.tensor(sum(vec) / len(vec)) #Convertir le vecteur moyen en type Tensor et en sortie
#Créer un vecteur de caractéristiques
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)
production
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]])
#Créer un vecteur d'étiquette
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)
production
torch.Size([10684])
tensor([0, 1, 3, ..., 0, 3, 2])
#sauvegarder
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')
Lisez la matrice enregistrée à la question 70 et effectuez les calculs suivants sur les données d'entraînement.
\hat{y}1=softmax(x_1W),\\hat{Y}=softmax(X{[1:4]}W)
Cependant, $ softmax $ est la fonction softmax, $ X_ {[1: 4]} ∈ \ mathbb {R} ^ {4 × d} $ est le vecteur de caractéristiques $ x_1 $, $ x_2 $, $ x_3 $, $ x_4 $ Est une matrice disposée verticalement.
>```math
X_{[1:4]}=\begin{pmatrix}x_1\\x_2\\x_3\\x_4\end{pmatrix}
La matrice $ W \ in \ mathbb {R} ^ {d \ times L} $ est la matrice de poids du réseau neuronal monocouche, qui peut être initialisée avec une valeur aléatoire (apprise dans le problème 73 et plus tard). .. Notez que $ \ hat {\ boldsymbol y_1} \ in \ mathbb {R} ^ L $ est un vecteur représentant la probabilité d'appartenir à chaque catégorie lorsque le cas $ x_1 $ est classé par la matrice non apprise $ W $. De même, $ \ hat {Y} \ in \ mathbb {R} ^ {n \ times L} $ exprime la probabilité d'appartenir à chaque catégorie comme une matrice pour les exemples de données d'apprentissage $ x_1, x_2, x_3, x_4 $. faites.
Tout d'abord, définissez un réseau neuronal monocouche appelé SLPNet ''. Définissez les couches qui composent le réseau avec
__init__```, et utilisez la méthode
`` forward``` pour organiser les couches à travers lesquelles les données d'entrée passent.
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) #Initialiser les poids avec des nombres aléatoires normaux
def forward(self, x):
x = self.fc(x)
return x
Il initialise ensuite le modèle défini et effectue les calculs indiqués.
model = SLPNet(300, 4) #Initialisation du réseau neuronal monocouche
y_hat_1 = torch.softmax(model.forward(X_train[:1]), dim=-1)
print(y_hat_1)
production
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)
production
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>)
Calculez la perte d'entropie croisée et le gradient pour la matrice $ W $ pour le cas $ x_1 $ et l'ensemble de cas $ x_1 $, $ x_2 $, $ x_3 $, $ x_4 $ des données d'apprentissage. Pour un certain cas $ x_i $, la perte est calculée par l'équation suivante.
Cependant, la perte d'entropie croisée pour l'ensemble de cas est la moyenne des pertes pour chaque cas inclus dans l'ensemble.
Ici, nous utiliserons le CrossEntropyLoss '' du module
nn ''.
En entrant le vecteur de sortie et le vecteur d'étiquette du modèle, la perte moyenne de l'équation ci-dessus peut être calculée.
criterion = nn.CrossEntropyLoss()
l_1 = criterion(model.forward(X_train[:1]), y_train[:1]) #Le vecteur d'entrée est la valeur avant softmax
model.zero_grad() #Initialiser le dégradé à zéro
l_1.backward() #Calculer le gradient
print(f'perte: {l_1:.4f}')
print(f'Pente:\n{model.fc.weight.grad}')
production
perte: 2.9706
Pente:
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'perte: {l:.4f}')
print(f'Pente:\n{model.fc.weight.grad}')
production
perte: 3.0799
Pente:
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]])
Apprenez la matrice $ W $ en utilisant la méthode SGD (Stochastic Gradient Descent). L'apprentissage peut être achevé selon une norme appropriée (par exemple, «Fin dans 100 époques»).
Pour l'apprentissage, préparez Dataset '' et
Dataloader ''.
dataset
Est un type qui peut contenir le vecteur de fonction et le vecteur d'étiquette ensemble et qui transforme le tenseur d'origine à l'aide de la classe suivante.
from torch.utils.data import Dataset
class CreateDataset(Dataset):
def __init__(self, X, y): #Spécifier les composants du jeu de données
self.X = X
self.y = y
def __len__(self): # len(dataset)Spécifiez la valeur à renvoyer avec
return len(self.y)
def __getitem__(self, idx): # dataset[idx]Spécifiez la valeur à renvoyer avec
if isinstance(idx, torch.Tensor):
idx = idx.tolist()
return [self.X[idx], self.y[idx]]
Après la conversion, créez DataLoader ''.
Dataloader``` peut être entré dans
Dataset```, et les données collectées dans la taille spécifiée (
batch_size```) peuvent être récupérées dans l'ordre. Ici,
batch_size = 1``` est défini, cela signifie donc créer
Dataloader``` pour récupérer les données une par une. Notez que
Dataloader``` peut être récupéré dans l'ordre avec l'instruction` `for```, ou le morceau suivant peut être appelé avec
next (iter (Dataloader))
`.
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)
Maintenant que les données sont prêtes, entraînons la matrice $ W $. La définition du modèle et la définition de la fonction de perte sont les mêmes que dans la question précédente. Cette fois, nous mettrons également à jour les poids à partir du gradient calculé, nous allons donc également définir un optimiseur. Ici, SGD est défini conformément aux instructions. Lorsque les pièces sont prêtes, l'apprentissage est exécuté avec le nombre d'époques fixé à 10.
#Définition du modèle
model = SLPNet(300, 4)
#Définition de la fonction de perte
criterion = nn.CrossEntropyLoss()
#Définition de l'optimiseur
optimizer = torch.optim.SGD(model.parameters(), lr=1e-1)
#Apprentissage
num_epochs = 10
for epoch in range(num_epochs):
#Mettre en mode entraînement
model.train()
loss_train = 0.0
for i, (inputs, labels) in enumerate(dataloader_train):
#Initialiser le dégradé à zéro
optimizer.zero_grad()
#Propagation vers l'avant+Erreur de propagation de retour+Mise à jour du poids
outputs = model.forward(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
#Record de perte
loss_train += loss.item()
#Calcul des pertes moyennes par lots
loss_train = loss_train / i
#Calcul de la perte de données de validation
model.eval()
with torch.no_grad():
inputs, labels = next(iter(dataloader_valid))
outputs = model.forward(inputs)
loss_valid = criterion(outputs, labels)
#Journal de sortie
print(f'epoch: {epoch + 1}, loss_train: {loss_train:.4f}, loss_valid: {loss_valid:.4f}')
production
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
Au fur et à mesure que l'époque avance, nous pouvons voir que la perte de données d'entraînement diminue progressivement.
Lors de la classification des cas de données d'entraînement et de données d'évaluation à l'aide de la matrice obtenue à la question 73, trouvez le taux de réponse correct pour chacun.
Avec le modèle entraîné et `` Dataloader '' comme entrée, définissez une fonction pour calculer le taux de précision.
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'Taux de réponse correct (données d'apprentissage):{acc_train:.3f}')
print(f'Taux de réponse correct (données d'évaluation):{acc_test:.3f}')
production
Taux de réponse correct (données d'apprentissage): 0.920
Taux de réponse correct (données d'évaluation): 0.891
En modifiant le code de la question 73, à chaque fois que la mise à jour des paramètres de chaque époque est terminée, la perte des données d'apprentissage, le taux de réponse correct, la perte des données de vérification et le taux de réponse correct peuvent être reportés sur un graphique pour vérifier la progression de l'apprentissage. Fais le.
La perte et le taux de réponse correcte sont enregistrés en modifiant la fonction de la question précédente afin que la perte puisse également être calculée et en l'appliquant à chaque époque.
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
#Définition du modèle
model = SLPNet(300, 4)
#Définition de la fonction de perte
criterion = nn.CrossEntropyLoss()
#Définition de l'optimiseur
optimizer = torch.optim.SGD(model.parameters(), lr=1e-1)
#Apprentissage
num_epochs = 30
log_train = []
log_valid = []
for epoch in range(num_epochs):
#Mettre en mode entraînement
model.train()
for inputs, labels in dataloader_train:
#Initialiser le dégradé à zéro
optimizer.zero_grad()
#Propagation vers l'avant+Erreur de propagation de retour+Mise à jour du poids
outputs = model.forward(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
#Calcul de la perte et taux de réponse correcte
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])
#Journal de sortie
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}')
production
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
#Visualisation
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()
Modifier le code en question 75 et écrire les points de contrôle (valeurs des paramètres en cours d'apprentissage (matrice de poids, etc.) et état interne de l'algorithme d'optimisation) dans un fichier à chaque fois que la mise à jour des paramètres de chaque époque est terminée.
Les paramètres pendant l'entraînement sont accessibles avec `model.state_dict ()`
, et l'état interne de l'algorithme d'optimisation peut être consulté avec ```optimizer.state_dict ()
, donc enregistrez-le avec le nombre d'époques dans chaque époque. Ajouter un traitement.
Le résultat est le même que la question précédente, il sera donc omis.
#Définition du modèle
model = SLPNet(300, 4)
#Définition de la fonction de perte
criterion = nn.CrossEntropyLoss()
#Définition de l'optimiseur
optimizer = torch.optim.SGD(model.parameters(), lr=1e-1)
#Apprentissage
num_epochs = 10
log_train = []
log_valid = []
for epoch in range(num_epochs):
#Mettre en mode entraînement
model.train()
for inputs, labels in dataloader_train:
#Initialiser le dégradé à zéro
optimizer.zero_grad()
#Propagation vers l'avant+Erreur de propagation de retour+Mise à jour du poids
outputs = model.forward(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
#Calcul de la perte et taux de réponse correcte
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])
#Enregistrer le point de contrôle
torch.save({'epoch': epoch, 'model_state_dict': model.state_dict(), 'optimizer_state_dict': optimizer.state_dict()}, f'checkpoint{epoch + 1}.pt')
#Journal de sortie
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}')
Modifiez le code du problème 76, calculez la perte / gradient pour chaque cas $ B $, et mettez à jour la valeur de la matrice $ W $ (mini-batch). Comparez le temps nécessaire pour apprendre une époque en changeant la valeur de $ B $ en 1,2,4,8 $,… $.
Puisqu'il est difficile d'écrire tous les processus à chaque fois que la taille du lot est modifiée, faites fonctionner les processus après la création de
Dataloader comme `` train_model
et prenez certains paramètres, y compris la taille du lot comme arguments. Définir comme.
import time
def train_model(dataset_train, dataset_valid, batch_size, model, criterion, optimizer, num_epochs):
#Créer un chargeur de données
dataloader_train = DataLoader(dataset_train, batch_size=batch_size, shuffle=True)
dataloader_valid = DataLoader(dataset_valid, batch_size=len(dataset_valid), shuffle=False)
#Apprentissage
log_train = []
log_valid = []
for epoch in range(num_epochs):
#Heure de début d'enregistrement
s_time = time.time()
#Mettre en mode entraînement
model.train()
for inputs, labels in dataloader_train:
#Initialiser le dégradé à zéro
optimizer.zero_grad()
#Propagation vers l'avant+Erreur de propagation de retour+Mise à jour du poids
outputs = model.forward(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
#Calcul de la perte et taux de réponse correcte
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])
#Enregistrer le point de contrôle
torch.save({'epoch': epoch, 'model_state_dict': model.state_dict(), 'optimizer_state_dict': optimizer.state_dict()}, f'checkpoint{epoch + 1}.pt')
#Enregistrer l'heure de fin
e_time = time.time()
#Journal de sortie
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}
Mesurez le temps de traitement tout en modifiant la taille du lot.
#Créer un jeu de données
dataset_train = CreateDataset(X_train, y_train)
dataset_valid = CreateDataset(X_valid, y_valid)
#Définition du modèle
model = SLPNet(300, 4)
#Définition de la fonction de perte
criterion = nn.CrossEntropyLoss()
#Définition de l'optimiseur
optimizer = torch.optim.SGD(model.parameters(), lr=1e-1)
#Apprentissage de modèle
for batch_size in [2 ** i for i in range(11)]:
print(f'Taille du lot: {batch_size}')
log = train_model(dataset_train, dataset_valid, batch_size, model, criterion, optimizer, 1)
production
Taille du lot: 1
epoch: 1, loss_train: 0.3237, accuracy_train: 0.8888, loss_valid: 0.3476, accuracy_valid: 0.8817, 5.4416sec
Taille du lot: 2
epoch: 1, loss_train: 0.2966, accuracy_train: 0.8999, loss_valid: 0.3258, accuracy_valid: 0.8847, 3.0029sec
Taille du lot: 4
epoch: 1, loss_train: 0.2883, accuracy_train: 0.8999, loss_valid: 0.3222, accuracy_valid: 0.8862, 1.5988sec
Taille du lot: 8
epoch: 1, loss_train: 0.2835, accuracy_train: 0.9023, loss_valid: 0.3179, accuracy_valid: 0.8907, 0.8732sec
Taille du lot: 16
epoch: 1, loss_train: 0.2817, accuracy_train: 0.9038, loss_valid: 0.3164, accuracy_valid: 0.8907, 0.5445sec
Taille du lot: 32
epoch: 1, loss_train: 0.2810, accuracy_train: 0.9038, loss_valid: 0.3159, accuracy_valid: 0.8900, 0.3482sec
Taille du lot: 64
epoch: 1, loss_train: 0.2806, accuracy_train: 0.9040, loss_valid: 0.3157, accuracy_valid: 0.8900, 0.2580sec
Taille du lot: 128
epoch: 1, loss_train: 0.2806, accuracy_train: 0.9041, loss_valid: 0.3156, accuracy_valid: 0.8900, 0.1984sec
Taille du lot: 256
epoch: 1, loss_train: 0.2801, accuracy_train: 0.9039, loss_valid: 0.3155, accuracy_valid: 0.8900, 0.1715sec
Taille du lot: 512
epoch: 1, loss_train: 0.2802, accuracy_train: 0.9038, loss_valid: 0.3155, accuracy_valid: 0.8900, 0.2177sec
Taille du lot: 1024
epoch: 1, loss_train: 0.2792, accuracy_train: 0.9038, loss_valid: 0.3155, accuracy_valid: 0.8900, 0.1603sec
En général, vous pouvez voir que plus la taille du lot est grande, plus le temps de calcul est court.
Modifiez le code en question 77 et exécutez la formation sur le GPU.
Ajoutez l'argument
device pour spécifier le GPU à `` `` Calculate_loss_and_accuracy
et `train_model```. Dans chaque fonction, vous pouvez utiliser le GPU en ajoutant le processus d'envoi du modèle et du Tensor d'entrée au GPU et en spécifiant
cuda '' pour `` périphérique ''.
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):
#Envoyer au GPU
model.to(device)
#Créer un chargeur de données
dataloader_train = DataLoader(dataset_train, batch_size=batch_size, shuffle=True)
dataloader_valid = DataLoader(dataset_valid, batch_size=len(dataset_valid), shuffle=False)
#Apprentissage
log_train = []
log_valid = []
for epoch in range(num_epochs):
#Heure de début d'enregistrement
s_time = time.time()
#Mettre en mode entraînement
model.train()
for inputs, labels in dataloader_train:
#Initialiser le dégradé à zéro
optimizer.zero_grad()
#Propagation vers l'avant+Erreur de propagation de retour+Mise à jour du poids
inputs = inputs.to(device)
labels = labels.to(device)
outputs = model.forward(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
#Calcul de la perte et taux de réponse correcte
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])
#Enregistrer le point de contrôle
torch.save({'epoch': epoch, 'model_state_dict': model.state_dict(), 'optimizer_state_dict': optimizer.state_dict()}, f'checkpoint{epoch + 1}.pt')
#Enregistrer l'heure de fin
e_time = time.time()
#Journal de sortie
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}
#Créer un jeu de données
dataset_train = CreateDataset(X_train, y_train)
dataset_valid = CreateDataset(X_valid, y_valid)
#Définition du modèle
model = SLPNet(300, 4)
#Définition de la fonction de perte
criterion = nn.CrossEntropyLoss()
#Définition de l'optimiseur
optimizer = torch.optim.SGD(model.parameters(), lr=1e-1)
#Spécification de l'appareil
device = torch.device('cuda')
#Apprentissage de modèle
for batch_size in [2 ** i for i in range(11)]:
print(f'Taille du lot: {batch_size}')
log = train_model(dataset_train, dataset_valid, batch_size, model, criterion, optimizer, 1, device=device)
production
Taille du lot: 1
epoch: 1, loss_train: 0.3300, accuracy_train: 0.8874, loss_valid: 0.3584, accuracy_valid: 0.8772, 9.0342sec
Taille du lot: 2
epoch: 1, loss_train: 0.3025, accuracy_train: 0.8994, loss_valid: 0.3374, accuracy_valid: 0.8870, 4.6391sec
Taille du lot: 4
epoch: 1, loss_train: 0.2938, accuracy_train: 0.9005, loss_valid: 0.3321, accuracy_valid: 0.8855, 2.4228sec
Taille du lot: 8
epoch: 1, loss_train: 0.2894, accuracy_train: 0.9039, loss_valid: 0.3299, accuracy_valid: 0.8855, 1.2517sec
Taille du lot: 16
epoch: 1, loss_train: 0.2876, accuracy_train: 0.9038, loss_valid: 0.3285, accuracy_valid: 0.8855, 0.7149sec
Taille du lot: 32
epoch: 1, loss_train: 0.2867, accuracy_train: 0.9050, loss_valid: 0.3280, accuracy_valid: 0.8862, 0.4323sec
Taille du lot: 64
epoch: 1, loss_train: 0.2863, accuracy_train: 0.9050, loss_valid: 0.3277, accuracy_valid: 0.8862, 0.2834sec
Taille du lot: 128
epoch: 1, loss_train: 0.2869, accuracy_train: 0.9051, loss_valid: 0.3276, accuracy_valid: 0.8862, 0.2070sec
Taille du lot: 256
epoch: 1, loss_train: 0.2864, accuracy_train: 0.9054, loss_valid: 0.3275, accuracy_valid: 0.8862, 0.1587sec
Taille du lot: 512
epoch: 1, loss_train: 0.2859, accuracy_train: 0.9056, loss_valid: 0.3275, accuracy_valid: 0.8862, 0.2016sec
Taille du lot: 1024
epoch: 1, loss_train: 0.2858, accuracy_train: 0.9056, loss_valid: 0.3275, accuracy_valid: 0.8862, 0.1303sec
Bien que la taille du lot soit petite, il semble que le temps d'envoi au GPU pour chaque lot soit plus long, et le temps de traitement est plus court lors de l'utilisation du CPU. D'autre part, à mesure que la taille du lot augmente, nous pouvons voir qu'elle est plus rapide lors de l'utilisation du GPU.
Modifiez le code du problème 78 et construisez un classificateur de catégorie haute performance tout en changeant la forme du réseau neuronal, comme l'introduction de termes de biais et de multicouches.
Définissez un nouveau réseau neuronal multicouche MLPNet ''. Ce réseau se compose d'une couche d'entrée -> couche intermédiaire -> couche de sortie, et la normalisation par lots est effectuée après la couche intermédiaire. De plus,
train_model '' introduit une nouvelle norme de fin d'apprentissage. Cette fois, la règle est simplement de s'arrêter lorsque la perte de données de vérification ne diminue pas pendant 3 époques consécutives.
De plus, nous ajouterons un ordonnanceur qui réduit progressivement le taux d'apprentissage, dans le but d'améliorer les performances de généralisation.
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):
#Envoyer au GPU
model.to(device)
#Créer un chargeur de données
dataloader_train = DataLoader(dataset_train, batch_size=batch_size, shuffle=True)
dataloader_valid = DataLoader(dataset_valid, batch_size=len(dataset_valid), shuffle=False)
#Paramètres du planificateur
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, num_epochs, eta_min=1e-5, last_epoch=-1)
#Apprentissage
log_train = []
log_valid = []
for epoch in range(num_epochs):
#Heure de début d'enregistrement
s_time = time.time()
#Mettre en mode entraînement
model.train()
for inputs, labels in dataloader_train:
#Initialiser le dégradé à zéro
optimizer.zero_grad()
#Propagation vers l'avant+Erreur de propagation de retour+Mise à jour du poids
inputs = inputs.to(device)
labels = labels.to(device)
outputs = model.forward(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
#Calcul de la perte et taux de réponse correcte
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])
#Enregistrer le point de contrôle
torch.save({'epoch': epoch, 'model_state_dict': model.state_dict(), 'optimizer_state_dict': optimizer.state_dict()}, f'checkpoint{epoch + 1}.pt')
#Enregistrer l'heure de fin
e_time = time.time()
#Journal de sortie
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')
#L'apprentissage se termine si la perte des données de vérification ne diminue pas pendant 3 époques consécutives
if epoch > 2 and log_valid[epoch - 3][0] <= log_valid[epoch - 2][0] <= log_valid[epoch - 1][0] <= log_valid[epoch][0]:
break
#Faites un pas vers le planificateur
scheduler.step()
return {'train': log_train, 'valid': log_valid}
#Créer un jeu de données
dataset_train = CreateDataset(X_train, y_train)
dataset_valid = CreateDataset(X_valid, y_valid)
#Définition du modèle
model = MLPNet(300, 200, 4, 1)
#Définition de la fonction de perte
criterion = nn.CrossEntropyLoss()
#Définition de l'optimiseur
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)
#Spécification de l'appareil
device = torch.device('cuda')
#Apprentissage de modèle
log = train_model(dataset_train, dataset_valid, 64, model, criterion, optimizer, 1000, device)
production
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
Il a été interrompu à 97 époques. Visualisez le taux de perte et de précision pour chaque époque.
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()
Vérifiez le taux d'exactitude des données d'évaluation.
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
#Confirmation du taux de réponse correct
acc_train = calculate_accuracy(model, dataloader_train, device)
acc_test = calculate_accuracy(model, dataloader_test, device)
print(f'Taux de réponse correct (données d'apprentissage):{acc_train:.3f}')
print(f'Taux de réponse correct (données d'évaluation):{acc_test:.3f}')
production
Taux de réponse correct (données d'apprentissage): 0.947
Taux de réponse correct (données d'évaluation): 0.921
Dans le réseau neuronal monocouche, le taux de précision des données d'évaluation était de 0,891, mais il est amélioré de 3 points en le rendant multicouche.
Traitement du langage 100 coups sont conçus pour que vous puissiez apprendre non seulement le traitement du langage naturel lui-même, mais également le traitement des données de base et l'apprentissage automatique général. Même ceux qui étudient l'apprentissage automatique dans des cours en ligne pourront pratiquer de très bons résultats, alors essayez-le.
Recommended Posts