Créons une application capable de rechercher des images similaires avec Python et Flask Part1

Créons une application capable de rechercher des images similaires avec Python et Flask Part1

Aperçu

J'ai créé un dépôt github parce que c'est un gros problème, s'il vous plaît star (mendiant) SSAM-SimilaritySearchAppwithMNIST

Comme le dit le titre. Cette fois, vous pouvez facilement obtenir les données et la taille des données est petite. Si vous mettez l'image MNIST dans le MNIST, vous trouverez une image MNIST similaire. En créant une telle application, vous pouvez parler de la recherche de voisinage approximative et de Flask. J'écrirai également mon propre mémo sur la création d'une application en utilisant.

procédure

Créez un répertoire de travail!

--Directeur de travail

C'est bien de décider d'un nom, mais je me suis bien entendu et lui ai donné un nom sophistiqué comme SSAM-SimilaritySearchAppwithMNIST.

--Répertoire pour MNIST

Ensuite, créons un dossier pour enregistrer les données MNIST. Sinon, vous devrez le télécharger à chaque fois, ce qui est gênant. Créez un dossier avec un nom tel que static / mnist \ _data.

――La structure actuelle du répertoire ressemble à ceci

SSAM-SimilaritySearchAppwithMNIST
└── static
    └── mnist_data

Téléchargeons les données MNIST!

Qu'est-ce que MNIST?

C'est célèbre, donc je ne pense pas avoir besoin de l'expliquer plus, mais pour ceux qui ne le savent pas, c'est une image de nombre manuscrite de 28x28. Comme il est facile d'utiliser des données, elles sont souvent utilisées pour évaluer la précision des modèles d'apprentissage automatique, mais comme il s'agit de données propres, la précision est généralement élevée. (Comme cet article) J'ai fait un modèle avec une grande précision avec MNIST! Vous devez faire attention aux articles comme celui-ci. Oh, il est plus sûr de le regarder avec une position qu'il y a des articles qui sont vivants comme MNIST. (Ce n'est qu'une histoire masochiste, donc je ne suis pas fan d'autres articles traitant de MNIST, c'est vrai.)

Bibliothèque à utiliser

Je suis un peu inquiet de savoir s'il est plus facile de comprendre si le code est divisé en petits morceaux et écrit avec des explications une par une, ou s'il est plus facile d'écrire le code à la fois et d'ajouter des explications supplémentaires plus tard. J'aimerais y réfléchir un peu à l'avenir, alors je serais heureux si vous avez une opinion de référence. (Je ne sais même pas si Qiita a une fonction de commentaire.)

Pour le moment, au moins je pense qu'il serait plus gentil d'écrire ensemble les bibliothèques utilisées cette fois-ci, alors je vais le faire. Veuillez installer pip par vous-même, le cas échéant.

import os
import gzip
import pickle
import numpy as np
import urllib.request
from mnist import MNIST
from annoy import AnnoyIndex

from sklearn.metrics import accuracy_score

Télécharger MNIST

J'écrirai le code immédiatement. Comme je l'ai écrit ci-dessus, je m'inquiète un peu de savoir s'il est gentil de diviser le code en petits morceaux et de l'écrire en ajoutant des explications une par une, ou s'il est plus gentil de tout écrire en même temps et d'ajouter des explications supplémentaires plus tard. J'ai écrit quelques commentaires avec soin, je vais donc les écrire tous en même temps cette fois, mais j'aimerais entendre vos opinions ici et là.

Le premier est le code qui télécharge MNIST.

def load_mnist():
    #Télécharger les données MNIST
    url_base = 'http://yann.lecun.com/exdb/mnist/'
    key_file = {
        'train_img':'train-images-idx3-ubyte.gz',
        'train_label':'train-labels-idx1-ubyte.gz',
        'test_img':'t10k-images-idx3-ubyte.gz',
        'test_label':'t10k-labels-idx1-ubyte.gz'
    }

    #Télécharger un fichier(.format gz)
    for filename in key_file.values():
        file_path = f'./static/mnist_data/{filename}' #Lire.chemin du fichier gz
        if os.path.isfile(file_path.replace('.gz', '')): continue #Ignorer s'il existe déjà un fichier

        urllib.request.urlretrieve(url_base + filename, file_path)

        # .Avec décompression GZ.Supprimer le fichier gz
        with gzip.open(file_path, mode='rb') as f:
            mnist = f.read()
            #Décompressez et enregistrez
            with open(file_path.replace('.gz', ''), 'wb') as w:
                w.write(mnist)
            os.remove(file_path) # .Supprimer le fichier gz

    #np les données de mnist.Lire et retourner sous forme de tableau
    mndata = MNIST('./static/mnist_data/')
    # train
    images, labels = mndata.load_training()
    train_images, train_labels = np.reshape(np.array(images), (-1,28,28)), np.array(labels) # np.L'image de la mnist à convertir en tableau est 28x28
    # test
    images, labels = mndata.load_testing()
    test_images, test_labels = np.reshape(np.array(images), (-1,28,28)), np.array(labels) # np.L'image de la mnist à convertir en tableau est 28x28
    return train_images, train_labels, test_images, test_labels

Lorsque vous exécutez le code ci-dessus, il télécharge d'abord un fichier compressé appelé fichier .gz à partir de l'emplacement où se trouvent les données MNIST et le place sous static / mnist_data /. (Si vous ne créez pas de dossier pour ./static/mnist_data/ à l'avance, vous pouvez recevoir une erreur indiquant qu'il n'y a pas de dossier. Je suis désolé.) Ensuite, décompressez le fichier .gz et supprimez le fichier .gz car vous n'en avez pas besoin. En fait, ce fichier décompressé est au format binaire, il est donc difficile à gérer,

from mnist import MNIST

Il semble que si vous utilisez une bibliothèque avec un nom stupide, elle séparera les données pour train et les données pour test et en fera un type de tableau. En fait, c'est le seul secret ici que je dois beaucoup m'inquiéter et passer plusieurs heures ici. Avec ce sentiment, la fonction de téléchargement des données MNIST a été achevée avant que je ne le sache.

Recherche approximative du voisin le plus proche

Quelle est la recherche de quartier la plus proche?

Quand je cite la définition de wikipedia, elle s'écrit comme suit.

La recherche de voisin le plus proche (NNS) est un type de problème d'optimisation qui trouve le point le plus proche dans l'espace de distance, ou une solution à celui-ci. Elle est également appelée recherche de proximité (en anglais: recherche de proximité), recherche de similarité (en anglais: recherche de similarité) et recherche de point le plus proche (en anglais: recherche de point le plus proche). Le problème est que lorsqu'il y a un ensemble de points S dans l'espace de distance M et qu'il y a un point d'interrogation q ∈ M, alors nous trouvons le point dans S qui est le plus proche de q. Dans de nombreux cas, M adopte un espace euclidien de dimension d, et la distance est mesurée comme la distance euclidienne ou la distance de Manhattan. Différents algorithmes sont utilisés pour les cas de faible dimension et de haute dimension. ~ De wikipedia

Lorsque cela est traduit en japonais, il est écrit qu'il s'agit d'un algorithme qui détermine une fonction qui mesure un certain degré de similitude et trouve des points avec une forte similitude sur la base de celle-ci. C'est ce que cela signifie.

Approximatif?

L'algorithme utilisé cette fois est ennuyeux, qui est appelé recherche "approximative" du plus proche voisin. Qu'est-ce que c'est «approximatif»? En fait, cet algorithme de recherche de voisinage consomme beaucoup de ressources de calcul lorsqu'il tente de calculer correctement. Par exemple, ce sera difficile le jour où vous pensez que la différence entre toutes les valeurs de pixel doit être calculée le plus simplement. Surtout dans le cas des images, le temps de calcul augmente de manière explosive car il y a vertical x horizontal x nombre de canaux. Ce MNIST est de 28x28 après tout, et c'est une échelle de gris (image en noir et blanc), donc ce n'est pas un gros problème. Papa est désespéré et ne peut pas dormir. Le montant de calcul sœur vous parlera de la peur d'une explosion exponentielle. C'est le contenu le plus fort qui soit à la fois intéressant et éducatif, donc si vous ne le savez pas, jetez un coup d'œil et pleurez sur l'obsession de votre sœur. "Comment compter Fukashigi" Avec votre sœur! Comptons ensemble!

Algorithme utilisé cette fois

Pour trouver des images similaires, nous utilisons une bibliothèque de recherche approximative du voisin le plus proche appelée ennuy. La raison pour laquelle je l'utilise est que j'y suis habitué et que je pense que le code est relativement facile à lire.

Pour plus d'informations sur les algorithmes, voir ce blog de l'auteur ennuy Voisins les plus proches et modèles vectoriels - partie 2 - algorithmes et structures de données -part-2-how-to-search-in-high-dimension-spaces.html) et en japonais [l'avant-garde de la recherche approximative du plus proche voisin](https://speakerdeck.com/matsui_528/jin- si-zui-jin-bang-tan-suo-falsezui-qian-xian? slide = 43) L'explication de ce partage de diapositives est facile à comprendre. Il est utile de regarder cela avant de lire cet article.

Pour ceux qui ne veulent pas être dérangés, voici une brève explication afin que vous puissiez rechercher le voisinage de O (logn) à grande vitesse en divisant récursivement l'espace où les points de données existent et en créant des arbres bifurqués. C'est un algorithme qui a été fait. Cependant, j'ai besoin de construire un arbre de décision.

En fait, l'auteur de ennuyer compare plusieurs bibliothèques de recherche voisines approximatives et recommande d'autres bibliothèques. Nouveaux benchmarks du voisin le plus proche approximatif Si vous voulez vraiment de la vitesse, il est probablement préférable d'utiliser la faiss créée par Facebook, mais il semble qu'elle ne puisse être installée qu'à partir de conda, donc je n'ai pas envie de l'utiliser.

Utilisation d'ennui

L'explication est un peu plus longue. Essayons la recherche de quartier en utilisant immédiatement ennuyer.

def make_annoy_db(train_imgs):
    #Utilisez l'ennui de la bibliothèque de recherche de quartier approximative
    #Décidez de la forme et de la métrique des données à saisir et insérez les données
    annoy_db = AnnoyIndex((28*28), metric='euclidean') #MNIST a une taille de 28x28, ce qui donne les données d'entrée et comment calculer la similitude.*J'écris 28
    for i, train_img in enumerate(train_imgs):
        annoy_db.add_item(i, train_img.flatten()) #Entrez l'index et les données correspondantes
    annoy_db.build(n_trees=10) #Construire
    annoy_db.save('./static/mnist_db.ann') #Enregistrez la base de données créée sous statique

Est-ce que ça ressemblera à ça? J'utilise le terme base de données, qui n'est pas exactement une base de données, simplement parce que je ne connais pas d'autres termes bons et appropriés, peut-être. À propos, AnnoyIndex ne peut recevoir qu'un tableau unidimensionnel, donc si vous voulez mettre des données d'image, utilisez flatten (), remodeler ou coder en dur comme cette fois.

Voyons la précision

Bien sûr, il y aura des erreurs car la similitude exacte n'est pas calculée simplement parce qu'il s'agit d'une recherche approximative du plus proche voisin. (Annoy résout ce problème en utilisant plusieurs arbres ou quelque chose. Pour plus de détails, voir l'URL ci-dessus) Vérifions un peu si c'est vraiment précis.

Heureusement, MNIST a déjà chaque image et son étiquette correcte correspondante. Demandez-leur de tirer une image similaire pour les données de test et de vérifier si elle correspond à la bonne réponse.

train_imgs, train_lbls, test_imgs, test_lbls = load_mnist()

if not os.path.isfile('./static/mnist_db.ann'):
    make_annoy_db(train_imgs) # .Construisez annoydb si vous n'avez pas encore de fichiers ann
annoy_db = AnnoyIndex((28*28), metric='euclidean')
annoy_db.load('./static/mnist_db.ann') #Charger la base de données d'ennui

#Essayez de voir la précision en entrant des données de test, en récupérant la proximité et en comparant avec le réel
y_pred = [train_lbls[annoy_db.get_nns_by_vector(test_img.flatten(), 1)[0]] for test_img in test_imgs]
score  = accuracy_score(test_lbls, y_pred)
print('acc:', score)
#Sortie acc: 0.9595

Une très grande précision est sortie. Si vous voulez dire 0,2525, vous pouvez faire une histoire avec Nico Kitchen, mais la réalité n'est pas si douce. (De quoi diable suis-je en train de parler?)

Dans le cas d'une image réelle, ce n'est pas parce que l'étiquette correcte est la même que c'est une image vraiment similaire (par exemple, si les deux sont des images de chats mais que l'arrière-plan est différent, ou s'il s'agit de chats noirs et de chats blancs) , Le cerveau humain ne juge pas que c'est une image similaire), mais si elle est limitée à ce MNIST, c'est un bel ensemble de données, alors peut-être que des images similaires sortent.

Code entier

Le code entier jusqu'à présent est le suivant.

import os
import gzip
import pickle
import numpy as np
import urllib.request
from mnist import MNIST
from annoy import AnnoyIndex

from sklearn.metrics import accuracy_score

def load_mnist():
    #Télécharger les données MNIST
    url_base = 'http://yann.lecun.com/exdb/mnist/'
    key_file = {
        'train_img':'train-images-idx3-ubyte.gz',
        'train_label':'train-labels-idx1-ubyte.gz',
        'test_img':'t10k-images-idx3-ubyte.gz',
        'test_label':'t10k-labels-idx1-ubyte.gz'
    }

    #Télécharger un fichier(.format gz)
    for filename in key_file.values():
        file_path = f'./static/mnist_data/{filename}' #Lire.chemin du fichier gz
        if os.path.isfile(file_path.replace('.gz', '')): continue #Ignorer s'il existe déjà un fichier

        urllib.request.urlretrieve(url_base + filename, file_path)

        # .Avec décompression GZ.Supprimer le fichier gz
        with gzip.open(file_path, mode='rb') as f:
            mnist = f.read()
            #Décompressez et enregistrez
            with open(file_path.replace('.gz', ''), 'wb') as w:
                w.write(mnist)
            os.remove(file_path) # .Supprimer le fichier gz

    #np les données de mnist.Lire et retourner sous forme de tableau
    mndata = MNIST('./static/mnist_data/')
    # train
    images, labels = mndata.load_training()
    train_images, train_labels = np.reshape(np.array(images), (-1,28,28)), np.array(labels) # np.L'image de la mnist à convertir en tableau est 28x28
    # test
    images, labels = mndata.load_testing()
    test_images, test_labels = np.reshape(np.array(images), (-1,28,28)), np.array(labels) # np.L'image de la mnist à convertir en tableau est 28x28
    return train_images, train_labels, test_images, test_labels

def make_annoy_db(train_imgs):
    #Utilisez l'ennui de la bibliothèque de recherche de quartier approximative
    #Décidez de la forme et de la métrique des données à saisir et insérez les données
    annoy_db = AnnoyIndex((28*28), metric='euclidean')
    for i, train_img in enumerate(train_imgs):
        annoy_db.add_item(i, train_img.flatten())
    annoy_db.build(n_trees=10) #Construire
    annoy_db.save('./static/mnist_db.ann')


def main():
    #processus de chargement d'image mnist
    train_imgs, train_lbls, test_imgs, test_lbls = load_mnist()
    print(train_imgs.shape, train_lbls.shape, test_imgs.shape, test_lbls.shape) #Je voulais vérifier la quantité de données dont dispose MNIST

    if not os.path.isfile('./static/mnist_db.ann'):
        make_annoy_db(train_imgs) # .Construisez annoydb si vous n'avez pas encore de fichiers ann
    annoy_db = AnnoyIndex((28*28), metric='euclidean')
    annoy_db.load('./static/mnist_db.ann') #Charger la base de données d'ennui


    #Essayez de voir la précision en entrant des données de test, en récupérant la proximité et en comparant avec le réel
    y_pred = [train_lbls[annoy_db.get_nns_by_vector(test_img.flatten(), 1)[0]] for test_img in test_imgs]
    score = accuracy_score(test_lbls, y_pred)
    print('acc:', score)


if __name__ == "__main__":
    main()

Prochain but

D'une manière ou d'une autre, le montant a déjà augmenté jusqu'à présent, je continuerai donc la prochaine fois. Cette fois, je suis allé au point où je pouvais rechercher des images similaires en utilisant ennuyer. La prochaine fois, créons une application qui produira des images similaires lorsque vous sélectionnez une image à l'aide de Flask. (Si je ne m'ennuie pas ou ne suis pas occupé) Continuez: -> La prochaine fois

Bonus: je m'en fiche vraiment, mais j'aime le mode org d'Emacs et j'ai commencé à écrire cet article en mode org, mais hélas je n'ai pas pu faire face à la faible compatibilité. Après tout, je l'ai écrit dans Markdown. Je pense que orgmode est le logiciel de création de documents le plus puissant à lui seul (ou Markdown n'est-il pas difficile à utiliser? Quel idiot pense à faire un saut de ligne avec deux espaces de demi-largeur), mais par tous les moyens C'est triste de ne pas pouvoir le dire lorsque je pense à la coopération avec les autres. Il y a quelque chose comme l'exportation en tant que démarque en mode org, et je pensais faire quelque chose à ce sujet, mais ce ne serait pas aussi propre que je le pensais sur Qiita ... c'est triste. Emacs a également une liaison de clé unique et il est difficile à utiliser si vous pouvez profiter de VScode, mais VScode a également une extension de mode organisation, mais la touche de tabulation la plus importante est une fonction permettant de changer l'état d'affichage pour plier les yeux. Et je pense qu'il n'y a pas de fonction d'exportation vers html etc., je pense à quelque chose comme VHS vs Betamax, c'est difficile à savoir.

Au fait, j'étais triste de voir la couverture du numéro d'août 2020 de Software Design. Emacs combat Vim, n'est-ce pas le cas depuis longtemps? Est-ce plus impossible? Emacs? Je voudrais fermer l'article comme ça. TH320_642008.jpg

Recommended Posts

Créons une application capable de rechercher des images similaires avec Python et Flask Part1
Créons une application capable de rechercher des images similaires avec Python et Flask Part2
Créez un Discord Bot qui peut rechercher et coller des images
Créez une application qui devine les étudiants avec Python
Créons une application qui affaiblit les nerfs avec Vue.js et Django-Rest-Framework [Partie 2] ~ Configuration de Vue ~
Faisons une application qui affaiblit les nerfs avec Vue.js et Django-Rest-Framework [Partie 1] ~ Django setup ~
Essayez de créer un jeu simple avec Python 3 et iPhone
Créons une application Mac avec Tkinter et py2app
Images en bordure avec python Partie 1
Introduction à Python que même les singes peuvent comprendre (partie 3)
Créons une application qui authentifie OIDC avec Azure AD
Introduction à Python que même les singes peuvent comprendre (partie 1)
Introduction à Python que même les singes peuvent comprendre (partie 2)
Faisons un diagramme sur lequel on peut cliquer avec IPython
[Python] Créez un graphique qui peut être déplacé avec Plotly
Faisons une application WEB pour l'annuaire téléphonique avec flacon Partie 1
Faisons une interface graphique avec python.
Créez une application de gestion automatique des notes pour Tenho Private Room avec le bot LINE et Python Partie 1
Faisons une application WEB pour l'annuaire téléphonique avec flacon Partie 2
Créez une application de gestion automatique des notes pour Tenho Private Room avec le bot LINE et Python Partie 2
Créez une application de gestion automatique des notes pour la salle privée Tenho avec le bot LINE et la partie Python ③
Faisons une application WEB pour l'annuaire téléphonique avec flacon Partie 3
Créez une application de scraping avec Python + Django + AWS et modifiez les tâches
Faisons un graphe avec python! !!
Faisons une application WEB pour l'annuaire téléphonique avec flacon Partie 4
Faisons une chemise IoT avec Lambda, Kinesis, Raspberry Pi [Partie 1]
Résoudre avec Python [100 questions passées que les débutants et les intermédiaires devraient résoudre] (028 --033 recherche de priorité de largeur)
Boîte de dialogue de recherche Web qui peut automatiquement compléter et saisir des mots-clés [Python] [Gtk]
Créons un modèle de reconnaissance d'image avec vos propres données et jouons!
Explication de la création d'une application pour afficher des images et dessiner avec Python
Faisons un jeu de shiritori avec Python
Modulation et démodulation FM avec Python Partie 3
POSTER diversement avec Python et recevoir avec Flask
Capturer des images avec Pupil, python et OpenCV
Fractal pour faire et jouer avec Python
Faisons la voix lentement avec Python
[Python] Rendons matplotlib compatible avec le japonais
Application Web facile avec Python + Flask + Heroku
Modulation et démodulation FM avec Python Partie 2
[Python] Créez rapidement une API avec Flask
Créez une application de mots anglais avec python
Créez un framework Web avec Python! (1)
Importer et exporter des images GeoTiff avec Python
Créez une application de bureau avec Python avec Electron
Faisons un bot Twitter avec Python!
Développons un algorithme d'investissement avec Python 1
Créez un framework Web avec Python! (2)
[Python] Création d'un outil qui peut lister, sélectionner et exécuter des fichiers python avec tkinter et à propos de la partie qui a été interceptée
Créez une application qui saisit, affiche et supprime des formulaires à l'aide de Python / Flask au lieu de DB.
Créons une application Web de conversion A vers B avec Flask! De zéro ...
Faisons une application qui affaiblit les nerfs avec Vue.js et Django-Rest-Framework [Partie 3] ~ Implémentation de la faiblesse nerveuse ~
Faisons une application qui affaiblit les nerfs avec Vue.js et Django-Rest-Framework [Partie 6] ~ Authentification utilisateur 2 ~
Faisons une application qui affaiblit les nerfs avec Vue.js et Django-Rest-Framework [Partie 5] ~ Authentification des utilisateurs ~
Résoudre avec Python [100 questions passées que les débutants et les intermédiaires devraient résoudre] (024 --027 Recherche de priorité en profondeur)
Rechercher et télécharger automatiquement des vidéos YouTube avec Python
Créez des données de test comme ça avec Python (partie 1)
Raisonnement causal et recherche causale par Python (pour les débutants)
Créez une application de composition d'images avec Flask + Pillow