الوحدة 10 — مشروع: وكيل مدرَّب على بيئة Gymnasium
آن أوان ربط كلّ الخيوط. سنُدرّب وكيل PPO على LunarLander-v2 من الصفر — من إعداد البيئة إلى تسجيل فيديو نهائي — ونناقش في الطريق ما لا تُخبركم به الأوراق: تباين البذور، وتقييم لا يخدع نفسه، وحدود النقل إلى العالم الحقيقي.
البيئة LunarLander
تحاكي LunarLander-v2 هبوط مركبة على القمر بمحرّكين جانبيّين ومحرّك رئيسي. الحالة متّجه بـ8 مركّبات (الموقع، السرعة، الزاوية، سرعة الزاوية، تلامس الأرجل). الأفعال أربعة فئويّة (لا فعل، محرّك أيسر، محرّك رئيسي، محرّك أيمن). المكافأة مركّبة: بون ص للهبوط الحسن، وعقوبة للاصطدام، وعقوبة صغيرة على استعمال الوقود. الحلّ المُعلن: متوسّط على 100 حلقة.
البنية الكاملة
import numpy as np
import torch
import torch.nn as nn
import gymnasium as gym
class ActeurCritique(nn.Module):
def __init__(self, dim_etat=8, dim_action=4):
super().__init__()
self.tronc = nn.Sequential(
nn.Linear(dim_etat, 128), nn.Tanh(),
nn.Linear(128, 128), nn.Tanh(),
)
self.tete_politique = nn.Linear(128, dim_action)
self.tete_valeur = nn.Linear(128, 1)
def forward(self, s):
h = self.tronc(s)
return self.tete_politique(h), self.tete_valeur(h).squeeze(-1)
def collecter(env, modele, num_steps=1024):
obs, actions, log_probs, valeurs, recompenses, dones = [], [], [], [], [], []
s, _ = env.reset()
for _ in range(num_steps):
s_t = torch.tensor(s, dtype=torch.float32)
with torch.no_grad():
logits, v = modele(s_t)
dist = torch.distributions.Categorical(logits=logits)
a = dist.sample()
obs.append(s); actions.append(int(a))
log_probs.append(float(dist.log_prob(a))); valeurs.append(float(v))
s, r, term, trunc, _ = env.step(int(a))
recompenses.append(r); dones.append(term or trunc)
if term or trunc:
s, _ = env.reset()
return dict(obs=np.array(obs), actions=np.array(actions),
log_probs=np.array(log_probs), valeurs=np.array(valeurs),
recompenses=np.array(recompenses), dones=np.array(dones))
نستعمل Tanh لا ReLU عمدًا: PPO على مسائل تحكّم مستمرّ يعمل عادةً أفضل مع Tanh (تدرّجات مقصورة، تعميم أنعم).
حساب الميزات بـGAE
الجزء الذي أهملناه في الوحدة السابقة: Generalized Advantage Estimation. بدلًا من خطأ TD خامٍ، تدمج GAE أخطاء TD متعدّدة الخطوات مع تخفيض :
مع يعطي تقديرًا مفاضلًا للتحيّز/التباين أفضل من TD() الخام. يجب تطبيع الميزات قبل استعمالها في الخسارة.
الحلقة الرئيسة
def entrainer(seed, total_steps=1_000_000):
torch.manual_seed(seed); np.random.seed(seed)
env = gym.make("LunarLander-v2")
env.reset(seed=seed)
modele = ActeurCritique()
optim = torch.optim.Adam(modele.parameters(), lr=3e-4)
historique = []
steps = 0
while steps < total_steps:
lot = collecter(env, modele, num_steps=1024)
# حساب الميزات والقيم المستهدفة (GAE)
...
# 4 مرورات تحسين بقصّ PPO
for _ in range(4):
... # خسارة PPO، خسارة القيمة، إنتروبيا
steps += 1024
historique.append(evaluer(modele, env))
return historique
نُقيّم كلّ 10 دفعات بتشغيل 20 حلقة تقييم بلا استكشاف (سياسة جشعة بعد ). نحفظ متوسّطها في historique.
البذور والتباين
هنا نصل إلى الدرس الأصعب في التعلّم المعزّز: تباين البذور هائل. تشغيل الخوارزميّة نفسها بخمس بذور مختلفة قد ينتج:
- بذرة 42: متوسّط 260 (نجاح)
- بذرة 0: متوسّط 180 (فشل)
- بذرة 7: متوسّط 240 (نجاح)
- بذرة 123: متوسّط 90 (فشل كامل)
- بذرة 99: متوسّط 220 (نجاح)
النتيجة: تقرير بذرة واحدة كذب علمي. الأوراق قبل 2018 كانت تُنشر بذرة نجاحها؛ ولذلك عجزت أعمال إعادة الإنتاج بشكل ممنهج. القاعدة الحديثة: متوسّط 5 بذور على الأقلّ، مع نطاق منسي على المنحنى.
resultats = [entrainer(seed) for seed in [0, 7, 42, 99, 123]]
moyenne = np.mean(resultats, axis=0)
ecart = np.std(resultats, axis=0)
# رسم moyenne مع ملء بين moyenne-ecart و moyenne+ecart
تسجيل فيديو
Gymnasium يُقدّم مغلّفًا يُسجّل حلقات:
from gymnasium.wrappers import RecordVideo
env_video = gym.make("LunarLander-v2", render_mode="rgb_array")
env_video = RecordVideo(env_video, video_folder="videos/",
episode_trigger=lambda e: e % 100 == 0)
الفائدة ليست جماليّة فحسب: مشاهدة الوكيل يعمل تكشف سلوكيّات لا يكشفها الرقم. وكيل بمتوسّط 200 قد يهبط بأمان أحيانًا ويحطّم المركبة في أخرى؛ منحنى المتوسّط وحده يخفي هذا. راقبوا فيديو النجاحات والفشل قبل الادّعاء بأنّ النموذج جاهز.
ما لا يُنقل إلى العالم الحقيقي
يعمل عملنا على LunarLander. ماذا لو أراد فريق روبوتيّات نقله لطائرة مسيّرة حقيقيّة؟ ثلاث فجوات كبرى:
- فجوة المحاكاة والواقع (Sim-to-real): الفيزياء المُبسّطة في المحاكي تختلف عن ديناميكيّات الطائرة الفعليّة. سلوك يعمل هنا قد يُفشِل بسرعة هناك. الحلول (نطاق العشوائيّة، ضبط دقيق، تقنيات النقل) مواضيع بحث نشطة.
- كفاءة العيّنة: عملنا استهلك مليون خطوة محاكاة. على طائرة حقيقيّة كلّ خطوة يقابلها سقوط محتمل، ومليون تفاعل مستحيل ماليًّا. لذلك يبقى التدريب من الصفر بالتعلّم المعزّز نادرًا في الروبوتيّات، ويستعمل عادةً بعد بذرة تدريب بالإشراف.
- الأمان: خلال التدريب، الوكيل يستكشف بأفعال عشوائيّة. على مركبة حقيقيّة الاستكشاف = ضرر. الأمان أثناء التعلّم (safe RL) اختصاص كامل، ولا يُختصر بأدواتنا في هذه الدورة.
هذه القيود لا تُقلّل من قيمة التعلّم المعزّز، بل تُحدّد إطار استعماله الرشيد: ألعاب فيديو، محاكيات دقيقة، تحسين محفظة، جدولة موارد رقميّة. الروبوتيّات الحقيقيّة تُضاف إلى القائمة تدريجيًّا، لكن دائمًا مع إشراف بشري ومحاكاة ممتازة.
- متوسّط 5 بذور، بفترات .
- مشاهدة 3 حلقات نجاح و3 حلقات فشل بالفيديو.
- تقييم على 100 حلقة قصيرة قبل التقرير النهائي.
- إعادة تشغيل الوكيل من حالات بداية غير مرئيّة (اختبار توزيع خارج).
الخلاصة
- LunarLander-v2 يقدّم مسألة تحكّم مركّبة كافية لتجميع مفاهيم الدورة كلّها.
- GAE لتقدير الميزة وقصّ PPO لاستقرار التحديث هما الحيلتان اللتان تجعلان التنفيذ يعمل.
- تباين البذور ضخم في التعلّم المعزّز، ولذلك تقرير أرقام موثوقة يتطلّب 5 بذور على الأقلّ ومنحنيات ذات نطاق ثقة.
- الفيديو تشخيص لا غنى عنه؛ منحنى المتوسّط وحده يخفي الفشل النادر.
- ما ينجح في المحاكاة لا يُنقل ميكانيكيًّا إلى الواقع؛ فجوة المحاكاة، وكفاءة العيّنة، والأمان أثناء التعلّم قيود بنيوية.
الوحدة التالية: المراجعة والاختبار الشامل الذي يقيس كلّ الوحدات العشر.