如何持久化Python random状态以跨程序运行复现随机游戏?
如何正确持久化random生成器状态以复现特定游戏过程
我需要保存应用当前的random生成器状态,用来复现过程中执行的大量随机操作。
初始实现及问题
初始代码如下:
import random import pickle from Model import Resnet def main(): fileName = "09-08-2022T200803" with open("data/" + fileName + ".txt", "rb") as rb: data = pickle.load(rb) random.setstate(data[0][0]) # data[0][0]是已保存的随机状态对象,文件仅做追加操作,该对象始终相同 myNetwork = Resnet.Network() model = myNetwork.getModel() playGame(model, 4, fileName)
首次运行得到游戏结果:
Turn: 14, Action:251 Turn: 15, Action:33 Turn: 15, Action:186 Turn: 15, Action:16 GAME_FINISHED
但关闭程序重新运行后,结果不一致:
Turn: 13, Action:251 Turn: 14, Action:29 Turn: 14, Action:251 Turn: 15, Action:33 GAME_FINISHED
我发现同一程序内循环运行时能完全复现,但重启程序后不行,推测是Resnet.Network()初始化过程修改了随机状态。
第二种尝试及问题
另一种实现方式在同一程序运行时结果一致,但跨程序运行仍有差异:
def main(): myNetwork = Resnet.Network() model = myNetwork.getModel() fileName = "09-08-2022T200803" with open("data/"+fileName+".txt", "rb") as rb: while True: data = pickle.load(rb) playGame(model, 4, fileName, data[0][0]) # 每次读取文件得到的data[0][0]是同一个对象,因此单程序运行时有效 def playGame(model, simulations, fileName, seedObject = None): if seedObject != None: random.setstate(seedObject) # ... 游戏逻辑
问题
请问如何正确将random生成器状态持久化到文件,以复现特定出问题的游戏过程?(当前游戏逻辑完全基于随机数)
解决方案
核心问题是任何在random.setstate()之后、游戏逻辑执行之前的代码,只要调用了random模块的随机方法,都会改变生成器状态,导致复现失败。你的情况就是Resnet.Network()初始化过程中使用了随机数(比如权重初始化),打乱了已经恢复的状态。
方案1:分离随机实例(推荐)
为神经网络初始化和游戏逻辑分别使用独立的随机生成器,避免状态冲突:
- 修改
Resnet.Network()的初始化逻辑,使用专属的random.Random()实例:
# Resnet.py中的Network类修改 class Network: def __init__(self): # 创建独立随机实例用于网络初始化 self.rng = random.Random() # 后续权重初始化等操作都用self.rng,比如 self.rng.randn(...) 代替 random.randn(...) def getModel(self): # ... 原有逻辑
- 主程序中先恢复游戏用的随机状态,再初始化网络:
import random import pickle from Model import Resnet def main(): fileName = "09-08-2022T200803" with open("data/" + fileName + ".txt", "rb") as rb: data = pickle.load(rb) # 先恢复游戏逻辑的随机状态 random.setstate(data[0][0]) # 网络初始化用独立随机实例,不会干扰全局random状态 myNetwork = Resnet.Network() model = myNetwork.getModel() playGame(model, 4, fileName)
方案2:固定前置操作顺序(无法修改Resnet代码时使用)
如果不能修改Resnet的代码,需保证首次运行和复现时的前置操作完全一致,再恢复游戏前的随机状态:
- 首次运行时,先初始化网络,再保存游戏开始前的随机状态:
def main(): # 先完成所有会消耗随机数的初始化操作 myNetwork = Resnet.Network() model = myNetwork.getModel() # 保存此时的全局随机状态(游戏开始前的基准状态) game_start_state = random.getstate() fileName = "09-08-2022T200803" # 将game_start_state写入文件保存 with open("data/" + fileName + ".txt", "wb") as wb: pickle.dump([[game_start_state]], wb) # 运行游戏 playGame(model, 4, fileName)
- 复现时,重复相同的初始化步骤,再恢复保存的状态:
def main(): # 完全复刻首次运行的初始化流程 myNetwork = Resnet.Network() model = myNetwork.getModel() fileName = "09-08-2022T200803" with open("data/" + fileName + ".txt", "rb") as rb: data = pickle.load(rb) # 恢复到游戏开始前的随机状态 random.setstate(data[0][0]) playGame(model, 4, fileName)
关键注意事项
- 所有会调用随机数的前置操作(如模块导入、对象初始化),在首次运行和复现时的顺序必须完全一致;
- 保存的随机状态必须是游戏逻辑开始前的最终状态,而非程序启动时的初始状态;
pickle序列化random.getstate()的返回值是官方支持的可靠方式,无需担心兼容性问题。
内容的提问来源于stack exchange,提问作者Lidbey
相关产品推荐
相关产品推荐

