Wstęp
Jest to pierwsza część serii mająca na celu pokazać, że rozpoczęcie przygody z Deep Learning wcale nie jest takie trudne. Na przestrzeni ostatnich kilku lat pojawiło się wiele rozwiązań, które znacząco obniżyły próg wejścia. Powstało wiele bibliotek, dzięki którym możemy zaprojektować sieć neuronową nie musząc koniecznie znać wszystkich detali na temat implementowanej sieci (choćby tego jak działa algorytm wstecznej propagacji błędu, na którym opiera się proces uczenia, a wymaga pewnej wiedzy z zakresu analizy matematycznej i algebry).
Przykładem takich bibliotek są np: Oprócz tego torch, Deeplearning4j, MxNet, Microsoft Cognitive Toolkit.Problem
Problemem jakim się zajmiemy będzie rozpoznawanie obrazów, a dokładnie ręcznie pisanych cyfr. Dane pochodzą z popularnego zbioru MNIST. Zawiera on 70000 danych, z czego 60000 to dane treningowe, a 10000 to dane testowe. Każda dana to po prostu obrazek o rozdzielczości 28x28 reprezentujący cyfrę. Każdy taki obrazek ma też etykietę - czyli informację jakiej dokładnie cyfry on dotyczy. Celem jest wykorzystanie zbioru treningowego do nauczenia się rozpoznawania cyfr. Skuteczność klasyfikacji będzie potem sprawdzana na zbiorze testowym, czyli porównane będą wyniki uzyskane przez sieć z tymi rzeczywistymi.
Rozwiązanie
Rozwiązanie zostało zaimplementowane w języku python przy pomocy biblioteki keras, która jest wrapperem do biblioteki tensorflow (sam tensorflow jest nieco trudniejszy, gdyż więcej "jest na naszej głowie"; biblioteka keras została tak zaprojektowana, żeby maksymalnie uprościć implementację sieci). Cały kod rozwiązujący problem znajduje się poniżej. Jest to implementacja konwolucyjnej sieci neuronowej. W dalszej części znajduje się omówienie poszczególnych fragmentów.
Opis
Pierwsze kilka linii to potrzebne importy, oprócz tych związanych z biblioteką keras dodana tu została biblioteka matplotlib, za pomocą której będzie można wizualizować dane.
Następnie mamy deklarację kilku stałych: num_classes
- liczba klas (czyli w tym przypadku liczba możliwych cyfr), image_rows
, image_cols
- wymiar obrazka, input_shape
- rozmiar używamy przez pierwszą konwolucyjną warstwę sieci neuronowej.
Wczytujemy dane ze zbioru MNIST.
Za pomocą poniższego kodu możemy dokonać wizualizacji wybranej danej treningowej. Sprawdźmy pierwszą.
Wynik jest następujący. Rzeczywiście jest to cyfra o wymiarach 28x28.
Musimy jeszcze przygotować dane, tak aby były zgodne z tym czego oczekuje sieć neuronowa. Najpierw dokonujemy odpowiedniego przekształcenia rozmiaru, następnie zmieniamy typ z całkowitego na zmiennoprzecinkowy. Na koniec jeszcze dokonujemy normalizacji, czyli zamieniamy zakres od 0-255
na 0-1
, po prostu dzieląc wszystkie wartości przez 255.
Kolejny krok to zmiana formatu danych wynikowych zgodnie na tzw one-hot encoding
. Każda klasa (czyli wartość liczby przypisana do obrazka) będzie zamieniona na wektor 10-elementowy, zgodnie z poniższą zasadą:
0 -> [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
1 -> [0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
2 -> [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
...
Po prostu sieć oczekuje danych wynikowych w takim formacie.
Teraz zajmiemy się architekturą samej sieci. Jak zostało wcześniej wspomniane mamy tutaj do czynienia z siecią konwolucyjną, której głównym zastosowaniem jest właśnie klasyfikacja obrazów. Po dwóch warstwach konwolucyjnych mamy tzw warstwę MaxPooling2D
, która służy do "zebrania wyników" z poprzedniej warstwy. Dense
to normalna warstwa (z wszystkimi połączeniami między neuronami), natomiast Dropout
to metoda regularyzacji, polegajaca na usuwaniu pewnych połączeń pomiędzy neuronami aby przeciwdziałać tzw przeuczeniu sieci (obrazowo: jeśli sieć neuronowa miałaby rozpoznawać samochody i nauczyłaby się rozpoznawać Mercedesy na podstawie tylko znaczka z przodu, to jeśli by go nie było, to nagle sieć mogłaby nie poradzić sobie z rozpoznaniem, dzięki dropout sieć stara się dostrzegać niezależne cechy i brak jednej z nich nie jest wtedy już problemem).
Poniżej mamy proces kompilacji sieci. Określamy tutaj jaka funkcja będzie używana do obliczania błędu pomiędzy danymi zwróconymi przez sieć oraz danymi rzeczywistymi (categorical_crossentropy
), optymalizator, który służy do jak najlepszej aktualizacji wag w procesie uczenia (AdaDelta
) oraz metrykę oceny sieci (accuracy
).
Teraz przyszedł czas na proces uczenia. Sieć dostaje na wejściu:
- dane treningowe: X_train, y_train
- liczbę danych, które są wykorzywane w pojedyńczym kroku uczenia: batch_size
- liczbę iteracji: epochs (im wyższa tym proces uczenia trwa dłużej, ale też zbyt mała liczba iteracji sprawa, że sieć nie jest optymalnie nauczona)
- dane walidacyjne, za pomocą których sprawdzana jest na bieżąco skuteczność
Końcowym jest sprawdzenie w pełni nauczonej sieci na danych testowych.
Wynik prezentuje się następująco. Skuteczność wyniosła 97.44%, co jest bardzo dobrym wynikiem, biorąc pod uwagę, że wykonana została tylko jedna iteracja.
Podsumowanie
Utworzona tutaj sieć neuronowa ma stosunkowo skomplikowaną strukturę - składa się z kilku warstw różnego typu, jednak jak widać kod jest dość prosty. I to jest największa zaleta biblioteki keras - umożliwia bardzo szybką implementację sieci neuronowej, bez potrzeby zagłębiania się w detale.
Ta seria będzie się skupiała właśnie na aspektach praktycznych, bo o teorii to można pisać i pisać.
Załącznik
Poniżej jeszcze raz kod, tym razem jako tekst (po prostu możliwy do skopiowania).
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D
num_classes = 10
image_rows, image_cols = 28, 28
input_shape = (image_rows, image_cols, 1)
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape[0], image_rows, image_cols, 1)
X_test = X_test.reshape(X_test.shape[0], image_rows, image_cols, 1)
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
model = Sequential([Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=input_shape),
Conv2D(64, (3, 3), activation='relu'),
MaxPooling2D(pool_size=(2, 2)),
Dropout(0.5),
Flatten(),
Dense(128, activation='relu'),
Dropout(0.5),
Dense(num_classes, activation='softmax')])
model.compile(loss=keras.losses.categorical_crossentropy,
optimizer=keras.optimizers.Adadelta(),
metrics=['accuracy'])
model.fit(X_train,
y_train,
batch_size=128,
epochs=1,
verbose=1,
validation_data=(X_test, y_test))
score = model.evaluate(X_test, y_test, verbose=0)
print('Accuracy:', score[1])
Hej,
Masz już jakiś plan postów? Chcesz ogólnie opisać różne przykłady wykorzystania czy skupisz się na jakimś projekcie?
Ostatnio zastanawiałem się czy dałoby się w podobny sposób przygotować sieć do czatu. Problem jest tylko uczenie nowych rzeczy. Tutaj jest zamknięty zbiór cyfer. Przy czacie dostajemy całe wyrazy na które wypada reagować. Widziałem na githubie fajną bibliotekę która rozpoznaje wyrazy i określa czy są pozytywne albo negatywne, intencję itd. Niestety przygotowana była pod język angielski i jego gramatykę, jednak sama idea jest według mnie ciekawa. Możliwe, że to będzie kolejny projekt kiedy ukończę serię o blockchain.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Mam kilka pomysłów na kolejne posty i póki co chciałem się skupić na przedstawieniu różnych ciekawych przykładów. Wiem, że klasyfikacja obrazów jest trochę oklepana, ale chciałem zacząć od czegoś względnie prostego. Teraz tak myślę, że to skupienie się na jednym projekcie to też byłby dobry pomysł, tylko nie wiem czy by to pasowało do konwencji tej serii. Bo to ma być seria opisująca ciekawe zastosowania, przy stosunkowo małym nakładzie pracy, żeby po prostu pokazać jak wiele rzeczy można w prosty sposób osiągnąć. Przemyślę to jeszcze.
To co opisujesz to tzw analiza sentymentu, kiedyś nawet miałem z tym trochę do czynienia. Tylko niestety, tak jak piszesz - wszystko jest przystosowane do języka angielskiego. Język polski jest bardzo niewdzięczny w tej materii.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Świetny post! Nawet nie wiesz, jak mnie kusi do deep learningu ciągle, szczególnie po twoich postach - bardzo żałuję, że nie mam na to czasu aktualnie. Wrzucaj wszystko dalej pod tym tagiem #pl-deep-learning proszę, to kiedyś odwiedzę przy okazji nauki.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Też mnie boli, że na wszystko nie starcza mi czasu. Bo tyle ciekawych rzeczy :) Co do tagu - właśnie miałem zamiar tak tagować.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
:o Czekałem na taką serię! Zabieram sie za to od dluzszego czasu i konczy sie na dobrych chęciach. Dzisiaj też tak będzie, bo nie dam rady przeczytać, ale jutro już z chęcią zapoznam się z całą treścią. Czekam na więcej, jak coś.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Mam w planach dłuższą serię, więc będzie więcej :)
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit