源代码地址:https://github.com/AndreaVidali/Deep-QLearning-Agent-for-Traffic-Signal-Control
如下对训练的主函数进行代码分析:
if __name__ == "__main__":
config = import_train_configuration(config_file='training_settings.ini')
sumo_cmd = set_sumo(config['gui'], config['sumocfg_file_name'], config['max_steps'])
path = set_train_path(config['models_path_name'])
第一行代码导入定义的配置文件training_settings.ini,配置文件里定义了 训练所需要的参数,如下是定义的import_train_configuration函数。
第二行代码根据配置设置 SUMO 命令行参数,如下是定义的set_sumo函数。
第三行代码用于保存训练后的模型,如下是定义的set_train_path函数。
接下来是初始化训练模型,使用配置文件中指定的参数。
Model = TrainModel(
config['num_layers'],
config['width_layers'],
config['batch_size'],
config['learning_rate'],
input_dim=config['num_states'],
output_dim=config['num_actions']
)
如下是定义的TrainModel类的部分代码
初始化存储经验的内存。
Memory = Memory(
config['memory_size_max'],
config['memory_size_min']
)
如下是定义的Memory类的部分代码
初始化交通生成器。
TrafficGen = TrafficGenerator(
config['max_steps'],
config['n_cars_generated']
)
如下是定义的TrafficGenerator类的部分代码
初始化可视化对象,用于保存和绘制训练过程中的数据。
Visualization = Visualization(
path,
dpi=96
)
如下是定义的Visualization类的部分代码
初始化模拟对象,传入模型、内存、交通生成器、SUMO 命令行参数和其他配置参数。
Simulation = Simulation(
Model,
Memory,
TrafficGen,
sumo_cmd,
config['gamma'],
config['max_steps'],
config['green_duration'],
config['yellow_duration'],
config['num_states'],
config['num_actions'],
config['training_epochs']
)
如下是定义的Simulation类的部分代码
初始化参数以后开始训练。 第一二行代码进行初始化训练周期计数器和开始时间戳。然后开始训练直到完成定义的训练轮次,打印当前周期信息,根据 epsilon-greedy 策略设置当前周期的探索率,运行模拟并获取模拟时间和训练时间(Simulation.run函数*),打印模拟和训练所用时间,增加周期计数。打印训练开始和结束时间以及保存会话信息的路径,保存训练好的模型。
episode = 0
timestamp_start = datetime.datetime.now()
while episode < config['total_episodes']:
print('\n----- Episode', str(episode+1), 'of', str(config['total_episodes']))
epsilon = 1.0 - (episode / config['total_episodes']) # set the epsilon for this episode according to epsilon-greedy policy
simulation_time, training_time = Simulation.run(episode, epsilon) # run the simulation
print('Simulation time:', simulation_time, 's - Training time:', training_time, 's - Total:', round(simulation_time+training_time, 1), 's')
episode += 1
print("\n----- Start time:", timestamp_start)
print("----- End time:", datetime.datetime.now())
print("----- Session info saved at:", path)
Model.save_model(path)
Simulation.run函数为训练函数,接下来进行具体分析。
定义了 run
方法,它接收两个参数:episode
(当前训练周期的编号)和 epsilon
(探索率,用于 epsilon-greedy 策略),记录模拟开始的时间。调用交通生成器的 generate_routefile
方法,使用当前周期编号作为种子生成交通路线文件。使用 SUMO 命令行参数启动 SUMO 模拟器。打印信息,表示模拟开始。
将当前步数设置为 0。初始化一个字典,用于存储每辆车的等待时间。初始化累积负奖励,用于记录整个周期内的总负奖励。初始化累积排队长度,用于记录整个周期内的总排队长度。初始化累积等待时间,用于记录整个周期内的总等待时间。初始化上一次的总等待时间。初始化上一个状态。初始化上一个动作。
开始一个循环,直到达到最大步数,调用 _get_state
方法获取当前交叉口的状态。调用 _collect_waiting_times
方法收集当前所有车辆的等待时间,并计算总等待时间。计算从上一个状态到当前状态的奖励,即总等待时间的变化。如果当前步数不为 0,将上一个状态的动作、奖励和当前状态存入内存。调用 _choose_action
方法根据当前状态和探索率选择一个动作。如果当前步数不为 0 且上一个动作与当前动作不同,则设置黄灯阶段,并模拟黄灯持续时间。根据选择的动作设置绿灯阶段。模拟绿灯阶段的持续时间。更新上一个状态。更新上一个动作。更新上一次的总等待时间。如果奖励为负,将其累加到累积负奖励中。
调用 _save_episode_stats
方法保存周期统计数据。打印总奖励和探索率。关闭 SUMO 模拟器。计算模拟所用时间,并四舍五入到最近的整数。打印训练开始的信息。记录训练开始的时间。进行指定次数的训练周期,每次周期调用 _replay
方法。计算训练所用时间,并四舍五入到最近的整数。返回模拟和训练所用的时间。
copyfile(src='training_settings.ini', dst=os.path.join(path, 'training_settings.ini'))
Visualization.save_data_and_plot(data=Simulation.reward_store, filename='reward', xlabel='Episode', ylabel='Cumulative negative reward')
Visualization.save_data_and_plot(data=Simulation.cumulative_wait_store, filename='delay', xlabel='Episode', ylabel='Cumulative delay (s)')
Visualization.save_data_and_plot(data=Simulation.avg_queue_length_store, filename='queue', xlabel='Episode', ylabel='Average queue length (vehicles)')
然后就是可视化,第一行代码为复制配置文件到模型保存路径。第二三四行代码分别为使用可视化对象保存数据并绘制三个关键指标的图表:累积负奖励、累积延迟和平均排队长度。
最后是运行结果