آموزش تشخیص اعداد یادگیری ماشینی


یادگیری ماشین، بینایی کامپیوتر، ساخت API های قدرتمند و ایجاد رابط های کاربری زیبا زمینه های هیجان انگیزی هستند که شاهد نوآوری های زیادی هستند.

دو مورد اول به ریاضیات و علوم گسترده نیاز دارند، در حالی که توسعه API و UI بر روی تفکر الگوریتمی و طراحی معماری های انعطاف پذیر متمرکز هستند. آنها بسیار متفاوت هستند، بنابراین تصمیم گیری در مورد اینکه کدام یک را می خواهید در مرحله بعد یاد بگیرید ممکن است چالش برانگیز باشد. هدف این مقاله نشان دادن این است که چگونه می توان از هر چهار مورد در ایجاد یک برنامه پردازش تصویر استفاده کرد.

برنامه‌ای که می‌خواهیم بسازیم یک تشخیص‌دهنده رقم ساده است. شما رسم می کنید، ماشین رقم را پیش بینی می کند. سادگی ضروری است زیرا به ما امکان می دهد به جای تمرکز بر جزئیات، تصویر بزرگ را ببینیم.

به خاطر سادگی، ما از محبوب ترین و آسان ترین فناوری ها استفاده خواهیم کرد. بخش یادگیری ماشینی از پایتون برای برنامه‌های بک‌اند استفاده می‌کند. در مورد جنبه تعاملی برنامه، ما از طریق یک کتابخانه جاوا اسکریپت کار خواهیم کرد که نیازی به معرفی ندارد: React.

یادگیری ماشینی برای حدس زدن ارقام

بخش اصلی برنامه ما الگوریتم حدس زدن عدد ترسیم شده است. یادگیری ماشین ابزاری خواهد بود که برای دستیابی به کیفیت حدس خوب استفاده می شود. این نوع هوش مصنوعی پایه به سیستم اجازه می دهد تا با مقدار معینی از داده ها به طور خودکار یاد بگیرد. به عبارتی گسترده تر، یادگیری ماشینی فرآیندی است برای یافتن یک تصادف یا مجموعه ای از تصادفات در داده ها برای تکیه بر آنها برای حدس زدن نتیجه.

فرآیند تشخیص تصویر ما شامل سه مرحله است:

  • تصاویری از ارقام ترسیم شده برای آموزش دریافت کنید
  • سیستم را آموزش دهید تا اعداد را از طریق داده های آموزشی حدس بزند
  • سیستم را با داده های جدید/ناشناخته تست کنید

محیط

ما به a نیاز خواهیم داشت محیط مجازی برای کار با یادگیری ماشین در پایتون. این رویکرد عملی است زیرا تمام بسته‌های پایتون مورد نیاز را مدیریت می‌کند، بنابراین نیازی نیست نگران آنها باشید.

بیایید آن را با دستورات ترمینال زیر نصب کنیم:

python3 -m venv virtualenv
source virtualenv/bin/activate

مدل آموزشی

قبل از شروع نوشتن کد، باید یک “معلم” مناسب برای ماشین های خود انتخاب کنیم. معمولاً متخصصان علوم داده مدل های مختلفی را قبل از انتخاب بهترین آنها امتحان می کنند. ما از مدل های بسیار پیشرفته که نیاز به مهارت زیادی دارند صرف نظر می کنیم و به آن ادامه می دهیم الگوریتم k-نزدیکترین همسایه.

این الگوریتمی است که برخی از نمونه‌های داده را دریافت می‌کند و آنها را در صفحه‌ای مرتب می‌کند که بر اساس مجموعه مشخصی از ویژگی‌ها مرتب شده‌اند. برای درک بهتر، تصویر زیر را مرور می کنیم:

تصویر: نمونه‌های داده‌های یادگیری ماشینی که در یک هواپیما مرتب شده‌اند

برای تشخیص نوع نقطه سبز، باید انواع را بررسی کنیم ک نزدیکترین همسایگان که در آن ک مجموعه آرگومان است. با توجه به تصویر بالا، اگر ک برابر با 1، 2، 3 یا 4 است، حدس یک خواهد بود مثلث سیاه به عنوان بسیاری از نقاط سبز نزدیکترین ک همسایگان مثلث سیاه هستند. اگر افزایش دهیم ک تا 5، اکثریت اشیاء مربع آبی هستند، بنابراین حدس a خواهد بود مربع آبی.

برای ایجاد مدل یادگیری ماشینی ما به برخی وابستگی ها نیاز است:

  • sklearn.neighbors.KNeighborsClassifier طبقه بندی کننده ای است که ما استفاده خواهیم کرد.
  • sklearn.model_selection.train_test_split تابعی است که به ما کمک می کند داده ها را به داده های آموزشی و داده های مورد استفاده برای بررسی درستی مدل تقسیم کنیم.
  • sklearn.model_selection.cross_val_score تابعی برای گرفتن علامت برای صحت مدل است. هر چه مقدار بالاتر باشد، صحت بهتر است.
  • sklearn.metrics.classification_report تابعی برای نشان دادن گزارش آماری از حدس های مدل است.
  • sklearn.datasets بسته ای است که برای دریافت داده ها برای آموزش (تصاویر ارقام) استفاده می شود.
  • بی حسی بسته ای است که به طور گسترده در علم مورد استفاده قرار می گیرد زیرا روشی سازنده و راحت برای دستکاری ساختارهای داده چند بعدی در پایتون ارائه می دهد.
  • matplotlib.pyplot بسته ای است که برای تجسم داده ها استفاده می شود.

بیایید با نصب و وارد کردن همه آنها شروع کنیم:

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()

با اجرای این کد نمودار زیر را نشان می دهد که دقت الگوریتم را با موارد مختلف توصیف می کند. ک ارزش های.

تصویر: نمودار مورد استفاده برای آزمایش دقت الگوریتم با مقادیر k مختلف.

همانطور که می بینید، الف ک مقدار 3 بهترین دقت را برای مدل و مجموعه داده ما تضمین می کند.

استفاده از Flask برای ساخت API

هسته برنامه، که الگوریتمی برای پیش بینی ارقام از تصاویر است، اکنون آماده است. در مرحله بعد، باید الگوریتم را با یک لایه 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)

یک بار دیگر با خطای واردات حل نشده مواجه خواهیم شد. را بازدیدها بسته متکی به سه فایل است که ما هنوز نداریم:

ما آنها را یکی یکی اجرا خواهیم کرد.

تنظیمات یک ماژول با تنظیمات و متغیرهای ثابت است. مسیر طبقه‌بندی‌کننده سریال را برای ما ذخیره می‌کند. یک سوال منطقی مطرح می کند: چرا باید طبقه بندی کننده را ذخیره کنم؟

زیرا این یک راه ساده برای بهبود عملکرد برنامه شما است. به‌جای آموزش طبقه‌بندی‌کننده هر بار که درخواستی دریافت می‌کنید، نسخه آماده‌شده طبقه‌بندی‌کننده را ذخیره می‌کنیم و به آن امکان می‌دهیم خارج از جعبه کار کند:

import os

BASE_DIR = os.getcwd()
CLASSIFIER_STORAGE = os.path.join(BASE_DIR, 'storage/classifier.txt')

مکانیسم تنظیمات – دریافت طبقه‌بندی‌کننده – در بسته بعدی لیست ما، یعنی، مقداردهی اولیه می‌شود مخزن. این یک کلاس با دو روش برای بازیابی و به‌روزرسانی طبقه‌بندی‌کننده آموزش‌دیده با استفاده از داخلی پایتون است. 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 خود نزدیک هستیم. در حال حاضر آن را فاقد تنها سرویس مدول. هدفش چیست؟

  • طبقه بندی کننده آموزش دیده را از ذخیره سازی دریافت کنید
  • تصویر ارسال شده از UI را به قالبی که طبقه بندی کننده می فهمد، تبدیل کنید
  • پیش بینی را با تصویر فرمت شده از طریق طبقه بندی محاسبه کنید
  • پیش بینی را برگردانید

بیایید این الگوریتم را کدگذاری کنیم:

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 برای عمل آماده است. اکنون می توانیم به مرحله پردازش تصویر برویم.

پردازش تصویر

پردازش تصویر روشی برای انجام عملیات خاصی بر روی یک تصویر برای بهبود آن یا استخراج اطلاعات مفید از آن است. در مورد ما، ما باید به آرامی تصویر ترسیم شده توسط کاربر را به قالب مدل یادگیری ماشین انتقال دهیم.

Image alt: تبدیل تصاویر ترسیم شده به فرمت یادگیری ماشین.

بیایید برای دستیابی به این هدف چند کمک وارد کنیم:

import numpy as np
from skimage import exposure
import base64
from PIL import Image, ImageOps, ImageChops
from io import BytesIO

ما می توانیم انتقال را به شش بخش مجزا تقسیم کنیم:

1. پس زمینه شفاف را با یک رنگ جایگزین کنید

Image alt: جایگزین کردن پس‌زمینه در یک تصویر نمونه.

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. اندازه تصویر را به فرمت 8×8 تغییر دهید

تصویر: تغییر اندازه تصویر نمونه به فرمت 8x8.

def resize_image(image):
    return image.resize((8, 8), Image.LINEAR)

اکنون می توانید برنامه را تست کنید. برنامه را اجرا کنید و دستور زیر را وارد کنید تا با آن درخواست ارسال شود این تصویر iStock به API:

تصویر: تصویر استوک یک عدد هشت که با دست کشیده شده است.

export FLASK_APP=app
flask run
curl " -X "POST" -H "Content-Type: application/json" -d "{\"image\": \"data:image/png;base64,$(curl " | 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 را به تصویر می‌کشد و برنامه ما به درستی آن را شناسایی کرده است.

ایجاد یک صفحه طراحی از طریق React

برای بوت استرپ سریع اپلیکیشن frontend، از آن استفاده خواهیم کرد دیگ بخار CRA:

create-react-app frontend
cd frontend

پس از راه اندازی محل کار، برای رسم ارقام نیز به وابستگی نیاز داریم. را واکنش نشان دادن بسته کاملاً با نیازهای ما مطابقت دارد:

npm i react-sketch

برنامه فقط یک جزء دارد. می توانیم این جزء را به دو قسمت تقسیم کنیم: منطق و دیدگاه.

قسمت view مسئول نمایش پنجره طراحی است، ارسال و بازنشانی کنید دکمه ها. هنگام تعامل، ما همچنین باید یک پیش بینی یا یک خطا را نشان دهیم. از منظر منطق وظایف زیر را بر عهده دارد: ارسال تصاویر و طرح را پاک کنید.

هر زمان که کاربر کلیک کند ارسال، کامپوننت تصویر را از مولفه طرح استخراج می کند و برای ماژول 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 (
    <div className="App" style={MAIN_CONTAINER_STYLE}>
      <div>
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Draw a digit</h1>
        </header>
        <div style={SKETCH_CONTAINER_STYLE}>
          <SketchField
            ref={sketchRef}
            width="100%"
            height="100%"
            tool={Tools.Pencil}
            imageFormat="jpg"
            lineColor="#111"
            lineWidth={10}
          />
        </div>
        {prediction && <h3>Predicted value is: {prediction}</h3>}
        <button onClick={handleClear}>Clear</button>
        <button onClick={handleSubmit}>Guess the number</button>
        {error && <p style={{ color: "red" }}>Something went wrong</p>}
      </div>
    </div>
  );
};

export default App;

کامپوننت آماده است، آن را با اجرا و رفتن به آن تست کنید localhost:3000 بعد از:

npm run start

برنامه آزمایشی موجود است اینجا. شما همچنین می توانید کد منبع را مرور کنید GitHub.

بسته بندی

کیفیت این طبقه بندی کننده بی نقص نیست و من چنین وانمود نمی کنم که اینطور است. تفاوت بین داده‌هایی که برای آموزش استفاده می‌کنیم و داده‌هایی که از UI می‌آیند بسیار زیاد است. با وجود آن، ما یک برنامه کاربردی از ابتدا در کمتر از 30 دقیقه ایجاد کردیم.

تصویر: انیمیشنی که برنامه نهایی شده را نشان می دهد که ارقام دست نویس را شناسایی می کند.

در این فرآیند، ما مهارت های خود را در چهار زمینه تقویت کردیم:

  • فراگیری ماشین
  • توسعه Back-end
  • پردازش تصویر
  • توسعه Frontend

هیچ کمبودی در موارد استفاده بالقوه برای نرم افزارهایی که قادر به تشخیص ارقام دست نویس هستند، از نرم افزار آموزشی و اداری گرفته تا خدمات پستی و مالی وجود ندارد.

بنابراین، امیدوارم این مقاله به شما انگیزه دهد تا توانایی های یادگیری ماشینی، پردازش تصویر، و توسعه front-end و back-end خود را بهبود ببخشید و از این مهارت ها برای طراحی برنامه های کاربردی فوق العاده و مفید استفاده کنید.

اگر می خواهید دانش خود را در مورد یادگیری ماشینی و پردازش تصویر گسترش دهید، ممکن است بخواهید آموزش یادگیری ماشین خصمانه ما را بررسی کنید.



منبع

Matthew Newman

Matthew Newman Matthew has over 15 years of experience in database management and software development, with a strong focus on full-stack web applications. He specializes in Django and Vue.js with expertise deploying to both server and serverless environments on AWS. He also works with relational databases and large datasets
[ Back To Top ]