التعلم المعزز باستخدام Keras (الجزء الثاني)

185

والان لنكمل في الجزء الثاني مع Q Learning للاختيار الجشع-ϵ وبناء شبكة عصبية من Keras

Q learning سياسة الاختيار الجشع greedy-ϵ

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

لذلك نحن بحاجة إلى وسيلة لكي تختار الأداة دائمًا “الأفضل” من الإجراءات في النطاق، وفي نفس الوقت تسمح للأداة بعدم الدخول في المسار “المغلق” وإعطائه بعض المساحة لاستكشاف البدائل. ما هو مطلوب هو سياسة الجشع ϵ-greedy.

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

def eps_greedy_q_learning_with_table(env, num_episodes=500):
    q_table = np.zeros((5, 2))
    y = 0.95
    eps = 0.5
    lr = 0.8
    decay_factor = 0.999
    for i in range(num_episodes):
        s = env.reset()
        eps *= decay_factor
        done = False
        while not done:
            # select the action with highest cummulative reward
            if np.random.random() < eps or np.sum(q_table[s, :]) == 0:
                a = np.random.randint(0, 2)
            else:
                a = np.argmax(q_table[s, :])
            # pdb.set_trace()
            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

يوضح البرنامج القيمة ϵ - eps. هناك أيضًا dib decay_factor مقترنًا بالتعطيل قيمة epsمع كل حلقة epps * = decay_factor. يمكن العثور على اختيار الإجراء القائم على الجشع هنا:

if np.random.random() < eps or np.sum(q_table[s, :]) == 0:
    a = np.random.randint(0, 2)
else:
    a = np.argmax(q_table[s, :])

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

q_table سيبدو كالتالي:

وأخيرًا، لدينا جدول يفضّل الإجراء 0 في الحالة 4 – بعبارة أخرى ما نتوقع حدوثه في ضوء مكافأة العشرة نقاط التي تم الاستيلاء عليها عبر هذا الإجراء في تلك الحالة. لاحظ أيضًا أنه، خلافاً للجداول السابقة من الطرق الأخرى، أنه لا توجد إجراءات ذات قيمة=0 ل Q – وذلك لأن مساحة الفعل الكاملة قد تم استكشافها عبر العشوائية التي أدخلتها سياسة الجشع- ϵ.

 

المقارنة

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

def test_methods(env, num_iterations=100):
    winner = np.zeros((3,))
    for g in range(num_iterations):
        m0_table = naive_sum_reward_agent(env, 500)
        m1_table = q_learning_with_table(env, 500)
        m2_table = eps_greedy_q_learning_with_table(env, 500)
        m0 = run_game(m0_table, env)
        m1 = run_game(m1_table, env)
        m2 = run_game(m2_table, env)
        w = np.argmax(np.array([m0, m1, m2]))
        winner[w] += 1
        print("Game {} of {}".format(g + 1, num_iterations))
    return winner

أولا، تُنشئ هذه الطريقة مصفوفة صغيرة بطول 3 للاحتفاظ بنتائج الفائز في كل عملية تكرار – الطريقة الفائزة هي الطريقة التي تعيد أعلى المكافآت بعد التدريب واللعب. تبدو وظيفة () run_game كما يلي:

def run_game(table, env):
    s = env.reset()
    tot_reward = 0
    done = False
    while not done:
        a = np.argmax(table[s, :])
        s, r, done, _ = env.step(a)
        tot_reward += r
    return tot_reward

هنا، يمكن ملاحظة أنه يتم استخدام الجدول المدرّب الممنوح للوظيفة في اختيار الإجراء، ويتم إرجاع المكافأة الإجمالية المتراكمة خلال اللعبة. يظهر نتيجة عينة من هذه التجربة (أي المتجه w) أدناه:

[13, 22, 65]

كما يمكن ملاحظته ايضا، من بين الـ 100 تجربة، فإن خوارزمية Q learning الجشع- ϵ (أي النموذج الثالث الذي تم تقديمه) تفوز بـ 65 منها. يلي ذلك التنفيذ الجشع القياسي ل Q learning، الذي فاز بـ 22 من التجارب. أخيرا، فازت طريقة المكافآت المتراكمة الساذجة فقط بـ 13 تجربة. لذلك، فإن طريقة Q learning الجشع- ϵهي طريقة فعالة لتنفيذ التعلم المعزز.

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

يصبح وقتها استخدام الشبكات العصبية ضرورة في التعلم المعزز. بدلاً من امتلاك جداول صريحة، يمكننا بدلاً من ذلك تدريب شبكة عصبية للتنبؤ بقيم Q لكل إجراء في حالة معينة. سيتم توضيح ذلك باستخدام Keras في القسم التالي.

 

التعلم المعزز مع Keras

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

يظهر هيكل التعلم التعزيزي الذي سنقوم ببنائه في Keras أدناه:

المدخلات إلى الشبكة هي ناقلات الحالة ذات التشفير الواحد one-hot encoded. على سبيل المثال، المتجه الذي يتوافق مع الحالة 1 هو [0، 1، 0، 0،0] والحالة 3 هي [0، 0، 0، 1، 0]. في هذه الحالة، سيتم استخدام طبقة مخفية من 10 عقد مع استخدام خاصية التنشيط السيني sigmoid activation. طبقة المخرجات عبارة عن مجموعة خطية مفعلة من عقدتين، تقابل قيمة Q في كل حالة لتمثيل الإجراءين الممكنين. يعني التنشيط الخطي أن المخرجات تعتمد فقط على التجميع الخطي للمدخلات والأوزان، مع عدم وجود وظيفة إضافية مطبقة على ذلك التجميع.

قبل ان نكمل أود أن أشرح ما معني ناقلات الحالة ذات التشفير الواحد one-hot encoded :

هي عملية يتم من خلالها تحويل المتغيرات الفئوية categorical value إلى شكل يمكن توفيره لخوارزميات Machine Learning للقيام بعمل أفضل في التنبؤ.

model = Sequential()
model.add(InputLayer(batch_input_shape=(1, 5)))
model.add(Dense(10, activation='sigmoid'))
model.add(Dense(2, activation='linear'))
model.compile(loss='mse', optimizer='adam', metrics=['mae'])

أولاً، تم إنشاء النموذج باستخدام Keras Sequential API. ثم إضافة طبقة المدخلات تأخذ مدخلات تتوافق مع متجهات الحالة ذات التشفير الواحد. ثم تتم إضافة الطبقة السينية المنشطة المخفية بعشرة نقاط، متبوعة بطبقة مخرجات خطية المنشط والتي ستنتج قيمة Q لكل إجراء. وأخيرًا، يتم تجميع النموذج باستخدام دالة الفقد لمتوسط الخطأ التربيعي (للتوافق مع وظيفة التكلفة التي تم تحديدها سابقًا) باستخدام مُحسِّن Adam المستخدم في حالة Keras الافتراضية.

لاستخدام هذا النموذج في هذا النطاق للتدريب، يتم تشغيل تم استعمال نموذج Q learning سياسة الاختيار الجشع ϵ:

# now execute the q learning
y = 0.95
eps = 0.5
decay_factor = 0.999
r_avg_list = []
for i in range(num_episodes):
    s = env.reset()
    eps *= decay_factor
    if i % 100 == 0:
        print("Episode {} of {}".format(i + 1, num_episodes))
    done = False
    r_sum = 0
    while not done:
        if np.random.random() < eps:
            a = np.random.randint(0, 2)
        else:
            a = np.argmax(model.predict(np.identity(5)[s:s + 1]))
        new_s, r, done, _ = env.step(a)
        target = r + y * np.max(model.predict(np.identity(5)[new_s:new_s + 1]))
        target_vec = model.predict(np.identity(5)[s:s + 1])[0]
        target_vec[a] = target
        model.fit(np.identity(5)[s:s + 1], target_vec.reshape(-1, 2), epochs=1, verbose=0)
        s = new_s
        r_sum += r
    r_avg_list.append(r_sum / 1000)

الاختلاف الرئيسي الأول في تطبيق Keras يكمن هنا:

if np.random.random() < eps:
    a = np.random.randint(0, 2)
else:
    a = np.argmax(model.predict(np.identity(5)[s:s + 1]))

الشرط الأول في جملة if هو تنفيذ سياسة اختيار الإجراء الجشع- ϵالتي تمت مناقشتها بالفعل. الشرط الثاني يستخدم نموذج Keras لإنتاج القيمتين Q – واحدة لكل حالة ممكنة. يقوم بذلك عن طريق استدعاء الدالة () model.predict. هنا يتم استخدام وظيفة هوية من مكتبة numpy، مع تقطيع المتجهات، لإنتاج المتجه ذات التشفير الواحد للحالة الحالية. يتم استخدام الدالة argmax من مكتبة numpy القياسية ايضاً لتحديد الإجراء ذي القيمة Q الأعلى المرتجعة من التنبؤ بنموذج Keras.

الاختلاف الرئيسي الثاني هنا:

target = r + y * np.max(model.predict(np.identity(5)[new_s:new_s + 1]))
target_vec = model.predict(np.identity(5)[s:s + 1])[0]
target_vec[a] = target
model.fit(np.identity(5)[s:s + 1], target_vec.reshape(-1, 2), epochs=1, verbose=0)

يحدد السطر الأول الهدف كقاعدة تحديث Q Learning التي تم تقديمها سابقًا. إنها مكافأة r زائد الحد الأقصى المخصوم لقيمة Q المتوقعة للحالة الجديدة new_s. هذه هي القيمة التي نرغب في أن يتعلمها نموذج Keras للتنبؤ بالحالات والأحداث مثل (Q(s,a. ومع ذلك، نموذج Keras على قادر علي حساب القيمة لكل إجراء من الإجراءين – فنحن لا نريد تغيير القيمة للإجراء الآخر، فقط الإجراء الذي تم اختياره. يتم إنشاء target_vec الذي يقوم باستخلاص قيمة Q المتوقعة للحالة s. في ثم، يتم تغيير قيمة Q المطابقة للإجراء المستهدف – يتم ترك قيمة Q في الإجراء الآخر بدون تغيير.

في الاخير يتم تحديث نموذج Keras في خطوة واحدة تدريب. المدخل الاول هو الحالة الراهنة – أي المدخلات الحالة الساخنة ذات التشفير الواحد للنموذج. والثاني هو المتجه المستهدف الذي يعاد تشكيله ليكون له الأبعاد المطلوبة (1، 2). يعبر الثالث اننا نرغب في تدريبها مرة واحدة فقط، وأخيرًا، فإن verbose يشير ببساطة إلى أن Keras لا تطبع خطوات التدريب.

عمل هذا التدريب على أكثر من 1000 حلقة لعبة يكشف عن متوسط ​​المكافأة التالية لكل خطوة في اللعبة:

كما يمكن ملاحظته، يزيد متوسط المكافأة في كل خطوة في اللعبة على كل حلقة من حلقات اللعبة، موضحًا أن نموذج Keras يتعلم جيدًا (إذا كان بطيئًا قليلاً).

يمكننا أيضًا الحصول على مخرجات لقيمة Q لكل حالة – وهذا يعني الحصول على نموذج Keras بشكل أساسي لإعادة إنتاج جدول Q الصريح الذي تم إنشاؤه في الطرق السابقة:

الحالة 0 – الإجراء [[62.734287 61.350456]]

الحالة 1 – الإجراء [[66.317955 62.27209]]

الحالة 2 – الإجراء [[70.82501 63.262383]]

الحالة 3 – الإجراء [[76.63797 64.75874]]

الحالة 4 – الإجراء [[84.51073 66.499725]]

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

 

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

تعليقات