[PYTHON] Outil de ligne de commande Chainer ChainerCMD

―― Il est difficile d'écrire le même code d'entraînement chaque fois que vous voulez essayer quelque chose en écrivant rapidement uniquement le code du modèle et l'ensemble de données. --Chainer a un outil pratique appelé Trainer, mais il est toujours difficile d'écrire du code pour ajouter une extension similaire à chaque fois pour créer train.py. ――Si vous passez un fichier YAML qui décrit les paramètres d'apprentissage, le fichier modèle, le fichier d'ensemble de données, etc. comme argument, ce serait facile s'il y avait un outil de ligne de commande qui commencerait à apprendre comme d'habitude. Je l'ai fait. Il s'agit essentiellement d'un outil oléorésabo complet, donc il n'a pas du tout été conçu avec l'idée d'être utilisé par les gens, mais il a été publié parce que c'était un gros problème.

ChainerCMD
https://github.com/mitmul/chainercmd

1. Installation

$ pip install chainercmd

2. Comment utiliser

$ chainer init

Ensuite, les quatre fichiers suivants seront créés.

Le contenu est comme ça.

config.yml


stop_trigger: [20, "epoch"]
# max_workspace_size: 8388608  # default: 8 * 1024 * 1024

dataset:
  train:
    file: dataset.py
    name: MNIST
    batchsize: 128
    args:
      split: train
      ndim: 3
  valid:
    file: dataset.py
    name: MNIST
    batchsize: 64
    args:
      split: valid
      ndim: 3

model:
  file: model.py
  name: LeNet5
  args:
    n_class: 10

loss:
  module: chainer.links.model.classifier
  # file: loss.py  # If you use your own loss definition, remove "module" key above and use "file" key to specify the path to the file which describes the "name" class for a Loss link.
  name: Classifier

optimizer:
  method: MomentumSGD
  args:
    lr: 0.01
  weight_decay: 0.0001
  lr_drop_ratio: 0.1
  lr_drop_triggers:
    points: [10, 15]
    unit: epoch

# You can ommit this part
# updater_creator:
#   file: updater_creator.py
#   name: MyUpdaterCreator
#   args:
#     print: True

trainer_extension:
  - custom:
      file: custom_extension.py
      name: CustomExtension
      args:
          message: 'I am learning...'
  - LogReport:
      trigger: [1, "epoch"]
  - dump_graph:
      root_name: main/loss
      out_name: cg.dot
  - observe_lr:
      trigger: [1, "epoch"]
  - ParameterStatistics:
      links:
        - conv1
        - conv2
        - conv3
        - fc4
        - fc5
      report_params: True
      report_grads: True
      prefix: null
      trigger: [1, "epoch"]
  - Evaluator:
      module: chainer.training.extensions
      name: Evaluator
      trigger: [1, "epoch"]
      prefix: val
    # You can specify other evaluator like this:
    #   module: chainercv.extensions
    #   name: SemanticSegmentationEvaluator
    #   trigger: [1, "epoch"]
    #   prefix: val
  - PlotReport:
      y_keys:
        - conv1/W/data/mean
        - conv2/W/data/mean
        - conv3/W/data/mean
        - conv4/W/data/mean
        - fc5/W/data/mean
        - fc6/W/data/mean
      x_key: epoch
      file_name: parameter_mean.png
      trigger: [1, "epoch"]
  - PlotReport:
      y_keys:
        - conv1/W/data/std
        - conv2/W/data/std
        - conv3/W/data/std
        - conv4/W/data/std
        - fc5/W/data/std
        - fc6/W/data/std
      x_key: epoch
      file_name: parameter_std.png
      trigger: [1, "epoch"]
  - PlotReport:
      y_keys:
        - main/loss
        - val/main/loss
      x_key: epoch
      file_name: loss.png
      trigger: [1, "epoch"]
  - PlotReport:
      y_keys:
        - main/accuracy
        - val/main/accuracy
      x_key: epoch
      file_name: accuracy.png
      trigger: [1, "epoch"]
  - PrintReport:
      entries:
        - epoch
        - iteration
        - main/loss
        - main/accuracy
        - val/main/loss
        - val/main/accuracy
        - elapsed_time
        - lr
      trigger: [1, "epoch"]
  - ProgressBar:
      update_interval: 10
      trigger: [10, "iteration"]
  - snapshot:
      filename: trainer_{.updater.epoch}_epoch
      trigger: [10, "epoch"]

custom_extension.py


import chainer


class CustomExtension(chainer.training.Extension):

    def __init__(self, message):
        self._message = message

    def initialize(self, trainer):
        self._message += ' and Trainer ID is: {}'.format(id(trainer))

    def __call__(self, trainer):
        pass

    def serialize(self, serializer):
        self._message = serializer('_message', self._message)

dataset.py


import chainer


class Dataset(chainer.dataset.DatasetMixin):

    def __init__(self, split='train'):
        super().__init__()

    def __len__(self):
        pass

    def get_example(self, i):
        pass


# You can delete this
class MNIST(chainer.dataset.DatasetMixin):

    def __init__(self, split='train', ndim=3):
        super().__init__()
        train, valid = chainer.datasets.get_mnist(ndim=ndim)
        self.d = train if split == 'train' else valid

    def __len__(self):
        return len(self.d)

    def get_example(self, i):
        return self.d[i]

loss.py


from chainer import link
from chainer import reporter
from chainer.functions.evaluation import accuracy
from chainer.functions.loss import softmax_cross_entropy


class MyLoss(link.Chain):

    def __init__(self, predictor):
        super().__init__()
        self.lossfun = softmax_cross_entropy.softmax_cross_entropy
        self.accfun = accuracy.accuracy
        self.y = None
        self.loss = None
        self.accuracy = None

        with self.init_scope():
            self.predictor = predictor

    def __call__(self, *args):
        assert len(args) >= 2
        x = args[:-1]
        t = args[-1]
        self.y = None
        self.loss = None
        self.accuracy = None
        self.y = self.predictor(*x)
        self.loss = self.lossfun(self.y, t)
        reporter.report({'loss': self.loss}, self)
        self.accuracy = self.accfun(self.y, t)
        reporter.report({'accuracy': self.accuracy}, self)
        return self.loss

model.py


import chainer
import chainer.functions as F
import chainer.links as L


class Model(chainer.Chain):

    """Model definition.
    This is a template of model definition.
    """

    def __init__(self, n_class):
        super().__init__()
        with self.init_scope():
            pass

    def __call__(self, x):
        pass


# You can delete this! It's a sample model
class LeNet5(chainer.Chain):

    def __init__(self, n_class):
        super().__init__()
        with self.init_scope():
            self.conv1 = L.Convolution2D(None, 6, 5, 1)
            self.conv2 = L.Convolution2D(6, 16, 5, 1)
            self.conv3 = L.Convolution2D(16, 120, 4, 1)
            self.fc4 = L.Linear(None, 84)
            self.fc5 = L.Linear(84, n_class)

    def __call__(self, x):
        h = F.relu(self.conv1(x))
        h = F.max_pooling_2d(h, 2, 2)
        h = F.relu(self.conv2(h))
        h = F.max_pooling_2d(h, 2, 2)
        h = F.relu(self.conv3(h))
        h = F.relu(self.fc4(h))
        return self.fc5(h)

Après cela, modifiez-le pour les tâches individuelles et lorsque vous avez terminé

$ chainer train config.yml --gpus 0

Ensuite, l'apprentissage commencera à utiliser l'appareil avec l'ID GPU: 0. Si vous souhaitez utiliser plusieurs GPU, spécifiez les ID des GPU que vous souhaitez utiliser, séparés par des espaces, tels que «--gpus 0 1 2 3».

3. Description de chaque fichier

config.yml

Il s'agit d'un fichier qui décrit les paramètres d'apprentissage et le fichier à utiliser comme fichier modèle.

dataset

dataset:
  train:
    file: dataset.py
    name: MNIST
    batchsize: 128
    args:
      split: train
      ndim: 3
  valid:
    file: dataset.py
    name: MNIST
    batchsize: 64
    args:
      split: valid
      ndim: 3

Il s'agit du réglage de l'ensemble de données d'entraînement et de l'ensemble de données de vérification. Définissez-les dans un autre fichier à l'avance, utilisez la clé file pour spécifier le chemin d'accès à ce fichier et utilisez nom pour spécifier le nom de classe de la classe de l'ensemble de données dans ce fichier. La valeur de ʻargsdoit être un dictionnaire, qui est passé comme argument de mot-clé au constructeur de la classe d'ensemble de données, tout comme** args. batchsize` est la taille du mini-lot créé à partir de chaque ensemble de données.

model & loss

model:
  file: model.py
  name: LeNet5
  args:
    n_class: 10

C'est presque la même chose, créer un modèle en instanciant une classe nommée name dans le fichier dans le chemin spécifié par file. S'il y a des «arguments» à ce moment-là, passez-le comme argument de mot-clé.

loss:
  module: chainer.links
  # file: loss.py  # If you use your own loss definition, remove "module" key above and use "file" key to specify the path to the file which describes the "name" class for a Loss link.
  name: Classifier

La partie loss est fondamentalement la même, et ʻargs est omis ici, mais si vous spécifiez un dictionnaire avec ʻargs comme clé comme la partie model, il calculera la perte. Il est passé comme argument de mot-clé au constructeur de la classe pour. Pour loss, vous pouvez utiliser le module de clé afin que vous puissiez également utiliser chainer.links.Classifieretc. préparé par Chainer à l'avance.module et file` ne peuvent pas être utilisés en même temps.

optimizer

optimizer:
  method: MomentumSGD
  args:
    lr: 0.01
  weight_decay: 0.0001
  lr_drop_ratio: 0.1
  lr_drop_triggers:
    points: [10, 15]
    unit: epoch

Il s'agit de la partie paramétrage de l'Optimizer. method sera le nom de la classe sous le module ʻoptimizers de Chainer. ʻArgs est un argument mot-clé à transmettre à son constructeur. Si la clé weight_decay est présente, la décroissance de poids est ajoutée en tant que crochet de l'optimiseur. Si les deux lr_drop_ratio et lr_drop_triggers sont présents, le taux d'apprentissage est supprimé en utilisant le ManualScheduleTrigger. Dans le dictionnaire passé à lr_drop_triggers, quand points double le taux d'apprentissage de lr_drop_ratio, et ʻunit est l'unité de ce timing ("epoch ou ʻitération` peut être spécifié). Dans le cas de l'exemple ci-dessus, le taux d'apprentissage de Momentum SGD est de 0,1 fois pour 10 époques et de 0,1 fois pour 15 époques à nouveau.

updater

Updater peut être personnalisé en préparant une fonction qui prend l'itérateur, l'optimiseur et les périphériques et renvoie un objet Updater, et en le spécifiant avec la clé updater_creater dans config.yml.

Autres fichiers

Une classe wrapper qui calcule les classes d'ensemble de données, les classes de modèle et les pertes écrites à l'aide de Chainer ordinaire. Pour custom_extension.py, écrivez votre propre classe d'extension, spécifiez-la à partir de config.yml, et réécrivez-la lorsque vous voulez l'ajouter à Trainer.

4. Apprendre

Le fichier créé par chainer init contient les modèles et les ensembles de données nécessaires pour exécuter l'exemple MNIST depuis le début, et si config.yml est laissé tel quel, ces ensembles de données de modèle, etc. Est prêt à exécuter l'exemple MNIST en utilisant. Donc, tout ce que vous avez à faire est d'appuyer sur la commande chainer. Spécifiez train dans la sous-commande.

$ chainer train config.yml --gpus 0

Spécifiez le chemin du fichier de configuration YAML que vous souhaitez utiliser à côté de la sous-commande train. Il n'est pas nécessaire que le nom de fichier soit config.yml. Si vous souhaitez utiliser le GPU, spécifiez l'ID de l'appareil avec l'option --gpus. Si plus d'un est spécifié, tel que --gpus 0 1 2 3, ParallelUpdater ou MultiprocessParallelUpdater (si NCCL est activé) sera automatiquement sélectionné et l'apprentissage sera effectué sur plusieurs GPU.

Lorsque vous faites sur Ubuntu Vous pouvez éviter les erreurs autour de PlotReport en définissant MPLBACKEND = Agg et les variables d'environnement.

Le résultat du tournage est le suivant. J'ai essayé d'utiliser 4 GPU en vain.

$ MPLBACKEND=Agg chainer train config.yml --gpus 0 1 2 3
chainer version: 2.0.1
cuda: True, cudnn: True, nccl: True
result_dir: result/config_2017-07-18_23-26-41_0
train: 60000
valid: 10000
/home/shunta/.pyenv/versions/anaconda3-4.4.0/lib/python3.6/site-packages/chainer/training/updaters/multiprocess_parallel_updater.py:142: UserWarning: optimizer.lr is changed to 0.0025 by MultiprocessParallelUpdater for new batch size.
  format(optimizer.lr))
epoch       iteration   main/loss   validation/main/loss  main/accuracy  validation/main/accuracy  elapsed_time  lr
1           118         0.890775    0.234464              0.739672       0.928896                  8.45449       0.0025
2           235         0.198075    0.141786              0.939503       0.957476                  13.914        0.0025
3           352         0.128017    0.120378              0.960737       0.960839                  19.1064       0.0025
4           469         0.100555    0.0979902             0.96895        0.969739                  24.3107       0.0025
5           586         0.0865762   0.077968              0.971888       0.97587                   29.2581       0.0025
6           704         0.0734014   0.0672336             0.976562       0.978837                  34.3428       0.0025
7           821         0.0683174   0.0618281             0.977564       0.979826                  39.1815       0.0025
8           938         0.0617364   0.0748559             0.980235       0.976958                  44.0893       0.0025
9           1055        0.0573468   0.0596004             0.981904       0.980024                  49.0457       0.0025
10          1172        0.0531992   0.0578394             0.98364        0.982694                  54.3706       0.0025
11          1290        0.047489    0.0485524             0.986096       0.984573                  59.3655       0.00025
12          1407        0.0417473   0.0482626             0.987513       0.984968                  64.18         0.00025
13          1524        0.0406346   0.0473873             0.987914       0.984771                  69.0114       0.00025
14          1641        0.0405981   0.0479212             0.987847       0.985265                  74.0731       0.00025
15          1758        0.0394898   0.0478847             0.988114       0.986155                  79.3369       0.00025
16          1875        0.0394069   0.0472816             0.988181       0.984968                  84.2785       2.5e-05
17          1993        0.0389244   0.0471326             0.988546       0.985166                  89.4715       2.5e-05
18          2110        0.0391655   0.046991              0.988181       0.985463                  94.6602       2.5e-05
19          2227        0.0390729   0.0468674             0.988381       0.985364                  99.7827       2.5e-05
20          2344        0.038587    0.0471131             0.988315       0.985166                  104.962       2.5e-05

Puisque deux PlotReports ont été définis dans config.yml, spécifiez dans config.yml dans le répertoire de résultats (un répertoire avec un nom qui concatène le nom de base du fichier de configuration et la date / heure est créée sous result). Des images des noms de fichiers (loss.png et precision.png) sont créées.

loss.png

accuracy.png

6. À propos du répertoire de résultats

Lorsque l'apprentissage est démarré avec la commande chainer, le nom de fichier du fichier de configuration spécifié automatiquement (lorsque config.yml est spécifié, la partie de config regardant dans l'extension) et l'heure de début sont également inclus. En conséquence, un répertoire est automatiquement créé sous le répertoire result qui peut être créé dans la hiérarchie exécutée, et les fichiers de modèle, les fichiers de perte, les fichiers de configuration, etc. y sont automatiquement copiés. Ce répertoire est spécifié dans ʻout` de Trainer, donc des instantanés et des fichiers journaux y sont également écrits.

7. Conclusion

Les choses qui peuvent être faites avec un tel outil sont très limitées, mais j'ai souvent écrit le même train.py à chaque fois, j'ai donc essayé de le généraliser de manière appropriée. Cependant, si vous utilisez NLP ou GAN, vous ne pouvez pas l'utiliser car il est strict à moins que vous ne puissiez également spécifier Updater. Vous pourrez peut-être l'utiliser pour des tâches simples de reconnaissance d'image.

Recommended Posts

Outil de ligne de commande Chainer ChainerCMD
Essayez d'ajouter Glances (l'outil de surveillance de ligne de commande de Python).
Note d'introduction à la ligne de commande
Votre propre Koredake est une commande Linux
Une collection de lignes de commande qui utilisent des environnements virtuels avec Anaconda
Raccourci de ligne de commande Linux
Un outil de ligne de commande qui place .gitkeep dans un répertoire vide
Rechercher des fichiers volumineux sous Linux à partir de la ligne de commande
Publier Twitter depuis la ligne de commande
Fait une commande pour FizzBuzz
Dictionnaire de commande Linux (pour moi-même)
Mémorandum de commande Linux [pour les débutants]
MyCLI: interface de ligne de commande avec auto-complétion pour MySQL, MariaDB, Percona
Créez un outil de ligne de commande pour convertir des dollars en yens en utilisant Python