Машинното обучение, компютърното зрение, изграждането на мощни API и създаването на красиви потребителски интерфейси са вълнуващи полета, свидетели на много иновации.
Първите две изискват обширна математика и наука, докато API и UI център за разработка на алгоритмично мислене и проектиране на гъвкави архитектури. Те са много различни, така че да решите кой искате да научите след това може да е предизвикателство. Целта на тази статия е да покаже как и четиримата могат да бъдат използвани при създаването на приложение за обработка на изображения.
Приложението, което ще изградим, е просто разпознаване на цифри. Теглиш, машината предсказва цифрата. Простотата е от съществено значение, защото ни позволява да видим общата картина, вместо да се фокусираме върху детайлите.
За по-голяма простота ще използваме най-популярните и лесни за научаване технологии. Частта за машинно обучение ще използва Python за back-end приложението. Що се отнася до интерактивната страна на приложението, ние ще работим чрез JavaScript библиотека, която не се нуждае от представяне: Реагирайте .
web api за удостоверяване на токен angularjs
Основната част на нашето приложение е алгоритъмът, който отгатва изтегленото число. Машинното обучение ще бъде инструментът, използван за постигане на добро качество на предположения. Този вид основен изкуствен интелект позволява на системата да се учи автоматично с дадено количество данни. В по-широк смисъл машинното обучение е процес на намиране на съвпадение или съвпадение на съвпаденията в данните, за да се разчита на тях за отгатване на резултата.
Нашият процес на разпознаване на изображения съдържа три стъпки:
Ще ни трябва виртуална среда за работа с машинно обучение в Python. Този подход е практичен, тъй като управлява всички необходими пакети на Python, така че не е нужно да се притеснявате за тях.
Нека го инсталираме със следните терминални команди:
python3 -m venv virtualenv source virtualenv/bin/activate
Преди да започнем да пишем кода, трябва да изберем подходящ „учител“ за нашите машини. Обикновено специалистите в областта на данните изпробват различни модели, преди да изберат най-добрия. Ще пропуснем много усъвършенствани модели, които изискват много умения, и ще продължим с k-алгоритъм на най-близките съседи .
Това е алгоритъм, който получава някои проби от данни и ги подрежда в равнина, подредена по даден набор от характеристики. За да го разберем по-добре, нека прегледаме следното изображение:
За откриване на типа на Зелена точка , трябва да проверим видовете да се най-близките съседи къде да се е набор от аргументи. Като се има предвид изображението по-горе, ако да се е равно на 1, 2, 3 или 4, предположението ще бъде a Черен триъгълник като най-близката до зелената точка да се съседите са черни триъгълници. Ако увеличим да се до 5, тогава по-голямата част от обектите са сини квадрати, следователно предположението ще бъде a Син площад .
Има някои зависимости, необходими за създаването на нашия модел за машинно обучение:
Нека започнем с инсталирането и импортирането на всички тях:
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
Сега трябва да заредим База данни MNIST . MNIST е класически набор от ръкописни изображения, използвани от хиляди начинаещи в областта на машинното обучение:
digits = load_digits()
След като данните бъдат извлечени и готови, можем да преминем към следващата стъпка на разделяне на данните на две части: обучение и тестване .
Ще използваме 75% от данните, за да обучим нашия модел да отгатва цифри и ще използваме останалите данни, за да тестваме верността на модела:
(X_train, X_test, y_train, y_test) = train_test_split( digits.data, digits.target, test_size=0.25, random_state=42 )
Данните вече са подредени и сме готови да ги използваме. Ще се опитаме да намерим най-добрия параметър да се за нашия модел, така че предположенията ще бъдат по-точни. Не можем да запазим да се ценим ума си на този етап, тъй като трябва да оценим модела с различни да се стойности.
Нека да видим защо е важно да се разгледа набор от да се стойности и как това подобрява точността на нашия модел:
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()
Изпълнението на този код ще ви покаже следния график, описващ точността на алгоритъма с различни да се стойности.
Както можете да видите, a да се стойност 3 осигурява най-добрата точност за нашия модел и набор от данни.
Ядрото на приложението, което представлява алгоритъм за предсказване на цифрите от изображения, вече е готово. След това трябва да украсим алгоритъма с API слой, за да го направим достъпен за използване. Нека използваме популярното Уеб рамка на колба за да направите това чисто и кратко.
Ще започнем с инсталирането на Flask и зависимостите, свързани с обработката на изображения във виртуалната среда:
pip install Flask Pillow scikit-image
Когато инсталацията завърши, преминаваме към създаването на файла за входната точка на приложението:
touch app.py
Съдържанието на файла ще изглежда така:
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)
Ще получите грешка, казвайки, че PredictDigitView
и IndexView
не са определени. Следващата стъпка е създаването на файл, който ще инициализира тези изгледи:
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)
За пореден път ще срещнем грешка относно неразрешен импорт. The Изгледи пакетът разчита на три файла, които все още нямаме:
Ще ги приложим един по един.
Настройки е модул с конфигурации и константни променливи. Той ще съхрани пътя към сериализирания класификатор за нас. Това поражда логичен въпрос: Защо трябва да запазя класификатора?
Тъй като това е лесен начин да подобрите ефективността на приложението си. Вместо да обучаваме класификатора всеки път, когато получите заявка, ние ще съхраняваме подготвената версия на класификатора, като му даваме възможност да работи нестандартно:
import os BASE_DIR = os.getcwd() CLASSIFIER_STORAGE = os.path.join(BASE_DIR, 'storage/classifier.txt')
Механизмът за настройки - получаване на класификатора - ще бъде инициализиран в следващия пакет от нашия списък, Репо . Това е клас с два метода за извличане и актуализиране на обучения класификатор, използвайки вградения в Python pickle
модул:
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_)
Ние сме близо до финализирането на нашия API. Сега му липсва само Обслужване модул. Каква е целта му?
каква е гещалт теорията
Нека кодираме този алгоритъм:
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
Тук можете да видите, че PredictDigitService
има две зависимости: ClassifierFactory
и process_image
.
Ще започнем, като създадем клас за създаване и обучение на нашия модел:
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
API е готов за действие. Сега можем да преминем към стъпката за обработка на изображения.
Обработката на изображения е метод за извършване на определени операции върху изображение, за да се подобри или да се извлече полезна информация от него. В нашия случай трябва плавно да прехвърлим изображението, нарисувано от потребител, към формата на модела за машинно обучение.
Нека импортираме някои помощници, за да постигнем тази цел:
import numpy as np from skimage import exposure import base64 from PIL import Image, ImageOps, ImageChops from io import BytesIO
Можем да разделим прехода на шест отделни части:
1. Заменете прозрачния фон с цвят
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. Подрежете отворените граници
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. Добавете граници с еднакъв размер
def pad_image(image): return ImageOps.expand(image, border=30, fill='#fff')
4. Преобразувайте изображението в режим на скала на сивото
кое от следните е вярно за вмъкване на контрола в етикет?
def to_grayscale(image): return image.convert('L')
5. Обърнете цветовете
def invert_colors(image): return ImageOps.invert(image)
6. Преоразмерете изображението във формат 8x8
def resize_image(image): return image.resize((8, 8), Image.LINEAR)
Сега можете да тествате приложението. Стартирайте приложението и въведете командата по-долу, за да изпратите заявка с това изображение на iStock към API:
export FLASK_APP=app flask run
curl 'http://localhost:5000/api/predict' -X 'POST' -H 'Content-Type: application/json' -d ' base64)'' -i
Трябва да видите следния изход:
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
Примерното изображение изобразява числото 8 и нашето приложение правилно го идентифицира като такова.
За бързо стартиране на фронтенд приложението ще използваме CRA котел :
create-react-app frontend cd frontend
След настройването на работното място, ние също се нуждаем от зависимост, за да изчертаем цифри. The реакция-скица пакет отговаря напълно на нашите нужди:
npm i react-sketch
Приложението има само един компонент. Можем да разделим този компонент на две части: логика и поглед .
колко приложения за запознанства има
Частта за изглед е отговорна за представянето на панела за рисуване, Изпращане и Нулиране бутони. Когато си взаимодействаме, ние също трябва да представяме прогноза или грешка. От гледна точка на логиката той има следните задължения: изпратете изображения и изчистете скицата .
Всеки път, когато потребител щракне Изпращане , компонентът ще извлече изображението от компонента на скицата и ще се обърне към модула на API makePrediction
функция. Ако заявката към задния край успее, ще зададем променливата на състоянието на прогнозата. В противен случай ще актуализираме състоянието на грешката.
Когато потребител кликне върху Нулиране , скицата ще се изчисти:
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 }
Логиката е достатъчна. Сега можем да добавим визуалния интерфейс към него:
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;
Компонентът е готов, изпробвайте го, като изпълните и отидете на localhost:3000
след:
npm run start
Демо приложението е налично тук . Можете също да разглеждате изходния код на GitHub .
Качеството на този класификатор не е перфектно и не се преструвам, че е така. Разликата между данните, които използвахме за обучение, и данните, идващи от потребителския интерфейс, е огромна. Въпреки това създадохме работещо приложение от нулата за по-малко от 30 минути.
В процеса усъвършенствахме уменията си в четири области:
Не липсват потенциални случаи на използване на софтуер, способен да разпознава ръчно написани цифри, вариращи от образователен и административен софтуер до пощенски и финансови услуги.
Затова се надявам тази статия да ви мотивира да подобрите способностите си за машинно обучение, обработка на изображения и разработка отпред и отзад и да използвате тези умения за проектиране на прекрасни и полезни приложения.
Ако искате да разширите познанията си за машинно обучение и обработка на изображения, може да искате да разгледате нашите Урок за състезателно машинно обучение .
MNIST е един от най-популярните набори от данни от начално ниво в компютърното зрение. Съдържа хиляди изображения на ръкописни цифри.
Моделите за машинно обучение се учат от данни. За да направим модела достатъчно умен, трябва да предоставим данни, с които разполагаме, с очакваните резултати. Моделът ще използва тези данни, за да открие връзките между параметрите на данните и желания резултат.
Обработката на изображения е метод за извършване на някои операции върху изображение, за да се получи подобрено изображение или да се извлече полезна информация.