You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ML-For-Beginners/translations/ar/8-Reinforcement/2-Gym
localizeflow[bot] c40c66d479
[de,ru,ar] chore(i18n): sync translations
1 month ago
..
solution chore(i18n): sync translations with latest source changes (chunk 1/1, 300 changes) 7 months ago
README.md [de,ru,ar] chore(i18n): sync translations 1 month ago
assignment.md chore(i18n): sync translations with latest source changes (chunk 1/1, 300 changes) 7 months ago
notebook.ipynb 🌐 Update translations via Co-op Translator 12 months ago

README.md

تزلج على العربة القطب

المشكلة التي كنا نحلها في الدرس السابق قد تبدو كمشكلة لعبة، ليست قابلة للتطبيق حقًا في سيناريوهات الحياة الواقعية. هذا ليس صحيحًا، لأن العديد من المشكلات الواقعية تشترك أيضًا في هذا السيناريو - بما في ذلك لعب الشطرنج أو جو. هي مشابهة، لأن لدينا أيضًا لوحة بقواعد معينة وحالة متقطعة.

اختبار ما قبل المحاضرة

مقدمة

في هذا الدرس سنطبق نفس مبادئ التعلم القيمي (Q-Learning) على مشكلة ذات حالة مستمرة، أي حالة تحددها واحدة أو أكثر من الأرقام الحقيقية. سنتعامل مع المشكلة التالية:

المشكلة: إذا أراد بيتر الهروب من الذئب، يحتاج لأن يكون قادرًا على التحرك بسرعة أكبر. سنرى كيف يمكن لبيتر تعلم التزلج، خصوصًا الحفاظ على التوازن، باستخدام التعلم القيمي.

الهروب العظيم!

بيتر وأصدقاؤه يبدعون للهروب من الذئب! الصورة بواسطة جين لوبير

سنستخدم نسخة مبسطة من التوازن تعرف بمشكلة العربة والقطب. في عالم العربة والقطب، لدينا منزلق أفقي يمكنه التحرك لليسار أو اليمين، والهدف هو موازنة عمود رأسي على قمة المنزلق.

عربة القطب

المتطلبات الأساسية

في هذا الدرس، سنستخدم مكتبة تسمى OpenAI Gym لمحاكاة بيئات مختلفة. يمكنك تشغيل كود هذا الدرس محليًا (مثلًا من Visual Studio Code)، وفي هذه الحالة ستفتح المحاكاة في نافذة جديدة. عند تشغيل الكود عبر الإنترنت، قد تحتاج إلى تعديل بعض الأكواد كما هو موضح هنا.

OpenAI Gym

في الدرس السابق، كانت قواعد اللعبة والحالة معينة بواسطة فئة Board التي عرفناها بأنفسنا. هنا سنستخدم بيئة محاكاة خاصة، ستحاكي الفيزياء وراء عمود التوازن. إحدى أشهر بيئات المحاكاة لتدريب خوارزميات التعلم المعزز هي ما يسمى بـ Gym، التي تديرها OpenAI. باستخدام هذه الصالة الرياضية (gym) يمكننا إنشاء بيئات مختلفة من محاكاة العربة والقطب إلى ألعاب آتاري.

ملاحظة: يمكنك رؤية بيئات أخرى متاحة من OpenAI Gym هنا.

أولاً، لنثبت gym ونستورد المكتبات اللازمة (كتلة الأكواد 1):

import sys
!{sys.executable} -m pip install gym 

import gym
import matplotlib.pyplot as plt
import numpy as np
import random

تمرين - تهيئة بيئة عربة القطب

للعمل مع مشكلة توازن العربة والقطب، نحتاج إلى تهيئة البيئة المقابلة. كل بيئة مرتبطة بـ:

  • فضاء الرصد الذي يحدد بنية المعلومات التي نستقبلها من البيئة. بالنسبة لمشكلة العربة والقطب، نستقبل موضع القطب، السرعة وبعض القيم الأخرى.

  • فضاء الإجراءات الذي يحدد الإجراءات الممكنة. في حالتنا، فضاء الإجراءات متقطع، ويتكون من إجراءين - اليسار واليمين. (كتلة الأكواد 2)

  1. للتهيئة، اكتب الكود التالي:

    env = gym.make("CartPole-v1")
    print(env.action_space)
    print(env.observation_space)
    print(env.action_space.sample())
    

لرؤية كيفية عمل البيئة، لنقم بتشغيل محاكاة قصيرة من 100 خطوة. في كل خطوة، نعطي إجراءً واحدًا ليتم تنفيذه - في هذه المحاكاة نختار إجراء عشوائيًا من action_space.

  1. شغل الكود أدناه وانظر إلى النتيجة.

    تذكر أنه من الأفضل تشغيل هذا الكود على تثبيت بايثون محلي! (كتلة الأكواد 3)

    env.reset()
    
    for i in range(100):
       env.render()
       env.step(env.action_space.sample())
    env.close()
    

    يجب أن ترى شيئًا مماثلًا لهذه الصورة:

    عربة القطب غير متوازنة

  2. أثناء المحاكاة، نحتاج إلى الحصول على الملاحظات لاتخاذ قرار الفعل. في الحقيقة، دالة step تُرجع الملاحظات الحالية، دالة المكافأة، وعلم الانتهاء الذي يشير إلى ما إذا كان من المنطقي الاستمرار في المحاكاة أم لا: (كتلة الأكواد 4)

    env.reset()
    
    done = False
    while not done:
       env.render()
       obs, rew, done, info = env.step(env.action_space.sample())
       print(f"{obs} -> {rew}")
    env.close()
    

    ستنتهي برؤية شيء مثل هذا في ناتج النوتبوك:

    [ 0.03403272 -0.24301182  0.02669811  0.2895829 ] -> 1.0
    [ 0.02917248 -0.04828055  0.03248977  0.00543839] -> 1.0
    [ 0.02820687  0.14636075  0.03259854 -0.27681916] -> 1.0
    [ 0.03113408  0.34100283  0.02706215 -0.55904489] -> 1.0
    [ 0.03795414  0.53573468  0.01588125 -0.84308041] -> 1.0
    ...
    [ 0.17299878  0.15868546 -0.20754175 -0.55975453] -> 1.0
    [ 0.17617249  0.35602306 -0.21873684 -0.90998894] -> 1.0
    

    متجه الملاحظات الذي يرجع في كل خطوة محاكاة يحتوي القيم التالية:

    • موضع العربة
    • سرعة العربة
    • زاوية القطب
    • معدل دوران القطب
  3. احصل على الحد الأدنى والحد الأقصى لهذه الأرقام: (كتلة الأكواد 5)

    print(env.observation_space.low)
    print(env.observation_space.high)
    

    قد تلاحظ أيضًا أن قيمة المكافأة في كل خطوة محاكاة دائمًا 1. هذا لأن هدفنا هو البقاء لأطول فترة ممكنة، أي الحفاظ على القطب في وضع عمودي معقول لأطول وقت.

    في الحقيقة، تعتبر محاكاة CartPole محلولة إذا استطعنا الحصول على المكافأة المتوسطة 195 خلال 100 محاكاة متتالية.

تقسيم الحالة

في التعلم القيمي، نحتاج لبناء جدول Q يحدد ما يجب فعله في كل حالة. لنتمكن من ذلك، نحتاج أن تكون الحالة متقطعة، وبشكل أدق أن تحتوي على عدد محدود من القيم المتقطعة. لذا، نحتاج إلى طريقة لـ تقسيم ملاحظاتنا، وتحويلها إلى مجموعة محدودة من الحالات.

هناك عدة طرق للقيام بذلك:

  • التقسيم إلى حاويات. إذا عرفنا فترة قيمة معينة، يمكننا تقسيم هذه الفترة إلى عدد من الحاويات، ثم نستبدل القيمة برقم الحاوية التي تنتمي إليها. يمكن القيام بذلك باستخدام دالة numpy digitize. في هذه الحالة، سنعرف بالضبط حجم الحالة، لأنه يعتمد على عدد الحاويات التي نختارها للرقمنة.

يمكننا استخدام الاستيفاء الخطي لجلب القيم إلى فترة محدودة (مثلاً من -20 إلى 20)، ثم تحويل الأرقام إلى أعداد صحيحة بتقريبها. هذا يمنحنا سيطرة أقل على حجم الحالة، خصوصًا إذا لم نكن نعرف نطاق القيم المدخلة بدقة. على سبيل المثال، في حالتنا، 2 من أصل 4 قيم لا تملك حدود عليا أو دنيا، مما قد يؤدي إلى عدد لا نهائي من الحالات.

في مثالنا، سنختار المنهج الثاني. كما قد تلاحظ لاحقًا، رغم الحدود العليا / الدنيا غير المحددة، فإن هذه القيم نادرًا ما تأخذ قيمًا خارج فترات محدودة معينة، لذلك الحالات ذات القيم القصوى ستكون نادرة جدًا.

  1. هذه هي الدالة التي تأخذ الملاحظة من نموذجنا وتنتج زوجًا مكونًا من 4 قيم صحيحة: (كتلة الأكواد 6)

    def discretize(x):
        return tuple((x/np.array([0.25, 0.25, 0.01, 0.1])).astype(np.int))
    
  2. لنستكشف أيضًا طريقة تقسيم أخرى باستخدام الحاويات: (كتلة الأكواد 7)

    def create_bins(i,num):
        return np.arange(num+1)*(i[1]-i[0])/num+i[0]
    
    print("Sample bins for interval (-5,5) with 10 bins\n",create_bins((-5,5),10))
    
    ints = [(-5,5),(-2,2),(-0.5,0.5),(-2,2)] # فترات القيم لكل معلمة
    nbins = [20,20,10,10] # عدد الحاويات لكل معلمة
    bins = [create_bins(ints[i],nbins[i]) for i in range(4)]
    
    def discretize_bins(x):
        return tuple(np.digitize(x[i],bins[i]) for i in range(4))
    
  3. لنشغل الآن محاكاة قصيرة ونلاحظ تلك القيم البيئية المتقطعة. يمكنك تجربة كل من discretize وdiscretize_bins لترى إذا كان هناك فرق.

    discretize_bins تعيد رقم الحاوية، الذي يكون مبدئيًا 0. لذا للقيم حول 0، تعيد الرقم من مركز الفترة (10). في discretize، لم نهتم بنطاق القيم المخرجة، مما سمح بأن تكون سالبة، لذا قيم الحالة ليست منتقلًة، و0 يمثل 0. (كتلة الأكواد 8)

    env.reset()
    
    done = False
    while not done:
       #عرض البيئة ()
       obs, rew, done, info = env.step(env.action_space.sample())
       #طباعة (discretize_bins(obs))
       print(discretize(obs))
    env.close()
    

    قم بإلغاء تعليق السطر الذي يبدأ بـ env.render إذا أردت رؤية كيف تنفذ البيئة. وإلا يمكنك تشغيلها في الخلفية، مما يكون أسرع. سنستخدم هذا التنفيذ "غير المرئي" خلال عملية التعلم القيمي.

هيكل جدول Q

في درسنا السابق، كانت الحالة زوجًا بسيطًا من الأرقام من 0 إلى 8، لذا كان من الملائم تمثيل جدول Q عن طريق مصفوفة numpy ذات شكل 8x8x2. إذا استخدمنا تقسيم الحاويات، سيكون حجم متجه الحالة معروفًا، لذا يمكننا استخدام نفس النهج وتمثيل الحالة كمصفوفة ذات شكل 20x20x10x10x2 (هنا 2 هو بُعد فضاء الإجراءات، والأبعاد الأولى تمثل عدد الحاويات التي اخترناها لكل من معايير فضاء الملاحظات).

أحيانًا لا تكون أبعاد فضاء الملاحظات الدقيقة معروفة. في حالة دالة discretize، قد لا نكون متأكدين أبدًا أن حالتنا تبقى ضمن حدود معينة، لأن بعض القيم الأصلية غير محدودة. لذلك، سنستخدم نهجًا مختلفًا قليلاً ونمثل جدول Q بواسطة قاموس.

  1. استخدم الزوج (state, action) كمفتاح في القاموس، والقيمة ستكون القيمة في جدول Q. (كتلة الأكواد 9)

    Q = {}
    actions = (0,1)
    
    def qvalues(state):
        return [Q.get((state,a),0) for a in actions]
    

    هنا نعرف أيضًا دالة qvalues() التي ترجع قائمة بقيم جدول Q لحالة معينة تتوافق مع كل الإجراءات الممكنة. إذا لم يكن الإدخال موجودًا في جدول Q، نُرجع 0 كافتراضي.

لنبدأ التعلم القيمي

الآن نحن جاهزون لتعليم بيتر كيفية التوازن!

  1. أولًا، دعنا نحدد بعض المعاملات الفائقة: (كتلة الأكواد 10)

    # المعلمات الفائقة
    alpha = 0.3
    gamma = 0.9
    epsilon = 0.90
    

    هنا، alpha هو معدل التعلم الذي يحدد مدى تعديل القيم الحالية في جدول Q في كل خطوة. في الدرس السابق بدأنا بـ 1، ثم خفضنا alpha إلى قيم أقل أثناء التدريب. في هذا المثال سنبقيه ثابتًا للبساطة، ويمكنك تجربة تعديل قيم alpha لاحقًا.

    gamma هو عامل الخصم الذي يوضح مدى أولوية المكافأة المستقبلية على المكافأة الحالية.

    epsilon هو عامل الاستكشاف / الاستغلال الذي يحدد ما إذا كان يجب أن نفضل الاستكشاف على الاستغلال أو العكس. في الخوارزمية لدينا، سنختار الإجراء التالي وفقًا لقيم جدول Q في نسبة epsilon من الحالات، وفي الحالات المتبقية ننفذ إجراءً عشوائيًا. هذا يسمح لنا باستكشاف مناطق لم نرها من قبل في فضاء البحث.

    من حيث التوازن - اختيار إجراء عشوائي (استكشاف) يشبه لكمة عشوائية في الاتجاه الخاطئ، ويجب على القطب أن يتعلم كيف يعيد التوازن من تلك "الأخطاء".

تحسين الخوارزمية

يمكننا أيضًا إجراء تحسينين لخوارزميتنا من الدرس السابق:

  • حساب متوسط المكافأة التراكمية عبر عدد من المحاكيات. سنطبع التقدم كل 5000 تكرار، وسنحسب المتوسط للمكافأة التراكمية خلال تلك الفترة. هذا يعني أنه إذا حصلنا على أكثر من 195 نقطة - يمكننا اعتبار المشكلة محلولة بجودة أعلى حتى من المطلوبة.

  • حساب الحد الأقصى للمتوسط التراكمي، Qmax، وسنخزن جدول Q المقابل لذلك. عند تشغيل التدريب ستلاحظ أن متوسط النتيجة يبدأ أحيانًا في الانخفاض، ونريد الاحتفاظ بقيم جدول Q التي تمثل أفضل نموذج تم ملاحظته أثناء التدريب.

  1. اجمع كل المكافآت التراكمية في كل محاكاة في متجه rewards للرسم اللاحق. (كتلة الأكواد 11)

    def probs(v,eps=1e-4):
        v = v-v.min()+eps
        v = v/v.sum()
        return v
    
    Qmax = 0
    cum_rewards = []
    rewards = []
    for epoch in range(100000):
        obs = env.reset()
        done = False
        cum_reward=0
        # == قم بالمحاكاة ==
        while not done:
            s = discretize(obs)
            if random.random()<epsilon:
                # الاستغلال - اختر الإجراء وفقًا لاحتمالات جدول Q
                v = probs(np.array(qvalues(s)))
                a = random.choices(actions,weights=v)[0]
            else:
                # الاستكشاف - اختر الإجراء عشوائيًا
                a = np.random.randint(env.action_space.n)
    
            obs, rew, done, info = env.step(a)
            cum_reward+=rew
            ns = discretize(obs)
            Q[(s,a)] = (1 - alpha) * Q.get((s,a),0) + alpha * (rew + gamma * max(qvalues(ns)))
        cum_rewards.append(cum_reward)
        rewards.append(cum_reward)
        # == طباعة النتائج دوريًا وحساب متوسط المكافأة ==
        if epoch%5000==0:
            print(f"{epoch}: {np.average(cum_rewards)}, alpha={alpha}, epsilon={epsilon}")
            if np.average(cum_rewards) > Qmax:
                Qmax = np.average(cum_rewards)
                Qbest = Q
            cum_rewards=[]
    

ما قد تلاحظ من تلك النتائج:

  • نحن قريبون من هدفنا. نحن قريبون جدًا من تحقيق الهدف بالحصول على 195 مكافأة تراكمية خلال 100+ جولة متتالية من المحاكاة، أو قد نكون قد حققناه بالفعل! حتى إذا حصلنا على أرقام أقل، لا يمكننا التأكد لأنه يتم التوسط خلال 5000 جولة، بينما فقط 100 جولة مطلوبة في المعايير الرسمية.

  • المكافأة تبدأ في الانخفاض. أحيانًا تبدأ المكافأة في الانخفاض، مما يعني أننا قد "ندمر" القيم التي تعلمناها في جدول Q بقيم تجعل الوضع أسوأ.

هذا الملاحظة تصبح أكثر وضوحًا إذا رسمنا تقدم التدريب.

رسم تقدم التدريب

أثناء التدريب، جمعنا قيمة المكافأة التراكمية في كل تكرار في متجه rewards. هكذا يبدو شكل الرسم مقابل رقم التكرار:

plt.plot(rewards)

تقدم الخام

من هذا الرسم لا يمكننا استنتاج شيء، لأن طبيعة عملية التدريب العشوائية تجعل طول جلسات التدريب يختلف كثيرًا. لجعل هذا الرسم أكثر معنى، يمكننا حساب المتوسط المتحرك عبر سلسلة من التجارب، لنقل 100 تجربة. يمكن تنفيذ ذلك بسهولة باستخدام np.convolve: (كتلة الأكواد 12)

def running_average(x,window):
    return np.convolve(x,np.ones(window)/window,mode='valid')

plt.plot(running_average(rewards,100))

تقدم التدريب

تغيير المعاملات الفائقة

لجعل التعلم أكثر استقرارًا، من المنطقي تعديل بعض معاملاتنا الفائقة أثناء التدريب. على وجه الخصوص:

  • لمعدل التعلم alpha، قد نبدأ بقيم قريبة من 1، ثم نخفض هذا المعامل تدريجيًا. مع الزمن، سنحصل على قيم احتمال جيدة في جدول Q، لذا يجب أن نعدلها قليلاً، وليس استبدالها تمامًا بقيم جديدة.

  • زيادة epsilon. قد نرغب في زيادة epsilon ببطء، لتقليل الاستكشاف وزيادة الاستغلال. من المنطقي أن نبدأ بقيمة منخفضة لـ epsilon ونصل بها إلى ما يقرب من 1.

المهمة 1: جرب قيم المعاملات الفائقة وانظر إن كنت تستطيع تحقيق مكافأة تراكمية أعلى. هل تحصل على أكثر من 195؟

المهمة 2: لحل المشكلة رسمياً، تحتاج إلى تحقيق متوسط مكافأة قدره 195 عبر 100 تجربة متتالية. قم بقياس ذلك أثناء التدريب وتأكد من أنك قد حللت المشكلة رسمياً!

مشاهدة النتيجة في التنفيذ

سيكون من المثير للاهتمام رؤية كيف يتصرف النموذج المدرب بالفعل. دعنا نشغل المحاكاة ونتبع نفس استراتيجية اختيار الإجراء كما في التدريب، مع أخذ عينات وفقاً لتوزيع الاحتمالية في جدول Q: (كتلة الكود 13)

obs = env.reset()
done = False
while not done:
   s = discretize(obs)
   env.render()
   v = probs(np.array(qvalues(s)))
   a = random.choices(actions,weights=v)[0]
   obs,_,done,_ = env.step(a)
env.close()

يجب أن ترى شيئاً مثل هذا:

a balancing cartpole


🚀التحدي

المهمة 3: هنا، كنا نستخدم النسخة النهائية من جدول Q، والتي قد لا تكون الأفضل. تذكر أننا قد خزنا أفضل جدول Q أداءً في متغير Qbest! جرّب المثال نفسه باستخدام أفضل جدول Q أداءً بنسخ Qbest إلى Q وانظر إذا لاحظت الفرق.

المهمة 4: هنا لم نكن نختار أفضل إجراء في كل خطوة، بل كنا نأخذ عينات باستخدام توزيع الاحتمالية المطابق. هل سيكون من المنطقي دائماً اختيار أفضل إجراء، ذو أعلى قيمة في جدول Q؟ يمكن القيام بذلك باستخدام دالة np.argmax لاكتشاف رقم الإجراء المقابل لأعلى قيمة في جدول Q. نفذ هذه الاستراتيجية وانظر إذا كانت تحسن التوازن.

اختبار ما بعد المحاضرة

المهمة

تدريب سيارة جبلية

الخلاصة

لقد تعلمنا الآن كيفية تدريب العوامل لتحقيق نتائج جيدة فقط من خلال تزويدهم بدالة مكافأة تعرف الحالة المرغوبة للعبة، ومن خلال منحهم فرصة لاستكشاف فضاء البحث بذكاء. لقد طبقنا بنجاح خوارزمية التعلم Q في حالات البيئات المتقطعة والمستمرة، ولكن مع أفعال متقطعة.

من المهم أيضاً دراسة الحالات التي يكون فيها فضاء الأفعال مستمراً، وعندما يكون فضاء الملاحظات أكثر تعقيداً، مثل الصورة من شاشة لعبة أتاري. في تلك المشاكل غالباً ما نحتاج لاستخدام تقنيات تعلم آلي أقوى، مثل الشبكات العصبية، من أجل تحقيق نتائج جيدة. هذه الموضوعات الأكثر تقدماً هي موضوع دورتنا القادمة المتقدمة في الذكاء الاصطناعي.


تنويه: تمت ترجمة هذا المستند باستخدام خدمة الترجمة بالذكاء الاصطناعي Co-op Translator. بينما نسعى للدقة، يرجى العلم أن الترجمات الآلية قد تحتوي على أخطاء أو عدم دقة. يجب اعتبار المستند الأصلي بلغته الأصلية المصدر الرسمي والمعتمد. للمعلومات الهامة، يُنصح بالاستعانة بترجمة بشرية محترفة. نحن غير مسؤولين عن أي سوء فهم أو تفسير ناتج عن استخدام هذه الترجمة.