[PYTHON] Chainer-Befehlszeilentool ChainerCMD

――Es ist mühsam, jedes Mal denselben Trainingscode zu schreiben, wenn Sie etwas ausprobieren möchten, indem Sie nur den Code des Modells und des Datensatzes schnell schreiben. --Chainer hat ein praktisches Tool namens Trainer, aber es ist immer noch mühsam, Code zu schreiben, um jedes Mal eine ähnliche Erweiterung hinzuzufügen, um train.py zu erstellen. ――Es wäre schön, ein Befehlszeilentool zu haben, das wie gewohnt mit dem Lernen beginnt, wenn Sie eine YAML-Datei übergeben, in der die Lerneinstellungen, die Modelldatei, die Datensatzdatei usw. als Argument beschrieben werden. Ich habe es gemacht. ――Es handelt sich im Grunde genommen um ein komplettes Oleoresabo-Tool, daher wurde es nicht mit der Idee erstellt, überhaupt von Menschen verwendet zu werden, aber es wurde veröffentlicht, weil es eine große Sache war.

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

1. Installation

$ pip install chainercmd

2. Verwendung

$ chainer init

Anschließend werden die folgenden vier Dateien erstellt.

Der Inhalt ist so.

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)

Bearbeiten Sie dies anschließend grundsätzlich für einzelne Aufgaben und wenn Sie fertig sind

$ chainer train config.yml --gpus 0

Anschließend beginnt das Lernen mit der Verwendung des Geräts mit der GPU-ID: 0. Wenn Sie mehrere GPUs verwenden möchten, geben Sie die IDs der zu verwendenden GPUs an, die durch Leerzeichen getrennt sind, z. B. "--gpus 0 1 2 3".

3. Beschreibung jeder Datei

config.yml

Es ist eine Datei, die Lerneinstellungen beschreibt und welche Datei als Modelldatei verwendet werden soll.

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

Dies ist die Einstellung des Trainingsdatensatzes und des Verifizierungsdatensatzes. Definieren Sie sie im Voraus in einer anderen Datei, geben Sie mit dem Schlüssel "file" den Pfad zu dieser Datei an und geben Sie mit "name" den Klassennamen der Dataset-Klasse in dieser Datei an. Der Wert von "args" muss ein Wörterbuch sein, das wie "** args" als Schlüsselwortargument an den Konstruktor der Dataset-Klasse übergeben wird. batchsize ist die Größe des Mini-Batches, der aus jedem Datensatz erstellt wird.

model & loss

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

Dies ist fast das gleiche: Erstellen eines Modells durch Instanziieren einer Klasse mit dem Namen "Name" in der Datei in dem durch "Datei" angegebenen Pfad. Wenn zu diesem Zeitpunkt args vorhanden ist, übergeben Sie es als Schlüsselwortargument.

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

Der "Verlust" -Teil ist im Grunde der gleiche, und "Argumente" wird hier weggelassen. Wenn Sie jedoch ein Wörterbuch mit "Argumenten" als Schlüssel wie den "Modell" -Teil angeben, wird der Verlust berechnet. Es wird als Schlüsselwortargument an den Konstruktor der Klasse für übergeben. Für "Verlust" können Sie das Schlüsselmodul verwenden, so dass Sie auch "chainer.links.Classifier" usw. verwenden können, die von Chainer im Voraus vorbereitet wurden. module und file können nicht gleichzeitig verwendet werden.

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

Dies ist der Einstellungsteil von Optimizer. method ist der Klassenname unter dem Chainer-Modul optimizers. args ist ein Schlüsselwortargument, das an seinen Konstruktor übergeben werden soll. Wenn der Schlüssel weight_decay vorhanden ist, wird Weight Decay als Optimizer-Hook hinzugefügt. Wenn sowohl lr_drop_ratio als auch lr_drop_triggers vorhanden sind, wird die Lernrate mit dem ManualScheduleTrigger gesenkt. In dem an "lr_drop_triggers" übergebenen Wörterbuch ist "unit" die Einheit dieses Timings, wenn "points" die Lernrate um "lr_drop_ratio" verdoppelt ("epoch" oder "iteration" kann angegeben werden). Im Fall des obigen Beispiels beträgt die Lernrate von Momentum SGD für 10 Epochen das 0,1-fache und für 15 Epochen erneut das 0,1-fache.

updater

Updater kann angepasst werden, indem eine Funktion vorbereitet wird, die Iterator, Optimierer und Geräte verwendet, ein Updater-Objekt zurückgibt und es mit dem Schlüssel updater_creater in config.yml angibt.

Andere Dateien

Eine Wrapper-Klasse, die Dataset-Klassen, Modellklassen und Verluste berechnet, die mit einem normalen Chainer geschrieben wurden. Schreiben Sie für custom_extension.py Ihre eigene Extension-Klasse, geben Sie sie in config.yml an und schreiben Sie sie neu, wenn Sie sie Trainer hinzufügen möchten.

4. Lernen

Die von "chainer init" erstellte Datei enthält die Modelle und Datensätze, die erforderlich sind, um das MNIST-Beispiel von Anfang an auszuführen. Wenn "config.yml" unverändert bleibt, werden diese Modelldatensätze usw. Ist bereit, das MNIST-Beispiel mit auszuführen. Alles was Sie tun müssen, ist den Befehl chainer zu drücken. Geben Sie im Unterbefehl train an.

$ chainer train config.yml --gpus 0

Geben Sie den Pfad der Konfigurations-YAML-Datei an, die Sie neben dem Unterbefehl train verwenden möchten. Der Dateiname muss nicht config.yml sein. Wenn Sie eine GPU verwenden möchten, geben Sie die Geräte-ID mit der Option "--gpus" an. Wenn mehr als eine angegeben wird, z. B. "--gpus 0 1 2 3", wird ParallelUpdater oder MultiprocessParallelUpdater (wenn NCCL aktiviert ist) automatisch ausgewählt und das Lernen wird auf mehreren GPUs durchgeführt.

Unter Ubuntu können Sie Fehler in PlotReport vermeiden, indem Sie "MPLBACKEND = Agg" und Umgebungsvariablen festlegen.

Das Ergebnis des tatsächlichen Drehens ist wie folgt. Ich habe vergeblich versucht, 4 GPUs zu verwenden.

$ 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

Da zwei plotReports in config.yml festgelegt wurden, geben Sie in config.yml im Ergebnisverzeichnis an (ein Verzeichnis mit einem Namen, der den Basisnamen der Konfigurationsdatei verkettet und das Datum / die Uhrzeit unter result erstellt). Bilder der Dateinamen (loss.png und Genauigkeit.png) werden erstellt.

loss.png

accuracy.png

6. Über das Ergebnisverzeichnis

Wenn das Lernen mit dem Befehl "chainer" gestartet wird, sind auch der Dateiname der automatisch angegebenen Konfigurationsdatei (wenn "config.yml" angegeben ist, der Teil von "config", der in die Erweiterung schaut) und die Startzeit enthalten. Infolgedessen wird automatisch ein Verzeichnis unter dem Verzeichnis "result" erstellt, das in der ausgeführten Hierarchie erstellt werden kann, und Modelldateien, Verlustdateien, Konfigurationsdateien usw. werden automatisch in dieses Verzeichnis kopiert. Dieses Verzeichnis ist in "out" des Trainers angegeben, daher werden dort auch Snapshots und Protokolldateien geschrieben.

7. Schlussfolgerung

Die Dinge, die mit einem solchen Tool gemacht werden können, sind sehr begrenzt, aber ich habe oft jedes Mal den gleichen train.py geschrieben, also habe ich versucht, ihn angemessen zu verallgemeinern. Wenn Sie jedoch NLP oder GAN ausführen, können Sie es nicht verwenden, da es streng ist, es sei denn, Sie können auch Updater angeben. Möglicherweise können Sie es für einfache Bilderkennungsaufgaben verwenden.

Recommended Posts

Chainer-Befehlszeilentool ChainerCMD
Versuchen Sie, Glances hinzuzufügen (Pythons Befehlszeilenüberwachungstool).
Einleitende Anmerkung zur Befehlszeile
Ihr eigener Koredake ist ein Linux-Befehl
Eine Sammlung von Befehlszeilen, die virtuelle Umgebungen mit Anaconda verwenden
Linux-Befehlszeilenverknüpfung
Ein Befehlszeilentool, das .gitkeep in ein leeres Verzeichnis legt
Suchen Sie unter Linux über die Befehlszeile nach großen Dateien
Poste Twitter über die Kommandozeile
Befehl für FizzBuzz erstellt
Linux Command Dictionary (für mich)
Linux Command Memorandum [für Anfänger]
MyCLI: Befehlszeilenschnittstelle mit automatischer Vervollständigung für MySQL, MariaDB, Percona
Erstellen Sie ein Befehlszeilenprogramm, um mit Python Dollar in Yen umzurechnen