软件测试是软件工程中最复杂的任务之一。虽然在传统的软件工程中,有一些原则明确地定义了应该如何测试软件,但对于机器学习来说并非如此,在机器学习中,测试策略并不总是被定义。在这篇文章中,我阐明了一种测试方法,它不仅受到软件工程中最公认的测试策略之一的高度影响,也就是测试驱动开发。但似乎也是一种与正在测试的机器学习模型家族无关的方法,并且非常适合导致今天大规模AI/ML服务的典型生产环境。
阅读完这篇文章后,您将了解如何在考虑到生产的情况下设置适用于机器学习模型的测试策略。考虑到生产意味着您正在操作的团队是异构的,正在测试的项目是与其他数据科学家、数据工程师、业务客户、开发人员和测试人员一起开发的。好的测试策略的目标是实现生产就绪并提高代码的可维护性。
这种方法的合适名称是Test-First机器学习,简称TFML,因为一切都是从编写测试开始的,而不是从模型开始的。
TFML的一个特点是从编写测试开始,而不是从机器学习模型开始。该方法基于嘲笑任何尚不可用的东西,以便项目中涉及的不同参与者无论如何都可以继续他们的任务。众所周知,数据科学家和数据工程师的运行速度不同。嘲笑世界上尚未出现的某一特定方面,不仅可以减少这种差异,还可以减少大型团队中的拦路虎。这反过来又提高了效率。下面是TFML方法的五个基本步骤。
顾名思义,TFML中的Test-First表示一切都从编写测试开始。即使对于尚不存在的功能也是如此。这样的测试通常很短,应该保持不变。更大、更复杂的测试应该分解为基本的和可测试的组件。在理解了需求分析(例如,用例和用户故事)过程中通常会在前面讨论的特性规格和需求之后,可以编写测试。
正常工作的测试会因为正确的原因而失败或通过。这是定义这些原因的步骤。定义快乐之路对于定义应该遵守和认为成功的东西至关重要。
在这一步中,实际上编写了通向快乐路径的代码。此代码将使测试通过。除了测试的愉快路径之外,不应该提供任何其他代码。例如,如果机器学习模型预期返回42,则只需返回42并强制测试在此处成功即可。如果需要时间限制,也可以添加睡眠(毫秒)。这样的模仿值将为工程师提供可见的约束,这样他们就可以继续执行任务,就好像模型已经完成并且正在工作一样。
添加新的测试不应该破坏以前的测试。拥有相互依赖的测试被认为是软件工程中的反模式。
当模拟了值、定义了成功条件并运行了测试时,就可以显示测试中的ML模型正在训练和执行预测。与上面的示例相关,在此步骤中应该找到答案的一些问题包括:
传统上,在此步骤中,开发人员执行代码清理、重复数据删除和重构(只要适用),以提高可读性和可维护性。这一策略也应该应用于ML开发人员。
由于机器学习的本质和数据科学家的热情,他们很快就连接-训练-分析数据,因此在机器学习中更容易落入替代方法的陷阱。
数据科学界最常见的方法可能是测试-最后方法,也称为“最后测试方法”(Test-Last Approach)。现在编码,稍后测试。这种方法在ML模型开发中可能非常危险,因为与传统软件(例如,UI、API调用、数据流、数据库、预处理步骤等)相比,即使对于微不足道的线性回归,也可能有太多的移动部分。事实上,Test-First方法鼓励并迫使开发人员根据这些移动部分(例如UI和数据库)将最少量的代码放入模块中,并实现应该属于代码库的可测试部分的逻辑。
需要避免的一个重要陷阱是开发人员偏见。在Test-First环境中创建的测试通常由编写测试代码的同一开发人员创建。这可能是一个问题,例如,如果开发人员不考虑要检查某些输入参数。在这种情况下,测试和代码都不会验证这些参数。在传统的软件开发中,测试工程师和开发人员通常不是同一个人,这是有原因的。
测试应该是独立的。依赖于他人的测试可能会导致级联失败或开发人员无法控制的成功。
在传统的软件工程中,测试精确的执行行为、时间或性能可能会导致测试失败。在机器学习中,考虑软约束甚至更重要,因为模型可以是概率的。此外,输出变量和输入数据的范围可以改变。这种动态的、有时定义松散的行为是ML中的规范,而不是例外。
测试模型实现细节(如统计和数学可靠性)不是TFML策略的一部分。这些细节应该单独测试,并且特定于所考虑的模型系列。
对于测试下的功能,测试面应该始终是最小的。保持测试单元较小可以让开发人员拥有更多控制权。较大的测试单元应该被分解成较小的测试,专门针对待测试模型的一个特定方面。
TFML方法迫使开发人员花费初始时间为他们的模型定义测试策略。这反过来又促进了将这些模型集成到复杂工程系统的更大图景中,其中涉及更大的团队。已经观察到,编写测试越多的程序员往往效率越高。测试代码与开发软件核心功能一样重要。测试代码的生成和维护应与生产代码一样严格。在ML中,由于涉及ML项目的系统和人员的异构性,所有这些都变得更加关键。