本文已参与「新人创作礼」活动,一起开启掘金创作之路。
1、什么是在线学习
在线学习是深度学习中模型的一种训练方法,说到在线学习,与之相对的就是离线学习。离线学习是指所有的训练数据都可以获取到,通过shuffle后以批数据的方式离线训练模型,只有在模型离线训练完成后方可用于预测。在线学习则不同,它可以通过线上流式的数据在线更新模型,根据实时得到的一个或一批数据样本来调整模型,可以及时的捕获数据的变化,有效的提高模型的更新频率。在线学习在推荐广告领域有着广泛的应用。
在线学习需要具备以下几个条件:
- 数据从流式数据源产生,例如Kafka、MQ;
- 可以对批数据甚至单样本训练并产生新模型;
- 特征和标签可以实时生成并实时拼接成样本。
2、在线学习的训练方法
在online learning中,每次获取到一个数据样本,需要计算当前模型的loss,然后求梯度更新模型。在离线学习中,随机梯度下降(SGD)是最常用的优化方法,在在线学习中使用随机梯度下降作为优化方法最大的问题是不能有效的产生稀疏解。即便加入L1正则化,由于浮点运算,也很难出现真正的零值。在大规模数据和高纬度特征中,稀疏解可以有效减少推理时的内存和计算量。
为了得到稀疏解,一种思路是当权重足够小时,直接将权重截断为0,这种粗暴的方式可能会导致训练不完全的问题。而后提出的FOBOS、RDA、FTRL等优化算法成为在线学习中常用的优化算法,下图是在线学习的优化算法的关系:
目前FTRL算法是应用最为广泛的算法,是由谷歌团队提出,并且在2013年发表了一篇工程化的paper,伪代码如下:
FTRL不仅可以解决LR的优化问题,更成为了一种通用的优化算子,它综合考虑了RDA和FOBOS的梯度和正则方式,代码实现如下:
class ftrl_model_unit
{
public:
double wi;
double w_ni;
double w_zi;
ftrl_model_unit()
{
wi = 0.0;
w_ni = 0.0;
w_zi = 0.0;
}
ftrl_model_unit(double mean, double stddev)
{
wi = utils::gaussian(mean, stddev);
w_ni = 0.0;
w_zi = 0.0;
}
}
void ftrl_trainer::train(int y, const vector<pair<string, double> > &x)
{
ftrl_model_unit *thetaBias = pModel->getOrInitModelUnitBias();
vector<ftrl_model_unit *> theta(x.size(), nullptr);
int xLen = x.size();
for (int i = 0; i < xLen; ++i) {
const string &index = x[i].first;
theta[i] = pModel->getOrInitModelUnit(index); // 获取相应的 ftrl_model_unit
}
for (int i = 0; i <= xLen; ++i) {
ftrl_model_unit &mu = i < xLen ? *(theta[i]) : *thetaBias;
if (fabs(mu.w_zi) <= w_l1)
mu.wi = 0.0;
else {
mu.wi = (-1) *
(1 / (w_l2 + (w_beta + sqrt(mu.w_ni)) / w_alpha)) *
(mu.w_zi - utils::sgn(mu.w_zi) * w_l1); // 更新 wi
}
}
double bias = thetaBias->wi;
double p = pModel->forecast(x, bias, theta); // 计算 f(x)
double mult = y * (1 / (1 + exp(-p * y)) - 1);
for (int i = 0; i <= xLen; ++i) {
ftrl_model_unit &mu = i < xLen ? *(theta[i]) : *thetaBias;
double xi = i < xLen ? x[i].second : 1.0;
double w_gi = mult * xi; // 更新 gi
double w_si = 1 / w_alpha * (sqrt(mu.w_ni + w_gi * w_gi) - sqrt(mu.w_ni));
mu.w_zi += w_gi - w_si * mu.wi; // 更新 zi
mu.w_ni += w_gi * w_gi; // 更新 ni
}
}
3、在线学习的应用
(1)美团 Online Learning算法理论与实践 - 美团技术团队 (meituan.com)
(2)腾讯 一文搞懂CTR建模 (qq.com)
(3)饿了么 「回顾」饿了么推荐算法演进及在线学习实践 (qq.com)
4、在线学习存在的问题
在online learning中,由于是实时学习新产生的数据,这会导致学习的局部的样本数据分布和全局的数据分布有差异,例如用户的行为数据在一天之内是不同的,上午的数据分布和下午的数据分布存在差异,那么模型在学习完上午的数据后,会对下午的请求不能很好的处理。
在腾讯这篇文章中针对上述问题给出了三种解决方法,现记录总结如下:
(1)参数冻结
Embedding层和复杂的交叉结构(如 FC、Cross 等结构)学习的是不同的信息,Embedding层侧重于user/item的表征,交叉结构主要学习的是user与item的交叉信息(如“男性喜欢玩枪战游戏”)。数据分布的变化主要体现在tem/User分布的变化,用户的偏好较为稳定,并且在离线模型中,模型已经用大量的数据,比较好的训练了交叉结构。
因此在在线训练时,冻结模型的 FC 层、Cross 层等交叉结构的参数,令其不进行更新,仅使用实时样本更新 Embedding 层的参数。缺点是牺牲了模型的学习能力。
(2)样本回放
冻结参数后,模型的效果在一天内也不稳定,为此可以选取历史的离线样本一同参与训练,离线样本的选取策略为:过去一天当前小时以后的样本进行采样,与实时样本按照一定比例混合。比如现在是下午 1 点,那实时样本都是 1 点左右的样本,而 1 点之前的样本模型已经训练过了,但是模型不知道 1 点之后的数据会是什么样子。所以我们就从昨天的样本中选取 1 点之后的样本加入进去。
(3)蒸馏学习
参数冻结需要牺牲模型的学习能力,样本回放会牺牲模型的实时性。蒸馏学习的方法是用 teacher 模型来影响student模型,让其学习teacher模型的信息。离线模型作为teacher,实时训练模型作为 student,在 loss 上除了 student 的 Logloss 损失,添加了 teacher 与 student 之间的交叉熵损失,让student不要离teacher太远。
【参考】