Partager via


Créer votre propre classifieur d’image à l’aide de Transfer Learning

Table des matières

Résumé

imageimageimageimage

Les images ci-dessus sont des images de test utilisées dans la deuxième partie de ce didacticiel. La tâche consiste à entraîner un classifieur qui peut distinguer différentes catégories d’images (dans notre exemple de mouton et de loup) en modifiant un modèle classifieur existant, le modèle de base. Ici, nous utilisons un modèle ResNet_18 formé sur le corpus ImageNet. Nous effectuons l’apprentissage sur seulement 15 images par classe en quelques secondes et prédisent correctement les 10 images de test (notez les quelques grains de sel).

Voici les principales ressources du didacticiel sur le transfert d’apprentissage :

Recette (recipe) TransferLearning.py et TransferLearning_Extended.py (voir Exemples/Image/TransferLearning).
modèles préentraînés En tant que modèle de base pour l’apprentissage de transfert, nous utilisons un modèle ResNet_18 préentraîné.
Données Jeu de données Fleurs avec 102 catégories et exemples d’images de moutons et de loups (voir Configuration).
Comment exécuter Suivez la description ci-dessous.

Programme d’installation

Pour exécuter le code dans cet exemple, vous avez besoin d’un environnement Python CNTK (consultez cette page pour obtenir de l’aide sur la configuration).

Pour télécharger les données requises et le modèle préentraîné, exécutez la commande suivante sous forme de dossier Examples/Image/TransferLearning :

python install_data_and_model.py

Exécuter l’exemple

imageimageimageimage

Dans cette section, nous allons créer un classifieur pour le jeu de données Fleurs. Le jeu de données a été créé par le groupe Visual Geometry de l’Université d’Oxford pour les tâches de classification d’images. Il se compose de 102 catégories différentes de fleurs communes au Royaume-Uni et contient environ 8 000 images qui sont divisées en trois ensembles d’une fois 6 000 et deux fois 1 000 images. Pour plus d’informations, consultez la page d’accueil VGG.

Pour entraîner et évaluer un modèle d’apprentissage de transfert sur l’exécution du jeu de données Fleurs

python TransferLearning.py

Le modèle atteint une précision de 93 % sur le jeu de données Fleurs après l’entraînement pour 20 époques.

Concept de base de l’apprentissage de transfert

Lorsque nous utilisons un modèle de base pour l’apprentissage du transfert, nous nous appuyons essentiellement sur les fonctionnalités et le concept qui ont été appris pendant l’entraînement du modèle de base. Pour un DNN convolutionnel, ResNet_18 dans notre cas, cela signifie, par exemple, que nous coupons la couche dense finale qui est chargée de prédire les étiquettes de classe du modèle de base d’origine et de la remplacer par une nouvelle couche dense qui prédira les étiquettes de classe de notre nouvelle tâche à la main. L’entrée de l’ancien et de la nouvelle couche de prédiction est la même, nous réutilisons simplement les fonctionnalités entraînées. Ensuite, nous effectuons l’apprentissage de ce réseau modifié, soit seulement les nouveaux poids de la nouvelle couche de prédiction, soit tous les poids de l’ensemble du réseau.

Le code suivant fait partie de TransferLearning.py ce qui crée le nouveau modèle à partir du modèle de base :

    # Load the pretrained classification net and find nodes
    base_model   = load_model(base_model_file)
    feature_node = find_by_name(base_model, feature_node_name)
    last_node    = find_by_name(base_model, last_hidden_node_name)

    # Clone the desired layers with fixed weights
    cloned_layers = combine([last_node.owner]).clone(
        CloneMethod.freeze if freeze else CloneMethod.clone,
        {feature_node: Placeholder(name='features')})

    # Add new dense layer for class prediction
    feat_norm  = input_features - Constant(114)
    cloned_out = cloned_layers(feat_norm)
    z          = Dense(num_classes, activation=None, name=new_output_node_name) (cloned_out)

Créer votre propre classifieur d’image personnalisé

Dans la section précédente, nous avons formé un classifieur qui distingue 102 catégories différentes de fleurs à l’aide d’environ 6 000 images pour l’entraînement. Dans cette section, nous n’utiliserons que 15 images par catégorie pour créer un classifieur qui peut indiquer à un loup d’un mouton. Nous utilisons le même ResNet_18 modèle de base pour le transfert d’apprentissage. Pour entraîner et évaluer l’exécution du modèle

python TransferLearning_Extended.py

Le modèle est testé sur cinq images de moutons et de loups chacun et prédit correctement toutes les étiquettes. Le fichier de sortie contient par ligne une représentation JSON des résultats de prédiction :

[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":0.997, "Wolf":0.003}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "unknown", "predictions": {"Sheep":0.994, "Wolf":0.006}, "image": "..."}]
[{"class": "unknown", "predictions": {"Sheep":0.614, "Wolf":0.386}, "image": "..."}]
[{"class": "unknown", "predictions": {"Wolf":0.980, "Sheep":0.020}, "image": "..."}]

Notez que les trois dernières images n’ont pas de classe de vérité de base affectée, ce qui est bien sûr un scénario valide, par exemple pour le scoring d’images invisibles dans un service web. Les images réelles sont les trois images d’oiseau illustrées ci-dessous. La classe de vérité de base pour celles-ci dans la sortie JSON est définie unknownsur . Notez que les prédictions pour les concepts sur lesquels le classifieur a été formé sont assez bonnes malgré les quelques images d’entraînement. Cela se trouve dans de grandes parties en raison du modèle de base préentraîné. Les prédictions pour des concepts invisibles, par exemple des images d’oiseaux, ne sont bien sûr pas très significatives, car le classifieur ne connaît que les moutons et les loups. Pour plus d’informations , plus tard.

imageimageimage

Structure de dossiers pour les jeux d’images personnalisés

Vous pouvez utiliser le TransferLearning_Extended.py script avec vos propres images. Voici ce dont vous avez besoin :

  1. class_mapping - Tableau contenant les noms de vos catégories, par exemple ['wolf', 'sheep']
  2. train_map_file - Un fichier texte qui contient par ligne d’abord une URL d’image et un onglet ont séparé l’index de catégorie correspondant, par exemple 0 pour le loup ou 1 pour les moutons :
  3. test_map_file - Fichier texte qui mappe les images de test à leur catégorie correspondante. Pour les catégories inconnues dans les images de test, utilisez -1 comme index de catégorie.

Le script peut générer les trois éléments ci-dessus si vous structurez vos images de la manière suivante :

<image root folder>
    Train
        Sheep
        Wolf
    Test
        Sheep
        Wolf
        <optional: image files with unknown label directly here>

Consultez <cntk root>/Examples/Image/DataSets/Animals/ un exemple. Chaque sous-dossier du Train dossier sera considéré comme une catégorie (niveau unique uniquement, aucune récursivité). Pour entraîner des images individuelles dans le Train dossier racine, elles sont ignorées, car elles n’ont pas de catégorie affectée. Les sous-dossiers supplémentaires du Test dossier qui ne se produisent pas dans le Train dossier sont ignorés. Pour tester des images non classées individuelles, elles sont également utilisées pour le scoring, c’est-à-dire les images stockées directement dans le Test dossier, comme les trois images d’oiseaux dans notre exemple.

Pour utiliser vos dossiers d’images personnalisés, vous devez définir train_image_folder et test_image_folder en haut du TransferLearning_Extended.py script :

# define data location and characteristics
train_image_folder = "<your_image_root_folder>/Train"
test_image_folder = "<your_image_root_folder>/Test"

Ensuite, exécutez python TransferLearning_Extended.pysimplement . L’utilisation d’un modèle de base différent est décrite ci-dessous.

Pour le scoring, vous n’avez pas besoin d’utiliser un test_map_file, par exemple si vous souhaitez noter des images uniques une par une. Chargez simplement le modèle d’apprentissage de transfert formé une seule fois, puis appelez eval_single_image chaque fois que vous souhaitez obtenir des prédictions pour une nouvelle image :

    # once:
    # load the trained transfer learning model
    trained_model = load_model(new_model_file)

    # for every new image:
    # get predictions for a single image
    probs = eval_single_image(trained_model, img_file, image_width, image_height)

Quelques grains de sel

Vos images d’entraînement doivent couvrir suffisamment les scénarios que vous souhaitez noter ultérieurement. Si le classifieur voit de nouveaux concepts ou contextes, il est probable qu’il fonctionne mal. Voici quelques exemples :

  • Vous effectuez l’apprentissage uniquement sur les images d’un environnement de contrainte (par exemple, à l’intérieur) et essayez de noter des images à partir d’un autre environnement (extérieur).
  • Vous entraînez uniquement sur des images d’une certaine marque et essayez de noter d’autres.
  • Vos images de test présentent des caractéristiques très différentes, par exemple en ce qui concerne l’éclairage, l’arrière-plan, la couleur, la taille, la position, etc.
  • Vos images de test contiennent des concepts entièrement nouveaux.

L’ajout d’une catégorie catch-all peut être une bonne idée, mais uniquement si les données d’apprentissage de cette catégorie contiennent des images suffisamment similaires aux images attendues au moment du scoring. Comme dans l’exemple ci-dessus, si nous entraînerons un classifieur avec des images de moutons et de loups et l’utilisons pour marquer une image d’un oiseau, le classifieur ne peut toujours affecter qu’une étiquette de mouton ou de loup, car il ne connaît pas d’autres catégories. Si nous devions ajouter une catégorie catch-all et ajouter des images d’entraînement des oiseaux à elle, le classifieur peut prédire la classe correctement pour l’image d’oiseau. Cependant, si nous le présentons, par exemple, une image d’une voiture, il fait face au même problème qu’avant qu’il ne connaisse que les moutons, le loup et l’oiseau (que nous venons d’appeler catch-all). Par conséquent, vos données d’entraînement, également pour catch-all, doivent couvrir suffisamment ces concepts et images que vous attendez plus tard au moment du scoring.

Un autre aspect à garder à l’esprit est qu’un modèle de base particulier peut fonctionner très bien pour certaines tâches d’apprentissage de transfert et non pas aussi bien pour d’autres. Par exemple, le modèle ci-dessus ResNet_18 a été préentraîné sur le corpus ImageNet, qui contient de nombreuses images d’animaux, de personnes, de voitures et bien d’autres objets tous les jours. L’utilisation de ce modèle de base dans l’apprentissage de transfert pour créer un classifieur pour des objets similaires tous les jours peut fonctionner correctement. L’utilisation du même modèle qu’un modèle de base pour créer un classifieur pour les images de micro-organismes ou de dessins de crayon ne peut produire que des résultats médiocres.

Utilisation d’un autre modèle de base

Pour utiliser un modèle différent en tant que modèle de base, vous devez adapter les paramètres suivants dans TransferLearning.py (identique à ) :TransferLearning_Extended.py

# define base model location and characteristics
_base_model_file = os.path.join(base_folder, "..", "..", "..", "PretrainedModels", "ResNet_18.model")
_feature_node_name = "features"
_last_hidden_node_name = "z.x"
_image_height = 224
_image_width = 224
_num_channels = 3

Pour examiner les noms des nœuds dans votre modèle et celui à choisir, car last_hidden_node vous pouvez imprimer tous les noms de nœuds et formes de nœud à l’aide des lignes suivantes (voir __main__ la méthode dans TransferLearning.py) :

    # You can use the following to inspect the base model and determine the desired node names
    node_outputs = get_node_outputs(load_model(_base_model_file))
    for out in node_outputs: print("{0} {1}".format(out.name, out.shape))