[PYTHON] A propos du comportement de enable_backprop de Chainer v2

TL;DR

Objectif

Quand j'ai finalement soulevé mon poids et essayé d'élever la bibliothèque d'aide Chainer et Oreore à la v2, j'ai remarqué que ʻenable_backprop` se comportait de manière erratique. En particulier, considérez les questions suivantes.

Au fait, ce post est à partir de la v2.0.0.

Enquête

Et si ʻenable_backprop` change au milieu du graphique?

Avec l'implémentation utilisant contextmanager, je suis très curieux de savoir ce qui se passe si ʻenable_backpropchange au milieu du graphique. Sino_backprop_mode a un effet lors de l'exécution de backward (), et que j'ai utilisé no_backprop_modeau moment du calcul avant, mais j'ai oublié de placer le calcul arrière avecno_backprop_mode`, la précision est donc anormalement bonne. J'ai des ennuis [^ 1].

[^ 1]: Bien sûr, les données ne changent pas réellement à moins que vous n'appeliez l'optimiseur, donc vous pouvez faire cette erreur en pratique.

La réponse se trouve à l'adresse function_node.py. Comme décrit ici, l'effet de no_backprop_mode est que le parent n'est pas enregistré lors de la construction du graphe de calcul, donc il a l'effet ** dans la plage spécifiée pendant ** le calcul avant.

Exécution normale

a = chainer.Variable(np.array([0.1], dtype=np.float32))
with chainer.configuration.using_config('enable_backprop', True):
    chainer.config.show()
    b = a * 2.0
b.backward()
print a.grad

production


cudnn_deterministic  False
debug                False
enable_backprop      True
keep_graph_on_report False
train                True
type_check           True
use_cudnn            auto
[ 2.]

Vous pouvez voir qu'il est efficace même si l'arrière est hors de portée.

a = chainer.Variable(np.array([0.1], dtype=np.float32))
with chainer.configuration.using_config('enable_backprop', False):
    chainer.config.show()
    b = a * 2.0
b.backward()
print a.grad

production


cudnn_deterministic  False
debug                False
enable_backprop      False
keep_graph_on_report False
train                True
type_check           True
use_cudnn            auto
None

De plus, comme mentionné ci-dessus, ʻenable_backprop` se déconnecte du ** parent **. Par conséquent, le gradient de son parent est 0, pas la variable nouvellement créée dans contextmanager.

a = chainer.Variable(np.array([0.1], dtype=np.float32))
with chainer.configuration.using_config('enable_backprop', False):
    b = a * 2.0
c = b + 0.5
c.backward()
print a.grad  # None
print b.grad  # [ 1.]

De plus, ** n'est pas complètement dépendant de la configuration lors de l'appel vers l'arrière? Non **. Par conséquent, il semble préférable d'utiliser ʻunchain_backward () docilement au lieu d'implémenter ʻenable_backprop plusieurs fois dans un graphe de calcul.

Si train est défini sur False, est-ce que ʻenable_backprop` sera automatiquement défini sur False?

** Non. ** **

a = chainer.Variable(np.array([0.1], dtype=np.float32))
with chainer.configuration.using_config('train', False):
    chainer.config.show()
    b = a * 2.0
b.backward()
print a.grad

production


cudnn_deterministic  False
debug                False
enable_backprop      True
keep_graph_on_report False
train                False
type_check           True
use_cudnn            auto
[ 2.]

Donc, si vous écrivez votre propre code comme ʻextensions.Evaluator, vous devez définir ʻenable_backprop et train sur False.

Est-il acceptable de faire confiance et d'utiliser la bibliothèque standard chainer.extensions.Evaluator?

** Ça a l'air bien. ** ʻenable_backprop et trainsont tous les deuxFalse`.

import chainer
import chainer.functions as F
import chainer.links as L
from chainer import training
from chainer.training import extensions

# Network definition
class MLP(chainer.Chain):
    def __init__(self, n_out):
        super(MLP, self).__init__()
        with self.init_scope():
            self.l1 = L.Linear(None, n_out)

    def __call__(self, x):
        chainer.config.show()
        print ""
        return self.l1(x)

model = L.Classifier(MLP(10))

optimizer = chainer.optimizers.Adam()
optimizer.setup(model)

# Load the MNIST dataset
train, test = chainer.datasets.get_mnist()
test = chainer.datasets.split_dataset(test, 1)[0]

train_iter = chainer.iterators.SerialIterator(train, 32)
test_iter = chainer.iterators.SerialIterator(test, 1,
                                             repeat=False, shuffle=False)
# Set up a trainer
updater = training.StandardUpdater(train_iter, optimizer)
trainer = training.Trainer(updater, (1, 'iteration'))
trainer.extend(extensions.Evaluator(test_iter, model), trigger=(1, 'iteration'))

# Run the training
trainer.run()

production


cudnn_deterministic  False
debug                False
enable_backprop      True
keep_graph_on_report False
train                True
type_check           True
use_cudnn            auto

cudnn_deterministic  False
debug                False
enable_backprop      False
keep_graph_on_report False
train                False
type_check           True
use_cudnn            auto

Comme vous pouvez le voir, ʻenable_backprop et train` sont tous les deux "False". En termes de code, cette zone est applicable.

Recommended Posts

A propos du comportement de enable_backprop de Chainer v2
À propos du comportement de yield_per de SqlAlchemy
À propos de la variable du chainer
À propos du comportement de copy, deepcopy et numpy.copy
À propos du comportement de la file d'attente pendant le traitement parallèle
À propos des composants de Luigi
À propos des fonctionnalités de Python
Une note sur le comportement de bowtie2 lors de plusieurs coups
À propos de la valeur de retour de pthread_mutex_init ()
À propos de la valeur de retour de l'histogramme.
À propos du type de base de Go
À propos de la limite supérieure de threads-max
À propos de la taille des points dans matplotlib
À propos de la liste de base des bases de Python
Vérifiez le comportement du destroyer en Python
À propos de l'environnement virtuel de Python version 3.7
A propos des arguments de la fonction setup de PyCaret
À propos de l'équation normale de la régression linéaire
Je voulais faire attention au comportement des arguments par défaut de Python
À propos de la précision de la méthode de calcul du rapport de circonférence d'Archimède
À propos du test
À propos de la notation de l'axe X du graphique à barres de Matplotlib
À propos de la vitesse de traitement de SVM (SVC) de scikit-learn
Comportement de multiprocessing.pool.Pool.map
Le comportement de signal () dépend de l'option de compilation
Écrire une note sur la version python de python virtualenv
À propos du contenu de développement de l'apprentissage automatique (exemple)
[Note] À propos du rôle du trait de soulignement "_" en Python
Visualisez le comportement de l'algorithme de tri avec matplotlib
À propos de la file d'attente
Pensez à la nouvelle génération de Rack et WSGI
À propos des tests dans la mise en œuvre de modèles d'apprentissage automatique
À propos de l'inefficacité du transfert de données dans luigi on-memory
À propos de l'ordre épuré dans l'ordre d'importation flake8
Analysons les émotions de Tweet en utilisant Chainer (2ème)
Une histoire sur le changement du nom principal de BlueZ
Notes personnelles sur l'intégration de vscode et anaconda
Un mémorandum sur la mise en œuvre des recommandations en Python
Analysons les émotions de Tweet en utilisant Chainer (1er)
Le début de cif2cell
À propos de tout numpy
À propos de l'attribution de numpy.ndarray
[python] comportement d'argmax
À propos de MultiIndex of Pandas
le zen de Python
L'histoire de sys.path.append ()
À propos de la fonction Déplier
À propos de la commande de service
À propos de la matrice de confusion
À propos du modèle de visiteur
La vengeance des types: la vengeance des types
Remarque sur le comportement par défaut de collate_fn dans PyTorch
Pensez à l'environnement d'analyse (Partie 1: Vue d'ensemble) * Depuis janvier 2017
À propos de l'événement de changement de caméra de l'API Google Maps Android