目录
工业算法实践不像高校追求理论创新,更关注算法效果。这也决定了算法不单单是数据+模型,也要综合考虑产品形态、业务策略和工程架构。我做算法功底也不深,但也想结合自己工作和身边同事在实践中遇到坑,跟大家一起分享交流、进步。
产品形态
首先我们要明确公司的产品主要服务对象是谁?
这很重要!!!
举个例子,一家2c公司care的是用户体验,而2b公司更care对接商家满意度。这也决定两种不同类型公司关注的业务指标是完全不同的,前者KPI考核的目标是dau、mau、用户留存、uv转化等,后者KPI考核的目标是商家拉新、转化等。
也许你会认为两者不就是换个对象吗?把商家替换成用户,其实完全不是这样。
需求和挑战
国内平台为王,c端客户诉求和反馈往往被忽视,没有b端那么积极有效。这对c端公司技术和算法来说也算件好事,可以按部就班和产品一起做优化;而b端的技术和算法就有点惨了,工程和算法设计但凡一点不合理,就会被商家倒逼整改,更别提很多商家不合理的要求。所以做b端的算法一定要有颗被挑战的大心脏,能够积极应对各种需求。
规范性
c端往往有一套适用于整个公司产品矩阵的业务规范,算法能够快速获取足够丰富的用户画像和交互行为数据;而b端各个商家参差不齐,业务规范不统一,算法要多留心业务复杂性带来数据上的坑,主要集中在数据稀疏和大量缺失。
优化约束
c端约束条件一般较弱或者不存在,而b端约束较强,如商家入驻和审核都需要满足较高条件,且产品规格和尺寸是强制性约束。至于优化目标,c端优化场景单一,目标性更强,大模型(end to end)往往能收获奇效,而b端优化场景多且链路长,涉及不同场景目标间的博弈,局部+全局目标要兼顾。
业务策略
算法不是万能的,很多优化需要策略去补充。
在信息feed流推荐中,用户在与产品深度交互后,看到的内容如资讯和视频越来越单一,这从推荐算法本身来说是好的,说明推荐准确性高;但对于用户体验来说,用户慢慢趋于审美疲劳,不利于用户留存。这需要做EE或打散重排策略,去打破现状。这就是业务策略很要的一个应用case。
再举一例,用户每次请求,rank模型都会重新刷出top K(电商购物K=20),用户不一定下拉list底部,这时候请千万区分好展示曝光和真实曝光,uv去重策略要去真实曝光。
其实,业务策略是对算法额外补充,或者在算法开发周期较长时,尽快对现有业务做的简单优化,对于提升效果是非常有帮助的。种类有很多,如拉新、反作弊、人气分、加价等,需要根据场景适配。
工程架构
算法要落地,必须要在现有的工程架构上造轮子。这也是很多算法头疼的事情。
不管是搜索推荐还是计算广告,首先要和业务后端做好模块对接,明确算法所涉及的应用和服务,越细越好。具体可以从以下几个方面考虑:
数据流
算法依赖数据是那些?
实时数据还是离线数据?
有没有做数据校验?
算法输出是什么?
流量分发
做AB测试流量是否足够?
是否是正交独立实验,不受上游业务影响?
AB测试数据怎么回收?
降级
算法输出被业务规则屏蔽或线上故障,怎么走降级方案?
降级后怎么排查和恢复?
服务告警
邮件、短信等通知线上故障
数据特征
数据决定算法上限,模型只是无限逼近这一上限。好的数据和特征,会极大提升算法效果。
常见数据的坑
数据归一化
zero-center ,这个挺常用的,主要有:
- X -= np.mean(X, axis = 0) # zero-center
- X /= np.std(X, axis = 0) # normalize
PCA whitening,这个用的比较少。
数据缺失
数据缺失会给模型带来噪声干扰,目前比较常用的处理办法就是补全或过滤但实际效果不怎么好,建议更粗粒度定量。
数据稀疏
比较常见,处理方法也比较多,如one-hot、embedding、更粗粒度定量。
数据不置信
以概率方式描述,如威尔逊置信、偏置等。
第三方数据校验和统一
第三方数据能够提供大量丰富数据,固然很好,但需要做好数据准确性校验,并进行数据格式统一。
模型调参
数据量级
在数据集很大的情况下,一上来就跑全量数据。建议先用 1/100、1/10 的数据跑一跑,对模型性能和训练时间有个底,外推一下全量数据到底需要跑多久。在没有足够的信心前不做大规模实验。
tying input & output embedding(就是词向量层和输出 softmax 前的矩阵共享参数,在语言模型或机器翻译中常用)时学习率需要设置得非常小,不然容易 Nan。
tensorboard
以前不怎么用,用了之后发现太有帮助,帮助你监视网络的状态,来调整网络参数。
参数随机初始化
参数初始化很重要,它决定了模型的训练速度与是否可以躲开局部极小。relu激活函数初始化推荐使用He normal,tanh初始化推荐使用Glorot normal,其中Glorot normal也称作Xavier normal初始化。
初始学习率
有时受 batch size、sequence length 各种因素的影响,loss 很大(比如说好几万),对于这种数字人是没有数感的,建议首先计算一下 per token loss(如果是多任务,可以每个任务单独算;类似地,某些 CV 任务可以计算 per pixel loss),心里有点感觉。脱离损失函数的形式谈学习率没有意义(例如单是对 batch size 求和或者取平均这个差异就会使梯度差成百上千倍)。
在确定初始学习率的时候,从一个很小的值(例如 1e-7)开始,然后每一步指数增大学习率(例如扩大1.05 倍)进行训练。训练几百步应该能观察到损失函数随训练步数呈对勾形,选择损失下降最快那一段的学习率即可。
激活函数选择
常用的激活函数有relu、leaky-relu、sigmoid、tanh等。对于输出层,多分类任务选用softmax输出,二分类任务选用sigmoid输出,回归任务选用线性输出。而对于中间隐层,则优先选择relu激活函数(relu激活函数可以有效的解决sigmoid和tanh出现的梯度弥散问题,多次实验表明它会比其他激活函数以更快的速度收敛)。另外,构建序列神经网络(RNN)时要优先选用tanh激活函数。
学习率设定
一般学习率从0.1或0.01开始尝试。学习率设置太大会导致训练十分不稳定,甚至出现Nan,设置太小会导致损失下降太慢。学习率一般要随着训练进行衰减。衰减系数设0.1,0.3,0.5均可,衰减时机,可以是验证集准确率不再上升时,或固定训练多少个周期以后自动进行衰减。
防止过拟合
一般常用的防止过拟合方法有使用L1正则项、L2正则项、dropout、提前终止、数据集扩充等。如果模型在训练集上表现比较好但在测试集上表现欠佳可以选择增大L1或L2正则的惩罚力度(L2正则经验上首选1.0,超过10很少见),或增大dropout的随机失活概率(经验首选0.5);或者当随着训练的持续在测试集上不增反降时,使用提前终止训练的方法。当然最有效的还是增大训练集的规模,实在难以获得新数据也可以使用数据集增强的方法,比如CV任务可以对数据集进行裁剪、翻转、平移等方法进行数据集增强,这种方法往往都会提高最后模型的测试精度。
优化器选择
如果数据是稀疏的,就用自适应方法,即 Adagrad, Adadelta, RMSprop, Adam。整体来讲,Adam 是最好的选择。SGD 虽然能达到极小值,但是比其它算法用的时间长,而且可能会被困在鞍点。如果需要更快的收敛,或者是训练更深更复杂的神经网络,需要用一种自适应的算法。
残差块与BN层
如果你希望训练一个更深更复杂的网络,那么残差块绝对是一个重要的组件,它可以让你的网络训练的更深。
BN层具有加速训练速度,有效防止梯度消失与梯度爆炸,具有防止过拟合的效果,所以构建网络时最好要加上这个组件。
自动调参方法
Grid Search
网格搜索,在所有候选的参数选择中,通过循环遍历,尝试每一种可能性,表现最好的参数就是最终的结果。其原理就像是在数组里找最大值。缺点是太费时间了,特别像神经网络,一般尝试不了太多的参数组合。
Random Search
经验上,Random Search比Gird Search更有效。实际操作的时候,一般也是先用Gird Search的方法,得到所有候选参数,然后每次从中随机选择进行训练。另外Random Search往往会和由粗到细的调参策略结合使用,即在效果比较好的参数附近进行更加精细的搜索。
Bayesian Optimization
贝叶斯优化,考虑到了不同参数对应的 实验结果值,因此更节省时间,贝叶斯调参比Grid Search迭代次数少, 速度快;而且其针对非凸问题依然稳健。
参考资料
https://www.zhihu.com/question/41631631/answer/776852832