تشخیص اعداد دست نویس با پایتون: فرض کنید از شما خواسته شده یک پروژه مبتنی بر یادگیری ماشین یا یادگیری عمیق بنویسید که بتواند اعداد دست نویس را تشخیص دهد و اگر برایش عددی نوشتید به شما بگوید که آن عدد چیست. برای این کار کافیه از پروژه زیر استفاده کنید:
فایل پروژه:
فایل کامل پروژه را از اینجا می توانید دانلود کنید.
نیازهای پروژه:
۱- نصب کتابخانه های مورد نیاز:
pip install numpy, tensorflow, keras, pillow,
۲- تهیه دیتاست MNIST که مخصوص یادگیری ماشینه.
مراحل انجام پروژه:
۱- اضافه کردن تابخانه ها و اجرای دیتاست:
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import backend as K
# the data, split between train and test sets
(x_train, y_train), (x_test, y_test) = mnist.load_data()
print(x_train.shape, y_train.shape)
۲- پردازش اولیه دیتا:
x_train = x_train.reshape(x_train.shape[0], 28, 28, 1)
x_test = x_test.reshape(x_test.shape[0], 28, 28, 1)
input_shape = (28, 28, 1)
# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')
۳- ایجاد مدل یادگیری:
batch_size = 128
num_classes = 10
epochs = 10
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3),activation='relu',input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(256, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))
model.compile(loss=keras.losses.categorical_crossentropy,optimizer=keras.optimizers.Adadelta(),metrics=['accuracy'])
۴- آموزش مدل:
hist = model.fit(x_train, y_train,batch_size=batch_size,epochs=epochs,verbose=1,validation_data=(x_test, y_test))
print("The model has successfully trained")
model.save('mnist.h5')
print("Saving the model as mnist.h5")
۵- ارزیابی مدل:
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
۶- ساخت GUI یا پنل کاربر برای پیش بینی اعداد نوشته شده:
from keras.models import load_model
from tkinter import *
import tkinter as tk
import win32gui
from PIL import ImageGrab, Image
import numpy as np
model = load_model('mnist.h5')
def predict_digit(img):
#resize image to 28x28 pixels
img = img.resize((28,28))
#convert rgb to grayscale
img = img.convert('L')
img = np.array(img)
#reshaping to support our model input and normalizing
img = img.reshape(1,28,28,1)
img = img/255.0
#predicting the class
res = model.predict([img])[0]
return np.argmax(res), max(res)
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.x = self.y = 0
# Creating elements
self.canvas = tk.Canvas(self, width=300, height=300, bg = "white", cursor="cross")
self.label = tk.Label(self, text="Thinking..", font=("Helvetica", 48))
self.classify_btn = tk.Button(self, text = "Recognise", command = self.classify_handwriting)
self.button_clear = tk.Button(self, text = "Clear", command = self.clear_all)
# Grid structure
self.canvas.grid(row=0, column=0, pady=2, sticky=W, )
self.label.grid(row=0, column=1,pady=2, padx=2)
self.classify_btn.grid(row=1, column=1, pady=2, padx=2)
self.button_clear.grid(row=1, column=0, pady=2)
#self.canvas.bind("<Motion>", self.start_pos)
self.canvas.bind("<B1-Motion>", self.draw_lines)
def clear_all(self):
self.canvas.delete("all")
def classify_handwriting(self):
HWND = self.canvas.winfo_id() # get the handle of the canvas
rect = win32gui.GetWindowRect(HWND) # get the coordinate of the canvas
im = ImageGrab.grab(rect)
digit, acc = predict_digit(im)
self.label.configure(text= str(digit)+', '+ str(int(acc*100))+'%')
def draw_lines(self, event):
self.x = event.x
self.y = event.y
r=8
self.canvas.create_oval(self.x-r, self.y-r, self.x + r, self.y + r, fill='black')
app = App()
mainloop()
۷- اجرای کد و خروجی آن:
توضیحات کامل پروژه:
پس از آنکه فایل پروژه را از اینجا دانلود کردید آن را از حالت فشرده خارج کنید که محتویات آن به صورت زیر است:
برای اجرا کردن پروژه کلمه cmd را در نوار مسیر فایل در تصویر بالا بنویسید و enter را بزنید تا صفحه Command Prompt در مسیر پروژه اجرا شود:( تصویر زیر):
در صفحه md باز شده عبارت python gui_digit_recognizer.py را بنویسید و اجرا کنید:
اگر همه کتابخانه ها و ابزارهارو از قبل نصب کرده باشید پروژه با موفقیت اجرا و صفحه رابط کاربری اجرا می شود که می تونید با قلم روی صفحه عدد رو بنویسید تا برنامه اونو تشخیص بده (شکل زیر):
اما اگه با خطاهای زیر مواجه شدید یعنی کتابخونه ها و افزونه ها روی سیستم شما هنوز نصب نشدن:
برای نصب افزونه ها توی همین محیط cmd افزونه های زیر رو با دستور pip install نصب کنید:
pip install tensorflow
pip install numpy
pip install keras
pip install pillow
بعد از نصب موارد بالا دوباره برنامه رو اجرا کنید اینبار باید پنل برنامه بالا بیاد
حالا بریم سراغ توضیحات کد:
توضیحات کد فایل : gui_digit_recognizer.py
محتوای کد:
from keras.models import load_model
from tkinter import *
import tkinter as tk
import win32gui
from PIL import ImageGrab, Image
import numpy as np
model = load_model('mnist.h5')
def predict_digit(img):
#resize image to 28x28 pixels
img = img.resize((28,28))
#convert rgb to grayscale
img = img.convert('L')
img = np.array(img)
#reshaping to support our model input and normalizing
img = img.reshape(1,28,28,1)
img = img/255.0
#predicting the class
res = model.predict([img])[0]
return np.argmax(res), max(res)
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.x = self.y = 0
# Creating elements
self.canvas = tk.Canvas(self, width=300, height=300, bg = "white", cursor="cross")
self.label = tk.Label(self, text="Draw..", font=("Helvetica", 48))
self.classify_btn = tk.Button(self, text = "Recognise", command = self.classify_handwriting)
self.button_clear = tk.Button(self, text = "Clear", command = self.clear_all)
# Grid structure
self.canvas.grid(row=0, column=0, pady=2, sticky=W, )
self.label.grid(row=0, column=1,pady=2, padx=2)
self.classify_btn.grid(row=1, column=1, pady=2, padx=2)
self.button_clear.grid(row=1, column=0, pady=2)
#self.canvas.bind("<Motion>", self.start_pos)
self.canvas.bind("<B1-Motion>", self.draw_lines)
def clear_all(self):
self.canvas.delete("all")
def classify_handwriting(self):
HWND = self.canvas.winfo_id() # get the handle of the canvas
rect = win32gui.GetWindowRect(HWND) # get the coordinate of the canvas
a,b,c,d = rect
rect=(a+4,b+4,c-4,d-4)
im = ImageGrab.grab(rect)
digit, acc = predict_digit(im)
self.label.configure(text= str(digit)+', '+ str(int(acc*100))+'%')
def draw_lines(self, event):
self.x = event.x
self.y = event.y
r=8
self.canvas.create_oval(self.x-r, self.y-r, self.x + r, self.y + r, fill='black')
app = App()
mainloop()
توضیحات خط به خط کد بالا:
خط۱: اضافه کردن load_model از مدلهای keras : از این افزونه برای load کردن یا save کردن مدلها استفاده میشه.
خط ۲ تا ۴: برای اضافه کردن tkinter که یک ابزار برای ساخت gui یا پنل کاربری است.
خط۵: اضافه کردن افزونه pil یا همون pillow که یک کتابخونه برای تصاویر است که به وسیله اون میشه تصاویر رو ادیت کرد.
خط۶: اضافه کردن کتابخونه numpy با نام np
خط۸: لود کردن یا اضافه کردن مدل (در واقع این همون فایل train_digit_recognizer هست که ما تبدیل به یک مدل کردیم تا ازش اینجا استفاده کنیم)
خط ۱۰: تعریف یک تابع با نام predict_digit که ورودی اون یک متغیر با نام img هست (در واقع ورودیش یه تصویر یا عکسه)
خط۱۲: با استفاده از دستور img.resize ابعاد تصویر رو به ۲۸ در ۲۸ پیکسل تبدیل می کنیم.
خط۱۳: با استفاده از دستور img.convert(‘L’) تصویر رنگی رو به خاکستری تبدیل می کنیم.
خط۱۵: با استفاده از دستور np.array تصویر را به یک آرایه دوبعدی تبدیل می کنیم و دوباره درون متغیر img می ریزیم.
خط۱۷: تبدیل img مرحله قبل که ۲ بعدی بود به یک آرایه ۴ بعدی برای اینکه بتوان به عنوان ورودی مدل استفاده کرد و اونو نرمالیزه کرد.
خط۱۸: تبدیل img مرحله قبل به بازه عددی ۰ تا ۱ برای استفاده در مدل
خط۲۰: در این خط با دستور model.predict که ورودی img رو میگیره کلاس یا class فایل رو تشخیص میدیم
خط۲۱: با استفاده از دستور np.argmax ما میتونیم در یک آرایه که در اینجا همون res هست محلی که بیشترین مقدار درون اونه رو پیدا کنیم مثلا آرایه ما اینجوریه: [۱,۲,۳] محل بزرگترین مقدار ایندکس یا محل [۲] هست. و با دستور max هم میتونیم ماکزیمم مقدار در یک آرایه رو بدست بیاریم مثلا در آرایه بالا max برابر ۳ است. و در نهایت با دستور return ما این دو مقدار رو به عنوان خروجی تابع ارسال می کنیم.
ادامه دارد..