rozpoznawanie obrazów za pomocą uczenia maszynowego w Pythonie, Konwolucyjnej sieci neuronowej

źródło: The Irish Times, pół Ó muirí

po wstępnym przetworzeniu danych nadszedł czas na zbudowanie naszego modelu, aby wykonać zadanie rozpoznawania obrazu. Jedną z technik jest wykorzystanie splotowej sieci neuronowej.

Ten artykuł jest zgodny z artykułem, który napisałem na temat przetwarzania obrazu. Po udostępnieniu danych do zadania rozpoznawania obrazu nadszedł czas na stworzenie algorytmu, który wykona zadanie. Wśród wielu technik używanych do rozpoznawania obrazów jako wielowarstwowy model perceptronu, splotowa sieć neuronowa (CNN) wydaje się bardzo wydajna. W tym artykule zobaczymy, jak zbudować CNN i jak zastosować go na zbiorze danych obrazów.

kiedy zaczynamy budować model rozpoznawania obrazu po raz pierwszy, zwykle dobrym pomysłem jest przeszkolenie i ocena go na stosunkowo prostym zbiorze danych.

jednym z najprostszych zadań jakie możemy wykonać jest Odręczne rozpoznawanie cyfr. Dany obraz odręcznie zapisanej cyfry (tj., 0, 1, …, 9), chcemy, aby nasz model był w stanie poprawnie sklasyfikować jego wartość liczbową. Chociaż to zadanie wydaje się stosunkowo proste, jest w rzeczywistości dość często używane w prawdziwym życiu, takie jak automatyczne wyodrębnianie numerów kart kredytowych ze zdjęcia. Zestaw danych, którego będziemy używać do rozpoznawania cyfr, to zestaw danych MNIST, który jest zestawem danych wykorzystywanym do rozpoznawania cyfr w oparciu o uczenie maszynowe.

baza danych MNIST (zmodyfikowany Narodowy Instytut Norm i Technologii) zawiera 60 000 przykładów szkoleniowych i 10 000 przykładów testowych. Baza danych zawiera Odręczne cyfry w skali szarości, które zostały zmienione tak, aby zmieściły się w polu 20×20 pikseli, które następnie zostało wyśrodkowane na obrazie 28×28 (wypełnionym białymi spacjami). Baza danych MNIST jest dostępna za pośrednictwem Pythona.

w tym artykule pokażę Ci, jak kodować Konwolucyjną sieć neuronową za pomocą keras, wysokiego poziomu API TensorFlow. Używam tensorflow 2.0 w tym artykule.

1 – Inicjalizacja

ponieważ każdy obraz w skali szarości ma wymiary 28×28, na obraz przypada 784 pikseli. Dlatego każdy obraz wejściowy odpowiada tensorowi 784 znormalizowanych wartości zmiennoprzecinkowych między 0,0 a 1,0. Etykietą obrazu jest tensor jednoogniskowy z 10 klasami (każda klasa reprezentuje jedną cyfrę). Jeśli chodzi o nasz kod, mamy img_rows = 28, img_cols = 28 i num_classes = 10. Tak więc wejście ma kształt (number_examples, img_rows, img_cols), a więc 60000x28x28.
kolejnym ważnym elementem do Ustawienia jest losowe ziarno, ponieważ chcemy zachować punkt początkowy, gdy komputer generuje losowy ciąg liczb.

importujemy również zbiór danych MNIST.

import tensorflow as tf # tensorflow 2.0
from keras.datasets import mnist
import numpy as npseed=0
np.random.seed(seed) # fix random seed
tf.random.set_seed(seed)# input image dimensions
num_classes = 10 # 10 digits
img_rows, img_cols = 28, 28 # number of pixels
# the data, shuffled and split between train and test sets
(X_train, Y_train), (X_test, Y_test) = mnist.load_data()

2 – zmiana kształtu i przeskalowanie

jak wspomniano w poprzedniej sekcji, wejścia mają kształt (number_examples, img_rows, img_cols). Aby jednak wykorzystać dane z naszą konwolucyjną siecią neuronową, musimy uzyskać je w formacie NHWC.

format NHWC ma kształt o czterech wymiarach:

  1. liczba próbek danych obrazu (rozmiar partii)
  2. wysokość każdego obrazu
  3. szerokość każdego obrazu
  4. kanały na obraz

wysokość i szerokość każdego obrazu z zestawu danych to img_rows i img_cols, podczas gdy liczba kanałów wynosi 1 (ponieważ obrazy są w skali szarości).

ponadto każdy piksel zawiera wartość w skali szarości określoną przez liczbę całkowitą między 0 a 255. Tak więc baza danych jest znormalizowana, aby miała wartości zmiennoprzecinkowe między 0.0 a 1.0. W tym przypadku 0.0 odpowiada wartości piksela w skali szarości wynoszącej 255 (czysta biel), podczas gdy 1.0 odpowiada wartości piksela w skali szarości równej 0 (czysta czerń).

X_train = X_train.reshape(X_train.shape, img_rows, img_cols, 1) 
X_test = X_test.reshape(X_test.shape, img_rows, img_cols, 1)input_shape = (img_rows, img_cols, 1)# cast floats to single precision
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')# rescale data in interval
X_train /= 255
X_test /= 255

3 – rzutowe wektory etykiet Y

musimy przekształcić nasze klasy w wektory. Robimy to, dotykając poniższej linii:

Y_train = keras.utils.to_categorical(Y_train, num_classes)
Y_test = keras.utils.to_categorical(Y_test, num_classes)

aby uzyskać lepsze wyjaśnienie tego kroku, powinieneś zobaczyć ten artykuł.

teraz, gdy przetwarzamy nasze dane, możemy zacząć budować model.

splotowy Model sieci neuronowej

jak wspomniano na końcu artykułu o przetwarzaniu obrazu, filtry odgrywają ogromną rolę w rozpoznawaniu obrazu. Używamy filtrów do przekształcania danych wejściowych i wyodrębniania funkcji, które pozwalają naszemu modelowi rozpoznawać określone obrazy. Przykładem bardzo wysokiego poziomu byłby filtr wykrywający krzywą, który pozwala naszemu modelowi odróżnić cyfry z krzywymi od cyfr bez krzywych.

1-filtry

jak wszystkie wagi sieci neuronowych, wagi filtra są zmiennymi, które można wytrenować. Szkolimy naszą sieć neuronową (poprzez wagi macierzy jądra), aby produkować filtry, które są w stanie wyodrębnić najbardziej przydatne ukryte funkcje.

gdy dane wejściowe mają wiele kanałów, filtr będzie miał osobną matrycę jądra na kanał. Zbiór danych MNIST ma tylko jeden kanał, ale w przypadku innych typów danych obrazu (np. RGB) trenujemy model, aby uzyskać optymalną wagę dla matrycy jądra każdego kanału.

2-splot

dotarliśmy do punktu ogniskowego splotowych sieci neuronowych: splot. Splot reprezentuje sposób, w jaki stosujemy nasze wagi filtrów do danych wejściowych. Głównym działaniem stosowanym przez splot jest iloczyn punktowy macierzy, czyli sumowanie nad iloczynem pierwiastkowym dwóch macierzy.

liczba iloczynów punktowych macierzy w splocie zależy od wymiarów danych wejściowych i macierzy jądra, a także wielkości kroku. Rozmiar kroku jest przesunięciem pionowym / poziomym macierzy jądra, gdy porusza się ona wzdłuż danych wejściowych.

3-Padding

czasami, gdy wykonujemy operację produktu kropkowego, jak pokazano wcześniej, nie używamy wiersza ani kolumny. Aby uniknąć tego zjawiska możemy użyć wyściółki.

tak więc, jeśli chcemy wykorzystać wszystkie dane wejściowe w naszym splocie, możemy umieścić wejściową macierz danych za pomocą 0. oznacza to, że dodajemy wiersze / kolumny wykonane w całości z 0 do krawędzi wejściowej macierzy danych. Ponieważ 0 pomnożone przez dowolną liczbę daje 0, wypełnienie nie ma wpływu na iloczyn punktów macierzy. Jest to ważne, ponieważ nie chcemy dodawać żadnych zniekształceń do naszego splotu.

4-Warstwa splotu

warstwa splotu w CNN nakłada wiele filtrów na tensor wejściowy. Podczas gdy każdy filtr ma oddzielną macierz jądra dla każdego z kanałów wejściowych, ogólny wynik splotu filtra jest sumą splotu we wszystkich kanałach wejściowych.

dodanie większej liczby filtrów do warstwy splotu pozwala na lepsze wyodrębnienie ukrytych funkcji. Wiąże się to jednak z dodatkowym czasem treningu i złożonością obliczeniową, ponieważ filtry dodają dodatkowe obciążenia do modelu. Liczba kanałów dla danych wyjściowych jest równa liczbie filtrów używanych przez warstwę splotu.

łączenie

podczas gdy warstwa splotu wyodrębnia ważne ukryte funkcje, liczba funkcji może być nadal dość duża. Możemy użyć poolingu, aby zmniejszyć rozmiar danych w wymiarach wysokości i szerokości. Pozwala to modelowi wykonywać mniej obliczeń i ostatecznie trenować szybciej. Zapobiega również nadmiernemu wyposażeniu, wydobywając tylko najbardziej istotne cechy i ignorując potencjalne zniekształcenia lub rzadkie cechy występujące tylko w kilku przykładach.

jak działa pooling?

podobnie jak splot, używamy matryc filtrujących w poolingu. Jednak filtr poolingowy nie ma żadnych wag, ani nie wykonuje matrycowych produktów punktowych. Zamiast tego stosuje operację redukcji do podsekcji danych wejściowych.

Typ poolingu, który jest zwykle używany w CNNs, jest określany jako Max pooling. Filtry Max poolingu wykorzystują operację max, aby uzyskać maksymalną liczbę w każdej podmatrycy danych wejściowych.

wiele warstw

1 – Dodawanie dodatkowych warstw

podobnie jak wszystkie sieci neuronowe, CNN mogą korzystać z dodatkowych warstw. Dodatkowe warstwy pozwalają CNN zasadniczo stos Wielu filtrów razem do wykorzystania na danych obrazu. Jednak, podobnie jak w przypadku budowy dowolnej sieci neuronowej, musimy uważać na to, ile dodatkowych warstw dodajemy. Jeśli dodamy zbyt wiele warstw do modelu, ryzykujemy, że będzie on zbyt dopasowany do danych treningowych, a tym samym bardzo słabo się uogólniamy. Ponadto każda dodatkowa warstwa zwiększa złożoność obliczeniową i wydłuża czas treningu dla naszego modelu.

2 – Zwiększenie filtrów

zazwyczaj zwiększamy ilość filtrów w warstwie splotu im głębiej jest ona w naszym modelu. W tym przypadku nasza druga warstwa splotu ma 64 filtry, w porównaniu do 32 filtrów pierwszej warstwy splotu. Im głębsza warstwa splotu, tym bardziej szczegółowe stają się wyodrębnione cechy. Na przykład pierwsza warstwa splotu może mieć filtry, które wyodrębniają funkcje, takie jak linie, krawędzie i krzywe. Kiedy przejdziemy do drugiego poziomu, filtry warstwy splotu mogą teraz wyodrębnić więcej wyróżniających cech, takich jak kąt ostry 77 lub przecinające się krzywe 88.

warstwa w pełni połączona

1-warstwa w pełni połączona

do danych wyjściowych drugiej warstwy zbiorczej stosujemy warstwę w pełni połączoną o rozmiarze 1024 (tj. liczbę neuronów w warstwie). Liczba jednostek jest nieco Dowolna. Wystarczająco, aby być potężnym, ale nie tak bardzo, aby być zbyt zasobochłonnym. Celem w pełni połączonej warstwy jest agregowanie cech danych przed konwersją ich do klas. Pozwala to modelowi na lepsze przewidywania, niż gdybyśmy po prostu przekształcili dane wyjściowe bezpośrednio na klasy.

2-spłaszczanie

dane, które wykorzystaliśmy w naszym modelu są w formacie NHWC. Aby jednak korzystać z w pełni połączonej warstwy, dane muszą być macierzą, w której liczba wierszy reprezentuje rozmiar partii, a kolumny reprezentują cechy danych. Tym razem musimy zmienić kształt w przeciwnym kierunku i przekształcić z nhwc w matrycę 2-D.

porzucenie

1 – współadaptacja

Współadaptacja odnosi się do sytuacji, gdy wiele neuronów w warstwie wyodrębnia te same lub bardzo podobne ukryte cechy z danych wejściowych. Może się to zdarzyć, gdy wagi połączenia dla dwóch różnych neuronów są prawie identyczne.

gdy w pełni połączona warstwa ma dużą liczbę neuronów, bardziej prawdopodobne jest wystąpienie koadaptacji. Może to być problem z dwóch powodów. Po pierwsze, jest to strata obliczeń, gdy mamy nadmiarowe neurony obliczające ten sam wynik. Po drugie, jeśli wiele neuronów wyodrębnia te same cechy, Zwiększa to znaczenie tych cech dla naszego modelu. Prowadzi to do przepełnienia, jeśli duplikaty wyodrębnionych funkcji są specyficzne tylko dla zestawu treningowego.

2-Dropout

sposób, w jaki minimalizujemy współadaptację dla w pełni połączonych warstw z wieloma neuronami, polega na zastosowaniu dropout podczas treningu. W przypadku przerwania, losowo wyłączamy część neuronów warstwy na każdym etapie treningu, zerując wartości neuronów.

warstwa Soft-max

ponieważ istnieje 10 możliwych cyfr, obraz MNIST może być, używamy 10 neuronów w pełni połączonych warstw, aby uzyskać klasy dla każdej klasy cyfr. Funkcja Softmax jest stosowana do klas, aby przekształcić je w prawdopodobieństwa dla danej klasy.

budowanie modelu

teraz jesteśmy gotowi do zbudowania naszego modelu. Oto Kod:

from keras.models import Sequential
from keras.layers import Dense, Conv2D, Flatten
from keras.layers import MaxPooling2D, Dropoutmodel = Sequential()#add model layers
model.add(Conv2D(32, kernel_size=(5, 5),
activation='relu',
input_shape=input_shape))
model.add(MaxPooling2D(pool_size=(2, 2)))# add second convolutional layer with 20 filters
model.add(Conv2D(64, (5, 5), activation='relu'))
# add 2D pooling layer
model.add(MaxPooling2D(pool_size=(2, 2)))
# flatten data
model.add(Flatten())
# add a dense all-to-all relu layer
model.add(Dense(1024, activation='relu'))
# apply dropout with rate 0.5
model.add(Dropout(0.5))
# soft-max layer
model.add(Dense(num_classes, activation='softmax'))

Typ modelu, którego będziemy używać, jest sekwencyjny. Sequential to najprostszy sposób na zbudowanie modelu w Keras. Pozwala na budowanie modelu warstwa po warstwie.

używamy metody add() do dołączania warstw do naszego modelu. Dla celów naszego wprowadzającego przykładu wystarczy skupić się na gęstych warstwach dla uproszczenia. Każda gęsta() warstwa przyjmuje jako pierwszy wymagany argument liczbę całkowitą, która określa liczbę neuronów. Typ funkcji aktywacji dla warstwy jest definiowany za pomocą opcjonalnego argumentu aktywacji, którego wejście jest nazwą funkcji aktywacji w formacie string. Przykłady to relu, tanh, elu, sigmoid, softmax.

w tej sieci neuronowej mamy 2 warstwy splotu, po których za każdym razem następuje warstwa zbiorcza. Następnie spłaszczymy dane, aby dodać gęstą warstwę, na którą nakładamy dropout z szybkością 0,5. Na koniec dodajemy gęstą warstwę, aby przydzielić każdy obraz odpowiednią klasą.

Kompilowanie modelu

następnie musimy skompilować nasz model. Kompilacja modelu wymaga trzech parametrów: optymalizatora, straty i metryki.

optymalizator kontroluje szybkość uczenia się. Będziemy używać ‘adam’ jako naszego optymalizatora. Adam jest ogólnie dobrym optymalizatorem do użycia w wielu przypadkach. Adam optimizer dostosowuje tempo uczenia się podczas całego szkolenia.

szybkość uczenia się określa, jak szybko obliczane są optymalne wagi dla modelu. Mniejsza szybkość uczenia się może prowadzić do dokładniejszych wag (do pewnego punktu), ale zmniejszenie to czas obliczeniowy.

użyjemy ‘categorical_crossentropy’ dla naszej funkcji straty. Jest to najczęstszy wybór klasyfikacji. Niższy wynik oznacza, że model radzi sobie lepiej.

aby jeszcze bardziej ułatwić interpretację, użyjemy metryki ‘accuracy’, aby zobaczyć wynik dokładności na zestawie walidacji, gdy trenujemy model.

#compile model using accuracy to measure model performance
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=)

Szkolenie modelu

teraz będziemy trenować nasz model. Aby trenować, użyjemy funkcji ‘fit ()’ w naszym modelu z następującymi parametrami: dane treningowe (X_train), dane docelowe (Y_train), dane walidacyjne i liczba epok.

dla naszych danych walidacyjnych użyjemy zestawu testów dostarczonego nam w naszym zbiorze danych, który podzieliliśmy na X_test i Y_test.

liczba epok to liczba czasów, w których model będzie przechodził przez dane. Im więcej epok prowadzimy, tym bardziej model będzie się poprawiał, aż do pewnego punktu. Po tym momencie model przestanie się poprawiać w każdej epoce. Dla naszego modelu ustawimy liczbę epok na 3.

#train the model
model.fit(X_train, Y_train, validation_data=(X_test, Y_test), epochs=3)

Oceń model

teraz wyszkoliliśmy nasz model, możemy ocenić jego wydajność:

# evaluate the model
score = model.evaluate(X_test, Y_test, verbose=1)
# print performance
print()
print('Test loss:', score)
print('Test accuracy:', score)

mamy więc dokładność 99,3% i stratę 0,025 na zestawie testowym, który jest bardzo dobry. Nadal możemy ulepszyć model, zwiększając liczbę epok i wprowadzając wielkość partii.

twórz prognozy

Jeśli chcesz zobaczyć rzeczywiste prognozy, które nasz model przygotował dla danych testowych, możemy użyć funkcji predict_classes. Możemy również do tego za pomocą funkcji predict da tablicę z 10 liczbami. Liczby te są prawdopodobieństwem, że obraz wejściowy reprezentuje każdą cyfrę (0-9). Indeks tablicy z najwyższą liczbą reprezentuje przewidywanie modelu. Suma każdej tablicy jest równa 1 (ponieważ każda liczba jest prawdopodobieństwem).

aby to pokazać, pokażemy prognozy dla pierwszych 4 obrazów w zestawie testowym.

Uwaga: jeśli mamy nowe dane, możemy wprowadzić nasze nowe dane do funkcji predict, aby zobaczyć prognozy, które nasz model tworzy na nowych danych. Ponieważ nie mamy żadnych nowych, niewidocznych danych, na razie pokażemy prognozy przy użyciu zestawu testowego.

#predict first 4 images in the test set
model.predict_classes(X_test)

#predict first 4 images in the test set
model.predict(X_test)

We can see that our model predicted 7, 2, 1 and 0 for the first four images.

Let’s compare this with the actual results.

#actual results for first 4 images in test set
y_test

rzeczywiste wyniki pokazują, że pierwsze cztery obrazy to również 7, 2,1 i 0. Nasz model przewidział poprawnie!

cały model

import tensorflow as tf # tensorflow 2.0
from keras.datasets import mnist
import numpy as np
seed=0
np.random.seed(seed) # fix random seed
tf.random.set_seed(seed)
# input image dimensions
num_classes = 10 # 10 digitsimg_rows, img_cols = 28, 28 # number of pixels# the data, shuffled and split between train and test sets
(X_train, Y_train), (X_test, Y_test) = mnist.load_data()X_train = X_train.reshape(X_train.shape, img_rows, img_cols, 1)
X_test = X_test.reshape(X_test.shape, img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)# cast floats to single precision
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')# rescale data in interval
X_train /= 255
X_test /= 255Y_train = keras.utils.to_categorical(Y_train, num_classes)
Y_test = keras.utils.to_categorical(Y_test, num_classes)from keras.models import Sequential
from keras.layers import Dense, Conv2D, Flatten
from keras.layers import MaxPooling2D, Dropout
model = Sequential()#add model layers
model.add(Conv2D(32, kernel_size=(5, 5),
activation='relu',
input_shape=input_shape))
model.add(MaxPooling2D(pool_size=(2, 2)))
# add second convolutional layer with 20 filters
model.add(Conv2D(64, (5, 5), activation='relu'))
# add 2D pooling layer
model.add(MaxPooling2D(pool_size=(2, 2)))
# flatten data
model.add(Flatten())
# add a dense all-to-all relu layer
model.add(Dense(1024, activation='relu'))
# apply dropout with rate 0.5
model.add(Dropout(0.5))
# soft-max layer
model.add(Dense(num_classes, activation='softmax'))#compile model using accuracy to measure model performance
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=)#train the model
model.fit(X_train, Y_train, validation_data=(X_test, Y_test), epochs=3)# evaluate the model
score = model.evaluate(X_test, Y_test, verbose=1)# print performance
print()
print('Test loss:', score)
print('Test accuracy:', score)#predict first 4 images in the test set
model.predict(X_test)model.predict_classes(X_test)#actual results for first 4 images in test set
Y_test

ciąg dalszy…

w tym artykule zająłem się drugą częścią rozpoznawania obrazu, która polega na budowaniu splotowej sieci neuronowej.

mam nadzieję, że znalazłeś to, po co tu przyszedłeś w tym artykule i zostań ze mną w następnych odcinkach tej podróży rozpoznawania obrazów!

PS: obecnie jestem studentem Master of Engineering w Berkeley i jeśli chcesz omówić ten temat, skontaktuj się ze mną. Oto mój e-mail.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.