lesson5 part 2 协同过滤

协同过滤

之前的协同过滤例子

在本例中,fit_one_cycle方法值传入了一个数字,这是合理的,因为在协同过滤里面,只有一层,里面有几部分,但是没有做矩阵相乘,传入激活函数,再做矩阵相乘。

仿射函数(Affine Function)

它是把所有数值加在一起的线性函数,但比矩阵乘法用得更广泛,叫做Affine Function(仿射函数)。它与矩阵乘法很像,矩阵乘法是最常见的仿射函数,至少在深度学习是这样。

协同过滤模型

两个矩阵的点积结果。
均方差下降到了0.39,我们要预测0.5到5之间的值,出错的平均值大概是0.4,这个效果很好,这里的目标值是3、5、1,预测值是3.23、5.1、0.98,这很接近,效果不错。

嵌入(Embedding)

嵌入矩阵(Embedding matrices)


嵌入矩阵

两个橙色的矩阵分别是用户矩阵与电影矩阵(转置后),它们初始时时随机的,我们可以用梯度下降训练它们。在原始数据里,用户ID与电影ID是这样的数字,为了方便,将这些数字转换成1到15(user_idxmovie_idx),在这些列里,对每一个rating(评分),我们都有一个转换后的用户ID和电影ID,它们从1开始。
现在我把1这个用户ID用这个向量代替:向量里第一个数字是1,其他的都是0。然后对第二个用户,用这个向量代替:向量里第一个数字是0,第二个是1,其他的都是0。对于14这个电影ID也是一样,用一个这样的向量代替:前面13个数字是0,然后是一个1,和一个0。这叫做one-hot编码。这不是神经网络的一部分,这是对输入的预处理,我把它作为新的输入:

用户ID转换

权重矩阵乘以用户矩阵

这(User activations)是输入矩阵和权重矩阵的矩阵乘积,这是一个普通的神经网络层,这是一个常规的矩阵乘法,我们可以对这电影数据做相同的事情,这是电影数据的矩阵乘法。
电影权重矩阵

电影ID以及加权后的矩阵

输入是用户ID的one-hot编码,这些激活值是ID是1的用户的激活值,原因在于one-hot编码的向量和一个矩阵的乘积,当one-hot里的1是在第n个位置时,实际上要找出矩阵的第N行。我们这里做的是,做一个计算出激活值的向量乘法,它是在输入矩阵中找出一行。

做完这个后,我们把这两组数乘在一起(做点积),然后我们可以得到损失值,做平方,然后得到平均值。


求预测值(表格movielens_1hot)

损失值加权平均

结果与前一个表的结果一致

随机权重矩阵的结果

这里(“dotprod”里)是找出特定的用户embedding,这里(“movielens_1hot”)是只做一个矩阵乘法,这样我们知道它们在数学上是等价的。

再次Embedding

在表格movielens_emb中:将user_id转换为从1到15的数,避免了one-hot矩阵:


通过Match函数取值

通过offset()函数取出权重矩阵的值:其中$表示绝对引用


offset取权重值
movielens_1hot中的user权重矩阵

与前一个表的结果一致,说明数组查询的结果与前一个表格的权重矩阵乘以one-hot矩阵得到的矩阵效果一致,这一过程并不需要真正生成one-hot矩阵,被称为embedding。

换句话说,乘以一个one-hot编码的矩阵和做数组查找(array lookup)是等价的。所以我们应该用这数组查找的版本,我们有这样一种方式,来用one-hot编码矩阵做矩阵乘法,但并不真正的创建one-hot矩阵。我只是传入一组数字,假装它们是one-hot编码的。这就叫embedding。emdedding实际上只是代表在数组里找出一些东西。不过,要知道在数组里找出一些东西和用one-hot做向量乘法是相同的。所以,在神经网络标准模型里,embedding很合适。
就像时我们有了另外一种层。在这种层里,我们从数组里查出数据。但实际上没有做任何其他事情。我们只是简化了计算,这只是一个快速的内存高效的做one-hot矩阵乘法的方法。
emdedding = 数组查找

就是这样,它有有意思的含义。当你用one-hot矩阵乘以别的矩阵时,你可以看到这样的特性,权重矩阵里,只有用户ID输入里值是1的对应的那行才会出来。换句话说,你可以得到和你的输入对应的一个权重矩阵。因为我们计算输出激活值的唯一方式是做两个输入向量的点积。这意味着它们需要相互对应。需要有这样一种方式,这些值对某个用户比较高,这些值对某个电影比较高,这样这个用户就喜欢这个电影。它的含义可能是这样的,这些数字代表用户兴趣特征和对应的电影特征。比如,这个电影里有John Travolta,并且这个用户喜欢John Travolta,那么这个用户就喜欢这个电影。

推测的依据

红框表示用户a的偏好特征,紫框表示电影的特征,比如用户a喜欢演员b,紫框的一个特征就是演员b参加了这步电影,就能推测用户对于这部电影喜欢的可能性大。

我们没有让这些行代表所有东西。我们没有做什么事情来让这些行代表所有东西。梯度下降可以得出好的结果的唯一方法是,权重矩阵能不能找出用户品味是什么以及对应的电影的特征是什么。这里说的潜在的特征被叫做latent factors或者latent features(潜在特征)。就是这些本来隐藏的,一训练它们马上就出现的东西。

偏置(Bias)

这里有个问题。没人会喜欢《Battlefield Earth》。尽管里面有John Travolta,它也不是一个好电影。那我们要怎样处理它。这里有一个叫做“我喜欢John Travolta”的特征,这有个叫做“这部电影里有John Travolta”的特征,看起来你会喜欢这个电影。但是我们补充一些信息:要么这部电影不是Battlefield Earth,要么你是山达基教(Scientologist)的信徒(John Travolta是这个教的信徒)。我们要怎样做呢?我们需要加入bias。

Bias

给两个权重矩阵各添加一行或一列bias,这代表,现在所有电影都有了一个总分代表“这是一个很棒的电影”或者“这不是一个很棒的电影”,每个用户有这样一个总分代表“这个用户给电影打分高”或者“这个用户给电影打分低”,这就叫bias。这看起来很熟悉。这是常见的线性模型里的概念,也是有矩阵乘积和偏置的神经网络线性层里的概念。

记得吗,从第二课SGD的notebook开始,你不会真的使用一个bias,你可以只是添加一列全部是1的数组到输入数据里,然后会自动得出bias,但是这很没有效率。在实践中,所有的神经网络类库都会有一个显式的bias的概念。我们不需要添加一列值是1的数组。

它的效果怎么样呢?就在今天我进来之前,我运行了求解器,我们我们可以检查RMSE。均方差根是0.32,没有bias的版本的值是0.39。可以看到这个更好的模型给出了更好的结果。说它更好,是因为它提供了更大的灵活性,它在语义上也更有意义,我喜欢这个电影不仅取决于它的演员是谁、它是不是很多对话、有多少动作等等,也取决于它是不是一个好电影和我是不是喜欢给电影评高分的人。

提问:
fit_one_cyclefit的第一个参数是epoch的数量,一次epoch是把所有输入看一遍,epoch为10就是把输入看了10遍,如果你有很多参数和一个比较高的学习率,你可能会过拟合。

后续查看Jupyterlab笔记本Lesson4_collab

collab_learner


image.png

这是collab_learner函数。像其他的一样,这个learner函数也接收一个data bunch。通常learner对象也要传入你要指定的网络架构信息。这个learner里,只有一个和这个有关的参数use_nn,表示你想用一个多层神经网络还是经典的协同过滤。今天我们只讲经典的协同过滤,可能也会简单地讲下神经网络,看情况。

这里究竟做了什么?我们基本上是创建了一个EmbeddingDotBias模型,然后返回了一个learner,这个learner包含了我们的数据和这个模型。显然,重要的东西是在EmbeddingDotBias里实现的,我们看下它。

image.png

这是EmbeddingDotBias。它是一个nn.Module。提醒下,在PyTorch里,所有的PyTorch层和模型都是nn.Moudle。它们是这样的东西:在创建时,像一个函数一样,你可以用圆括号来调用,可以传入参数。但它们不是函数,它们没有call。通常在Python里,要让一个东西看起来像函数,你需要给它一个叫duner call(call)的内置方法,就是__call__,这里并没有。原因是,PyTorch希望你有叫forward的东西,当你像调用函数一样调用nn.module时,PyTorch会为你调用forward()

当训练模型时,要得到预测值,它会为我们调用forward()。预测值是在forward里计算的。 可以看到,这里传入...为什么是users而不是user,这是因为每次数据都是一个mini-batch(小批次)。当我在读PyTorch module里看到forward()代码时,我一般会在大脑里忽略这里有一个mini batch。我会当作只有一个数据样本。因为PyTorch会对自动为你处理mini batch中所有需要处理的内容。所以让我们加假装这里只有一个user就好。取用户数据(给到self.u_weight()),这个self.u_weight是什么?它是一个embedding。我们创建了一个embedding,(u_users,n_factors)为第一个内容,(n_items,n_factors)为第二个内容,(n_users,1)为第三个内容(n_items,1)为第四个内容,这很合理。(n_users,1)指的是这里的用户偏差,(u_users,n_factors)这一块大矩阵。第一个元组(u_users,n_factors)就是u_weight的值,第三个元组(u_users,1)就是u_bias的值。

记住,当Pytorch创建nn,module时,会执行init(),我们在这里创建权重矩阵,不过我们一般不需要自己创建权重矩阵张量,通常用PyTorch的快捷函数来创建即可。这里,你只需要知道这个函数会为我们创建一个嵌入矩阵,这也是一个Pytorch nn.module,你也会在那里看到.forward。所以当实际将数据传入嵌入矩阵,并获得激活值输出时,你是将u_weight当作了一个函数来用,后面带()和参数。如果你去看Pytorch的源代码,找到nn.Embedding,用于进行数组查找。我们在这里获得用户参数,我们在这里获取电影参数,这样我们就有了用户和电影的嵌入矩阵,至此各种(权重/参数)准备就绪,我们将它们相乘再相加,让后再加上用户偏差和电影偏差,如果我们设置了y_range,就可以使用sigmoid函数。至此你已经明白,整个模型(的构建和计算流程)。这不是任意的一个模型, 这是我们刚刚创建的,至少非常有竞争力,可能比某些只做这个的公司公布的貌似不错的结果更好。

(n_users,1)指的是这里的用户偏差

(u_users,n_factors)指的是这一块大矩阵

Embeddings令人惊奇

解释embedding很有用。就像这节课里我们稍后会看到的,我们在表格数据里为类别变量创建的也是embedding,这用得更广泛。再说一遍,它只是一个普通的矩阵乘以一个one-hot编码的输入,我们跳过计算和内存消耗,使用一个更有效率的方式实现它,恰好有这种有意思的效果。有一篇有意思的论文,它的作者在一个叫做Rossman的Kaggle比赛中得到了第二名。我们大概会在课程第二部分学习Rossman的更多细节。我想在课程第一部分没有时间了。它基本上是很标准的表格数据问题。有意思的地方在于预处理。他们得了第二名,得第一名的人和其他在榜单前列的非常多的人都用了巨量的高特异性特征工程。得了第二名的文章作者用的特征工程要远少于其他人。独特之处在于,他们用了神经网络,那是在2016年,那时没有人这样做。没人用神经网络处理表格数据。

文章

我们讲的这些东西是那时开始出现的,至少是那时才开始流行的。当我说流行时,我指的是只有一点点流行,大多数人还不知道这个方法。但这很酷,因为在他们的论文里,他们比较了多种方法的主要平均百分比误差(main average percentage error):K近邻、随机森林、gradient boosted trees。

评价参数

首先,可以看到,神经网络做得好很多,然后使用entity embedding后效果更好了,他们在训练完成后,给所有的任务都添加了entity embedding。添加了entity embedding的神经网络的效果是最好的,添加了空的embedding的随机森林差得远。这很好,你可以在生产、仓储、基因序列或其它各种领域的应用中训练这些实体矩阵(entity matrices),然后用在很多不同的模型中,可以用在随机森林这样比较快的方法中,也能得到很多提升。

有一点值得注意。他们为德国省份的embedding矩阵做了一个二维的投影,因为这是一个德国的连锁超市,用的方法和我们用过的一样,我记不太清他们用的是PCA还是有些细微区别的其它东西。然后出现了有趣的事情。在这个embedding空间里,我圈出了一些东西,我用相同的颜色在这个地图上圈出了这些点,结果就像是“天呀,embedding投射(embedding projection)学会了地理”。实际上,它们并没有学会这个,但它们找到了在超市采购模式上相互接近的一些东西,因为这是关于预测这里会有多少销量的。里面有一些地理因素。

image.png

有一点值得注意。他们为德国各州的embedding矩阵做了一个二维的投影,因为这是一个德国的连锁超市,用的方法和我们用过的一样,我记不太清他们用的是PCA还是有些细微区别的其它东西。然后出现了有趣的事情。在这个embedding空间里,我圈出了一些东西,我用相同的颜色在这个地图上圈出了这些点,结果就像是“天呀,embedding投射(embedding projection)学会了地理”。实际上,它们并没有学会这个,而是通过日常购物模式,找到了那些相邻的城市,因为这个(数据模型)是为了预测销售额的,肯定有一些地理位置特征在其中。

image.png

事实上,这是一张两个embedding向量间距离的图。你可以取一个embedding向量,看一下它与其他的向量的平方和相差多少。这是欧几里得距离(在embedding空间里的距离),将嵌入向量间的距离数据,与现实世界连锁超市间的实际距离,组合起来作图,你可以看到这样明显强正相关性。

image.png

这是一周各天的embedding空间,可以看到,它们之间有一个清晰的路径。这是一年各月的embedding空间,同样的,也有一个清晰的路径。

Embedding是令人惊奇的,我觉得没有人像我这样做过解释。如果你在做基因序列、或者植物物种、或者商店商品预测或者其他什么东西,可以训练一些模型,然后尝试微调一些embedding,然后用这种方式查看下相关性,把它们聚类,投影到2d空间之类的,这是很有用的。

你可能感兴趣的:(lesson5 part 2 协同过滤)