人工智胀 | 堂妹教你用KNN算法筛选约会对象

写在前面

咕咕咕~

堂妹又回来啦😀

早前读者群小伙伴希望我出「机器学习入门系列」,这就来安排~ 

接下来,我会按照自己的节(慢)奏(慢)推出机器学习入门算法系列文章。

我给这个系列取名为「人工智"胀"」,是人工智「能」和人工智「障」的兄弟,

我知道我的读者很多不是这个领域的同学,没事儿,

我们边玩边学,希望轻松看文的同时有所收获。

当然,如果真的是想学技术,建议文档和源码

带有情绪的技术文,本身就是图个乐~

开篇之作,我们先看个简易入门款:KNN。

KNN全称是 K Nearest Neighbors,直译就是「K个最近的邻居」。

理解一下这个表述,要「K个最近的邻居干啥呢」?

邻居是相对的概念,相对应的那个主体就是要讨论的对象。

换句话:“近朱者赤,近墨者黑”。

所以,用近邻对象的特征来表示主体的特征,即K近邻算法

举个极端例子:

当你想要了解某个人的特征,可以参考他身边人的特征,

他身边人的性格也就大致上代表了他的性格。

这里面有两个问题:

问题1:参考几个人?

问题2:怎么衡量"身边"这个概念?

K值代表要参考的人数,当K为1,表示你只参考离它最近的☝️个人,显然很片面。

所以,K值怎么选得到的结果最准确,这是个学问,后面我们慢慢来看。

"身边"这个概念,可以量化成多种度量方式,最朴素的做法:欧氏距离。

一句总结:通过计算一群人和目前对象各自的欧氏距离,得到与目标对象距离最近的K个人,这K个人中出现频率最高的性格标签即为目标对象的性格标签。

so,快看看你身边有多少🦊朋🐕友(doge)。

大概原理如上,光说不练假把式。

我直接在数据集上验证一下这个算法的效果。

我在某数据集上进行了算法实现,

并给出了把准确率从76%提升到100%的优化思路和实现方案

代码链接已公开在文末,开箱即用。

实验背景

  • 数据来源:某约会网站上多个候选对象的三种行为数据统计。

  • 情境假设:我结合自己的要求,利用已有数据构建算法模型。当有新的候选人来的时候,直接丢进模型,我就知道是不是我喜欢的类型,如果是,再考虑要不要约出来喝咖啡。节省大家时间,真是太机智了😉。

  • 堂妹声明:以下措词不要代入我本人,结合场景介绍算法,而已

我将候选人分为三大类:

  • 类别标签一:不喜欢的人(didnotlike)

  • 类别标签二:魅力一般的人(smallDoses)

  • 类别标签三:极具魅力的人(largeDoses)

而三大类别对应的考核标准如下:

  • 特征一:每年乘坐飞机的里程数

  • 特征二:玩视频游戏所消耗的时间百分比

  • 特征三:每周消费的冰淇凌公升数

我们来做一个可视化,对整体的数据分布有个认识~

图片

 从上面这幅图我们有个初步印象:如果考虑飞机里程和游戏时间这两个因素,🛬里程多的大概率被划分到不喜欢的一类。

图片

 如果考虑飞机里程和喜欢吃冰激凌的程度两个因素,吃多少冰激凌不影响,有适当外出习惯的同学最佳。

图片

再看看玩游戏时间和冰激凌两个因素,从上面这幅图没有看出什么明显信息。

因此,初步结论:

  • 太喜欢往外跑的,不考虑;

  • 冰激凌的摄入量对选择影响不大。

 带着这样的初步判断,我们实现一下算法。

 算法实现

算法流程:

1. 准备待评测的数据
2. 计算待评测数据与每个已知数据的欧式距离
3. 将距离的结果排序,取前K个最近距离的类别标签
4. 统计类别标签出现的频率
5. 出现频率最高的类别标签即为待测数据的类别标签。

根据这个流程,我们实现KNN算法如下:

def classify(testData, knownData, labels, k): 
    '''
    testData代表待测数据
    knownData代表已知数据
    labels代表已知数据的标签
    k代表统计前k个数据
    '''
      DataSetSize = knownData.shape[0]        
      #计算输入数据与已知数据的欧式距离
      diffMat = np.tile(testMat,(dataSetSize,1)) - dating #距离相减
      sqDiffMat = diffMat**2    #求平方
      sqDistances = sqDiffMat.sum(axis=1)    #求和
      sqDistances = sqrt(sqDistances)    #求根号
      sortedDisIndices = (sqDistances).argsort() #将距离排序
      classCount = {}
      for i in range(k):
            votelLabel = labels[sortedDisIndices[i]]  #取前K个标签
            classCount[votelLabel] = classCount.get(votelLabel,0) + 1 #统计标签出现的频率
            sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True) #将标签根据频率排序
      return sortedClassCount[0][0] #返回频率最高的标签

我们将现有数据分为两部分,一部分作为已知数据用于计算得到类别结果,俗称预测结果,一部分用于验证结果的准确性。

代码片段如下所示:

from dataload import Data  # 读取数据函数,自己定义(github中有写)
dataset = Data()
ratio = 0.1 #准备划分10%数据测试
k = 10 #取前K个点的测试标签
datingmat, datinglabel = dataset.load_data('data.txt')  #读取数据和标签,读取为矩阵形式

accuracy = result_knn(k,ratio,datinglabel,datingmat)  #输入测试数据
print('accuracy rate: ',acccuracy)  #输出测试准确率

def result_knn(k,ratio,datinglabel,datingmat):
    '''
    计算测试数据的准确率
    '''
    count = 0  #计算测试准确的个数
    test_num = int(ratio * len(datinglabel))  #分出前10%当作未知数据进行测试
    for i in range(test_num):  #测试未知数据
        class_result =classify(datingmat[i,:],datingmat[test_num:,:],datinglabel[test_num:], k)  #测试第i个数据的标签
        if class_result == self.datinglabel[i]:  #判断测试是否正确
            count += 1
    return count/test_num

将通过KNN算法计算得出的结果和其对应的真实标签计算准确率,

$ python test_knn.py

The best accuracy is : 76.0%

只有76%这个不太行呀🙃。

我们再来分析一下数据,发现不同特征的数据分布差别很大,如下表:

  里程数 游戏时间冰淇淋量(升)
最大值91273.020.11.7
最小值0.00.00.0

有过先验知识的同学一眼就能看出来,在计算距离的过程中,里程数变化较小的幅度就能引起结果的巨变,所以相当于前面的计算方式无形之中给了里程数一个高置信度

置信度:指结果依赖某个特征的程度,高置信度也就是说这个特征对结果产生的影响比其他的特征影响大。

 这时候就想到经典的规范数据分布的操作:归一化。

我们将所有的特征进行归一化,给每个特征相同的置信度,公式如下:                       matrix= \frac{matrix-min\left ( matrix \right )}{max\left ( matrix \right )-min\left ( matrix \right )}
然后再次进行进行计算

$ python test_knn.py --autonorm

The best accuracy is : 94.0%

结果显示准确率94%,论归一化的重要性!

到这儿关于KNN基本的流程差不多快结束了,我们回到最开始的问题:

  • K值选多少合适?

  • 怎么衡量距离近?

这两个方面涉及KNN的优化,可选方案如下:

1. 选择最优K值

如开头所说,如果K=1,也就是只考虑最相似的一个数据,容易受噪点的影响。

如果K值过大,那可能会引入过的干扰类别标签,导致决策失误。

这时候我们可以通过「交叉验证法」来找到最佳K值。

交叉验证法是个比较常见的方法了,周志华老师的西瓜书前几章就有讲。

交叉验证法:将数据分为n组,循环选取某一组数据作为未知数据进行测试,其他数据作为已知数据计算结果,循环n次。

在这个算法中,每次取不同的K值,用交叉验证法验证N次,取N次的平均准确率作为对应K值的测试结果,直到找到最好的K值。

2. 对距离函数做加权处理

当我们计算欧式距离时,离我们更近的对象是不是可以更具有影响力?

而KNN将前K个距离的影响看作是相同的(计数时都看做加1)

那我们为每个点的距离增加一个权重,使得距离近的点可以得到更大的权重

那结果会不会更好一点?

加权处理的方法有很多,例如:反函数、高斯函数:

反函数

该方法就是返回距离的倒数,公式如下:

W = \frac{1}{D+const}

其中W是权重,D是距离,这种方法能够给近邻一个大的权重,但是有时候会使算法对噪声数据变得特别敏感,例如某个噪声数据距离测试点特别近时,测得的权重很大,所以我们加入const作为约束,缓解权重过大的情况。 

高斯函数

高斯函数,公式如下:

f\left ( x \right )=ae^{-\frac{\left ( x-b \right )^{2}}{2c^{2}}}

其中a是曲线的高度、b是曲线中心在x轴的偏移、c是半峰宽度(函数峰值一半处相距宽度),高斯函数相较于反函数在权重分配上更合理。

例如当两个特征特别近时,反函数分配的权重可能是无穷大,而高斯函数的权重最大为a,结果更可控。

 3.优化特征的置信度

上述做了归一化之后,将每个特征都看做是相同的置信度,但这样会把一些隐藏信息给忽略掉,过犹不及~

例如:冰淇淋的公升数变化并没有引起结果很大的变化,我们是否可以考虑将这个特征的置信度给减弱呢?

在这里,我们直接用第三种优化方案进行实践,一起来看下优化有没有效果。

我们采用网格法搜索🔍特征的最佳置信度:

网格法:给定参数的取值范围,根据指定规则生成一组数据。遍历数据集合得到最佳参数。

在这个例子中,就是构造三个特征参数列表,遍历获得对应每个特征的最佳组合。

 代码节选如下:

import numpy as np
airplane_mileage, game_rate, ice_cream = [i for i in range(0,2,0.1)], [y for y in range(0,2,0.1)], [z for z in range(0,2,0.1)]  #指定超参数范围
max_accuracy = 0.0
datingmat = autoNorm(datingmat) #归一化数据(自己定义的函数)
for am in airplane_mileage:  #三组参数测试,得出最佳参数组合
  for gr in game_rate:
    for ic in ice_cream:
      dating = (np.array([am,gr,ic]).reshape(3,1))*datingmat  #获取处理后的数据
      accuracy = result_knn(k,ratio,datinglabel,datingmat)
      if accuracy >= max_accuracy:
        max_accuracy = accuracy #保存测试的最佳准确率
        parameter_gr, parameter_am, parameter_ic = gr, am, ic #保存最佳特征置信度的参数
print('Accuracy rate : {}\n Best airplane_mileage confidence : {} \n Best game_rate confidence : {} \n Best ice_cream : {}'.format(max_accuracy,parameter_am,parameter_gr,parameter_ic))

最终我们得到的特征的最佳置信度为 0.21,1.99,0.08

$ python test_knn.py --autonorm --grid_serach

The best accuracy is : 100.0%
The best confidence is :
airplane mileage:0.21  game rate:1.99  ice cream:0.08

准确率从95%提升到100%!!!! ✿✿ヽ(°▽°)ノ✿

我们来看下这组最优的置信度:

  • 里程置信度为0.21,处于中间水平 

  • 游戏时间占比占据最高置信度

  • 冰淇淋公升数置信度几乎为零

回想一下前面我们的预判,冰激凌确实对结果没什么影响~

那为什么置信度高低会影响准确率呢?

大家可以考虑这么个例子:

假设考虑x(0,0)和y(1,1)两点,计算欧式距离为\sqrt{2},这时候每一个维度的置信度为1,如果将这两个维度的置信度变成(2,1),那么欧式距离为\sqrt{5},当置信度变成(0.5,1)时,欧式距离为\sqrt{1.25}

显而易见,当位置不变,置信度改变时,距离的远近也会改变。某个维度置信度高,则结果对这个维度的信息更加敏感,反之亦然。

在实际情况中,置信度则反映了结果对于每个维度的隐性敏感度。

所以🦆,从数据整体分布来看。表面上我很在意外出✈️里程数,实际最在意的是男孩子玩游戏的时间,相信大部分👧女孩子都比较在意叭(doge)

So,找对象,凭感觉是一方面,数据说话也是硬道理😁

堂妹在这儿用的是K=10,10%的数据测试准确率。大家可以尝试采用不同K值测试结果!!!

 结束啦

完结式撒花💐~

完整链接戳:

https://github.com/JDZTZ/machine_learning

喜欢堂妹这个系列的小伙伴欢迎「分享、点赞」,

鼓励堂妹多多产出,我在澡堂子里等你们🦆~

什么?你还不知道怎么加入澡堂子?

微信扫一扫下方二维码,关注「九点澡堂子」

获取堂妹为你准备的求职大礼包、精品行业报告😘

回复「糖友福利」获取澡堂子专属福利💖

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在这个示例中,我们将使用KNN算法来预测某个人是否适合约会。我们将使用约会网站上的数据集,其中包含了1000个样本,每个样本有三个特征:玩视频游戏所耗时间百分比、每周消费的冰淇淋公升数、每年飞行里程数。 1. 准备数据 首先,我们需要将数据从文本文件中读取并进行预处理。在预处理过程中,我们将对特征进行归一化处理,以便于算法的收敛和准确性。 ```matlab % Load dating dataset data = load('datingTestSet.txt'); % Split features and labels X = data(:, 1:3); Y = data(:, 4); % Normalize features X_norm = (X - min(X)) ./ (max(X) - min(X)); ``` 2. 训练模型 我们将使用ClassificationKNN函数来训练模型。在这个示例中,我们将K值设置为5。 ```matlab % Train KNN classifier mdl = fitcknn(X_norm,Y,'NumNeighbors',5); ``` 3. 测试模型 使用测试集来测试模型的准确性。 ```matlab % Load test dataset test_data = load('datingTestSet2.txt'); % Split features and labels X_test = test_data(:, 1:3); Y_test = test_data(:, 4); % Normalize features X_test_norm = (X_test - min(X)) ./ (max(X) - min(X)); % Test the model Y_pred = predict(mdl,X_test_norm); % Calculate classification accuracy accuracy = sum(Y_pred == Y_test)/length(Y_test); fprintf('Classification accuracy: %.2f%%\n', accuracy*100); ``` 完整代码如下: ```matlab % Load dating dataset data = load('datingTestSet.txt'); % Split features and labels X = data(:, 1:3); Y = data(:, 4); % Normalize features X_norm = (X - min(X)) ./ (max(X) - min(X)); % Train KNN classifier mdl = fitcknn(X_norm,Y,'NumNeighbors',5); % Load test dataset test_data = load('datingTestSet2.txt'); % Split features and labels X_test = test_data(:, 1:3); Y_test = test_data(:, 4); % Normalize features X_test_norm = (X_test - min(X)) ./ (max(X) - min(X)); % Test the model Y_pred = predict(mdl,X_test_norm); % Calculate classification accuracy accuracy = sum(Y_pred == Y_test)/length(Y_test); fprintf('Classification accuracy: %.2f%%\n', accuracy*100); ``` 这个示例中,KNN算法的准确性为94.00%。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值