在本节中,我们将基于内容的推荐算法,与基于协同过滤的推荐算法,集合在一起,形成深度学习推荐算法,同时为了提高运行效率,我们还将介绍算法的向量表示。
在这里我们希望同时学习学生对题目需要程度的参数集{θ(1),θ(2),...,θ(nu)} { θ ( 1 ) , θ ( 2 ) , . . . , θ ( n u ) } 和每道题目的特征向量{x(1),x(2),...,x(nm)} { x ( 1 ) , x ( 2 ) , . . . , x ( n m ) } ,我们可以将上两节的代价函数进行合并,上两节的代价函数如下所示:

J(θ(1),θ(2),...,θ(nu))=12∑j=1nu∑i:r(i,j)=1(θ(j)2x(i)−y(i,j))2+λ2∑j=1nu∑k=1nθ(j)k2 J ( θ ( 1 ) , θ ( 2 ) , . . . , θ ( n u ) ) = 1 2 ∑ j = 1 n u ∑ i : r ( i , j ) = 1 ( θ ( j ) 2 x ( i ) − y ( i , j ) ) 2 + λ 2 ∑ j = 1 n u ∑ k = 1 n θ k ( j ) 2


J(x(1),x(2),...,x(nm))=12∑i=1nm∑i:r(i,j)=1(θ(j)2x(i)−y(i,j))2+λ2∑i=1nm∑k=1nx(i)k2 J ( x ( 1 ) , x ( 2 ) , . . . , x ( n m ) ) = 1 2 ∑ i = 1 n m ∑ i : r ( i , j ) = 1 ( θ ( j ) 2 x ( i ) − y ( i , j ) ) 2 + λ 2 ∑ i = 1 n m ∑ k = 1 n x k ( i ) 2


我们先来分析第一项也就是平方和叠加项,第一个公式的意思是对每个用户,求出该用户所有打过分的影片,求出计算出的打分与用户实际打分之间误差的平方和,第二个公式的意思是对每部影片,求出所有对这部影片打过分的用户,求出计算出的打分与用户实际打分之间误差的平方和。实际上,这两个求误差平方和项计算出来的结果都是所有打过分的影片,计算的打分值与实际打分值之差的平方和,即这两项都等价于:


∑i:r(i,j)=1(θ(j)2x(i)−y(i,j))2 ∑ i : r ( i , j ) = 1 ( θ ( j ) 2 x ( i ) − y ( i , j ) ) 2


所以我们得到新的代价函数公式为:


J(x(1),x(2),...,x(nm),θ(1),θ(2),...,θ(nu))=∑i:r(i,j)=1(θ(j)2x(i)−y(i,j))2+λ2∑i=1nm∑k=1nx(i)k2+λ2∑j=1nu∑k=1nθ(j)k2 J ( x ( 1 ) , x ( 2 ) , . . . , x ( n m ) , θ ( 1 ) , θ ( 2 ) , . . . , θ ( n u ) ) = ∑ i : r ( i , j ) = 1 ( θ ( j ) 2 x ( i ) − y ( i , j ) ) 2 + λ 2 ∑ i = 1 n m ∑ k = 1 n x k ( i ) 2 + λ 2 ∑ j = 1 n u ∑ k = 1 n θ k ( j ) 2


我们学习算法要优化的目标就变为:


minx(1),x(2),...,x(nm)θ(1),θ(2),...,θ(nu)J(x(1),x(2),...,x(nm),θ(1),θ(2),...,θ(nu)) min x ( 1 ) , x ( 2 ) , . . . , x ( n m ) θ ( 1 ) , θ ( 2 ) , . . . , θ ( n u ) J ( x ( 1 ) , x ( 2 ) , . . . , x ( n m ) , θ ( 1 ) , θ ( 2 ) , . . . , θ ( n u ) )


与之前算法不同的是,我们这里不再需要传统线性回归

y=wx+b y = w x + b 因为根据上一节内容, θ(0)=0 θ ( 0 ) = 0 ,所以我们不再需要向 x x 和 θ θ 上添加第0维了,因此 x∈Rn,θ∈Rn x ∈ R n , θ ∈ R n ,在我们这个问题中 n=2 n = 2 ,表示题目是知识点1还是知识点2。


我们首先利用随机数初始化

{θ(1),θ(2),...,θ(nu)} { θ ( 1 ) , θ ( 2 ) , . . . , θ ( n u ) } 和每道题目的特征向量 {x(1),x(2),...,x(nm)} { x ( 1 ) , x ( 2 ) , . . . , x ( n m ) } 为小的随机数。


对于

i=1,…,nm;k=1,…,n i = 1 , … , n m ; k = 1 , … , n :


x(i)k=x(i)k−α(∑i:r(i,j)=1(θ(j)Tx(i)−y(i,j))θ(j)k+λx(i)k) x k ( i ) = x k ( i ) − α ( ∑ i : r ( i , j ) = 1 ( θ ( j ) T x ( i ) − y ( i , j ) ) θ k ( j ) + λ x k ( i ) )


对于

j=1,…,nu;k=1,…,n j = 1 , … , n u ; k = 1 , … , n :


θ(j)k=θ(j)k−α(∑i:r(i,j)=1(θ(j)Tx(i)−y(i,j))x(i)k+λθ(j)k) θ k ( j ) = θ k ( j ) − α ( ∑ i : r ( i , j ) = 1 ( θ ( j ) T x ( i ) − y ( i , j ) ) x k ( i ) + λ θ k ( j ) )


当训练完成之后,如果学生j没有见过题目i,我们希望预测学生j对题目i的需要程度,进而决定是否向学生j推荐题目i,方法如下所示:


θ(j)Tx(i) θ ( j ) T x ( i )


得到这个值之后,我们就可以决定是否向学生j推荐题目i了。


在前面章节中,我们讨论算法时都是用下标形式来讨论的,这样做的好处是比较直观,我们很容易理解算法。但是在实际应用中,如果直接应用前面章节中的公式,那么计算效率将是比较低的。因此,我们通常将算法转化为矩阵运算形式。


我们来看我们所研究的数据集:

*

题目

张一

李二

王三

赵四

x(1)

x ( 1 )

题目1

5

5

0

0

x(2)

x ( 2 )

题目2

5



0

x(3)

x ( 3 )

题目3


4

0


x(4)

x ( 4 )

题目4

0

0

5

4

x(5)

x ( 5 )

题目5

0

0

5


我们可以将上述评分结果,组成一个结果矩阵Y,如下所示:

Y=⎡⎣⎢⎢⎢⎢⎢⎢5.05.0?0.00.05.0?4.00.00.00.0?0.05.05.00.00.0?4.0?⎤⎦⎥⎥⎥⎥⎥⎥ Y = [ 5.0 5.0 0.0 0.0 5.0 ? ? 0.0 ? 4.0 0.0 ? 0.0 0.0 5.0 4.0 0.0 0.0 5.0 ? ]


由上面定义可以看出,

Y∈Rnm×nu Y ∈ R n m × n u ,即行对应于某道题目,列对应于某个用户。我们以前的 y(i,j)=Yi,j=θ(j)Tx(i) y ( i , j ) = Y i , j = θ ( j ) T x ( i ) 。


则结果矩阵可以表示为:


Y=⎡⎣⎢⎢⎢⎢⎢⎢5.05.0?0.00.05.0?4.00.00.00.0?0.05.05.00.00.0?4.0?⎤⎦⎥⎥⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢θ(1)Tx(1)θ(1)Tx(2)θ(1)Tx(3)θ(1)Tx(4)θ(1)Tx(5)θ(2)Tx(1)θ(2)Tx(2)θ(2)Tx(3)θ(2)Tx(4)θ(2)Tx(5)θ(3)Tx(1)θ(3)Tx(2)θ(3)Tx(3)θ(3)Tx(4)θ(3)Tx(5)θ(4)Tx(1)θ(4)Tx(2)θ(4)Tx(3)θ(4)Tx(4)θ(4)Tx(5)⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥ Y = [ 5.0 5.0 0.0 0.0 5.0 ? ? 0.0 ? 4.0 0.0 ? 0.0 0.0 5.0 4.0 0.0 0.0 5.0 ? ] = [ θ ( 1 ) T x ( 1 ) θ ( 2 ) T x ( 1 ) θ ( 3 ) T x ( 1 ) θ ( 4 ) T x ( 1 ) θ ( 1 ) T x ( 2 ) θ ( 2 ) T x ( 2 ) θ ( 3 ) T x ( 2 ) θ ( 4 ) T x ( 2 ) θ ( 1 ) T x ( 3 ) θ ( 2 ) T x ( 3 ) θ ( 3 ) T x ( 3 ) θ ( 4 ) T x ( 3 ) θ ( 1 ) T x ( 4 ) θ ( 2 ) T x ( 4 ) θ ( 3 ) T x ( 4 ) θ ( 4 ) T x ( 4 ) θ ( 1 ) T x ( 5 ) θ ( 2 ) T x ( 5 ) θ ( 3 ) T x ( 5 ) θ ( 4 ) T x ( 5 ) ]


我们令设计矩阵X为:


X=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢x(1)Tx(2)Tx(3)Tx(4)Tx(5)T⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥ X = [ x ( 1 ) T x ( 2 ) T x ( 3 ) T x ( 4 ) T x ( 5 ) T ]


其中

X∈Rnm×n X ∈ R n m × n 的矩阵。


上式中两侧的横线代有将列向量转置为行向量,同样我们令参数矩阵

Θ Θ 为:


Θ=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢θ(1)Tθ(2)Tθ(3)Tθ(4)Tθ(5)T⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥ Θ = [ θ ( 1 ) T θ ( 2 ) T θ ( 3 ) T θ ( 4 ) T θ ( 5 ) T ]


其中

Θ∈Rnu×n Θ ∈ R n u × n 。


有了上面两个定义,我们就可以将结果矩阵表示为:


Y=X⋅ΘT=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢x(1)Tx(2)Tx(3)Tx(4)Tx(5)T⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⋅[θ(1)θ(2)θ(3)θ(4)] Y = X ⋅ Θ T = [ x ( 1 ) T x ( 2 ) T x ( 3 ) T x ( 4 ) T x ( 5 ) T ] ⋅ [ θ ( 1 ) θ ( 2 ) θ ( 3 ) θ ( 4 ) ]


上面讨论的是前向传播问题,而在计算梯度和利用随机梯度下降算法来进行学习时,还需要用我们之前介绍的公式来计算。


有了这些理论知识之后,我们就可以来解决一类非常常见的推荐问题了,例如学生j观做过题目i,我们就可以向学生j推荐5个他可能需要的题目了。


我们首先取出题目

i i 的特征向量x(i)x(i),然后对所有其他题目算出欧式距离 d(l) d ( l ) :


d(l)=∥x(i)−x(l)∥22,l=1,2,...,nm且l≠i d ( l ) = ‖ x ( i ) − x ( l ) ‖ 2 2 , l = 1 , 2 , . . . , n m 且 l ≠ i


对于所有

d(l) d ( l ) 按从小到大进行排序,选出其中距离值最小的5个,就可以作为结果推荐给用户 j j <script type="math/tex" id="MathJax-Element-41">j</script>了。


这里面实际上还有一个问题,如果我们有成千上万部影片,我们算出某部影片的与其他所有影片的欧式距离,并从小到大排序,最后选出5个最小的推荐给用户,这将花费很长时间,用户肯定不愿意等待那么长时间。怎么解决这一问题呢?我们可以通过一个定时启动的后台线程,算出所有影片之间的欧式距离,并按从小到大排序,然后缓存在NoSQL数据库中,这样需要向用户推荐时,直接返回缓存中结果就可以了。如果我们定时启动的后台线程运行频率足够的话,我们就可以得到相对实时的效果了。


在这一节中,我们讲述了基于深度学习的推荐系统。但是在这里我们有一个实际问题,我们一直没有讨论,就是推荐系统的冷启动问题,如何从对学生一无所知开始进行推荐,这是一个非常实际的问题,在下一节中,我们将讨论怎样解决这个问题。