CNTK - konwolucyjna sieć neuronowa
W tym rozdziale przyjrzyjmy się, jak zbudować konwolucyjną sieć neuronową (CNN) w CNTK.
Wprowadzenie
Konwolucyjne sieci neuronowe (CNN) również składają się z neuronów, które mają możliwe do nauczenia wagi i odchylenia. Dlatego w ten sposób przypominają zwykłe sieci neuronowe (NN).
Jeśli przypomnimy sobie działanie zwykłych sieci neuronowych, każdy neuron otrzymuje jeden lub więcej danych wejściowych, pobiera sumę ważoną i przechodzi przez funkcję aktywacji, aby wygenerować wynik końcowy. Tutaj pojawia się pytanie, że jeśli CNN i zwykłe sieci NN mają tak wiele podobieństw, to co sprawia, że te dwie sieci różnią się od siebie?
Co je różni, to traktowanie danych wejściowych i typy warstw? Struktura danych wejściowych jest ignorowana w zwykłym NN, a wszystkie dane są konwertowane na tablicę 1-D przed wprowadzeniem ich do sieci.
Ale architektura konwolucyjnych sieci neuronowych może uwzględniać strukturę 2D obrazów, przetwarzać je i pozwalać na wyodrębnienie właściwości charakterystycznych dla obrazów. Ponadto CNN mają tę zaletę, że mają jedną lub więcej warstw konwolucyjnych i warstwę łączącą, które są głównymi elementami budulcowymi CNN.
Po tych warstwach znajduje się jedna lub więcej w pełni połączonych warstw, jak w standardowych wielowarstwowych sieciach NN. Możemy więc myśleć o CNN jako szczególnym przypadku w pełni połączonych sieci.
Architektura konwolucyjnych sieci neuronowych (CNN)
Architektura CNN to w zasadzie lista warstw, która przekształca trójwymiarową, tj. Szerokość, wysokość i głębię objętości obrazu, w trójwymiarową objętość wyjściową. Należy tutaj zauważyć, że każdy neuron w bieżącej warstwie jest podłączony do małego fragmentu danych wyjściowych z poprzedniej warstwy, co jest jak nałożenie filtru N * N na obraz wejściowy.
Używa filtrów M, które są w zasadzie ekstraktorami funkcji, które wyodrębniają cechy takie jak krawędzie, rogi i tak dalej. Poniżej znajdują się warstwy [INPUT-CONV-RELU-POOL-FC] używane do budowy konwolucyjnych sieci neuronowych (CNN) -
INPUT- Jak sama nazwa wskazuje, ta warstwa przechowuje nieprzetworzone wartości pikseli. Surowe wartości pikseli oznaczają dane obrazu w takim stanie, w jakim jest. Przykład: INPUT [64 × 64 × 3] to 3-kanałowy obraz RGB o szerokości 64, wysokości 64 i głębokości 3.
CONV- Ta warstwa jest jednym z elementów składowych sieci CNN, ponieważ większość obliczeń jest wykonywana w tej warstwie. Przykład - jeśli zastosujemy 6 filtrów na wspomnianym WEJŚCIU [64 × 64 × 3], może to skutkować głośnością [64 × 64 × 6].
RELU- Nazywana również rektyfikowaną liniową warstwą jednostkową, która stosuje funkcję aktywacji do wyjścia poprzedniej warstwy. W inny sposób nieliniowość zostałaby dodana do sieci przez RELU.
POOL- Ta warstwa, tj. Warstwa łącząca, jest kolejnym elementem składowym CNN. Głównym zadaniem tej warstwy jest próbkowanie w dół, co oznacza, że działa ona niezależnie na każdym wycinku wejścia i zmienia jego rozmiar przestrzennie.
FC- Nazywa się to warstwą w pełni połączoną, a dokładniej warstwą wyjściową. Służy do obliczania wyniku klasy wyjściowej, a wynikowym wynikiem jest objętość o rozmiarze 1 * 1 * L, gdzie L jest liczbą odpowiadającą wynikowi klasy.
Poniższy diagram przedstawia typową architekturę CNN−
Tworzenie struktury CNN
Poznaliśmy architekturę i podstawy CNN, teraz zamierzamy zbudować sieć konwolucyjną za pomocą CNTK. Tutaj najpierw zobaczymy, jak złożyć strukturę CNN, a następnie przyjrzymy się, jak wyszkolić jej parametry.
W końcu zobaczymy, jak możemy ulepszyć sieć neuronową, zmieniając jej strukturę za pomocą różnych konfiguracji warstw. Będziemy używać zestawu danych obrazu MNIST.
Więc najpierw stwórzmy strukturę CNN. Generalnie, kiedy tworzymy CNN do rozpoznawania wzorców w obrazach, wykonujemy następujące czynności:
Używamy kombinacji warstw splotu i pulowania.
Jedna lub więcej ukrytych warstw na końcu sieci.
Na koniec kończymy sieć warstwą softmax w celu klasyfikacji.
Wykonując następujące kroki, możemy zbudować strukturę sieci
Step 1- Najpierw musimy zaimportować wymagane warstwy dla CNN.
from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling
Step 2- Następnie musimy zaimportować funkcje aktywacyjne dla CNN.
from cntk.ops import log_softmax, relu
Step 3- Następnie, aby później zainicjować warstwy konwolucyjne, musimy zaimportować plik glorot_uniform_initializer w następujący sposób -
from cntk.initializer import glorot_uniform
Step 4- Następnie, aby utworzyć zmienne wejściowe, zaimportuj plik input_variablefunkcjonować. I importujdefault_option aby nieco ułatwić konfigurację NN.
from cntk import input_variable, default_options
Step 5- Teraz, aby zapisać obrazy wejściowe, utwórz nowy input_variable. Będzie zawierał trzy kanały, a mianowicie czerwony, zielony i niebieski. Miałby rozmiar 28 na 28 pikseli.
features = input_variable((3,28,28))
Step 6−Następnie musimy stworzyć kolejny input_variable przechowywać etykiety do przewidywania.
labels = input_variable(10)
Step 7- Teraz musimy stworzyć plik default_optiondla NN. I musimy użyćglorot_uniform jako funkcja inicjalizacyjna.
with default_options(initialization=glorot_uniform, activation=relu):
Step 8- Następnie, aby ustawić strukturę NN, musimy utworzyć nowy Sequential zestaw warstw.
Step 9- Teraz musimy dodać Convolutional2D warstwa z filter_shape 5 i a strides ustawienie 1, w ramach Sequentialzestaw warstw. Włącz także dopełnienie, aby obraz był wypełniony, aby zachować oryginalne wymiary.
model = Sequential([
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),
Step 10- Teraz czas dodać MaxPooling warstwa z filter_shape 2 i a strides ustawienie 2, aby zmniejszyć obraz o połowę.
MaxPooling(filter_shape=(2,2), strides=(2,2)),
Step 11- Teraz, tak jak w kroku 9, musimy dodać kolejny Convolutional2D warstwa z filter_shape 5 i a stridesustawienie 1, użyj 16 filtrów. Włącz także dopełnianie, aby rozmiar obrazu utworzonego przez poprzednią warstwę puli został zachowany.
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),
Step 12- Teraz, tak jak w kroku 10, dodaj kolejny MaxPooling warstwa z filter_shape 3 i a strides ustawienie 3, aby zmniejszyć obraz do jednej trzeciej.
MaxPooling(filter_shape=(3,3), strides=(3,3)),
Step 13- Na koniec dodaj gęstą warstwę z dziesięcioma neuronami dla 10 możliwych klas, które sieć może przewidzieć. Aby przekształcić sieć w model klasyfikacji, użyj plikulog_siftmax funkcja aktywacji.
Dense(10, activation=log_softmax)
])
Kompletny przykład tworzenia struktury CNN
from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling
from cntk.ops import log_softmax, relu
from cntk.initializer import glorot_uniform
from cntk import input_variable, default_options
features = input_variable((3,28,28))
labels = input_variable(10)
with default_options(initialization=glorot_uniform, activation=relu):
model = Sequential([
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),
MaxPooling(filter_shape=(2,2), strides=(2,2)),
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),
MaxPooling(filter_shape=(3,3), strides=(3,3)),
Dense(10, activation=log_softmax)
])
z = model(features)
Szkolenie CNN ze zdjęciami
Ponieważ stworzyliśmy strukturę sieci, przyszedł czas na jej przeszkolenie. Ale przed rozpoczęciem uczenia naszej sieci musimy skonfigurować źródła-minibatchy, ponieważ uczenie sieci NN, która działa z obrazami, wymaga więcej pamięci niż większość komputerów.
W poprzednich sekcjach utworzyliśmy już źródła minibatchów. Poniżej znajduje się kod Pythona do skonfigurowania dwóch źródeł minibatchów -
Ponieważ mamy create_datasource funkcji, możemy teraz utworzyć dwa oddzielne źródła danych (uczące i testujące) w celu trenowania modelu.
train_datasource = create_datasource('mnist_train')
test_datasource = create_datasource('mnist_test', max_sweeps=1, train=False)
Teraz, gdy przygotowaliśmy obrazy, możemy rozpocząć szkolenie naszego NN. Tak jak to zrobiliśmy w poprzednich sekcjach, możemy użyć metody trenowania na funkcji straty, aby rozpocząć trening. Poniżej znajduje się kod do tego -
from cntk import Function
from cntk.losses import cross_entropy_with_softmax
from cntk.metrics import classification_error
from cntk.learners import sgd
@Function
def criterion_factory(output, targets):
loss = cross_entropy_with_softmax(output, targets)
metric = classification_error(output, targets)
return loss, metric
loss = criterion_factory(z, labels)
learner = sgd(z.parameters, lr=0.2)
Z pomocą poprzedniego kodu skonfigurowaliśmy stratę i ucznia dla NN. Poniższy kod wytrenuje i zweryfikuje NN−
from cntk.logging import ProgressPrinter
from cntk.train import TestConfig
progress_writer = ProgressPrinter(0)
test_config = TestConfig(test_datasource)
input_map = {
features: train_datasource.streams.features,
labels: train_datasource.streams.labels
}
loss.train(train_datasource,
max_epochs=10,
minibatch_size=64,
epoch_size=60000,
parameter_learners=[learner],
model_inputs_to_streams=input_map,
callbacks=[progress_writer, test_config])
Pełny przykład implementacji
from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling
from cntk.ops import log_softmax, relu
from cntk.initializer import glorot_uniform
from cntk import input_variable, default_options
features = input_variable((3,28,28))
labels = input_variable(10)
with default_options(initialization=glorot_uniform, activation=relu):
model = Sequential([
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),
MaxPooling(filter_shape=(2,2), strides=(2,2)),
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),
MaxPooling(filter_shape=(3,3), strides=(3,3)),
Dense(10, activation=log_softmax)
])
z = model(features)
import os
from cntk.io import MinibatchSource, StreamDef, StreamDefs, ImageDeserializer, INFINITELY_REPEAT
import cntk.io.transforms as xforms
def create_datasource(folder, train=True, max_sweeps=INFINITELY_REPEAT):
mapping_file = os.path.join(folder, 'mapping.bin')
image_transforms = []
if train:
image_transforms += [
xforms.crop(crop_type='randomside', side_ratio=0.8),
xforms.scale(width=28, height=28, channels=3, interpolations='linear')
]
stream_definitions = StreamDefs(
features=StreamDef(field='image', transforms=image_transforms),
labels=StreamDef(field='label', shape=10)
)
deserializer = ImageDeserializer(mapping_file, stream_definitions)
return MinibatchSource(deserializer, max_sweeps=max_sweeps)
train_datasource = create_datasource('mnist_train')
test_datasource = create_datasource('mnist_test', max_sweeps=1, train=False)
from cntk import Function
from cntk.losses import cross_entropy_with_softmax
from cntk.metrics import classification_error
from cntk.learners import sgd
@Function
def criterion_factory(output, targets):
loss = cross_entropy_with_softmax(output, targets)
metric = classification_error(output, targets)
return loss, metric
loss = criterion_factory(z, labels)
learner = sgd(z.parameters, lr=0.2)
from cntk.logging import ProgressPrinter
from cntk.train import TestConfig
progress_writer = ProgressPrinter(0)
test_config = TestConfig(test_datasource)
input_map = {
features: train_datasource.streams.features,
labels: train_datasource.streams.labels
}
loss.train(train_datasource,
max_epochs=10,
minibatch_size=64,
epoch_size=60000,
parameter_learners=[learner],
model_inputs_to_streams=input_map,
callbacks=[progress_writer, test_config])
Wynik
-------------------------------------------------------------------
average since average since examples
loss last metric last
------------------------------------------------------
Learning rate per minibatch: 0.2
142 142 0.922 0.922 64
1.35e+06 1.51e+07 0.896 0.883 192
[………]
Transformacje obrazu
Jak widzieliśmy, trudno jest wyszkolić NN używane do rozpoznawania obrazów, a do ich trenowania również potrzeba dużej ilości danych. Kolejną kwestią jest to, że mają tendencję do nadmiernego dopasowania na obrazach używanych podczas treningu. Zobaczmy na przykładzie, kiedy mamy zdjęcia twarzy w pozycji pionowej, nasz model będzie miał trudności z rozpoznaniem twarzy, które są obrócone w innym kierunku.
Aby przezwyciężyć ten problem, możemy skorzystać z powiększania obrazu, a CNTK obsługuje określone transformacje podczas tworzenia źródeł minibatchów dla obrazów. Możemy użyć kilku transformacji w następujący sposób:
Możemy losowo przycinać obrazy używane do treningu za pomocą zaledwie kilku wierszy kodu.
Możemy również użyć skali i koloru.
Zobaczmy, korzystając z poniższego kodu Pythona, jak możemy zmienić listę transformacji, włączając transformację przycinania w ramach funkcji używanej do wcześniejszego utworzenia źródła minibatch.
import os
from cntk.io import MinibatchSource, StreamDef, StreamDefs, ImageDeserializer, INFINITELY_REPEAT
import cntk.io.transforms as xforms
def create_datasource(folder, train=True, max_sweeps=INFINITELY_REPEAT):
mapping_file = os.path.join(folder, 'mapping.bin')
image_transforms = []
if train:
image_transforms += [
xforms.crop(crop_type='randomside', side_ratio=0.8),
xforms.scale(width=28, height=28, channels=3, interpolations='linear')
]
stream_definitions = StreamDefs(
features=StreamDef(field='image', transforms=image_transforms),
labels=StreamDef(field='label', shape=10)
)
deserializer = ImageDeserializer(mapping_file, stream_definitions)
return MinibatchSource(deserializer, max_sweeps=max_sweeps)
Za pomocą powyższego kodu możemy rozszerzyć funkcję o zestaw transformacji obrazu, dzięki czemu podczas treningu możemy losowo przyciąć obraz, aby uzyskać więcej wariantów obrazu.