1. 程式人生 > >ML作業1——KNN+k折交叉驗證

ML作業1——KNN+k折交叉驗證

程式碼:

# -*- coding: utf-8 -*-
"""
Created on Fri Mar 23 11:48:16 2018

@author: 安穎
"""
import numpy as np
import matplotlib.pyplot as plt 

    
#計算兩樣本間距離
def getinstance(x,y):
    #歐氏距離求平方和
    for i in range(4):
        inst = pow(x[i]-y[i],2)
    return np.sqrt(inst)

#knn 演算法
def knn(x,t_train,k):
    #距離矩陣
    disarr = []
    for i in range(len(t_train)):
        disarr.append(getinstance(x,t_train[i]))
    #argsort函式返回的是陣列值從小到大的索引值
    index = list(np.argsort(disarr))
    #記錄k近鄰點屬於不同類的數目
    indexlable = [0]*3
    for i in range(k):
        #y為第i近的點
        y = t_train[index[i]]
        if y[4] == 1:
            indexlable[0] += 1
        elif y[4] == 2:
            indexlable[1] += 1
        else:
            indexlable[2] += 1
    lable = np.argsort(indexlable)
    #返回同一類中最多的類
    return (lable[2]+1)


# k_fold 產生一個迭代器
    
#n為資料集,n_folds為折數
def k_fold(data,n_folds):
    #根據資料集格式,1/2/3類分別集中在三段
    label_a = data[:50]
    label_b = data[50:100]
    label_c = data[100:]
    #構建一個折數為n_folds的陣列,每組中的數為均分的數目,用於k折交叉驗證迴圈產生訓練集和測試集
    fold_sizes = (50 / n_folds) * np.ones(n_folds, dtype=np.int)
    #開始位置為0
    current = 0
    #迴圈產生訓練集和測試集
    for fold_size in fold_sizes:
        start, stop = current, current + int(fold_size)
        #拼接訓練集
        train_index = list(np.concatenate((label_a[:start], label_a[stop:])))
        index1 = list(np.concatenate((label_b[:start], label_b[stop:])))
        index2 = list(np.concatenate((label_c[:start], label_c[stop:])))
        train_index.extend(index1)
        train_index.extend(index2)
        #拼接測試集
        test_index = list(label_a[start:stop])
        test_index.extend(label_b[start:stop])
        test_index.extend(label_c[start:stop])
        #yield 傳回函式  用.next()或在迴圈中呼叫
        yield train_index, test_index
        current = stop # move one step forward

#交叉驗證
#迴圈計算錯誤率求均值
def cross_validate(X,kn):
    error = 0.0
    n_folds=5
    kf = k_fold(X, n_folds)
    for train_index, test_index in kf:
        error += error_rate(test_index,train_index,kn)
    return round(error/n_folds , 2)

#計算錯誤率
def error_rate(test,train,k):
    error = 0
    for i in range(len(test)):
        #預測類別
        pre_lable = knn(test[i],train,k)
        if pre_lable != int(test[i][4]):
            error += 1
    return float(error/len(test))

if  __name__    ==   '__main__':
    t_data = []
    #資料預處理
    with open('iris.txt', 'r') as data_txt:
        data = data_txt.readlines()
        for line in data:
            temp = line.split(',')
            t_data.append([float(temp[0]),float(temp[1]),float(temp[2]),float(temp[3]),int(temp[4])])
    t_data = np.array(t_data)
    #最優錯誤率
    minerror = 1.0
    #y存放錯誤率陣列
    y = []
    for j in range(120):
        newerror = cross_validate(t_data,j+1)
        if minerror > newerror:
            minerror = newerror
            mink = j+1
        y.append(newerror)
    x = range(1,121)
    #畫折線圖
    plt.plot(x,y,linewidth=3,color='r') 
    print("最小錯誤率為:"+str(minerror)+"此時k值為:"+str(mink))

知識點:

1、畫折線圖

import matplotlib.pyplot as plt
y = []
x = range(1,121)
plt.plot(x,y,linewidth=3,color='r') 

2、append()方法是指在列表末尾增加一個數據項。格式為list.append(***)

extend()方法是指在列表末尾增加一個數據集合。格式為list.extend(anotherlist)

insert()方法是指在某個特定位置前面增加一個數據項。格式為list.insert(index,***)

3、yield關鍵字(還不太懂)

yield 的作用就是把一個函式變成一個 generator,帶有 yield 的函式不再是一個普通函式,Python 直譯器會將其視為一個 generator,呼叫 fab(5) 不會執行 fab 函式,而是返回一個 iterable 物件!在 for 迴圈執行時,每次迴圈都會執行 fab 函式內部的程式碼,執行到 yield b 時,fab 函式就返回一個迭代值,下次迭代時,程式碼從 yield b 的下一條語句繼續執行,而函式的本地變數看起來和上次中斷執行前是完全一樣的,於是函式繼續執行,直到再次遇到 yield。