00:00
我们给大家有一个代码可以来实现一下,让大家加深一下理解,好,那么这个代码我们还是重新来实现一下吧。呃,大家如果要是回去之后没有看的话,也没关系,那我们就是按照之前给大家讲的这个内容,还是把它做一个梳理啊,呃,这个叫。大家想到这其实就不是as了,对吧?这个应该是隐语翼模型的梯度下降求解对不对?好,所以我们叫F,呃,LFM啊,就写错了,好,我们把这个写出来,然后第一步还是引入依赖对不对?
01:00
呃,当然了,我们一般情况下做这个数值计算肯定要用派,另外还有就是呃,我们这里边可能用到这个,呃,跟这个pandas数据分析相关的一些东西,对不对,把这两个先引入,呃,然后接下来我们这个一般引入依赖是给零对吧,还是风格跟之前一样吧。然后我们下面一步,按照我们之前的这个大家的习惯,应该是要先定义数据,对不对啊,我们定义出数据吧,呃,数据准备,我们这个数据准备就简单一些,我们初始的数据是什么,大家回忆一下是什么。是不是就是应该有一个评分矩阵啊,对吧?评分矩阵R是不是就是我们的初始的数据,我们就基于这个评分,然后要把它拆成两个特征特征矩阵对不对,拆成它两个特征矩阵的乘积,所以这就是我们想要做的一件事情,那评分矩阵R我们就直接去自己定义了,它是一个矩阵的话,我们的是NP里边的一个ARRA对不对,Nd array啊,然后这里边就会是一个二维的数组,这样的一个形式,那这里边我们就随便写吧,比方说呃,40201,那大家看到这是不是相当于一个用户对。
02:41
所有物品的一个评分对不对?那我这里边按这个维度定义的话,是不是有几个物品啊,大家看一下12345是不是有五个物品啊,好,所以我后边都可以把它直接复制一下啊,那大家看一下我现在有几个用户啊,好,我复制了六行,那应该有六个用户对不对?呃,我们可以把这个再稍微的改一下啊,比方说呃,我们就把这个一些评分都改变成不一样的,到时候看起来会舒服一点。
03:20
好,呃,这里再改一下,好,这个差不多了,对吧?有了这样一个基本的东西之后,那那大家就是还是我们还是希望知道它的这个维度是什么样,那大家可能知道我们之前有一个这个写法对不对?R ship,这是不是就代表它的维度啊,几乘几的维度对不对啊,那另外就是如果说我们想要比方说拿到它的行数怎么拿呢?当然是可以r.SHIP0对不对,可以这么拿对吧?要拿到它的列怎么拿呢?是不是可以r.SHE1啊,或者我们还有都给大家介绍一下,我是不是可以直接LSR。
04:20
是不是也可以,那这个拿到的是什么,是不是它的行啊对吧,第一个维度那要拿列怎么拿呢?对,是不是R0去拿出来就是它的列数啊,啊这都是就是在这个矩阵二维数组操作里边经常用的一些小技巧,给大家简单说一说,好,那接下来我们的核心部分其实就是要做算法的实现了,我们来写一下。好,首先我们给大家定义一下,呃,大家会看到在之前给大家这里边,我们把这些基本的东西是不是都都做了一个定义啊,呃,我这里边可以给大家直接copy过来啊,大家要把这个了解一下,这代表什么呢?就是我们在这个模型里边,或者说我们的算法里边需要用到的东西,对吧?我们是需要用到有哪些输入参数呢?大家会想到是不是首先要有评分矩阵啊,首先要有这个R对不对?然后大家会想到P和Q,这个其实不应该是输入参数,对吧?哎,我把这两个。
05:34
放到后边来啊,分解之后的P和Q,我们大家会想到这是不是R是一个M乘N的平分矩阵,我们把它分解之后,如果假设给定的是K个特征特征值的话,对吧?特征的维度是K的话,我们分解之后的P是不是应该是M乘K啊,然后分解出来的Q这个物品特征矩阵是不是应该是N乘K啊,这是它的特征矩阵对吧?啊,那大家会想到我们要让它乘回去还是R的话,那是不是应该是怎么样的一个相乘,是不是还得把这个Q是不是还得做个转置啊啊,因为我们默认的这个特征向量让它都是列,对不对?所以我们最后把这个Q再做一个转置,转成一个我们想要的这个K乘N这样。
06:24
和P乘起来就变成了M乘N对吧?那大家会看到这个K,这是我们新引入的一个数了,那这个数是从哪里来的呢?这是不是我们得预先定义好啊,对,所以这是我们影特征向量的个数,那呃,这个应该叫它的维度个数对吧?维度,或者我们直接就叫影特征向量维度,那大家会想到这个维度就代表了我们模型的复杂度对吧?就是出来之后一个用户特征,一个这个物品特征,它到底有几个维度,几个特征值,然后大家会想到接下来我们是不是还需要有一个有一个最大迭代次数啊,比方说这里边我们叫steps啊,当然大家如果按之前的习惯,大家习惯的叫这个最大迭代的话,叫呃,Maxer也可以,对不对,就跟我们之前这个迭代法都一样啊,叫max eater吧,改一个名字。
07:23
然后大家会想到我们是不是既然梯度下降,是不是里面有一个阿尔法,呃,计算的时候的迭代不长,对不对?另外是不是还有一个叫做拉姆达的东西,拉姆达是我们的对正则化系数,所以大家会想到这几个值。是不是都应该提前指定好,传进我们的这个模型里面去啊,哎,所以大家会看到啊,我们一开始其实可以有一部就是呃,给定超参数对不对,我们这里边的超参数有哪几个呢?是不是首先应该有一个K啊,我们比方说就定义一个,先给一个最少的K,是不是二维就最少啊,给一个二,然后大家会想到这个maxer。
08:17
我们给一个啊,比方说我这里给一个5000吧,然后呃,这个阿尔法布偿,这里边我给一个经验值啊,大家会看到我这里边为什么迭代次数非常的多呢?呃,那可能就是我后边这个布偿要很小对吧,比方说我这里给一个0.0002啊,很小的一个经验值,当然大家可以去去给不同的值去试一试,看看效果怎么样,然后这个LA达我们给一个0.004啊,这样一个正则化参数啊,然后接下来就是我们的核心算法,我们DeFine一个LFM的,它的梯度下降GR的。
09:07
第三对不对,大家会想到里边我们的参数需要什么,是不是需要R啊啊,其实就是前面我们定义好的这几个对吧?RK,然后max,当然我这里既然上面给了,我这里就没有默认的这个默认值了,大家想给默认值也可以,对不对,好呃,然后阿尔法。呃,我们这里默认值也给一个0.001吧,然后拉姆达好有了这样的一个定义之后,首先我们是不是先得把大家会看到啊,我们把R是传进去了,我们先得把它的维度提取出来,然后才能去做循环,对不对,先知道有多少个用户,多少个物品,所以一开始其实我们是要对这个R做一个分析的,那大家会看到R里边包含两个维度,这是不是就是我们的M乘N的一个提取啊,对吧,基本参维度参数。
10:17
定义M等于,呃,对,我们是不是可以直接用它的length来表达,那同样前面我们讲了N是不是可以用对R0来表达,这就是M跟N,那然后接下来大家会想到我们是不是应该去定义一个P和Q啊,最后我们就要返回P和Q对不对啊,所以我们定义一个P等于什么呢?一开始是不是应该有一个初始值啊,大家会想到P和Q有一个初始值,那我们的初始值怎么选呢?
11:01
初始值应该怎么生成?哎,我们是不是找一个随机生成的一个初始值啊,所以所以这样的方式大家可以认为这就是随机梯度下降对不对,从一开始的这个,呃,取值点是随机选取,好,那么我们既然是随机,肯定要用到NP里边的random这个类,那么它里边有一个random的这个方法可以调用,那这个run的时候生成,我们怎么生成呢?大家看啊,我可以直接给参数MK,这代表什么?是不是我们生成的就是一个M乘K的一个矩阵啊,哎,所以大家看这个随机生成一个矩阵非常简单对吧?然后接下来我们同样给一个QNP.random random,那么Q按照我们之前的这个定义,是不是它应该是一个N乘K啊,我们都是按照向量的这个模式的啊,所以是NK,当然最后我们如果要P和Q要能相乘的话,Q是不是得做一个转置啊,这个大家都是知道的,所以我们怎么转制呢?来看这个number派里边,Python里边这个写法非常简单,直接Q点T。
12:27
啊,就代表转制了啊,这跟我们数学上的写法很接近对吧?好,那么这样已经搞定之后,我们最后想要的是不是就是P和Q啊,对吧?所以我们先把它写在这里,接下来我们是不是就开始迭代了。那迭代的过程当中,大家会想到啊,肯定就是说是不是要在这个呃,Max eater这个迭代次数里边,每一次都去迭代,对不对啊,我们这里就把把它叫做每一步吧,Step in max哦,这里我们是不是应该有一个range,对吧?
13:10
然后我们迭代的过程当中,大家会想象一下我们应该是怎么样去考察呢?是不是在公式里边大家看一眼啊,我们梯度下降的过程当中,是不是要针对每一个用户,每一个物品都要去算它梯度下降之后的那个状态啊。哎,所以大家会想到我们对于用户特征向量这个PU去做梯度下降的时候,肯定是每一个用户都得变历道对不对啊,所以是每一个PU都得去做一个操作,那每一个PU它在梯度下降的过程当中,是不是还要对每一个物品这个Qi去做一个求和啊,同样的大家会想到下边我们对每一个I去做梯度下降的时候,那Qi是不是得做一个便利,把每一个I都得做一个便利对不对每一个物品做一个便利,然后它里边是不是还得对每一个用户都得做一个求和啊,所以大家会想到这两个是不是相当于都是把所有用户所有物品做了一个便利,哎,那我们是不是把它放在一起,就是一个双重的for循环就搞定了呀?哎,外层对所有的用户便利一遍,内层对所有的物品便利一遍,是不是就搞定了,诶所以我们接下来里边。
14:36
写法怎么写呢?是不是for每一个u range u是有多少个?大家回忆一下诶,对,非常好,M这个行数是不是就是U的个数啊,也就是我们这个,呃,最后得到用户特征矩阵P的这个行数对不对,这就是我们的U的个数,所以是运M,然后接下来我们是不是对I也要做一个便利,呃,Item对吧,它是有多少个对,这个当然就是N对不对?所以我们接下来的这两步啊,大家看到是对所有的用户U物品I做便利,呃,然后。
15:30
对应的特征向量PU和Qi是不是要梯度下降啊,好,这是我们的一个想法,呃,那大家会想一下,这个在考察他们所有的这个物品,就是U和I的时候,我们做梯度下降的时候,首先是要拿到一个什么东西啊,我们的这个按按照公式来看,每一步迭代的时候,以这个P作为一个例子啊,我们对P做梯度下降的时候,是不是当前的P和当前的Q都应该知道啊,啊,对吧?因为它是一个迭代法嘛,所以我们的P和Q应该都是知道的,然后大家会看到这个表达式里边还有什么,还有拉姆达对吧,拉姆达是不是也给定了呀?还有阿尔法对不对,阿尔法也知道了,就差一个RUIRUI是什么?
16:31
其实就是用户优对商品I的一个评分对不对,所以这个是不是就从我们的R那个评分矩阵里面拿出来就可以了,哎,刚好我们现在既然已经做了这个变例U和I,那是不是从R里边拿到它的第行的Di列那个评分,就是我们这里的这个值啊,诶,所以我们这里其实就是要拿到这个就可以了,那这里大家看我们拿到这个RUI到底是要干什么呢?是不是要算?
17:04
它的这个评分的误差啊,大家看这里边这个是不是平分的误差,诶,所以我们这里边先做一个平分误差的计算,那这个评分误差大家想一下,我们本来的这个R里边是不是默认它是一个稀疏矩阵啊,也就是说我们一开始这个R里边是不是有零的呀,这个零并不代表评分是零,因为一般情况我们评分都是有最高分最低分的,对吧,即使是差评也不可能是零分,呃,所以差评的话,一星二星,这就代表差评并不会给一个零分,那零分表示什么呢?是不是表示没评价过啊啊,电影的话就是没看过对不对,商品就是没买过,没有关注过这个商品,所以为零的这些位置,其实是想要我们最后去填充上做一个预测的位置,对不对,那大家会想到我们在去考量误差的时候。为零的这些地方是不是就不应该考察它的误差啊,因为这是没预测的地方,对吧?我们并不是要最后误差出来就跟这个零接近,是说是不是有值的地方接近就可以了,零这个位置是不是随便填啊,诶这是我们的一个想法,所以大家看在考察误差的时候,一开始我们是不是应该先判断必须它要大于这个评分应该大于零我们才考虑误差啊,诶所以大家看到评分大于零是不是就是RUI大于零啊,这里的RUI是不是就是对应的平分对吧?
18:38
呃,对于每一个大于零的评分,求出预测评分误差,那当然了,下面我们肯定首先要算一个这样的,呃,这个误差啊,我们这个误差就叫做EUI吧,就是用户U对I的评分的一个误差,对不对?Error对吧?好,那么它应该等于什么?
19:15
看公式大家会看到是不是就是我们这里边的pet乘以QY这两个向量乘积,得到的是不是一个预测评分,然后减去RUI,这就是误差对吧?好,那么我们这里边怎么样去乘呢?当然了,我们这里边的向量乘积是不是得用NP里边的点乘啊,Dot对不对?这是不是代表我们那个矩阵的点乘,因为PU和Qi是不是都是向量,所以我们这里边直接用这个dot来做一个乘法,那里边谁跟谁呢?是不是PUPU怎么显示?PU就是P矩阵里边的。
20:00
第行对不对,那大家想一想,第行怎么表示,是不是应该是第行,是不是第一个索引就是U啊。第二个索引,第一行一行全要,那是不是第二个索引就是冒号啊对,大家想一下这个表达式啊,所以这个表达式写出来是不是这就是标准的这个PU对不对,后边是所有的对吧?那然后第二个参数是不是要从Q里边去取了,要取Qi对吧?那Qi怎么表示呢。Qi是不是反过来我们要Q已经转置过了,对不对?Q是K乘I的一个矩阵,那我们Qi是不是要找Q里边的D列啊?对不对,因为Q已经已经转制过了,对吧?好,那么我们要找第I列是不是前面这个维度应该是冒号,后边是I啊,哎,所以大家看就是这样,所以这里边全取它的维度是什么样的呢?这个维度是不是K个啊,所以是大家会想到这里的U,后边K个维度,这里也是K个维度,后边这里是I,这是一个对不对,那最后得到的结果是不是就是一乘一的矩阵,就是一个数啊。
21:22
哎,这就是我们这个两个向量相乘,那这个乘完了之后,后边是不是还得减去实际平分啊,是不是RUI,所以大家看尽管看着写起来这个稍微的有点复杂,但是事实上这是不是就是我们的向量相乘,向量乘法大家可以认为它就是P乘以Q对P乘以Qi对不对?好,接下来我们已经得到这个平分误差之后,就可以做什么了,是不是代入公式里边求我们这个梯度了啊,而且大家会看到我们直接把阿尔法带进来,可以直接梯度下降了,对不对,带入公式。
22:12
呃,梯更新按照梯度下降算法更新当前的P和Qi对吧?啊,那大家会想到这个这个更新的时候怎么样去更新呢?PU和Qi它是向量对不对。那PU它一共包含几个值,几个维度?用户特征向量包含几个维度?在我们这里面它是不是有两个维度啊?就是有K个维度对不对?PU是不是应该有K个值啊,PU里面包含K个值对不对?所以我们更新这个PU的时候,大家会想到是不是就是把对应的那K个特征维度都更新一遍,是不是PU就更新了?哎,所以这里边我们是不是又得有一个for循环啊,诶,For什么呢?是不是要for每个K啊,K的范围是什么?
23:18
是不是在大K的范围内,你有两个对不对,我们对每一个这个PUK,那么大家想到每一个PUUK,这是不是就是向量P里边的每一个特征值啊,我们把它每一个都更新完,这个放循环结束之后,整个PU是不是就更新了,诶,这就是我们这个特征向量按照梯度下降更新的过程,那当然我们首先是不是还得啊,当然我们可以写成这个减减等于对吧,我们这里按照公式就还是把这个完整的写出来吧,是不是用它的原始值要减去一个值啊,梯度下降减去阿尔法乘以后边一坨对吧?好,那么。
24:05
阿尔法我们已经有了定义,就是阿尔法乘以后边的值,那么后边大家会想到乘的是什么呢?大家看这里有一个求和,求和这个东西,大家想我们要在这里去做求和吗?啊,当然也可以在在这里求和,但是大家看我们这里外层是不是对爱本来就是有一个便利的呀?是不是我们就是这里对每一个I都要去做便利,那大家想我这里是不是就不用单独再去求和了,我每次这个减的时候都把它减掉,每一个I都减掉,是不是相当于对里边做了一个求和啊?呃,所以这里就有一个小技巧,大家看我外层把这个U和I双重遍历之候,本来我们更新PU的时候,是不是这前面这一部分对爱还没有关系啊,我们应该是对所有的爱,你你这里边是不是都应该指定啊,对吧?指定一个I,我们每一个爱都要去指定,那大家会想到我们接下来是不是对每一个I都要求一个误差,然后带到下边的这个梯度下降算法里边去。
25:18
去递减啊,然后最后我们把后面那个递减的值要做一个做一个求和对不对,那我这样就可以把这个直接提到外边来,是不是也是一样的啊。For每一个I,最后我把每一个I都求出来之后,在这里每一次都去减去阿尔法乘以里边这个值,是不是相当于把里边这个值做了一个求和啊,就是这样的一个过程啊,因为这个for循环已经在外面做了,对吧?所以里边我们的这个写法就可以很简单,就不需要管这个求和了,对不对?那接下来是不是就把里边的这些内容实现一遍就可以了,好,那么里边是什么呢?是不是二倍啊,然后乘以乘以什么?哎,前面的这一部分我们已经算出来了,对吧?这一部分叫做这里算出来的是不是EUI啊。
26:12
然后后边继续乘,乘的是什么呢?大家看乘的是Qi对吧,那我们这里的Qi是什么。大家想一下。我们是不是就是如果要写Qi的话,是不是应该是上面这这这一个内容啊,这是一个向量对不对,我们现在是不是针对这个向量里边的每一个值,每一个K是不是都要去做更新啊,所以我们这里边乘的是不是就也是对应K那个位置的那个值啊,诶,所以我们这里Q乘的就是是不是QKI啊,那每一个K变立之后,是不是相当于我们这个冒号把所有的呃这个值都变利完了,是不是就是Qi好,然后后边还要干什么事情,还要再加上二倍的。
27:11
乘以还要乘以拉达对不对,然后再乘以PUPU是什么?PU是不是对应的就是PUK啊啊,我们在这里就是这样,好,所以大家看,这就是我们按照公式把它的梯度下降迭代就已经写出来了,那所以看就是如果公式已经推完的话,这个写法好像也也不复杂对不对,很简单啊,就是有一个求和,我们把它提到外面,这好像有一个小技巧而已。为什么我们把它提到外边呢?大家会看到,如果我们不把它提出来的话,那是不是相当于我们对每一个U这里便利,然后在这里边我们再对每一个I做一个方循环,去做便利,去求解啊。那对应的下边大家会想到我PU都迭代完了,Qi是不是还得迭代啊?那Qi的写法是什么?是不是相当于Y层是对每一个I做便利,里边到里边之后再对每一个U做便利去求和啊?
28:15
那大家会想到这两个,我们是不是可以把它合二为一放在一起啊,你既然是对每一个优,每一个I是不是都要做便利,所以我们把它提到外层来,那我们是不是就可以放在一个for循环里边,把每一个P和Qi全部更新了?下面的这个更新过程是不是完全类似啊?更新Qi Qi怎么表示是不是QKI啊啊,大家想一下啊,那后边我们这里边直接把它这个复制过来,我们直接把这个先复制过来吧,大家看这个表达式是不是完全类似啊,完全完全一样对不对?所以直接把它copy过来,然后一开始这个是不是就变成QKI啊,我们要更新QKI,然后这里的PQPUK是不是最后也是Qi啊,对吧,QKI,然后前面的这一部分这个误差是不是都一样,只不过就是乘的这个QKI要变成前面的PU对不对,UK,所以就是把这两个对称的位置改变是不是就可以了啊,所以大家看我们好处就是把这个UI的便利提到前面去之后下边我们是不是在一个循环里边把它俩就全部更新了呀,啊,就不。
29:39
需要分别去更新了,所以这个实现起来就会代码量非常的少,看起来非常的简洁。
我来说两句