机器学习之初识KNN算法——针对泰坦尼克号生存记录建模的两种方法

KNN算法原理

本篇博客基于《机器学习实战》实现
算法原理简要概括,重在代码实现

k-近邻算法(kNN)的工作原理是:存在一个样本数据集合,称训练样本集,并且样本集中每个数据都存在标签,即样本集中每一数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似的数据(距离最近)的分类标签。

如图,图中绿点的标签是未知的,但已知它属于蓝方块和红三角二者其一,怎么判断出它属于哪一方呢?


在这里插入图片描述

kNN算法的核心思想是如果一个样本在特征空间中的k个最相邻的样本中的大多数属于某一个类别,则该样本也属于这个类别,并具有这个类别上样本的特性。

在上图实线圆圈内,红三角有两个,而蓝方块只有一个,所以它是红三角的可能性大;但在虚线圈内,红三角有两个,蓝方块却有三个,那么它是蓝方块的可能性就越大;所以对于kNN算法,k的取值不同,得出的结果可能也会不同,k的取值很大程度上决定了这个模型的准确率。

KNN算法步骤

  1. 收集数据:爬虫、公开数据源
  2. 数据清洗:处理缺失值、无关特征
  3. 导入数据,转化为结构化的数据格式
  4. 数据归一化、标准化
  5. 计算距离(欧氏距离最通用)
  6. 对距离升序排列,取前K个
  7. 判断测试数据属于哪个类别
  8. 计算模型准确率

KNN算法实现

在这里插入图片描述

其中Pclass,Sex,Age,SibSp,Parch五个特征会对标签Survived造成较大影响,在Age这列中有缺失值,这里采用中位数(median),也可以选择平均数(mean)填充。

数据获取:泰坦尼克号生存数据

方法一

首先我们需要导入数据,将DataFrame转化为一个矩阵,并将标签存入一个列表

'''
Survived:1代表生存,0代表死亡
Sex:1代表男性,0代表女性
pclass:舱位等级
sibsp:配偶、兄弟姐妹个数
parch:父母、子女个数
'''
#打开文件,导入数据
def file(path):
    # 打开文件
    data = pd.read_csv(path)
    #将DataFrame转化为矩阵
    feature_matrix = array(data.iloc[:, 1:6])
    label = []
    for i in data['Survived']:
        label.append(i)
    return feature_matrix, label

多维数组转化为矩阵在后期对于数据归一化很友好,将标签存入列表在比较真实结果与预测结果时索引简便

k-NN算法的核心步骤就是计算两者之间的距离、距离排序、类别统计,本文采用欧几里得距离公式


在这里插入图片描述

具体函数如下

#计算距离
def classify(test_data,train_data,label,k):
    Size = train_data.shape[0]
    #将测试数据每一行复制Size次减去训练数据,横向复制Size次,纵向复制1次
    the_matrix = tile(test_data,(Size,1)) - train_data
    #将相减得到的结果平方
    sq_the_matrix = the_matrix ** 2
    #平方加和,axis = 1 代表横向
    all_the_matrix = sq_the_matrix.sum(axis = 1)
    #结果开根号得到最终距离
    distance = all_the_matrix ** 0.5
    #将距离由小到大排序,给出结果为索引
    sort_distance = distance.argsort()
    dis_Dict = {}
    #取到前k个
    for i in range(k):
        #获取前K个标签
        the_label = label[sort_distance[i]]
        #将标签的key和value传入字典
        dis_Dict[the_label] = dis_Dict.get(the_label,0)+1
    #将字典按value值的大小排序,由大到小,即在K范围内,生存和死亡的个数
    sort_Count = sorted(dis_Dict.items(), key=operator.itemgetter(1), reverse=True)
    return sort_Count[0][0]

numpy有一个tile方法,可以将一个一维矩阵横向复制若干次,纵向复制若干次,所以将一个测试数据经过tile方法处理后再减去训练数据,得到新矩阵后,再将该矩阵中每一条数据(横向)平方加和并开根号后即可得到测试数据与每一条训练数据之间的距离。

下一步将所有距离升序排列,取前K个距离,并在这个范围里,统计1(生存)、0(死亡)两个类别的个数,并返回出现次数较多那个类别的标签。

这份数据中,就Age这一列而言,数据分布在0-80之间,而其他特征中,数据都分布在0-3之间,相比而言,Age这个特征的权重比较大,所以在计算距离时,需要进行归一化处理,不然会出现大数吃小数的情况

归一化公式: x' = (x - X_min) / (X_max - X_min)

#归一化
def normalize(train_data):
    #获得训练矩阵中的最小和最大的一个
    min = train_data.min(0)
    max = train_data.max(0)
    #最大值和最小值的范围
    ranges = max - min
    #训练数据减去最小值
    normalmatrix = train_data - tile(min, (train_data.shape[0], 1))
    #除以最大和最小值的范围,得到归一化数据
    normalmatrix = normalmatrix / tile(ranges, (train_data.shape[0], 1))
    #返回归一化数据结果,数据范围,最小值
    return normalmatrix

这个函数返回的是一个所有数据都分布在0-1之间的特征矩阵,不会出现偏重的情况。

最后一步:划分数据集,取九份作为训练数据集,取一份作为测试数据集,比较预测结果和真实结果,并计算出该模型的准确率,代码如下:

#测试数据
def Test():
    #打开的文件名
    path = "Titanic.csv"
    #返回的特征矩阵和特征标签
    feature_matrix, label = file(path)
    #返回归一化后的特征矩阵
    normalmatrix = normalize(feature_matrix)
    #获取归一化矩阵后的行数
    m = normalmatrix.shape[0]
    #取所有数据的百分之十
    num = m//10
    correct = 0.0
    for i in range(num):
        #前num数据作为测试集,num-m的数据作为训练集
        classifierResult = classify(normalmatrix[i,:], normalmatrix[num:m,:],
            label[num:m], 9)
        #比对预测结果和真实结果
        print("预测结果:%d\t真实结果:%d" % (classifierResult, label[i]))
        if classifierResult == label[i]:
            correct += 1.0
    print("正确率:{:.2f}%".format(correct/float(num)*100))
    # 程序结束时间,并输出程序运行时间
    end = time.time()
    print (str(end-start))

代码部分结束,代码运行截图如下


在这里插入图片描述

不同K取值对应模型准确率如下

1 2 3 4 5 6 7 8 9 10
69.66% 69.66% 75.28% 73.03% 74.16% 73.03% 77.53% 77.53% 79.78% 77.53%
对部分数据分析
在这里插入图片描述

由上图可以得到以下结论:

  1. 三舱遇难的人数最多,幸存人数微乎其微;二舱遇难的人年龄分布较广,但年龄比较小的都幸存了;而一舱遇难人数最少且大多为年纪较大的老人——舱位等级比较重要
  2. 女性整体存活率要比男性高出很多——这里存在着真爱

方法二

部分代码如下:

#计算距离
def distance(d1,d2):
    res = 0
    for key in ('Pclass','Sex','Age','SibSp','Parch'):
        #将每一行数据两两对应相减,计算距离
        res += (float(d1[key])-float(d2[key]))**2
    return res ** 0.5
#KNN算法
def KNN(data,train_data):
    data_list = [
        #只保留Survived和数据之间的距离两个量
        ({'result':train['Survived'],'distance':distance(data,train)})
        for train in train_data
    ]
    #将列表按照distance的大小排序
    data_list = sorted(data_list,key= lambda item:item['distance'])
    #取到前K个
    data_list2 = data_list[0:K]
    result_list = []
    #判断在K范围内,测试数据更偏向哪一方
    for i in data_list2:
        m = i['result']
        result_list.append(m)
    sum_1 = 0
    sum_0 = 0
    for i in result_list:
        if i == '1':
            sum_1 +=1
        else:
            sum_0 +=1
    if sum_1>sum_0:
        return '1'
    else:
        return '0'

方法二主要是运用字典方法,对数据进行读取与统计,不同于方法一的特征矩阵,但万变不离其宗,算法的核心思想都是一致的。代码运行截图如下:

在这里插入图片描述

方法一K值最终取9,方法二K值最终取8,两种方法相比,方法一建模的准确率更高,并且程序运行时间也较短,个人认为方法二运用字典知识比较容易理解,而方法一较多运用矩阵知识。

公众号“奶糖猫”后台回复“Titanic”可获取源码和数据供参考,感谢支持。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容