快速上手微软 “群策 MARO” 平台,打造简易的共享单车场景

微软技术栈
企业官方账号
2021-09-17 16:03:13

编者按:2020年9月,微软亚洲研究院发布了多智能体资源优化平台“群策 MARO”,并在 Github 上开源。近日,MARO 更新了0.2版本,新版本进一步完善了多项功能,提升了使用体验。作为一个面向多行业横截面上的全链条资源优化 AI 解决方案,MARO 平台可支持多种预设定的资源优化任务,例如航运网络中的空集装箱调度问题、共享单车服务中的单车调度问题、云平台上的虚拟机分配问题等,同时也支持用户利用核心组件,快速地定义高效的场景。不仅如此,MARO 还提供了全栈的强化学习支持,包含常用算法以及相关的分布式训练。本文将通过构造一个简单的共享单车场景,来帮助大家理解 MARO 的核心功能和逻辑,以及其与环境交互所实现的优化策略。

*本文仅介绍场景设计逻辑和与环境进行交互的关键代码,完整的 Notebook 代码可以参考 Notebook (https://github.com/microsoft/maro/blob/master/notebooks/articles/simple_bike_repositioning.ipynb)。


共享单车是大家非常熟悉的出行方式,既健康又低碳环保,但是经常使用共享单车的小伙伴们肯定遇到过这样的苦恼:早上稍微晚些出门,小区门口就空空荡荡,没有一辆可用的单车,而在地铁站入口则会发现大量的单车几乎把道路堵得水泄不通;等下班从公司去地铁站的时候,同样的状况再次上演。这种单车分布的不均衡事实上是由出行需求的不均衡所导致的。如果任由其发展会显著影响单车的使用效率以及用户体验。

想要打破这种不平衡,必须通过主动调度单车资源的方式来解决,即在合适的时间把单车从需求小的地方调度到需求大的地方。那么,如何设计有效的单车平衡调度策略来解决单车需求的不平衡呢?接下来让我们将从一个简易的问题出发,看看如何使用“群策 MARO”来构造模拟场景,并基于简单规则进行调度尝试。

问题设定

为了简化问题,可以考虑只有四个单车停放站点的情况,这里用 {s_i∣i=0,1,2,3} 来表示单车站点。如图1左侧所示,s_i 和 s_j 之间的有向边代表会有从 s_i 到 s_j 的单车需求,其具体的需求量是由一个时间 t 上的函数 F_ij (t) 决定的。不同站点之间的需求明显是不一样的,若模拟这一点则要求设计多种需求函数,图1右侧即为这些需求函数的曲线示例。

图1:共享单车站点间的拓扑关系与需求

可以发现,对于 s_0 和 s_2,F_02 (t) 和 F_20 (t) 呈现对称的周期性转换,而其他站点对之间的需求函数相对稳定。具体而言,对于任意一个时间点,F_02 (t) 和 F_20 (t) 中仅有一个值可以大于0,即 s_0 和 s_2 之间仅有一个方向的单车需求数量大于0,在需求函数周期的前半段,F_02 (t) 会从0逐渐增长到峰值再降回0,而在需求函数周期的后半段,则换做 F_20 (t) 从0逐渐增长到峰值再降回0。对于 s_1 和 s_3 来说,F_13 (t) 和 F_13 (t) 基本相当,仅在极少数时刻会存在小的数值差异。而 F_01 (t) 和 F_32 (t) 则表现为一个常数函数,取值固定且相等。

在这样的单车需求设定下,想要最大化这个单车世界内的单车需求满足率,s_0 和 s_2 需要依据给定时间段内其供需角色的不同,将富余的单车从没有单车需求的站点调度到单车需求多的站点(如:t=15时,将单车从 s_2 调度到 s_0,而 t=50 时,将单车从 s_0 调度到 s_2)。同时,由于 s_1 和 s_3 之间的供需基本守恒,在无法准确预见未来单车需求量的情况下,无需额外的单车调度。对于 F_01 (t) 和 F_32 (t) 这两个需求函数所带来的单车稳定地被从行程起始站点运往目的站点的现象,可以规律地将单车从行程目的站点运往起始站点。

单车场景在MARO中的运行逻辑

为了方便用户自己定义场景,微软亚洲研究院的研究员们在 MARO 中采取了模拟器(Simulator)、业务引擎(Business Engine)和智能体(Agents)分离的设计。

概括来讲:

  • 模拟器是用来承载整个场景的运行容器,它衔接着业务引擎和智能体。其中有两个关键模块:数据模型(Data Model)和事件缓存(Event Buffer),前者用于高效地存储整个环境的结构化数据,后者主要用来处理场景的业务事件以及智能体的决策事件,进而推动环境运行;
  • 业务引擎维护的是整个场景的业务逻辑,包括修改环境数据、触发事件、执行智能体的动作等等,每个场景需要有自己的业务引擎,它也是自定义场景中主要需要实现的模块;
  • 智能体则主要负责对动作事件响应,并返回合适的动作。

在这个简易的单车调度场景中,可以把主要业务逻辑归为两类:由单车需求触发的单车消耗与回收,以及由此带来的单车移动;为了优化单车使用率,提升单车需求满足度而主动驱动的单车调度。其中,单车需求的处理逻辑如图2所示:在每个时间点上,业务引擎会按照预先设定的单车需求曲线生成特定的单车使用请求,当站点存在空闲单车能满足该请求时,单车将被消耗(模拟器将记录由此引发的状态变化)并记录为一次订单满足(fulfillment)。在特定时间之后该单车会到达请求的目的地站点,成为目的地站点的可用单车(模拟器将再次对各站点状态进行更新);若请求产生时站点空闲单车不足,对应的单车请求将得不到满足,会被记录为一次请求未满足(shortage)。

图2:单车需求的处理逻辑

图3则展示了单车调度逻辑:在每个时间点上,业务引擎会检查各个站点的剩余单车数量,当余车不足时,业务引擎会生成并抛出一个单车调度请求事件。模拟器接收到这一请求事件后会将此事件转发给决策方,即智能体(Agent)。智能体会依据当前系统的状态,按照自己的策略给出执行决策——(a,b,n) 从 s_a 调度 n 辆单车运往 s_b。被调度的单车会在特定时间后到达目的地站点,成为目的地站点的可用单车,在这一过程中模拟器负责对相关站点的状态进行更新。

图3:单车调度逻辑

上述是整个场景的运行逻辑,也就是业务引擎需要实现的内容,具体代码可参考 Notebook(https://github.com/microsoft/maro/blob/master/notebooks/articles/simple_bike_repositioning.ipynb)中 SimpleCitibikeBusinessEngine 类。

基于规则的调度算法

在完成业务引擎的实现之后,下面就可以利用 MARO 提供的接口,实例化一个环境示例,并与之交互。具体过程如下图代码所示:

首先是创建环境,参数分别为环境执行总时间和业务逻辑引擎。环境在执行过程中的所有状态信息都存储在 snapshot_list 中。其次是创建策略对象,策略对象可以是任意类的实例,只需在环境需要执行动作时提供决策即可。接下来是与环境的交互,这里用一个 while 循环实现,即执行环境的 step 操作直至环境结束。当调用 step 函数时,环境会持续推进业务逻辑,直到需要执行动作。此时,需要调用策略对象获得动作。该动作是一个三元组,包括调度源站点 A、调度目的地站点 B 和调度数量 x,描述了从站点 A 到站点 B 运送 x 辆车的调度操作。此外,动作为 None 代表不做任何调度。

接下来将实现三种调度策略,分别是不做任何调度(no action)、基于规则的调度(rule based)和基于统计数据的调度(statistics based)。图4展示了单车总需求和这三种策略下的各个时刻单车缺口数量曲线。

图4:各时刻单车总需求和三种策略下单车缺口数量曲线

在策略的具体实现上,用户可以定义一个类,并重载__call__(event) 函数,在该函数中根据环境的信息处理当前事件。首先可以看一下如果不做调度会产生多少缺箱。这个策略非常简单,只需要对任何的事件返回 None 值即可。

整个环境跑下来总的单车需求为2096,单车需求缺口为945。图4展示了每个时间点的单车缺口曲线。

下面再通过对环境内部的逻辑观察,给出一个合理的基线。为了叙述方便,定义第 i 个站点为 s_i。通过观察可以发现,如果把系统分为左右两个子集,即 {s_0,s_2 } 和 {s_1,s_3 },从左子集指向右子集的需求(由函数 F_01 定义)和从右子集指向左子集的需求(由函数 F_32 定义)刚好抵消。这意味着,如果两个子集各自能够提供足够多的单车满足其内部需求,那么只需要分别在两个子集内部调度即可。

首先考虑右子集 {s_1,s_3 }。由于外界的单车总是从 s_1 流入,并从 s_3 流出,因此只需要将所有从 s_1 流入的单车都发往 s_3 即可。为了简单起见,在每次调度时保留固定数量的单车而将剩下所有单车发给 s_3。设保留数量为常数 R_13。

对于左子集 {s_0,s_2 } 而言,情况要稍微复杂一些。由于左子集内部的需求函数 F_02 和 F_20 是以 20π 为周期的函数,因此可以以周期为单位进行分析。在前半周期,左子集内部只有从 s_0 指向 s_2 的需求,且外界的单车总是从 s_2 流入,并从 s_0 流出。s_0 只出不进,而 s_2 只进不出。因此在这个期间,将所有 s_2 的单车都运往 s_0 即可。在后半周期,左子集内部只有从 s_2 指向 s_0 的需求。由于 s_0 还需要满足 F_01 的需求,所以 s_0 需要保留一部分单车,并将剩余的全部运给 s_2。同样,设此保留数量为 R_02。

当该算法在系统中存在单车数量为0的站点时触发。算法需要从环境中获取当前每个站点的单车数量,再通过上述规则返回调度结果。调度结果以三元组 (s_i,s_j,x) 的形式给出,表示从站点 s_i 运输 x 个单车到站点 s_j。

整个算法如下图所示:

在代码中设置:R_13=5,R_02=5。用户可以通过 event 的 station_index 成员变量获取当前需要单车的站点,还可以通过环境的 tick 变量获得当前时刻,通过 snapshot_list 获得各个站点的状态。

这里简单介绍一下 snapshot_list 的接口:通过键值 “station” 可以获得每个站点在历史上每个 tick 的属性值,属性值由三个部分索引组成,按顺序分别为时间 tick、站点下标 index 和属性名,若其中任一索引省略,那么接口将返回该索引的所有值。例如,上述程序中 [self.env.tick::”bikes”] 代表获得在环境当前 tick 所有站点的单车数量。

执行该策略最后总的单车短缺数量为107,各时刻的缺口数量可以参见图4。

在实际场景中我们并不能实时了解真实的用车需求,用车需求时时刻刻都在发生变化,无法通过固定的策略获得很好的调度效果。因此可以采用一种自适应的方法,该方法的思想也很简单:如果人在不清楚用车需求的情况下做决策,最直接的办法就是,先让系统运行一段时间,观察哪个站点缺车就往哪个站点运输。这种方法虽然存在延迟响应的问题,但执行起来往往简单而有效。

具体而言,研究员们统计了每个站点缺车数量的指数衰减累积量,并以此衡量站点的缺车程度。每次系统发现缺车点,并触发调度动作,就从最不缺车的站点运单车过去。此外,由于系统可能在同一个 tick 多次触发调度,为了防止一个站点多次被选择,所以每次选择都会降低该站点的优先级。具体代码可参考 Notebook 内容。其中的超参可以通过简单的网格搜索进行优化,找到最优的超参组合:{decay_factor=0.8,weight=0.8},此时单车缺口为278。各时刻的缺口数量可参见图4,详细代码请参见 Notebook。

结语

本文通过自定义一个简单的共享单车场景介绍了 MARO 的核心模块、运行逻辑以及与智能体的交互方式。除了方便使用者自定义场景之外,MARO 的另一个核心优势是拥有很多基于真实数据与业务逻辑打造的预定义场景,例如基于纽约自行车数据的 Citi Bike 场景[1]、基于真实海洋拓扑的空集装箱调度、使用真实 Azure 数据的虚拟机分配问题[2]。基于 MARO,研究员们也已经在集装箱调度问题上做了一系列的研究工作[3,4]。

除了场景之外,MARO 还拥有易用的多智能体框架,高效的分布式训练算法和预定义的主流强化学习算法工具包,这些部分会在后续的文章中向大家介绍,帮助大家使用 MARO,提高开发和训练效率,助力强化学习在资源优化领域的应用。

相关链接

MARO 0.2 版本具体更新历史:https://github.com/microsoft/maro/pull/239

MARO 中预设定的资源优化任务:

  • 航运网络中的空集装箱调度问题:https://maro.readthedocs.io/en/latest/scenarios/container_inventory_management.html
  • 共享单车服务中的单车调度问题:https://maro.readthedocs.io/en/latest/scenarios/citi_bike.html
  • 云平台上的虚拟机分配问题:https://maro.readthedocs.io/en/latest/scenarios/vm_scheduling.html

参考资料

[1] Citi Bike 数据集:https://www.citibikenyc.com/system-data

[2] Azure数据集:https://github.com/Azure/AzurePublicDataset

[3] Li X, Zhang J, Bian J, et al. A cooperative multi-agent reinforcement learning framework for resource balancing in complex logistics network. AAMAS, 2019.

[4] Shi W, Wei X, Zhang J, et al. Cooperative Policy Learning with Pre-trained Heterogeneous Observation Representations. AAMAS, 2021.

 

...全文
848 1 打赏 收藏 转发到动态 举报
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
zgh691015 2021-10-05
  • 打赏
  • 举报
回复
学习学习。。。。。。
源码直接下载地址: https://pan.quark.cn/s/1c143f32ee83 华为作为全球领先的通信设备供应商,其产品系列广泛涉及各类网络设备,其中包括我们接下来要探讨的上网卡产品。华为上网卡驱动程序是一种专门为华为品牌旗下多种型号上网卡开发的软件模块,其主要功能在于保障这些设备与计算机操作系统的无缝对接。涉及的型号涵盖EC8189、EC226、EC169C、EC360、EC1260、EC1261、EC189、EC122、EC150以及EC168,这些均是由华为公司推出的移动宽带调制解调器,旨在通过移动网络实现便捷的互联网接入服务。驱动程序在计算机系统中的地位举足轻重,它充当了硬件设备与操作系统之间的媒介,负责对硬件设备发出的指令进行解读和执行,并将操作系统的指令传递给硬件设备。华为上网卡驱动程序的及时更新和精准安装是保障设备稳定运作和性能达到最优的关键因素。"天翼宽带安装程序V1.3.3.exe"是由中国电信提供的一个整合性软件包,内含华为上网卡的驱动程序及配套的管理工具。用户可借助此安装程序来执行华为上网卡的相关驱动安装及更新,同步享受中国电信的3G或4G网络服务。版本标识V1.3.3代表软件经过迭代优化,通常包含了对错误的修正、性能的改善以及新功能的引入。 "SetupInfo.xml"作为安装程序的配置文档,其中收录了安装流程中的各项设定和元数据信息,例如安装流程、文件定位、依赖条件等。它是安装程序在执行时参照和遵循的纲领,旨在确保安装流程按既定方案进行。文件名"CT_HW_EVDO_Driver"或许指向华为的EVDO(Evolution-Data Optimized)驱动程序,EVDO是一种3G无线通信规范,具备高速数据传输的特性。该驱动可...
内容概要:本文围绕【博士论文复现】基于小信号扫频辨识的光伏并网逆变器正负序交互稳定性分析展开,结合Matlab代码与Simulink仿真实现,系统研究了光伏并网逆变器在弱电网环境下的正负序阻抗建模与交互稳定性问题。重点采用小信号扫频法进行系统辨识,获取逆变器的正负序阻抗特性,并通过奈奎斯特稳定判据等方法分析其在不同电网强度下的稳定性表现。文中详细阐述了扫频激励信号的设计原理、频域响应数据的提取与处理流程、阻抗模型的拟合与验证方法等关键技术环节,实现了对逆变器在复杂电网条件下动态交互行为的精确刻画。该研究不仅深入揭示了新能源并网系统中潜在的宽频振荡机理,也为提升系统稳定性、优化控制器设计提供了坚实的理论依据和有效的技术手段。; 适合人群:具备电力电子、自动控制及电力系统基础知识,从事新能源并网、电力系统稳定性研究的研究生、科研人员及工程技术人员。; 使用场景及目标:① 掌握基于小信号扫频法的电力电子装置阻抗建模方法;② 深入理解光伏并网逆变器在弱电网下的正负序交互稳定性机理;③ 学习并复现高水平博士论文中的核心仿真技术,提升科研实践能力;④ 为实际工程中新能源并网系统的稳定性分析、振荡问题诊断与控制器优化设计提供理论支持和技术参考。; 阅读建议:学习者应结合提供的Matlab代码与Simulink模型,深入理解扫频辨识的原理与实现步骤,重点关注锁相环、电流控制环等关键模块对系统阻抗特性的影响,并尝试改变系统参数以观察稳定性变化,从而加深对理论知识的掌握。
代码转载自:https://pan.quark.cn/s/a4b39357ea24 OPC(OLE for Process Control)是由微软推出的一种应用于工业自动化场景下的数据交换规范,其目的是使多样化的自动化装置与软件平台之间能够实现信息互通。在本项研究中,我们集中探讨的是一个运用C#语言构建的完整OPC客户端的源代码实现。该客户端具备与OPC服务器建立连接、获取或设置数据的能力,从而促成设备间的协同工作。鉴于C#是.NET框架的核心编程语言,并且拥有丰富的类库资源及强大的面向对象支持,它特别适合用于开发此类工业环境的应用程序。接下来将针对OPC客户端源代码中可能涉及的核心技术要点进行详尽的阐述: 1. **OPC Foundation .NET库**:为了在C#环境下实现OPC通信功能,开发人员通常会选择采用OPC Foundation提供的.NET库,例如OPC-UA .NET Standard或OPC Classic .NET。这些库提供了操作OPC服务器的必要API,涵盖了建立连接、遍历服务器节点、读取与写入数据等一系列操作。 2. **OPC连接配置**:客户端在运行前必须先与OPC服务器建立通信通道。这一过程通常需要配置服务器的位置信息、身份验证凭证(包括用户名和密码)以及连接的详细参数。在源代码中,可能会包含一个`Connect()`方法来处理这些连接细节。 3. **数据项订阅机制**:OPC客户端通过向服务器订阅数据项来实时获取数据更新。在订阅阶段,客户端会指定需要监控的数据项的唯一标识,并设定当数据发生变化时触发的回调函数。在C#编程语言中,这一过程可能通过`AddSubscription()`和`AddItem()`方法来完...

288

社区成员

发帖
与我相关
我的任务
社区描述
微软公司官方社区。予力众生,成就不凡!微软致力于用技术改变世界,助力企业实现数字化转型。
社区管理员
  • 微软技术栈
  • CSDN官方博客
  • 坚强打工人
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧