機器學習之初識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閱讀 230,527評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內容