00:00
好,那么我们接下来继续来看,接下来要做的就是一步一步把刚才我们的步骤填满了,对吧?好,首先第一步计算距离,那计算距离大家会想到我定义一个distances,对吧,这是一个距离的一个列表吧,或者大家会想到我如果计算这个距离,我是不是跟每一个训练数据,我的训练数据是有105个对不对,那训练出来,呃,计算完了之后,这个distances是不是应该是一个一百零五行一列的一个数据啊,正常来讲应该是这样的一个状态,对吧?好,那么我们接下来这个东西怎么怎么计算呢?我们前面是不是已经把这个距离函数都定义好了,然后现在我们默认是不是也已经指定了它的Dis funk,就是我们的L1啊啊,那我们这里如果不改的话就用它了,对吧?所以是不是直接调Dis fok传入什么参数呢?这个是不是非常简单,就是按照我们的要求,是不是前面传一个矩阵,后面传一个向量啊呃,所以我们这里边前面的这个内容是不是self点点什么跟什么做距离计算呢?训练数据对不对,所以我们要传的是不是所有的训练数据这个向量啊的XCH对吧?X好,然后后边我们要传的是什么?嗯,是不是就是当前的这个X test啊啊,这个不是我们就是就是原始的这个X test。
01:45
是我们这里拿出来的每一个测试数据对吧?好,所以这里边就没有self了,是X test,好,这就是我们的距离的计算,这个非常简单对不对?因为前面我们距离函数都定义好了,非常简单,然后接下来我们要按照它的这个距离从大到小进行排序,诶所以大家会想到如果要做排序的话,我们是不是可以用NP里边的对应的这些sot方法啊呃,这里我们要用的是不光排序,而且我们最后拿的大家会想到我如果说诶,我我最后这个距离到底是多大,我感兴趣吗?
02:32
这个距离到底是多大,按照我们现在的简单算法,其实并不计算权重对吧?所以你到底这个距离有多大,其实我并不感兴趣,我感兴趣的其实是什么呢?其实就是你本来是第几个,然后现在排到了第几对不对,所以我们更感兴趣的其实是不是这个爱的这个本身,它的这个索引值啊。所以大家这里注意啊,我们这里要取的是什么呢?排序,然后取出我们取的是索引值,取出索引值,那这里我们就定义一个索引了,对吧?比方说我们叫它的N个精灵对不对?NN的index,那么我们可以用一个什么方法来取它的索引呢?可以用一个这个方法up so,然后大家会想到是不是把这个distances传进去,那么这个做出来的内容是什么呢?其实就是把distances首先做一个thought,做一个排序,然后a thought之前我们是不是用过类似于这样的表达啊,阿G命阿g max对吧?数学里边这个表达就是什么呢?就是取得最小值或者最大值时候的那个参数对不对?我们这里是不是也是排序之后的参数啊?这里的参数是什么,是不是就是这。
03:57
这个这是一个列表对吧,是不是这个列表的索引值啊,所以大家这里大家也可以看一下啊,我们看这个。
04:05
比方说我们这里定义一个,我们把它定义成一个nprray。呃,比方说我们就这样吧,呃,随便给几个数啊,我们看一眼,然后我们调用这个NP.a然后把这个Dis传进去。大家会看到,诶大家看啊,我们把这个还是print一下吧,出来之后大家会看到它是10423,这代表什么呢?从小到大排序对不对,第一个是不是最小啊,二是不是最小啊,第零个是不是第二小三第二小对不对?第呃第三小的是第四个101234,最后一个423对不对,然后是532,然后是3242对不对?所以大家看得到的是不是就是按照我们索引值的一个排序啊。
05:15
哎,所以我们是不是其实就要这个东西啊,哎,我们就是想知道,哎,我现在从近到远,它分别对应的索引是什么,然后我们到我们的y train里边去,是不是就可以找到对应这个索引它的那个当时的分类值啊,然后是不是就可统计频率了,诶这就是我们要做的事情,所以大家如果不了解这每一步做什么的话,大家在这里写一个类似的一个小的测试用例看一看,对吧?这种解释型语言比较好的就是你随时写,随时print,然后随时看,这个非常的,呃,大家如果想要去看这些的话,我把这些东西就是还是保留在这里吧。
06:02
大家知道这个三个引号,可以认为这是一个多行注释对吧?呃,就是保留在这里,到时候我把文件拷给大家,大家可以看这个测试怎么测的啊,好,那么呃,接下来我们已经拿到了他们由近到远所有的这个索引之后,那接下来大家知道我是不是要去拿到他们对应的那个类别了呀,所以这里我们。呃,我们的类别就叫呃,还是叫predict吧,因为跟下面的这个y y predict很接近,对吧?我们就是说他的那个预测值对不对,就是它对应的那个分类值,这个其实应该叫分类值啊,或者我们叫NNY吧,NNY就是说我们N个近邻,它们对应的那个Y值到底是什么对不对,它的那个呃,就是分类的类别到底是什么?所以大家想我们是不是要到self的y train里边去取了呀,是不是到这个里边去取啊,根据我们这里的n n index是不是就可以把它取出来,是不是这样的一个做法啊,所以大家可以在这里看到,诶,我们后边这个取的时候怎么去取呢?我们是不是还要取最近的KK的点啊啊,那大家会想到我们这里是把所有的都取出来了,这里要取K个怎么取?
07:29
对呃,首先我们是不是n n index先传进去啊,然后在里边还要取K个点对吧,我把这里空开一个格啊,大家看到里边就是这个参数取K个点怎么取呢?啊,那我们还是在这里给大家测一测啊好呃,那么假如说我在这里边。把这个先给出来啊。我们的n n index,假如就等于这个的话,然后大家会想到我们的n n index。
08:09
呃,大家直接这个print一下,能看到它是这样的一个列表,对吧?假如我们现在要取前K个,假如说这个里边有五个值,我们取前三个吧,怎么取呢?是不是可以做切片啊,因为它已经排好序了,我们是不是从头开始取切片,取前三个是不是就是三个值啊?哎,这是我们前面讲过的这个数组切片对不对?所以我们这里的选取是不是就是然后从头开始取对吧?取几个呢?这个K是在哪里定的?是不是我们self里边预设了一个属性叫n nes啊,哎,所以我们还是从这里去取啊。那就是self.n nes,大家不要看这个,好像写起来这个表达式挺长,但是其实这这就是一个东西对吧?其实就是要把它做一个切切片,把这个前K取出来,好呃,那么做完之后呢,大家会发现这个得到的是一个什么东西呢?我们把这个再做一个,呃,就是我们把这个y train这个对应的东西可以拿出来看一下啊,Y train我们在这里外面应该就有y train对不对?呃,我们要根据这个来取,好大家看到这个拿出来的是不是,哎,这这就是Y值对不对,因为我们这里对应的Y肯定有这几个对吧?所以这是可以把它拿出来的,211,这就是分类的值,但是拿到的这个Y是一个二维数组,对吧?那这个之后,我们其实是想要根据这个拿出来的,这个Y是不是要统计频率的呀。
09:53
那么在矩阵里边统计频率好像有点复杂,所以我们这里边把它再做一个转化,呃,大家看一下这个转化就又是一个后面做一个一个函数啊,这个RI,这个函数是做什么呢?就是把我们这里的一个矩阵,一个NDRA的这样一个矩阵展开成为一个一维的数组,那比方说这里我如果直接一下的话。
10:19
大家会看到是不是就变成了一个一维啊,对吧?所以当然大家也可以reha对吧?呃,但是我们这里就很简单的做一个RI就可以了,好,那么接下来我们已经做完了三步,最后一步了,对吧?最后一步就是把我们这里边的频率要统计出来,那么大家能够想到这个应该怎么做呢?这这里边我们现在的这个,呃,就是NNY里边是不是全是这个012这样的类型值啊,对吧?那么这样的类型值我们应该怎么样去统计一下呢?呃,这样啊,我把我把前面这个值还是全改到150之内啊,102,呃,比方说这里边13哦,呃,前面这是我们的Dis,这个不用改对吧?那我们可能要多给几个数。
11:16
好,那么这里我们可能选取前六个吧,多一点啊,然后现在我们看一下,诶,这我们把它print一下。因为我们定义了变量,所以它不直接输出了,对吧?好呃,那么大家看到这里边有这个012都有了,对吧?然后我们现在要做的是统计这里边分012分别出现的次数对不对,我们能想到最简单的实现是不是写一个for循环遍历一遍啊,遍历一遍然后定义三个计数器对不对,然后你有几类我就定义几个计数器,然后我一个一个去比对,出现一个一,我我这个一的计数器就加一,出现一个二,二的计数器就加一,这是我们能想到的一个简单实现,对吧?啊,大家可以去自己去写一下,这里给大家介绍一个就是Python里边,大家看它经常就有这种这种做法啊,这种黑魔法,然后就是用很简单的一个做法就能把它搞定,比方说我们这里最后是要给这个y predict I,对不对,最后要把它付出来,那么我们要选取频率出现。
12:37
最高的那个分类值,那么我们怎么选呢?来看一下这个表达NP点阿格max,阿格max是什么?就相当于是要选取最大的那个值的下标,对不对啊,那么大家看他为什么用了这么一个东西呢?我们关键看里边的这个东西啊,NP.b count n NY,就这么一句,把这个直接搞定,那么大家看一下这个看起来确实有点呃,这个操作确实有点有点奇怪啊,我们看一眼吧,那么这个冰count它到底是一个什么操作呢?
13:19
并其实是binary的意思,对吧,二进制的统计对不对?那么b count到底是做了什么什么事情呢?他其实是要统计后边这个数组里边每个值出现的次数,它是这样一个操作,所以对于我们前面这一个,我们这不是已经有了NNY了吗?那么大家这里如果要是去测试一下的话,B count n NY大家会看到,诶,我们还是把这个。复制过来啊,大家会看到这是一个什么东西呢?它怎么会变成了一个132这样的一个东西呢?我们还是把NNY先打出来啊,这个NNY是怎么出来的,这个132呢?
14:15
大家会看到一代表什么?诶,是不是零出现了一次啊,是不是这里是一,三代表什么?诶,一是不是出现了三次啊,这里是一,二代表什么?二出现了两次对不对?哎,所以大家会看到它其实就是说做了这个冰抗的操作之后,它的结果又是一个数组,这个数组代表什么呢?这个数组零索引位置就代表零出现的次数,一索引位置就代表一出现的次数,二索引位置就代表二出现的次数。啊,所以大家会看到它为什么叫做b count呢?就是相当于把我们整个所有的值,它的索引值就是当当做索引全排在这里了,然后统计他们每一个出现的次数,对不对?那这个用法是不是就特别适合我们这个应用场景啊,我们是不是就是要统计它的次数啊,然后大家看后边这里又做了一个什么操作,是不是阿max啊。
15:22
Max是不是统计这里边最大的那个,找到最大的那个阿max是不是得到它的那个索引值啊,那它的索引值是不是本身就是当时我们那个分类值啊,所以这是不是最后就是我们的要的那个分类值就是这个啊啊所以大家可以看,按照我们这个例子,是不是它的分类应该是一啊,那我们看一下我们如果外面再来一个阿max的话,最后输出是不是就是一对吧,因为一出现了三次对不对?阿格max得到最大的是三,那么三对应的所以是一,所以反馈是不是就是一啊,诶,所以我们对应的类别就是一啊,这就实现了我们这个功能,所以呃,大家可以就是下来之后把这个再好好的梳理梳理看一看啊,当然,如果大家觉得这个太麻烦的话,也也不用整这么这么拉风的操作,一行搞定非要一行搞定对吧,老老实实写一个放。
16:22
啊,定义三个计数器,一个一个加对不对,这个就很很容易理解啊,好,那么接下来我们已经把这个核心算法就已经搞定了,对吧?好,那么我们直接运行完成,然后接下来第二步啊,第三步算法已经完成,接下来是不是我们可以测试了,好,我们做一个测试吧,调用我们实现好的这个类库,然后去实现我们的这个算法,那测试的时候首先是不是得定义一个KNN这个类的实例啊,先定义一个实例对象对吧?那么我们这里是KNN,大家看这个跟我们这个面对对象的这种创建实例也是一样的啊,只不过就是不需要各种,呃,Java里边的DA new对不对,我们这里面不需要啊,那么创建实例,创建实例的时候可以调创建实例,就是要调用它的构造方法,对不对,那么这个构造方法里边有参数,我们是不是可以创。
17:22
团参啊,那这个里边我们默认的这个精灵我们不要给,我们不要给这个一了,嗯,Nes我们给一个给一个三吧,稍微多一点吧,因为直接选最近的这个一,感觉有点不太靠谱对吧?好,那么接下来怎么做呢?先定义一个实例对吧?呃,然后我们是不是就是做这个训练啊,那训练模型,其实这个很简单,我我们这里就没有做任何的事情,那我们也得调一下这个fit方法,是不是要把这个X train和y train要附进去啊。
18:14
啊,这是我们需要做的啊,所以训练的时候把训练数据传进去,然后接下来那么大家会想到训训练完成之后,是不是就可以对测试数据去做预测了啊,对吧?传入测试数据做预测,那么预测我们调的是什么方法呢?是不是predict啊。传的数据是什么?诶大大家注意啊,这个是,呃,我们是不是传入了测试数据之后,其实我们是想拿到他最后预测的结果的呀,所以我们还是定义一个这个y predict吧,这个变量名一样没关系对吧?它的作用域作用域不同,里边我们是不是要传入X test啊,然后传入进去,我们再把它一个一个遍利对吧?所以大家会发现我们这里边定义的时候考虑的复杂一点,可以传这个多个样本点,我们在外边是不是就非常舒服啊啊,这个就直接一行搞定对吧?直接把这个所有的X test传进就完事了,好,那么接下来我们还可以,那这个we predict已经算出来之后,我们本身的X test,它对应真实的分类是不是在y test里边是已经有的呀,然后根据。
19:43
Y test和y predict,我们是不是可以去考察一下它的准预测准确率,诶,这就是我们评估的策略了,对吧?那么这个预测准确率我们就简单的按它的分类,就按我们分类的这种,呃,评估方式不是之前跟大家说过是有这个准确率,精确率,还有还有那个recall召回率吗?呃,这个精确率和召回率一般是在二元分类里边可能用的会多一点,对吧?我们这个直接分成三类,这种平铺的这种分类,可能一般我们就用精确准确率就可以了,对不对啊,就是你算一个分类对的是多少就可以了。好,那么我们这里呃,求。
20:27
求出预测准确率,好,那么我们这里就定义一个a accc accuracy,定义一个accuracy,它应该等于什么呢?是不是我们可以直接调库啊,啊,这就省得我们再去再遍历一遍,挨个去比对了,对不对啊,大家想写也可以自己去写,那我们这里就直接调啊,我们那个库叫什么呢?SQ2里边有一个叫accuracy score这样一个方法,对吧?所以我们可以直接把它传进去,那它的参数是什么呢?当然就是我们的真实值y test,然后还有我们的预测值y predict,对不对啊,直接传进去就完事,因为我们的这个类型是不是都是一个数组啊,对吧?这个都是一个二维的这个数组,那么我们这里的方法,他要的就是这个矩阵形式,二维数组直接传进去就好,呃,然后我们可能想要看一个百分比的话,那么。
21:27
就是大家还可以在后面再乘个100对吧,这个就无所谓了,好,那么我们接下来就可以做一个print啊好,我们这里预测准确率,呃,当然这里大家可以做那个标准输出对吧,就是加上那个,呃,就是什么点几F啊,做一个小数点截取对不对,我们这里就不做那个复杂的复杂的做法,诶这个看一眼啊,看起来好像不是半角哈,呃,我们这里把这个accuracy输出。
22:04
大家看一下诶,哪里写错了。Can predict。Funk,呃,Not啊,我们看一下。A funk。Dis funk。诶,我们看他报错是报在了27啊。哦,我们这里直接调这个Dis fok了,这个是不是有问题啊,对,我们是不是得用self的Dis fok对吧?因为在我们这个里,这个代码块里边并没有定义Dis方这样一个变量或者函数对不对,所以我们得调用self里边啊,就经常有这些乱七八糟的这些这种错误,好我们看一眼,现在就得到了预测准确率,这是不是93.3%啊啊,就看起来还不错的一个结果,对吧?啊,那么大家肯定会想到,那这个你的准确率看起来还不错,那我实际肯定还是想比对一下的嘛,因为我这里边选取的K值是三,然后呢,我这里边那个,呃,还有就是我的这个距离函数,我选的是L1,对不对,我是不是还还想换L2试一试啊,那当然大家可以就是一个一个手动去试,那我们这里可以直接再去写一个自动化的测试程序,让它一个for循环跑完是不是就可以了,大家可以这样看一下啊,我们把这个再实现一下,那我。
23:37
就重新定义了,呃,KNN对吧,那我们直接就把这个先copy过来啊,上面这一部分其实都没有什么区别。直接copy过来。呃,我们这里大家注意,如果我们是要做一个循环的话,那我们一开始创建的时候是不是就不应该指定这个到底几个邻居啊,K就不应该指定对吧?然后我们的这个测试数据还是一样,呃,然后我们在做这个fate之前,大家会想到啊F啊,这个还是一样,但是做预测的时候,这个我们可能就需要去针对不同的模型,然后传不同的距离函数,还有K值,然后去做预测了,对吧?呃,训练数据可以先传进去,这个是一样的,然后我们这里可能需要呃,用一个list来保存我们的结果,对吧?比方说我们这里保存结果list好,那么我们这里就用一个叫result list吧,定义一个空数组,然后方便我们最后显示对不对,那。
24:50
接下来大家会想到,我们是不是要针对不同的情况,针对不同的参数选取。
25:02
呃,做预测对吧,那是不是就是一个for循环啊?呃,For什么呢?首先我们先想到是不是应该是应该有两重放循环对吧?一个是我们要针对这个你选L1还是L2这个距离函数做一个不同的循环,然后另外一个就是你还需要针对不同的K值对吧,循环一下,所以我们外层我们定义一个这个范数吧,范数不是用那个LP范式,是用P来取值的吗?我们定义一个范数,它的取值就是一和二之间。好,那么我们是不是就可以定义这个KNN的,我们已经定义了这个实例对不对,是不是可以直接他点第次的放卡这个我我们定义了这个实例之后,它的属性可以直接去赋值对不对啊好,那么它的第方K应该等于什么呢?呃,我们给它等于l distance。
26:06
呃,大家看,就是在这个Python里边是可以用这个类似类似我们在Java里边那个三元的那个判断表达式去做赋值的,对吧?Scla里边尽管没有三元表达式,但是其实和我们这里的这个Python的实现其实非常类似,对吧?是不是skyla也可以后面加if啊,对吧?所以这个大家都很熟悉啊,如果它等于一的话,那么我们就把这个Dis funk复成led对不对?然后如果要是else。L的话,那就直接附这个L2DISTANCE就完了,好,呃,那么这个我们就已经先判定好了它的距离函数,然后那么接下来大家会想到我是不是还得去呃,考虑不同的K取值。
27:01
那么大家这里注意啊,我要for k in range啊,运几呢?比方说我们这个就不应该从零开始了,对不对,所以我们应该指定起始对吧,比方说从一开始到十结束,这里大家注意一下啊,我们这里可能会对它的选取,还有一个有一个讲究,比方说这里我后面再加一个参数二,这是什么意思?诶,不长对不对?那么大家会能想到吗?为什么我们这里要加一个不长呢?也就是说从一开始到十柱长为二,那选出来的K值应该是什么呢?是不是就是13579啊,为什么要选13579,不选二四六八十呢?我们的KKNN最后是不是要判断哪个类别出现的更多,对不对,那一般情况我们是不是考察这种类别分类的时候,尽量避免它是偶数,在二元分类的时候就容易出现这个打平的情况啊啊对,所以这是一个基本的选择啊,当然在这种这个问题里边,我们是一个三元分类,其实呃,那那其实你这个奇数偶数,呃本身的效果没有那么明显对吧?但是在二元分类里边,一般我们选取K都要避免它是呃选一个这个偶数值,大家知道这个我们,呃这个中央政治局一般这个常委对吧,一般都是七大常委,九大常委,听说过这个。
28:38
选偶数个常委吗?问为什么不选偶数个呢?就是比举手表决的时候,对吧,至少得决出这个胜负对不对啊,所以大家能够想到啊,我们这里也是一样的思想,好那么接下来我们是不是先得把这个K值附进来啊,那么我们的K值是不是a neighbors对吧?等于K好那么接下来大家会想到是不是跟我们的这个做法就一样了呀,对吧,我们直接把这个诶直接copy过来,哎哟,所以大家可以看到,我就可以直接针对当前的这个X test,然后去做预测了,对不对,然后我传入呃到这个predict,调用predict方法就可以得到当前的y predict,那大家想这个准确率是不是也应该是一样求啊呃,这个刚才我就应该。
29:38
一起考比过来啊,好,那么呃,这里我们求出准确率之后,是不是应该把这个准确率每一次都保存在我们的result list里边呀,对吧?那么这个list我们想要添加的话,是不是有一个方法叫apad,呃,上次我们打印它那个损失函数随着迭代次数增加而下降的过程的时候,我们是不是也用了这这套方式啊啊,这里是类似的啊,所以我们判的一个什么东西呢?我们是不是要把K传进去,然后我们是不是要把当前用的距离函数传进去啊,所以这里边又得来来一套这个了啊,所以我就直接把这个复制过来吧。L1 distance,如果P等于一的时候给这个对吧,如果不是的话,L2 distance,这里注意我们就不传这个函数进去了,我们是不是只要知道它这个。
30:38
字就可以了呀,好,然后最后一个我们是不是把这个accuracy要传进去啊,好,这样的话,当这个循环结束,我们是不是就把每一种情况它的这个,呃,所有的结果都已经保存下来了啊,那最后我们方便显示的话,我们还是用一个data frame来做一个显示啊,当然大家也可以直接把那个表格打印出来,Data的话好处在于呃呃,画个这个就就不用画图了,对吧,直接列出来应该很明显,因为我们最关键的就是看它准确率嘛,就一个数对不对?呃,而且你这个还有两个维度,就是你选择不同的距离函数,还有这个不同的K值,这个图不太好画,得画三维图了,那我们这个有点难,对不对?呃,所以这个result list把它传进来,然后那么就是data是result list,对吧,那么。
31:38
S应该等于什么呢?应该等于我们定义一下啊,第一个我们就叫K对吧,然后第二个我们应该叫什么?呃,第二个是距离函数对不对?大家看我们传进来的就是这个数据对吧?第一个是K,第二个是距离函数,第三个是第三个是预测准确度对吧?预测准确率好,那最后我们DF一下把它打印出来,好,所以大家可以看一眼啊,诶大家可以看到我们按照当前的这个选择得到的结果啊,大概是这个这个样子,就是大家直观的看的话,应该是就是距离函数的话,是L1的效果好还是L2的效果好啊呃,可能相对是L2表现好一些对吧,但是好像差别不大对不对,然后这个预测准确率,大家觉得这个应该是选取多大呢?
32:41
看起来如果选一的时候跟选三的时候其实差不多,对不对,我们刚才选三其实没有多大多大差别,但是选五的时候好像就会好很多,对不对啊,就不管是L1L2选五的时候都不错,然后如果更大的话,大家会发现其实更大不一定更好,对不对,对吧?L2的话,如果选七和九的时候,跟五的时候还都一样,都是97.7%,呃七九十七点八,那如果是L1的时候,其实你选的这个距离K值更大的时候,反而准确预测准确率变小了,对吧?啊,所以大家这就会发现这个我们KN的这个算法在做预测的时候,它的准确率跟数据本身跟我们K值的选取,距离函数的选取都是有关联的,所以这个大家可以去自己去测一测,好,那么我们就先讲到这里。
我来说两句