Makine Öğrenimi Numara Tanıma - Sıfırdan Uygulamaya
Yayınlanan: 2022-03-11Makine öğrenimi, bilgisayarla görme, güçlü API'ler oluşturma ve güzel kullanıcı arayüzleri oluşturma, pek çok yeniliğe tanık olan heyecan verici alanlardır.
İlk ikisi kapsamlı matematik ve bilim gerektirirken, API ve UI geliştirme merkezi algoritmik düşünme ve esnek mimariler tasarlama üzerine kuruludur. Çok farklılar, bu yüzden hangisini öğrenmek istediğinize karar vermek zor olabilir. Bu makalenin amacı, bir görüntü işleme uygulaması oluştururken dördünün de nasıl kullanılabileceğini göstermektir.
Yapacağımız uygulama basit bir rakam tanıyıcı. Siz çizersiniz, makine rakamı tahmin eder. Basitlik esastır çünkü ayrıntılara odaklanmak yerine büyük resmi görmemizi sağlar.
Basitlik adına, en popüler ve öğrenmesi kolay teknolojileri kullanacağız. Makine öğrenimi bölümü, arka uç uygulaması için Python kullanacaktır. Uygulamanın etkileşim yönüne gelince, giriş gerektirmeyen bir JavaScript kitaplığı aracılığıyla çalışacağız: React.
Rakamları Tahmin Etmek için Makine Öğrenimi
Uygulamamızın temel kısmı, çizilen sayıyı tahmin eden algoritmadır. Makine öğrenimi, iyi bir tahmin kalitesi elde etmek için kullanılan araç olacaktır. Bu tür temel yapay zeka, bir sistemin belirli bir miktarda veri ile otomatik olarak öğrenmesini sağlar. Daha geniş anlamda, makine öğrenimi, sonucu tahmin etmek için bunlara güvenmek için verilerde bir tesadüf veya tesadüfler kümesi bulma sürecidir.
Görüntü tanıma sürecimiz üç adımdan oluşur:
- Eğitim için çizilen rakamların resimlerini alın
- Eğitim verileri aracılığıyla sayıları tahmin etmek için sistemi eğitin
- Sistemi yeni/bilinmeyen verilerle test edin
Çevre
Python'da makine öğrenimi ile çalışmak için sanal bir ortama ihtiyacımız olacak. Bu yaklaşım pratiktir çünkü gerekli tüm Python paketlerini yönetir, bu yüzden onlar için endişelenmenize gerek yoktur.
Aşağıdaki terminal komutları ile kurulumunu yapalım:
python3 -m venv virtualenv source virtualenv/bin/activate
Eğitim Modeli
Kodu yazmaya başlamadan önce, makinelerimiz için uygun bir “öğretmen” seçmemiz gerekiyor. Genellikle, veri bilimi uzmanları en iyisini seçmeden önce farklı modeller dener. Çok fazla beceri gerektiren çok gelişmiş modelleri atlayıp k-en yakın komşu algoritması ile ilerleyeceğiz.
Bazı veri örneklerini alan ve bunları belirli bir dizi özelliğe göre sıralanmış bir düzlemde düzenleyen bir algoritmadır. Daha iyi anlamak için aşağıdaki görseli inceleyelim:
Yeşil Noktanın türünü saptamak için, k'nin bağımsız değişken kümesi olduğu k en yakın komşu türlerini kontrol etmeliyiz. Yukarıdaki görüntü göz önüne alındığında, k 1, 2, 3 veya 4'e eşitse, yeşil noktanın en yakın k komşularının çoğu siyah üçgen olduğundan, tahmin bir Siyah Üçgen olacaktır. Eğer k'yi 5'e çıkarırsak, nesnelerin çoğu mavi karelerdir, dolayısıyla tahminimiz Mavi Kare olacaktır.
Makine öğrenimi modelimizi oluşturmak için gereken bazı bağımlılıklar vardır:
- sklearn.neighbors.KNeighborsClassifier kullanacağımız sınıflandırıcıdır.
- sklearn.model_selection.train_test_split , verileri eğitim verilerine ve modelin doğruluğunu kontrol etmek için kullanılan verilere bölmemize yardımcı olacak işlevdir.
- sklearn.model_selection.cross_val_score , modelin doğruluğu için bir işaret alma işlevidir. Değer ne kadar yüksek olursa, doğruluk o kadar iyi olur.
- sklearn.metrics.classification_report , modelin tahminlerinin istatistiksel bir raporunu gösterme işlevidir.
- sklearn.datasets , eğitim için veri almak için kullanılan pakettir (rakam görüntüleri).
- numpy , Python'da çok boyutlu veri yapılarını işlemek için verimli ve rahat bir yol sunduğu için bilimde yaygın olarak kullanılan bir pakettir.
- matplotlib.pyplot , verileri görselleştirmek için kullanılan pakettir.
Hepsini yükleyip içe aktararak başlayalım:
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
Şimdi MNIST Veritabanını yüklememiz gerekiyor. MNIST, makine öğrenimi alanında binlerce acemi tarafından kullanılan, elle yazılmış görsellerden oluşan klasik bir veri kümesidir:
digits = load_digits()
Veriler alınıp hazır olduğunda, verileri iki parçaya ayırmanın bir sonraki adımına geçebiliriz: eğitim ve test .
Modelimizi rakamları tahmin etmek üzere eğitmek için verilerin %75'ini kullanacağız ve kalan verileri modelin doğruluğunu test etmek için kullanacağız:
(X_train, X_test, y_train, y_test) = train_test_split( digits.data, digits.target, test_size=0.25, random_state=42 )
Veriler şimdi düzenlendi ve onu kullanmaya hazırız. Tahminlerin daha kesin olması için modelimiz için en iyi parametre k'yi bulmaya çalışacağız. Modeli farklı k değerleri ile değerlendirmek zorunda olduğumuz için bu aşamada k değerini aklımızdan çıkaramayız.
Bir dizi k değerini dikkate almanın neden önemli olduğunu ve bunun modelimizin doğruluğunu nasıl geliştirdiğini görelim:
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()
Bu kodu çalıştırmak, algoritmanın doğruluğunu farklı k değerleriyle açıklayan aşağıdaki grafiği size gösterecektir.
Gördüğünüz gibi, k değeri 3, modelimiz ve veri kümemiz için en iyi doğruluğu sağlar.
API Oluşturmak için Flask'ı Kullanma
Görüntülerden rakamları tahmin eden bir algoritma olan uygulama çekirdeği artık hazır. Ardından, kullanıma hazır hale getirmek için algoritmayı bir API katmanı ile dekore etmemiz gerekiyor. Bunu temiz ve özlü bir şekilde yapmak için popüler Flask web çerçevesini kullanalım.
Flask'ı ve sanal ortamda görüntü işlemeyle ilgili bağımlılıkları kurarak başlayacağız:
pip install Flask Pillow scikit-image
Kurulum tamamlandığında, uygulamanın giriş noktası dosyasının oluşturulmasına geçiyoruz:
touch app.py
Dosyanın içeriği şöyle görünecektir:
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
ve IndexView
tanımlı olmadığını söyleyen bir hata alacaksınız. Sonraki adım, bu görünümleri başlatacak bir dosya oluşturmaktır:
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)
Yine çözülmemiş bir içe aktarma hatasıyla karşılaşacağız. Görünümler paketi henüz elimizde olmayan üç dosyaya dayanır:
- Ayarlar
- depo
- Hizmet
Bunları tek tek uygulayacağız.
Ayarlar , konfigürasyonları ve sabit değişkenleri olan bir modüldür. Bizim için serileştirilmiş sınıflandırıcının yolunu saklayacaktır. Mantıklı bir soru soruyor: Sınıflandırıcıyı neden kaydetmem gerekiyor?
Çünkü uygulamanızın performansını artırmanın basit bir yolu. Her istek aldığınızda sınıflandırıcıyı eğitmek yerine, sınıflandırıcının hazır sürümünü depolayarak kutudan çıkar çıkmaz çalışmasını sağlarız:
import os BASE_DIR = os.getcwd() CLASSIFIER_STORAGE = os.path.join(BASE_DIR, 'storage/classifier.txt')
Ayarlar mekanizması - sınıflandırıcıyı alma - listemizdeki bir sonraki paket olan Repo'da başlatılacaktır. Python'un yerleşik pickle
modülünü kullanarak eğitimli sınıflandırıcıyı almak ve güncellemek için iki yöntemi olan bir sınıftır:
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'mizi tamamlamaya yaklaştık. Artık yalnızca Servis modülünden yoksundur. Amacı ne?
- Depodan eğitimli sınıflandırıcıyı alın
- Kullanıcı arabiriminden geçirilen görüntüyü sınıflandırıcının anlayacağı bir biçime dönüştürün
- Sınıflandırıcı aracılığıyla biçimlendirilmiş görüntü ile tahmini hesaplayın
- Tahmini döndür
Bu algoritmayı kodlayalım:
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
Burada PredictDigitService
iki bağımlılığı olduğunu görebilirsiniz: ClassifierFactory
ve process_image
.

Modelimizi oluşturmak ve eğitmek için bir sınıf oluşturarak başlayacağız:
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 eyleme hazır. Artık görüntü işleme adımına geçebiliriz.
Görüntü işleme
Görüntü işleme, bir görüntüyü geliştirmek veya ondan bazı yararlı bilgiler çıkarmak için bir görüntü üzerinde belirli işlemleri gerçekleştirme yöntemidir. Bizim durumumuzda, bir kullanıcı tarafından çizilen görüntüyü sorunsuz bir şekilde makine öğrenme modeli formatına geçirmemiz gerekiyor.
Bu hedefe ulaşmak için bazı yardımcıları içe aktaralım:
import numpy as np from skimage import exposure import base64 from PIL import Image, ImageOps, ImageChops from io import BytesIO
Geçişi altı ayrı bölüme ayırabiliriz:
1. Saydam bir arka planı bir renkle değiştirin
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. Açık sınırları kırpın
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. Eşit boyutta kenarlıklar ekleyin
def pad_image(image): return ImageOps.expand(image, border=30, fill='#fff')
4. Görüntüyü gri tonlama moduna dönüştürün
def to_grayscale(image): return image.convert('L')
5. Renkleri ters çevir
def invert_colors(image): return ImageOps.invert(image)
6. Resmi 8x8 formatında yeniden boyutlandırın
def resize_image(image): return image.resize((8, 8), Image.LINEAR)
Şimdi uygulamayı test edebilirsiniz. Uygulamayı çalıştırın ve bu iStock görüntüsüyle API'ye bir istek göndermek için aşağıdaki komutu girin:
export FLASK_APP=app flask run
curl "http://localhost:5000/api/predict" -X "POST" -H "Content-Type: application/json" -d "{\"image\": \"data:image/png;base64,$(curl "https://media.istockphoto.com/vectors/number-eight-8-hand-drawn-with-dry-brush-vector-id484207302?k=6&m=484207302&s=170667a&w=0&h=s3YANDyuLS8u2so-uJbMA2uW6fYyyRkabc1a6OTq7iI=" | base64)\"}" -i
Aşağıdaki çıktıyı görmelisiniz:
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
Örnek resim 8 sayısını gösteriyordu ve uygulamamız onu doğru bir şekilde tanımladı.
React Yoluyla Çizim Bölmesi Oluşturma
Ön uç uygulamasını hızlı bir şekilde başlatmak için CRA ortak plakasını kullanacağız:
create-react-app frontend cd frontend
İşyerini kurduktan sonra, rakam çizmek için de bir bağımlılığa ihtiyacımız var. Reaksiyon-skeç paketi ihtiyaçlarımızı mükemmel bir şekilde karşılıyor:
npm i react-sketch
Uygulamanın yalnızca bir bileşeni vardır. Bu bileşeni iki kısma ayırabiliriz: mantık ve görünüm .
Görünüm bölümü, çizim bölmesini, Gönder ve Sıfırla düğmelerini temsil etmekten sorumludur. Etkileşime girdiğimizde, bir tahmin veya hatayı da temsil etmeliyiz. Mantıksal açıdan, aşağıdaki görevleri vardır: görüntüleri göndermek ve taslağı temizlemek .
Bir kullanıcı Gönder'i tıkladığında, bileşen çizim bileşeninden görüntüyü çıkarır ve API modülünün makePrediction
işlevine başvurur. Arka uca yapılan istek başarılı olursa, tahmin durumu değişkenini ayarlayacağız. Aksi takdirde, hata durumunu güncelleyeceğiz.
Bir kullanıcı Reset'e tıkladığında çizim temizlenir:
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 }
Mantık yeterlidir. Şimdi görsel arayüzü buna ekleyebiliriz:
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;
Bileşen hazır, çalıştırarak ve aşağıdakilerden sonra localhost:3000
giderek test edin:
npm run start
Demo uygulaması burada mevcuttur. Kaynak koduna GitHub'da da göz atabilirsiniz.
Toplama
Bu sınıflandırıcının kalitesi mükemmel değil ve öyle olduğunu iddia etmiyorum. Eğitim için kullandığımız verilerle kullanıcı arayüzünden gelen veriler arasındaki fark çok büyük. Buna rağmen 30 dakikadan daha kısa bir sürede sıfırdan çalışan bir uygulama oluşturduk.
Bu süreçte becerilerimizi dört alanda geliştirdik:
- Makine öğrenme
- Arka uç geliştirme
- Görüntü işleme
- Ön uç geliştirme
Eğitim ve idari yazılımlardan posta ve finansal hizmetlere kadar, el yazısı rakamları tanıyabilen yazılımlar için potansiyel kullanım örnekleri sıkıntısı yoktur.
Bu nedenle, umarım bu makale sizi makine öğrenimi becerilerinizi, görüntü işlemeyi ve ön uç ve arka uç geliştirmenizi geliştirmek ve bu becerileri harika ve kullanışlı uygulamalar tasarlamak için kullanmak için motive edecektir.
Makine öğrenimi ve görüntü işleme konusundaki bilginizi genişletmek istiyorsanız, Çelişkili Makine Öğrenimi Eğitimimize göz atmak isteyebilirsiniz.