[PYTHON] Tutoriel TensorFlow - Reconnaissance d'image (traduction)

Tutoriel TensorFlow (reconnaissance d'image) https://www.tensorflow.org/versions/master/tutorials/image_recognition C'est une traduction de. Nous sommes impatients de signaler toute erreur de traduction.


Notre cerveau semble faciliter la vision. Il n'est pas nécessaire de faire un effort pour distinguer entre un lion et un jaguar, lire un signe et reconnaître un visage humain. Cependant, en réalité, ce sont des problèmes difficiles à résoudre sur un ordinateur. Cela semble facile, simplement parce que notre cerveau comprend très bien les images.

Au cours des dernières années, le domaine de l'apprentissage automatique a fait d'énormes progrès pour résoudre ces problèmes difficiles. En particulier, un type de modèle appelé Deep Convolution Neural Network a des performances décentes dans les tâches de reconnaissance visuelle difficiles. Il s'avère que dans le royaume, cela peut être aussi bon ou meilleur que les humains.

Les chercheurs ont fait des progrès constants en vision par ordinateur en vérifiant leurs réalisations avec ImageNet (référence académique pour la vision par ordinateur). J'ai fait. QuocNet, [AlexNet](http://www.cs.toronto.edu/%7Efritz /absps/imagenet.pdf), Inception (GoogLeNet), BN-Inception-v2, etc. Les modèles qui apparaissent les uns après les autres continuent de montrer des améliorations et d'atteindre des résultats de pointe à chaque étape. Des chercheurs internes et externes à Google ont publié des articles décrivant ces modèles, mais il est encore difficile de reproduire ces résultats. Nous passons à l'étape suivante en publiant le code qui effectue la reconnaissance d'image sur notre dernier modèle, Inception-v3 (http://arxiv.org/abs/1512.00567).

Inception-v3 a été formé avec des données depuis 2012 pour le défi de reconnaissance visuelle à grande échelle ImageNet. C'est une tâche standard en vision par ordinateur, où le modèle rend l'image entière "classe 1000" (http://image-net.org/, comme "simauma", "dalmesian", "lave-vaisselle". Tentatives de classification en défis / LSVRC / 2014 / blowse-synsets). Par exemple, AlexNet classe certaines images comme suit:

図

Pour comparer les modèles, regardez à quelle fréquence les 5 premiers prédits par le modèle n'avaient pas la bonne réponse (appelé «taux d'erreur des 5 premiers»). AlexNet a atteint un taux d'erreur de 15,3% par rapport à l'ensemble de données de validation 2012, BN-Inception-v2 a atteint 6,66% et Inception-v3 a atteint 3,46%.

Dans quelle mesure une personne peut-elle réussir le défi ImageNet? [Article de blog] d'Andrej Karpathy qui a tenté de mesurer sa propre performance (http://karpathy.github.io/2014/09/02/what-i-learned-from-competing-against-a-convnet-on- Il y a imagenet /). Il avait un taux d'erreur des 5 premiers de 5,1%.

Ce didacticiel vous apprendra à utiliser Inception-v3. Apprenez à classer des images en 1000 classes en Python ou C ++. Il décrit également comment extraire des fonctionnalités de niveau supérieur de ce modèle qui peuvent être réutilisées pour d'autres tâches de vision.

Nous sommes ravis d'imaginer que la communauté est ce modèle.

Utilisation dans l'API Python

La première fois que vous exécutez le programme classify_image.py, le modèle entraîné sera téléchargé à partir de tensorflow.org. Vous avez besoin d'environ 200 Mo d'espace libre disponible sur votre disque dur.

Les étapes suivantes supposent que vous avez installé TensorFlow à partir du package PIP et que votre terminal se trouve dans le répertoire racine de TensorFlow.

cd tensorflow/models/image/imagenet
python classify_image.py

La commande ci-dessus classe l'image d'un panda donné.

図

Si le modèle s'exécute correctement, le script produira une sortie similaire à la suivante:

giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca (score = 0.88493)
indri, indris, Indri indri, Indri brevicaudatus (score = 0.00878)
lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens (score = 0.00317)
custard apple (score = 0.00149)
earthstar (score = 0.00127)

Vous pouvez donner d'autres images JPEG en éditant l'argument --image_file.

Si vous téléchargez les données du modèle dans un autre répertoire, vous devez spécifier le répertoire à utiliser pour --model_dir.

Utilisation dans l'API C ++

Le même modèle Inception-v3 peut être exécuté en C ++ pour les environnements de production. Vous pouvez télécharger une archive contenant GraphDef qui définit un tel modèle (en exécutant à partir du répertoire racine du référentiel TensorFlow):

wget https://storage.googleapis.com/download.tensorflow.org/models/inception_dec_2015.zip -O tensorflow/examples/label_image/data/inception_dec_2015.zip

unzip tensorflow/examples/label_image/data/inception_dec_2015.zip -d tensorflow/examples/label_image/data/

Ensuite, vous devez compiler un binaire C ++ contenant le code qui charge et exécute le graphique. Si TensorFlow est installé à partir des sources sur votre plate-forme (http://www.tensorflow.org/versions/master/get_started/os_setup.html#source), exécutez la commande suivante à partir de votre terminal shell: Vous devriez être en mesure d'exécuter et de créer l'exemple:

bazel build tensorflow/examples/label_image/...

Un exécutable binaire devrait être créé et vous devriez pouvoir l'exécuter comme ceci:

bazel-bin/tensorflow/examples/label_image/label_image

L'exemple d'image par défaut inclus dans le framework doit être utilisé et la sortie doit ressembler à ceci:

I tensorflow/examples/label_image/main.cc:200] military uniform (866): 0.647296
I tensorflow/examples/label_image/main.cc:200] suit (794): 0.0477196
I tensorflow/examples/label_image/main.cc:200] academic gown (896): 0.0232411
I tensorflow/examples/label_image/main.cc:200] bow tie (817): 0.0157356
I tensorflow/examples/label_image/main.cc:200] bolo tie (940): 0.0145024

Ici, en utilisant l'image par défaut Admiral Grace Hopper, le réseau l'identifie correctement en tenue militaire avec un score élevé de 0,6. Vous pouvez voir que c'est fait.

図

Essayez ensuite avec votre propre image en donnant l'argument --image =, par exemple

bazel-bin/tensorflow/examples/label_image/label_image --image=my_image.png

tensorflow / examples / label_image / main.cc Si vous regardez le contenu du fichier, à quoi il ressemble Vous pouvez voir si cela fonctionne. Examinons de plus près la fonction principale afin que ce code puisse aider à intégrer TensorFlow dans votre propre application:

Les indicateurs de ligne de commande contrôlent la source du fichier et les propriétés de l'image d'entrée. Le modèle prend une image RVB carrée de 299x299 en entrée, alors définissez-les sur les indicateurs input_width et input_height. Vous devez également mettre à l'échelle les valeurs de pixel des nombres entiers compris entre 0 et 255 aux valeurs à virgule flottante auxquelles le graphique fonctionne. Contrôlez la mise à l'échelle avec les indicateurs input_mean et input_std: soustrayez d'abord input_mean de chaque valeur de pixel, puis divisez par input_std.

Ces valeurs, qui peuvent sembler un peu magiques, sont basées sur ce que le modeleur d'origine a utilisé comme image d'entrée d'entraînement. Si vous utilisez un graphique que vous avez vous-même formé, vous devrez ajuster les valeurs pour qu'elles correspondent à celles que vous avez utilisées pendant le processus de formation.

Dans la fonction ReadTensorFromImageFile () (https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/label_image/main.cc#L88), vous pouvez voir comment les appliquer à l'image.

// Given an image file name, read in the data, try to decode it as an image,
// resize it to the requested size, and then scale the values as desired.
Status ReadTensorFromImageFile(string file_name, const int input_height,
                               const int input_width, const float input_mean,
                               const float input_std,
                               std::vector<Tensor>* out_tensors) {
  tensorflow::GraphDefBuilder b;

Commencez par générer un GraphDefBuilder. Il s'agit de l'objet utilisé pour spécifier le modèle à exécuter ou à charger.

  string input_name = "file_reader";
  string output_name = "normalized";
  tensorflow::Node* file_reader =
      tensorflow::ops::ReadFile(tensorflow::ops::Const(file_name, b.opts()),
                                b.opts().WithName(input_name));

Générez ensuite un petit nœud de modèle. Ce nœud charge, redimensionne et met à l'échelle les valeurs de pixel pour obtenir les résultats attendus par le modèle principal en entrée. Le premier nœud que vous créez est une opération Const qui contient simplement un tenseur avec le nom de fichier de l'image que vous souhaitez charger. Il est ensuite passé comme première entrée pour l'opération ReadFile. Avez-vous remarqué que nous passons b.opts () comme dernier argument de chaque fonction de création d'opération? Cet argument garantit que le nœud sera ajouté à la définition de modèle détenue par GraphDefBuilder. Nommez également l'opération ReadFile en appelant WithName () après b.opts (). Cela donne un nom au nœud. Si vous ne donnez pas de nom au nœud, un nom lui sera attribué automatiquement, donc ce n'est pas strictement obligatoire, mais cela facilite un peu le débogage.

  // Now try to figure out what kind of file it is and decode it.
  const int wanted_channels = 3;
  tensorflow::Node* image_reader;
  if (tensorflow::StringPiece(file_name).ends_with(".png ")) {
    image_reader = tensorflow::ops::DecodePng(
        file_reader,
        b.opts().WithAttr("channels", wanted_channels).WithName("png_reader"));
  } else {
    // Assume if it's not a PNG then it must be a JPEG.
    image_reader = tensorflow::ops::DecodeJpeg(
        file_reader,
        b.opts().WithAttr("channels", wanted_channels).WithName("jpeg_reader"));
  }
  // Now cast the image data to float so we can do normal math on it.
  tensorflow::Node* float_caster = tensorflow::ops::Cast(
      image_reader, tensorflow::DT_FLOAT, b.opts().WithName("float_caster"));
  // The convention for image ops in TensorFlow is that all images are expected
  // to be in batches, so that they're four-dimensional arrays with indices of
  // [batch, height, width, channel]. Because we only have a single image, we
  // have to add a batch dimension of 1 to the start with ExpandDims().
  tensorflow::Node* dims_expander = tensorflow::ops::ExpandDims(
      float_caster, tensorflow::ops::Const(0, b.opts()), b.opts());
  // Bilinearly resize the image to fit the required dimensions.
  tensorflow::Node* resized = tensorflow::ops::ResizeBilinear(
      dims_expander, tensorflow::ops::Const({input_height, input_width},
                                            b.opts().WithName("size")),
      b.opts());
  // Subtract the mean and divide by the scale.
  tensorflow::ops::Div(
      tensorflow::ops::Sub(
          resized, tensorflow::ops::Const({input_mean}, b.opts()), b.opts()),
      tensorflow::ops::Const({input_std}, b.opts()),
      b.opts().WithName(output_name));

Continuez à ajouter des nœuds. Ces nœuds décodent les données du fichier en images, transtypent les entiers en nombres à virgule flottante, les redimensionnent et enfin exécutent des opérations de soustraction et de division de valeur de pixel.

  // This runs the GraphDef network definition that we've just constructed, and
  // returns the results in the output tensor.
  tensorflow::GraphDef graph;
  TF_RETURN_IF_ERROR(b.ToGraphDef(&graph));

À la fin de la section précédente, vous obtiendrez la définition du modèle stockée dans la variable b. La fonction ToGraphDef () transforme cela en une définition de graphe complète.

  std::unique_ptr<tensorflow::Session> session(
      tensorflow::NewSession(tensorflow::SessionOptions()));
  TF_RETURN_IF_ERROR(session->Create(graph));
  TF_RETURN_IF_ERROR(session->Run({}, {output_name}, {}, out_tensors));
  return Status::OK();

Après cela, créez un objet Session (une interface pour exécuter réellement le graphique) et créez-le. Spécifiez à partir de quel nœud vous souhaitez obtenir la sortie et où placer les données de sortie, puis exécutez.

Cela renvoie un vecteur d'objets Tensor. Il s'agit simplement d'un seul objet que nous connaissons depuis longtemps dans ce cas. Dans ce contexte, vous pouvez considérer Tensor comme un tableau multidimensionnel. Il contient une image à 3 canaux de 299 pixels de haut et 299 pixels de large en tant que valeur à virgule flottante. Si votre produit utilise son propre cadre de traitement d'image, vous devriez pouvoir l'utiliser à la place avant d'alimenter l'image dans le graphique principal, tant que vous appliquez les mêmes transformations.

Il s'agit d'un exemple simple de création dynamique d'un petit graphe TensorFlow en C ++, mais il charge une définition beaucoup plus grande à partir du fichier pour utiliser le modèle Inception pré-entraîné. Vous pouvez voir comment faire cela avec la fonction LoadGraph ().

// Reads a model graph definition from disk, and creates a session object you
// can use to run it.
Status LoadGraph(string graph_file_name,
                 std::unique_ptr<tensorflow::Session>* session) {
  tensorflow::GraphDef graph_def;
  Status load_graph_status =
      ReadBinaryProto(tensorflow::Env::Default(), graph_file_name, &graph_def);
  if (!load_graph_status.ok()) {
    return tensorflow::errors::NotFound("Failed to load compute graph at '",
                                        graph_file_name, "'");
  }

En regardant le code de chargement d'image, de nombreux termes devraient vous sembler familiers. Au lieu d'utiliser GraphDefBuilder pour générer l'objet GraphDef, chargez directement le fichier protobuf qui contient GraphDef.

  session->reset(tensorflow::NewSession(tensorflow::SessionOptions()));
  Status session_create_status = (*session)->Create(graph_def);
  if (!session_create_status.ok()) {
    return session_create_status;
  }
  return Status::OK();
}

Il crée ensuite un objet Session à partir de ce GraphDef et le transmet à l'appelant pour une exécution ultérieure.

La fonction GetTopLabels () est en grande partie similaire au chargement d'image, mais elle prend le résultat de l'exécution du graphique principal et le convertit en une liste triée d'étiquettes avec les scores les plus élevés. Comme un chargeur d'image, il crée un GraphDefBuilder, y ajoute deux nœuds et exécute un court graphique pour obtenir une paire de tenseurs de sortie. La paire de tenseur de sortie représente le score trié et la meilleure position d'index.

// Analyzes the output of the Inception graph to retrieve the highest scores and
// their positions in the tensor, which correspond to categories.
Status GetTopLabels(const std::vector<Tensor>& outputs, int how_many_labels,
                    Tensor* indices, Tensor* scores) {
  tensorflow::GraphDefBuilder b;
  string output_name = "top_k";
  tensorflow::ops::TopK(tensorflow::ops::Const(outputs[0], b.opts()),
                        how_many_labels, b.opts().WithName(output_name));
  // This runs the GraphDef network definition that we've just constructed, and
  // returns the results in the output tensors.
  tensorflow::GraphDef graph;
  TF_RETURN_IF_ERROR(b.ToGraphDef(&graph));
  std::unique_ptr<tensorflow::Session> session(
      tensorflow::NewSession(tensorflow::SessionOptions()));
  TF_RETURN_IF_ERROR(session->Create(graph));
  // The TopK node returns two outputs, the scores and their original indices,
  // so we have to append :0 and :1 to specify them both.
  std::vector<Tensor> out_tensors;
  TF_RETURN_IF_ERROR(session->Run({}, {output_name + ":0", output_name + ":1"},
                                  {}, &out_tensors));
  *scores = out_tensors[0];
  *indices = out_tensors[1];
  return Status::OK();

La fonction PrintTopLabels () prend ces résultats de tri et les affiche d'une manière facile à lire. La fonction CheckTopLabel () est très similaire, mais à des fins de débogage, assurez-vous simplement que l'étiquette supérieure correspond à ce que vous attendez.

Enfin, main () lie tous ces appels ensemble.

int main(int argc, char* argv[]) {
  // We need to call this to set up global state for TensorFlow.
  tensorflow::port::InitMain(argv[0], &argc, &argv);
  Status s = tensorflow::ParseCommandLineFlags(&argc, argv);
  if (!s.ok()) {
    LOG(ERROR) << "Error parsing command line flags: " << s.ToString();
    return -1;
  }

  // First we load and initialize the model.
  std::unique_ptr<tensorflow::Session> session;
  string graph_path = tensorflow::io::JoinPath(FLAGS_root_dir, FLAGS_graph);
  Status load_graph_status = LoadGraph(graph_path, &session);
  if (!load_graph_status.ok()) {
    LOG(ERROR) << load_graph_status;
    return -1;
  }

Chargez le graphique principal.

  // Get the image from disk as a float array of numbers, resized and normalized
  // to the specifications the main graph expects.
  std::vector<Tensor> resized_tensors;
  string image_path = tensorflow::io::JoinPath(FLAGS_root_dir, FLAGS_image);
  Status read_tensor_status = ReadTensorFromImageFile(
      image_path, FLAGS_input_height, FLAGS_input_width, FLAGS_input_mean,
      FLAGS_input_std, &resized_tensors);
  if (!read_tensor_status.ok()) {
    LOG(ERROR) << read_tensor_status;
    return -1;
  }
  const Tensor& resized_tensor = resized_tensors[0];

Chargez, redimensionnez et traitez l'image d'entrée.

  // Actually run the image through the model.
  std::vector<Tensor> outputs;
  Status run_status = session->Run({{FLAGS_input_layer, resized_tensor}},
                                   {FLAGS_output_layer}, {}, &outputs);
  if (!run_status.ok()) {
    LOG(ERROR) << "Running model failed: " << run_status;
    return -1;
  }

Ici, nous exécutons le graphique chargé, en utilisant une image comme entrée.

  // This is for automated testing to make sure we get the expected result with
  // the default settings. We know that label 866 (military uniform) should be
  // the top label for the Admiral Hopper image.
  if (FLAGS_self_test) {
    bool expected_matches;
    Status check_status = CheckTopLabel(outputs, 866, &expected_matches);
    if (!check_status.ok()) {
      LOG(ERROR) << "Running check failed: " << check_status;
      return -1;
    }
    if (!expected_matches) {
      LOG(ERROR) << "Self-test failed!";
      return -1;
    }
  }

À des fins de test, vous pouvez vérifier ici pour vous assurer que vous obtenez le résultat attendu.

  // Do something interesting with the results we've generated.
  Status print_status = PrintTopLabels(outputs, FLAGS_labels);

Enfin, imprimez l'étiquette trouvée.

  if (!print_status.ok()) {
    LOG(ERROR) << "Running print failed: " << print_status;
    return -1;
  }

La gestion des erreurs utilise ici l'objet TensorFlow Status. Cet objet est très utile car le vérificateur ok () peut vous dire si une erreur s'est produite et imprimer un message d'erreur.

Dans ce cas, nous faisons la démonstration de la reconnaissance d'objets, mais dans divers domaines, vous devriez pouvoir utiliser un code très similaire dans d'autres modèles que vous avez trouvés et formés. Espérons que ce petit exemple vous donnera quelques idées sur la façon d'utiliser TensorFlow dans votre propre produit.

Exercice: L'apprentissage par transfert est l'idée que si vous savez comment bien résoudre une tâche, vous devriez être capable de transférer une partie de cette compréhension à la résolution de problèmes connexes. Une façon d'effectuer l'apprentissage par transfert est de supprimer la dernière couche classifiée du réseau, l'avant-dernière couche du CNN, dans ce cas 2048 dimensions. Est d'extraire le vecteur de. Vous pouvez le spécifier en définissant --output_layer = pool_3 et en modifiant la gestion du tenseur de sortie dans [Exemple d'API C ++](#C ++ API Usage). Essayez d'extraire cette fonctionnalité d'une collection d'images et voyez que vous pouvez prédire de nouvelles catégories que ImageNet n'a pas.

Ressources pour en savoir plus

Le livre en ligne gratuit de Michael Nielsen (http://neuralnetworksanddeeplearning.com/chap1.html) est une excellente ressource pour l'apprentissage des réseaux de neurones en général. Surtout pour les réseaux de neurones convolutifs, il existe d'excellents articles de blog de Chris Olah (http://colah.github.io/posts/2014-07-Conv-Nets-Modular/) et le livre de Michael Nielsen Il y a un Grand Chapitre qui les couvre.

Pour en savoir plus sur la mise en œuvre d'un réseau de neurones convolutifs, accédez au [Tutoriel sur le réseau de convolution profonde] de TensorFlow (http://www.tensorflow.org/tutorials/deep_cnn/index.html) ou un peu plus lâche ML Beginner et ML Expert Vous pouvez commencer avec le didacticiel de démarrage MNIST en). Enfin, si vous souhaitez obtenir les dernières informations sur la recherche dans ce domaine, lisez les résultats récents dans les articles référencés dans ce didacticiel.

Recommended Posts

Tutoriel TensorFlow - Reconnaissance d'image (traduction)
Tutoriel TensorFlow - Ensemble de Mandelbrot (traduction)
Tutoriel TensorFlow - TensorFlow Mechanics 101 (Traduction)
Tutoriel TensorFlow - Téléchargement de données MNIST (traduction)
Modèle de transformation de séquence de didacticiel TensorFlow (traduction)
Tutoriel TensorFlow - Réseau neuronal à convolution (traduction)
Traduction TensorFlow MNIST pour les débutants en ML
TensorFlow Deep MNIST pour la traduction d'experts
Reconnaissance faciale des membres Momokuro par TensorFlow (partie 2)
Essayez la reconnaissance d'objets en temps réel avec YOLOv2 (TensorFlow)