انتقل إلى المحتوى الرئيسي

الوحدة 8 — طرائق تدرّج السياسة

طرح مختلف جذريًّا: بدل تعلّم قيمة ثمّ استخلاص السياسة، لماذا لا نتعلّم السياسة مباشرة؟ لدينا شبكة πθ(as)\pi_\theta(a \mid s) تُخرج توزيعًا احتماليًّا على الأفعال، ونضبط θ\theta بتدرّج على الأداء المُتوقّع. تفتح هذه العائلة الباب لثلاث ميزات لا يوفّرها DQN: سياسات احتماليّة، أفعال مستمرّة، وضمانات تحسين متجانسة.

دالّة الأداء وتدرّجها

نُعرّف أداء السياسة بأنّها العائد المتوقّع الأصلي:

J(θ)=Eπθ[G0]=Eπθ[t=0T1γtrt+1]J(\theta) = \mathbb{E}_{\pi_\theta}\left[ G_0 \right] = \mathbb{E}_{\pi_\theta}\left[ \sum_{t=0}^{T-1} \gamma^t r_{t+1} \right]

نُريد θJ\nabla_\theta J لتطبيق صعود تدرّج. المشكلة: JJ يُحسب على حلقات تولّدها السياسة نفسها، فتدرّجها ليس بديهيًّا. مبرهنة تدرّج السياسة تقدّم لنا صيغة قابلة للحساب:

θJ(θ)=Eπθ[t=0T1Gtθlogπθ(atst)]\nabla_\theta J(\theta) = \mathbb{E}_{\pi_\theta}\left[ \sum_{t=0}^{T-1} G_t \nabla_\theta \log \pi_\theta(a_t \mid s_t) \right]

القراءة الحدسيّة: يحرّك التدرّج θ\theta في اتّجاه يزيد احتمال الأفعال التي أعطت عائدات كبيرة، ويقلّل احتمال الأفعال التي أعطت عائدات صغيرة. حرفيًّا: هذا هو ما نريده.

خوارزميّة REINFORCE

أبسط استعمال للصيغة أعلاه، من ورقة ويليامز 1992:

  1. شغّل حلقة كاملة تحت πθ\pi_\theta، وسجّل (st,at,rt+1)(s_t, a_t, r_{t+1}).
  2. احسب GtG_t لكلّ خطوة (المجموع المخفوض للمكافآت القادمة).
  3. حدّث: θθ+αtGtθlogπθ(atst)\theta \leftarrow \theta + \alpha \sum_t G_t \nabla_\theta \log \pi_\theta(a_t \mid s_t).
import torch
import torch.nn as nn
import torch.optim as optim
import gymnasium as gym

class Politique(nn.Module):
def __init__(self, dim_etat, dim_action):
super().__init__()
self.reseau = nn.Sequential(
nn.Linear(dim_etat, 128),
nn.ReLU(),
nn.Linear(128, dim_action),
)
def forward(self, s):
logits = self.reseau(s)
return torch.distributions.Categorical(logits=logits)

env = gym.make("CartPole-v1")
politique = Politique(4, 2)
optimiseur = optim.Adam(politique.parameters(), lr=1e-3)
gamma = 0.99

for episode in range(1000):
etat, _ = env.reset()
log_probs, recompenses = [], []
done = False
while not done:
dist = politique(torch.tensor(etat, dtype=torch.float32))
action = dist.sample()
log_probs.append(dist.log_prob(action))
etat, r, term, trunc, _ = env.step(int(action))
recompenses.append(r)
done = term or trunc

# حساب G_t لكلّ خطوة
G, retours = 0, []
for r in reversed(recompenses):
G = r + gamma * G
retours.insert(0, G)
retours = torch.tensor(retours)

perte = -torch.stack([lp * G for lp, G in zip(log_probs, retours)]).sum()
optimiseur.zero_grad()
perte.backward()
optimiseur.step()

نقطة تنفيذ حاسمة: نستعمل .sample() لا .argmax(). السياسة احتماليّة، والاستكشاف يأتي مجّانًا من عيّنة التوزيع. لا حاجة إلى إبسيلون-جشعة صريحة كما في DQN.

المشكلة الكبرى: التباين

تعمل REINFORCE، لكنّها تعمل ببطء وبتذبذب واسع. السبب: GtG_t تعتمد على كامل الحلقة، وتباينها ضخم، فيدخل هذا التباين مباشرة في التدرّج.

مثال ملموس: على CartPole، حلقة قد تدوم 10 خطوات (سقوط سريع) أو 500 خطوة (نجاح). العائد يتغيّر من 1010 إلى 500500. التدرّج المستند إلى قيمة G=500G = 500 يهيمن على تدرّج قيمته G=10G = 10، حتّى إن كانت الأفعال في الحلقة الثانية مشابهة تمامًا لأفعال في الأولى.

الخطّ الأساسي: الحلّ الجميل

الحيلة: طرح خطّ أساسي b(s)b(s) لا يعتمد على الفعل من GtG_t:

θJ=E[t(Gtb(st))θlogπθ(atst)]\nabla_\theta J = \mathbb{E}\left[ \sum_t (G_t - b(s_t)) \nabla_\theta \log \pi_\theta(a_t \mid s_t) \right]

يمكن إثبات أنّ التدرّج لا يتغيّر في المتوسّط ما دام b(s)b(s) لا يعتمد على aa. لكنّه يمكن أن يُقلّص التباين تقليصًا هائلًا إذا اخترناه بحكمة.

خيار طبيعي: b(s)=Vπ(s)b(s) = V^{\pi}(s). عندها GtV(st)G_t - V(s_t) يمثّل الميزة A(st,at)A(s_t, a_t): كم كان الفعل أفضل من المتوسّط في تلك الحالة. لا نتوقّف إلّا عند الحاجة إلى تحديث θ\theta إذا كان الفعل أفضل من المتوسّط، والقيمة أعلاه أفضل نسبيًّا لا مطلقًا.

هذه الفكرة — تعلّم VV إلى جانب π\pi، واستخدام VV خطًّا أساسيًّا — تُنتج الممثّل-الناقد، وستكون موضوع الوحدة التالية.

الأفعال المستمرّة

لا يستطيع DQN التعامل مع فضاءات أفعال مستمرّة (maxa\max_a على مجموعة مستمرّة ثقيل). تدرّج السياسة يتعامل معها بشكل طبيعي: نستعمل توزيعًا مستمرًّا (غاوسي عادةً) بمعاملات تعطيها الشبكة.

class PolitiqueGaussienne(nn.Module):
def __init__(self, dim_etat, dim_action):
super().__init__()
self.moyenne = nn.Sequential(
nn.Linear(dim_etat, 128), nn.ReLU(),
nn.Linear(128, dim_action),
)
self.log_ecart = nn.Parameter(torch.zeros(dim_action))
def forward(self, s):
mu = self.moyenne(s)
sigma = torch.exp(self.log_ecart)
return torch.distributions.Normal(mu, sigma)

مفيد على القيادة الذاتيّة، الروبوتيّات، وضبط عزم دوران محرّك — كلّ ما يتطلّب فعلًا يأخذ قيمة حقيقيّة لا فئويّة.

متى تدرّج السياسة، ومتى DQN

  • الأفعال المستمرّة: تدرّج السياسة إجباري.
  • سياسة احتماليّة مطلوبة: تدرّج السياسة (في الشطرنج والبوكر مثلًا).
  • كفاءة العيّنة حرجة: DQN غالبًا (يستخدم إعادة الرجعة، عيّنات كلّ خطوة، بينما REINFORCE يستخدم كلّ حلقة مرّة واحدة ثمّ ينساها).
  • بيئة بسيطة، فضاء أفعال فئوي صغير: كلاهما يعمل؛ DQN غالبًا أسرع.
قاعدة التطبيع

قبل تطبيق تدرّج REINFORCE، طبّعوا GtG_t: retours = (retours - retours.mean()) / (retours.std() + 1e-8). تعديل بسيط يُقلّص التباين تقليصًا كبيرًا، ويجعل معدّل التعلّم أقلّ حسّاسيّة للمهمّة. إسقاطه سبب شائع لتدريب لا يتعلّم.

الخلاصة

  • تدرّج السياسة يُعدّل θ\theta في اتّجاه يرفع احتمال الأفعال ذات العائد المرتفع؛ الاستكشاف يأتي من سياسة احتماليّة.
  • REINFORCE هو النسخة الأبسط، لكن تباينه ضخم لأنّه يستعمل GtG_t الكامل من الحلقة.
  • الخطّ الأساسي يُقلّص التباين دون تحيّز؛ استعمال V(s)V(s) خطًّا أساسيًّا يُعطي الميزة A(s,a)A(s, a).
  • الأفعال المستمرّة حصريّة تقريبًا لعائلة تدرّج السياسة؛ وسياسة احتماليّة مفيدة في مسائل الخصم.

الوحدة التالية: الممثّل والناقد، حيث نمزج قوّة VV وسياسة احتماليّة في شبكتين تعمل معًا، ثمّ نبني على ذلك A2C وPPO — نقطة بداية العمل الصناعي الحديث في التعلّم المعزّز.