0.前言
- 使用逻辑回归的方法实现电影推荐
- 项目地址见github
1.主要思想
- 逻辑回归
- 数据向量化
2.逻辑回归
2.1 逻辑回归是什么?
- 逻辑回归是监督学习中的 分类 算法。
什么是监督学习?
带标签的学习,即给出的数据用标签标注,有明显的正确答案。
什么是分类算法?与分类对应的回归算法又是什么?
简单的说,分类算法用于分类;回归算法用于计算具体的值。
比如:预测明天青浦区的天气问题【雨,晴,多云】。其值是确定的(数学里面说就是离散的),那就是分类问题;预测明天的股价这就是回归问题。其值不确定,但是其值连续,任何一个数值点都有可能取到,这就是回归问题。
2.2 逻辑回归能干什么?
进行分类预测。
在推荐系统中,可以将是否点击一个商品看成一个概率事件,被推荐的商品无非只有两种可能性:被点击;不被点击。那么就可以将这个推荐问题转换成一个分类问题。哪些商品会被点击,哪些商品不会被点击?所以可以使用逻辑回归来进行一个分类预测。
2.3 逻辑回归怎么实现?
- 协同过滤模型仅利用用户与物品的相互行为信息进行推荐【单一特征】。
- 逻辑回归模型能够综合利用用户,物品,上下文等多种不同的特征生成较全面的推荐结果【多特征融合】。融合体现在将所有的特征放到同一个向量【称之为特征向量】中。
3.实现
3.1 具体步骤
- (1)将用户年龄、性别、物品属性、物品描述、当前时间、当前地点等特征转换成数值型特征向量;
- (2)确定逻辑回归模型的优化目标(以优化点击率为例),利用已有样本数据对逻辑回归模型进行训练,确定逻辑回归模型的内部参数
- (3)在模型服务阶段,将特征向量输入逻辑回归模型,经过逻辑回归模型的推断,得到用户“点击”物品的概率
- (4)利用“点击概率”对所有候选物品进行排序,得到推荐列表
3.2 可疑之处
上述描述中的问题:
- 得到用户点击的概率,那么这个概率肯定是个连续值,为啥要用“逻辑回归”这么一个 分类器 来做?
3.3 数学形式
- (1)将特征向量: X = ( x 1 , x 2 , . . . . x n ) T X = ({x_1,x_2,....x_n})^T X=(x1,x2,....xn)T 作为模型的输入
- (2)通过为个特征赋予相应的权重: w = ( w 1 , w 2 , . . . . w n ) w=({w_1,w_2,....w_n}) w=(w1,w2,....wn) 来表示各特征的重要性差异,将各特征进行加权求和,得到 x T w x^Tw xTw
- (3)将
x
T
w
x^Tw
xTw 输入到
sigomid 函数中,使之映射到0~1 的区间,最终得到“点击率”
简单说一下sigmoid函数:
函数公式
f
(
z
)
=
1
1
+
e
−
z
f(z) = \frac{1}{1+e^{-z}}
f(z)=1+e−z1
函数图像
4.训练过程
前提:
(1)使用逻辑回归完成二分类
那么具体是怎么训练的?怎么样才能找到一个合适的w?
step1.初始化w 初始化可以随意进行,但是也是有讲究的,这里不再涉及。我们就以最简单的初始化方式,将其全部取成0。即w=[0,0,...,0]
step2.确定损失函数
损失函数该是什么样子呢?因为是二分类,那也就是说:每个物品都有点(记为y=1)、不点(记为y=0)两种选择。在电影评分这个系统中,虽然电影评分有高低之分,但是我们将其简化处理为 看过、没看过 两种状态【这种状态就是监督学习中所说的标签】。根据这个标签,那么就可以由极大似然估计思想得到一个损失函数。极大似然估计我们后面再补充。
{
p
(
y
=
1
∣
x
;
w
)
=
f
w
(
x
)
p
(
y
=
0
∣
x
;
w
)
=
1
−
f
w
(
x
)
\left \{ \begin{aligned} &p(y=1|x;w)=f_w(x)\\ &p(y=0|x;w)=1-f_w(x) \end{aligned} \right.
{p(y=1∣x;w)=fw(x)p(y=0∣x;w)=1−fw(x)
将上式综合起来,可以得到式: p ( y ∣ x ; w ) = [ f w ( x ) ] y [ 1 − f w ( x ) ] 1 − y p(y|x;w) = [f_w(x)]^y [1-f_w(x)]^{1-y} p(y∣x;w)=[fw(x)]y[1−fw(x)]1−y
统一化之后,就得到了如下的目标函数: L ( w ) = ∏ i = 1 m p ( y ∣ x ; w ) L(w) = \prod_{i=1}^m p(y|x;w) L(w)=∏i=1mp(y∣x;w),在本例中,m=1。为了计算方便,将其取对数后,再乘以系数 − 1 m -\frac{1}{m} −m1,得到的目标函数:
J ( w ) = − 1 m l o g [ L ( w ) ] = l o g { ∏ i = 1 m p ( y ∣ x ; w ) } = − 1 m { ∑ i = 1 m [ y i l o g f w ( x i ) + ( 1 − y i ) l o g ( 1 − f w ( x i ) ) ] } J(w) =-\frac{1}{m} log[L(w)] =log\{ \prod_{i=1}^m p(y|x;w) \} = -\frac{1}{m} \{ \sum_{i=1}^m[ \space y^ilogf_w(x^i)+(1-y^i)log(1-f_w(x^i))] \space \} J(w)=−m1log[L(w)]=log{i=1∏mp(y∣x;w)}=−m1{i=1∑m[ yilogfw(xi)+(1−yi)log(1−fw(xi))] }
注:
(1)取完log后,阶数变成了系数。所以上面这个公式有系数
y
i
y^i
yi 和 系数
(
1
−
y
i
)
(1-y^i)
(1−yi)
(2)这个损失函数也就是处理分类问题中最常用且著名的函数——交叉熵函数
二分类的交叉熵损失函数
L = − [ y l o g y ^ + ( 1 − y ) l o g ( 1 − y ^ ) ] L=-[ylog\hat y + (1-y)log(1-\hat y)] L=−[ylogy^+(1−y)log(1−y^)] 其中y是真实值, y ^ \hat y y^是预测值。因为是二分类,所以它们的取值集合是{0,1}。
- step3.优化方法
得到这个损失函数之后,就可以开始采用传统的梯度下降方法进行训练,并得到最终的结果。对参数 w j w_j wj求偏导的结果如下:
∂ J ( w ) ∂ w j = 1 m ∑ i = 1 m ( f w ( x i ) − y i ) x j i \frac{\partial J(w)}{\partial w_j} = \frac{1}{m} \sum_{i=1}^m (f_w(x^i)-y^i)x_j^i ∂wj∂J(w)=m1i=1∑m(fw(xi)−yi)xji
在得到梯度之后,即可得到模型的参数更新公式,如下所示:
w j = w j − γ 1 m ∑ i = 1 m ( f w ( x i ) − y i ) x j i w_j = w_j - \gamma \frac{1}{m} \sum_{i=1}^m (f_w(x^i)-y^i)x_j^i wj=wj−γm1i=1∑m(fw(xi)−yi)xji
至此,便完成了逻辑回归模型的推导过程。
5.电影推荐如何实现?
针对上面的叙述,我来结合电影数据和我的代码来讲解如何完成一次推荐过程。
5.0 说明
5.0.1 数据
-rw-r-----@ 1 gamidev staff 1.5M 3 9 2001 u1.base
-rw-r-----@ 1 gamidev staff 383K 3 9 2001 u1.test
-rw-r-----@ 1 gamidev staff 1.5M 3 9 2001 u2.base
-rw-r-----@ 1 gamidev staff 386K 3 9 2001 u2.test
-rw-r-----@ 1 gamidev staff 1.5M 3 9 2001 u3.base
-rw-r-----@ 1 gamidev staff 387K 3 9 2001 u3.test
-rw-r-----@ 1 gamidev staff 1.5M 3 9 2001 u4.base
-rw-r-----@ 1 gamidev staff 388K 3 9 2001 u4.test
-rw-r-----@ 1 gamidev staff 1.5M 3 9 2001 u5.base
-rw-r-----@ 1 gamidev staff 388K 3 9 2001 u5.test
- base是基础训练数据;test是测试数据
- 使用到模型训练工具
pytorch
5.1主要步骤
5.1.1 获取特征向量
在公式y = wx + b 中,需要确定各个向量中的数据是什么。这一般会结合实际的数据映映射+抽象得到。例如:结合用户信息、电影信息得到如下特征向量的格式:x=【用户id,用户年龄,性别,职业,电影id,电影类别】
其中电影类别不是一个值,是一个1*18的向量,其每个维度代表的信息是:
unknown | Action | Adventure | Animation |Children’s | Comedy
| Crime | Documentary | Drama | Fantasy |Film-Noir
| Horror | Musical | Mystery | Romance | Sci-Fi |Thriller | War | Western
例如有如下数据:
u.user => 19|40|M|librarian|02138
数据说的是:userid=19,年龄是40,性别是Male=>0, librarian => 1[统一映射]
因为需要针对这个人推荐,所以我这里找出他看过的电影以及评分记录:
19 274 2 879539794也就是说,看过的电影id是274,那么得到的特征向量就是 x = [19,40,0,1,274,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0]。下面给出一个得到的特征向量的例子:
featureDs 是类FeatureDataset的实例。我们主要用到其 feature 和 label 这两个属性。如下图所示:

同时,在 featureDs 这个实例中下的feature有80000条数据,每条数据都是一个特征向量,如下图所示:
我们接下来就会用这些特征向量去训练模型。
y表示的是正样本【推荐看的电影1】亦或是负样本【不推荐看的电影0】。这里有几个地方需要注意:
- 训练部分
我们训练的时候是按照y=1,y=0这个标签来训练的,利用MLE方法进行模型的训练(所以需要分类,即有正负样本之分),得到一个最终的预测模型。 - 预测部分
根据训练好的模型预测时,输入特征向量x,会得到一个概率值【而不是一个特征值】,这个概率值表示的则是这个电影被推荐的概率大小【是个连续值】。
5.1.2 模型训练
简单回顾一下整个模型简图:
上图中的逻辑回归主要会做如下几件事儿:
- x 1 − x 3 x_1 - x_3 x1−x3 是特征向量中的个体数据
- 执行一个线性变换操作
- 执行激活函数操作
- 最后可设定一个阈值,对激活函数的值做一个分类,便得出最后的结果
再对应来看我的代码:
- 这里的 x 1 , x 2 , x 3 x_1,x_2,x_3 x1,x2,x3 就对应我们特征向量中的每个数据。只不过我们这里是 x 0 − x 23 x_0 - x_{23} x0−x23。
- 线性变换和激活函数操作怎么来?
由我们事先定义的类 LogR(nn.Module) 实现这个部分,具体如下图所示:
-
__init__() 函数是用于定义模型会具体使用什么包,比如说:这里的 self.linR = nn.Linear(in_features,out_features) 说明linR是pytorch 包下Linear(...)的实例。我们指定输入数据的特征,它就会在这个数据上执行线性变换操作。self.sg = nn.Sigmoid()同理。 -
forward()函数是模型真正训练时执行的。它强调的是 模型的调用顺序,比如说,由上图显示可知,我们这里是先执行线性变换,然后执行激活操作 (sigmoid) 。
现在模型已经有了输入,输出,但是还不知道模型的好坏,所以我们得搞个东西来衡量模型的好坏,这个东西就是 损失函数,同时,我们还需要将损失函数的损失降到最小,所以就需要 优化器 来优化模型。
5.1.3 模型优化

上面方框中的分别就是我们使用的逻辑回归模型的损失函数和优化器。通常为了加速训练,可以将数条【”数条“的值=BATCH_SIZE】特征向量拼接成一个矩阵的形式,然后得到一个mini-batch。在pytorch中,为了方便将数据搞成mini-batch 和并行加载数据。搞出来一个Dataset类和DataLoader类。
上面四个框,分别对应的是:
- 01.从DataLoader 的实例中得到训练数据。其包括两个部分:【特征向量,和标签】
- 02.把数据放入我们的模型中,得到结果数据
- 03.将结果数据同label 进行比较,得到损失值
- 04.通过优化器降低损失值
5.1.4 模型效果
可以看到训练的正确率 在94% 上下,但是测试模型效果在 93% 左右。
6.问题
本节提出逻辑回归中的系列可疑之处,供大家参考
6.1 为什么训练的时候是用 y=1/0,而预测得到的值却是一个概率值?
- 训练和预测本来就没有关系
- 逻辑回归做的目的就是分类。所以需要使用
y=1/0去表示是属于1类【正样本】还是0类【负样本类】,损失函数用的是适合处理分类问题的交叉熵算法(cross entropy)
6.2 为什么用交叉熵算法而不用别的算法【如均方误差算法(mse)】?
这个涉及到梯度下降时参数更新的问题。在mse算法的梯度计算公式中,因为 sigmoid 导数的性质会导致参数无法更新,所以使用了交叉熵算法而不是均方误差。
6.3 单纯使用点击率是否不妥?
在商品广告的推荐里,使用点击率无可厚非。但是在推荐电影的这个系统中,仅仅使用点击率来衡量问题是否欠妥?
7.补充
7.1 极大似然估计
- 已知:某个随机样本满足某种概率分布,但是其中具体的参数不清楚
- 参数估计:就是通过若干次试验,观察其结果,利用结果推出参数的最大概率的取值
常用MLE方法去估计一个参数的取值。求极大似然估计值的一般步骤如下:
- step 1.通过分布函数确定每种情况的概率取值 P i P_{i} Pi 1<=i<=n
- step 2.将上述的概率做一个乘积,这个乘式就是似然函数
- step 3.将似然函数取对数,并化简整理
- step 4.求偏导数,然后令其值为0,得出相关参数的解析解。这个解析解就是参数估计值。
















