目錄
- 簡介
- 代碼
簡介
DQN(Deep Q-Network)是一種基于深度神經網絡的強化學習算法,于2013年由DeepMind提出。它的目標是解決具有離散動作空間的強化學習問題,并在多個任務中取得了令人矚目的表現。
DQN的核心思想是使用深度神經網絡來逼近狀態-動作值函數(Q函數),將當前狀態作為輸入,輸出每個可能動作的Q值估計。通過不斷迭代和更新網絡參數,DQN能夠逐步學習到最優的Q函數,并根據Q值選擇具有最大潛在回報的動作。
DQN的訓練過程中采用了兩個關鍵技術:經驗回放和目標網絡。經驗回放是一種存儲并重復使用智能體經歷的經驗的方法,它可以破壞數據之間的相關性,提高訓練的穩定性。目標網絡用于解決訓練過程中的估計器沖突問題,通過固定一個與訓練網絡參數較為獨立的目標網絡來提供穩定的目標Q值,從而減少訓練的不穩定性。
DQN還采用了一種策略稱為epsilon-貪心策略來在探索和利用之間進行權衡。初始時,智能體以較高的概率選擇隨機動作(探索),隨著訓練的進行,該概率逐漸降低,讓智能體更多地依靠Q值選擇最佳動作(利用)。
DQN在許多復雜任務中取得了顯著的成果,特別是在Atari游戲等需要視覺輸入的任務中。它的成功在很大程度上得益于深度神經網絡的強大擬合能力和經驗回放的效果,使得智能體能夠通過與環境的交互進行自主學習。
代碼
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
import gym# Hyper Parameters
BATCH_SIZE = 32
LR = 0.01 # learning rate
EPSILON = 0.9 # greedy policy
GAMMA = 0.9 # reward discount
TARGET_REPLACE_ITER = 100 # target update frequency
MEMORY_CAPACITY = 2000
env = gym.make('CartPole-v1',render_mode="human")
#env = gym.make('CartPole-v0')
env = env.unwrapped
N_ACTIONS = env.action_space.n
N_STATES = env.observation_space.shape[0]
ENV_A_SHAPE = 0 if isinstance(env.action_space.sample(), int) else env.action_space.sample().shape # to confirm the shapeclass Net(nn.Module):def __init__(self, ):super(Net, self).__init__()self.fc1 = nn.Linear(N_STATES, 50)self.fc1.weight.data.normal_(0, 0.1) # initializationself.out = nn.Linear(50, N_ACTIONS)self.out.weight.data.normal_(0, 0.1) # initializationdef forward(self, x):x = self.fc1(x)x = F.relu(x)actions_value = self.out(x)return actions_valueclass DQN(object):def __init__(self):self.eval_net, self.target_net = Net(), Net()self.learn_step_counter = 0 # for target updatingself.memory_counter = 0 # for storing memoryself.memory = np.zeros((MEMORY_CAPACITY, N_STATES * 2 + 2)) # initialize memoryself.optimizer = torch.optim.Adam(self.eval_net.parameters(), lr=LR)self.loss_func = nn.MSELoss()def choose_action(self, x):x = torch.unsqueeze(torch.FloatTensor(x), 0)# input only one sampleif np.random.uniform() < EPSILON: # greedyactions_value = self.eval_net.forward(x)action = torch.max(actions_value, 1)[1].data.numpy()action = action[0] if ENV_A_SHAPE == 0 else action.reshape(ENV_A_SHAPE) # return the argmax indexelse: # randomaction = np.random.randint(0, N_ACTIONS)action = action if ENV_A_SHAPE == 0 else action.reshape(ENV_A_SHAPE)return actiondef store_transition(self, s, a, r, s_):transition = np.hstack((s, [a, r], s_))# replace the old memory with new memoryindex = self.memory_counter % MEMORY_CAPACITYself.memory[index, :] = transitionself.memory_counter += 1def learn(self):# target parameter updateif self.learn_step_counter % TARGET_REPLACE_ITER == 0:self.target_net.load_state_dict(self.eval_net.state_dict())self.learn_step_counter += 1# sample batch transitionssample_index = np.random.choice(MEMORY_CAPACITY, BATCH_SIZE)b_memory = self.memory[sample_index, :]b_s = torch.FloatTensor(b_memory[:, :N_STATES])b_a = torch.LongTensor(b_memory[:, N_STATES:N_STATES+1].astype(int))b_r = torch.FloatTensor(b_memory[:, N_STATES+1:N_STATES+2])b_s_ = torch.FloatTensor(b_memory[:, -N_STATES:])# q_eval w.r.t the action in experienceq_eval = self.eval_net(b_s).gather(1, b_a) # shape (batch, 1)q_next = self.target_net(b_s_).detach() # detach from graph, don't backpropagateq_target = b_r + GAMMA * q_next.max(1)[0].view(BATCH_SIZE, 1) # shape (batch, 1)loss = self.loss_func(q_eval, q_target)self.optimizer.zero_grad()loss.backward()self.optimizer.step()dqn = DQN() # 創建 DQN 對象print('\nCollecting experience...')
for i_episode in range(400): # 進行 400 個回合的訓練s, info = env.reset() # 環境重置,獲取初始狀態 s 和其他信息ep_r = 0 # 初始化本回合的總獎勵 ep_r 為 0while True:env.render() # 顯示環境,通過調用 render() 方法,可以將當前環境的狀態以圖形化的方式呈現出來.a = dqn.choose_action(s) # 根據當前狀態選擇動作 a# 下一個狀態(nextstate):返回智能體執行動作a后環境的下一個狀態。在示例中,它存儲在變量s_中。獎勵(reward):返回智能體執行動作a后在環境中獲得的獎勵。在示例中,它存儲在變中。# 完成標志(doneflag):返回一個布爾值,指示智能體是否已經完成了當前環境。在示例中,它存儲在變量done中。# 截斷標志(truncatedflag):返回一個布爾值,表示當前狀態是否是由于達到了最大時間步驟或其他特定條件而被截斷。在示例中,它存儲在變量truncated中。# 其他信息(info):返回一個包含其他輔助信息的字典或對象。在示例中,它存儲在變量info中。# 執行動作,獲取下一個狀態 s_,獎勵 r,done 標志位,以及其他信息s_, r, done, truncated, info = env.step(a)# 修改獎勵值#根據智能體在x方向和theta方向上與目標位置的偏離程度,計算兩個獎勵值r1和r2。具體計算方法是將每個偏離程度除以相應的閾值,然后減去一個常數(0.8和0.5)得到獎勵值。這樣,如果智能體在這兩個方向上的偏離程度越小,獎勵值越高。x, x_dot, theta, theta_dot = s_ # 從 s_ 中提取參數r1 = (env.x_threshold - abs(x)) / env.x_threshold - 0.8 # 根據 x 的偏離程度計算獎勵 r1r2 = (env.theta_threshold_radians - abs(theta)) / env.theta_threshold_radians - 0.5 # 根據 theta 的偏離程度計算獎勵 r2r = r1 + r2 # 組合兩個獎勵成為最終的獎勵 rdqn.store_transition(s, a, r, s_) # 存儲狀態轉換信息到經驗池ep_r += r # 更新本回合的總獎勵if dqn.memory_counter > MEMORY_CAPACITY: # 當經驗池中的樣本數量超過閾值 MEMORY_CAPACITY 時進行學習dqn.learn()if done: # 如果本回合結束print('Ep: ', i_episode,'| Ep_r: ', round(ep_r, 2)) # 打印本回合的回合數和總獎勵if done: # 如果任務結束break # 跳出當前回合的循環s = s_ # 更新狀態,準備進行下一步動作選擇