这一章,我们以我自己从事的推荐算法为例,来给大家讲讲工业是如何应用机器学习算法的。主要想让大家明白,这工业应用究竟是怎么跑通的,和自己在学校跑的小数据集有什么区别。
3-1 工程基础:线上系统,代码先行
第一节,先来讨论一下算法工作的基础工程能力。
在本节中,我不会过分深入工程能力的细节,而是希望能够帮你理解一个核心问题:工程能力是一切算法工程师的基础。没有工程,一切都是空谈。以及,我希望能够在本篇大概解释一些工程相关的术语。在后续的行文中,如果这些术语再次出现的,我将会默认大家都已经了解,不再做解释。
基础
那么成为一个算法工程师需要哪些最基础的工程能力呢?那自然是基础的代码能力。
代码能力
- 趁手的编程语言。Online模块的C++/Java,offline模块的python最好都能熟悉。至少相关语言的标准库,基本语法得会使用。我面试过有些发过论文的同学,结果Python的class都不会定义。真的是只会写TensorFlow了么。
- 数据结构算法。对代码效率有概念。对常见数据结构及其简单实现有概念,知道这些数据结构的查找插入操作的常见操作的复杂度是怎样的。比如你得至少得知道python的dict背后是哈希表。这样你就能知道它虽然可以去重,但会乱序。
- 代码实现能力。有时候有的代码题不会考察代码实现的具体算法,而是在知道应该怎么计算的情况下把它实现出来。这是代码实现的能力。常见的函数定义和封装,一些细节如参数的值传递or引用传递都属于此类。实际上是能不能把脑子里的想法翻译成代码实现的能力。
工作场景
下面我以信息流推荐文章场景为例,大致讲一下推荐系统的工程架构。
目前,大多数的推荐系统,都可以分为online和offline两个模块。
online场景讲求效率,一般需要C++或者Java等效率高的编程语言,主要的应用场景是:需要在比如1s的时间内,从海量,比如一千万可推荐的文章中找到用户当下最喜欢那些文章,返回给用户。Online模块通常会包括召回和排序两个大的模块。
而offline模块就是一个笼统的称呼:不在online里面包含的工作,都属于offline的模块。offline的代码语言一般以python等脚本语言为主。使用场景包括但不限于,用户日志的收集与分析、模型的训练、作者新发文进入推荐系统等等。
从工程角度说,主要需要强调一下代码效率,尤其是online模块的代码效率。
代码效率,除了标识着一个码农的专业性以及减少公司的机器开销之外,对于算法工程师而言,在online场景下,代码效率与推荐效果也是挂钩的。
为什么有这个挂钩呢?因为系统排序阶段增加预估条数,一般是可以获得推荐效果的提升的。之所以不全部预估,实际上就是一个延迟和效果的平衡。所以假设你的模块代码写的不好,增加了10ms延时,其实可以兑换成假设排序吃了你这10ms延迟而可以多预估一些文章,带来效果上的收益。而优化代码效率,相应的也可以转化成模型效果指标上的收益,这种收益有时候比模型迭代效果提升容易得多。
工作中80%场景代码效率提升一般都是一些看起来很愚蠢的原因导致的,比如参数的值传递和引用传递、冗余的拷贝构造等。所以,一些基础的细节一定要多注意。这些细节,实际上也是面试喜欢问的,也是为什么算法工程师的面试会那么强调代码能力的原因。
此外,从工程角度,强调一些代码套路也是必要的。比如一些经典的设计模式的原则,详细的可以参考《设计模式》,我这里只列出我觉得比较重要的几个原则。
- DRY(Don’t Repeat Yourself),不要重复你自己。意为在系统中,最好某一块逻辑,在系统中只有一个模块可以对齐进行控制。不要把相同的逻辑重复多遍。这样很容易出现修改只改了一个地方,导致出现不符合预期的情况。这个原则在online serving时候和offline training时候的feature一致性有体现。尽可能的把feature抽取逻辑设计在一处可以最大程度的避免online/offline不一致的问题。
- KISS(Keep It Simple, Stupid),注重简约,别人看了就能用。简单说,如果你自己开发的代码模块,在每次做操作前自己都要check上两三遍,那这个地方大概率是不符合要求的,需要重新设计。这种可以最大限度的排除那些,实验某个策略没有效果的时候,到底是因为策略本身没效果,还是代码没有生效。
代码的组织能力。工作中的代码量显然是比自己平时练手大很多的,对于那些没有大量代码经验的同学。推荐《重构》《代码大全》这两本书。让自己的代码尽可能的更好维护,也更容易让他人读懂。
工具
下面,我来列举算法工程师工作中常用到的一些依赖工具。如果你对这些工具不熟悉的话,可以先自行google了解一下。
- HDFS: 对HDFS有基础的了解是必要的。现在的数据肯定都不是单机能带起来的了。
- kafka: 流式数据的载体。本质上是一个消息队列。
- MapReduce/Spark: 经典的HDFS Batch运算框架。近些年spark这个计算框架已经愈发成熟,成为主流的大数据场景的计算框架了,已经很少有MR能做的,而Spark做不了的任务了。
- 思考题: Spark为什么会比MapReduce快?
- Flink/Spark Streaming: 现在比较流行的流式计算框架。目前好像Flink的势头更猛一点。主要看公司整体的架构的选择。
- 出于对用户反馈实时性的考虑,算法工程师处理流式数据的场景目前越来越多了,会有比较多的应用场景。
- Hbase/redis: 大型Key-Value 存储模块。需要了解日常的交互以及这两个模块的主要功能。
- Protobuf/Thrift: Protobuf现在应该是业界比较通用的数据序列化的模块。而Thrift本身除了自身具有的序列化能力之外,通常也会作为Online模块内部RPC调用的一种协议。
推荐资料
- 对于设计模式没有概念的同学,可以先看《Head First 设计模式》Head First 设计模式(中文版) (豆瓣)
- 《设计模式》: 有点枯燥,但是经典 设计模式 (豆瓣)
- 《重构》: 可读性比较强。重构 (豆瓣)
- 《代码大全》: 书很厚,可以跳着读 代码大全(第2版) (豆瓣)
- 《设计数据密集型应用》英文名ddia。开源中译本,翻译的不错。一本书读懂数据相关的基础知识。第四章:编码与演化 · ddia-cn