التعلم المعزز باستخدام Keras (الجزء الأول)

424

في هذا المقال، سوف أعرض مفهوم التعلم المعزز  Reinforcement Learning، وأعرض لك كيفية إنشاء أداة مستقلة يمكنها لعب لعبة بسيطة بنجاح. يعد التعلم المعزز مجالًا نشطًا ومثيرًا للاهتمام في مجال البحث في التعلم الآلي Machine Learning، وقد أثبت كفاءته من خلال العديد من النجاحات مثل نظام AlphaGo ، الذي فاز بشكل مقنع على أفضل اللاعبين البشريين في العالم. حدث هذا في لعبة كان يُعتقد أنها صعبة للغاية على الآلات أن تتعلمها. في هذا المقال، سأقوم أولاً بتفصيل بعض النظريات أثناء التعامل مع لعبة الدمى في مجموعة أدوات Open AI Gym. سننشئ بعد ذلك جدول Q لهذه اللعبة باستخدام Python، ثم ننشئ شبكة Q باستخدام Keras. تتوفر جميع البرامج الموجودة في هذا المقال على صفحة Github.

 

التعلم المعزز  – الأساسيات

يمكن اعتبار التعلم المعزز الاخ الثالث من الثلاثي الخاص ب Machine Learning وهم: التعلم بدون إشراف، التعلم تحت الإشراف، التعلم المعزز. في التعلم الخاضع للإشراف، نقوم بتزويد النظام بأزواج التدريب المنسقة (x، y)، حيث يكون القصد هو أن تتعلم الشبكة الخريطة من x إلى y. في التعلم التعزيزي، نخلق اداة تؤدي أعمالًا في النطاق “مثل لعبة بسيطة” وتتلقى الأداه مكافآت مختلفة اعتمادًا على الحالة التي تكون عليها عند تنفيذ الإجراء. بعبارة أخرى، تستكشف الأداة نوعًا من الألعاب، فيتم تدريبها من خلال محاولة زيادة المكافآت في هذه اللعبة. هذه الدورة موضحة في الشكل أدناه:

كما يمكن ملاحظته أعلاه، تقوم الأداه بتنفيذ بعض الإجراءات في النطاق. ويترجم المفسر هذا الإجراء في النطاق ويطبع حالة محدثة تتواجد بها الأداة الآن، وكذلك المكافأة لاتخاذ هذا الإجراء. النطاق غير معروفة من قبل الأداة مسبقا، ولكن بالأحرى يتم اكتشافها من قبل الأداة باتخاذ خطوات تدريجية في الوقت المناسب. لذا، على سبيل المثال، في الوقت المناسب، يمكن للأداة، في الحالة ما st، اتخاذ إجراء. هذا يؤدي إلى حالة جديدة st + 1 ومكافأة r. يمكن أن تكون هذه المكافأة عبارة عن رقم حقيقي موجب، أو صفر، أو رقم حقيقي سلبي. الهدف من الأداة هو معرفة الإجراء الذي تعتمد عليه الحالة والذي يزيد من مكافآته. الطريقة التي تتعلم بها الأداة بشكل مثالي هي موضوع نظرية ومنهجيات التعلم المعزز. من أجل دراسة النظريات والنهج الممكنة وراء التعلم المعزز بشكل أكثر جدوى، من المفيد أن يكون لديك مثال بسيط للعمل من خلاله. سوف يأتي هذا المثال البسيط من النطاق المتوفر في Open AI Gym يسمى NChain.

 

نطاق Open AI Gym

نطاق NChain على Open AI Gym هو نطاق بسيط من 5 حالات. هناك نوعان من الإجراءات الممكنة في كل حالة، والمضي قدما (الإجراء 0) والانتقال إلى الوراء (الاجراء 1). عندما يتم اتخاذ الإجراء 1، أي الانتقال إلى الخلف، هناك مكافأة فورية قدرها 2 تُعطى للأداة – ويتم إرجاع الأداة إلى الحالة 0 (عودة إلى بداية السلسلة). ومع ذلك، عندما يتم اتخاذ إجراء التقدم إلى الأمام (الإجراء 0)، لا توجد مكافأة فورية حتى الحالة 4. عندما يتحرك الوكيل إلى الأمام في حالة 4، يتم تلقي الأداة مكافأة قيمتها 10. تبقى الأداة في الحالة 4 في هذه النقطة أيضًا، لذا يمكن تكرار المكافأة. هناك أيضًا احتمال عشوائي بأن يتم تحريك إجراء الأداة بواسطة النطاق (بمعنى أنه يتم تحويل الإجراء 0 إلى إجراء 1 والعكس). يوضح الرسم البياني أدناه هذا النطاق:

يمكنك اللعب مع هذا النطاق من خلال تثبيت Open AI Gym. ثم قم ببساطة بفتح نافذة الأوامر الخاص بـ Python وتشغيله – راجع الشكل أدناه للحصول على مثال لبعض الأوامر المتاحة:

إذا قمت بفحص البرنامج أعلاه، فيمكنك ملاحظة أنه يتم أولاً استيراد وحدة Python، ومن ثم يتم تحميل البيئة عبر الأمر ()gym.make. تتمثل الخطوة الأولى في إحياء / إعادة ضبط النطاق عن طريق تشغيل ()env.reset – يقوم هذا الأمر بإرجاع الحالة الأولية للنطاق – في هذه الحالة 0. الأمر الأول الذي أقوم بتشغيله هو (env.step (1 – القيمة في القوس هو مٌعرف الإجراء. كما هو موضح مسبقًا، يمثل الإجراء 1 خطوة إلى الوراء إلى بداية السلسلة (الحالة 0). يقوم الأمر ()step بإرجاع 4 متغيرات في المجموعة، وهذه هي (بالترتيب):

  • الحالة الجديدة بعد الإجراء
  • المكافأة بسبب الإجراء
  • سواء تم “إنهاء اللعبة” أم لا – تنتهي لعبة NChain بعد 1000 خطوة
  • معلومات التصحيح Debugging – ليست ذات صلة في هذا المثال

يمكنك ملاحظه، بدءًا من الحالة 0 وأخذ (step (1، تظل الأداة في الحالة 0 ويحصل على 2 كمكافأة. بعد ذلك، أرسلت سلسلة من أوامر (الإجراء 0). بعد كل إجراء 0، نتوقع تطور الأداة على طول السلسلة، مع زيادة الحالات (أي 0 -> 1 -> 2 وما إلى ذلك). ومع ذلك، ستلاحظ بعد الخطوة الأولى (0)، أن الأداة سيبقى في الحالة 0 ويحصل على مكافأتين. ويرجع ذلك إلى ميل عشوائي للنطاق إلى “قلب” الإجراء من حين لآخر، لذلك قامت الأداة بالفعل بالإجراء 1. هذا محزن نوعا ما.

ومع ذلك، يمكنني المثابرة ويمكن ملاحظة أن الحالة تزداد كما هو متوقع، ولكن لا يوجد مكافأة فورية للقيام بذلك للأداة حتى يصل إلى الحالة 4. عندما تكون في الحالة 4، سيحافظ الإجراء 0 على الأداة في الخطوة 4 ويعطي الوكيل 10 مكافأة. ليس ذلك فحسب، فالنطاق يسمح بإجراء ذلك بشكل متكرر، طالما أنه لا يقوم ب “قلب” الإجراء، والتي ستعيد الأاة إلى الحالة 0 – بداية السلسلة.

الآن بعد أن فهمنا النطاق الذي سيتم استخدامه في هذا المقال، فقد حان الوقت للنظر في الطريقة التي يمكن استخدامها لتدريب الأداة.

 

بناء اول مستكشف بدائي لتطبيق التعلم المعزز

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

دعنا نفكر في جدول، ونطلق عليه جدول المكافآت الذي يبدو كالتالي:

يتوافق كل من الصفوف مع الحالات الخمسة المتاحة في نطاق NChain، ويتطابق كل عمود مع الإجراءين المتوفرين في كل حالة – إلى الأمام والخلف، 0 و1. وتتوافق قيمة المكافأة في كل من خلايا الجدول مقياس أن الأداة قد “تعلمت” يحدث عندما تكون في تلك الحالة وتقوم بهذا الإجراء. إذاً، القيمة rs0,a0 قد تكون، على سبيل المثال، مجموع المكافآت التي استلمتها الأداة في الماضي كانت في الحالة 0 واتخذت الإجراء 0. سيسمح هذا الجدول للأداة بالاختيار بين الإجراءات بناءً على مجموع أو (متوسط، العدد الاوسط، وما إلى ذلك – اختر انت) مبلغ المكافأة التي تلقتها الأداة في الماضي عند اتخاذ الإجراءات 0 أو 1.

قد تكون هذه سياسة جيدة – اختر الإجراء الذي أدى إلى أكبر مكافأة تم جمعها سابقًا. دعنا نجربها:

def naive_sum_reward_agent(env, num_episodes=500):
    # this is the table that will hold our summated rewards for
    # each action in each state
    r_table = np.zeros((5, 2))
    for g in range(num_episodes):
        s = env.reset()
        done = False
        while not done:
            if np.sum(r_table[s, :]) == 0:
                # make a random selection of actions
                a = np.random.randint(0, 2)
            else:
                # select the action with highest cummulative reward
                a = np.argmax(r_table[s, :])
            new_s, r, done, _ = env.step(a)
            r_table[s, a] += r
            s = new_s
    return r_table

في تعريف الوظيفة، يتم تمرير النطاق كالوسيط الأول، ثم عدد الحلقات (أو عدد الألعاب) التي سنقوم بتدريب r_table عليها. نقوم أولاً بإنشاء المصفوفة r_table التي قدمتها سابقاً والتي ستحمل مكافآتنا المجمعة لكل حالة وإجراء. ثم هناك حلقة خارجية تدور خلال عدد الحلقات. يبدأ الأمر()env.reset اللعبة من جديد في كل مرة تبدأ فيها حلقة جديدة. كما يعيد حالة البداية للعبة، والتي يتم تخزينها في المتغير s.

تستمر الحلقة الداخلية الثانية حتى يتم إرجاع إشارة “مكتملة” بعد أن يتم تمرير الإجراء إلى النطاق. إذا كانت العبارة if في السطر الأول من الحلقة الداخلية تتحقق لمعرفة ما إذا كانت هناك أي قيم موجودة في r_table للحالة الحالية – فهي تفعل ذلك من خلال التأكيد على ما إذا كان مجموع الصف يساوي 0. إذا كان صفرًا، يتم اختيار إجراء عشوائي – لا توجد معلومات أفضل متوفرة في هذه المرحلة للحكم على الإجراء الذي يجب اتخاذه.

هذا الشرط لن يستمر إلا لفترة قصيرة من الزمن. بعد هذه النقطة، ستكون هناك قيمة مخزنة في واحد على الأقل من الإجراءات لكل حالة، وسيتم اختيار الإجراء بناءً على قيمة العمود التي تكون أكبر لحالة الصف. يتم تنفيذ هذا الاختيار للعمود الأقصى بواسطة ()numpy.argmax تقوم هذه الوظيفة بإرجاع فهرس vector / matrix بأعلى قيمة. على سبيل المثال، إذا كان الأداة في الحالة 0 ولدينا r_table مع قيم [100، 1000] للصف الأول، فسيتم تحديد الإجراء 1 حيث يكون الفهرس ذو القيمة الأعلى هو العمود 1.

بعد تحديد الإجراء وتخزينه في a، يتم إدخال هذا الإجراء في النطاق باستخدام (env.step (a. يقوم هذا الأمر بإرجاع الحالة الجديدة، والمكافأة على هذا الإجراء، سواء تم “إنهاء” اللعبة في هذه المرحلة ومعلومات تصحيح الأخطاء التي لا نهتم بها. في السطر التالي، فإن الخلية r_table المقابلة للحالة s والإجراء a تتحدث عن طريق إضافة المكافأة إلى ما هو موجود بالفعل في خلية الجدول.

وأخيراً يتم تحديث الحالة s إلى new_s – الحالة الجديدة للأداة.

إذا قمنا بتشغيل هذه الوظيفة، سيبدو r_table بالشكل التالي:

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

من الواضح – هناك شيء خاطئ في هذا الجدول. يتوقع المرء أنه في الحالة رقم 4، سيكون الإجراء الأكثر مكافأة بالنسبة للأداة هو اختيار الإجراء 0، والذي من شأنه أن يكافئ الأداة بعشرة نقاط، بدلاً من النقطتين المعتادتين من الإجراء 1. ليس ذلك فحسب، بل تم اختيار الإجراء 0 لجميع الحالات – وهذا يتعارض مع المنطق – من المؤكد أنه سيكون من الأفضل أن نتجه في بعض الأحيان نحو الحالة 4 باختيار إجراء 0 عدة مرات في صف واحد، وبهذه الطريقة سنجني العشر درجات.

في الواقع، هناك عدد من المشاكل المتعلقة بهذه الطريقة في إجراء التعلم المعزز:

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

دعونا نرى كيف يمكن إصلاح هذه المشاكل.

 

مكافأة المتأخرة في التعليم المعزز

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

يمكننا أن نجلب هذه المفاهيم إلى فهمنا للتعلم المعزز. لنفترض أننا في حالة 3 – في الحالة السابقة، عندما اختارت الأداة الإجراء 0 للوصول إلى الحالة 3، كانت المكافأة 0، وبالتالي r_table [3, 0] = 0. من الواضح أن الأداة لن ترى ذلك على أنه جذاب كخطوة مقارنة بالبدائل لهذه الحالة أي r_table [3, 1] >= 2. ولكن ماذا لو اعطينا هذه الحالة المكافأة التي ستحصل عليها الأداة إذا اختارت الإجراء 0 في الحالة 4؟ سيبدو هذا r_table [3, 0] = r + 10 = 10 – a وبالتالي يصبح هذا بديل أكثر جاذبية بكثير!

تُعد فكرة نشر المكافأة المحتملة من أفضل الإجراءات الممكنة في الحالات المستقبلية عنصراً أساسياً فيما يُعرف باسم Q learning. فيه يتم تحديث القيمة Q لكل إجراء في كل حالة عندما يتم توفير المعلومات ذات الصلة. قاعدة Q learning هي:

أولاً، كما يمكنك ملاحظة، هذه قاعدة تحديث – يتم إضافة قيمة Q الموجودة، وليس استبدالها. تجاهل الدالة α في الوقت الحالي، يمكننا التركيز على ما يوجد داخل الأقواس. المصطلح الأول، r، هو المكافأة التي تم الحصول عليها عندما تم اتخاذ إجراء في الحالة. بعد ذلك، لدينا تعبير أكثر تعقيدًا بعض الشيء. تجاهل γ للحظة والتركيز على (maxa′Q(s′,a. ما يعنيه هذا هو أننا ننظر إلى الحالة التالية بعد الإجراء وإرجاع أقصى قيمة ممكنة للقيمة Q في الحالة التالية. بمعنى آخر، قم بإرجاع قيمة Q القصوى للحصول على أفضل إجراء ممكن في الحالة التالية. وبهذه الطريقة، تتطلع الأداة إلى تحديد أفضل المكافآت المستقبلية المحتملة قبل اتخاذ الخطوة التالية.

تسمى القيمة γ عامل الخصم – وهذا يقلل من تأثير المكافآت المستقبلية على اتخاذ القرار الفوري في الحالة. هذا أمر مهم، لأن هذا يمثل صبرًا محدودًا من الأداة – لن يدرس إلى الأبد للحصول على هذه الدرجة الطبية. لذا، سيظل γ أقل من 1. يعمل مصطلح (Q (s, a على تقييد نمو قيمة Q حيث يتطور تدريب الأداة خلال العديد من التكرارات. وأخيرًا، يتم ضرب هذا المجموع من خلال معدل التعلم α الذي يقيد التحديث لضمان عدم “التسرع” إلى حل – وهذا أمر مهم لتحقيق التقارب الأمثل.

لاحظ أنه بينما تفحص قاعدة التعلم فقط أفضل إجراء في الحالة التالية، في الواقع، لا تزال المكافآت المخصومة تتدرج من الحالات المستقبلية. على سبيل المثال، إذا فكرنا في المكافآت المتتالية من جميع الإجراءات 0 (أي المضي قدما على طول السلسلة) والبدء في الحالة 3، فستكون مكافأة r+γmaxaQ(s′,a′) =0+0.95∗10=9.5 مع γ = 0.95. إذا عدنا من الحالة 3 إلى الحالة 2 فسيكون 0 + 0.95 * 9.5 = 9.025. وبالمثل، فإن المكافأة المتتالية، المخصومة من الحالة 1 ستكون 0 + 0.95 * 9.025 = 8.57، وهكذا. لذلك، في حين أن حساب التحديث الفوري لا ينُظر إلا إلى الحد الأقصى لقيمة Q للحالة التالية، إن المكافآت التي تم اكتشافها من قبل من خلال الأداة لا تزال تتدرج في اتخاذ القرار الحالي للحالة والإجراء. هذا يعتبر تبسيط، بسبب المعدل المتغير للتعلم والأحداث العشوائية في هذا النطاق، لكنه يمثل الفكرة العامة.

الآن وبعد أن فهمت Q Learning، دعنا نرى كيف يبدو في الحقيقة:

def q_learning_with_table(env, num_episodes=500):
    q_table = np.zeros((5, 2))
    y = 0.95
    lr = 0.8
    for i in range(num_episodes):
        s = env.reset()
        done = False
        while not done:
            if np.sum(q_table[s,:]) == 0:
                # make a random selection of actions
                a = np.random.randint(0, 2)
            else:
                # select the action with largest q value in state s
                a = np.argmax(q_table[s, :])
            new_s, r, done, _ = env.step(a)
            q_table[s, a] += r + lr*(y*np.max(q_table[new_s, :]) - q_table[s, a])
            s = new_s
    return q_table

هذه الوظيفة تقريبًا تمامًا مثل الدالة r_table الساذجة السابقة التي تمت مناقشتها. الإضافات والتغييرات هي:

  • المتغير y الذي يحدد عامل الخصم γ وlr هو معدل تحديث عملية التعلم
  • q_table[s, a] += r + lr*(y*np.max(q_table[new_s, :]) - q_table[s, a])

    ينفذ هذا السطر قاعدة Q Learning التي تم تقديمها مسبقًا. يعتبر ([:,np.max(q_table[new_s طريقة سهلة لتحديد الحد الأقصى للقيمة في q_table للصف new_s. بعد تشغيل هذه الوظيفة ، يكون الناتج q_table مثال:

هذا الناتج غريب، أليس كذلك؟ مرة أخرى، نتوقع على الأقل الحالة 4 – إجراء 0 من أجل الحصول على أعلى قيمة Q، ولكنها لا تفعل ذلك. قد نتوقع أيضًا أن تكون المكافأة من هذا الإجراء في هذه الحالة متدرجة عبر الحالات من 0 إلى 3. لقد حدث خطأ ما بشكل واضح – والجواب هو أنه لا يوجد ما يكفي من الاستكشافات في إطار أسلوب تدريب الأداة.

و بذلك ينتهي الجزء الاول. الي اللقاء في الجزء الثاني ان شاء الله

 

المصدر:(Reinforcement learning tutorial using Python and Keras)

تعليقات