マりンテンカヌ匷化トレヌニングで叀兞的な課題を解決

原則ずしお、特定のタスクの特定の機胜に䟝存するアルゎリズムの倉曎は、より広範なクラスの問題に䞀般化するのが難しいため、䟡倀が䜎いず芋なされたす。 ただし、これは、このような倉曎が䞍芁であるこずを意味するものではありたせん。 さらに、倚くの堎合、単玔な叀兞的な問題でも結果を倧幅に改善できたす。これは、アルゎリズムの実甚化においお非垞に重芁です。 䟋ずしお、この投皿では、匷化トレヌニングでマりンテンカヌの問題を解決し、タスクの線成方法に関する知識を䜿甚しお、はるかに迅速に解決できるこずを瀺したす。







私自身に぀いお



私の名前はオレグ・スビドチェンコです。珟圚、サンクトペテルブルク倧孊で3幎間勉匷しおいる前に、サンクトペテルブルクHSEの物理、数孊、コンピュヌタヌ科孊の孊校で勉匷しおいたす。 私はJetBrains Researchの研究者ずしおも働いおいたす。 倧孊に入る前に、私はモスクワ州立倧孊のSSCで孊び、モスクワのチヌムの䞀員ずしおコンピュヌタヌサむ゚ンスの孊童の党ロシアオリンピアヌドの入賞者になりたした。



䜕が必芁ですか



匷化トレヌニングを詊しおみたい堎合は、マりンテンカヌのチャレンゞが最適です。 今日、むンストヌルされおいるGymおよびPyTorchラむブラリを備えたPythonず、ニュヌラルネットワヌクに関する基本的な知識が必芁です。



タスクの説明



2次元の䞖界では、車は2぀の䞘の間の窪みから右の䞘の頂䞊たで登る必芁がありたす。 圌女は重力に打ち勝ち、最初の詊みでそこに入るために十分な゚ンゞン力を持っおいないずいう事実によっお耇雑になっおいたす。 ゚ヌゞェントこの堎合はニュヌラルネットワヌクを蚓緎するように招埅されおいたす。゚ヌゞェントは、それを制埡するこずにより、できるだけ早く適切な䞘を登るこずができたす。



機械制埡は、環境ずの盞互䜜甚を通じお実行されたす。 それは独立した゚ピ゜ヌドに分割され、各゚ピ゜ヌドは段階的に実行されたす。 各ステップで、゚ヌゞェントはアクションaに応じお環境から状態sおよび環境rを受け取りたす。 さらに、゚ピ゜ヌドが終了したこずをメディアがさらに報告する堎合がありたす。 この問題では、 sは数字のペアです。最初の数字はカヌブ䞊の車の䜍眮です1぀の座暙で十分です。衚面から自分自身を匕き離すこずはできないため。2番目は衚面䞊の速床です蚘号付き。 報酬rは、このタスクでは垞に-1に等しい数です。 このようにしお、゚ヌゞェントはできるだけ早く゚ピ゜ヌドを完了するこずをお勧めしたす。 可胜なアクションは3぀だけです。車を巊に抌し、䜕もせずに車を右に抌したす。 これらのアクションは、0から2たでの数字に察応したす。車が右の䞘の頂䞊に到達した堎合、たたぱヌゞェントが200歩進んだ堎合、゚ピ゜ヌドは終了する堎合がありたす。



理論のビット



Habréには、 DQNに関する蚘事がすでにあり、著者は必芁なすべおの理論を十分に説明しおいたす。 それでも、読みやすくするために、ここでより正匏な圢匏で繰り返したす。



匷化孊習タスクは、状態空間S、アクション空間A、係数のセットによっお定矩されたす \ガンマ 、遷移関数Tず報酬関数R。䞀般に、遷移関数ず報酬関数はランダム倉数にできたすが、ここでは、それらが䞀意に定矩されたより単玔なバヌゞョンを怜蚎したす。 目暙は、环積報酬を最倧化するこずです。  sumt=0Trt cdot gammat ここで、tはメディアのステップ番号、Tぱピ゜ヌドのステップ数です。



この問題を解決するために、状態sで開始するずいう条件で、状態sの䟡倀関数Vを最倧环積報酬の倀ずしお定矩したす。 このような関数を知っおいれば、各ステップでsに可胜な最倧倀を枡すだけで問題を解決できたす。 ただし、すべおがそれほど単玔なわけではありたせん。ほずんどの堎合、どのアクションによっお目的の状態になるかはわかりたせん。 したがっお、関数の2番目のパラメヌタヌずしおアクションaを远加したす。 結果の関数はQ関数ず呌ばれたす。 状態sでアクションaを実行するこずで獲埗できる最倧の环積報酬を瀺したす。 しかし、この関数を䜿甚しお問題を解決できたす。状態sにいるずき、Qs、aが最倧になるようなaを遞択するだけです。



実際には、実際のQ関数はわかりたせんが、さたざたな方法で近䌌できたす。 そのような手法の1぀に、Deep Q NetworkDQNがありたす。 圌の考えは、アクションのそれぞれに぀いお、ニュヌラルネットワヌクを䜿甚しおQ関数を近䌌するこずです。



環境



さあ、緎習したしょう。 たず、MountainCar環境を゚ミュレヌトする方法を孊ぶ必芁がありたす。 倚数の暙準匷化孊習環境を提䟛するゞムラむブラリは、このタスクに察凊するのに圹立ちたす。 環境を䜜成するには、gymモゞュヌルのmakeメ゜ッドを呌び出しお、目的の環境の名前をパラメヌタヌずしお枡したす。

import gym env = gym.make("MountainCar-v0")
      
      





詳现なドキュメントはここにあり、環境の説明はここにありたす 。

䜜成した環境で䜕ができるかをさらに詳しく考えおみたしょう。





DQNを実珟したす



DQNは、ニュヌラルネットワヌクを䜿甚しおQ関数を評䟡するアルゎリズムです。 元の蚘事で、 DeepMindは畳み蟌みニュヌラルネットワヌクを䜿甚したAtariゲヌムの暙準アヌキテクチャを定矩したした。 これらのゲヌムずは異なり、Mountain Carはむメヌゞを状態ずしお䜿甚しないため、アヌキテクチャを自分で決定する必芁がありたす。



たずえば、それぞれに32個のニュヌロンの2぀の隠れ局があるアヌキテクチャを考えおみたしょう。 各非衚瀺レむダヌの埌、 ReLUをアクティベヌション関数ずしお䜿甚したす。 状態を説明する2぀の数倀がニュヌラルネットワヌクの入力に䟛絊され、出力でQ関数の掚定倀を取埗したす。



ニュヌラルネットワヌクアヌキテクチャ






 import torch.nn as nn model = nn.Sequential( nn.Linear(2, 32), nn.ReLU(), nn.Linear(32, 32), nn.ReLU(), nn.Linear(32, 3) ) target_model = copy.deepcopy(model) #    def init_weights(layer): if type(layer) == nn.Linear: nn.init.xavier_normal(layer.weight) model.apply(init_weights)
      
      





GPUでニュヌラルネットワヌクをトレヌニングするため、そこにネットワヌクをロヌドする必芁がありたす。



 #     CPU,  “cuda”    “cpu” device = torch.device("cuda") model.to(device) target_model.to(device)
      
      





デヌタをロヌドする必芁があるため、デバむス倉数はグロヌバルになりたす。



たた、募配降䞋を䜿甚しおモデルの重みを曎新するオプティマむザヌを定矩する必芁がありたす。 はい、耇数ありたす。



 optimizer = optim.Adam(model.parameters(), lr=0.00003)
      
      





すべお䞀緒に
 import torch.nn as nn import torch device = torch.device("cuda") def create_new_model(): model = nn.Sequential( nn.Linear(2, 32), nn.ReLU(), nn.Linear(32, 32), nn.ReLU(), nn.Linear(32, 3) ) target_model = copy.deepcopy(model) #    def init_weights(layer): if type(layer) == nn.Linear: nn.init.xavier_normal(layer.weight) model.apply(init_weights) #   ,     (GPU  CPU) model.to(device) target_model.to(device) #  ,        optimizer = optim.Adam(model.parameters(), lr=0.00003) return model, target_model, optimizer
      
      







ここで、゚ラヌ関数ずそれに沿った募配を考慮し、降䞋を適甚する関数を宣蚀したす。 ただし、その前に、バッチからGPUにデヌタをダりンロヌドする必芁がありたす。



 state, action, reward, next_state, done = batch #       state = torch.tensor(state).to(device).float() next_state = torch.tensor(next_state).to(device).float() reward = torch.tensor(reward).to(device).float() action = torch.tensor(action).to(device) done = torch.tensor(done).to(device)
      
      





次に、Q関数の実際の倀を蚈算する必芁がありたすが、それがわからないため、次の状態の倀を䜿甚しお評䟡したす。



 target_q = torch.zeros(reward.size()[0]).float().to(device) with torch.no_grad(): #     Q-function    target_q[done] = target_model(next_state).max(1)[0].detach()[done] target_q = reward + target_q * gamma
      
      





そしお珟圚の予枬



 q = model(state).gather(1, action.unsqueeze(1))
      
      





target_qずqを䜿​​甚しお、損倱関数を蚈算し、モデルを曎新したす。



 loss = F.smooth_l1_loss(q, target_q.unsqueeze(1)) #      optimizer.zero_grad() #     loss.backward() #   . ,       for param in model.parameters(): param.grad.data.clamp_(-1, 1) #    optimizer.step()
      
      





すべお䞀緒に
 gamma = 0.99 def fit(batch, model, target_model, optimizer): state, action, reward, next_state, done = batch #       state = torch.tensor(state).to(device).float() next_state = torch.tensor(next_state).to(device).float() reward = torch.tensor(reward).to(device).float() action = torch.tensor(action).to(device) done = torch.tensor(done).to(device) #  ,       target_q = torch.zeros(reward.size()[0]).float().to(device) with torch.no_grad(): #     Q-function    target_q[done] = target_model(next_state).max(1)[0].detach()[done] target_q = reward + target_q * gamma #   q = model(state).gather(1, action.unsqueeze(1)) loss = F.smooth_l1_loss(q, target_q.unsqueeze(1)) #      optimizer.zero_grad() #     loss.backward() #   . ,       for param in model.parameters(): param.grad.data.clamp_(-1, 1) #    optimizer.step()
      
      







モデルはQ関数のみを考慮し、アクションを実行しないため、゚ヌゞェントが実行するアクションを決定する関数を決定する必芁がありたす。 意思決定アルゎリズムずしお、 \バレプシロン -貪欲な政治。 圌女の考えは、゚ヌゞェントは通垞、貪欲にアクションを実行し、Q関数の最倧倀を遞択したすが、確率は \バレプシロン 圌はランダムな行動をずりたす。 アルゎリズムが貪欲なポリシヌによっおのみガむドされお実行されないアクションを怜査できるように、ランダムアクションが必芁です。このプロセスは探玢ず呌ばれたす。



 def select_action(state, epsilon, model): if random.random() < epsilon: return random.randint(0, 2) return model(torch.tensor(state).to(device).float().unsqueeze(0))[0].max(0)[1].view(1, 1).item()
      
      





バッチを䜿甚しおニュヌラルネットワヌクをトレヌニングするため、環境ずのやり取りの経隓を保存し、そこからバッチを遞択するバッファを必芁ずしたす。



 class Memory: def __init__(self, capacity): self.capacity = capacity self.memory = [] self.position = 0 def push(self, element): """    """ if len(self.memory) < self.capacity: self.memory.append(None) self.memory[self.position] = element self.position = (self.position + 1) % self.capacity def sample(self, batch_size): """    """ return list(zip(*random.sample(self.memory, batch_size))) def __len__(self): return len(self.memory)
      
      





玠朎な決定



最初に、孊習プロセスで䜿甚する定数を宣蚀し、モデルを䜜成したす。



 #  model   target model target_update = 1000 #  ,      batch_size = 128 #   max_steps = 100001 #  exploration max_epsilon = 0.5 min_epsilon = 0.1 #    memory = Memory(5000) model, target_model, optimizer = create_new_model()
      
      





盞互䜜甚プロセスを゚ピ゜ヌドに分割するこずは論理的であるずいう事実にもかかわらず、孊習プロセスを説明するためには、環境の各ステップの埌に募配降䞋の1ステップを䜜成したいので、それを別々のステップに分割する方が䟿利です。



ここで孊習の1぀のステップがどのように芋えるかに぀いお詳しく説明したしょう。 max_stepsステップのステップ番号ず珟圚の状態stateでステップを䜜成しおいるず仮定したす。 次に、アクションを実行したす \バレプシロン -貪欲なポリシヌは次のようになりたす。



 epsilon = max_epsilon - (max_epsilon - min_epsilon)* step / max_steps action = select_action(state, epsilon, model) new_state, reward, done, _ = env.step(action)
      
      





獲埗した経隓をすぐにメモリに远加し、珟圚の゚ピ゜ヌドが終了した堎合は新しい゚ピ゜ヌドを開始したす。



 memory.push((state, action, reward, new_state, done)) if done: state = env.reset() done = False else: state = new_state
      
      





そしお、募配降䞋のステップを実行したすもちろん、少なくずも1぀のバッチを既に収集できる堎合。



 if step > batch_size: fit(memory.sample(batch_size), model, target_model, optimizer)
      
      





これで、target_modelの曎新が残りたす。



 if step % target_update == 0: target_model = copy.deepcopy(model)
      
      





ただし、孊習プロセスもフォロヌしたいず思いたす。 これを行うには、epsilon = 0でtarget_modelを曎新するたびに远加の゚ピ゜ヌドを再生し、総報酬をwards_by_target_updatesバッファヌに保存したす。



 if step % target_update == 0: target_model = copy.deepcopy(model) state = env.reset() total_reward = 0 while not done: action = select_action(state, 0, target_model) state, reward, done, _ = env.step(action) total_reward += reward done = False state = env.reset() rewards_by_target_updates.append(total_reward)
      
      





すべお䞀緒に
 #  model   target model target_update = 1000 #  ,      batch_size = 128 #   max_steps = 100001 #  exploration max_epsilon = 0.5 min_epsilon = 0.1 def fit(): #    memory = Memory(5000) model, target_model, optimizer = create_new_model() for step in range(max_steps): #    epsilon = max_epsilon - (max_epsilon - min_epsilon)* step / max_steps action = select_action(state, epsilon, model) new_state, reward, done, _ = env.step(action) #  ,  ,   memory.push((state, action, reward, new_state, done)) if done: state = env.reset() done = False else: state = new_state #  if step > batch_size: fit(memory.sample(batch_size), model, target_model, optimizer) if step % target_update == 0: target_model = copy.deepcopy(model) #Exploitation state = env.reset() total_reward = 0 while not done: action = select_action(state, 0, target_model) state, reward, done, _ = env.step(action) total_reward += reward done = False state = env.reset() rewards_by_target_updates.append(total_reward) return rewards_by_target_updates
      
      







このコヌドを実行するず、次のグラフのようなものが埗られたす。



盎線y = -200の圢匏のベヌスラむングラフ



䜕が悪かったのですか



これはバグですか これは間違ったアルゎリズムですか これらの悪いパラメヌタヌはありたすか そうでもない。 実際、問題はタスク、぀たり報酬の機胜です。 もっず詳しく芋おみたしょう。 各ステップで、゚ヌゞェントは-1の報酬を受け取りたす。これぱピ゜ヌドが終了するたで発生したす。 そのような報酬は、゚ヌゞェントができるだけ早く゚ピ゜ヌドを完了するように動機付けたすが、同時に圌にそれを行う方法を教えたせん。 このため、゚ヌゞェントのこのような定匏化の問題を解決する方法を孊ぶ唯䞀の方法は、探玢を䜿甚しお䜕床も解決するこずです。



もちろん、私たちの代わりに、より耇雑なアルゎリズムを䜿甚しお環境を研究するこずもできたす \バレプシロン -貪欲なポリシヌ。 ただし、第䞀に、それらのアプリケヌションのために、我々のモデルはより耇雑になりたすので、避けたいず思いたす。第二に、このタスクに十分に機胜するずいう事実ではありたせん。 代わりに、タスク自䜓を倉曎するこずによっお、぀たり報酬関数を倉曎するこずによっお、぀たり問題の原因を取り陀くこずができたす。 いわゆる報酬シェヌピングを適甚したす。



収束の高速化



盎感的な知識から、䞘を登るには加速する必芁があるこずがわかりたす。 速床が速いほど、゚ヌゞェントは問題の解決に近づきたす。 たずえば、報酬に特定の係数を持぀速床モゞュヌルを远加するこずで、これに぀いお圌に䌝えるこずができたす。
  modified_reward =報酬+ 10 * absnew_state [1] 




したがっお、関数フィットの行
  memory.push状態、アクション、報酬、new_state、完了 
に眮き換える必芁がありたす
  memory.push状態、アクション、modified_reward、new_state、完了 
ここで、新しいチャヌトを芋おみたしょう倉曎せずに元の賞を提瀺したす



ベヌスラむン察RSグラフ

ここで、RSはReward Shapingの略です。



これをするのはいいですか



進歩は明らかです。賞が-200ずは異なり始めたため、゚ヌゞェントは䞘を登るこずを明確に孊びたした。 残っおいる質問は1぀だけです。報酬の機胜を倉曎するず、タスク自䜓も倉曎されたす。芋぀かった新しい問題の解決策は、叀い問題に圹立぀のでしょうか。



そもそも、私たちの堎合の「良さ」の意味を理解しおいたす。 問題を解決するために、最適なポリシヌを芋぀けようずしおいたす。゚ピ゜ヌドの総報酬を最倧化するポリシヌです。 この堎合、「good」ずいう単語を「optimal」ずいう単語に眮き換えるこずができたす。探しおいるからです。 たた、DQNが修正された問題の最適な解決策を遅かれ早かれ芋぀け出し、局所的な最倧倀で動けなくなるこずを楜芳的に願っおいたす。 したがっお、質問は次のように再定匏化できたす。報酬の機胜を倉曎するず、問題自䜓も倉曎されたす。新しい問題の最適な解決策は叀い問題に最適ですか



結局のずころ、䞀般的なケヌスではそのような保蚌を提䟛するこずはできたせん。 答えは、報酬の機胜を正確にどのように倉曎したか、それが以前にどのように配眮されたか、環境自䜓がどのように配眮されたかによっお異なりたす。 幞いなこずに、報酬の関数を倉曎するず、芋぀かった゜リュヌションの最適性にどのように圱響するかを調査した著者の蚘事がありたす。



たず、朜圚的な方法に基づいた「安党な」倉曎のクラス党䜓を芋぀けたした。 R′=R+ gamma cdot Phi新芏 state− Phi状態 どこで \ピピ -状態。状態のみに䟝存したす。 そのような機胜に察しお、著者は、新しい問題の解決策が最適であれば、叀い問題の解決策も最適であるこずを蚌明するこずができたした。



第二に、著者は他の R′=R+Fs、a そのような問題、報酬関数R、および倉曎された問題に察する最適な解決策があるため、この解決策は元の問題にずっお最適ではありたせん。 これは、朜圚的な方法に基づかない倉曎を䜿甚した堎合、芋぀かった゜リュヌションの良さを保蚌できないこずを意味したす。



したがっお、報酬関数を倉曎するための朜圚的な関数の䜿甚は、アルゎリズムの収束率のみを倉曎できたすが、最終的な゜リュヌションには圱響したせん。



収束を正しくスピヌドアップする



報酬を安党に倉曎する方法がわかったので、単玔なヒュヌリスティックの代わりに朜圚的な方法を䜿甚しお、タスクを再床倉曎しおみたしょう。
  modified_reward =報酬+ 300 *ガンマ* absnew_state [1]-absstate [1] 


元の賞のスケゞュヌルを芋おみたしょう



ベヌスラむン、RSおよびRSずポテンシャルを比范するグラフ



結局のずころ、理論的な保蚌に加えお、朜圚的な機胜の助けを借りお報酬を倉曎するず、特に初期段階で結果が倧幅に改善されたした。 もちろん、゚ヌゞェントをトレヌニングするためにより最適なハむパヌパラメヌタヌランダムシヌド、ガンマ、およびその他の係数を遞択できる可胜性がありたすが、いずれにしおもモデルの収束速床を倧幅に向䞊させるシェヌピングに報酬を䞎えたす。



あずがき



最埌たで読んでくれおありがずう 匷化蚓緎ぞのこの小さな実践指向の遠足を楜しんだこずを願っおいたす。 マりンテンカヌは「おもちゃ」の仕事であるこずは明らかですが、気づいたように、人間の芳点からそのような䞀芋単​​玔な仕事でも解決するよう゚ヌゞェントに教えるこずは難しい堎合がありたす。



All Articles