[kaggle系列 一] 使用贝叶斯分类器判断是否能从泰坦尼克号生还

前两天有个朋友给我推荐了kaggle这个网站,感觉对于我这种想要学习却不太清楚如何实践的新手来说是个很有效的学习方式。
之前学的东西都比较乱,这边学一点,那边学一点,这次要一步一步的,从简到难好好搞一搞~
做这个的目的呢,主要是学习机器学习人工智能这方面的东西,通过具体的问题,把不同的算法放上去尝试,同时,也可以给想要学习的同学提供一些比较简单的方式~
之前看教程,感觉都是没有例子或者没有理论知识或者没有数据,或者理论知识讲的太专业,公式太多,有时候感觉很难下手,所以我还是通过这个系列把需要学的和做的都讲明白,让入门的人有一些良好的体验~
好了,废话不多说,开始正题

题目

连接:https://www.kaggle.com/c/titanic
这个问题大概的意思就是说,在泰坦尼克号撞冰山沉没的时候,由于没有足够的救生艇,导致不是所有人都能获救,虽然有幸运的成分在里面,但是有一部分群体获救的可能性更高,比如妇女小孩之类的人。你需要做的就是通过给定的数据,通过机器学习的方法,判断某个人是否能够生还。

简析

可以看出来这是一个二分类问题,所以我们需要一个分类器,我打算先用贝叶斯分类器搞一下,试试水,其实有其他的分类器,后面可以都试一试。

贝叶斯公式

首先可以看一下贝叶斯公式:



换个姿势可能更好理解一些:

这里所谓的求解对象,其实就是你关注的信息而已,你也可以关注别的信息,让其他的信息成为求解对象。贝叶斯公式求概率大概可以理解为:我知道过去发生了一些事情,在一件我关注的信息(求解对象)出现的时候,还有其他一些信息或者说特征,现在我掌握了除求解对象以外的信息,我关注的求解对象发生的概率是多少?

下面说一个简单的例子,也是我面试碰到过的问题,大概意思是说,我有两个完全一样的盒子,第一个盒子里放了2个红球和2个白球,另一个盒子里放了3个红球和1个白球,现在我从某一个盒子里拿了一个球,发现是红球,那么这个红球来自第一个盒子里的概率是多少?


我们定义:
P(A) = 从第一个盒子里取球的概率
P(B) = 取到红球的概率
根据前面的公式就可以算了,最后我们要求的就是

上面是贝叶斯公式简单的介绍,如果还是不太清楚就去找找别的教程吧~

贝叶斯分类器

那么贝叶斯分类器又是什么东西呢?结合我们要求解的问题,我们想要一个分类器,它的输入是乘客的各种信息(性别,年龄,舱位……),输出这个乘客是否生还。我们其实可以把乘客的信息作为输入信息,是否生还作为求解对象,根据给定训练数据,我们可以求出在某个信息下是否生还的概率和死亡的概率,取概率较大的那个,不就是一个分类器了吗?
假设我们有n个信息:



我们想要求解的对象就是是否存活,即:

利用贝叶斯公式,就有:

刚才也说了,我们要求生还概率和死亡概率两者中的较大值,两者中的分母是相同的,可以忽略,因此,我们要求的就是:


其中:



argmax 就是求最大值的坐标,这些值都可以算出来,我们就得到了一个贝叶斯分类器。
另外,虽然不需要求分母,但是我最开始写代码的时候是求了分母的,因为我想先算出个概率,然后比较一下谁比较大,但是我翻了个错误,是这样,当给定的n个事件都是互相独立的,那么就有:

但是,实际上对于这个问题,并不能用这个公式,因为事件并不是相互独立的,你可以想象当时我求出概率大于1时的懵逼心情,如果事件不是独立的,那么要求这个概率就要利用条件概率的链式法则了,这个公式比较麻烦,就不写了,有兴趣的可以去看看。

代码与结果

这是第一版代码,基本是完全按照公式写的,数据可以在前面给的链接里下到。
数据的格式大概是这样的:

import csv
import os

def readData(fileName):
    result = {}
    with open(fileName,'rb') as f:
        rows = csv.reader(f)
        count = 0
        for row in rows:
            if result.has_key('attr_list'):
                count += 1
                for i in range(len(result['attr_list'])):
                    if not result.has_key(i):
                        result[i] = []
                    result[i].append(row[i])
            else:
                result['attr_list'] = row
    return result

def writeData(fileName, data):
    csvFile = open(fileName, 'w')
    writer = csv.writer(csvFile)
    n = len(data)
    for i in range(n):
        writer.writerow(data[i])
    csvFile.close()

def calProb(data):
    result = {}
    for i in range(len(data['attr_list'])):
        result[i] = {}
    n = len(data[0])
    for i in range(len(data['attr_list'])):
        for j in range(n):
            key = data[i][j]
            if not result[i].has_key(key):
                result[i][key] = 0;
            result[i][key] += 1.0/n;
    return result

def calCondiProb(data):
    result = {}
    n = len(data[0])
    sum_n = 0
    sum_n0 = 0
    for i in range(n):
        sum_n += float(data[1][i])
        sum_n0 += 1 - float(data[1][i])
    for i in range(2, len(data['attr_list'])):
        result[i] = {}
        for j in range(n):
            key = data[i][j]
            if not result[i].has_key(key):
                result[i][key] = {0:0,1:0}
            result[i][key][0] += (1 - float(data[1][j])) / sum_n0
            result[i][key][1] += float(data[1][j]) / sum_n
    return result

def calResult(P, CP, inputs,pos,  n):
    ignores = {0:True, 1:True,3:True,8:True,9:True,10:True}
    pa0 = P[1]["0"]
    pa1 = P[1]["1"] 
    pba0 = 1
    pba1 = 1
    for i in range(n):
        if not ignores.has_key(i):
            if CP[i].has_key(inputs[i- 1][pos]):
                pba0 *= CP[i][inputs[i - 1][pos]][0]
                pba1 *= CP[i][inputs[i - 1][pos]][1]
    if (pba0*pa0 > pba1*pa1):
        return inputs[0][pos],0
    return inputs[0][pos],1

def run():
    dataRoot = '../../kaggledata/titanic/'
    data = readData(dataRoot + 'train.csv')
    P = calProb(data)
    CP = calCondiProb(data)
    test_data = readData(dataRoot + 'test.csv')
    n = len(test_data[0])
    result_list = []
    result_list.append([data['attr_list'][0], data['attr_list'][1]])
    for i in range(n):
        pid,suvived = calResult(P,CP, test_data, i, len(data['attr_list']))
        result_list.append([pid, suvived])
    writeData(dataRoot + 'result.csv', result_list)

run()

上面的代码跑出来的结果有0.74641的准确率,看起来一般般。
下面的代码用了开源机器学习框架scikit-learn,毕竟自己写公式造轮子很容易出错,用已经造好的就会方便很多,但是学习的过程写一写还是有助于理解的。这个版本又对原始数据做了一些处理,比如年龄这种需要原本的值,并且有些人没有年龄数据,我让他等于训练数据的平均值,其他的映射为一个数字了,然后有些可能对于是否生还没有太大影响的数据去掉了。并且这里用了贝叶斯公式的变种:高斯朴素贝叶斯公式,它的好处是对于像年龄这种连续的数据会处理的比较好一些。
可以在http://sklearn.lzjqsdd.com/modules/naive_bayes.html#gaussian-naive-bayes上看到其他公式,可以尝试一下。

import csv
import os
from sklearn.naive_bayes import GaussianNB

def readData(fileName):
    result = {}
    with open(fileName,'rb') as f:
        rows = csv.reader(f)
        for row in rows:
            if result.has_key('attr_list'):
                for i in range(len(result['attr_list'])):
                    key = result['attr_list'][i]
                    if not result.has_key(key):
                        result[key] = []
                    result[key].append(row[i])
            else:
                result['attr_list'] = row
    return result

def writeData(fileName, data):
    csvFile = open(fileName, 'w')
    writer = csv.writer(csvFile)
    n = len(data)
    for i in range(n):
        writer.writerow(data[i])
    csvFile.close()

def convertData(dataList):
    hashTable = {}
    count = 0.0
    for i in range(len(dataList)):
        if not hashTable.has_key(dataList[i]):
            hashTable[dataList[i]] = count
            count += 1
        dataList[i] = hashTable[dataList[i]]

def convertValueData(dataList):
    sumValue = 0.0
    count = 0
    for i in range(len(dataList)):
        if dataList[i] == "":
            continue
        sumValue += float(dataList[i])
        count += 1
        dataList[i] = float(dataList[i])
    avg = sumValue / count
    for i in range(len(dataList)):
        if dataList[i] == "":
            dataList[i] = avg

def dataPredeal(data):
    convertValueData(data["Age"])
    convertData(data["Fare"])
    convertData(data["Pclass"])
    convertData(data["Sex"])
    convertData(data["SibSp"])
    convertData(data["Parch"])
    convertData(data["Embarked"])
  
def getX(data): 
    x = []
    ignores = {"PassengerId":1, "Survived":1, "Name":1,"Ticket":1, "Cabin":1,"Fare":1,"Embarked":1}    
    for i in range(len(data["PassengerId"])):
        x.append([])
        for j in range(len(data["attr_list"])):
            key = data["attr_list"][j]
            if not ignores.has_key(key):
                x[i].append(data[key][i])
    return x

def getLabel(data):
    label = []
    for i in range(len(data["PassengerId"])):
        label.append(int(data["Survived"][i]))
    return label

def calResult(x,label, input_x):
    bayes = GaussianNB().fit(x,label)
    return bayes.predict(input_x)

def run():
    dataRoot = '../../kaggledata/titanic/'
    data = readData(dataRoot + 'train.csv')
    test_data = readData(dataRoot + 'test.csv')
    dataPredeal(data)
    dataPredeal(test_data)
    x = getX(data)
    label = getLabel(data)
    input_x = getX(test_data)
    x_result = calResult(x, label,input_x)
    res = [[test_data["PassengerId"][i], x_result[i]] for i in range(len(x_result))]
    res.insert(0, ["PassengerId", "Survived"])
    writeData(dataRoot + 'result.csv', res) 

run()

这个版本的准确率可以达到0.77511,相比上一个提高了不少,不过在kaggle上的rank还是不算太高,后面可以用用别的分类器试试~

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

推荐阅读更多精彩内容