[PYTHON] [Introduction à Pytorch] Je souhaite générer des phrases dans des articles de presse

Aperçu

J'ai rassemblé des articles de presse sur le virus corona et j'aimerais l'utiliser pour contester la génération de phrases. J'ai commencé à étudier le Deep Learning en utilisant Pytorch à la maison, alors laissez-moi le sortir. J'étudie toujours, alors comprenez qu'il peut y avoir des erreurs ...

environnement

Bibliothèque à utiliser, etc.

import torch
import torch.nn as nn
import torch.optim as optimizers
from torch.utils.data import DataLoader
import torch.nn.functional as F
from tensorflow.keras.preprocessing.sequence import pad_sequences
from sklearn.utils import shuffle
import random
from tqdm import tqdm_notebook as tqdm
import pickle
import matplotlib.pyplot as plt
import logging
import numpy as np

Préparation des données

Lit le texte pré-traité et séparé des nouvelles sur le virus corona gratté. Le prétraitement n'est pas un gros problème.
Les nouvelles sur le virus corona de la semaine dernière ont été recueillies sur Yahoo News, alors je les ai obtenues à partir de là. Je l'ai gratté sans remarquer que c'était pendant une semaine, donc la quantité de données que j'ai réellement obtenue était trop petite et elle était foirée. Je suis trop inquiet si je peux bien apprendre avec ça, mais je vais l'essayer. ..

data_news = pickle.load(open("Destination/corona_wakati.pickle", "rb"))

Les données utilisées sont comme ça

data_news[0]

['patient', «Ya», 'Les travailleurs du domaine de la santé', 'La', 'Mais', 'Nouveau coronavirus', 'À', 'infection', 'Shi', «Ta», 'chose', 'Mais', 'Révélé', 'Shi', «Ta», «Ikuno-ku, ville d'Osaka», 'de', '「', «Nami», 'Ha', «Ya», 'Réhabilitation', 'hôpital', '」', 'sur', '、', «Préfecture d'Osaka», 'Ha', '0', 'Journée', 'Nuit', '、', 'plus loin', '0', 'Homme', 'de', 'infection', 'Mais', 'clair', 'À', 'Maintenant', «Ta», 'Quand', 'Présentation', 'Shi', «Ta», '。']

Faire le mot id

Puisque le mot lui-même ne peut pas être traité par le réseau neuronal, il est défini sur id. Puisqu'il est nécessaire de revenir d'id en mot lors de la génération effective d'une phrase, nous implémenterons également le décodeur.
Je voulais définir une classe à usage général, j'ai donc essayé de mettre des symboles au début et à la fin de la phrase, mais cette fois il y a toujours un signe de ponctuation à la fin de la phrase, donc ce n'est pas nécessaire.

class EncoderDecoder(object):
    def __init__(self):
        # word_to_dictionnaire d'identification
        self.w2i = {}
        # id_to_dictionnaire de mots
        self.i2w = {}
        #Mot reservé(Rembourrage,Le début de la phrase)
        self.special_chars = ['<pad>', '<s>', '</s>', '<unk>']
        self.bos_char = self.special_chars[1]
        self.eos_char = self.special_chars[2]
        self.oov_char = self.special_chars[3]

    #Fonction à appeler
    def __call__(self, sentence):
        return self.transform(sentence)

    #Créer un dictionnaire
    def fit(self, sentences):
        self._words = set()

        #Créer un ensemble de mots inconnus
        for sentence in sentences:
            self._words.update(sentence)

        #Décalez le mot réservé et secouez l'identifiant
        self.w2i = {w: (i + len(self.special_chars))
                    for i, w in enumerate(self._words)}

        #Ajouter des mots réservés au dictionnaire(<pad>:0, <s>:1, </s>:2, <unk>:3)
        for i, w in enumerate(self.special_chars):
            self.w2i[w] = i

        # word_to_id en utilisant le dictionnaire id_to_Créer un dictionnaire de mots
        self.i2w = {i: w for w, i in self.w2i.items()}

    #Convertissez les données lues en identifiant à la fois
    def transform(self, sentences, bos=False, eos=False):
        output = []
        #Ajouter des symboles de début et de fin si spécifié
        for sentence in sentences:
            if bos:
                sentence = [self.bos_char] + sentence
            if eos:
                sentence = sentence + [self.eos_char]
            output.append(self.encode(sentence))

        return output

    #Faire une phrase à la fois
    def encode(self, sentence):
        output = []
        for w in sentence:
            if w not in self.w2i:
                idx = self.w2i[self.oov_char]
            else:
                idx = self.w2i[w]
            output.append(idx)

        return output

    #Convertir phrase par phrase en liste de mots
    def decode(self, sentence):
        return [self.i2w[id] for id in sentence]

Utilisez la classe définie comme suit

en_de = EncoderDecoder()
en_de.fit(data_news)
data_news_id = en_de(data_news)
data_news_id[0]
[7142,
 5775,
 3686,
 4630,
 5891,
 4003,
 358,
 3853,
 4139,
 4604,
 4591,
 5891,
 2233,
 4139,
 4604,
 5507,
 7378,
 2222,
 6002,
 3277,
 5775,
 7380,
 7234,
 5941,
 5788,
 2982,
 4901,
 3277,
 6063,
 5812,
 4647,
 2982,
 1637,
 6063,
 6125,
 7378,
 3853,
 5891,
 1071,
 358,
 7273,
 4604,
 5835,
 1328,
 4139,
 4604,
 1226]

Le décodage revient à l'instruction d'origine

en_de.decode(data_news_id[0])

['patient', «Ya», 'Les travailleurs du domaine de la santé', 'La', 'Mais', 'Nouveau coronavirus', 'À', 'infection', 'Shi', «Ta», 'chose', 'Mais', 'Révélé', 'Shi', «Ta», «Ikuno-ku, ville d'Osaka», 'de', '「', «Nami», 'Ha', «Ya», 'Réhabilitation', 'hôpital', '」', 'sur', '、', «Préfecture d'Osaka», 'Ha', '0', 'Journée', 'Nuit', '、', 'plus loin', '0', 'Homme', 'de', 'infection', 'Mais', 'clair', 'À', 'Maintenant', «Ta», 'Quand', 'Présentation', 'Shi', «Ta», '。']

Créer des données et des étiquettes

Dans cette tâche de génération de phrases, vous apprendrez comme indiqué dans l'image ci-dessous. Par conséquent, l'étiquette de réponse correcte est celle avec une donnée décalée de l'étiquette. Cette fois, je vais créer mon propre jeu de données spécifique à Pytorch et y créer les données et les étiquettes.
En outre, complétez avec 0 à la longueur spécifiée pour uniformiser la longueur des données, puis renvoyez-les sous forme de type Long Tensor.
À propos, la pad_sequence de keras est utilisée ici, mais pour le moment, une séquence similaire est préparée pour pytorch. Cependant, j'utilise le keras car la longueur à rembourrer et à associer ne peut pas être spécifiée pour le pyroch. blog_rnnlm.png

class MyDataset(torch.utils.data.Dataset):

    def __init__(self, data, max_length=50):
        self.data_num = len(data)
        #Décaler les données de 1
        self.x = [d[:-1] for d in data]
        self.y = [d[1:] for d in data]
        #Longueur à garnir et à assortir
        self.max_length = max_length

    def __len__(self):
        return self.data_num

    def __getitem__(self, idx):

        out_data = self.x[idx]
        out_label =  self.y[idx]

        #Pad pour correspondre à la longueur
        out_data = pad_sequences([out_data], padding='post', maxlen=self.max_length)[0]
        out_label = pad_sequences([out_label], padding='post', maxlen=self.max_length)[0]

        #Convertir en type LongTensor
        out_data = torch.LongTensor(out_data)
        out_label = torch.LongTensor(out_label)

        return out_data, out_label
dataset = MyDataset(data_news_id, max_length=50)
dataset[0]
(tensor([7142, 5775, 3686, 4630, 5891, 4003,  358, 3853, 4139, 4604, 4591, 5891,
         2233, 4139, 4604, 5507, 7378, 2222, 6002, 3277, 5775, 7380, 7234, 5941,
         5788, 2982, 4901, 3277, 6063, 5812, 4647, 2982, 1637, 6063, 6125, 7378,
         3853, 5891, 1071,  358, 7273, 4604, 5835, 1328, 4139, 4604,    0,    0,
            0,    0]),
 tensor([5775, 3686, 4630, 5891, 4003,  358, 3853, 4139, 4604, 4591, 5891, 2233,
         4139, 4604, 5507, 7378, 2222, 6002, 3277, 5775, 7380, 7234, 5941, 5788,
         2982, 4901, 3277, 6063, 5812, 4647, 2982, 1637, 6063, 6125, 7378, 3853,
         5891, 1071,  358, 7273, 4604, 5835, 1328, 4139, 4604, 1226,    0,    0,
            0,    0]))

Créez des unités par lots avec Data Loader

Enfin, le chargeur de données de Pytorch divise les données en lots. Si le nombre de données n'est pas divisible par la taille du lot, il sera différent du dernier nombre de lots, alors définissez drop_last sur True.

data_loader = DataLoader(dataset, batch_size=50, drop_last=True)

Vérifiez uniquement le premier lot

for (x, y) in data_loader:
    print("x_dim: {}, y_dim: {}".format(x.shape, y.shape))
    break
x_dim: torch.Size([50, 50]), y_dim: torch.Size([50, 50])

Modélisation / apprentissage

C'est difficile à comprendre car la taille du lot et le nombre de données d'une donnée sont identiques, mais dans le processus de génération de données jusqu'à présent, la dimension des données est (taille du lot, nombre de séries temporelles (lorsqu'on considère les phrases comme des séries chronologiques de mots), Bien qu'il soit (dimension d'entrée), Pytorch utilise par défaut (numéro de série chronologique, taille du lot, dimension d'entrée), donc batch_first = True doit être spécifié. A part ça, il n'y a rien de spécial à mentionner.

class RNNLM(nn.Module):
    def __init__(self, embedding_dim, hidden_dim, vocab_size, batch_size=100, num_layers=1, device="cuda"):
        super().__init__()
        self.num_layers = num_layers
        self.batch_size = batch_size
        self.hidden_dim = hidden_dim
        self.device = device

        self.embedding = nn.Embedding(vocab_size, embedding_dim, padding_idx=0)
        self.dropout1 = nn.Dropout(0.5)
        self.lstm1 = nn.LSTM(embedding_dim, hidden_dim, batch_first=True, num_layers=self.num_layers)
        self.dropout2 = nn.Dropout(0.5)
        self.lstm2 = nn.LSTM(hidden_dim, hidden_dim, batch_first=True, num_layers=self.num_layers)
        self.dropout3 = nn.Dropout(0.5)
        self.lstm3 = nn.LSTM(hidden_dim, hidden_dim, batch_first=True, num_layers=self.num_layers)
        self.linear = nn.Linear(hidden_dim, vocab_size)

        nn.init.xavier_normal_(self.lstm1.weight_ih_l0)
        nn.init.orthogonal_(self.lstm1.weight_hh_l0)
        nn.init.xavier_normal_(self.lstm2.weight_ih_l0)
        nn.init.orthogonal_(self.lstm2.weight_hh_l0)
        nn.init.xavier_normal_(self.lstm3.weight_ih_l0)
        nn.init.orthogonal_(self.lstm3.weight_hh_l0)
        nn.init.xavier_normal_(self.linear.weight)
        

    def init_hidden(self):
        self.hidden_state = (torch.zeros(self.num_layers, self.batch_size, self.hidden_dim, device=self.device), torch.zeros(self.num_layers, self.batch_size, self.hidden_dim, device=self.device))

    def forward(self, x):
        x = self.embedding(x)
        x = self.dropout1(x)
        h, self.hidden_state = self.lstm1(x, self.hidden_state)
        h = self.dropout2(h)
        h, self.hidden_state = self.lstm2(h, self.hidden_state)
        h = self.dropout3(h)
        h, self.hidden_state = self.lstm3(h, self.hidden_state)
        y = self.linear(h)
        return y

apprendre

Les paramètres sont tout à fait appropriés. Pardon. ..
Comme le nombre de données est petit, j'ai augmenté le nombre d'époques et je les ai beaucoup tournées pour les enregistrer fréquemment.
Dans d'autres tâches d'apprentissage en profondeur, il peut être possible d'évaluer si le surapprentissage ne se fait pas à l'aide des données d'évaluation, ou de mettre fin à l'apprentissage tôt en fonction de cela, mais de la probabilité comme cette fois. La tâche de sortie de la distribution est difficile à évaluer quantitativement. Cette fois, nous évaluons la progression de l'apprentissage et du modèle à l'aide d'un indice appelé perplexité. La perplexité est un peu compliquée lorsqu'elle est exprimée dans une formule mathématique, mais intuitivement, c'est l'inverse de la probabilité de sortie et représente le nombre de branches. En d'autres termes, dans le cas de la tâche de prédire le mot suivant comme cette fois, si la perplexité est de 2, la prédiction de mot est réduite à 2 choix.

if __name__ == '__main__':
    np.random.seed(123)
    torch.manual_seed(123)
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

    EMBEDDING_DIM = HIDDEN_DIM = 256
    VOCAB_SIZE = len(en_de.i2w)
    BATCH_SIZE=50

    model = RNNLM(EMBEDDING_DIM, HIDDEN_DIM, VOCAB_SIZE, batch_size=BATCH_SIZE).to(device)

    criterion = nn.CrossEntropyLoss(reduction='mean', ignore_index=0)
    optimizer = optimizers.Adam(model.parameters(),
                                lr=0.001,
                                betas=(0.9, 0.999), amsgrad=True)
    
    hist = {'train_loss': [], 'ppl':[]}
    epochs = 1000

    def compute_loss(label, pred):
        return criterion(pred, label)

    def train_step(x, t):
        model.train()
        model.init_hidden()
        preds = model(x)
        loss = compute_loss(t.view(-1),
                            preds.view(-1, preds.size(-1)))
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        return loss, preds

    for epoch in tqdm(range(epochs)):
        print('-' * 20)
        print('epoch: {}'.format(epoch+1))

        train_loss = 0.
        loss_count = 0
        
        for (x, t) in data_loader:
            x, t = x.to(device), t.to(device)
            loss, _ = train_step(x, t)
            train_loss += loss.item()
            loss_count += 1

        # perplexity
        ppl = np.exp(train_loss / loss_count)    
        train_loss /= len(data_loader)

        print('train_loss: {:.3f}, ppl: {:.3f}'.format(
            train_loss, ppl
        ))
        
        hist["train_loss"].append(train_loss)
        hist["ppl"].append(ppl)

        
        #Sauver toutes les 20 époques.
        if epoch % 20 == 0:
            model_name = "Destination/embedding{}_v{}.pt".format(EMBEDDING_DIM, epoch)
            torch.save({
                'epoch': epoch,
                'model_state_dict': model.state_dict(),
                'optimizer_state_dict': optimizer.state_dict(),
                'loss': train_loss
            }, model_name)
            logging.info("Saving the checkpoint...")


    torch.save(model.state_dict(), "Destination/embedding{}_v{}.model".format(EMBEDDING_DIM, epoch))
--------------------
epoch: 1
train_loss: 6.726, ppl: 833.451
--------------------
epoch: 2
train_loss: 6.073, ppl: 433.903
--------------------
epoch: 3
train_loss: 6.014, ppl: 409.209
--------------------
epoch: 4
train_loss: 5.904, ppl: 366.649
--------------------
epoch: 5
train_loss: 5.704, ppl: 300.046

・ ・ epoch: 995 train_loss: 0.078, ppl: 1.081 -------------------- epoch: 996 train_loss: 0.077, ppl: 1.081 -------------------- epoch: 997 train_loss: 0.076, ppl: 1.079 -------------------- epoch: 998 train_loss: 0.077, ppl: 1.080 -------------------- epoch: 999 train_loss: 0.077, ppl: 1.080 -------------------- epoch: 1000 train_loss: 0.077, ppl: 1.080

Évaluation

Voyons la transition de train_loss et de la perplexité

 #Visualisation des erreurs
 train_loss = hist['train_loss']

 fig = plt.figure(figsize=(10, 5))
 plt.plot(range(len(train_loss)), train_loss,
             linewidth=1,
             label='train_loss')
 plt.xlabel('epochs')
 plt.ylabel('train_loss')
 plt.legend()
 plt.savefig('output.jpg')
 plt.show()

output_26_0.png

 ppl = hist['ppl']

 fig = plt.figure(figsize=(10, 5))
 plt.plot(range(len(ppl)), ppl,
             linewidth=1,
             label='perplexity')
 plt.xlabel('epochs')
 plt.ylabel('perplexity')
 plt.legend()
 plt.show()

output_27_0.png

train_loss diminue régulièrement. La perplexité a fortement chuté au début et est restée faible au second semestre, atteignant finalement une valeur assez faible de 1,08. La seconde moitié n'a pas beaucoup changé, donc je n'ai peut-être pas dû étudier 1000 fois.

Ensuite, générons une phrase. Choisissez un mot de départ et laissez-le deviner le mot qui le suit. En donnant la sortie de probabilité comme poids au choix dans la partie np.random.choice, le mot sélectionné change à chaque fois qu'il est exécuté.

def generate_sentence(morphemes, model_path, embedding_dim, hidden_dim, vocab_size, batch_size=1):
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    model = RNNLM(embedding_dim, hidden_dim, vocab_size, batch_size).to(device)
    checkpoint = torch.load(model_path)
    model.load_state_dict(checkpoint)

    model.eval()

    with torch.no_grad():
        for morpheme in morphemes:
            model.init_hidden()
            sentence = [morpheme]
            for _ in range(50):
                input_index = en_de.encode([morpheme])
                input_tensor = torch.tensor([input_index], device=device)
                outputs = model(input_tensor)
                probs = F.softmax(torch.squeeze(outputs))
                p = probs.cpu().detach().numpy()
                morpheme = en_de.i2w[np.random.choice(len(p), p=p)]
                sentence.append(morpheme)
                if morpheme in ["。", "<pad>"]:
                    break
            print("".join(sentence))
            print('-' * 50)


EMBEDDING_DIM = HIDDEN_DIM = 256
VOCAB_SIZE = len(en_de.i2w)
model_path ="Destination/embedding{}_v{}.model"
morphemes = ["premier ministre", "Gouverneur de Tokyo", "couronne", "Nouveau modèlecouronneウイルス", "Nouveau modèle", "Japon", "Tokyo", "Personne infectée", "Urgence"]

generate_sentence(morphemes, model_path, EMBEDDING_DIM, HIDDEN_DIM, VOCAB_SIZE)

La voix du changement de sprint du Premier ministre est trop pour élever l'année! " Dites au médecin et que le secrétaire général est parti -------------------------------------------------- Le gouverneur de Tokyo l'a également demandé, mais même s'il n'y avait pas de lit, je pensais que c'était une décision de magasin.Que dois-je me dépêcher de la nouvelle infection à coronavirus, mesures liées à la vitesse d'infection du nouveau coronavirus LDP Ce nombre de pauses de pratique d'excuses est interdit Anxiété Passé la fièvre de Crémone au moins -------------------------------------------------- Corona Avantages Psychologie Le gouvernement n'a pas l'échelle et les ressources nécessaires à l'échelle nationale, et il a envie de l'éviter tout en le disant. Sentiment d'évitement 0 minute d'augmentation Taux de participation Fourni de cette manière Traiter le «Nombre, Monohealth découvert à minuit Avertissement maximum, anxiété, effets secondaires De la nouvelle infection à coronavirus -------------------------------------------------- Un nouveau commis au fonds contre le virus corona a également signalé et présenté ses excuses pour la chaleur et la réponse du passé Tokyo. -------------------------------------------------- Il n'y a pas de magasins annoncés comme de nouvelles appellations, et plus que des résidents (expliquez ..). "On rapporte que le nombre de Yuriko va diminuer. Pourquoi le gouvernement demande-t-il une augmentation?" -------------------------------------------------- Le pays voisin du Japon appelle à un match. Désinfection des tee-shirts. Explication de la marche à suivre. -------------------------------------------------- De Tokyo ou au-dessus, de 0 yen à 0 aller-retour par évaluation de l'épidémie à 0 aller-retour. Nombre d'effondrements (le moment de l'installation de secours à domicile et le nombre de commentaires demandés ont également diminué. Il n'y a aucune perspective à minuit. -------------------------------------------------- 0 établissements médicaux aller-retour de personnes infectées, etc. LINE Tokyo Diminuer la déclaration de transition Un peu de pratique pour les clients Environ 0 établissements médicaux Confirmé à minuit dans le rapport précédent Tele-east Il n'y a pas de vitesse d'infection merveilleuse Administration Trump Il n'y a pas de Komei Party Magasins à réduire ou étrangers ... "Avec la nouvelle infection à coronavirus -------------------------------------------------- Confirmer les situations d'urgence pour les nouveaux arrivants Faire face et fournir une vitesse estimée (Merci d'avoir réduit le nombre de personnes dans l'année. Désinfection. Leader émis Problème passé -------------------------------------------------- Je n'ai pas fait de phrase qui ait du sens, mais il semble que j'ai un peu appris sur la position des paroles des parties.
La génération de phrases était difficile. Je souhaite poursuivre mes études et m'améliorer
Si vous avez des conseils ou des suggestions, je vous serais reconnaissant de bien vouloir commenter ..!

Sites / documents référencés

Recommended Posts

[Introduction à Pytorch] Je souhaite générer des phrases dans des articles de presse
[Introduction à Pytorch] J'ai joué avec sinGAN ♬
Je veux intégrer Matplotlib dans PySimpleGUI
[Langage C] Je souhaite générer des nombres aléatoires dans la plage spécifiée
Je veux faire le test de Dunnett en Python
Je veux corriger Datetime.now dans le test de Django
Je veux créer une fenêtre avec Python
J'ai écrit "Introduction à la vérification des effets" en Python
Je souhaite stocker les informations de la base de données dans la liste
Je veux fusionner des dictionnaires imbriqués en Python
Je veux afficher la progression en Python!
Je veux utiliser PyTorch pour générer quelque chose comme les paroles de Japari Park
Introduction à Lightning Pytorch
Je veux écrire en Python! (1) Vérification du format de code
Je souhaite intégrer une variable dans une chaîne Python
Je veux facilement implémenter le délai d'expiration en python
Je veux générer rapidement UUID (memo memo) ~ Edition Python ~
Je veux faire la transition avec un bouton sur le ballon
Je veux utiliser self avec Backpropagation (tf.custom_gradient) (tensorflow)
Je veux écrire en Python! (2) Écrivons un test
Je veux échantillonner au hasard un fichier avec Python
Je veux travailler avec un robot en python.
Je veux écrire en Python! (3) Utiliser des simulacres
[Introduction à Pytorch] J'ai essayé de catégoriser Cifar10 avec VGG16 ♬
[Échec] Je voulais générer des phrases en utilisant TextRegressor de Flair
Je veux utiliser le jeu de données R avec python
Je veux faire quelque chose avec Python à la fin
Je veux manipuler des chaînes dans Kotlin comme Python!
Je souhaite supprimer facilement une colonne contenant NA dans R
Je veux faire quelque chose comme sort uniq en Python
Je veux générer automatiquement un nom de groupe de metal moderne
J'ai fait une commande pour générer un commentaire pour une table dans Django
Je veux résoudre SUDOKU
[Django] Je souhaite me connecter automatiquement après une nouvelle inscription
Je veux rendre le type de dictionnaire dans la liste unique
Je veux compter des valeurs uniques dans un tableau ou un tuple
Je veux aligner les nombres valides dans le tableau Numpy
Je veux pouvoir exécuter Python avec VS Code
Je veux ajouter un joli complément à input () en python
Je ne voulais pas écrire la clé AWS dans le programme
[Pytorch] Je souhaite attribuer manuellement les paramètres d'entraînement du modèle
[Linux] Je souhaite connaître la date à laquelle l'utilisateur s'est connecté
Je veux résoudre APG4b avec Python (seulement 4.01 et 4.04 au chapitre 4)
Je veux utiliser des rails avec des rails même dans un environnement vagabond
Développement LINEbot, je souhaite vérifier le fonctionnement dans l'environnement local
[Couches Python / AWS Lambda] Je souhaite réutiliser uniquement le module dans AWS Lambda Layers
[Introduction] Je veux créer un robot Mastodon avec Python! 【Débutants】
Je veux créer un fichier pip et le refléter dans le menu fixe
Je veux faire de la deuxième ligne le nom de la colonne dans pandas
Je veux réussir le test G dans un mois Jour 1
Je veux connaître la population de chaque pays du monde.
Je veux comprendre à peu près systemd
[Détails (?)] Introduction au pytorch ~ CNN de CIFAR10 ~
J'ai essayé d'expliquer l'ensemble de données de Pytorch
J'ai écrit Gray Scale avec Pytorch
Je veux gratter des images et les former
Je veux faire ○○ avec les Pandas
Je veux copier l'annotation de yolo
Je veux déboguer avec Python