معاينة مختبر آمنة
Cart Pole-RL-Py Torch
هذي معاينة منقّحة للقراءة فقط؛ ما فيه أي شيء يشتغل داخل الصفحة.
قراءة فقط
معاينة الدفتر
Cart Pole-RL-Py Torch
> **ملاحظة بيئة التشغيل المدمجة:** هالمعاينة تستخدم عيّنة صغيرة وثابتة وآمنة من ناحية الحقوق عشان تكون النتايج قابلة للتكرار. النتايج بالحجم الكامل تحتاج مجموعة البيانات أو النموذج الموثّق بالدرس داخل بيئة خارجية معتمدة.
# تدريب التعلم المعزز على موازنة CartPole
هالدفتر جزء من AI for Beginners، واستُلهم من [الشرح الرسمي في PyTorch](https://pytorch.org/tutorials/intermediate/reinforcement_q_learning.html) ومن [هالتطبيق لـ CartPole باستخدام PyTorch](https://github.com/yc930401/Actor-Critic-pytorch).
في هالمثال بنستخدم التعلم المعزز (Reinforcement Learning, RL) عشان ندرّب نموذجًا يوازن عمودًا فوق عربة تتحرك أفقيًا يمين ويسار. وبنستخدم بيئة [Gymnasium](https://gymnasium.farama.org/) لمحاكاة العمود.
> **ملاحظة**: تستخدم هالنسخة واجهة Gymnasium الحديثة. في البيئات السحابية اختاروا إخراج `rgb_array` بدل الاعتماد على نافذة رسومية محلية.
بنبدأ ونتأكد إن Gym مثبّت:
import sys
# Dependencies are provisioned by this course edition's pinned runtime profile.الحين خلونا ننشئ بيئة CartPole ونشوف كيف نتعامل معها. لكل بيئة هالخاصيتين:
* **مساحة الإجراءات**: مجموعة الإجراءات اللي نقدر ننفذها في كل خطوة من المحاكاة.
* **مساحة الملاحظات**: نطاق الملاحظات اللي نقدر نستقبلها.
import gymnasium as gym
env = gym.make("CartPole-v1", render_mode="rgb_array")
print(f"Action space: {env.action_space}")
print(f"Observation space: {env.observation_space}")خلونا نشوف كيف تشتغل المحاكاة. الحلقة الجاية تشغّلها لين ترجع `env.step` علامة الانتهاء `done`. بنختار الإجراءات عشوائيًا باستخدام `env.action_space.sample`؛ وعشان كذا غالبًا بتفشل التجربة بسرعة، لأن بيئة CartPole تنهي التجربة إذا طلعت سرعة العربة أو موقعها أو زاوية العمود عن حدود معيّنة.
> بتنفتح المحاكاة في نافذة جديدة. تقدرون تشغّلون الكود أكثر من مرة وتلاحظون كيف يتغيّر سلوكها.
env.reset()
done = False
total_reward = 0
while not done:
env.render()
obs, rew, terminated, truncated, info = env.step(env.action_space.sample())
done = terminated or truncated
total_reward += rew
print(f"{obs} -> {rew}")
print(f"Total reward: {total_reward}")بتلاحظون إن الملاحظة تحتوي على 4 أرقام، وهي:
- موقع العربة
- سرعة العربة
- زاوية العمود
- معدل دوران العمود
`rew` هي المكافأة اللي نستقبلها في كل خطوة. في بيئة CartPole نحصل على 1 نقطة مقابل كل خطوة محاكاة، وهدفنا نرفع مجموع المكافآت لأكبر قيمة؛ يعني نخلي CartPole يحافظ على توازنه أطول مدة من دون ما يطيح.
في التعلم المعزز، هدفنا ندرّب **سياسة** $\pi$ تختار لنا الإجراء $a$ المناسب لكل حالة $s$؛ وباختصار: $a = \pi(s)$.
ولو نبغى حلًا احتماليًا، نقدر نفكر في السياسة على إنها ترجع مجموعة احتمالات، احتمال لكل إجراء. يعني $\pi(a|s)$ تمثّل احتمال إننا نختار الإجراء $a$ وإحنا في الحالة $s$.
## طريقة تدرّج السياسة
في أبسط خوارزمية للتعلم المعزز، واسمها **تدرّج السياسة** (Policy Gradient)، بندرّب الشبكة العصبية تتوقع الإجراء الجاي.
import numpy as np
import matplotlib.pyplot as plt
import torch
num_inputs = 4
num_actions = 2
model = torch.nn.Sequential(
torch.nn.Linear(num_inputs, 128, bias=False, dtype=torch.float32),
torch.nn.ReLU(),
torch.nn.Linear(128, num_actions, bias = False, dtype=torch.float32),
torch.nn.Softmax(dim=1)
)
np.random.seed(2026)بندرّب الشبكة عن طريق تشغيل تجارب كثيرة وتحديثها عقب كل تشغيل. خلونا نعرّف دالة تشغّل التجربة وترجع نتائجها، أو ما يسمّى **الأثر** (trace): كل الحالات والإجراءات واحتمالاتها المقترحة والمكافآت.
def run_episode(max_steps_per_episode = 10000,render=False):
states, actions, probs, rewards = [],[],[],[]
state, _ = env.reset()
for _ in range(max_steps_per_episode):
if render:
env.render()
action_probs = model(torch.from_numpy(np.expand_dims(state,0)))[0]
action = np.random.choice(num_actions, p=np.squeeze(action_probs.detach().numpy()))
nstate, reward, terminated, truncated, info = env.step(action)
done = terminated or truncated
if done:
break
states.append(state)
actions.append(action)
probs.append(action_probs.detach().numpy())
rewards.append(reward)
state = nstate
return np.vstack(states), np.vstack(actions), np.vstack(probs), np.vstack(rewards)تقدرون تشغّلون حلقة واحدة (episode) بشبكة ما تدربت للحين، وبتلاحظون إن مجموع المكافآت، وهو نفسه طول الحلقة، منخفض مرة:
s, a, p, r = run_episode()
print(f"Total reward: {np.sum(r)}")من الجوانب اللي تحتاج انتباه في خوارزمية تدرّج السياسة استخدام **المكافآت المخصومة**. الفكرة إننا نحسب متجه مجموع المكافآت عند كل خطوة من اللعبة، وأثناء الحساب نخصم المكافآت المبكرة باستخدام معامل $gamma$. وبعدها نطبّع المتجه الناتج، لأننا بنستخدمه وزنًا يؤثر في التدريب:
eps = 0.0001
def discounted_rewards(rewards,gamma=0.99,normalize=True):
ret = []
s = 0
for r in rewards[::-1]:
s = r + gamma * s
ret.insert(0, s)
if normalize:
ret = (ret-np.mean(ret))/(np.std(ret)+eps)
return retالحين نبدأ التدريب الفعلي! بنشغّل 300 حلقة، وفي كل حلقة نسوي الخطوات الجاية:
1. نشغّل التجربة ونجمع الأثر.
1. نحسب الفرق (`gradients`) بين الإجراءات اللي اتخذناها والاحتمالات اللي توقعها النموذج. كل ما صغر الفرق، زادت ثقتنا إن الإجراء كان مناسبًا.
1. نحسب المكافآت المخصومة ونضرب التدرجات فيها؛ وبكذا يكون أثر الخطوات ذات المكافآت الأعلى في النتيجة النهائية أكبر من أثر الخطوات ذات المكافآت الأقل.
1. نكوّن الإجراءات المستهدفة اللي تتوقعها الشبكة العصبية من جزأين: الاحتمالات اللي توقعتها أثناء التشغيل، والتدرجات اللي حسبناها. نستخدم المعامل `alpha` عشان نحدد مقدار تأثير التدرجات والمكافآت؛ وهذا يسمّى *معدل التعلم* في خوارزمية التعلم المعزز.
1. وأخيرًا، ندرّب الشبكة على الحالات والإجراءات المتوقعة، ثم نكرر العملية.
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
def train_on_batch(x, y):
x = torch.from_numpy(x)
y = torch.from_numpy(y)
optimizer.zero_grad()
predictions = model(x)
loss = -torch.mean(torch.log(predictions) * y)
loss.backward()
optimizer.step()
return lossalpha = 1e-4
history = []
for epoch in range(300):
states, actions, probs, rewards = run_episode()
one_hot_actions = np.eye(2)[actions.T][0]
gradients = one_hot_actions-probs
dr = discounted_rewards(rewards)
gradients *= dr
target = alpha*np.vstack([gradients])+probs
train_on_batch(states,target)
history.append(np.sum(rewards))
if epoch%100==0:
print(f"{epoch} -> {np.sum(rewards)}")
plt.plot(history)الحين خلونا نشغّل الحلقة مع العرض ونشوف النتيجة:
_ = run_episode(render=True)المفروض تشوفون الحين إن العمود صار يوازن بشكل زين!
## نموذج الممثل والناقد
نموذج الممثل والناقد (Actor-Critic) تطوير إضافي لتدرّج السياسة. فيه نبني الشبكة العصبية وهي تتعلم السياسة والمكافآت المقدّرة مع بعض. للشبكة مخرجان، أو تقدرون تتعاملون معها على إنها شبكتان منفصلتان:
* **الممثل** يقترح الإجراء اللي نتخذه عن طريق إعطائنا توزيع احتمالات الإجراءات في الحالة الحالية، مثل نموذج تدرّج السياسة.
* **الناقد** يقدّر المكافأة اللي بتنتج عن هالإجراءات، ويرجع مجموع المكافآت المستقبلية المقدّرة عند الحالة المعطاة.
خلونا نعرّف هالنموذج:
from itertools import count
import torch.nn.functional as Fdevice = torch.device("cuda" if torch.cuda.is_available() else "cpu")
env.close()
env = gym.make("CartPole-v1", render_mode="rgb_array")
state_size = env.observation_space.shape[0]
action_size = env.action_space.n
lr = 0.0001
class Actor(torch.nn.Module):
def __init__(self, state_size, action_size):
super(Actor, self).__init__()
self.state_size = state_size
self.action_size = action_size
self.linear1 = torch.nn.Linear(self.state_size, 128)
self.linear2 = torch.nn.Linear(128, 256)
self.linear3 = torch.nn.Linear(256, self.action_size)
def forward(self, state):
output = F.relu(self.linear1(state))
output = F.relu(self.linear2(output))
output = self.linear3(output)
distribution = torch.distributions.Categorical(F.softmax(output, dim=-1))
return distribution
class Critic(torch.nn.Module):
def __init__(self, state_size, action_size):
super(Critic, self).__init__()
self.state_size = state_size
self.action_size = action_size
self.linear1 = torch.nn.Linear(self.state_size, 128)
self.linear2 = torch.nn.Linear(128, 256)
self.linear3 = torch.nn.Linear(256, 1)
def forward(self, state):
output = F.relu(self.linear1(state))
output = F.relu(self.linear2(output))
value = self.linear3(output)
return valueنحتاج نعدّل الدالتين `discounted_rewards` و`run_episode` شوي:
def discounted_rewards(next_value, rewards, masks, gamma=0.99):
R = next_value
returns = []
for step in reversed(range(len(rewards))):
R = rewards[step] + gamma * R * masks[step]
returns.insert(0, R)
return returns
def run_episode(actor, critic, n_iters):
optimizerA = torch.optim.Adam(actor.parameters())
optimizerC = torch.optim.Adam(critic.parameters())
for iter in range(n_iters):
state, _ = env.reset()
log_probs = []
values = []
rewards = []
masks = []
entropy = 0
env.reset()
for i in count():
env.render()
state = torch.FloatTensor(state).to(device)
dist, value = actor(state), critic(state)
action = dist.sample()
next_state, reward, terminated, truncated, _ = env.step(action.cpu().numpy())
done = terminated or truncated
log_prob = dist.log_prob(action).unsqueeze(0)
entropy += dist.entropy().mean()
log_probs.append(log_prob)
values.append(value)
rewards.append(torch.tensor([reward], dtype=torch.float, device=device))
masks.append(torch.tensor([1-done], dtype=torch.float, device=device))
state = next_state
if done:
print('Iteration: {}, Score: {}'.format(iter, i))
break
next_state = torch.FloatTensor(next_state).to(device)
next_value = critic(next_state)
returns = discounted_rewards(next_value, rewards, masks)
log_probs = torch.cat(log_probs)
returns = torch.cat(returns).detach()
values = torch.cat(values)
advantage = returns - values
actor_loss = -(log_probs * advantage.detach()).mean()
critic_loss = advantage.pow(2).mean()
optimizerA.zero_grad()
optimizerC.zero_grad()
actor_loss.backward()
critic_loss.backward()
optimizerA.step()
optimizerC.step()الحين بنشغّل حلقة التدريب الرئيسية. بنستخدم تدريبًا يدويًا للشبكة: نحسب دوال الخسارة المناسبة، وبعدها نحدّث معاملات الشبكة:
actor = Actor(state_size, action_size).to(device)
critic = Critic(state_size, action_size).to(device)
run_episode(actor, critic, n_iters=100)وأخيرًا، خلونا نقفل البيئة.
env.close()## الزبدة
شفنا في هالعرض خوارزميتين للتعلم المعزز: تدرّج السياسة البسيطة، والممثل والناقد الأكثر تطورًا. هالخوارزميات تتعامل مع مفاهيم مجردة مثل الحالة والإجراء والمكافأة؛ وعشان كذا نقدر نطبقها على بيئات مختلفة جدًا.
يخلينا التعلم المعزز نتعلم أفضل استراتيجية لحل المسألة بمجرد مراقبة المكافأة النهائية. وبما إننا ما نحتاج مجموعات بيانات عليها تسميات، نقدر نكرر المحاكاة مرات كثيرة ونحسّن نماذجنا. ومع هذا، ما زالت فيه تحديات كثيرة في التعلم المعزز، وبتتعرفون عليها أكثر لو قررتوا تتخصصون في هالمجال الممتع من الذكاء الاصطناعي.
حذفنا المخرجات وعدّادات التشغيل والودجات والمحتوى النشط وقت الاستيراد. شغّل الدفاتر بس في بيئة خارجية تثق فيها.
سجّل تطبيقك
التسجيل اختياري، يفيدك تتذكر وش طبّقت، ولا يمنع إكمال الدورة.