构建可扩展的机器学习系统:你所需的架构设计知识

2019 年 5 月 24 日 AI前线
作者 | Semi Koen
译者 | 杨雷
编辑 | Natalie
AI 前线导读: 本文介绍了机器学习应用中的一些软件工程基础,快速浏览了最流行的一些架构模式、设计模式,以及面向对象设计的 SOLID 原则,目的是让读者尽可能多地了解构建可扩展软件的主要贡献因素。应用程序设计是否能够适应变化,是构建成功解决方案的关键,如果设计过程很仓促,项目结束时,一定会为犯下的错误交学费。

更多优质内容请关注微信公众号“AI 前线”(ID:ai-front)

本专题将通过 两篇系列文章,介绍与体系结构和设计有关的软件工程基础知识,以及如何在机器学习管道(ML Pipeline)的每个步骤中应用这些基础知识:

  • 第 1 部分:问题陈述 |架构风格 |设计模式 |SOLID

  • 第 2 部分:构建机器学习管道

介   绍

就像以前在 Steven Geringer 著名的 维恩图 中看到的,数据科学是 3 个学科的交集:计算机科学、数学 / 统计学和特定的领域知识。

数据科学维恩图 [©Steven Geringer]

拥有基本(甚至是高级)的编程技能,是将端到端的经验结合在一起的关键,但还不足以创建一个可以发布的应用程序。除非从 IT 背景 进入数据科学和机器学习(ML),并且在构建企业级的、分布式的、可靠的系统方面拥有实际经验,否则你的 Jupyter 笔记本(译者注:可创建和共享自己的文学化程序文档)没资格成为优秀的软件,遗憾的是,你也不能成为软件工程师!

虽然你打算构建的是一个很好的预测产品的 原型,但仍然需要通过工程技术的路线图来推动它。所需要的是一个由专业软件工程师组成的团队,可以将(一次性)概念验证转化为 高性能可靠松耦合可扩展的 系统!

一切都是设计出来的 ; 但设计得好的东西很少!

在本系列中,我们将展示如何实现良好设计的一些想法。第一部分从基础知识开始,第二部分逐步设计整体架构。这里建议的架构是 技术无关的, 机器学习管道将被分解成责任明确的不同层,可以从许多技术堆栈中选择如何来实现每一层。

接下来看看成功的解决方案应该是如何实现的。

问题陈述

我们的主要目标是建立一个系统,具备以下特性:

  • 减少延迟;

  • 集成系统的其他部分(例如数据存储,报告,图形用户界面),但松耦合;

  • 可以水平和垂直伸缩;

  • 消息驱动,即系统通过异步、非阻塞的消息传递进行通信;

  • 提供工作负载管理相关的高效计算;

  • 容错和自我修复,即故障管理;

  • 支持批量和实时处理。

架构风格

我们将首先介绍一个反应式系统,并快速浏览最流行的架构模式。

反应式系统

反应式系统设计范式采用前后一致的方法来建立更好的系统,这些系统是根据“反应式宣言” 的原则进行设计的。每个反应式原则对应一个可扩展性的重要系统维度:

  • 易响应的 → 时间

  • 易扩展的 → 负载

  • 可恢复的 → 错误

  • 消息驱动的 → 通信

反应系统的特性

面向服务架构(SOA)

SOA 将业务问题分解为服务,这些服务通过网络来共享信息。它们还共享代码(即公共组件),以保持反应式系统的一致性和特性,从而减少开发工作。

服务 提供者 发布合约,规定服务的特征以及如何使用服务。服务 使用者 可以在注册中心中找到服务元数据,开发所需的客户端组件来绑定和使用它。

协调器(Orchestrator)是一个综合服务,负责调用和组合其他服务。此外,编排(Choreography)采用去中心化的方法组合服务,服务通过消息 / 事件的交换进行交互。

SOA

流式(Streaming)架构

流式架构包括以下组件:

  • 生产者:生成和发送消息的应用程序

  • 消费者:订阅和使用消息的应用程序

  • 主题:类别特定的记录流,存储成有序和不可变的记录序列,通过分布式集群进行分区和复制

  • 流处理器:以特定方式处理消息的应用程序(例如数据转换、ML 模型等)。

流式架构

Lambda 架构

Lambda(λ)架构旨在以集成的方式处理 实时 和过去聚合的 批量数据。它分离了实时和批处理的职责,查询层提供了所有数据的统一视图。

这个概念很简单:生成数据时,会在存储之前对其进行处理,因此分析可以包括最后一秒、最后一分钟或最后一小时生成的数据,只处理传入的数据,而不是所有的数据。

Lambda 架构

微服务架构

微服务是一种架构风格,它将应用程序构造为小型、自主、松耦合和协作的服务集合,围绕业务领域进行建模。这些服务使用同步协议(HTTP/REST)或异步协议(AMQP)进行通信,可以彼此 独立 地开发和 部署。每个服务都有自己的数据库,以便与其他服务分离。

微服务架构

REST 架构

REST 是一种用于开发 Web 服务 的架构风格,它建立在 Internet HTTP 的现有特性之上,允许以无状态的方式传输、访问和操作文本数据,即应用程序可以在不知道状态的情况下进行通信。

RESTful API 服务通过统一资源定位器(URL)公开,提供了创建、请求、更新或删除(CRUD)数据的功能。它最适合处理解耦(生成 / 消费的 ) 信息和(生成 / 使用信息的 ) 技术的系统。

REST 架构

设计模式

我们将浅显地介绍这个主题,并且只讨论我在本系列的第二部分中可能提到的那些模式。(虽然我每天都在使用它们,但很难用简单的语言全部解释清楚)

软件设计模式是针对软件工程中常见问题的优化的、可重复的解决方案。它是一个解决问题的模板,可以在许多不同的情况下使用。

策略

策略模式定义了一系列算法,将每个算法放在一个单独的类中,相互之间 可互换。将行为封装在单独的类中,消除了任何条件语句,在运行时可以选择正确的算法(即策略)。

  • 使用说明:业务规则有不同的实现,或者需要算法的不同变体。

策略模式

模板方法

模板方法旨在从不同的过程中抽象出一个通用的过程。它定义了算法的 骨架,将某些步骤推迟到子类。子类可以覆盖某些行为,但不能更改骨架。

  • 使用说明:遵循一系列一致的步骤,各个步骤可能有不同的实现。

与策略模式的区别

  • 模板:子类编译时 选择算法。

  • 策略:控制运行时 选择算法。

模板方法模式

责任链

责任链模式通过启用一个或多个 处理程序 来满足请求,避免将客户端(请求的发送者)与接收者耦合。这些处理程序连接到一个链中,每个处理程序都包含对链中下一个处理程序的引用。

  • 使用说明:多个对象可以处理一个请求,处理程序(或序列)的优先级事先不知道。

责任链模式

观察者

观察者模式(缩写为 Publish/Subscribe 或简称 PubSub)通过定义对象之间的一对多依赖关系,可以轻松地进行通信 广播,当一个对象的状态发生变化时,它的所有依赖关系都会自动得到通知和更新。观察者负责注册它们所“观察”的事件。

  • 使用说明:当改变某个对象时同时需要改变其他对象,不知道多少对象需要改变。

观察者模式

建造者

建造者模式旨在 逐步 构造一个复杂的对象,分离构造与表示。实质上,它允许使用相同的代码,生成对象的不同类型和表示形式。

  • 使用说明:虽然各个构造步骤有所不同,但可以使用相同的整体构建过程来构建多种复杂对象。

建造者模式

工厂方法

工厂方法定义了一个用于 创建对象 的接口,由子类来完成实例化。

  • 使用说明:对象的具体类型和依赖关系事先不知道。

抽象工厂

抽象工厂关注如何创建 相关产品的系列, 不指定它们具体的类。

  • 使用说明:不同的规则采用不同的实现,这些规则要么是未知的,要么是可扩展的。

⭐️ 与抽象方法的区别

  • 抽象工厂:创建其他工厂,这些工厂反过来创建从基类派生的对象。

  • 抽象方法:创建从特定基类派生的对象。

装饰

装饰模式动态地将新的职责附加到对象上,将对象放置在包含行为的特殊包装类(wrapper)中,因此不会影响原来方法的签名(继承上的组合)。

  • 使用说明:运行时为对象分配额外的行为,不会破坏使用这些对象的代码。

仓库

仓库模式解决了数据检索和持久化的代码集中化问题,并为 数据访问 操作提供了 抽象, 类似内存中域对象的集合,允许执行 CRUD 方法,消除了各种数据库问题。

  • 使用说明:分离业务逻辑和访问数据的代码。

小奖励

想进一步了解模式?可以先从“Gang of Four”的书《设计模式:可重用的面向对象软件的基础》开始。下面的图展示了模式之间的关系,非常重要:

设计模式之间的关系

SOLID

我们唯一的设计原则是 SOLID,它们对于每个软件开发人员来说都是必不可少的。

正如 Bob 大叔 所说:“它们不是法律,也不是完美的真理。它们类似于一个规则:每天一个苹果,医生远离我 ”。

这意味着,它们不是某种“魔法”,不是带来牛奶、蜂蜜和优质软件的应许之地,它们是健壮、持久软件的关键贡献者。

简而言之,这些原则围绕两个主要的概念展开,是成功企业应用程序的基石:耦合 是类了解另一个类并与之交互的程度;而 内聚 表示类具有单一用途的程度。换一种说法:

  • 耦合是关于类如何相互作用的;

  • 内聚关注单个类的设计方式。

单一责任原则

一个类应该有一个,而且只有一个变化的理由

这是不言自明的,但说起来容易做起来难。我们总是想在现有类中添加新的行为,但这是一个引发灾难的处方:每个行为都可能成为未来变化的理由,因此行为越少,在变化时产生错误的机会就越少。

开闭原则

能够扩展类的行为,而无需对其进行修改

类应该对扩展“开放”,但是对修改“关闭”。要实现这一点可以通过继承,即创建一个子类,关闭对原始类进行修改,将自定义的代码添加到子类来引入新的行为。

里氏替换原则

派生类必须可替代基类

当类 A 的行为扩展到子类 B 时,可以确保在不造成任何破坏的情况下用 B 替换 A。这点可能比较吸引人,特别是当把这一原则与开闭原则结合起来时。

接口隔离原则

创建特定于客户端的细粒度接口

接口和类必须尽可能特定,客户端的调用不依赖未使用的方法。这与单一责任原则是一致的。

依赖倒置原则

依赖抽象,而不是具体实现

高层的类不应该依赖于低层的类。它们都应该依赖于抽象。同样,抽象不依赖于细节,细节依赖于抽象。

小奖励

我创建了下面这个快速参考图。如果想知道左边小符号的灵感来自哪里,请参看:“SOLID 原则,用励志海报解释” —— 我喜欢作者在这些原则上增加的有趣改变。

脚    注

这里无法涵盖所有软件工程概念的详尽列表,它只是阅读下一篇文章的基础,我希望它能让读者了解构建可扩展软件的主要贡献因素。应用程序设计是否能够 适应变化,是构建成功解决方案的关键,如果设计过程很仓促,项目结束时,一定会为犯下的错误交付学费。

好的设计是易懂的。伟大的设计是透明的。

原文链接

https://towardsdatascience.com/being-a-data-scientist-does-not-make-you-a-software-engineer-c64081526372


你也「在看」吗?👇

登录查看更多
2

相关内容

软件工程 (Software Engineering) 是一门研究和应用如何以系统性的、规范化的、可定量的过程化方法去开发和维护软件,以及如何把经过时间考验而证明正确的管理技术和当前能够得到的最好的技术方法结合起来的学科。
【论文扩展】欧洲语言网格:概述
专知会员服务
6+阅读 · 2020年3月31日
Sklearn 与 TensorFlow 机器学习实用指南,385页pdf
专知会员服务
126+阅读 · 2020年3月15日
KGCN:使用TensorFlow进行知识图谱的机器学习
专知会员服务
80+阅读 · 2020年1月13日
知识图谱的自动构建
DataFunTalk
55+阅读 · 2019年12月9日
【数字化】制造业数字化转型的实战路线图
产业智能官
39+阅读 · 2019年9月10日
工行基于MySQL构建分布式架构的转型之路
炼数成金订阅号
15+阅读 · 2019年5月16日
ML通用指南:文本分类详细教程(上)
论智
19+阅读 · 2018年7月29日
【知识图谱】 一个有效的知识图谱是如何构建的?
产业智能官
56+阅读 · 2018年4月5日
【机器学习】基于TensorFlow搭建一套通用机器学习平台
文本聚类:从非结构化数据快速获取见解
Datartisan数据工匠
15+阅读 · 2017年10月12日
A Survey on Bayesian Deep Learning
Arxiv
60+阅读 · 2020年7月2日
Arxiv
14+阅读 · 2020年2月6日
Arxiv
5+阅读 · 2019年9月25日
Arxiv
5+阅读 · 2017年7月23日
VIP会员
相关资讯
知识图谱的自动构建
DataFunTalk
55+阅读 · 2019年12月9日
【数字化】制造业数字化转型的实战路线图
产业智能官
39+阅读 · 2019年9月10日
工行基于MySQL构建分布式架构的转型之路
炼数成金订阅号
15+阅读 · 2019年5月16日
ML通用指南:文本分类详细教程(上)
论智
19+阅读 · 2018年7月29日
【知识图谱】 一个有效的知识图谱是如何构建的?
产业智能官
56+阅读 · 2018年4月5日
【机器学习】基于TensorFlow搭建一套通用机器学习平台
文本聚类:从非结构化数据快速获取见解
Datartisan数据工匠
15+阅读 · 2017年10月12日
Top
微信扫码咨询专知VIP会员