Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Pamiętaj, że ten samouczek wymaga najnowszej wersji głównej lub nadchodzącej wersji CNTK 1.7, która zostanie wkrótce wydana. Pośrednie pobieranie binarne można znaleźć w instrukcjach dotyczących usługi KDD CNTK Hands-On Tutorial, dla którego ten samouczek został pierwotnie zaprojektowany.
Hands-On Lab: rozpoznawanie obrazów za pomocą sieci konwolucyjnych, normalizacji usługi Batch i sieci reszt
W tym praktycznym laboratorium pokazano, jak zaimplementować rozpoznawanie obrazów oparte na konwolucji za pomocą CNTK. Zaczniemy od wspólnej architektury rozpoznawania obrazów splotowych, dodamy normalizację usługi Batch, a następnie rozszerzymy ją do sieci reszt (ResNet-20).
Techniki, które będziesz ćwiczyć, obejmują:
- modyfikowanie definicji sieci CNTK w celu dodania wstępnie zdefiniowanej operacji (dropout)
- tworzenie funkcji zdefiniowanych przez użytkownika w celu wyodrębniania powtarzających się części w sieci do modułu wielokrotnego użytku
- implementowanie niestandardowych struktur sieciowych (pomiń połączenie usługi ResNet)
- tworzenie wielu warstw jednocześnie przy użyciu pętli cyklicznych
- trenowanie równoległe
- sieci splotowe
- normalizacja wsadowa
Wymagania wstępne
Przyjęto założenie, że masz już zainstalowane CNTK i można uruchomić polecenie CNTK. Ten samouczek odbył się w usłudze KDD 2016 i wymaga najnowszej kompilacji. Aby uzyskać instrukcje dotyczące konfiguracji, zobacz tutaj . Możesz po prostu postępować zgodnie z instrukcjami dotyczącymi pobierania binarnego pakietu instalacyjnego z tej strony. W przypadku zadań związanych z obrazami należy to zrobić na maszynie z procesorem GPU zgodnym z funkcją CUDA.
Następnie pobierz archiwum ZIP (około 12 MB): kliknij ten link, a następnie kliknij przycisk Pobierz.
Archiwum zawiera pliki na potrzeby tego samouczka. Zarchiwizować i ustawić katalog roboczy na ImageHandsOn.
Będziesz pracować z następującymi plikami:
ImageHandsOn.cntk: plik konfiguracji CNTK przedstawimy poniżej i będziemy z tym pracować.cifar10.pretrained.cmf: Wynikowy model konfiguracji zaczniemy od.cifar10.ResNet.cmf: Wynikowy model wersji usługi ResNet, którą utworzymy poniżej.
Na koniec musimy pobrać i przekonwertować zestaw danych CIFAR-10. Krok konwersji potrwa około 10 minut. Wykonaj następujące dwa skrypty języka Python, które znajdziesz również w katalogu roboczym:
wget -rc http://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
tar xvf www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
python CifarConverter.py cifar-10-batches-py
Spowoduje to przekonwertowanie obrazów na pliki PNG, 50000 na potrzeby trenowania i 10000 na potrzeby testowania, które zostaną umieszczone odpowiednio w dwóch następujących katalogach: cifar-10-batches-py/data/train i cifar-10-batches-py/data/test.
Struktura modelu
Rozpoczniemy ten samouczek od prostego modelu splotowego. Składa się z 3 warstw 5x5 konwolucji + nieliniowości + 2x redukcji wymiarów przez 3x3 max-pooling, które następnie są następnie gęstą ukrytą warstwą i gęstą transformacją, aby utworzyć dane wejściowe do klasyfikatora softmax 10-way.
Lub jako opis sieci CNTK. Zapoznaj się z krótkim wyglądem i dopasuj go do powyższego opisu:
featNorm = features - Constant (128)
l1 = ConvolutionalLayer {32, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 0.0043} (featNorm)
p1 = MaxPoolingLayer {(3:3), stride = (2:2)} (l1)
l2 = ConvolutionalLayer {32, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 1.414} (p1)
p2 = MaxPoolingLayer {(3:3), stride = (2:2)} (l2)
l3 = ConvolutionalLayer {64, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 1.414} (p2)
p3 = MaxPoolingLayer {(3:3), stride = (2:2)} (l3)
d1 = DenseLayer {64, activation = ReLU, init = "gaussian", initValueScale = 12} (p3)
z = LinearLayer {10, init = "gaussian", initValueScale = 1.5} (d1)
Więcej informacji na temat tych operatorów można znaleźć tutaj: ConvolutionalLayer{}, MaxPoolingLayer{}, DenseLayer{}, . LinearLayer{}
konfiguracja CNTK
Plik konfiguracji
Aby wytrenować i przetestować model w CNTK, musimy podać plik konfiguracji, który informuje CNTK, jakie operacje chcesz uruchomić (command zmienna) i sekcję parametrów dla każdego polecenia.
W przypadku polecenia szkoleniowego CNTK należy powiedzieć:
- jak odczytywać dane (
readersekcja) - funkcja modelu oraz jej dane wejściowe i wyjściowe na wykresie obliczeniowym (
BrainScriptNetworkBuildersekcja) - hiperpara parametry dla ucznia (
SGDsekcja)
W przypadku polecenia oceny CNTK musi wiedzieć:
- jak odczytywać dane testowe (
readersekcja) - które metryki do oceny (
evalNodeNamesparametr)
Poniżej przedstawiono plik konfiguracji, od który zaczniemy. Jak widać, plik konfiguracji CNTK jest plikiem tekstowym składającym się z definicji parametrów, które są zorganizowane w hierarchii rekordów. Możesz również zobaczyć, jak CNTK obsługuje podstawowe zastępowanie parametrów przy użyciu $parameterName$ składni. Rzeczywisty plik zawiera tylko kilka parametrów, niż wspomniano powyżej, ale przeskanuj go i znajdź wymienione elementy konfiguracji:
# CNTK Configuration File for training a simple CIFAR-10 convnet.
# During the hands-on tutorial, this will be fleshed out into a ResNet-20 model.
command = TrainConvNet:Eval
makeMode = false ; traceLevel = 0 ; deviceId = "auto"
rootDir = "." ; dataDir = "$rootDir$" ; modelDir = "$rootDir$/Models"
modelPath = "$modelDir$/cifar10.cmf"
# Training action for a convolutional network
TrainConvNet = {
action = "train"
BrainScriptNetworkBuilder = {
imageShape = 32:32:3
labelDim = 10
model (features) = {
featNorm = features - Constant (128)
l1 = ConvolutionalLayer {32, (5:5), pad=true, activation=ReLU,
init="gaussian", initValueScale=0.0043} (featNorm)
p1 = MaxPoolingLayer {(3:3), stride=(2:2)} (l1)
l2 = ConvolutionalLayer {32, (5:5), pad=true, activation=ReLU,
init="gaussian", initValueScale=1.414} (p1)
p2 = MaxPoolingLayer {(3:3), stride=(2:2)} (l2)
l3 = ConvolutionalLayer {64, (5:5), pad=true, activation=ReLU,
init="gaussian", initValueScale=1.414} (p2)
p3 = MaxPoolingLayer {(3:3), stride=(2:2)} (l3)
d1 = DenseLayer {64, activation=ReLU, init="gaussian", initValueScale=12} (p3)
z = LinearLayer {10, init="gaussian", initValueScale=1.5} (d1)
}.z
# inputs
features = Input {imageShape}
labels = Input {labelDim}
# apply model to features
z = model (features)
# connect to system
ce = CrossEntropyWithSoftmax (labels, z)
errs = ErrorPrediction (labels, z)
featureNodes = (features)
labelNodes = (labels)
criterionNodes = (ce)
evaluationNodes = (errs)
outputNodes = (z)
}
SGD = {
epochSize = 50000
maxEpochs = 30 ; minibatchSize = 64
learningRatesPerSample = 0.00015625*10:0.000046875*10:0.000015625
momentumAsTimeConstant = 600*20:6400
L2RegWeight = 0.03
firstMBsToShowResult = 10 ; numMBsToShowResult = 100
}
reader = {
verbosity = 0 ; randomize = true
deserializers = ({
type = "ImageDeserializer" ; module = "ImageReader"
file = "$dataDir$/cifar-10-batches-py/train_map.txt"
input = {
features = { transforms = (
{ type = "Crop" ; cropType = "RandomSide" ; sideRatio = 0.8 ; jitterType = "UniRatio" } :
{ type = "Scale" ; width = 32 ; height = 32 ; channels = 3 ; interpolations = "linear" } :
{ type = "Transpose" }
)}
labels = { labelDim = 10 }
}
})
}
}
# Eval action
Eval = {
action = "eval"
minibatchSize = 16
evalNodeNames = errs
reader = {
verbosity = 0 ; randomize = true
deserializers = ({
type = "ImageDeserializer" ; module = "ImageReader"
file = "$dataDir$/cifar-10-batches-py/test_map.txt"
input = {
features = { transforms = (
{ type = "Scale" ; width = 32 ; height = 32 ; channels = 3 ; interpolations = "linear" } :
{ type = "Transpose" }
)}
labels = { labelDim = 10 }
}
})
}
}
Odczytywanie danych i danych
Po pobraniu danych CIFAR-10 i uruchomieniu skryptu CifarConverter.py zgodnie z żądaniem na początku tego samouczka znajdziesz katalog o nazwie cifar-10-batches-py/data, który zawiera dwa podkatalogi train i test, pełen plików PNG.
CNTK ImageDeserializer używa standardowych formatów obrazów.
Znajdziesz również dwa pliki train_map.txt i test_map.txt. Patrząc na tę ostatnią,
% more cifar-10-batches-py/test_map.txt
cifar-10-batches-py/data/test/00000.png 3
cifar-10-batches-py/data/test/00001.png 8
cifar-10-batches-py/data/test/00002.png 8
...
Oba pliki składają się z dwóch kolumn, gdzie pierwsza zawiera ścieżkę do pliku obrazu, a drugą etykietę klasy jako indeks liczbowy. Te kolumny odpowiadają danym wejściowym czytnika features i labels które zostały zdefiniowane jako:
features = { transforms = (
{ type = "Crop" ; cropType = "RandomSide" ; sideRatio = 0.8 ; jitterType = "UniRatio" } :
{ type = "Scale" ; width = 32 ; height = 32 ; channels = 3 ; interpolations = "linear" } :
{ type = "Transpose" }
)}
labels = { labelDim = 10 }
Dodatkowa transforms sekcja informuje o ImageDeserializer zastosowaniu sekwencji (wspólnych) przekształceń do obrazów podczas ich odczytywania.
Aby uzyskać więcej informacji, zobacz tutaj.
Uruchamianie
Powyższy plik konfiguracji można znaleźć pod nazwą ImageHandsOn.cntk w folderze roboczym.
Aby go uruchomić, wykonaj powyższą konfigurację za pomocą tego polecenia:
cntk configFile=ImageHandsOn.cntk
Ekran będzie żywy z lawiną komunikatów dziennika (CNTK może być czasami talkative), ale jeśli wszystko poszło prawidłowo, wkrótce zobaczysz to:
Training 116906 parameters in 10 out of 10 parameter tensors and 28 nodes with gradient
następnie dane wyjściowe w następujący sposób:
Finished Epoch[ 1 of 10]: [Training] ce = 1.66950797 * 50000; errs = 61.228% * 50000
Finished Epoch[ 2 of 10]: [Training] ce = 1.32699016 * 50000; errs = 47.394% * 50000
Finished Epoch[ 3 of 10]: [Training] ce = 1.17140398 * 50000; errs = 41.168% * 50000
Dzięki temu dowiesz się, że uczy się. Każda epoka reprezentuje jeden przechodzi przez 50000 obrazów treningowych.
Informuje również, że po drugiej epoki kryterium trenowania, które konfiguracja o nazwie ceosiągnęła 1,33 mierzone na próbkach tej epoki 50000 i że współczynnik błędów wynosi 47% dla tych samych 50000 próbek treningowych.
Zwróć uwagę, że maszyny tylko do procesora CPU są o 20 razy wolniejsze. Zanim zobaczysz nawet pierwsze dane wyjściowe dziennika, potrwa to wiele minut. Aby upewnić się, że system postępuje, możesz włączyć śledzenie, aby wyświetlić częściowe wyniki, które powinny wyglądać dość szybko:
cntk configFile=ImageHandsOn.cntk traceLevel=1
Epoch[ 1 of 10]-Minibatch[-498- 1, 0.13%]: ce = 2.30260658 * 64; errs = 90.625% * 64
...
Epoch[ 1 of 10]-Minibatch[ 1- 100, 12.80%]: ce = 2.10434176 * 5760; errs = 78.472% * 5760
Epoch[ 1 of 10]-Minibatch[ 101- 200, 25.60%]: ce = 1.82372971 * 6400; errs = 68.172% * 6400
Epoch[ 1 of 10]-Minibatch[ 201- 300, 38.40%]: ce = 1.69708496 * 6400; errs = 62.469% * 6400
Po zakończeniu trenowania (co trwa około 3 minut na Surface Book i na komputerze stacjonarnym z procesorem GPU Titan-X), końcowy komunikat będzie podobny do następującego:
Finished Epoch[10 of 10]: [Training] ce = 0.74679766 * 50000; errs = 25.486% * 50000
co pokazuje, że sieć pomyślnie zmniejszyła ce utratę i osiągnęła błąd klasyfikacji 25,5% w zestawie treningowym. Ponieważ nasza command zmienna określa drugie polecenie Eval, CNTK następnie przejdzie do tej akcji. Mierzy współczynnik błędów klasyfikacji na 10000 obrazów zestawu testowego.
Final Results: Minibatch[1-625]: errs = 24.180% * 10000
Współczynnik błędów testowania jest zbliżony do trenowania. Ponieważ CIFAR-10 jest dość małym zestawem danych, jest to wskaźnik, że nasz model nie został jeszcze w pełni zbieżny (a w rzeczywistości uruchomienie go dla 30 epok pozwoli Ci osiągnąć około 20%).
Jeśli nie chcesz czekać na zakończenie tego działania, możesz uruchomić model pośredni, np.
cntk configFile=ImageHandsOn.cntk command=Eval modelPath=Models/cifar10.cmf.5
Final Results: Minibatch[1-625]: errs = 31.710% * 10000
lub uruchom nasz wstępnie wytrenowany model, jak również:
cntk configFile=ImageHandsOn.cntk command=Eval modelPath=cifar10.pretrained.cmf
Final Results: Minibatch[1-625]: errs = 24.180% * 10000
Modyfikowanie modelu
W poniższych krokach zostaną podane zadania do przećwiczyć modyfikowanie CNTK konfiguracji. Na końcu tego dokumentu podano rozwiązania... ale proszę spróbować bez!
Zadanie 1. Dodawanie listy rozwijanej
Typową techniką poprawy uogólniania modeli jest dropout. Aby dodać listę rozwijaną do modelu CNTK, potrzebujesz
- dodaj wywołanie funkcji
Dropout()CNTK, w której chcesz wstawić operację dropout - dodawanie parametru
dropoutRateSGDdo sekcji o nazwie w celu zdefiniowania prawdopodobieństwa dropout
W tym konkretnym zadaniu określ brak spadku dla pierwszej epoki, po którym następuje spadek o 50%.
Zapoznaj się z dokumentacją Dropout() , aby dowiedzieć się, jak to zrobić.
Jeśli wszystko poszło dobrze, nie będziesz obserwować żadnych zmian w pierwszej epoki, ale znacznie mniejsza poprawa ce raz dropout kicks in z drugiej epoki. Jest to oczekiwane zachowanie. (W przypadku tej konkretnej konfiguracji dokładność rozpoznawania nie poprawia się w rzeczywistości). Wynik końcowy podczas trenowania tylko 10 epok wynosi około 32%.
10 epok nie wystarczy dla dropout.
Zapoznaj się z rozwiązaniem tutaj.
Zadanie 2. Upraszczanie definicji modelu przez wyodrębnianie powtarzających się części do funkcji
W tym przykładzie sekwencja (buforowanie relu >> konwolucyjnego>>) jest powtarzana trzy razy. Twoim zadaniem jest napisanie funkcji BrainScript, która grupuje te trzy operacje w module wielokrotnego użytku. Należy pamiętać, że wszystkie trzy zastosowania tej sekwencji używają różnych parametrów (wymiary wyjściowe, waga inicjowania). W związku z tym zapisywana funkcja powinna przyjmować te dwa parametry jako parametry oprócz danych wejściowych. Na przykład można go zdefiniować jako
MyLayer (x, depth, initValueScale)
Po uruchomieniu tego polecenia należy oczekiwać, że wynikowe ce wartości i errs będą takie same.
Jeśli jednak uruchomisz na procesorze GPU, niedeterminizm w implementacji propagacji wstecznej cuDNN spowoduje drobne zmiany.
Zapoznaj się z rozwiązaniem tutaj.
Zadanie 3. Dodawanie funkcji BatchNormalization
(To zadanie wymaga procesora GPU, ponieważ implementacja normalizacji wsadowej CNTK jest oparta na cuDNN).
Normalizacja wsadowa to popularna technika przyspieszania i ulepszania zbieżności.
W CNTK normalizacja wsadowa jest implementowana jako BatchNormalizationLayer{}.
Formularz przestrzenny (gdzie wszystkie pozycje pikseli są znormalizowane za pomocą parametrów udostępnionych) jest wywoływany przez opcjonalny parametr: BatchNormalizationLayer{spatialRank=2}.
Dodaj normalizację partii do wszystkich trzech warstw konwolucji i między dwiema gęstą warstwami.
Należy pamiętać, że normalizacja wsadowa powinna zostać wstawiona bezpośrednio przed nieliniowością.
W związku z tym należy usunąć activation parametr i zamiast tego wstawić jawne wywołania funkcji CNTK ReLU().
Ponadto normalizacja wsadowa zmienia szybkość zbieżności. Zwiększmy więc wskaźniki nauki dla pierwszych 7 epok 3-krotnych i wyłączmy rozmach i uregulowania L2, ustawiając ich parametry na 0.
Podczas uruchamiania zostaną wyświetlone dodatkowe parametry z możliwością nauki wymienione w dzienniku trenowania. Wynik końcowy będzie wynosić około 28%, co jest o 4 punkty lepsze niż bez normalizacji partii po tej samej liczbie iteracji. Konwergencja rzeczywiście przyspieszyła.
Zapoznaj się z rozwiązaniem tutaj.
Zadanie 4. Konwertowanie na resztę netto
Powyższa konfiguracja jest przykładem "toy", aby twoje ręce były brudne z uruchamiania i modyfikowania konfiguracji CNTK i celowo nie uruchamialiśmy pełnej zbieżności, aby utrzymać czas realizacji dla Ciebie niskie. Przejdźmy teraz do bardziej rzeczywistej konfiguracji — reszty netto. Siatka reszt (https://arxiv.org/pdf/1512.03385v1.pdf) to zmodyfikowana struktura sieci głębokiej, w której warstwy, zamiast uczyć się mapowania z danych wejściowych do danych wyjściowych, poznaj termin korekty.
(To zadanie wymaga również procesora GPU dla operacji normalizacji wsadowej, chociaż jeśli masz dużo czasu, możesz spróbować uruchomić go na procesorze, edytując wywołania normalizacji wsadowej, przy pewnej utracie dokładności).
Aby rozpocząć, zmodyfikuj poprzednią konfigurację. Najpierw zastąp funkcję model(features) modelu następującą:
MySubSampleBN (x, depth, stride) =
{
s = Splice ((MaxPoolingLayer {(1:1), stride = (stride:stride)} (x) : ConstantTensor (0, (1:1:depth/stride))), axis = 3) # sub-sample and pad: [W x H x depth/2] --> [W/2 x H/2 x depth]
b = BatchNormalizationLayer {spatialRank = 2, normalizationTimeConstant = 4096} (s)
}.b
MyConvBN (x, depth, initValueScale, stride) =
{
c = ConvolutionalLayer {depth, (3:3), pad = true, stride = (stride:stride), bias = false,
init = "gaussian", initValueScale = initValueScale} (x)
b = BatchNormalizationLayer {spatialRank = 2, normalizationTimeConstant = 4096} (c)
}.b
ResNetNode (x, depth) =
{
c1 = MyConvBN (x, depth, 7.07, 1)
r1 = ReLU (c1)
c2 = MyConvBN (r1, depth, 7.07, 1)
r = ReLU (c2)
}.r
ResNetIncNode (x, depth) =
{
c1 = MyConvBN (x, depth, 7.07, 2) # note the 2
r1 = ReLU (c1)
c2 = MyConvBN (r1, depth, 7.07, 1)
r = ReLU (c2)
}.r
model (features) =
{
conv1 = ReLU (MyConvBN (features, 16, 0.26, 1))
rn1 = ResNetNode (ResNetNode (ResNetNode (conv1, 16), 16), 16)
rn2_1 = ResNetIncNode (rn1, 32)
rn2 = ResNetNode (ResNetNode (rn2_1, 32), 32)
rn3_1 = ResNetIncNode (rn2, 64)
rn3 = ResNetNode (ResNetNode (rn3_1, 64), 64)
pool = AveragePoolingLayer {(8:8)} (rn3)
z = LinearLayer {labelDim, init = "gaussian", initValueScale = 0.4} (pool)
}.z
i zmień konfigurację SGD na:
SGD = {
epochSize = 50000
maxEpochs = 160 ; minibatchSize = 128
learningRatesPerSample = 0.0078125*80:0.00078125*40:0.000078125
momentumAsTimeConstant = 1200
L2RegWeight = 0.0001
firstMBsToShowResult = 10 ; numMBsToShowResult = 500
}
Twoim zadaniem jest zmodyfikowanie ResNetNode() i ResNetNodeInc() zaimplementowanie struktury określonej w następującej godnej nagrody sztuce ASCII:
ResNetNode ResNetNodeInc
| |
+------+------+ +---------+----------+
| | | |
V | V V
+----------+ | +--------------+ +----------------+
| Conv, BN | | | Conv x 2, BN | | SubSample, BN |
+----------+ | +--------------+ +----------------+
| | | |
V | V |
+-------+ | +-------+ |
| ReLU | | | ReLU | |
+-------+ | +-------+ |
| | | |
V | V |
+----------+ | +----------+ |
| Conv, BN | | | Conv, BN | |
+----------+ | +----------+ |
| | | |
| +---+ | | +---+ |
+--->| + |<---+ +------>+ + +<-------+
+---+ +---+
| |
V V
+-------+ +-------+
| ReLU | | ReLU |
+-------+ +-------+
| |
V V
Upewnij się, że dane wyjściowe weryfikacji w dzienniku zostały prawidłowo utworzone.
Ukończenie tego procesu potrwa długo. Oczekiwane dane wyjściowe będą podobne do następujących:
Finished Epoch[ 1 of 160]: [Training] ce = 1.57037109 * 50000; errs = 58.940% * 50000
Finished Epoch[ 2 of 160]: [Training] ce = 1.06968234 * 50000; errs = 38.166% * 50000
Finished Epoch[ 3 of 160]: [Training] ce = 0.85858969 * 50000; errs = 30.316% * 50000
podczas gdy nieprawidłowy model bez pominięcia połączeń wygląda następująco:
Finished Epoch[ 1 of 160]: [Training] ce = 1.72901219 * 50000; errs = 66.232% * 50000
Finished Epoch[ 2 of 160]: [Training] ce = 1.30180430 * 50000; errs = 47.424% * 50000
Finished Epoch[ 3 of 160]: [Training] ce = 1.04641961 * 50000; errs = 37.568% * 50000
Zapoznaj się z rozwiązaniem tutaj.
Zadanie 5. Automatyczne generowanie wielu warstw
Na koniec najlepsza sieć ResNet ma 152 warstwy. Ponieważ byłoby bardzo żmudne i podatne na błędy zapisu 152 pojedynczych wyrażeń, teraz zmodyfikujemy definicję, aby automatycznie wygenerować stos .ResNetNode()
Twoim zadaniem jest napisanie funkcji z tym podpisem:
ResNetNodeStack (x, depth, L)
gdzie L określa, ile ResNetNodes powinno być ułożone, aby można było zastąpić wyrażenie powyżej rn1 parametryzowanym wywołaniem:
rn1 = ResNetNodeStack (conv1, 16, 3) # 3 means 3 such nodes
i podobnie dla rn2 i rn3.
Potrzebne narzędzia to wyrażenie warunkowe :
z = if cond then x else y
i rekursja.
To szkolenie będzie prowadzone przez około połowę naszej na Titan-X. Jeśli zrobisz to prawidłowo, na początku dziennika zostanie wyświetlony następujący komunikat:
Training 200410 parameters in 51 out of 51 parameter tensors and 112 nodes with gradient:
Do celów referencyjnych dołączymy wstępnie wytrenowane wersje tego modelu. Szybkość błędów można zmierzyć za pomocą tego polecenia:
cntk configFile=ImageHandsOn.ResNet.cntk command=Eval
powinien zostać wyświetlony wynik podobny do następującego:
Final Results: Minibatch[1-625]: errs = 8.400% * 10000; top5Errs = 0.290% * 10000
Ten współczynnik błędów jest bardzo zbliżony do tego zgłoszonego w oryginalnym dokumencie ResNet (https://arxiv.org/pdf/1512.03385v1.pdftabela 6).
Zapoznaj się z rozwiązaniem tutaj.
Zadanie 6. Trenowanie równoległe
Na koniec, jeśli masz wiele procesorów GPU, CNTK umożliwia równoległe trenowanie przy użyciu interfejsu MPI (Message-Passing Interface). Ten model jest zbyt mały, aby spodziewać się większej szybkości bez dalszego dostrajania, np. rozmiarów minibatch (bieżące ustawienie rozmiaru minibatch jest zbyt małe, aby uzyskać pełne wykorzystanie dostępnych rdzeni procesora GPU). Niemniej jednak pozwól nam przejść przez ruch, aby wiedzieć, jak to zrobić po przejściu do rzeczywistych obciążeń.
Dodaj następujący wiersz do SGD bloku:
SGD = {
...
parallelTrain = {
parallelizationMethod = "DataParallelSGD"
parallelizationStartEpoch = 2
distributedMBReading = true
dataParallelSGD = { gradientBits = 1 }
}
}
a następnie wykonaj następujące polecenie:
mpiexec -np 4 cntk configFile=ImageHandsOn.cntk stderr=Models/log parallelTrain=true
Co dalej?
Ten samouczek przećwiczyć, aby podjąć istniejącą konfigurację i zmodyfikować ją w określony sposób:
- dodawanie wstępnie zdefiniowanej operacji (dropout)
- wyodrębnianie powtarzających się części do modułów wielokrotnego użytku (funkcje)
- refaktoryzacja (aby wstawić normalizację partii)
- niestandardowe struktury sieci (pomiń połączenie z siecią ResNet)
- parametryzowanie powtarzających się struktur przy użyciu rekursji
i widzieliśmy, jak przyspieszyć trenowanie przez równoległe.
Więc gdzie idziemy stąd? Być może już okazało się, że wzorzec używany w tych przykładach, który nazywamy stylem tworzenia grafu , może być dość podatny na błędy. Czy wykryć błąd?
model (features) =
{
l1 = ConvolutionalLayer {32, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 0.0043} (featNorm)
p1 = MaxPoolingLayer {(3:3), stride = (2:2)} (l1)
l2 = ConvolutionalLayer {64, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 1.414} (p1)
p2 = MaxPoolingLayer {(3:3), stride = (2:2)} (l1)
d1 = DenseLayer {64, activation = ReLU, init = "gaussian", initValueScale = 12} (p2)
z = LinearLayer {10, init = "gaussian", initValueScale = 1.5} (d1)
}.z
Aby uniknąć tego błędu, należy użyć kompozycji funkcji. Poniżej przedstawiono alternatywną, bardziej zwięzłą metodę pisania tego samego:
model = Sequential (
ConvolutionalLayer {32, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 0.0043} :
MaxPoolingLayer {(3:3), stride = (2:2)} :
ConvolutionalLayer {64, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 1.414} :
MaxPoolingLayer {(3:3), stride = (2:2)} :
DenseLayer {64, activation = ReLU, init = "gaussian", initValueScale = 12} :
LinearLayer {10, init = "gaussian", initValueScale = 1.5}
)
Ten styl zostanie wprowadzony i użyty w następnym praktycznym samouczku Opis tekstu z rekursowymi sieciami.
Rozwiązania
Rozwiązanie 1. Dodawanie listy rozwijanej
Zmodyfikuj definicję modelu w następujący sposób:
p3 = MaxPoolingLayer {(3:3), stride = (2:2)} (l3)
d1 = DenseLayer {64, activation = ReLU, init = "gaussian", initValueScale = 12} (p3)
d1_d = Dropout (d1) ##### added
z = LinearLayer {10, init = "gaussian", initValueScale = 1.5} (d1_d) ##### d1 -> d1_d
i sekcja SGD:
SGD = {
...
dropoutRate = 0*5:0.5 ##### added
...
}
Rozwiązanie 2. Upraszczanie definicji modelu przez wyodrębnianie powtarzających się części do funkcji
Dodaj definicję funkcji w następujący sposób:
MyLayer (x, depth, initValueScale) =
{
c = ConvolutionalLayer {depth, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = initValueScale} (x)
p = MaxPoolingLayer {(3:3), stride = (2:2)} (c)
}.p
i zaktualizuj definicję modelu, aby jej używać
featNorm = features - Constant (128)
p1 = MyLayer (featNorm, 32, 0.0043) ##### replaced
p2 = MyLayer (p1, 32, 1.414) ##### replaced
p3 = MyLayer (p2, 64, 1.414) ##### replaced
d1 = DenseLayer {64, activation = ReLU, init = "gaussian", initValueScale = 12} (p3)
Rozwiązanie 3. Dodawanie funkcji BatchNormalization
Modyfikuj MyLayer():
MyLayer (x, depth, initValueScale) =
{
c = ConvolutionalLayer {depth, (5:5), pad = true, ##### no activation=ReLU
init = "gaussian", initValueScale = initValueScale} (x)
b = BatchNormalizationLayer {spatialRank = 2} (c)
r = ReLU (b) ##### now called explicitly
p = MaxPoolingLayer {(3:3), stride = (2:2)} (r)
}.p
i użyj go. Wstaw również normalizację wsadu przed :z
d1 = DenseLayer {64, init = "gaussian", initValueScale = 12} (p3)
d1_bnr = ReLU (BatchNormalizationLayer {} (d1)) ##### added BN and explicit ReLU
d1_d = Dropout (d1_bnr) ##### d1 -> d1_bnr
z = LinearLayer {10, init = "gaussian", initValueScale = 1.5} (d1_d)
I zaktualizuj te parametry w sekcji SGD:
SGD = {
....
learningRatesPerSample = 0.00046875*7:0.00015625*10:0.000046875*10:0.000015625
momentumAsTimeConstant = 0
L2RegWeight = 0
...
}
Rozwiązanie 4. Konwertowanie na resztę netto
Prawidłowe implementacje dla elementu ResNetNode() i ResNetNodeInc() to:
ResNetNode (x, depth) =
{
c1 = MyConvBN (x, depth, 7.07, 1)
r1 = ReLU (c1)
c2 = MyConvBN (r1, depth, 7.07, 1)
r = ReLU (x + c2) ##### added skip connection
}.r
ResNetIncNode (x, depth) =
{
c1 = MyConvBN (x, depth, 7.07, 2) # note the 2
r1 = ReLU (c1)
c2 = MyConvBN (r1, depth, 7.07, 1)
xs = MySubSampleBN (x, depth, 2)
r = ReLU (xs + c2) ##### added skip connection
}.r
Rozwiązanie 5. Automatyczne generowanie wielu warstw
Jest to implementacja:
ResNetNodeStack (x, depth, L) =
{
r = if L == 0
then x
else ResNetNode (ResNetNodeStack (x, depth, L-1), depth)
}.r
lub, krótsze:
ResNetNodeStack (x, depth, L) =
if L == 0
then x
else ResNetNode (ResNetNodeStack (x, depth, L-1), depth)
Należy również zmodyfikować funkcję modelu:
conv1 = ReLU (MyConvBN (features, 16, 0.26, 1))
rn1 = ResNetNodeStack (conv1, 16, 3) ##### replaced
rn2_1 = ResNetIncNode (rn1, 32)
rn2 = ResNetNodeStack (rn2_1, 32, 2) ##### replaced
rn3_1 = ResNetIncNode (rn2, 64)
rn3 = ResNetNodeStack (rn3_1, 64, 2) ##### replaced