حقوق الطبع والنشر 2021 The TF-Agents Authors.
عرض على TensorFlow.org | تشغيل في Google Colab | عرض المصدر على جيثب | تحميل دفتر |
مقدمة
الهدف من التعلم المعزز (RL) هو تصميم وكلاء يتعلمون من خلال التفاعل مع البيئة. في إعداد RL القياسي ، يتلقى الوكيل ملاحظة في كل خطوة زمنية ويختار إجراءً. يتم تطبيق الإجراء على البيئة وتعيد البيئة مكافأة وملاحظة جديدة. يقوم الوكيل بتدريب سياسة لاختيار الإجراءات لتعظيم مجموع المكافآت ، والمعروف أيضًا باسم العائد.
في TF-Agents ، يمكن تنفيذ البيئات إما في Python أو TensorFlow. عادةً ما تكون بيئات Python أسهل في التنفيذ والفهم والتصحيح ، لكن بيئات TensorFlow أكثر كفاءة وتسمح بالتوازي الطبيعي. يتمثل سير العمل الأكثر شيوعًا في تنفيذ بيئة في Python واستخدام أحد أغلفةنا لتحويلها تلقائيًا إلى TensorFlow.
دعونا نلقي نظرة على بيئات بايثون أولاً. تتبع بيئات TensorFlow واجهة برمجة تطبيقات مشابهة جدًا.
اقامة
إذا لم تقم بتثبيت وكلاء tf أو صالة الألعاب الرياضية حتى الآن ، فقم بتشغيل:
pip install "gym>=0.21.0"
pip install tf-agents
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import abc
import tensorflow as tf
import numpy as np
from tf_agents.environments import py_environment
from tf_agents.environments import tf_environment
from tf_agents.environments import tf_py_environment
from tf_agents.environments import utils
from tf_agents.specs import array_spec
from tf_agents.environments import wrappers
from tf_agents.environments import suite_gym
from tf_agents.trajectories import time_step as ts
بيئات بايثون
بيئات الثعبان لها step(action) -> next_time_step
الأسلوب الذي ينطبق هذا الإجراء على البيئة، وإرجاع المعلومات التالية حول الخطوة التالية:
-
observation
: هذا هو جزء من الدولة بيئة أن وكيل يمكن مراقبة في اختيار أعمالها في الخطوة التالية. -
reward
: عامل هو التعلم لتحقيق أقصى قدر مجموع هذه المكافآت عبر خطوات متعددة. -
step_type
: التفاعلات مع البيئة وعادة ما تكون جزءا من سلسلة / حلقة. على سبيل المثال حركات متعددة في لعبة الشطرنج. step_type يمكن أن تكون إماFIRST
،MID
أوLAST
لبيان ما إذا كانت هذه الخطوة مرة هي الخطوة الأولى والمتوسطة أو الأخيرة في تسلسل. -
discount
: هذا هو تعويم يمثل كم لوزن جزاء في الوقت المقبلة خطوة بالنسبة للمكافأة في الخطوة الزمنية الحالية.
يتم تجميع هذه في الصفوف (tuple) اسمه TimeStep(step_type, reward, discount, observation)
.
واجهة أن جميع البيئات بيثون يجب أن تنفذ هي في environments/py_environment.PyEnvironment
. الطرق الرئيسية هي:
class PyEnvironment(object):
def reset(self):
"""Return initial_time_step."""
self._current_time_step = self._reset()
return self._current_time_step
def step(self, action):
"""Apply action and return new time_step."""
if self._current_time_step is None:
return self.reset()
self._current_time_step = self._step(action)
return self._current_time_step
def current_time_step(self):
return self._current_time_step
def time_step_spec(self):
"""Return time_step_spec."""
@abc.abstractmethod
def observation_spec(self):
"""Return observation_spec."""
@abc.abstractmethod
def action_spec(self):
"""Return action_spec."""
@abc.abstractmethod
def _reset(self):
"""Return initial_time_step."""
@abc.abstractmethod
def _step(self, action):
"""Apply action and return new time_step."""
بالإضافة إلى step()
الأسلوب، كما توفر بيئات ل reset()
الأسلوب الذي يبدأ سلسلة جديدة ويوفر الأولي TimeStep
. وليس من الضروري استدعاء reset
طريقة صراحة. نفترض أن البيئات تتم إعادة تعيينها تلقائيًا ، إما عند وصولها إلى نهاية الحلقة أو عندما يتم استدعاء الخطوة () في المرة الأولى.
علما بأن الفئات الفرعية لا تنفذ step()
أو reset()
مباشرة. أنها بدلا من ذلك تجاوز _step()
و _reset()
الأساليب. سوف يكون مؤقتا الخطوات الوقت الذي عاد من هذه الأساليب ويتعرض من خلال current_time_step()
.
و observation_spec
و action_spec
طرق العودة عش (Bounded)ArraySpecs
التي تصف الاسم والشكل ونوع البيانات ونطاقات الملاحظات والإجراءات على التوالي.
في TF-Agents نشير مرارًا وتكرارًا إلى الأعشاش التي يتم تعريفها على أنها أي بنية تشبه الشجرة تتكون من قوائم أو مجموعات أو مجموعات مسماة أو قواميس. يمكن أن تتكون هذه بشكل تعسفي للحفاظ على هيكل الملاحظات والإجراءات. لقد وجدنا أن هذا مفيد جدًا للبيئات الأكثر تعقيدًا حيث لديك العديد من الملاحظات والإجراءات.
استخدام البيئات القياسية
وقد بنيت في TF وكلاء مغلفة للعديد من البيئات القياسية مثل OpenAI رياضة، DeepMind السيطرة وأتاري، بحيث أنها تتبع لدينا py_environment.PyEnvironment
اجهة. يمكن تحميل هذه البيئات المغلفة بسهولة باستخدام أجنحة البيئة الخاصة بنا. لنقم بتحميل بيئة CartPole من صالة الألعاب الرياضية OpenAI وننظر إلى الإجراء و time_step_spec.
environment = suite_gym.load('CartPole-v0')
print('action_spec:', environment.action_spec())
print('time_step_spec.observation:', environment.time_step_spec().observation)
print('time_step_spec.step_type:', environment.time_step_spec().step_type)
print('time_step_spec.discount:', environment.time_step_spec().discount)
print('time_step_spec.reward:', environment.time_step_spec().reward)
action_spec: BoundedArraySpec(shape=(), dtype=dtype('int64'), name='action', minimum=0, maximum=1) time_step_spec.observation: BoundedArraySpec(shape=(4,), dtype=dtype('float32'), name='observation', minimum=[-4.8000002e+00 -3.4028235e+38 -4.1887903e-01 -3.4028235e+38], maximum=[4.8000002e+00 3.4028235e+38 4.1887903e-01 3.4028235e+38]) time_step_spec.step_type: ArraySpec(shape=(), dtype=dtype('int32'), name='step_type') time_step_spec.discount: BoundedArraySpec(shape=(), dtype=dtype('float32'), name='discount', minimum=0.0, maximum=1.0) time_step_spec.reward: ArraySpec(shape=(), dtype=dtype('float32'), name='reward')
لذلك نرى أن البيئة تتوقع إجراءات من نوع int64
في [0، 1] والعوائد TimeSteps
حيث الملاحظات هي float32
ناقل بطول 4 وعامل الخصم هو float32
في [0.0، 1.0]. الآن، دعونا في محاولة لاتخاذ إجراءات ثابتة (1,)
للحلقة كاملة.
action = np.array(1, dtype=np.int32)
time_step = environment.reset()
print(time_step)
while not time_step.is_last():
time_step = environment.step(action)
print(time_step)
TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.0138565 , -0.03582913, 0.04861612, -0.03755046], dtype=float32), 'reward': array(0., dtype=float32), 'step_type': array(0, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.01313992, 0.15856317, 0.0478651 , -0.3145069 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.01631118, 0.35297176, 0.04157497, -0.5917188 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.02337062, 0.54748774, 0.02974059, -0.87102115], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.03432037, 0.74219286, 0.01232017, -1.1542072 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.04916423, 0.93715197, -0.01076398, -1.4430016 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.06790727, 1.1324048 , -0.03962401, -1.7390285 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.09055536, 1.327955 , -0.07440457, -2.04377 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.11711447, 1.523758 , -0.11527998, -2.3585167 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([ 0.14758962, 1.7197047 , -0.16245031, -2.6843033 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(0., dtype=float32), 'observation': array([ 0.18198372, 1.9156038 , -0.21613638, -3.0218334 ], dtype=float32), 'reward': array(1., dtype=float32), 'step_type': array(2, dtype=int32)})
إنشاء بيئة بايثون الخاصة بك
بالنسبة للعديد من العملاء ، فإن حالة الاستخدام الشائعة هي تطبيق أحد الوكلاء المعياريين (انظر الوكلاء /) في TF-Agents على مشكلتهم. للقيام بذلك ، يتعين عليهم تأطير مشكلتهم كبيئة. لذلك دعونا نلقي نظرة على كيفية تنفيذ بيئة في بايثون.
لنفترض أننا نريد تدريب وكيل على لعب لعبة الورق التالية (مستوحاة من Black Jack):
- تُلعب اللعبة باستخدام مجموعة لا نهائية من البطاقات مرقمة 1 ... 10.
- عند كل منعطف يمكن للوكيل القيام بأمرين: الحصول على بطاقة عشوائية جديدة ، أو إيقاف الجولة الحالية.
- الهدف هو الحصول على مجموع أوراقك أقرب ما يمكن إلى 21 في نهاية الجولة ، دون تجاوز.
يمكن أن تبدو البيئة التي تمثل اللعبة كما يلي:
- الإجراءات: لدينا عملين. الإجراء 0: الحصول على بطاقة جديدة ، والإجراء 1: إنهاء الجولة الحالية.
- ملاحظات: مجموع الأوراق في الجولة الحالية.
- المكافأة: الهدف هو الاقتراب من 21 قدر الإمكان دون تجاوز ، لذلك يمكننا تحقيق ذلك باستخدام المكافأة التالية في نهاية الجولة: sum_of_cards - 21 إذا كان sum_of_cards <= 21 ، وإلا -21
class CardGameEnv(py_environment.PyEnvironment):
def __init__(self):
self._action_spec = array_spec.BoundedArraySpec(
shape=(), dtype=np.int32, minimum=0, maximum=1, name='action')
self._observation_spec = array_spec.BoundedArraySpec(
shape=(1,), dtype=np.int32, minimum=0, name='observation')
self._state = 0
self._episode_ended = False
def action_spec(self):
return self._action_spec
def observation_spec(self):
return self._observation_spec
def _reset(self):
self._state = 0
self._episode_ended = False
return ts.restart(np.array([self._state], dtype=np.int32))
def _step(self, action):
if self._episode_ended:
# The last action ended the episode. Ignore the current action and start
# a new episode.
return self.reset()
# Make sure episodes don't go on forever.
if action == 1:
self._episode_ended = True
elif action == 0:
new_card = np.random.randint(1, 11)
self._state += new_card
else:
raise ValueError('`action` should be 0 or 1.')
if self._episode_ended or self._state >= 21:
reward = self._state - 21 if self._state <= 21 else -21
return ts.termination(np.array([self._state], dtype=np.int32), reward)
else:
return ts.transition(
np.array([self._state], dtype=np.int32), reward=0.0, discount=1.0)
دعونا نتأكد من أننا فعلنا كل شيء بشكل صحيح لتحديد البيئة المذكورة أعلاه. عند إنشاء بيئتك الخاصة ، يجب عليك التأكد من أن الملاحظات والخطوات الزمنية التي تم إنشاؤها تتبع الأشكال والأنواع الصحيحة على النحو المحدد في المواصفات الخاصة بك. تُستخدم هذه لإنشاء رسم بياني TensorFlow وبالتالي يمكن أن تخلق مشاكل يصعب تصحيحها إذا فهمناها بشكل خاطئ.
للتحقق من صحة بيئتنا ، سنستخدم سياسة عشوائية لإنشاء إجراءات وسنكرر أكثر من 5 حلقات للتأكد من أن الأشياء تعمل على النحو المنشود. يظهر خطأ إذا تلقينا time_step لا يتبع مواصفات البيئة.
environment = CardGameEnv()
utils.validate_py_environment(environment, episodes=5)
الآن بعد أن علمنا أن البيئة تعمل على النحو المنشود ، فلنقم بتشغيل هذه البيئة باستخدام سياسة ثابتة: اطلب 3 بطاقات ثم ننهي الجولة.
get_new_card_action = np.array(0, dtype=np.int32)
end_round_action = np.array(1, dtype=np.int32)
environment = CardGameEnv()
time_step = environment.reset()
print(time_step)
cumulative_reward = time_step.reward
for _ in range(3):
time_step = environment.step(get_new_card_action)
print(time_step)
cumulative_reward += time_step.reward
time_step = environment.step(end_round_action)
print(time_step)
cumulative_reward += time_step.reward
print('Final Reward = ', cumulative_reward)
TimeStep( {'discount': array(1., dtype=float32), 'observation': array([0], dtype=int32), 'reward': array(0., dtype=float32), 'step_type': array(0, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([9], dtype=int32), 'reward': array(0., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(1., dtype=float32), 'observation': array([12], dtype=int32), 'reward': array(0., dtype=float32), 'step_type': array(1, dtype=int32)}) TimeStep( {'discount': array(0., dtype=float32), 'observation': array([21], dtype=int32), 'reward': array(0., dtype=float32), 'step_type': array(2, dtype=int32)}) TimeStep( {'discount': array(0., dtype=float32), 'observation': array([21], dtype=int32), 'reward': array(0., dtype=float32), 'step_type': array(2, dtype=int32)}) Final Reward = 0.0
أغلفة البيئة
يأخذ برنامج تضمين البيئة بيئة Python ويعيد نسخة معدلة من البيئة. كل من البيئة الأصلية والبيئة المعدلة هي حالات py_environment.PyEnvironment
، ومغلفة متعددة يمكن بالسلاسل معا.
بعض مغلفة شيوعا يمكن العثور عليها في environments/wrappers.py
. على سبيل المثال:
-
ActionDiscretizeWrapper
: تحويل مساحة العمل المستمر لمساحة عمل منفصلة. -
RunStats
: يلتقط تشغيل إحصاءات البيئة مثل عدد الخطوات التي اتخذت، أكمل عدد من الحلقات الخ -
TimeLimit
: إنهاء الحلقة بعد عدد محدد من الخطوات.
مثال 1: غلاف تمييز الإجراء
InvertedPendulum هي بيئة PyBullet أن يقبل الإجراءات مستمرة في نطاق [-2, 2]
. إذا أردنا تدريب عامل عمل منفصل مثل DQN على هذه البيئة ، فعلينا تحديد (تحديد) مساحة العمل. وهذا هو بالضبط ما ActionDiscretizeWrapper
يفعل. مقارنة action_spec
قبل وبعد التفاف:
env = suite_gym.load('Pendulum-v1')
print('Action Spec:', env.action_spec())
discrete_action_env = wrappers.ActionDiscretizeWrapper(env, num_actions=5)
print('Discretized Action Spec:', discrete_action_env.action_spec())
Action Spec: BoundedArraySpec(shape=(1,), dtype=dtype('float32'), name='action', minimum=-2.0, maximum=2.0) Discretized Action Spec: BoundedArraySpec(shape=(), dtype=dtype('int32'), name='action', minimum=0, maximum=4)
واختتم discrete_action_env
هو مثيل py_environment.PyEnvironment
ويمكن علاج مثل بيئة بيثون العادية.
بيئات TensorFlow
يتم تعريف واجهة لبيئات TF في environments/tf_environment.TFEnvironment
وتبدو مشابهة جدا لبيئات بيثون. تختلف بيئات TF عن بيئة Python بطريقتين:
- يقومون بإنشاء كائنات موتر بدلاً من المصفوفات
- تضيف بيئات TF بعدًا دفعيًا إلى الموترات التي تم إنشاؤها عند مقارنتها بالمواصفات.
يسمح تحويل بيئات Python إلى TFEnvs لتدفق التوتر بموازنة العمليات. على سبيل المثال، يمكن للمرء تحديد collect_experience_op
التي تقوم بجمع البيانات من بيئة ويضيف إلى replay_buffer
، و train_op
الذي يقرأ من replay_buffer
والقطارات وكيل، وتشغيلها في نفس الوقت بشكل طبيعي في TensorFlow.
class TFEnvironment(object):
def time_step_spec(self):
"""Describes the `TimeStep` tensors returned by `step()`."""
def observation_spec(self):
"""Defines the `TensorSpec` of observations provided by the environment."""
def action_spec(self):
"""Describes the TensorSpecs of the action expected by `step(action)`."""
def reset(self):
"""Returns the current `TimeStep` after resetting the Environment."""
return self._reset()
def current_time_step(self):
"""Returns the current `TimeStep`."""
return self._current_time_step()
def step(self, action):
"""Applies the action and returns the new `TimeStep`."""
return self._step(action)
@abc.abstractmethod
def _reset(self):
"""Returns the current `TimeStep` after resetting the Environment."""
@abc.abstractmethod
def _current_time_step(self):
"""Returns the current `TimeStep`."""
@abc.abstractmethod
def _step(self, action):
"""Applies the action and returns the new `TimeStep`."""
و current_time_step()
الأسلوب بإرجاع time_step الحالي وتهيئة البيئة إذا لزم الأمر.
ل reset()
قوات طريقة إعادة تعيين في البيئة وعوائد CURRENT_STEP.
إذا كان action
لا يتوقف على السابق time_step
على tf.control_dependency
هو مطلوب في Graph
واسطة.
الآن، دعونا نلقي نظرة على كيف TFEnvironments
يتم إنشاؤها.
إنشاء بيئة TensorFlow الخاصة بك
هذا أكثر تعقيدًا من إنشاء البيئات في بايثون ، لذلك لن نغطيه في هذا الكولاب. مثال على ذلك هو متاح هنا . حالة الاستخدام الأكثر شيوعا هو تنفيذ بيئتك في بيثون والتفاف عليه في TensorFlow استخدام لدينا TFPyEnvironment
المجمع (انظر أدناه).
تغليف بيئة Python في TensorFlow
يمكننا بسهولة التفاف أي بيئة بيثون في بيئة TensorFlow باستخدام TFPyEnvironment
المجمع.
env = suite_gym.load('CartPole-v0')
tf_env = tf_py_environment.TFPyEnvironment(env)
print(isinstance(tf_env, tf_environment.TFEnvironment))
print("TimeStep Specs:", tf_env.time_step_spec())
print("Action Specs:", tf_env.action_spec())
True TimeStep Specs: TimeStep( {'discount': BoundedTensorSpec(shape=(), dtype=tf.float32, name='discount', minimum=array(0., dtype=float32), maximum=array(1., dtype=float32)), 'observation': BoundedTensorSpec(shape=(4,), dtype=tf.float32, name='observation', minimum=array([-4.8000002e+00, -3.4028235e+38, -4.1887903e-01, -3.4028235e+38], dtype=float32), maximum=array([4.8000002e+00, 3.4028235e+38, 4.1887903e-01, 3.4028235e+38], dtype=float32)), 'reward': TensorSpec(shape=(), dtype=tf.float32, name='reward'), 'step_type': TensorSpec(shape=(), dtype=tf.int32, name='step_type')}) Action Specs: BoundedTensorSpec(shape=(), dtype=tf.int64, name='action', minimum=array(0), maximum=array(1))
لاحظ المواصفات هي الآن من نوع: (Bounded)TensorSpec
.
أمثلة على الاستخدام
مثال بسيط
env = suite_gym.load('CartPole-v0')
tf_env = tf_py_environment.TFPyEnvironment(env)
# reset() creates the initial time_step after resetting the environment.
time_step = tf_env.reset()
num_steps = 3
transitions = []
reward = 0
for i in range(num_steps):
action = tf.constant([i % 2])
# applies the action and returns the new TimeStep.
next_time_step = tf_env.step(action)
transitions.append([time_step, action, next_time_step])
reward += next_time_step.reward
time_step = next_time_step
np_transitions = tf.nest.map_structure(lambda x: x.numpy(), transitions)
print('\n'.join(map(str, np_transitions)))
print('Total reward:', reward.numpy())
[TimeStep( {'discount': array([1.], dtype=float32), 'observation': array([[-0.0078796 , -0.04736348, -0.04966116, 0.04563603]], dtype=float32), 'reward': array([0.], dtype=float32), 'step_type': array([0], dtype=int32)}), array([0], dtype=int32), TimeStep( {'discount': array([1.], dtype=float32), 'observation': array([[-0.00882687, -0.24173944, -0.04874843, 0.32224613]], dtype=float32), 'reward': array([1.], dtype=float32), 'step_type': array([1], dtype=int32)})] [TimeStep( {'discount': array([1.], dtype=float32), 'observation': array([[-0.00882687, -0.24173944, -0.04874843, 0.32224613]], dtype=float32), 'reward': array([1.], dtype=float32), 'step_type': array([1], dtype=int32)}), array([1], dtype=int32), TimeStep( {'discount': array([1.], dtype=float32), 'observation': array([[-0.01366166, -0.04595843, -0.04230351, 0.01459712]], dtype=float32), 'reward': array([1.], dtype=float32), 'step_type': array([1], dtype=int32)})] [TimeStep( {'discount': array([1.], dtype=float32), 'observation': array([[-0.01366166, -0.04595843, -0.04230351, 0.01459712]], dtype=float32), 'reward': array([1.], dtype=float32), 'step_type': array([1], dtype=int32)}), array([0], dtype=int32), TimeStep( {'discount': array([1.], dtype=float32), 'observation': array([[-0.01458083, -0.24044897, -0.04201157, 0.2936384 ]], dtype=float32), 'reward': array([1.], dtype=float32), 'step_type': array([1], dtype=int32)})] Total reward: [3.]
حلقات كاملة
env = suite_gym.load('CartPole-v0')
tf_env = tf_py_environment.TFPyEnvironment(env)
time_step = tf_env.reset()
rewards = []
steps = []
num_episodes = 5
for _ in range(num_episodes):
episode_reward = 0
episode_steps = 0
while not time_step.is_last():
action = tf.random.uniform([1], 0, 2, dtype=tf.int32)
time_step = tf_env.step(action)
episode_steps += 1
episode_reward += time_step.reward.numpy()
rewards.append(episode_reward)
steps.append(episode_steps)
time_step = tf_env.reset()
num_steps = np.sum(steps)
avg_length = np.mean(steps)
avg_reward = np.mean(rewards)
print('num_episodes:', num_episodes, 'num_steps:', num_steps)
print('avg_length', avg_length, 'avg_reward:', avg_reward)
num_episodes: 5 num_steps: 131 avg_length 26.2 avg_reward: 26.2