Dueling-DQN

deep learning reinforcement learning

ผู้อ่านบทความนี้ควรอ่านบทความ Q learning และ Deep Q Learning มาก่อน

บทความนี้อธิบายวิธีจากเปเปอร์ Dueling Network Architectures for Deep Reinforcement Learning

State Value Function และ Advantage Function

ก่อนจะเริ่มเรื่อง dueling-DQN อยากให้ทุกคนได้รู้จักสองค่านี้ก่อน

Dueling DQN

Motivation

ในหลาย ๆ ปัญหา มันมักจะมี state ที่ไม่ว่าจะทำ action ใด ก็ได้ผลเหมือน ๆ กัน หรือมี action ที่ซ้ำ ๆ กันอยู่ เช่น เราเล่นเกมขับรถหลบรถคันอื่นบนถนน ใน state ที่บนถนนไม่มีรถเลย ทุก action ก็มีค่าเท่ากัน เพราะบังคับไปทางไหนก็ได้ ไม่ชนรถคันอื่นอยู่ดี

ในกรณีเหล่านี้ advantage function ของทุก action ก็จะเป็น 0 หมดเลย และถ้าย้อนไปดูในสมการที่ \eqref{eq:Q} นั้นเราก็จะได้ว่าค่า $Q(s,a)$ นั้นจะเท่ากับ $V(s,a)$ ไปโดยปริยาย

ซึ่งถ้าเราใช้วิธี DQN เหมือนที่เคยทำกันมา ในการอัพเดท neural network ด้วยแต่ละ experience เราจะอัพเดทแค่ค่า $Q(s,a)$ ของ action ใน experience นั้น ๆ ซึ่งถ้าเกิดกรณีอย่างที่กล่าวไว้ขึ้นมา เราก็ต้องค่อย ๆ ปรับค่า $Q(s,a)$ ของทุก action เท่ากันหมด ซึ่งก็อาจจะใช้เวลานานหน่อย

แต่ถ้าเรามีวิธีที่เราสามารถแยกการอัพเดทค่า $V(s)$ และ $A(s,a)$ ออกมาได้ แล้วเราสามารถอัพเดทค่า $V(s)$ ทุกครั้ง (ไม่ว่า experience นั้นจะทำ action ไหนก็ตาม) อย่างน้อย ๆ มันก็จะทำให้เราได้ค่าโดยรวมว่า state นั้นเป็นอย่างไรได้เร็วขึ้น

Proposed Method

ซึ่งวิธีที่เค้าใช้ในการแชร์ค่า $V(s)$ นั้นก็คือ เค้าบอกให้เปลี่ยนตัว network architecture ของ DQN เล็กน้อยดังรูปด้านล่าง

alt text

จากภาพด้านบนจะเห็นได้ว่า ใน Dueling-DQN นั้น

วิธีคำนวณค่า $Q(s,a)$ ใน aggregate layer

ถ้าเราดูจากสมการที่ \eqref{eq:Q} เราก็แค่ย้ายข้างสมการซะ จะได้ว่า $Q(s,a)$ เกิดจากการนำค่า $V(s)$ มาบวกกับ $A(s,a)$ แค่นั้นเอง

\begin{equation} \label{eq:Q-2} \color{red} Q(s,a) = V(s) + A(s,a) \end{equation}

งั้นเราเอาสมการนี้ไปใช้เป็น aggregate layer เลยได้บ่ ? –> คำตอบก็คือไม่ได้นะะะะะะ

เพราะสมการ \eqref{eq:Q-2} มันมีปัญหาที่ชื่อว่า unindentifiable ชื่อดูน่ากลัว แต่จริง ๆ แล้วมันเป็นปัญหาแบบบ้าน ๆ เลย ก็คือถ้าเรารู้ค่า $Q(s,a)$ เนี่ย เราก็ไม่รู้ค่า $V(s)$ และ $A(s,a)$ อยู่ดี แล้วเราจะ back propagate แยกสองทางกลับไปอัพเดทค่า $V(s)$ กับ $A(s,a)$ ได้ยังไง

ยกตัวอย่างเช่น ถ้าเราต้องการจะอัพเดท dueling network ให้ทำนายค่า $Q(s,a)$ ของ state หนึ่งออกมาเป็น $[15,20,25]$ เราก็ต้อง back propagate กลับไปอัพเดทค่า $V$ และ $A$ แต่ละตัว แต่ว่าถ้าสมการที่ \eqref{eq:Q-2} เป็น aggregate layer นั้น เราอาจจะได้ค่า target ของ $V(s)$ และ $A(s,a)$ ได้หลาย ๆ แบบ เช่น

วิธีการแก้ปัญหา unidentifiability

เริ่มจากย้อนไปมองว่าตอนเราทำ DQN หรือ Dueling-DQN เนี่ย policy ที่เราใช้คือเราจะเลือก action ที่ให้ค่า $Q(s,a)$ มากที่สุด หรือก็คือ

\begin{equation} \label{eq:policy} a^\ast = \pi(s) = argmax_a Q(s,a) \end{equation}

ถ้าเป็นแบบนั้นก็แสดงว่าใน state ใด ๆ เราก็เลือกแต่ action ที่เป็น optimal action แค่อันเดียวอยู่แล้ว เพราะฉะนั้น $V(s)$ ของ state ใด ๆ จะมีค่าเท่ากับ $Q(s,a)$ ของ optimal action ใน state นั้น ๆ

\begin{equation} \label{eq:V-t} V_\pi (s) = V_{argmax_a Q(s,a)} (s) = Q(s,a^\ast) \end{equation}

ถ้าเรานำสมการที่ \eqref{eq:V-t} ไปใช้ร่วมกับสมการที่ \eqref{eq:Q-2} จะทำให้เราสามารถหาค่า $A(s,a)$ และ $V(s)$ ที่ unique จากค่า $Q(s,a)$

ยกตัวอย่างเช่น ถ้าเรารู้ว่าค่า $Q(s,a)$ ของทั้ง 3 actions เป็นดังนี้ $[15,20,25]$ ก็แปลว่า

โดยสรุปก็คือ ถ้าเราสามารถล็อคค่า $V(s)$ ให้เท่ากับ $Q(s,a^\ast)$ ได้นั้นเราก็จะสามารถแก้ปัญหานี้ได้

เพราะฉะนั้น ในเปเปอร์เค้าก็เลยเปลี่ยนสมการที่ \eqref{eq:Q-2} เป็นสมการด้านล่าง

\begin{equation} \label{eq:aggregate_layer_max} Q(s,a) = V(s) + (A(s,a) - max_{a’} A(s,{a’})) \end{equation}

จะเห็นได้ว่าสมการที่ $\eqref{eq:aggregate_layer_max}$ จะกลายเป็น

ซึ่งเราจะใช้สมการที่ \eqref{eq:aggregate_layer_max} มาเป็น aggregate layer เลยก็ได้ แต่ว่าคนเขียนเปเปอร์เค้าก็ได้นำเสนออีกสมการนึงขึ้นมาใช้แทน เพื่อเพิ่ม stability ระหว่างการเทรนด้วยการเปลี่ยนจากการหาค่า max มาเป็นการคำนวณค่า mean แทน ดังแสดงในสมการที่ \eqref{eq:aggregate_layer} ซึ่งจะทำให้ค่า $Q(s,a)$ ที่ทำนายออกมานั้นค่อย ๆ เปลี่ยนไปตามค่า mean ที่เปลี่ยนไป

\begin{equation} \label{eq:aggregate_layer} Q(s,a) = V(s) + (A(s,a) - \frac{1}{|A|} \sum_{a’} A(s,a’)) \end{equation}

อันที่จริงแล้วมันจะทำให้ความสัมพันธ์ระหว่างค่า $Q(s,a)$, $V(s)$ และ $A(s,a)$ มันผิดเพี้ยนไปจากเดิมหน่อย ๆ อย่างไรก็ตามอันดับของค่า $A(s,a)$ ของแต่ละ action มันยังเหมือนเดิมอยู่ มันก็ทำให้ค่า $Q(s,a)$ มี rank ที่เหมือนเดิม เราก็เลยใช้ได้

เช่น สมมติว่าถ้าคำนวณค่า $Q(s,a)$ ตามสมการที่ \eqref{eq:aggregate_layer_max} แล้วเราเอาค่า $Q(s,a)$ มาเรียงได้ดังนี้ $Q(s,1) > Q(s,2) > Q(s,3)$ ต่อมาเรามาคำนวณด้วยสมการที่ \eqref{eq:aggregate_layer} ก็ขอแค่ให้ได้ลำดับ $Q(s,1) > Q(s,2) > Q(s,3)$ เหมือนเดิมก็พอ

เพราะที่จริงแล้วเราก็ไม่ได้แคร์ว่าค่า $Q(s,a)$ มันจะเป็นเท่าไหร่ เราแค่เลือกอันที่ดีที่สุดมาทำ ขอแค่อันที่ดีที่สุดที่คำนวณตามสมการ \eqref{eq:aggregate_layer_max} ยังเป็นอันที่ดีที่สุดถ้าคำนวณจากสมการ \eqref{eq:aggregate_layer} ก็เป็นอันใช้ได้

กล่าวโดยสรุปก็คือ ตัว Dueling-DQN นั้น เค้าก็ได้นำเสนอ network architecture ใหม่เท่านั้น ซึ่งจะเห็นได้ว่าตัว input และ output สุดท้ายของ neural network นั้นเหมือนกันกับ DQN หมดทุกอย่าง เพราะฉะนั้นแล้ว การเปลี่ยนจาก DQN มาเป็น Dueling-DQN นั้น เราแค่เปลี่ยนการต่อ neural network เท่านั้นเอง ซึ่งตัวอย่างการการเปลี่ยนโค้ดของการสร้าง neural network จาก DQN มาเป็น Dueling-DQN ก็ง่าย ๆ แค่นี้เอง

### Simple DQN
def build_neural_network_dqn(n_action):
  input_layer = Input(shape=(8,)) #input shape เอามาจาก env.observation_space 
  dense = Dense(100, activation='relu', kernel_initializer='random_normal')(input_layer)
  dense = Dense(100, activation='relu', kernel_initializer='random_normal')(dense)
  dense = Dense(50, activation='relu', kernel_initializer='random_normal')(dense)
  Q = Dense(n_action, activation='linear', kernel_initializer='random_normal')(dense)
  model = Model(input_layer,Q) 
  adam = Adam(learning_rate = 0.001)
  model.compile(loss='mean_squared_error', optimizer=adam)
  return model

### Simple Dueling DQN
def build_neural_network_dueling_dqn(n_action):
  input_layer = Input(shape=(8,)) #input shape เอามาจาก env.observation_space 
  dense = Dense(100, activation='relu', kernel_initializer='random_normal')(input_layer)
  V = Dense(50, activation='relu', kernel_initializer='random_normal')(dense)
  V = Dense(1, activation='linear', kernel_initializer='random_normal')(V) # มี 1 node ใช้สำหรับประมาณค่า V(s)
  A = Dense(100, activation='relu', kernel_initializer='random_normal')(dense) 
  A = Dense(n_action, activation='linear', kernel_initializer='random_normal')(A) # มีจำนวน nodes เท่ากับจำนวน actions สำหรับประมาณค่า A(s,a) ของแต่ละ action
  Q = V + (A - tf.math.reduce_mean(A, axis=1, keepdims=True)) # สร้าง aggregate layer ด้วยสมการที่ 7
  model = Model(input_layer,Q) 
  adam = Adam(learning_rate = 0.001)
  model.compile(loss='mean_squared_error', optimizer=adam)
  return model

นอกนั้นในกระบวนการเทรนโมเดลก็ทำเหมือนเดิมหมดทุกอย่าง เช่น การใช้ experience replay, การใช้ target network, ฯลฯ

Experiment

ในเปเปอร์เค้าก็ได้ทดลองเปรียบเทียบ DQN และ Dueling-DQN ใน environment เบสิค ๆ แบบรูปด้านล่าง

alt text

โดยที่ agent นั้นจะเริ่มที่จุด $\star$ และมี action ให้เลือกทำได้แก่ ไปทางซ้าย,ทางขวา,ข้างบน,ข้างล่าง, และอยู่เฉย ๆ โดยที่ episode จะสิ้นสุดต่อเมื่อ agent สามารถไปถึงปลายทางที่มีสีแดงอยู่ได้ โดยที่ความเข้มของสีแดงนั้นแสดงถึงจำนวน reward ที่จะได้รับเมื่อจบ episode

ซึ่งด้วยความที่ environment มันค่อนข้างง่าย เค้าก็เลยสามารถคำนวณค่า $Q^\ast(s,a)$ ที่ถูกต้องไว้ได้ก่อนหน้า (ถ้าใคร งงๆ ว่าเค้าหาค่า $Q^\ast(s,a)$ ยังไงก็กลับไปทวนเรื่อง Q learning ได้นะ)

จากนั้นเค้าลองเทรนทั้ง DQN และ Dueling DQN กับ environment นี้ และในระหว่างที่เทรน เค้าก็หา squared error ระหว่าง $\hat{Q}(s,a)$ กับ $Q^\ast(s,a)$ ไปด้วยระหว่างเทรนเพื่อดูว่าตัว algorithm นั้นสามารถทำนายค่า Q ได้แม่นยำเท่าไหร่

การทดลองแรก

การทดลองที่ 2

alt text

จะเห็นได้ว่า dueling dqn นั้นจะมีประโยชน์มาก ๆ เมื่อเราอยู่ใน environment ที่มี action ที่คล้าย ๆ กัน ในบาง state (แต่ที่จริงแล้วในกรณีนี้คือทุก state เลย)

Disclaimer

รายละเอียดในบทความนี้มาจากความเข้าใจส่วนตัว อาจมีข้อผิดพลาด หากพบจุดผิดพลาด ขอความกรุณาแจ้งทาง facebook หรือ email: thammasorn.han@hotmail.com

Reference: