前言
笔者是名半吊子算法工程师,毕业专业是物理类学科,标准半路出家。所以,基础薄弱,技术能力颇为捉急。到现在,从事推荐算法岗位也算有一段时日,勉强积攒了一些关于这一领域的浅显知识,不少还是谬误。因此,有空就来分享一下,共同交流,共同进步。错误之处,恳请大家不吝指出,感谢。
摘要
为了更好地说明推荐算法,个人觉得还是应该简单的花点篇幅介绍一下推荐原理,避免后期的博文一大堆公式,让人不知道怎么来的。下面,将结合简单的一元方程,给出简单的推导。
示例
例如,有个电阻值未知的电阻。我们进行了一次实验,电压值设为
U
1
U_{1}
U1,测得电流值为
I
1
I_{1}
I1。请问,不考虑其他电阻情况下,该未知电阻的电阻值是?以上均为国际单位。
回答:
基于初中的物理知识里的欧姆定律,我们可知,
U
=
I
∗
R
U=I\ast R
U=I∗R,
其中,
U
U
U为电压,
I
I
I为电流,
R
R
R为电阻。 因此,可列出公式:
I
1
∗
R
=
U
1
\\I_{1}\ \ast R\ =\ U_{1}
I1 ∗R = U1
非常简单的一元方程,根据小学知识可以算得,
R
=
U
1
/
I
1
\\R=U_{1}\ /\ I_{1}
R=U1 / I1
这个是准确解,测量一次即可获得结果。
然而(不然而的话,后面的段落就没法引出来了,笑),假设由于仪器测量的精度,我们记录的数值是有误差的,并且误差是无偏的,也就是说,测量越多,误差的整体偏差越小。因此,我们测量了多次结果,也就是
(
U
1
,
I
1
)
\left( U_{1},\ I_{1}\right)
(U1, I1),
(
U
2
,
I
2
)
\left( U_{2},\ I_{2}\right)
(U2, I2), …,
(
U
N
,
I
N
)
\left( U_{N},\ I_{N}\right)
(UN, IN)。从而,可以定义一个整体误差函数:
D
e
v
a
t
i
o
n
(
R
)
=
∑
i
=
1
N
(
I
i
∗
R
−
U
i
)
2
/
2
Devation(R)\ =\ \sum^{N}_{i=1} \left( I_{i}\ \ast R-U_{{}_{i}}\right)^{2}/\ 2
Devation(R) = ∑i=1N(Ii ∗R−Ui)2/ 2
其中,除以2这一部分纯粹是为了后面的公式好看,并没有什么影响。
可以轻易看出,这是一个凸函数(标准抛物线,二次导数恒
>
=
0
>=0
>=0)。当其取到最小值时,此时对应的
R
R
R的值就是我们的最优结果。
所以,对其求导取极值点即可:
∂
D
e
v
a
t
i
o
n
/
∂
R
=
0
\ \ \ \ \ \ {\partial Devation}/{\partial R}\ =\ 0
∂Devation/∂R = 0
⇒
∑
i
=
1
N
(
I
i
∗
R
−
U
i
)
∗
I
i
=
0
\Rightarrow\sum^{N}_{i=1} \left( I_{i}\ \ast R-U_{{}_{i}}\right)\ast I_{i}\ =\ 0
⇒∑i=1N(Ii ∗R−Ui)∗Ii = 0
⇒
R
=
∑
i
=
1
N
(
I
i
∗
U
i
)
/
∑
i
=
1
N
I
i
2
\Rightarrow R\ =\ \sum^{N}_{i=1} (I_{i}\ast U_{i} )/ \sum^{N}_{i=1} I_{i}^{2}
⇒R = ∑i=1N(Ii∗Ui)/∑i=1NIi2
非常简单。实际上,这就是最小二乘法的解。(即
A
T
A
X
→
=
A
T
Y
→
A^{T}A\overrightarrow{X} =A^{T}\overrightarrow{Y}
ATAX=ATY)
我们也可以拓展一下,当存在两个未知数呢?
也就是
U
=
I
∗
R
+
B
U=I\ast R +B
U=I∗R+B, 其中B是个固定偏差,也是未知的。则整体误差函数为:
D
e
v
a
t
i
o
n
(
R
)
=
∑
i
=
1
N
(
I
i
∗
R
+
B
−
U
i
)
2
/
2
Devation(R)=\sum^{N}_{i=1} \left( I_{i}\ \ast R+B-U_{{}_{i}}\right)^{2}/\ 2
Devation(R)=∑i=1N(Ii ∗R+B−Ui)2/ 2
一样的,对其求导。并且,考虑到两个变量是独立不相关的,转为求各自的偏导(因为各自都取到极值时,合在一起的值也自然是极值。类似于,语文课得到了满分,数学课也是,所以两门课总分的满分也是这两个满分的叠加):
∂
D
e
v
a
t
i
o
n
/
∂
R
=
0
\ \ \ \ \ \ {\partial Devation}/{\partial R}=0
∂Devation/∂R=0
∂
D
e
v
a
t
i
o
n
/
∂
B
=
0
\ \ \ \ \ \ {\partial Devation}/{\partial B}=0
∂Devation/∂B=0
⇒
∑
i
=
1
N
(
I
i
∗
R
+
B
−
U
i
)
∗
I
i
=
0
\Rightarrow\sum^{N}_{i=1} \left( I_{i}\ast R+B-U_{i}\right)\ast I_{i}=0
⇒∑i=1N(Ii∗R+B−Ui)∗Ii=0
∑
i
=
1
N
(
I
i
∗
R
+
B
−
U
i
)
=
0
\ \ \ \ \ \sum^{N}_{i=1} \left( I_{i}\ast R+B-U_{i}\right) =0
∑i=1N(Ii∗R+B−Ui)=0
⇒
R
∗
∑
i
=
1
N
I
i
2
+
B
∗
∑
i
=
1
N
I
i
=
∑
i
=
1
N
U
i
I
i
\Rightarrow R\ast\sum^{N}_{i=1}I_{i}^{2}+B\ast \sum^{N}_{i=1}I_{i}=\sum^{N}_{i=1}U_{i}I_{i}
⇒R∗∑i=1NIi2+B∗∑i=1NIi=∑i=1NUiIi
R
∗
∑
i
=
1
N
I
i
+
B
∗
N
=
∑
i
=
1
N
U
i
\ \ \ \ \ \ R \ast\sum^{N}_{i=1} I_{i}+B\ast N=\sum^{N}_{i=1}U_{i}
R∗∑i=1NIi+B∗N=∑i=1NUi
⇒
R
=
N
∗
∑
i
=
1
N
(
U
i
I
i
)
−
∑
i
=
1
N
U
i
∑
i
=
1
N
I
i
N
∗
∑
i
=
1
N
I
i
2
−
(
∑
i
=
1
N
I
i
)
2
\Rightarrow R=\frac{N \ast \sum^{N}_{i=1}(U_{i}I_{i})-\sum^{N}_{i=1}U_{i}\sum^{N}_{i=1}I_{i}}{N \ast\sum^{N}_{i=1}I_{i}^{2}-(\sum^{N}_{i=1}I_{i})^{2}}
⇒R=N∗∑i=1NIi2−(∑i=1NIi)2N∗∑i=1N(UiIi)−∑i=1NUi∑i=1NIi
B
=
∑
i
=
1
N
U
i
∑
i
=
1
N
I
i
2
−
∑
i
=
1
N
(
U
i
I
i
)
∑
i
=
1
N
I
i
N
∗
∑
i
=
1
N
I
i
2
−
(
∑
i
=
1
N
I
i
)
2
\ \ \ \ \ B=\frac{\sum^{N}_{i=1}U_{i}\sum^{N}_{i=1}I_{i}^{2}-\sum^{N}_{i=1}(U_{i}I_{i})\sum^{N}_{i=1}I_{i}}{N\ast\sum^{N}_{i=1}I_{i}^{2}-(\sum^{N}_{i=1}I_{i})^{2}}
B=N∗∑i=1NIi2−(∑i=1NIi)2∑i=1NUi∑i=1NIi2−∑i=1N(UiIi)∑i=1NIi
还是最小二乘法的解。
推荐的简化思路:
实际上,这个就是推荐算法的简化思路(很不准确,但大概意思到了)。即,通过定义一个误差函数(在算法里称为损失函数,常为凸函数),我们通过“调整”变量的值,使得损失函数值尽量往变小的方向上去迭代。当损失函数到达最小值时,我们就可以认为,此时的变量值就是让整体误差最小的最优解。
这里也要额外提一下,这个误差函数是基于反馈变量(电流
I
{I}
I)而给出来的,也就是说,我们的自变量是电压
U
{U}
U,电流
I
{I}
I是个反馈值。这种反馈值,在推荐算法里面称为“标签”。
基于“标签”的模型生成,被称为“监督学习”。如果“标签”的值是连续数值,则称为“回归”;“标签”的值为离散(即类别),则称为“分类”。实际场景里,被提及的大多数都是“监督学习”,非监督学习(就是没有反馈值)的具体思路,这里不做探讨了。
梯度下降
话说回来,在上面的示例中,明明最优解是可以直接通过解方程来算出来的,为何简化思路里需要提到“调整”这一操作呢?
原因在于,实际场景下,考虑到数据量级 + 变量维度,会产生极高维度的方程组,会导致计算机的节点压力超过负荷(也就是无法计算)以及其他可能的问题。并且,这种方法每次都需要聚合历史数据+新数据,展开重新计算,压力会越来越大。
因此,能够简化这类问题的梯度下降法,是个很合适的选择。根据下图(单变量凸函数)可以看出,当在极值点左侧时,导数值(此时,就是切线的斜率,也是梯度)为负值;在极值点右边时,导数值为正值。
因此,随机初始化变量值,然后基于累积数据对应的梯度,对变量值进行反向变化,
X
n
e
x
t
=
X
n
o
w
−
η
∗
G
r
a
d
i
e
n
t
X_{next}=X_{now}-\eta \ast Gradient
Xnext=Xnow−η∗Gradient(变化的幅度
η
\eta
η,称为“学习率”),直到梯度值趋于0时为止。由于凸函数的特性,梯度值趋于0时,此时的极值就是全局最优解。补充一下,梯度值在实际做法里是可以调整为只基于新数据进行迭代计算的(无需重复聚合)。
所以,可以遍历训练数据,每次一条或者几条(batch模式),迭代变量值,直到遍历结束。实际场景下,很多公司也也是采取这种方法。
顺便补充一下,上面的梯度下降法思路,是比较简单的直观思路,但并非基于函数优化的方法出发的,考虑到后续章节的延伸,这边还是贴出优化函数:
g
_
n
o
w
→
∗
x
→
+
1
2
η
∗
∣
∣
x
→
−
x
_
n
o
w
→
∣
∣
2
2
\ \ \ \ \ \ \ \overrightarrow{g\_now} \ast\overrightarrow{x}+\frac{1}{2\eta}\ast||\overrightarrow{x}-\overrightarrow{x\_now}||_{2}^{2}
g_now∗x+2η1∗∣∣x−x_now∣∣22
这里的
∣
∣
A
→
∣
∣
2
2
||\overrightarrow{A}||_{2}^{2}
∣∣A∣∣22符号,是指对
A
→
\overrightarrow{A}
A向量的各维度值的平方和。
∣
∣
∣
m
|||{}_{m}
∣∣∣m是指
∑
i
=
1
N
A
i
m
m
\sqrt[m]{\sum^{N}_{i=1}A_{i}^{m}}
m∑i=1NAim,其中
N
N
N是
A
→
\overrightarrow{A}
A向量的维度大小,因此,
∣
∣
∣
m
|||{}_{m}
∣∣∣m本质上就是取模操作。而,
x
_
n
o
w
→
\overrightarrow{x\_now}
x_now是当前的变量值,
g
_
n
o
w
→
\overrightarrow{g\_now}
g_now是当前梯度值。而
x
→
\overrightarrow{x}
x则是未知量,也就是说,对其进行求偏导。
因此,对优化函数进行求导(各维度分别求偏导),得
⇒
g
_
n
o
w
i
+
1
η
∗
(
x
i
−
x
_
n
o
w
i
)
=
0
\Rightarrow g\_now_{i} + \frac{1}{\eta}\ast(x_{i}-x\_now_{i})=0
⇒g_nowi+η1∗(xi−x_nowi)=0
⇒
x
i
=
x
_
n
o
w
i
−
η
∗
g
_
n
o
w
i
\Rightarrow x_{i}=x\_now_{i} -\eta\ast g\_now_{i}
⇒xi=x_nowi−η∗g_nowi
合并各维度,即
x
→
=
x
_
n
o
w
→
−
η
∗
g
_
n
o
w
→
\overrightarrow{x}=\overrightarrow{x\_now} -\eta\ast \overrightarrow{g\_now}
x=x_now−η∗g_now
正则项
在实际工作里,基于监督标签所建立的损失函数,往往效果是不太尽如人意。很多时候,模型对于训练数据里的预测结果,正确率很高,但对于测试数据,正确率反而偏低。这个就是老生常谈的“过拟合”现象。
如下图所示:
最右端即为过拟合。
基于实际生活里的“奥卡姆剃刀”哲理(简单的模型更符合真实,例如万有引力等定律中的2次方,哥白尼的日心说,欧姆定律,质能方程、电磁方程等),我们认为,影响结果的维度(敏感变量)是比较少的,也就是说,其他大部分变量的权重值应该很小或者是0值。因此,我们引入正则项:
可以看出,正则项函数也都是凸函数。通过对损失函数添加正则项,相对而言,可以较好的抑制过拟合问题。具体原理,有兴趣的可以查询一下,由于笔者水平有限,也就不做摘抄。
后续章节
下一章节,将会对计算广告进行介绍,着重广告推荐的部分。
PS: 相关图片为网上搜集,若有侵权,通知删除。