Dans l'article précédent (j'ai essayé d'implémenter DeepPose avec PyTorch), j'ai comparé Chainer et PyTorch lors de l'implémentation de DeepPose. PyTorch est aussi facile à mettre en œuvre que Chainer, et en termes de performances, la prédiction est à peu près la même que Chainer, et l'apprentissage est plus rapide que Chainer. Cette fois, nous approfondirons l'aspect performance en menant l'enquête et la vérification qui n'ont pas été terminées la dernière fois.
La dernière fois, concernant le fait que la vitesse d'apprentissage de PyTorch est plus rapide que celle de Chainer, PyTorch a émis l'hypothèse que la différenciation automatique du calcul en arrière de la fonction Loss est exécutée (nativement) en C. Cette fois, j'ai changé l'implémentation en deux points pour la vérifier.
Ajout du processus pour spécifier la graine aléatoire avant de commencer l'apprentissage. Notez que l'itérateur de Chainer utilise MultiprocessIterator et qu'il était difficile de fixer des nombres aléatoires en multi-processus, donc l'augmentation des données dans l'itération est désactivée.
Chainer
def start(self):
""" Train pose net. """
+ # set random seed.
+ if self.seed is not None:
+ random.seed(self.seed)
+ np.random.seed(self.seed)
+ if self.gpu >= 0:
+ chainer.cuda.cupy.random.seed(self.seed)
# initialize model to train.
model = AlexNet(self.Nj, self.use_visibility)
PyTorch
def start(self):
""" Train pose net. """
+ # set random seed.
+ if self.seed is not None:
+ random.seed(self.seed)
+ torch.manual_seed(self.seed)
+ if self.gpu:
+ torch.cuda.manual_seed(self.seed)
# initialize model to train.
model = AlexNet(self.Nj)
Selon Extending PyTorch, dans PyTorch, la différenciation de «Module» est une différenciation automatique, et la différenciation de «Function» est une implémentation requise. Il semble. Donc, cette fois, il semble que vous devriez implémenter Function.backward
. Notez également que l'entrée pour «Module» est «Variable» et que l'entrée pour «Fonction» est «Tensor».
Notez que «Function» a une méthode pratique appelée «save_for_backward», qui vous permet d'enregistrer des variables pour «backward», mais elle ne prend pas en charge les valeurs au milieu du calcul et est utilisée pour le calcul «backward». Le résultat du calcul avant est stocké dans la variable membre.
PyTorch
def forward(self, *inputs):
x, t, v = inputs
- diff = x - t
+ self.diff = x - t
if self.use_visibility:
- N = (v.sum()/2).data[0]
- diff = diff*v
+ self.N = v.sum()/2
+ self.diff = self.diff*v
else:
- N = diff.numel()/2
- diff = diff.view(-1)
- return diff.dot(diff)/N
+ self.N = self.diff.numel()/2
+ diff = self.diff.view(-1)
+ return torch.Tensor([diff.dot(diff)/self.N])
+
+ def backward(self, *grad_outputs):
+ coeff = grad_outputs[0][0]*2/self.N
+ gx0 = coeff*self.diff
+ return gx0, None, None
Une expérience supplémentaire a été menée pour vérifier la construction provisoire mise en place la dernière fois. L'ensemble de données et l'environnement utilisés pour la vérification sont les mêmes que la dernière fois.
Afin de vérifier l'effet de la différenciation automatique de PyTorch sur la vitesse d'apprentissage en étant exécutée (nativement) en C, il est nécessaire d'entraîner la différenciation automatique et la différenciation explicite de PyTorch par 100 époques dans l'environnement CPU et l'environnement GPU, respectivement. J'ai mesuré le temps.
Dans l'environnement CPU, le temps d'apprentissage de la différenciation automatique et de la différenciation explicite de PyTorch est presque le même.
Bibliothèque | Temps requis[h] |
---|---|
PyTorch(Différenciation automatique) | 47.6 |
PyTorch(Différenciation explicite) | 47.6 |
Puisque les nombres aléatoires sont fixes, les courbes d'apprentissage de PyTorch se chevauchent presque.
Dans l'environnement GPU, le temps d'apprentissage était légèrement plus lent pour la différenciation automatique de PyTorch que pour la différenciation explicite. Il est difficile de comprendre que la mise en œuvre de Python est plus rapide, mais cela peut être dû au caractère aléatoire du GPU, et les résultats peuvent changer avec quelques essais.
Bibliothèque | Temps requis[h] |
---|---|
PyTorch(Différenciation automatique) | 2.60 |
PyTorch(Différenciation explicite) | 2.49 |
Bien que les nombres aléatoires soient fixes, la courbe d'apprentissage de PyTorch peut être décalée dans la direction de l'axe des temps en fonction de la méthode de mise en œuvre en raison du caractère aléatoire causé par le GPU.
En regardant les résultats expérimentaux ci-dessus, il semble que la construction temporaire faite la dernière fois n'est pas toujours la bonne réponse. En regardant à nouveau le code pour enquêter sur la cause, dans Chainer, l'implémentation de chaque couche telle que Convolution était Python, et dans PyTorch c'était C. Comme cela semble avoir un effet dominant sur le temps d'apprentissage, Chainer et PyTorch ont mesuré le temps total requis pour les calculs avant et arrière de la fonction de perte (y compris le réseau). Cette fois, pour une taille de lot de 2 $ ^ n $, nous avons mesuré 100 fois chacun et calculé la moyenne et la variance.
Dans l'environnement CPU, lorsque $ n = 0 $, le temps de traitement est presque le même, mais à mesure que le $ n $ augmente, PyTorch devient supérieur. Considérant que le temps d'apprentissage moyen d'une époque de Chainer et PyTorch dans l'environnement CPU est de 8,2 [sec] et 5,0 [sec], respectivement, l'hypothèse ci-dessus semble appropriée.
Dans l'environnement GPU ainsi que dans l'environnement CPU, le résultat est que PyTorch devient supérieur à mesure que $ n $ augmente. Considérant que le temps d'apprentissage moyen d'une époque de Chainer et PyTorch dans l'environnement GPU est de 0,45 [sec] et 0,28 [sec], respectivement, l'hypothèse ci-dessus semble appropriée.
L'hypothèse précédente selon laquelle «le temps d'apprentissage de PyTorch est plus rapide que celui de Chainer est que le calcul bakcward de la fonction Loss est exécuté (nativement) en C» a abouti à des réponses à moitié correctes et à moitié incorrectes. C'était. L'effet sur le temps d'apprentissage dû à la différence de méthode de mise en œuvre du calcul à rebours de la fonction Loss semble insignifiant. Il semble que l'influence dominante sur le temps d'apprentissage soit la méthode de mise en œuvre telle que la convolution nécessaire pour calculer chaque couche du réseau. Une fois que vous l'avez compris, c'est une conclusion naturelle. Cependant, bien que j'aie utilisé PyTorch 0.1.10 dans cette expérience, j'ai également essayé l'expérience avec la dernière version 0.1.12 au moment de la rédaction, et le résultat était que Chainer était plutôt plus rapide. J'ai l'impression que PyTorch est juste en cours de développement. Le code est sur github, donc je réessayerai une fois le développement terminé.
Recommended Posts