L'apprentissage automatique, la vision par ordinateur, la création d'API puissantes et la création de superbes interfaces utilisateur sont des domaines passionnants qui témoignent de nombreuses innovations.
Les deux premiers nécessitent des mathématiques et des sciences approfondies, tandis que le développement d'API et d'interface utilisateur se concentre sur la pensée algorithmique et la conception d'architectures flexibles. Ils sont très différents, il peut donc être difficile de décider lequel vous voulez apprendre ensuite. Le but de cet article est de montrer comment les quatre peuvent être utilisés pour créer une application de traitement d'image.
L'application que nous allons créer est un simple outil de reconnaissance de chiffres. Vous dessinez, la machine prédit le chiffre. La simplicité est essentielle car elle nous permet d'avoir une vue d'ensemble plutôt que de nous concentrer sur les détails.
Par souci de simplicité, nous utiliserons les technologies les plus populaires et les plus faciles à maîtriser. La partie apprentissage automatique utilisera Python pour l'application back-end. En ce qui concerne le côté interactionnel de l'application, nous fonctionnerons via une bibliothèque JavaScript qui n'a pas besoin d'être présentée: Réagir .
fichier .cc vs .cpp
La partie centrale de notre application est l'algorithme de deviner le nombre tiré. L'apprentissage automatique sera l'outil utilisé pour obtenir une bonne qualité de supposition. Ce type d'intelligence artificielle de base permet à un système d'apprendre automatiquement avec une quantité donnée de données. En termes plus larges, l'apprentissage automatique est un processus consistant à trouver une coïncidence ou un ensemble de coïncidences dans les données sur lesquelles s'appuyer pour deviner le résultat.
Notre processus de reconnaissance d'image comprend trois étapes:
Nous aurons besoin d'un environnement virtuel pour travailler avec l'apprentissage automatique en Python. Cette approche est pratique car elle gère tous les packages Python requis, vous n'avez donc pas à vous en soucier.
Installons-le avec les commandes de terminal suivantes:
python3 -m venv virtualenv source virtualenv/bin/activate
Avant de commencer à écrire le code, nous devons choisir un «enseignant» approprié pour nos machines. Habituellement, les professionnels de la science des données essaient différents modèles avant de choisir le meilleur. Nous allons ignorer les modèles très avancés qui nécessitent beaucoup de compétences et procéder à la algorithme des k-plus proches voisins .
C’est un algorithme qui récupère des échantillons de données et les organise sur un plan ordonné par un ensemble donné de caractéristiques. Pour mieux comprendre, examinons l'image suivante:
Pour détecter le type de Point vert , nous devrions vérifier les types de à voisins les plus proches où à est l'ensemble d'arguments. Compte tenu de l'image ci-dessus, si à est égal à 1, 2, 3 ou 4, la supposition sera un Triangle noir comme la plupart des points verts les plus proches à les voisins sont des triangles noirs. Si nous augmentons à à 5, alors la majorité des objets sont des carrés bleus, donc la supposition sera un Carré bleu .
Certaines dépendances sont nécessaires pour créer notre modèle d'apprentissage automatique:
Commençons par les installer et les importer tous:
pip install sklearn numpy matplotlib scipy from sklearn.datasets import load_digits from sklearn.neighbors import KNeighborsClassifier from sklearn.model_selection import train_test_split, cross_val_score import numpy as np import matplotlib.pyplot as plt
Maintenant, nous devons charger le Base de données MNIST . MNIST est un ensemble de données classique d'images manuscrites utilisées par des milliers de novices dans le domaine de l'apprentissage automatique:
digits = load_digits()
Une fois les données récupérées et prêtes, nous pouvons passer à l'étape suivante consistant à diviser les données en deux parties: formation et essai .
Nous utiliserons 75% des données pour entraîner notre modèle à deviner les chiffres et nous utiliserons le reste des données pour tester l'exactitude du modèle:
(X_train, X_test, y_train, y_test) = train_test_split( digits.data, digits.target, test_size=0.25, random_state=42 )
Les données sont maintenant organisées et nous sommes prêts à les utiliser. Nous essaierons de trouver le meilleur paramètre à pour notre modèle afin que les suppositions soient plus précises. Nous ne pouvons pas garder le à à ce stade, car nous devons évaluer le modèle avec différents à valeurs.
Voyons pourquoi il est essentiel d'envisager une gamme de à valeurs et comment cela améliore la précision de notre modèle:
ks = np.arange(2, 10) scores = [] for k in ks: model = KNeighborsClassifier(n_neighbors=k) score = cross_val_score(model, X_train, y_train, cv=5) score.mean() scores.append(score.mean()) plt.plot(scores, ks) plt.xlabel('accuracy') plt.ylabel('k') plt.show()
L'exécution de ce code vous montrera le tracé suivant décrivant la précision de l'algorithme avec différents à valeurs.
Comme vous pouvez le voir, un à La valeur 3 garantit la meilleure précision pour notre modèle et notre jeu de données.
Le cœur de l'application, qui est un algorithme de prédiction des chiffres à partir d'images, est maintenant prêt. Ensuite, nous devons décorer l'algorithme avec une couche API pour le rendre disponible à l'utilisation. Utilisons le populaire Framework Web Flask pour le faire proprement et de manière concise.
Nous allons commencer par installer Flask et les dépendances liées au traitement d'image dans l'environnement virtuel:
pip install Flask Pillow scikit-image
Une fois l'installation terminée, nous passons à la création du fichier de point d'entrée de l'application:
touch app.py
Le contenu du fichier ressemblera à ceci:
import os from flask import Flask from views import PredictDigitView, IndexView app = Flask(__name__) app.add_url_rule( '/api/predict', view_func=PredictDigitView.as_view('predict_digit'), methods=['POST'] ) app.add_url_rule( '/', view_func=IndexView.as_view('index'), methods=['GET'] ) if __name__ == 'main': port = int(os.environ.get('PORT', 5000)) app.run(host='0.0.0.0', port=port)
Vous obtiendrez une erreur indiquant que PredictDigitView
et IndexView
ne sont pas définis. L'étape suivante consiste à créer un fichier qui initialisera ces vues:
from flask import render_template, request, Response from flask.views import MethodView, View from flask.views import View from repo import ClassifierRepo from services import PredictDigitService from settings import CLASSIFIER_STORAGE class IndexView(View): def dispatch_request(self): return render_template('index.html') class PredictDigitView(MethodView): def post(self): repo = ClassifierRepo(CLASSIFIER_STORAGE) service = PredictDigitService(repo) image_data_uri = request.json['image'] prediction = service.handle(image_data_uri) return Response(str(prediction).encode(), status=200)
Une fois de plus, nous rencontrerons une erreur concernant une importation non résolue. La Vues package repose sur trois fichiers que nous n'avons pas encore:
Nous les mettrons en œuvre un par un.
Paramètres est un module avec des configurations et des variables constantes. Il stockera le chemin vers le classificateur sérialisé pour nous. Cela soulève une question logique: Pourquoi dois-je enregistrer le classificateur?
Parce que c'est un moyen simple d'améliorer les performances de votre application. Au lieu de former le classificateur chaque fois que vous recevez une demande, nous stockons la version préparée du classificateur, ce qui lui permet de fonctionner immédiatement:
import os BASE_DIR = os.getcwd() CLASSIFIER_STORAGE = os.path.join(BASE_DIR, 'storage/classifier.txt')
Le mécanisme des paramètres - obtenir le classificateur - sera initialisé dans le prochain paquet de notre liste, le Repo . Il s’agit d’une classe avec deux méthodes pour récupérer et mettre à jour le classificateur entraîné à l’aide de Python intégré pickle
module:
import pickle class ClassifierRepo: def __init__(self, storage): self.storage = storage def get(self): with open(self.storage, 'wb') as out: try: classifier_str = out.read() if classifier_str != '': return pickle.loads(classifier_str) else: return None except Exception: return None def update(self, classifier): with open(self.storage, 'wb') as in_: pickle.dump(classifier, in_)
Nous sommes sur le point de finaliser notre API. Maintenant, il ne manque plus que le Un service module. Quel est son objectif?
que fait un directeur financier dans une startup
Codons cet algorithme:
from sklearn.datasets import load_digits from classifier import ClassifierFactory from image_processing import process_image class PredictDigitService: def __init__(self, repo): self.repo = repo def handle(self, image_data_uri): classifier = self.repo.get() if classifier is None: digits = load_digits() classifier = ClassifierFactory.create_with_fit( digits.data, digits.target ) self.repo.update(classifier) x = process_image(image_data_uri) if x is None: return 0 prediction = classifier.predict(x)[0] return prediction
Ici vous pouvez voir que PredictDigitService
a deux dépendances: ClassifierFactory
et process_image
.
Nous allons commencer par créer une classe pour créer et entraîner notre modèle:
from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier class ClassifierFactory: @staticmethod def create_with_fit(data, target): model = KNeighborsClassifier(n_neighbors=3) model.fit(data, target) return model
L'API est prête pour l'action. Nous pouvons maintenant passer à l'étape de traitement d'image.
Le traitement d'image est une méthode permettant d'effectuer certaines opérations sur une image pour l'améliorer ou en extraire des informations utiles. Dans notre cas, nous devons effectuer une transition en douceur de l'image dessinée par un utilisateur au format de modèle d'apprentissage automatique.
Importons quelques aides pour atteindre cet objectif:
import numpy as np from skimage import exposure import base64 from PIL import Image, ImageOps, ImageChops from io import BytesIO
Nous pouvons diviser la transition en six parties distinctes:
1. Remplacez un arrière-plan transparent par une couleur
def replace_transparent_background(image): image_arr = np.array(image) if len(image_arr.shape) == 2: return image alpha1 = 0 r2, g2, b2, alpha2 = 255, 255, 255, 255 red, green, blue, alpha = image_arr[:, :, 0], image_arr[:, :, 1], image_arr[:, :, 2], image_arr[:, :, 3] mask = (alpha == alpha1) image_arr[:, :, :4][mask] = [r2, g2, b2, alpha2] return Image.fromarray(image_arr)
2. Coupez les bordures ouvertes
def trim_borders(image): bg = Image.new(image.mode, image.size, image.getpixel((0,0))) diff = ImageChops.difference(image, bg) diff = ImageChops.add(diff, diff, 2.0, -100) bbox = diff.getbbox() if bbox: return image.crop(bbox) return image
3. Ajouter des bordures de taille égale
documentation en développement logiciel agile
def pad_image(image): return ImageOps.expand(image, border=30, fill='#fff')
4. Convertissez l'image en mode niveaux de gris
def to_grayscale(image): return image.convert('L')
5. Inverser les couleurs
def invert_colors(image): return ImageOps.invert(image)
6. Redimensionner l'image au format 8x8
def resize_image(image): return image.resize((8, 8), Image.LINEAR)
Vous pouvez maintenant tester l'application. Exécutez l'application et entrez la commande ci-dessous pour envoyer une demande avec cette image iStock à l'API:
export FLASK_APP=app flask run
curl 'http://localhost:5000/api/predict' -X 'POST' -H 'Content-Type: application/json' -d ' base64)'' -i
Vous devriez voir la sortie suivante:
HTTP/1.1 100 Continue HTTP/1.0 200 OK Content-Type: text/html; charset=utf-8 Content-Length: 1 Server: Werkzeug/0.14.1 Python/3.6.3 Date: Tue, 27 Mar 2018 07:02:08 GMT 8
L'exemple d'image représentait le numéro 8 et notre application l'a correctement identifié comme tel.
Pour démarrer rapidement l'application frontend, nous allons utiliser Passe-partout CRA :
create-react-app frontend cd frontend
Après avoir configuré le lieu de travail, nous avons également besoin d'une dépendance pour dessiner des chiffres. La réagir-croquis le package correspond parfaitement à nos besoins:
npm i react-sketch
L'application n'a qu'un seul composant. Nous pouvons diviser ce composant en deux parties: logique et vue .
combien y a-t-il d'applications de rencontres
La partie vue est chargée de représenter le volet de dessin, Soumettre et Réinitialiser boutons. Lorsqu'ils interagissent, nous devons également représenter une prédiction ou une erreur. Du point de vue logique, il a les tâches suivantes: soumettre des images et effacer l'esquisse .
Chaque fois qu'un utilisateur clique Soumettre , le composant extraira l'image du composant d'esquisse et fera appel au module API makePrediction
fonction. Si la demande au back-end aboutit, nous définirons la variable d'état de prédiction. Sinon, nous mettrons à jour l'état d'erreur.
Lorsqu'un utilisateur clique sur Réinitialiser , l'esquisse sera effacée:
import React, { useRef, useState } from 'react'; import { makePrediction } from './api'; const App = () => { const sketchRef = useRef(null); const [error, setError] = useState(); const [prediction, setPrediction] = useState(); const handleSubmit = () => { const image = sketchRef.current.toDataURL(); setPrediction(undefined); setError(undefined); makePrediction(image).then(setPrediction).catch(setError); }; const handleClear = (e) => sketchRef.current.clear(); return null }
La logique est suffisante. Maintenant, nous pouvons y ajouter l'interface visuelle:
import React, { useRef, useState } from 'react'; import { SketchField, Tools } from 'react-sketch'; import { makePrediction } from './api'; import logo from './logo.svg'; import './App.css'; const pixels = (count) => `${count}px`; const percents = (count) => `${count}%`; const MAIN_CONTAINER_WIDTH_PX = 200; const MAIN_CONTAINER_HEIGHT = 100; const MAIN_CONTAINER_STYLE = { width: pixels(MAIN_CONTAINER_WIDTH_PX), height: percents(MAIN_CONTAINER_HEIGHT), margin: '0 auto', }; const SKETCH_CONTAINER_STYLE = { border: '1px solid black', width: pixels(MAIN_CONTAINER_WIDTH_PX - 2), height: pixels(MAIN_CONTAINER_WIDTH_PX - 2), backgroundColor: 'white', }; const App = () => { const sketchRef = useRef(null); const [error, setError] = useState(); const [prediction, setPrediction] = useState(); const handleSubmit = () => { const image = sketchRef.current.toDataURL(); setPrediction(undefined); setError(undefined); makePrediction(image).then(setPrediction).catch(setError); }; const handleClear = (e) => sketchRef.current.clear(); return ( {prediction && Predicted value is: {prediction}
} Clear Guess the number {error && Something went wrong
} ); }; export default App;
Le composant est prêt, testez-le en exécutant et en allant à localhost:3000
après:
npm run start
L'application de démonstration est disponible Ici . Vous pouvez également parcourir le code source sur GitHub .
La qualité de ce classificateur n'est pas parfaite, et je ne prétends pas qu'elle le soit. La différence entre les données que nous avons utilisées pour la formation et les données provenant de l'interface utilisateur est énorme. Malgré cela, nous avons créé une application fonctionnelle à partir de zéro en moins de 30 minutes.
Dans la foulée, nous avons affiné nos compétences dans quatre domaines:
Les cas d'utilisation potentiels de logiciels capables de reconnaître des chiffres manuscrits ne manquent pas, allant des logiciels éducatifs et administratifs aux services postaux et financiers.
Par conséquent, j'espère que cet article vous motivera à améliorer vos capacités d'apprentissage automatique, le traitement d'images et le développement frontal et back-end, et à utiliser ces compétences pour concevoir des applications merveilleuses et utiles.
Si vous souhaitez approfondir vos connaissances sur l'apprentissage automatique et le traitement d'images, vous pouvez consulter notre Tutoriel d'apprentissage automatique contradictoire .
MNIST est l'un des ensembles de données d'entrée de gamme les plus populaires en vision par ordinateur. Il contient des milliers d'images de chiffres manuscrits.
Les modèles d'apprentissage automatique apprennent des données. Pour rendre le modèle suffisamment intelligent, nous devons fournir les données dont nous disposons avec les résultats attendus. Le modèle utilisera ces données pour détecter les relations entre les paramètres de données et le résultat souhaité.
Le traitement d'image est une méthode pour effectuer certaines opérations sur une image pour obtenir une image améliorée ou extraire des informations utiles.