You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义py_environment多episode验证时time_step与spec不匹配问题

自定义PyEnvironment验证时的ValueError问题及解决方法

更新: 我已解决该问题,解决方案见本文末尾。

我使用Python 3.10创建了一个自定义py_environment,当尝试用utils.validate_py_environment验证时,出现了ValueError。仅当episode>2时才会报错,执行utils.validate_py_environment(env, episode=1)时无异常。

环境规格

action_spec: BoundedArraySpec (shape=(3,), dtype=dtype('int32'), name='action', minimum=[0 0 0], maximum=[1 1 1])
time_step_spec.observation: BoundedArraySpec (shape=(4,), dtype=dtype('float32'), name='observation', minimum=[0. 0. 0. 0.], maximum=[1. 1. 1. 1.])
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')

报错信息

in validate_py_environment
if not array_spec.check_arrays_nest (time_step, batched_time_step_s raise ValueError(while episode_count < episodes:
Given time_step`: %r does not match expected
"`time_step_spec: %r' % (time_step, batched_time_step_spec)


ValueError: Given `time_step`: TimeStep(
{'discount': array(0., dtype=float32),
'observation': array([[0.9375 0.9694037, 0.7618361, 0.0593321]],
dtype float32),
'reward': array(-0.9964797, dtype=float32),
'step_type': array(2)}) does not match expected `time_step_spec`: TimeStep( {'discount': BoundedArraySpec(shape=(), dtype=dtype('float32'),
name' discount minimum=0.0, maximum=1.0),
'observation': BoundedArraySpec(shape=(4,), dtype=dtype('float32'), name='observation minimum=[0.0.0.0.], maximum=[1. 1. 1. 1.]),
'reward': ArraySpec (shape=(), dtype=dtype('float32'), name=' reward'), 'step_type': ArraySpec(shape=(), dtype=dtype('int32'), name='step_type')})

可复现错误的简化代码

# Import all relevant libs
import numpy as np
from tf_agents.environments import py_environment
from tf_agents.environments import utils
from tf_agents.specs import array_spec
from tf_agents.trajectories import time_step as ts

class SimpleTestEnv(py_environment.PyEnvironment):
   def __init__(self):
      self._action_spec=array_spec.BoundedArraySpec(
        shape=(3,), dtype=np.int32, minimum=[0,0,0], maximum=[1,1,1], name='action')
      self._observation_spec = array_spec.BoundedArraySpec(
       shape=(4,), dtype=np.float32, minimum=[0,0,0,0], maximum=[1,1,1,1],  name='observation')
      self._state = np.array([1, 1, 1, 0], dtype=np.float32) # initial state
      self._episode_ended = False
   def action_spec(self):
      return self._action_spec
   def observation_spec(self):
      return self._observation_spec

   def scale_value(self, normed_value, min_value, max_value):
      scaled_value = min_value + (normed_value* (max_value - min_value)) 
      return scaled_value
   def normalize_value(self, value, min_value, max_value):
      normalized_value = (value - min_value) / (max_value - min_value) 
      return normalized_value
  # resets everything to the initial state
   def _reset(self):
      self._state = [1, 1, 1, 0] # initial state, everything starts at 1 
      self._episode_ended = False
      return ts.restart(np.array(self._state, dtype=np.float32))
  # gets new actions
   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()
      # scale the current state to its real values
      current_state1 = self.scale_value(self._state[0], 0, 0.3)
      current_state2 = self.scale_value(self._state[1], 0, 0.3)
      current_state3 = self.scale_value(self._state[2], 20, 100)
      current_state4 = self._state[3]

      # calc the new state based on the actions
      new_state1 = 0.2 * current_state1 * action[0]
      new_state3 = current_state3 - action[1] * 20  # just some simple calcs 
      new_state4 = current_state4 + 0.1
      if action[2] == 1: #  check if the material was rotated or not 
        new_state2 = current_state1
      else:
        new_state2 = current_state2

      # norm the new state
      new_normed_state1 = self.normalize_value(new_state1, 0, 0.3) 
      new_normed_state2 = self.normalize_value(new_state2, 0.1, 0.3)
      new_normed_state3 = self.normalize_value(new_state3, 20, 100)

      # calc the reward
      reward = new_state3**2 - 1 # just an example
      # return the new state and reward & if not termination than discount, too
      self._state = np.array([new_normed_state1, new_normed_state2, new_normed_state3, new_state4], dtype=np.float32)
      # Make sure episodes don't go on forever.
      if new_normed_state1 <= 0.15:
         self._episode_ended = True
      if self._episode_ended:
         return ts.termination (np.array([self._state], dtype=np.float32), reward)
      else:
         return ts.transition(
           np.array([self._state], dtype=np.float32), reward, discount=1.0)

environment = SimpleTestEnv()
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)
utils.validate_py_environment (environment, episodes=3)

解决方案

我犯了两个错误:

  • 状态定义的形状问题:需要将self._observation_spec的shape设置为(1,4),而非(4,),确保和返回的观测数组维度匹配。
  • 类型匹配问题:tf_agents.trajectories.time_step需要传入numpy数组,但self._state需保持为列表类型,避免维度混乱。

修复后的简化环境代码

class CardGameEnv(py_environment.PyEnvironment):

  def __init__(self):
    self._action_spec = array_spec.BoundedArraySpec(
        shape=(3,), dtype=np.int32, minimum=[0,0,0], maximum=[1,1,1], name='action')
    self._observation_spec = array_spec.BoundedArraySpec(
        shape=(1,4), dtype=np.float32, minimum=0, name='observation')
    
    self._state = [0, 0, 0, 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, 0, 0, 0]
    self._episode_ended = False
    return ts.restart(np.array([self._state], dtype=np.float32))

  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()

    # check action
    print('Action: ', action)
    print('Action data type: ', type(action))

    # calc state
    print('State before: ', self._state)
    print('Action data type: ', type(self._state))
    self._state[0] = self._state[0] + action[0]  
    self._state[1] = self._state[1] + action[1]
    self._state[2] = self._state[2] + action[2]
    self._state[3] = self._state[3] + action[2]
    print('State after: ', self._state)    

    if self._state[3] >= 5.0:
      self._episode_ended = True
      reward = self._state[0] - 21
      return ts.termination(np.array([self._state], dtype=np.float32), reward)
    else:
      return ts.transition(
          np.array([self._state], dtype=np.float32), reward=0.0, discount=1.0)

内容的提问来源于stack exchange,提问作者chris_2091

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 17:35:57