[PYTHON] [Traitement du langage 100 coups 2020] Chapitre 8: Réseau neuronal

introduction

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.

Préparation préalable

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.

Chapitre 8: Réseau neuronal

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.

70. Caractéristiques par somme de vecteurs de mots

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')

71. Prédiction par réseau neuronal monocouche

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>)

72. Calcul de la perte et de la pente

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.

l_i = −log [Probabilité que le cas x_i soit classé comme y_i]

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]])

73. Apprentissage par la méthode probabiliste de descente de gradient

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 ''. datasetEst 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.

74. Mesure du taux de réponse correcte

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

75. Graphique des taux de perte et de précision

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()

75.png

76. Point de contrôle

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}')  

77. Mini lot

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.

78. Apprentissage sur GPU

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.

79. Réseau neuronal multicouche

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()

79.png

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.

en conclusion

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.

À toutes les 100 questions

Recommended Posts

[Traitement du langage 100 coups 2020] Chapitre 8: Réseau neuronal
100 coups de traitement du langage ~ Chapitre 1
Le traitement de 100 langues frappe le chapitre 2 (10 ~ 19)
100 traitements linguistiques frappent 03 ~ 05
100 coups de traitement linguistique (2020): 40
100 coups de traitement linguistique (2020): 32
100 coups de traitement linguistique (2020): 35
[Traitement du langage 100 coups 2020] Chapitre 3: Expressions régulières
100 coups de traitement linguistique (2020): 47
100 coups de traitement linguistique (2020): 39
100 traitements du langage naturel frappent le chapitre 4 Commentaire
[Traitement du langage 100 coups 2020] Chapitre 6: Machine learning
100 coups de traitement linguistique (2020): 22
100 coups de traitement du langage 2020: Chapitre 4 (analyse morphologique)
[Traitement du langage 100 coups 2020] Chapitre 5: Analyse des dépendances
100 coups de traitement linguistique (2020): 42
100 coups de traitement linguistique (2020): 29
100 coups de traitement linguistique (2020): 49
[Traitement du langage 100 coups 2020] Chapitre 1: Mouvement préparatoire
100 coups de traitement linguistique (2020): 45
100 coups de traitement linguistique (2020): 10-19
[Traitement du langage 100 coups 2020] Chapitre 7: Vecteur Word
100 coups de traitement linguistique (2020): 30
100 coups de traitement linguistique (2020): 00-09
100 Language Processing Knock 2020: Chapitre 3 (expression régulière)
100 Language Processing Knock 2020 Chapitre 8: Neural Net
100 coups de traitement linguistique (2020): 48
[Traitement du langage 100 coups 2020] Chapitre 2: Commandes UNIX
100 coups de traitement linguistique (2020): 44
100 coups de traitement linguistique (2020): 41
100 coups de traitement linguistique (2020): 37
[Traitement du langage 100 coups 2020] Chapitre 9: RNN, CNN
100 coups de traitement linguistique (2020): 25
100 coups de traitement linguistique (2020): 23
100 coups de traitement linguistique (2020): 33
100 coups de traitement linguistique (2020): 20
100 coups de traitement linguistique (2020): 27
[Traitement du langage 100 coups 2020] Chapitre 4: Analyse morphologique
100 coups de traitement linguistique (2020): 46
100 coups de traitement linguistique (2020): 21
100 coups de traitement linguistique (2020): 36
Traitement du langage 100 coups Chapitre 4: Analyse morphologique 31. Verbes
100 coups de traitement du langage amateur: 41
100 coups de traitement du langage amateur: 71
100 coups de traitement du langage amateur: 56
100 coups de traitement du langage amateur: 24
100 coups de traitement du langage amateur: 59
100 coups de traitement du langage amateur: 70
100 coups de traitement du langage amateur: 60
100 Language Processing Knock 2020 Chapitre 1
100 coups de traitement du langage amateur: 92
100 coups de langue amateur: 30
100 coups de langue amateur: 06
100 coups de traitement du langage amateur: 84
100 coups de traitement du langage amateur: 81
100 coups de langue amateur: 33
100 coups de traitement du langage amateur: 40
100 coups de traitement du langage amateur: 45
100 coups de traitement du langage amateur: 43
100 coups de traitement du langage amateur: 55
100 coups de traitement du langage amateur: 22