本文分享了由Vitor Lemos编写的关于AnyLogic的使用实践技巧,希望能为正在学习或使用AnyLogic的用户提供帮助。post/the-simulation-model-life-cycle-part-5-experimentation
作者:Jaco-Ben Vosloo
更新:Apr 6, 2022
阅读时间:12 min
本系列文旨在对仿真项目的生命周期进行说明,并给出一些最佳实践和实用建议。
这篇文章是关于仿真模型生命周期的 7 部分系列文章的第 5 部分。
注意:在后面的步骤中可能会“返回”前面的步骤中执行一些活动,但并不需要重做整个步骤。 上面列出的步骤是典型项目遵循的一般步骤。在所有步骤中,您都可以后退并重新访问之前的步骤,但在完成至少 80%-90% 的步骤之前,您不太可能跳过一个步骤或向前移动。
实验可以说是仿真模型生命周期中比较有难度的一个步骤。我有时觉得这更像是一门艺术,而不是一门科学。想要从一开始就只考虑正确实验是非常难的,更不用说以正确的方式执行它们。本文并不是一篇创建良好实验的全面指南,更像是正确掌握基本知识的快速入门指南。
我们都知道,我们不能为了给出想要或期望的答案而设计实验,让我们看一些实践来帮助我们创建有意义的实验。 下面,我们将深入研究之前的示例,其中包含一些在 AnyLogic 中设置实验的最佳实践。
在仿真阶段,您需要将重点放在模型的原始目标上,如仿真模型生命周期的part 1(定义问题)中所定义的。否则,你可能会因为一些有趣的想法和假设转移重心,虽然这些想法和假设具有增值作用,但不是模型或项目的主要目标。
因此,需要创建不同的场景来测试目标的不同部分,以及一次性测试整个目标的组合场景。听起来比实际场景要容易得多!慢慢来,把你的场景组织好。如果您遵循了part 2和part 3中的最佳实践,那么您的场景应该只是一个简单的Excel或其他一些基本数据文件,您可以导入并在模型中运行,而无需任何麻烦。
根据我们的零售商示例,我们的场景非常简单:
在我们的两个场景文件中,唯一的区别是队列的数量这一个参数。
或许难以置信,但现实生活中的例子通常也是这样的,场景之间的唯一区别通常就是单个参数的变化。
压力测试所花费的时间在很大程度上取决于可用时间、模型输出的置信度以及决策的重要性。
基本上,你可以这么问自己:“出错的代价是什么,而要保证自己是正确的代价是什么?”
在这个阶段中,你会创建一些场景,有时是离谱的,但这些场景是为了推进模型的极限,以确定解决方案空间。当然,也要小心不要测试一些离谱的输入,例如模型中没有队列。这种类型的测试是为了查看模型是否中断,最好在执行单元测试的part 3中完成。这里的重点应该是测试在什么条件下第 1 点的假设和发现是有效的。
例如:
这里的目的是测试您得出的结论是否仍然有效,以及在哪些条件下它们无效或只是有所不同。这也可以帮助您识别逻辑中可能存在的错误。如果结果无法解释,则可能是存在错误,或者您只需要添加更好的输出数据,以便提供足够的上下文和洞察力来解释结果。
这是从实验中获得有效仿真结果的绝对必要条件。您必须使用不同的随机流运行多个复制实验,以获得输出的分布。通常通过创建蒙特卡洛实验来实现,在该实验中,您只需收集特定的模型输出,同时在多个运行中改变种子。
如果您只对每个场景运行一次迭代,即使两个场景都具有相同的随机种子,您也不知道这些结果在可能性的宇宙中位于何处。
注:向您推荐一篇文章“在AnyLogic 中如何设置随机性?”帮助您理解和使用AnyLogic的随机功能。
示例:假设您创建了一个仿真模型,可以模拟纽约市一年中任何一天的平均温度,并要求您给出冬季和夏季温度之间的预期差值。如果你只仿真夏季的某一天,并将其与冬季的某一天进行比较,由于纯粹的随机性,你可能最终模拟出最冷的夏季和最温暖的冬季,这将给你一个小于10。F的增量。
那我们可以假设冬季和夏季温度之间的差异几乎可以忽略不计吗?
请注意,如果没有数千天数据,你至少需要模拟100天才能得到一个具有代表性的平均值。。。。然后您会得到夏季和冬季的平均温差约为40。F。
这就引出了我们的第二点:平均值。你可能听说过一句格言,如果你的头在冰箱里,脚在烤箱里,平均来说,你处于一个非常舒适的温度。
你们不仅需要多次运行才能得到一个有代表性的平均值,而且还需要以此来获得可能结果的分布。你需要知道最小值、最大值、标准差以及任何其他适用于特定项目的值。通常,在直方图中显示测量结果可以很好地直观地表示各种运行中的输出。
在我们的零售商示例中:如果客户的平均等待时间为10分钟,最长为2小时,我们还需要问问自己,模型或输入数据中是否存在缺陷,或者这只是过程的本质吗?
注意:由于我们之前是以客户的历史服务时间进行仿真,因此在实验阶段进行多次复制运行是没有意义的,因为我们将一遍又一遍地模拟相同的服务时间......因此,每次运行都会得到相同的结果。我们需要向场景文件中添加新参数,并在模型中使用它们来模拟服务时间的分布。
在前一点的基础上,你的实验输出结果需要包含足够的信息,以便你不仅可以给出结果,而且能够证明和解释结果。为此,您需要以下几点:
详细结果将与单次运行实验中输出的结果相同,并且摘要将只是每个场景的多次运行的摘要。通常,只有当您在模型中看到较大的标准差或显著的最小值或最大值,并且怀疑其中一个复制可能遇到错误时,您才会查看详细数据。由于输出文件中提供了每个复制的单独结果,因此我们可以在一些外部工具(例如Excel)中轻松计算汇总统计数据。但是,当我们可以让模型为我们做这件事时,为什么还要为每个场景比较计算这个呢。
现在,我们已经了解了良好实验的基础,让我们继续执行part 4中的示例,并将这些操作实现到模型中。
注意:在本例中,我们将继续使用在part 3中设置的自定义实验对象。AnyLogic确实提供了一些用于灵敏度、蒙特卡洛、参数变化和其他实验的预定义实验选项,但对于一些更为复杂和长期的仿真模型,我并不喜欢使用它们,具体有以下几个原因:
该示例分为三个部分,第一部分侧重于我们需要对模型和场景文件进行的一些升级,以便我们可以为每个场景设置和保存一些细节。 第二部分是关于保存输出并一个接一个地运行多个场景。 最后一部分的重点是我们现在如何使用第 1 部分和第 2 部分中的更新来一个接一个地运行多个场景,保存输出并从模型中下载它。
这些新列将定义一些之前未通过场景对象设置的模型行为。
我发现最好在场景文件中添加一个命名为“ModelSetup”的新表,在这里我有一个包含多个列的单行,用于存储模型运行的设置。同时,我还添加了一列场景名称,然后在运行多个场景时可以将其输出到结果文件。
下面是对每个新字段的快速解释:
在 Excel 文件内的新工作表中创建这些字段后。 您可以将新工作表作为新数据库表导入模型。 现在,此数据可在场景创建中使用。
现在,我们需要将场景文件中的相同字段添加到场景对象中,以便模型可以使用它们。
我们还需要在我们创建的“getScenarioFromDB”场景加载器函数中填充这些字段`
请注意以下事项:
注:Project Lombok 是一个 java 库,可自动插入您的编辑器和构建工具。
既然参数在场景对象中可用,我们可以在模型逻辑中使用它们来设置模型。
第一步(也就是a)在场景文件中添加新列)是使用场景对象内的设置,该设置定义是否使用历史数据或三角形分布。
下一步是使用场景变量来定义我们是使用单个队列还是多个队列。
现在模型正在使用新的设置变量,我们可以将更多仿真结果保存到输出文件中。
a) 详细结果
在part 4中,我们只将单个仿真输出保存到文本文件中。文本文件是main中的一个对象,因此如果我们有多个仿真运行,每个复制场景都将覆盖前一复制的数据。
在part 4中将客户等待时间保存到文本文件
为了让所有模型的顾客等待时间写入一个文本文件,我们需要将文本文件对象存储在仿真对象上,并使用参数传递给模型。现在,所有迭代都将写入同一个文本文件,所有结果都将在同一个文件中。幸运的是,文本文件也是线程安全的,所以如果我们为实验进行多线程处理,结果仍然会正确保存。 (更多关于多线程的信息将在以后的文章中介绍;-))
但是,我们如何区分某次迭代和下一次迭代呢?或者某个场景和下一个场景?
在文本文件中,我们添加了两个新列,场景名称和种子,并使用制表符分隔所有列(因此代码中有“/t”)。仅供参考:选项卡可以在Excel中轻松打开单独的文本文件,每个选项卡表示一个新列。
在part 5中,使用参数将模型输出保存到文本文件
但是种子参数从哪里来?
在仿真中识别种子通常是一个大问题,不时会在Stack Overflow上发布。。。
举个例子:
如何输出由 AnyLogics 随机种子(独特的仿真运行)RNG 生成的种子?
在这个例子中,我将我们的自定义实验对象从part 4升级到现在,还包括种子和文本文件作为“startRound”函数的输入;
种子现在可以提供给定制实验,也就是说我们在创建定制实验时可以完全控制它。
您还将注意到另一个名为Results的参数,该参数在代码中进一步使用。
b) 总结结果
通常我们对每次复制实验的单个结果关注不多,而是对多次运行的汇总统计信息更加关注。为了实现这一点,我们创建了一个名为Results的新Java类。每次复制都会将结果保存在此对象中,并在其中存储统计信息,我们可以将摘要统计信息写入下一个文本文件。
下面的结果文件几乎概括了所有模型中我会使用的最基本的形式。
使用这种方法,会将所有统计信息都存储在这一个对象中,所以您无需将数百个对象从面板拖放到main或仿真页面。
现在,我们已经对模型和结果类进行了更新,我们可以在仿真首页上设置一个新功能,让我们可以选择多个Excel文件,将它们一次导入到DB中,并让它们所代表的场景在设置完成后执行并保存所有结果。
1.我们创建了一个按钮,允许用户使用以下代码选择多个excel文件:
FileDialog fileDialog = new FileDialog(new Frame(), "Select all input files", FileDialog.LOAD );
fileDialog.setMultipleMode(true);
fileDialog.setFile( "*.xlsx" );
fileDialog.setVisible( true );
File[] files = fileDialog.getFiles();
for (File file:files) {
filesToLoad.add(file.getAbsolutePath());
}
2.然后,我们允许用户设置复制的数量
3.然后创建按顺序运行所有场景的按钮,为不同场景运行之间的每个对应迭代设置相同的种子。此函数还将传递txt文件以获取每个模型运行的详细结果。它还为每个场景传递一个新的结果对象,并将其保存到具有相应场景名称的映射中。
一旦执行了所有场景,它将从结果映射中获取结果,并将摘要统计信息写入一个名为“summaryResults”的新txt文件
下面是场景比较功能的简短视频
如果我们在Excel中打开摘要文件,我们会得到一个摘要表,如下所示。
在本文中,我们研究了仿真模型生命周期的实验阶段,以及如何在模型内正确设置实验。我们研究了一些利用自定义实验的高级方法,允许我们完全控制场景比较和敏感性分析实验。与前一篇文章相比,这无疑增加了复杂性,但是这是值得的,尤其是当您在构建复杂模型时,需要完全控制实验执行。
在下一篇文章中,我们将稍作休息,看看一些分析结果的最佳实践。