[PYTHON] Classification d'images avec un réseau de neurones auto-fabriqué par Keras et PyTorch

introduction

Pratique PyTorch. Ceci est le contenu jusqu'à la dernière fois ↓ Implémentation d'une analyse de régression simple avec Keras Classification des vins par Keras Machine Sommelier by Keras- Préparation de l'ensemble de données pour PyTorch

Problème de réglage

--L'ensemble de données sera utilisé comme une image d'apprentissage / d'évaluation (vérification) en collectant et en développant une clé à crochet (62 feuilles) et une clé à molette (62 feuilles) à titre d'essai (Figure 1-a, b). C'est une classification d'outils.

fig1_a1_hook.png fig1_b1_spanner.png
fig1-a2-hook.gif fig1-b2-spanner.gif
Figure 1-a. Hook Wrench Figure 1-b. Spanner Wrench

Structure du réseau

Le NN self-made est appelé MyNet dans cet article. Il s'agit d'un réseau composé d'une couche d'entrée (28 * 28 * 3 nœuds), d'une couche intermédiaire (200 nœuds) et d'une couche de sortie (2 sorties). Cette fois, nous avons permis de considérer 3 canaux RVB. Le schéma conceptuel de la structure est la figure 2.

fig2_network.png
Figure 2.Schéma conceptuel de MyNet

Dans la couche intermédiaire, ReLU est appliqué en tant que fonction d'activation, et Dropout est également appliqué. Appliquer la fonction softmax en tant que fonction d'activation dans la couche de sortie, et sortie pour chaque classe (2) Obtenir

Un bref glossaire sur les réseaux

fig2_NN_terms.png
Figure 3.Diagramme conceptuel des termes et de l'apprentissage en machine learning

・ ** neurones, nœuds </ font> ** La partie qui reçoit un signal d'entrée et produit quelque chose. Comme le montre la figure 3, la partie arrondie est appelée un neurone (nœud), et elle convertit une fonction en un signal d'entrée et délivre un signal de sortie.

・ ** Fonction d'activation </ font> **: ReLU, softmax Une fonction qui transforme chaque neurone (nœud) lorsqu'il reçoit une sortie d'une entrée. Quelque chose comme $ f_ {()} $ montré dans la figure.

Cliquez ici pour une description de la fonction d'activation Sur la figure, la fonction softmax est illustrée à titre d'exemple. La fonction softmax est utilisée dans la couche finale, et la somme des sorties correspondant à chaque classe est 1 (elle peut être considérée comme une probabilité de classe). La fonction d'activation est utilisée pour imiter le phénomène que les synapses cérébrales déclenchent lorsqu'elles dépassent un certain seuil. En faisant de la fonction d'activation une fonction non linéaire, la précision de reconnaissance dans la reconnaissance d'image s'est considérablement améliorée. En plus de Softmax, il y a ReLU, Sigmaid, etc., qui doivent être utilisés correctement en fonction de la situation, mais de nouveaux apparaissent les uns après les autres. * Non-linéaire signifie qu'elle ne peut pas être écrite avec une seule ligne droite, et une fonction linéaire est une fonction qui peut être écrite avec une seule ligne droite.
fig4_a.png fig4_b.png fig4_c.png
Figure 4-a.Fonction Softmax Figure 4-b. ReLU Figure 4-c.Fonction Sigmaid

・ ** Fonction de perte </ font> **: categorical_crossentropy La valeur de perte est l'erreur entre la valeur prédite par le réseau neuronal et la bonne réponse, et la fonction pour trouver l'erreur est la fonction de perte. Comme le montre la figure, c'est une fonction qui calcule l'erreur à partir de la sortie du modèle et de l'étiquette de réponse correcte.

Cliquez ici pour une description de la fonction de perte Sur la figure, l'entropie croisée est présentée à titre d'exemple. L'entropie croisée est utilisée dans les tâches de classification. La formule de l'entropie croisée est la suivante. $$E=-\sum_{k=1}^{K} t_{n k} \log y_{n k}$$ $ n $ est le numéro de l'échantillon, $ K $ est le nombre de classes, $ y_ {nk} $ est la sortie de la classe d'échantillon $ n $ $ k $, $ t_ {nk} $ est la classe d'échantillon $ n $ $ k $ Étiquette de réponse correcte

・ ** Fonction d'optimisation </ font> **: SGD La fonction d'optimisation est une fonction qui modifie le poids de sorte que la valeur de la fonction de perte diminue. Comme le montre la figure, calculez le gradient à partir de l'erreur et du poids et ajustez le poids.

Cliquez ici pour une description de la fonction d'optimisation Un algorithme d'optimisation est plus approprié qu'une fonction, et SGD est présenté à titre d'exemple dans la figure. SGD (Probabilistic Gradient Descent Method) met à jour à plusieurs reprises les poids petit à petit dans l'apprentissage par mini-lots. La figure ci-dessous est une animation conceptuelle de la mise à jour du poids de SGD, et elle est mise à jour comme $ w ← w ± εΔE $. De plus, il existe des fonctions d'optimisation telles que Adam et RMSprop. ![SGD.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282390/657e4792-2b07-2462-deab-c0bc7cb500ee.gif)

・ ** Keras ** Une bibliothèque de réseau neuronal de haut niveau écrite en Python qui peut être exécutée sur TensorFlow, CNTK ou Theano.

・ ** PyTorch ** It’s a Python-based scientific computing package targeted at two sets of audiences: ・ Un remplacement pour NumPy pour utiliser la puissance des GPU ・ Une plate-forme de recherche d'apprentissage en profondeur qui offre une flexibilité et une vitesse maximales

Implémentation dans Keras

Voir GitHub pour le programme complet. Ce qui suit est une extraction de la partie MyNet.

Implémenté dans Keras
# Build a model
from keras.applications.mobilenet import MobileNet
from keras.applications.resnet50 import ResNet50
from keras.layers.pooling import GlobalAveragePooling2D
from keras.layers.core import Dense, Dropout, Flatten
from keras.models import Model, load_model, Sequential
from keras.optimizers import Adam, RMSprop, SGD

base_model = Sequential()
top_model = Sequential()

INPUT_SHAPE = (img_size[0], img_size[1], 3)
neuron_total = 500

elif type_backbone == "MyNet":
    INPUT_SHAPE = (img_size[0], img_size[1], 3)
    base_model.add(Dense(neuron_total, activation='relu',
                         input_shape=(INPUT_SHAPE[0]*INPUT_SHAPE[1]*INPUT_SHAPE[2],)))
    base_model.add(Dropout(0.5))
    top_model.add(Dense(nb_classes, activation='softmax', 
                  input_shape=base_model.output_shape[1:]))

# Concatenate base_model(backbone) with top model
model = Model(input=base_model.input, output=top_model(base_model.output))

print("{}couche".format(len(model.layers)))

# Compile the model
model.compile(
    optimizer = SGD(lr=0.001),
    loss = 'categorical_crossentropy',
    metrics = ["accuracy"]
)

model.summary()

Implémentation dans PyTorch

Voir GitHub pour le programme complet. Ce qui suit est une extraction de la partie MyNet.

<détails>

Implémentation PyTorch </ font> </ summary>

from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
import torchvision.models
from torchsummary import summary

neuron_total = 200
INPUT_SHAPE = (img_size[0], img_size[1], 3)
print(INPUT_SHAPE)
print(nb_classes)

# Create my model
class MyNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.l1 = nn.Linear(INPUT_SHAPE[0]*INPUT_SHAPE[1]*INPUT_SHAPE[2], neuron_total)# Input Layer to Intermediate modules
        self.dropout1 = torch.nn.Dropout2d(p=0.5)
        self.l2 = nn.Linear(neuron_total, 2) #Intermediate modules to Output Layer

    def forward(self, x):#Propagation vers l'avant
        x = x.view(-1, INPUT_SHAPE[0]*INPUT_SHAPE[1]*INPUT_SHAPE[2] ) # x.view : Transform a tensor shape. If the first argument is "-1", automatically adjust to the second argument.
        x = self.l1(x)
        x = self.dropout1(x)
        x = self.l2(x)
        return x

if type_backbone == "ResNet50":
    model = Resnet()
elif type_backbone == "Mobilenet":
    model = Mobilenet()
elif type_backbone == "MyNet":
    model = MyNet()
model = model.to(device)
# Show the model 
summary(model, ( 3, img_size[1], img_size[0]))#channel, w, h

Comparaison des deux en programmation

Comparaison de la préparation des ensembles de données

Premièrement, comme mentionné dans Preparing a dataset for PyTorch, il est dit que Keras utilise le format numpy et PyTorch utilise les formats DataLoader et tensor. Le point est différent.

Comparaison de la construction du modèle

Ensuite, en ce qui concerne la façon de créer un modèle, Keras correspondra automatiquement à la forme lors de la connexion de couches avec Dense etc., mais PyTorch doit le clarifier. Par exemple, si vous ajoutez des couches intermédiaires dans la figure 2.

base_model = Sequential()
top_model = Sequential()
INPUT_SHAPE = (img_size[0], img_size[1], 3)
base_model.add(Dense(neuron_total, activation='relu',
                         input_shape=(INPUT_SHAPE[0]*INPUT_SHAPE[1]*INPUT_SHAPE[2],)))
base_model.add(Dense(neuron_total, activation='relu'))
base_model.add(Dense(neuron_total, activation='relu'))
base_model.add(Dropout(0.5))
top_model.add(Dense(nb_classes, activation='softmax', 
                  input_shape=base_model.output_shape[1:]))

# Concatenate base_model(backbone) with top model
model = Model(input=base_model.input, output=top_model(base_model.output))

Dans PyTorch

class MyNet2(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(INPUT_SHAPE[0]*INPUT_SHAPE[1]*INPUT_SHAPE[2], neuron_total)# Input Layer to Intermediate modules
        self.fc2 = nn.Linear(neuron_total, int(neuron_total/2)) #Intermediate modules to Output Layer
        self.dropout1 = torch.nn.Dropout2d(p=0.5)
        self.fc3 = nn.Linear(int(neuron_total/2), 2)

    def forward(self, x):#Propagation vers l'avant
        x = x.view(-1, INPUT_SHAPE[0]*INPUT_SHAPE[1]*INPUT_SHAPE[2] ) # x.view : Transform a tensor shape. If the first argument is "-1", automatically adjust to the second argument.
        x = self.fc1(x)
        x = self.fc2(x)
        x = F.relu(x)
        x = self.dropout1(x)
        x = self.fc3(x)
        return x

Dans PyTorch, le nombre de nœuds est spécifié pour l'entrée et la sortie.

Comparaison des abandons

J'ai peur de ne pas en savoir grand-chose, mais Keras ne devrait pas avoir à basculer l'application Dropout entre l'apprentissage et l'évaluation. Dans PyTorch, Dropout est désactivé par model.eval (), donc lors du chargement de l'image de test, il est clairement indiqué qu'elle n'est pas en mode d'apprentissage.

param = torch.load(weights_folder_path + "/" + best_weights_path)
model.load_state_dict(param, strict=False)
model.eval()
# ~ Inference

Comparaison de model_summary (nombre de paramètres)

Comme vous pouvez le voir, le nombre de paramètres correspondait exactement.

fig_modelsummary.png
Figure 5.Résumé de Keras par modèle(la gauche)Et PyTorch(droite)Comparaison

Comparaison de l'utilisation du GPU

C'est une petite histoire, mais dans Keras, vous n'avez pas besoin de changer la description lors de l'utilisation du GPU, mais dans le cas de PyTorch

#image, label = Variable(image), Variable(label)
image, label = Variable(image).cuda(), Variable(label).cuda()

Il doit être réécrit comme.

Comparaison de la boucle d'apprentissage

Dans Keras, en écrivant comme model.fit, la boucle d'évaluation d'apprentissage est répétée pour le nombre d'époques. Dans PyTorch, répétez pour le nombre d'époques comme suit dans une boucle for.

def train(epoch):
    #~Abréviation
def validation():
    #~Abréviation
for epoch in range(1, total_epochs + 1):
    train(epoch)
    validation()

Comparaison de sortie

De plus, PyTorch utilise log_softmax par défaut, donc la probabilité de classe totale n'est pas 1 (spécifiez softmax ou convertissez-la vous-même).

Comparaison des deux pendant l'apprentissage

Tout d'abord, lorsque j'ai vérifié l'état de fonctionnement du PC avec le gestionnaire de tâches, il y avait les différences suivantes.

fig4_tm.png
Figure 6. Kera(la gauche)Et PyTorch(droite)Performance du gestionnaire de tâches lors de chaque apprentissage (par 10 époques)

L'utilisation de la mémoire était faible du côté PyTorch. Puisque Keras contient des ensembles de données dans des listes et des tableaux numpy (dans ce programme), il consomme inévitablement de la mémoire. L'utilisation du GPU était également faible du côté de PyTorch.

Ensuite, comparez la vitesse d'exécution de l'apprentissage de chaque réseau de Keras et PyTorch. Le tableau ci-dessous résume le [s] temps requis pour 40 époques lors de la formation à l'aide d'un réseau.

Keras PyTorch
ResNet 3520 s 3640 s
Mobilenet 1600 s 1760 s
MyNet 40 s 680 s

Keras définit verbose = 1 dans model.fit, donc je regarde la valeur des secondes qui a été sortie sans autorisation. Il est précis de calculer à partir du temps par pas, mais c'est ennuyeux, c'est donc une valeur approximative. D'après le tableau ci-dessus, PyTorch est légèrement plus lent (environ 3 secondes plus lent en 1 époque). Surtout MyNet est assez lent. Cependant, PyTorch est plus économe en énergie (?). Je voulais que PyTorch soit plus rapide, mais j'ai l'impression que le code est mauvais. Je pense que PyTorch est meilleur pour économiser de l'énergie à presque la même vitesse.

Comparaison des résultats

Résultats d'inférence de ResNet, Mobilenet, MyNet à Keras

Les résultats estimés de la perte, de la précision et des images de test des résultats entraînés sont résumés ci-dessous. La courbe d'apprentissage est terrible, mais les résultats sont raisonnablement raisonnables.

fig5_Keras_l_a.png
Figure 7.Perte et précision (Keras) pour les époques d'apprentissage
fig6_a.png
Figure 8-a.Résultat estimé par ResNet50(Keras)
fig6_b.png
Figure 8-b.Devinez les résultats par Mobilenet v1(Keras)
fig6_c.png
Figure 8-c.Devinez les résultats par MyNet(Keras)

ResNet, Mobilenet, MyNet devinent les résultats dans PyTorch

Les résultats estimés de la perte, de la précision et des images de test des résultats entraînés sont résumés ci-dessous. Similaire à Keras, le résultat est donc affiché dans le pli.

** Cliquez ici pour un résumé des résultats des hypothèses d'apprentissage avec PyTorch **
fig9_l_a.png
Figure 9.Perte et précision (PyTorch) pour les époques d'apprentissage
fig10_a.png
Figure 10-a.Résultat estimé par ResNet50 (PyTorch)
fig10_b.png
Figure 10-b.Deviner le résultat par Mobilenet v1 (PyTorch)
fig10_c.png
Figure 10-c.Devinez les résultats par MyNet (PyTorch)

Basé sur les résultats de Keras et PyTorch

Les deux ont tendance à être les mêmes (parce que j'ai essayé d'apprendre presque la même chose).

Keras et PyTorch peuvent être classés par ResNet et Mobilenet, mais pas par le niveau MNIST MyNet. Cependant, il semble que l'apprentissage ne se passe pas bien avec ResNet et Mobilenet pour voir comment la perte diminue. Cette fois, l'image de test est similaire aux données d'entraînement, donc je pense que c'était la bonne réponse. Dans le cas de problèmes de classification aussi similaires que la clé à ergot et la clé à molette, il semble que le nombre de données soit faible avec environ 60 feuilles. De plus, j'estime que même si toutes les données sont disponibles, elles ne peuvent pas être classées.

À propos, le résultat de la formation du nœud de couche intermédiaire avec 500 et le nombre d'apprentissage avec 100 epoch dans MyNet est le suivant.

fig_mynet100.png
Figure 11.Le résultat de la formation du nœud de couche intermédiaire avec 500 et le nombre d'apprentissage avec 100 epoch avec MyNet

La valeur de perte de la validation ne diminuera pas. C'est peut-être un problème qui ne peut pas être catégorisé uniquement par un réseau neuronal non profond. Il est nécessaire de déterminer s'il faut augmenter le nombre de couches ou utiliser CNN (réseau de neurones convolutifs).

prime

Nous classerons les logos des deux sociétés pour le logo du fabricant apparaissant dans le précédent sommelier de la machine-outil. Il existe des différences de forme, mais peut-il être classé comme un réseau neuronal? Je vais essayer ceci sur MyNet. Pour l'apprentissage et l'évaluation, j'ai utilisé le logo Makino Milling Co., Ltd. et le logo Okuma collectés en ligne, et pour le test, j'ai utilisé mon propre logo manuscrit. Celui que j'ai écrit moi-même.

makino_logo_test1.png Okuma_logo_test1.png
Figure 12-a.Logo Makino Milling manuscrit Figure 12-b.Logo Okuma peint à la main

La transition de la perte et de la précision est la suivante.

graph_loss.png graph_acc.png
Figure 13-a.Transition de la perte par rapport à l'époque Figure 13-b.Transition de la précision par rapport à l'époque

Vous apprenez peut-être mieux que d'apprendre la clé à crochet et la clé à molette. Je suppose que cela ressemble à ceci:

2figure.png 4figure.png
Figure 14-a.Résultat de l'estimation du logo Makino Milling Figure 14-b.Résultat de l'estimation du logo Okuma

Ce résultat est très bien catégorisé. Il semble qu'il soit possible de classer même un réseau de neurones qui n'est pas profond s'il y a une différence de forme comme un logo.

Résumé

  • La clé à crochet et la clé à molette ne peuvent pas être classées par un simple réseau neuronal --S'il s'agit d'un logo d'entreprise, il peut être classé même s'il n'est pas profond

Environnement d'exécution Environnement

  • Windows10
  • CPU:Core i7-7700HQ
  • Memory: 16GB
  • Graphic board: GTX1060 6GB
  • Strage: NVMe M.2 SSD 1TB
  • CUDA 9.0.176
  • cuDNN 7.0.5
  • Si vous n'avez pas installé CUDA ou cuDNN, vous devez créer un environnement.
  • Keras==2.1.5
  • tensorflow-gpu==1.11.0
  • torch==1.1.0
  • scikit-learn==0.19.1
  • scipy==1.4.1

référence

https://keras.io/ja/ https://pytorch.org/tutorials/beginner/blitz/tensor_tutorial.html https://qiita.com/sheep96/items/0c2c8216d566f58882aa https://rightcode.co.jp/blog/information-technology/pytorch-mnist-learning https://water2litter.net/rum/post/pytorch_tutorial_classifier/ https://qiita.com/jyori112/items/aad5703c1537c0139edb https://pystyle.info/pytorch-cnn-based-classification-model-with-fashion-mnist/ https://pytorch.org/docs/stable/torchvision/models.html https://qiita.com/perrying/items/857df46bb6cdc3047bd8 https://qiita.com/sakaia/items/5e8375d82db197222669 https://discuss.pytorch.org/t/low-accuracy-when-loading-the-model-and-testing/44991/5

Recommended Posts