Main Content

La traduction de cette page n'est pas à jour. Cliquez ici pour voir la dernière version en anglais.

Utiliser un réseau de Deep Learning pour classer de nouvelles images

Cet exemple indique comment utiliser l’apprentissage par transfert pour entraîner à nouveau un réseau de neurones à convolution afin de classer un nouveau jeu d’images.

Des réseaux de classification d’images préentraînés ont été entraînés sur plus d’un million d'images et peuvent classer des images dans 1 000 catégories d’objets, par exemple un clavier, une tasse à café, un crayon et de nombreux animaux. Les réseaux ont appris des représentations avec de nombreuses caractéristiques pour une grande variété d’images. Le réseau utilise une image comme entrée puis produit une étiquette pour l’objet dans l’image avec les probabilités pour chaque catégorie d’objet.

L’apprentissage par transfert est communément utilisé dans les applications de Deep Learning. Vous pouvez utiliser un réseau préentraîné comme point de départ pour apprendre une nouvelle tâche. L’ajustement précis d’un réseau avec l’apprentissage par transfert est généralement beaucoup plus rapide et plus facile que l’apprentissage d’un réseau à partir de zéro avec des pondérations initialisées de manière entièrement aléatoire. Vous pouvez transférer rapidement les caractéristiques apprises vers une nouvelle tâche avec un nombre réduit d’images d’apprentissage.

Charger les données

Décompressez et chargez les nouvelles images sous la forme d'un datastore d’images. Ce très petit jeu de données contient seulement 75 images. Divisez les données en jeux de données d’apprentissage et de validation. Utilisez 70 % des images pour l’apprentissage et 30 % pour la validation.

unzip('MerchData.zip');
imds = imageDatastore('MerchData', ...
    'IncludeSubfolders',true, ...
    'LabelSource','foldernames'); 
[imdsTrain,imdsValidation] = splitEachLabel(imds,0.7);

Charger un réseau préentraîné

Chargez un réseau GoogLeNet préentraîné. Si le support package Deep Learning Toolbox™ Model for GoogLeNet Network n’est pas installé, le software vous fournit un lien de téléchargement.

Pour essayer un autre réseau préentraîné, ouvrez cet exemple dans MATLAB® et sélectionnez un réseau différent. Par exemple, vous pouvez essayer squeezenet, un réseau qui est encore plus rapide que googlenet. Vous pouvez exécuter cet exemple avec d’autres réseaux préentraînés. Pour une liste de tous les réseaux disponibles, veuillez consulter Load Pretrained Neural Networks.

net = googlenet;

Utilisez analyzeNetwork pour afficher une visualisation interactive de l’architecture du réseau et des informations détaillées sur les couches du réseau.

analyzeNetwork(net)

Le premier élément de la propriété Layers du réseau est la couche d’entrée des images. Pour un réseau GoogLeNet, cette couche nécessite des images en entrée de taille 224 x 224 x 3, où 3 est le nombre de canaux de couleur. D’autres réseaux peuvent nécessiter des images en entrée de tailles différentes. Par exemple, le réseau Xception nécessite des images de taille 299 x 299 x 3.

net.Layers(1)
ans = 
  ImageInputLayer with properties:

                      Name: 'data'
                 InputSize: [224 224 3]

   Hyperparameters
          DataAugmentation: 'none'
             Normalization: 'zerocenter'
    NormalizationDimension: 'auto'
                      Mean: [224×224×3 single]

inputSize = net.Layers(1).InputSize;

Remplacer les couches finales

Les couches de convolution du réseau extraient les caractéristiques de l’image que la dernière couche entraînable et la couche de classification finale utilisent pour classer l’image d’entrée. Ces deux couches, 'loss3-classifier' et 'output' dans GoogLeNet, contiennent des informations sur la manière de combiner les caractéristiques extraites par le réseau dans des probabilités de classe, une valeur de perte (loss) et des étiquettes prédites. Pour réentraîner un réseau préentraîné afin de classer de nouvelles images, remplacez ces deux couches par de nouvelles couches adaptées au nouveau jeu de données.

Convertir le réseau entraîné en un graphe de couches.

lgraph = layerGraph(net);

Trouvez les noms des deux couches à remplacer. Vous pouvez le faire manuellement ou vous pouvez utiliser la fonction de support findLayersToReplace pour trouver ces couches automatiquement.

[learnableLayer,classLayer] = findLayersToReplace(lgraph);
[learnableLayer,classLayer] 
ans = 
  1×2 Layer array with layers:

     1   'loss3-classifier'   Fully Connected         1000 fully connected layer
     2   'output'             Classification Output   crossentropyex with 'tench' and 999 other classes

Dans la plupart des réseaux, la dernière couche avec des poids entraînables est une couche entièrement connectée. Remplacez cette couche entièrement connectée par une nouvelle couche entièrement connectée avec un nombre de sorties égal au nombre de classes dans le nouveau jeu de données (5, dans cet exemple). Dans certains réseaux comme SqueezeNet, le dernière couche entraînable est une couche de convolution 1 x 1. Dans ce cas, remplacez la couche entièrement connectée par une nouvelle couche de convolution avec un nombre de filtres égal au nombre de classes. Pour que l’apprentissage soit plus rapide dans la nouvelle couche que dans les couches transférées, augmentez les facteurs de taux d’apprentissage de la couche.

numClasses = numel(categories(imdsTrain.Labels));

if isa(learnableLayer,'nnet.cnn.layer.FullyConnectedLayer')
    newLearnableLayer = fullyConnectedLayer(numClasses, ...
        'Name','new_fc', ...
        'WeightLearnRateFactor',10, ...
        'BiasLearnRateFactor',10);
    
elseif isa(learnableLayer,'nnet.cnn.layer.Convolution2DLayer')
    newLearnableLayer = convolution2dLayer(1,numClasses, ...
        'Name','new_conv', ...
        'WeightLearnRateFactor',10, ...
        'BiasLearnRateFactor',10);
end

lgraph = replaceLayer(lgraph,learnableLayer.Name,newLearnableLayer);

La couche de classification spécifie les classes de sortie du réseau. Remplacez la couche de classification par une nouvelle sans étiquettes de classe. trainNetwork définit automatiquement les classes de sortie de la couche au moment de l’apprentissage.

newClassLayer = classificationLayer('Name','new_classoutput');
lgraph = replaceLayer(lgraph,classLayer.Name,newClassLayer);

Pour vérifier que les nouvelles couches sont correctement connectées, tracez le graphe des nouvelles couches et zoomez sur les dernières couches du réseau.

figure('Units','normalized','Position',[0.3 0.3 0.4 0.4]);
plot(lgraph)
ylim([0,10])

Geler les couches initiales

Le réseau est maintenant prêt à être réentraîné sur le nouveau jeu d’images. Si vous le souhaitez, vous pouvez « geler » les poids des couches précédentes du réseau en définissant les taux d’apprentissage de ces couches à zéro. Pendant l’apprentissage, trainNetwork ne met pas à jour les paramètres des couches gelées. Les gradients des couches gelées n’ayant pas besoin d’être calculés, le gel des poids de nombreuses couches initiales peut accélérer de manière significative l’apprentissage du réseau. Si le nouveau jeu de données est petit, le gel des couches précédentes du réseau évite un surajustement de ces couches au nouveau jeu de données.

Extrayez les couches et connexions du graphe de couches et sélectionnez les couches à geler. Dans GoogLeNet, les 10 premières couches constituent la « tige » (stem) initiale du réseau. Utilisez la fonction de support freezeWeights pour définir les taux d’apprentissage à zéro dans les 10 premières couches. Utilisez la fonction de support createLgraphUsingConnections pour reconnecter toutes les couches dans l’ordre d’origine. Le nouveau graphe de couches contient les mêmes couches, mais avec les taux d’apprentissage des couches précédentes égaux à zéro.

layers = lgraph.Layers;
connections = lgraph.Connections;

layers(1:10) = freezeWeights(layers(1:10));
lgraph = createLgraphUsingConnections(layers,connections);

Entraîner le réseau

Le réseau nécessite des images d’entrée de taille 224 x 224 x 3, mais les images dans le datastore d’images ont des tailles différentes. Utilisez un « Augmented Image Datastore » pour redimensionner automatiquement les images d’apprentissage. Spécifiez les opérations d’augmentation supplémentaires à réaliser sur les images d’apprentissage : retournez aléatoirement les images d’apprentissage le long de l’axe vertical, effectuez aléatoirement une translation jusqu’à 30 pixels et agrandissez-les jusqu’à 10 % horizontalement et verticalement. L’augmentation des données contribue à éviter un surajustement (overfitting) du réseau et la mémorisation des détails exacts des images d’apprentissage.

pixelRange = [-30 30];
scaleRange = [0.9 1.1];
imageAugmenter = imageDataAugmenter( ...
    'RandXReflection',true, ...
    'RandXTranslation',pixelRange, ...
    'RandYTranslation',pixelRange, ...
    'RandXScale',scaleRange, ...
    'RandYScale',scaleRange);
augimdsTrain = augmentedImageDatastore(inputSize(1:2),imdsTrain, ...
    'DataAugmentation',imageAugmenter);

Pour redimensionner automatiquement les images de validation sans effectuer d’augmentation des données, utilisez un « Augmented Image Datastore » sans spécifier d’opérations de prétraitement supplémentaires.

augimdsValidation = augmentedImageDatastore(inputSize(1:2),imdsValidation);

Spécifiez les options d’apprentissage. Définissez InitialLearnRate à une faible valeur afin de ralentir l’apprentissage dans les couches transférées qui ne sont pas encore gelées. Lors de l'étape précédente, vous avez augmenté les facteurs de taux d’apprentissage pour la dernière couche entraînable afin d’accélérer l’apprentissage dans les nouvelles couches finales. Cette combinaison de réglages du taux d’apprentissage entraîne un apprentissage rapide dans les nouvelles couches, un apprentissage plus lent dans les couches intermédiaires et aucun apprentissage dans les couches gelées précédentes.

Spécifiez le nombre d’epochs pour l’apprentissage. Lors d’un apprentissage par transfert, vous n’avez pas besoin d’effectuer l’apprentissage sur autant d’epochs. Un epoch est un cycle d’apprentissage complet sur l’ensemble du jeu de données d’apprentissage. Spécifiez la taille des mini-batchs et les données de validation. Calculez la précision de la validation une fois par epoch.

miniBatchSize = 10;
valFrequency = floor(numel(augimdsTrain.Files)/miniBatchSize);
options = trainingOptions('sgdm', ...
    'MiniBatchSize',miniBatchSize, ...
    'MaxEpochs',6, ...
    'InitialLearnRate',3e-4, ...
    'Shuffle','every-epoch', ...
    'ValidationData',augimdsValidation, ...
    'ValidationFrequency',valFrequency, ...
    'Verbose',false, ...
    'Plots','training-progress');

Entraînez le réseau avec les données d’apprentissage. Par défaut et selon disponibilité trainNetwork utilise un GPU. Cela nécessite Parallel Computing Toolbox™ et un dispositif GPU supporté. Pour plus d'information sur les dispositifs supportés, veuillez consulter GPU Computing Requirements (Parallel Computing Toolbox). Sinon, trainNetwork utilise un CPU. Vous pouvez également spécifier l’environnement d’exécution en utilisant l’argument 'ExecutionEnvironment' de type nom-valeur dans trainingOptions. Le jeu de données étant petit, l’apprentissage est rapide.

net = trainNetwork(augimdsTrain,lgraph,options);

Classer les images de validation

Classez les images de validation avec le réseau ajusté et calculez la précision de la classification.

[YPred,probs] = classify(net,augimdsValidation);
accuracy = mean(YPred == imdsValidation.Labels)
accuracy = 0.9000

Affichez quatre exemples d’images de validation avec les étiquettes prédites et les probabilités associées à ces prédictions.

idx = randperm(numel(imdsValidation.Files),4);
figure
for i = 1:4
    subplot(2,2,i)
    I = readimage(imdsValidation,idx(i));
    imshow(I)
    label = YPred(idx(i));
    title(string(label) + ", " + num2str(100*max(probs(idx(i),:)),3) + "%");
end

Références

[1] Szegedy, Christian, Wei Liu, Yangqing Jia, Pierre Sermanet, Scott Reed, Dragomir Anguelov, Dumitru Erhan, Vincent Vanhoucke, and Andrew Rabinovich. "Going deeper with convolutions." In Proceedings of the IEEE conference on computer vision and pattern recognition, pp. 1-9. 2015.

Voir aussi

| | | | | | | | |

Sujets associés