Python中如何使用多维键对(如numpy数组)存储奖励值?
嘿,我来帮你梳理这个问题——你想知道能不能用多维numpy数组(比如你代码里的状态数组s)作为键来存储奖励值对吧?直接给结论:不能直接用多维numpy数组作为numpy数组的索引来实现这个需求,但我们有两种实用的办法来搞定它,下面详细给你解释原因和修改方案:
为什么你当前的代码会出问题
你现在用的r_table是一个二维numpy数组np.zeros((10000, 10000)),但s是一个多维数组(比如形状是(1,25))。当你尝试写r_table[s, :]的时候,numpy根本没法把这个多维数组当成合法的索引去访问二维数组的行,大概率会直接抛出索引维度不匹配的错误。另外,numpy数组本身是不可哈希的,也不能直接当字典的键——而字典才是处理多维键存储的常用工具。
方案1:把多维状态转成元组,用字典存奖励表
这是最灵活的方式,不管你的状态空间多大都能用。我们可以把多维状态数组展平后转成元组(元组是可哈希的,符合字典键的要求),然后用字典来存储每个状态对应的动作奖励数据。
给你改好的代码示例,保留了你原来的核心逻辑,只调整了奖励表的存储方式:
import numpy as np from scipy.optimize import brute, minimize num_episodes = 500 # 改用字典存储奖励表:键是状态的元组,值是对应动作的奖励数组(或嵌套字典) r_table = {} # 假设你的动作是3维的,这里先定义动作空间相关的参数 action_dim = 3 for g in range(num_episodes): s = np.array(state.sample(), dtype=np.int) # 把多维状态转成元组,作为字典的键 s_key = tuple(s.flatten()) done = False count = 0 while not done: # 检查当前状态是否在奖励表中,或者奖励总和为0时,走随机/优化选动作的逻辑 if s_key not in r_table or np.sum(r_table[s_key]) == 0: EUR_elec_sell = 0.050 EUR_elec_buy = 0.100 EUR_gas = 0.030 rranges = ((0, 1250), (0, 2000), (0, 3000)) res0 = brute(reward, rranges, finish=None) res1 = minimize(reward, res0, bounds=[(0, 1250), (0, 2000), (0, 3000)]) a = res1.x a = list(map(int, a.round(decimals=-1))) else: # 这里注意:如果你的动作是多维数组,argmax就不适用了,得改成按动作元组找最大奖励 # 先按你原代码的逻辑,假设这里a是索引(如果是多维动作,要改成嵌套字典) a = np.argmax(r_table[s_key]) # 状态转换逻辑保留 s_t1 = model.predict([np.append(s, a)]).astype(int) new_s = np.append(s_t1, np.delete(s, 1)) r = reward(a) count += 1 if count == 1000: done = True # 更新奖励表:如果状态不在字典里,先初始化奖励数组 if s_key not in r_table: r_table[s_key] = np.zeros(action_dim) # 如果a是多维动作,要转成元组用嵌套字典存储,比如: # a_key = tuple(a) # r_table[s_key][a_key] = r_table[s_key].get(a_key, 0) + r # 这里先按原代码逻辑更新 r_table[s_key][a] += r # 更新当前状态的键 s = new_s s_key = tuple(s.flatten())
方案2:把多维状态编码成唯一整数索引
如果你的状态空间是有限的,而且每个维度的取值范围都已知,可以把多维状态编码成一个唯一的整数,这样就能继续用numpy数组存储奖励表了。比如用进制转换的思路:
# 示例:假设状态是(1,25)的数组,每个元素取值范围是0-99 def state_to_index(s): s_flat = s.flatten() # 按100进制计算每个维度的权重,把多维数组转成整数 weights = [100**i for i in reversed(range(len(s_flat)))] return np.dot(s_flat, weights) # 初始化奖励表时,要根据最大可能的索引确定大小(注意:如果状态维度多,这个数组会非常大) max_possible_index = 100**25 r_table = np.zeros((max_possible_index, action_dim))
不过这个方法有个明显的缺点:如果状态维度多或者每个维度的取值范围大,numpy数组会占用巨量内存,所以只适合小规模的状态空间。
额外提醒
你原代码里的a = np.argmax(r_table[s, :])有个小问题:如果你的动作是多维数组(比如代码里a是长度为3的数组),那argmax得到的是整数索引,和你的动作格式不匹配。这种情况下,最好把奖励表改成嵌套字典——外层字典的键是状态元组,内层字典的键是动作元组,值是对应的奖励值。
内容的提问来源于stack exchange,提问作者Glenn C.

