Moje zadanie polega na nauczeniu 10 perceptronów do rozpoznawania cyfr (0-9). Każdy perceptron powinien nauczyć się jednej cyfry. Jako dane treningowe stworzyłem 30 obrazków (5x7 bmp). Są 3 warianty dla każdej cyfry.
Mam klasę perceptronu:
import numpy as np
def unit_step_func(x):
return np.where(x > 0, 1, 0)
def sigmoid(x):
return 1 / (1 + np.exp(-x))
class Perceptron:
def __init__(self, learning_rate=0.01, n_iters=1000):
self.lr = learning_rate
self.n_iters = n_iters
self.activation_func = unit_step_func
self.weights = None
self.bias = None
#self.best_weights = None
#self.best_bias = None
#self.best_error = float('inf')
def fit(self, X, y):
n_samples, n_features = X.shape
self.weights = np.zeros(n_features)
self.bias = 0
#self.best_weights = self.weights.copy()
#self.best_bias = self.bias
for _ in range(self.n_iters):
for x_i, y_i in zip(X, y):
linear_output = np.dot(x_i, self.weights) + self.bias
y_predicted = self.activation_func(linear_output)
update = self.lr * (y_i - y_predicted)
self.weights += update * x_i
self.bias += update
#current_error = np.mean(np.abs(y - self.predict(X)))
#if current_error < self.best_error:
# self.best_weights = self.weights.copy()
# self.best_bias = self.bias
# self.best_error = current_error
def predict(self, X):
linear_output = np.dot(X, self.weights) + self.bias
y_predicted = self.activation_func(linear_output)
return y_predicted
Próbowałem zarówno funkcji aktywacji unit_step_func, jak i sigmoid, oraz algorytmu kieszeniowego, aby sprawdzić, czy istnieje jakaś różnica. Jestem noobem, więc nie jestem pewien, czy to jest nawet poprawnie zaimplementowane.
Tak trenuję te perceptrony:
import numpy as np
from PIL import Image
from Perceptron import Perceptron
import os
def load_images_from_folder(folder, digit):
images = []
labels = []
for filename in os.listdir(folder):
img = Image.open(os.path.join(folder, filename))
if img is not None:
images.append(np.array(img).flatten())
label = 1 if filename.startswith(f"{digit}_") else 0
labels.append(label)
return np.array(images), np.array(labels)
digits_to_recognize = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
perceptrons = []
for digit_to_recognize in digits_to_recognize:
X, y = load_images_from_folder("data", digit_to_recognize)
p = Perceptron()
p.fit(X, y)
perceptrons.append(p)
W skrócie: nazwa pliku danych treningowych ma format cyfra_wariant. Jak wspomniałem wcześniej, dla każdej cyfry są 3 warianty.
Funkcja load_images_from_folder
wczytuje 30 obrazków i sprawdza nazwę. Jeśli część cyfra
nazwy jest taka sama jak cyfra wejściowa, to dodaje 1 do etykiet, aby perceptron wiedział, że to pożądana cyfra.
Dla cyfry 0 tablica etykiet to [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Dla cyfry 1 tablica etykiet to [0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
itd.
Następnie trenuję 10 perceptronów przy użyciu tych danych.
To ćwiczenie wymaga również stworzenia jakiegoś interfejsu graficznego, który pozwala mi rysować cyfrę. Wybrałem pygame, ale mogłem użyć pyQT, to faktycznie nie ma znaczenia.
Oto kod, możesz go pominąć, nie jest ważny (oprócz funkcji on_rec_button
, ale do tego wrócimy):
import pygame
import sys
pygame.init()
cols, rows = 5, 7
square_size = 50
width, height = cols * square_size, (rows + 2) * square_size
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Zad1")
rec_button_color = (0, 255, 0)
rec_button_rect = pygame.Rect(0, rows * square_size, width, square_size)
clear_button_color = (255, 255, 0)
clear_button_rect = pygame.Rect(0, (rows + 1) * square_size + 1, width, square_size)
mouse_pressed = False
drawing_matrix = np.zeros((rows, cols), dtype=int)
def color_square(x, y):
col = x // square_size
row = y // square_size
if 0 <= row < rows and 0 <= col < cols:
drawing_matrix[row, col] = 1
def draw_button(color, rect):
pygame.draw.rect(screen, color, rect)
def on_rec_button():
np_array_representation = drawing_matrix.flatten()
for digit_to_recognize in digits_to_recognize:
p = perceptrons[digit_to_recognize]
predicted_number = p.predict(np_array_representation)
if predicted_number == digit_to_recognize:
print(f"Image has been recognized as number {digit_to_recognize}")
def on_clear_button():
drawing_matrix.fill(0)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 3:
mouse_pressed = True
elif event.type == pygame.MOUSEBUTTONUP and event.button == 3:
mouse_pressed = False
elif event.type == pygame.MOUSEMOTION:
mouse_x, mouse_y = event.pos
if mouse_pressed:
color_square(mouse_x, mouse_y)
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
if rec_button_rect.collidepoint(event.pos):
on_rec_button()
if clear_button_rect.collidepoint(event.pos):
on_clear_button()
for i in range(rows):
for j in range(cols):
if drawing_matrix[i, j] == 1:
pygame.draw.rect(screen, (255, 0, 0), (j * square_size, i * square_size, square_size, square_size))
else:
pygame.draw.rect(screen, (0, 0, 0), (j * square_size, i * square_size, square_size, square_size))
draw_button(rec_button_color, rec_button_rect)
draw_button(clear_button_color, clear_button_rect)
pygame.display.flip()
Teraz, gdy uruchomiłem aplikację, narysowałem cyfrę 3
i kliknąłem zielony przycisk uruchamiający funkcję on_rec_button
, spodziewałem się zobaczyć "Obraz został rozpoznany jako cyfra 3", ale otrzymuję "Obraz został rozpoznany jako cyfra 0".
To jest to, co narysowałem:
To są dane treningowe:
Gdy rysuję cyfrę 1
, otrzymuję 2 wyniki: "Obraz został rozpoznany jako cyfra 0" i "Obraz został rozpoznany jako cyfra 1".
Co powinienem zrobić, aby działało tak, jak chcę? Nie oczekuję, że będzie to działać z dokładnością 100%, ale myślę, że może być lepiej.