Szkolenie Flappy Bird: samouczek do nauki wzmacniania
Opublikowany: 2022-03-11W klasycznym programowaniu instrukcje oprogramowania są jawnie tworzone przez programistów i w ogóle niczego się nie uczy z danych. Natomiast uczenie maszynowe to dziedzina informatyki, która wykorzystuje metody statystyczne, aby umożliwić komputerom uczenie się i wydobywanie wiedzy z danych bez wyraźnego programowania.
W tym samouczku uczenia się ze wzmocnieniem pokażę, jak możemy użyć PyTorch, aby nauczyć sieci neuronowej uczenia się ze wzmocnieniem, jak grać w Flappy Bird. Ale najpierw musimy omówić kilka elementów konstrukcyjnych.
Algorytmy uczenia maszynowego można z grubsza podzielić na dwie części: tradycyjne algorytmy uczenia i algorytmy uczenia głębokiego. Tradycyjne algorytmy uczenia zwykle mają znacznie mniej parametrów uczenia się niż algorytmy uczenia głębokiego i mają znacznie mniejszą zdolność uczenia się.
Ponadto tradycyjne algorytmy uczące się nie są w stanie wyodrębnić cech : specjaliści od sztucznej inteligencji muszą znaleźć dobrą reprezentację danych, które są następnie przesyłane do algorytmu uczącego. Przykłady tradycyjnych technik uczenia maszynowego obejmują SVM, losowy las, drzewo decyzyjne i średnie $k$, podczas gdy głównym algorytmem w uczeniu głębokim jest głęboka sieć neuronowa .
Dane wejściowe do głębokiej sieci neuronowej mogą stanowić surowe obrazy, a specjalista ds. sztucznej inteligencji nie musi znajdować żadnej reprezentacji danych — sieć neuronowa znajduje najlepszą reprezentację podczas procesu uczenia.
Wiele technik głębokiego uczenia jest znanych od bardzo dawna, ale ostatnie postępy w sprzęcie szybko przyspieszyły badania i rozwój głębokiego uczenia się. Nvidia jest odpowiedzialna za rozwój tej dziedziny, ponieważ jej procesory graficzne umożliwiły szybkie eksperymenty z głębokim uczeniem.
Parametry i hiperparametry do nauki
Algorytmy uczenia maszynowego składają się z parametrów uczących się, które są dostrajane w procesie uczenia oraz parametrów nieuczących się, które są ustawiane przed procesem uczenia. Parametry ustawione przed uczeniem nazywane są hiperparametrami .
Wyszukiwanie siatkowe jest powszechną metodą znajdowania optymalnych hiperparametrów. Jest to metoda brutalna: oznacza wypróbowanie wszystkich możliwych kombinacji hiperparametrów w określonym zakresie i wybranie kombinacji, która maksymalizuje predefiniowaną metrykę.
Nadzorowane, nienadzorowane i wzmacniające algorytmy uczenia się
Jednym ze sposobów klasyfikacji algorytmów uczenia się jest rozgraniczenie pomiędzy algorytmami nadzorowanymi i nienadzorowanymi. (Ale to niekoniecznie jest takie proste: uczenie się ze wzmacnianiem leży gdzieś pomiędzy tymi dwoma typami).
Kiedy mówimy o uczeniu nadzorowanym, patrzymy na pary $ (x_i, y_i) $. $ x_i $ to wejście algorytmu, a $ y_i $ to wyjście. Naszym zadaniem jest znalezienie funkcji, która wykona poprawne mapowanie z $ x_i $ na $ y_i $.
W celu dostrojenia parametrów uczenia się, aby zdefiniowały funkcję, która odwzorowuje $ x_i $ na $ y_i $, należy zdefiniować funkcję straty i optymalizator. Optymalizator minimalizuje funkcję strat. Jednym z przykładów funkcji straty jest błąd średniokwadratowy (MSE):
\[MSE = \sum_{i=1}^{n} (y_i - \widehat{y_i} )^2\]Tutaj $ y_i $ jest podstawową etykietą prawdy, a $ \widehat{y_i} $ jest przewidywaną etykietą. Jednym z optymalizatorów, który jest bardzo popularny w głębokim uczeniu się, jest stochastyczne zejście gradientowe . Istnieje wiele odmian, które próbują ulepszyć metodę stochastycznego spadku gradientu: Adam, Adadelta, Adagrad i tak dalej.
Nienadzorowane algorytmy próbują znaleźć strukturę danych bez wyraźnego opatrzenia ich etykietami. $k$-means to jeden z przykładów nienadzorowanych algorytmów, które próbują znaleźć optymalne klastry w danych. Poniżej znajduje się obraz z 300 punktami danych. $k$-oznacza, że algorytmy znalazły strukturę w danych i przypisały etykietę klastra do każdego punktu danych. Każda grupa ma swój własny kolor.
Uczenie się ze wzmacnianiem wykorzystuje nagrody: rzadkie, opóźnione w czasie etykiety. Agent podejmuje działanie, które zmienia otoczenie, z którego może uzyskać nową obserwację i nagrodę. Obserwacja to bodziec, który agent odbiera z otoczenia. Może to być to, co agent widzi, słyszy, wącha i tak dalej.
Agent otrzymuje nagrodę, gdy wykonuje akcję. Mówi agentowi, jak dobra jest akcja. Dostrzegając obserwacje i nagrody, agent uczy się, jak optymalnie zachowywać się w środowisku. Zajmę się tym bardziej szczegółowo poniżej.
Aktywne, pasywne i odwrotne uczenie ze wzmacnianiem
Istnieje kilka różnych podejść do tej techniki. Przede wszystkim mamy do czynienia z aktywnym uczeniem się ze wzmocnieniem, którego tutaj używamy. W przeciwieństwie do tego, istnieje pasywne uczenie się ze wzmocnieniem, w którym nagrody są jedynie innym rodzajem obserwacji, a decyzje są podejmowane zgodnie z ustaloną polityką.
Wreszcie, uczenie się z odwrotnym wzmocnieniem próbuje zrekonstruować funkcję nagrody, biorąc pod uwagę historię działań i ich nagrody w różnych stanach.
Generalizacja, overfitting i underfitting
Każda stała instancja parametrów i hiperparametrów nazywana jest modelem. Eksperymenty z uczeniem maszynowym zwykle składają się z dwóch części: szkolenia i testowania.
Podczas procesu uczenia parametry, których można się nauczyć, są dostrajane za pomocą danych treningowych. W procesie testowym uczące się parametry są zamrażane, a zadaniem jest sprawdzenie, jak dobrze model wykonuje predykcje na wcześniej niewidocznych danych. Uogólnienie to zdolność maszyny uczącej się do dokładnego wykonywania nowego, niewidocznego przykładu lub zadania po tym, jak doświadczyła zestawu danych uczących.
Jeśli model jest zbyt prosty w odniesieniu do danych, nie będzie w stanie dopasować danych uczących i będzie działał słabo zarówno na uczącym zestawie danych, jak i testowym zestawie danych. W takim przypadku mówimy, że model jest niedopasowany .
Jeśli model uczenia maszynowego działa dobrze na uczącym zestawie danych, ale słabo na testowym zestawie danych, mówimy, że jest przesadnie dopasowany . Overfitting to sytuacja, w której model jest zbyt złożony pod względem danych. Może idealnie dopasować dane uczące, ale jest tak bardzo przystosowany do zestawu danych uczących, że słabo radzi sobie z danymi testowymi — tj. po prostu nie uogólnia.
Poniżej znajduje się obraz przedstawiający niedopasowanie i nadmierne dopasowanie w porównaniu ze zrównoważoną sytuacją między ogólnymi danymi a funkcją przewidywania.
Skalowalność
Dane mają kluczowe znaczenie w budowaniu modeli uczenia maszynowego. Zazwyczaj tradycyjne algorytmy uczenia się nie wymagają zbyt dużej ilości danych. Ale ze względu na ich ograniczoną pojemność, wydajność jest również ograniczona. Poniżej znajduje się wykres pokazujący, jak dobrze skalują się metody uczenia głębokiego w porównaniu z tradycyjnymi algorytmami uczenia maszynowego.
Sieci neuronowe
Sieci neuronowe składają się z wielu warstw. Poniższy obraz przedstawia prostą sieć neuronową z czterema warstwami. Pierwsza warstwa to warstwa wejściowa, a ostatnia to warstwa wyjściowa. Dwie warstwy między warstwą wejściową i wyjściową są warstwami ukrytymi.
Jeśli sieć neuronowa ma więcej niż jedną ukrytą warstwę, nazywamy ją głęboką siecią neuronową. Zbiór wejściowy $ X $ jest podawany do sieci neuronowej i otrzymujemy wynik $ y $. Uczenie odbywa się za pomocą algorytmu wstecznej propagacji błędów, który łączy funkcję straty i optymalizator.
Propagacja wsteczna składa się z dwóch części: podania do przodu i podania do tyłu. W przejściu do przodu dane wejściowe są umieszczane na wejściu sieci neuronowej i uzyskiwane są dane wyjściowe. Obliczana jest strata między prawdą gruntową a przewidywaniem, a następnie w przejściu wstecznym parametry sieci neuronowych są dostrajane pod kątem straty.
Konwolucyjna sieć neuronowa
Jedną z odmian sieci neuronowej jest splotowa sieć neuronowa . Jest używany głównie do zadań widzenia komputerowego.
Najważniejszą warstwą w splotowych sieciach neuronowych jest warstwa splotowa (stąd nazwa). Jego parametry składają się z uczących się filtrów, zwanych również jądrami. Warstwy splotowe stosują operację splotu do danych wejściowych, przekazując wynik do następnej warstwy. Operacja konwolucji zmniejsza liczbę możliwych do uczenia się parametrów, działając jako rodzaj heurystyki i ułatwiając trenowanie sieci neuronowej.
Poniżej przedstawiamy, jak działa jedno jądro splotowe w warstwie splotowej. Jądro jest nakładane na obraz i uzyskuje się zawiłą cechę.
Warstwy ReLU służą do wprowadzania nieliniowości w sieci neuronowej. Nieliniowości są ważne, ponieważ za ich pomocą możemy modelować wszelkiego rodzaju funkcje, nie tylko liniowe, czyniąc sieć neuronową uniwersalnym aproksymatorem funkcji. To sprawia, że funkcja ReLU jest zdefiniowana w następujący sposób:
\[ReLU = \max(0, x)\]ReLU jest jednym z przykładów tzw. funkcji aktywacyjnych służących do wprowadzania nieliniowości w sieciach neuronowych. Przykłady innych funkcji aktywacji obejmują funkcje sigmoidalne i hiperstyczne. ReLU jest najpopularniejszą funkcją aktywacji, ponieważ wykazano, że sprawia, że pociąg sieci neuronowej jest wydajniejszy w porównaniu z innymi funkcjami aktywacji.
Poniżej znajduje się wykres funkcji ReLU.
Jak widać, ta funkcja ReLU po prostu zmienia wartości ujemne na zera. Pomaga to zapobiec problemowi znikającego gradientu. Jeśli gradient zniknie, nie będzie to miało dużego wpływu na dostrojenie wagi sieci neuronowej.
Konwolucyjna sieć neuronowa składa się z wielu warstw: warstw splotowych, warstw ReLU i warstw w pełni połączonych. W pełni połączone warstwy łączą każdy neuron w jednej warstwie z każdym neuronem w innej warstwie, jak widać z dwiema ukrytymi warstwami na obrazku na początku tej sekcji. Ostatnia w pełni połączona warstwa mapuje dane wyjściowe z poprzedniej warstwy na, w tym przypadku, wartości number_of_actions
.
Aplikacje
Głębokie uczenie jest skuteczne i przewyższa klasyczne algorytmy uczenia maszynowego w kilku poddziedzinach uczenia maszynowego, w tym wizji komputerowej, rozpoznawaniu mowy i uczeniu ze wzmocnieniem. Te dziedziny uczenia głębokiego są stosowane w różnych dziedzinach świata rzeczywistego: finansach, medycynie, rozrywce itp.
Nauka wzmacniania
Uczenie się przez wzmacnianie opiera się na agencie . Agent podejmuje działania w środowisku i otrzymuje z niego obserwacje i nagrody. Agent musi zostać przeszkolony, aby zmaksymalizować skumulowaną nagrodę. Jak zauważono we wstępie, w przypadku klasycznych algorytmów uczenia maszynowego inżynierowie zajmujący się uczeniem maszynowym muszą wyodrębniać cechy, tj. tworzyć dobre cechy, które dobrze reprezentują środowisko i które są wprowadzane do algorytmu uczenia maszynowego.
Korzystając z uczenia głębokiego, można stworzyć kompleksowy system, który pobiera wielowymiarowe dane wejściowe — np. wideo — i na jego podstawie uczy się optymalnej strategii podejmowania dobrych działań przez agenta.
W 2013 r. londyński startup AI DeepMind dokonał wielkiego przełomu w nauce kontrolowania agentów bezpośrednio z wielowymiarowych danych sensorycznych. Opublikowali artykuł Playing Atari with Deep Reinforcement Learning , w którym pokazali, w jaki sposób nauczyli sztuczną sieć neuronową grać w gry Atari od samego patrzenia na ekran. Zostały przejęte przez Google, a następnie opublikowały nowy artykuł w Nature z pewnymi ulepszeniami: Kontrola na poziomie człowieka poprzez głębokie uczenie się ze wzmocnieniem .
W przeciwieństwie do innych paradygmatów uczenia maszynowego, uczenie ze wzmocnieniem nie ma nadzorcy, a jedynie sygnał nagrody. Sprzężenie zwrotne jest opóźnione: nie jest natychmiastowe, jak w przypadku algorytmów uczenia nadzorowanego. Dane są sekwencyjne, a działania agenta wpływają na kolejne otrzymywane przez niego dane.
Teraz agent znajduje się w swoim otoczeniu, które jest w pewnym stanie. Aby opisać to bardziej szczegółowo, używamy procesu decyzyjnego Markowa, który jest formalnym sposobem modelowania tego środowiska uczenia się przez wzmocnienie. Składa się ze zbioru stanów, zbioru możliwych akcji i reguł (np. prawdopodobieństw) przejścia z jednego stanu do drugiego.
Agent jest w stanie wykonywać działania, przekształcając środowisko. Nagrodę nazywamy $R_t $. Jest to skalarny sygnał zwrotny wskazujący, jak dobrze agent radzi sobie w kroku $t$.
Aby uzyskać dobre wyniki długoterminowe, należy wziąć pod uwagę nie tylko natychmiastowe nagrody, ale także przyszłe nagrody. Całkowita nagroda za jeden odcinek od kroku czasowego $t$ wynosi $ R_t = r_t + r_{t+1} + r_{t+2} + \ldots + r_n $. Przyszłość jest niepewna, a im dalej pójdziemy, tym bardziej przyszłe prognozy mogą się różnić. Z tego powodu stosowana jest zniżkowa przyszła nagroda: $ R_t = r_t +\gamma r_{t+1} + \gamma^2r_{t+2} + \ldots + \gamma^{nt}r_n = r_t + \gamma R_{t+1} $. Agent powinien wybrać akcję, która maksymalizuje zdyskontowaną przyszłą nagrodę.
Głębokie Q-learning
Funkcja $ Q(s, a) $ reprezentuje maksymalną zdyskontowaną przyszłą nagrodę, gdy akcja $ a $ jest wykonywana
Oszacowanie przyszłej nagrody jest podane przez równanie Bellmana: $ Q(s, a) = r + \gamma \max_{a'}Q(s', a') $ . Innymi słowy, maksymalna przyszła nagroda dana stanowi $ s $ i akcja $ a $ to nagroda natychmiastowa plus maksymalna przyszła nagroda dla następnego stanu.
Aproksymacja wartości Q za pomocą funkcji nieliniowych (sieci neuronowych) nie jest zbyt stabilna. Z tego powodu powtarzanie doświadczenia służy do stabilizacji. Doświadczenia z odcinków sesji treningowej są przechowywane w pamięci powtórek. Zamiast najnowszego przejścia używane są losowe minipartie z pamięci powtórek. To łamie podobieństwo kolejnych próbek uczących, które w przeciwnym razie doprowadziłyby sieć neuronową do lokalnego minimum.

Istnieją jeszcze dwa ważne aspekty głębokiego Q-learningu, o których warto wspomnieć: Eksploracja i eksploatacja. W przypadku eksploatacji podejmowana jest najlepsza decyzja, biorąc pod uwagę aktualne informacje. Eksploracja zbiera więcej informacji.
Kiedy algorytm wykonuje działanie zaproponowane przez sieć neuronową, robi eksploatację: wykorzystuje wyuczoną wiedzę sieci neuronowej. Natomiast algorytm może podjąć losowe działanie, odkrywając nowe możliwości i wprowadzając potencjalną nową wiedzę do sieci neuronowej.
Poniżej przedstawiono „Algorytm Deep Q-learning with Experience Replay” z artykułu DeepMind Playing Atari with Deep Reinforcement Learning.
DeepMind odnosi się do sieci konwolucyjnych wyszkolonych w swoim podejściu jako Deep Q-networks (DQN).
Przykład głębokiego Q-learningu z użyciem Flappy Bird
Flappy Bird to popularna gra mobilna stworzona przez wietnamskiego artystę i programistę gier wideo Dong Nguyen. W nim gracz kontroluje ptaka i próbuje latać między zielonymi rurami, nie uderzając w nie.
Poniżej znajduje się zrzut ekranu z klona Flappy Bird zakodowanego przy użyciu PyGame:
Klon został rozwidlony i zmodyfikowany: tło, dźwięki i różne style ptaków i rur zostały usunięte, a kod został dostosowany, aby można go było łatwo używać z prostymi strukturami uczenia się przez wzmacnianie. Zmodyfikowany silnik gry pochodzi z tego projektu TensorFlow:
Ale zamiast używać TensorFlow, zbudowałem platformę uczenia głębokiego wzmacniania za pomocą PyTorch. PyTorch to platforma do głębokiego uczenia się, która umożliwia szybkie i elastyczne eksperymentowanie. Zapewnia tensory i dynamiczne sieci neuronowe w Pythonie z silną akceleracją GPU.
Architektura sieci neuronowej jest taka sama, jak w przypadku DeepMind użytego w artykule Kontrola na poziomie człowieka poprzez głębokie uczenie ze wzmocnieniem .
Warstwa | Wejście | Rozmiar filtra | Krok | Liczba filtrów | Aktywacja | Wyjście |
---|---|---|---|---|---|---|
konw1 | 84x84x4 | 8x8 | 4 | 32 | ReLU | 20x20x32 |
konw.2 | 20x20x32 | 4x4 | 2 | 64 | ReLU | 9x9x64 |
konw3 | 9x9x64 | 3x3 | 1 | 64 | ReLU | 7x7x64 |
fc4 | 7x7x64 | 512 | ReLU | 512 | ||
fc5 | 512 | 2 | Liniowy | 2 |
Istnieją trzy warstwy splotowe i dwie w pełni połączone warstwy. Każda warstwa wykorzystuje aktywację ReLU, z wyjątkiem ostatniej, która wykorzystuje aktywację liniową. Sieć neuronowa wyprowadza dwie wartości reprezentujące jedyne możliwe działania gracza: „Podnieś się” i „Nie rób nic”.
Wejście składa się z czterech kolejnych czarno-białych obrazów 84x84. Poniżej znajduje się przykład czterech obrazów, które są przesyłane do sieci neuronowej.
Zauważysz, że obrazy są obrócone. Dzieje się tak, ponieważ wyjście silnika gry klona jest obracane. Ale jeśli sieć neuronowa zostanie wyuczona, a następnie przetestowana przy użyciu takich obrazów, nie wpłynie to na jej wydajność.
Możesz również zauważyć, że obraz jest przycięty tak, że pominięto podłogę, ponieważ nie ma to znaczenia dla tego zadania. Wszystkie piksele reprezentujące rury i ptaka są białe, a wszystkie piksele reprezentujące tło są czarne.
Jest to część kodu, który definiuje sieć neuronową. Wagi sieci neuronowych są inicjowane zgodnie z rozkładem jednolitym $\mathcal{U}(-0.01, 0.01)$. Część bias parametrów sieci neuronowych jest ustawiona na 0,01. Wypróbowano kilka różnych inicjalizacji (uniwersalny Xavier, normalny Xavier, normalny Kaiming, normalny Kaiming, jednolity i normalny), ale powyższa inicjalizacja spowodowała, że sieć neuronowa zbiega się i trenuje najszybciej. Rozmiar sieci neuronowej to 6,8 MB.
class NeuralNetwork(nn.Module): def __init__(self): super(NeuralNetwork, self).__init__() self.number_of_actions = 2 self.gamma = 0.99 self.final_epsilon = 0.0001 self.initial_epsilon = 0.1 self.number_of_iterations = 2000000 self.replay_memory_size = 10000 self.minibatch_size = 32 self.conv1 = nn.Conv2d(4, 32, 8, 4) self.relu1 = nn.ReLU(inplace=True) self.conv2 = nn.Conv2d(32, 64, 4, 2) self.relu2 = nn.ReLU(inplace=True) self.conv3 = nn.Conv2d(64, 64, 3, 1) self.relu3 = nn.ReLU(inplace=True) self.fc4 = nn.Linear(3136, 512) self.relu4 = nn.ReLU(inplace=True) self.fc5 = nn.Linear(512, self.number_of_actions) def forward(self, x): out = self.conv1(x) out = self.relu1(out) out = self.conv2(out) out = self.relu2(out) out = self.conv3(out) out = self.relu3(out) out = out.view(out.size()[0], -1) out = self.fc4(out) out = self.relu4(out) out = self.fc5(out) return out
W konstruktorze zauważysz, że są zdefiniowane hiperparametry. Optymalizacja hiperparametrów nie została wykonana na potrzeby tego wpisu na blogu. Zamiast tego hiperparametry są najczęściej używane z artykułów DeepMind. Tutaj niektóre hiperparametry są skalowane tak, aby były niższe niż w artykule DeepMind, ponieważ Flappy Bird jest mniej skomplikowany niż gry na Atari, których używali do strojenia.
Zmieniono także epsilon, aby był bardziej rozsądny dla tej gry. DeepMind używa jednego epsilon, ale tutaj używamy 0.1. Dzieje się tak, ponieważ wyższe epsilony zmuszają ptaka do mocnego trzepotania, co popycha ptaka w kierunku górnej granicy ekranu, zawsze powodując zderzenie ptaka z rurą.
Kod uczenia się przez wzmacnianie ma dwa tryby: Trenuj i testuj. Podczas fazy testowej możemy zobaczyć, jak dobrze algorytm uczenia się przez wzmacnianie nauczył się grać w tę grę. Ale najpierw trzeba wyszkolić sieć neuronową. Musimy zdefiniować funkcję straty, która ma być minimalizowana oraz optymalizatory, które będą minimalizować funkcję straty. Użyjemy metody optymalizacji Adama i błędu średniokwadratowego dla funkcji straty:
optimizer = optim.Adam(model.parameters(), lr=1e-6) criterion = nn.MSELoss()
Gra powinna mieć instancję:
game_state = GameState()
Pamięć powtórek jest zdefiniowana jako lista Pythona:
replay_memory = []
Teraz musimy zainicjować pierwszy stan. Akcja jest dwuwymiarowym tensorem:
- [1, 0] oznacza „nic nie rób”
- [0, 1] oznacza „latać w górę”
Metoda frame_step
daje nam następny ekran, nagrodę i informację, czy następny stan jest terminalem. Nagroda wynosi 0.1
za ruch każdego ptaka bez śmierci, jeśli nie przechodzi przez rurę, 1
, jeśli ptak pomyślnie przejdzie przez rurę i -1
, jeśli ptak się rozbije.
Funkcja resize_and_bgr2gray
podłogę, zmienia rozmiar ekranu do obrazu 84x84 i zmienia przestrzeń kolorów z BGR na czarno-białą. Funkcja image_to_tensor
konwertuje obraz na tensor PyTorch i umieszcza go w pamięci GPU, jeśli CUDA jest dostępne. Na koniec, ostatnie cztery kolejne ekrany są połączone razem i są gotowe do wysłania do sieci neuronowej.
action = torch.zeros([model.number_of_actions], dtype=torch.float32) action[0] = 1 image_data, reward, terminal = game_state.frame_step(action) image_data = resize_and_bgr2gray(image_data) image_data = image_to_tensor(image_data) state = torch.cat((image_data, image_data, image_data, image_data)).unsqueeze(0)
Początkowy epsilon jest ustawiany za pomocą tego wiersza kodu:
epsilon = model.initial_epsilon
Następuje główna nieskończona pętla. Komentarze są zapisywane w kodzie i możesz porównać kod z Deep Q-learning z algorytmem Experience Replay opisanym powyżej.
Algorytm pobiera próbki mini-partii z pamięci odtwarzania i aktualizuje parametry sieci neuronowej. Akcje są wykonywane za pomocą żarłocznej eksploracji epsilon . Epsilon jest z czasem wyżarzany. Minimalizowana funkcja straty to $ L = \frac{1}{2}\left[\max_{a'}Q(s', a') - Q(s, a)\right]^2 $ . $ Q(s, a) $ to podstawowa wartość prawdy obliczona za pomocą równania Bellmana, a $ \max_{a'}Q(s', a') $ jest otrzymywana z sieci neuronowej. Sieć neuronowa podaje dwie wartości Q dla dwóch możliwych działań, a algorytm przyjmuje działanie o najwyższej wartości Q.
while iteration < model.number_of_iterations: # get output from the neural network output = model(state)[0] # initialize action action = torch.zeros([model.number_of_actions], dtype=torch.float32) if torch.cuda.is_available(): # put on GPU if CUDA is available action = action.cuda() # epsilon greedy exploration random_action = random.random() <= epsilon if random_action: print("Performed random action!") action_index = [torch.randint(model.number_of_actions, torch.Size([]), dtype=torch.int) if random_action else torch.argmax(output)][0] if torch.cuda.is_available(): # put on GPU if CUDA is available action_index = action_index.cuda() action[action_index] = 1 # get next state and reward image_data_1, reward, terminal = game_state.frame_step(action) image_data_1 = resize_and_bgr2gray(image_data_1) image_data_1 = image_to_tensor(image_data_1) state_1 = torch.cat((state.squeeze(0)[1:, :, :], image_data_1)).unsqueeze(0) action = action.unsqueeze(0) reward = torch.from_numpy(np.array([reward], dtype=np.float32)).unsqueeze(0) # save transition to replay memory replay_memory.append((state, action, reward, state_1, terminal)) # if replay memory is full, remove the oldest transition if len(replay_memory) > model.replay_memory_size: replay_memory.pop(0) # epsilon annealing epsilon = epsilon_decrements[iteration] # sample random minibatch minibatch = random.sample(replay_memory, min(len(replay_memory), model.minibatch_size)) # unpack minibatch state_batch = torch.cat(tuple(d[0] for d in minibatch)) action_batch = torch.cat(tuple(d[1] for d in minibatch)) reward_batch = torch.cat(tuple(d[2] for d in minibatch)) state_1_batch = torch.cat(tuple(d[3] for d in minibatch)) if torch.cuda.is_available(): # put on GPU if CUDA is available state_batch = state_batch.cuda() action_batch = action_batch.cuda() reward_batch = reward_batch.cuda() state_1_batch = state_1_batch.cuda() # get output for the next state output_1_batch = model(state_1_batch) # set y_j to r_j for terminal state, otherwise to r_j + gamma*max(Q) y_batch = torch.cat(tuple(reward_batch[i] if minibatch[i][4] else reward_batch[i] + model.gamma * torch.max(output_1_batch[i]) for i in range(len(minibatch)))) # extract Q-value q_value = torch.sum(model(state_batch) * action_batch, dim=1) # PyTorch accumulates gradients by default, so they need to be reset in each pass optimizer.zero_grad() # returns a new Tensor, detached from the current graph, the result will never require gradient y_batch = y_batch.detach() # calculate loss loss = criterion(q_value, y_batch) # do backward pass loss.backward() optimizer.step() # set state to be state_1 state = state_1
Teraz, gdy wszystkie elementy są na swoim miejscu, oto ogólny przegląd przepływu danych za pomocą naszej sieci neuronowej:
Oto krótka sekwencja z wytrenowaną siecią neuronową.
Pokazana powyżej sieć neuronowa była trenowana przy użyciu wysokiej klasy GPU Nvidia GTX 1080 przez kilka godzin; używając zamiast tego rozwiązania opartego na procesorze, to konkretne zadanie zajęłoby kilka dni. FPS silnika gry został ustawiony na bardzo dużą liczbę podczas treningu: 999…999 — innymi słowy, jak najwięcej klatek na sekundę. W fazie testów FPS został ustawiony na 30.
Poniżej znajduje się wykres pokazujący, jak zmieniła się maksymalna wartość Q podczas iteracji. Wyświetlana jest co 10 000 iteracja. Skok w dół oznacza, że dla określonej klatki (jedna iteracja to jedna klatka) sieć neuronowa przewiduje, że ptak otrzyma bardzo niską nagrodę w przyszłości, tj. bardzo szybko się zawiesi.
Pełny kod i wstępnie wytrenowany model są dostępne tutaj.
Głębokie uczenie się przez wzmacnianie: 2D, 3D, a nawet prawdziwe życie
W tym samouczku dotyczącym uczenia się przez wzmacnianie PyTorch pokazałem, jak komputer może nauczyć się grać w Flappy Bird bez wcześniejszej wiedzy na temat gry, używając tylko metody prób i błędów, tak jak zrobiłby to człowiek, gdy spotyka się z grą po raz pierwszy.
Ciekawe, że algorytm można zaimplementować w kilku linijkach kodu za pomocą frameworka PyTorch. Artykuł, na którym opiera się metoda w tym blogu, jest stosunkowo stary i dostępnych jest wiele nowszych artykułów z różnymi modyfikacjami umożliwiającymi szybszą konwergencję. Od tego czasu uczenie głębokiego wzmocnienia jest wykorzystywane do grania w gry 3D oraz w rzeczywistych systemach robotycznych.
Firmy takie jak DeepMind, Maluuba i Vicarious intensywnie pracują nad uczeniem się przez głębokie wzmocnienie. Taka technologia została zastosowana w AlphaGo, która pokonała Lee Sedola, jednego z najlepszych graczy na świecie w Go. W tamtym czasie uważano, że minie co najmniej dziesięć lat, zanim maszyny pokonają najlepszych graczy w Go.
Zalew zainteresowania i inwestycji w uczenie się przez głębokie wzmocnienie (i ogólnie w sztuczną inteligencję) może nawet prowadzić do potencjalnej sztucznej ogólnej inteligencji (AGI) – inteligencji na poziomie człowieka (lub nawet poza nią), która może być wyrażona w formie algorytmu i symulowane na komputerach. Ale AGI będzie musiało być tematem innego artykułu.
Bibliografia:
- Demistyfikacja głębokiego wzmacniania nauki
- Nauka głębokiego wzmacniania dla Flappy Bird
- „Splotowa sieć neuronowa” w Wikipedii
- „Uczenie się przez wzmacnianie” w Wikipedii
- „Proces decyzyjny Markowa” w Wikipedii
- Kurs University College London na RL