00:00
在这个模型评估的过程当中,我们首先要给定一个评估的标准,对吧?你既然是要评价这个模型好不好,那你得先说出来什么样的模型好,什么样的模型不好,我们当然对它有一个评价的判断准则,我们这里是做做了一个预测评分,之前给大家讲过,在推荐系统里边分成两大类的这个评判标准,对吧?对于预测评分这种它有一个明确数值的这种问题,这有点像回归对不对?我们对他的评价拿什么来评价呢?一般会有一个类似于损失函数的东西,评估这个模型的好坏,我们一般把它取的这个参数叫做RMSE,对吧?啊,这个大家回忆一下,那RMSE是什么呢?其实就是军方跟误差,呃,就是军方跟误差的一个缩写,它里边的计算方式大家看到了,其实就是每一个预测值和每一个真实值这个平分之间有一个误差,我们要把这个误差做一个统计,合在一起求一个平均数对不对?那求和的过程当中,大家就想到了它俩相减,有正有负,我们求误差的时候是不是应该取一个全部要取正的啊,你要不是绝对值,要不是不是就应该是拿平方项,我们这里边最经典的算法还是用到了平方项。
01:23
啊,大家可能知道诶,为什么我们这里边用平方向呢。嗯,因为我们本身在算法求解的过程当中用的损失函数是不是就是平方损失函数啊啊,当然这里边没有再加那个正则化系数,对吧,大家会想到我们本来求解的时候用到的就是。平方损失函数,所以既然当时你按照平方损失函数去求解,那我们现在去平测的时候是不是也是基于平方损失函数啊,只不过这里边的这个,呃,具体的计算公式可以有所不同,而且大家发现这里我们就没有再加正则画像了,所以我们这一部分应该得到哪里去做评测啊?是还是基于之前的训练数据做评测吗?
02:13
大家想一下,这里去评价我们模型的时候,是不是理论上应该是跟之前的训练数据应该是不同的数据啊,我们应该用一个测试数据,你之前并没有拿它来做训练的数据来考察它的,呃,到底这个误差是多少对不对?呃,因为如果之前我们的这个训练的时候用的数据,大家把这个还带进去的话,这种很容易出现过拟合的现象,对不对啊,你根据之前的数据那算出来这个误差很小,但是发现其实是学过了,你再来一个新数据的时候,可能并不能正确的预测,所以为了避免这样的情况,我们往往是。计算模型的时候,后边加上正则画像,然后我们最后回过头来评价模型的时候,是不是直接在测试集上用我们的这个定义好的RMSE去做一个评价就可以了,诶,这就是我们呃主要的这个想法啊,RMSE当然在就是我们在这个讲解的时候,大家会想到还可以有不同的评价标准,比方说还有Mae对不对啊,就是你直接不取平方,再开根号,直接取绝对值求和是不是也可以啊,所以这里的这个评价标准并不是唯一的。
03:32
我们这里边就是以最经典的RSE为例。那大家看这个本身这个公式并不难,那我们现在要做已经有了这个评估的标准了,我们做参数选取,怎么选取呢?其实想法也很简单,大家想一想我们怎么去做。是不是就是给出一组参数,然后我们挨个根据这个参数去训练一个模型,对不对,因为这个是模型的超参数,对吧?我们是在调als.train的时候,这里边是不是有这几个参数啊,所以我们有了,我们指定一组参数,是不是就可以als.train训练出一个模型,得到这个模型之后,我们是不是就可以得到它的所有的预测评分啊?
04:20
然后是不是基于这个预测评分和真实的去做一个对比,就可以得到它的RMSE,那大家就可以想到我给一组参数进来,就可以训练一个模型,计算一个RSE,那我给不同的参数是不是可以得到不同的RMSE啊,我最后的标准是不是选一个RMSE,对在测试级上表现最好,最小的那个,是不是就是当前我们认为在这一组参数里边最好的那个,哎,所以在这个过程里边,我们的参数选取其实也很简单,就是把能想到的一些参数遍历一遍,对吧?那在便利之前我们可能还要做一些准备工作啊,所以大家会想到我们整体的这个前边的这个项目架构,整个的这个程序架构,跟我们讲到的之前的这个离线推荐,应该是基基于模型的推荐,其实是类类似,对不对,数据的一些准备都类似,就。
05:21
是做到这个训练出模型,然后我们要根据不同的参数去训练模型,然后得到预测评分去求它的RSE,主要的思路是这样好,那我们在代码里边怎么样去实现呢?我们就把它放在同一个包下面吧,大大家会想到我们是不是在这里可以去再新建一个类啊?那大家想这个应该是个object还是一个class,还是一个treat呢?啊,大家想到我们这个训练。尽管是要选取不同的模型,但其实是不是相当于还是一个程序离线在那跑就可以了,对吧,你就不停的跑,不停的找对应这个一一组参数,然后里边选出计算出来一个RMSE,最后我们把所有的计算结果做一个对比,选出一个最小的RMSE,对应的参数是不是就是我们的最优参数啊,哎,所以这个过程很明显就是一个单利对象对吧?啊,就是直接跑一趟运行完了就完事了,它里边的方法别的地方不会用到,好,那这个名字我们就叫做as参数选取模型训练,我们就叫这个trainer吧,Arser啊。
06:34
定义这样的一个单例对象,然后大家会想到我们说它跟这个盈余模型离线推荐很像,那我们这里一开始是定义了这么多的这个样例类对不对?我们现在要做这个参数选取的时候,需要样例类吗?大家想到这些样例类都是做什么的呀?是不是要不是跟我们最后啊mongo里边存储数据相关数据结构对不对,要不就是我们用到的一些配置对象,那这些是不是我们在这里都都没用啊,我们这里其实就不存对吧,是不是跟网购没关系啊,所以大家会想到,诶,前面这些样例类不需要,然后进入这个我们的对象里边来之后,大家看到我们这里的这些设置表名和常量是不是也不需要啊,我是不是只需要那些参数,然后去计算就完事了,然后只要有数据就可以,对不对,那所以大家会想到这个数据是不是我们还是。
07:34
是需要的呀,当然数据我们怎么样去提取呢?大家其实可以想到就是我们直接还是把它加载数据,用这个Spark session的read方法把它加载进来就可以了,对不对?好,所以我们在这里就直接。写写它的一个面方法,直接写里边的内容啊,所以大家会想到,既然里边基本的一些东西,如果还要有的话。
08:06
那大家会想到我们在这里是不是还要read的时候,还要指定你从哪个表里边去去读对不对啊,所以这个表大家如果觉得嫌麻烦,我们直接就是把它写出来也是可以的,那我们在这里直接就是把上边的这一部分配置应该先抄过来,对不对,As trainer。然后大家想到我们这里边需要读哪个表里边的数据呢?我们的原始数据评分矩阵,诶评分矩阵是我们的原始数据,对不对啊,大家想到是不是都得从那个rating表里边去取啊,诶,所以我们这里定义的,大家会想到这里定义出来的常量,我是不是其实在它的同一个包下面也可以直接去引引用啊,哎,所以我这里边已经定义出来的这个reading表,其实在这里直接用也是可以的,那如果大家这么想的话,我这里不写也可以,对吧?那接下来我们的操作是什么呢?是不是该创建Spark config,创建Spark config,该创建Spark session,创建SPA Spark session啊,这个过程是不是完全一样?呃,那大家会想到,我干脆就把这个都抄下来吧,那mango这部分不写尽,尽管不写,我也有可能要读,我就都不管了啊,直接把它抄过来算了。
09:30
呃,大家看到我们这里边会引入SPAF和Spark session。呃呃,大家会想到其实我们这个mongoig好像不需要去去用,对吧?因为我们当时用这个mongo conig的时候是在下边做这个保存,诶,我们我们在哪里用了哦,我们是在这里给这个UI的时候把它写进去了,对不对,那大家会想到我这里边如果要是不设定onego conig的话,是不是直接从conig里边把它拿出来也是一样的呀,啊,当然这个就是大家会看我,我尽管上面没有写这个样例类,这里边也有mongo f,因为在同一个包下,是不是我们这里已经定义出来了呀,对吧?所以它直接可以访问的啊好,那么这里我们就不做详细的这个考察了,接下来我们是不是就应该去加载数据了。
10:24
评分数据加载评分数据的时候,大家会想到跟这边应该类似对吧?好,我们直接把这一部分copy过来,因为我们要,哎,大家看这里边有一个这个offline recommender里边的这个RA collection,那大家想到我这里边如果要是自己不想定义的话,是不是直接引也可以啊,哎,那我把它直接引进来就可以了,好,现在就完全没有问题对吧?还是同样的表,同样的操作方法,我们as movie rating,那这个是不是也也从这里边用这个就可以了,我们就不定义,另外定义样例类了。好呃,接下来大家会想到之前我们这里其实有一个操作啊,大家会看到我们最后想要的数据其实是什么数据啊,其实是不是应该是一个RA类型的数据啊,就是说ars里边本身这个包里边就定义了一个叫做RA的数据。
11:25
呃,一个样例类对吧?这样的一个数据格式,我们其实最后想要的是它,那大家会想到我们在这里做转换map的时候何必那么麻烦呢,对不对?我直接这里map的时候,把它统一map过来是不是就完事啊?大家看我这里边做一个RA,那当然这个RA还是要引入的,对吧?ML lab recommendation下面的RA,好,同样的这个操作转换过来之后,评分数据就已经有了,那大家会想到我们这里刚在这个之前,我们训练的时候直接把这个train data就放到ars.train里边去训练了,我们现在是不是没那么简单啊,现在从这里开始就不一样了,大家会想到我们是不是先要把它做一个切分啊,对,我们随机切分数据集生成训练集。
12:25
和测试级对吧,那大家会想到这里边我们用什么样的方式去做一个切分呢?呃,在RDD里边有random split方法啊,大家之前不知道用过没有,没用过是吧?那我们这里给大家调用一下啊,比方说这里我定义一个叫做splitt的一个,呃,一个常量啊,RDD大大家看我可以random split对吧?那这个后边大家看给的一个它的参数是什么呢?
13:00
它是一个权重构成的一个array,对吧?那大家会想到里边应该输的是什么东西啊,权重,那是不是就是我们说的哎,0.7 0.3 0.80.2这样的东西啊,而且他给的是一组,这是一个数组,对吧?那我们是不是应该比方说三七分开的话,我们是不是应该给0.70.3这样的一组数啊,而且大家注意啊,我们这个我们本身切分的时候,是不是并不知道谁是测试机,谁是训练机,这是我们后边定义出来的对不对,所以这里只是做一个随机的切分而已,好,那里边我们给一个scla里边的AR类型。那里边我们就是比方说我们用八二开这种方式吧,0.80.2把它做数据集划分成两个对不对,随机切分成两个,那接下来我们就给这个训练集。
14:02
Training data啊,我们就叫RDD吧,因为它切出来之后还是一个RDD对吧?最后我们传入到ars.ch里边的,要的也是一个RA类型的RDD,它就应该等于split里边的,大家会想到它是不是最后返回来的应该还是一个一个数组啊,RDD的数组对吧?所以我们是不是取它的第一个。值,这就是我们的training r DD 0.8对不对,然后下一个就是我们的test r DD,呃,这个是SPLIT1对吧,这是我们的训练级和测试级的一个划分,呃,有了这些之后,下面是我们的核心实现部分。模型参数选择,呃,那么这里需要最后输出最优参数对不对,我们这里边就不用返回了,直接在这个下面,我们把它包装,封装成一个函数吧,还是封装成一个方法啊,比方说这个叫呃,既然是模型调优,我们叫adjust。
15:19
Als parameter定义这样的一个函数里边,我们是不是要传入training r DD和test r DD啊,要把这两个东西传入,那当然了,做完之后我们把Spark。Session关掉,这就是我们整个的这个流程,然后现在最核心的是不是就是里边这个address as parameter这个方法,好,那接下来我们把这个做一个实现def just。好,那么大家看到里边我们要传入的是两个RDD,那这里边还是我们叫train data,这个RDD我们需要引入一下。
16:10
然后后边我们给一个叫test的data。呃,我们看一下这里。呃,Undein这个是因为后面我们还没有做完对吧?那大家想一下,这里边我们如果不给它的具体的返回值的话,那我们输出最优参数是不是直接打印在控制台里边就可以了,或者大家把这个定义一个返回值,我们把它呃另外定义一个变量去收集这个值,最后打印也是一样的对吧?我们这里边就直接控制台输出就好了。呃,那大家会想到在这个过程当中我们应该怎么做呢?既然是模就是参数选取,那是不是相当于我们参数应该有一组备选值啊?呃,或者说我们应该有一个取值范围,然后挨个去遍历是不是就可以了啊?在这里边我们就直接简单写啊,我们就直接定义一个result这样的一个结果,最后得到的一个结果,它应该是什么样子呢?大家想到是不是一个for循环啊,那么这个for循环我们应该对什么去做,便利是不是就是可以取得的?
17:22
参数去做一个便利啊,好,那么这里我们大家会想到,首先有rank对不对,Rank从大家会想之前我们这个经验数据给了一个50对不对,实际的这个项目里边,一般这个影特征的维度一般都是要给到几十到几百啊,甚至有些特征维度选取的很很多,上千都有可能啊,那当然了,我们这个本身数据相对来讲没有那么大,所以几十应该还是可以的,对吧?那所以我这里边做测试的时候,大家会想到可以给一组数据,比方说我这里就是20,五十一百对吧?呃,这是我们rank本身的这个影特征维度这样的一个选择,另外大家想到我是不是还应该有别的参数也可以去做选择,应该给一个参数列表啊,那在这里边。
18:22
我们还涉及到的参数有迭代次数inters,那大家想到这个,呃,其实对于我们本身模型而言是不是影响不大啊,我们这里是本身这个给的次数比较少,如果这个给的比较多,我们应该相信它基本上都能趋近于它能达到的那个最小值对吧?好,这个,所以我们这个就不去给他这个列表了,那大家更关心的是不是后面这个正则化系数啊,拉姆DA,可能这个是我们需要单独去做一个评估的,Lada它的取值,呃,这个大家写法是比较熟悉的,对吧?Skyla里边的这种遍利一个数组的一个写法,我们这里边就给它几个取值用,呃,比方说我们这里是0.001,前面选的是0.01,对不对?我们这里用0.001 0.01,我们是不是给的太太多了,我们前面取的值是什么?0.01是吧。
19:22
那我们不要不要这么多小数了,我们取一个比它大的,取一个比它小的就完事,0.1这里只是做一个示例,所以说大家会看到我们选取啊,还有一个括号对吧。大家可以看到我们这里做选取的时候,其实只是给了很简单的几个取值,我们真实在做模型选择,做这个参数选择的时候,肯定是需要给很多值去做一个复杂的计算的,对吧?挨个去校验一遍,好,那有了这个for循环之后,大家会想到接下来就是要对它,大家会想到是不是里边就是把这些参数带进去,我们去训练模型,然后去预测评分,拿到RVSE啊,这就是我们最后想做的事情,对吧?啊,那我们最后想要返回的东西是不是每一次,每大家想这个便利的时候,每一次便利拿每一次迭代拿到的就是针对一组rank和拉姆DA得到的一个模型,对不对?那我们其实是不是应该把对应的那个结果都保存一下呀,中间那个那个数据都保存一下对不对,当然我们可以另外定义一个list,然后每一次。
20:37
我们都在后面追加一个内容,这个也是可以的,这里我们用一个,呃,另外的一个写法,大家之前应该也接触过,对不对?呃,业务的它表示什么呢?是不是啊,就是我们在这个循环过程当中,每一次的中间结果是不是都会保存下来啊,对吧?所以我们在这里大家会想到这个业务的里边,我们每一次做的操作肯定是要先去训练模型,对不对?Model等于als已经有参数了,是不是直接可以as.CH了。
21:14
这个没问题对吧,这个as要要引入啊。好,然后大家看到点train里边的参数,我们传什么呢?首先是不是应该是有这个train data对不对,纯data,然后大家想到第二个参数是什么,是不是我们定义好的rank啊,第三个是ters,那这里我们就直接写死好了,比方说我们这里给一个50,我们觉得这个次数比较多的话,他应该是能够搞得定的,对吧?然后最后还有一个对,还有一个lada正则化系数。好,那做完之后大家会想到有了这样一个模型,我是不是可以计算它对应的RMSE啊?呃,那里边我们想到这个计算RMSE可能还有好几个步骤,所以我们还是把它封装成一个函数,这里边我们看起来就清爽一些,对不对?这里边我们就叫做get RM SE吧,那这个里边我们需要传入什么呢?是不是model里传入啊,因为我们要用到它里边的predict方法对不对?呃,要用到它里边最后的结果,然后我们是不是还需要传什么?计算RMSE的时候,我们要在在测试数据上做计算对不对?哎,所以这里大家要搞清楚,我们用train data去做训练,然后用测试数据去做这个测试,计算RMSE,评估它的效果怎么样,最后我们是不是返回要保存下来的东西,是什么呢?
22:54
是不是当前的rank,当前的拉姆达这个参数对吧,最后是不是还有当前的RMSE啊,我们把这三个参数选取出来,每一次迭代循环,每次迭代都把它保存下来,最后result是不是就是这样的一个列表啊好那。
23:15
我们把这个for循环做完之后,最后我们不是还要直接打印输出吗?控制台打印输出最优参数,呃,那大家会想到我直接这里一个print line什么呢?是不是应该是result里边的。嗯。大家想我是不是应该做一个salt了,呃,我是要根据哪一个元素做salt呢?是不是最后这个RSE这个元素啊,所以大家会想到这里边其实我如果要是这个thought by的话,是不是应该是用它的第三个元素去做一个salt,或者说我们thought with的话,第三个元素去做比较对不对?我们这里边其实想选取的就是选完之后它的那个I'SE最小的那个就可以了,对不对啊,所以我们可能是先做一个sort by。
24:19
这个RMSE,然后大家会想到,呃,不是RSE啊,是我们每一个元素里边的是不是第三个最后一个元素啊,然后我们最后是不是把它的第一个元素拿出来head就可以了,大家就会想这个是相对来讲就有点麻烦了,对不对?你先salt,再再had,再取第一个,尽管我们的算法是这样,大家看它有提示,我是不是可以把它replace一下呀,用一个更简单的方式来写,那就是直接mean by是不是就可以了,大家看有这个方法对吧?就是用这个第三个元素按照它做一个判断标准,我们取它最小的对应的那一组对吧?所以我们把这个参数打印出来就可以。
我来说两句