1. 程式人生 > 其它 >《機器學習》西瓜書課後習題3.4——python解交叉驗證和留一法的對率迴歸錯誤率

《機器學習》西瓜書課後習題3.4——python解交叉驗證和留一法的對率迴歸錯誤率

《機器學習》西瓜書課後習題3.4——python解交叉驗證和留一法的對率迴歸錯誤率

《機器學習》西瓜書P69

3.3 選擇兩個UCI資料集,比較10折交叉驗證法和留一法所估計出的對率迴歸的錯誤率

資料集:鳶尾花資料集

資料集屬性資訊:

1.萼片長度(以釐米計)
2.萼片寬度(以釐米計)
3.花瓣長度(以釐米計)
4.花瓣寬度(以釐米計)
5.類別:

資料集處理說明:該資料集中鳶尾花種類共有3種,分別是:Iris-setosa、Iris-versicolor和Iris-virginica,由於題目中要求採用兩種方法對資料集進行處理,因此我們將Iris-setosa和Iris-versicolor劃分在一個數據集(稱為1號資料集),並採用留一法法進行資料集的劃分,Iris-versicolor和Iris-virginica放入另一個數據集(稱為2號資料集)並使用交叉驗證法進行劃分。

【程式碼】

#Iris-setosa標記為0,Iris-versicolor標記為1,Iris-virginica標記為2
def loadDataset(filename):
    dataset_12=[]
    dataset_23=[]

    with open(filename,'r',encoding='utf-8') as csvfile:
        csv_reader = csv.reader(csvfile)
        for row in csv_reader:
            if row[4] == 'Iris-setosa':
                row[
4]=0 dataset_12.append(copy.deepcopy(row)) elif row[4]=='Iris-virginica': row[4]=2 dataset_23.append(copy.deepcopy(row) ) else: row[4]=1 dataset_12.append(copy.deepcopy(row)) dataset_23.
append(copy.deepcopy(row)) data_12 = [[float(x) for x in row] for row in dataset_12] data_23= [[float(x) for x in row] for row in dataset_23] # print(data_12) # print(data_23) return data_12,data_23

注意:在該程式中我們使用append(copy.deepcopy(row))進行深度複製,目的是避免對陣列的操作影響原陣列的變化,下同!!!!


針對2號資料集:

【程式碼思路】我們使用10折交叉驗證法對資料集每次劃分為訓練集和測試集,然後使用梯度下降法對訓練集進行訓練,並使用測試集求得每次的準確率,最終我們將10次準確率取平均值,即為最終的正確率。

【詳細過程】

  1. 首先利用python中自帶的函式進行10折交叉驗證劃分,由於返回的是劃分資料的下標,因此我們需要找到對應的資料元素,然後,對得到的訓練集和測試集中的資料進行預處理(在陣列最後增加一列1,0,1儲存真實標記),接著就可以參與訓練,我們將迭代次數設定為2000次,我們發現當迭代次數達到2000之後,準確率很難再增長,於是取2000作為終止條件,將得到的w分別與10個測試集進行運算比較,得到10組準確率,取平均值即可。

  2. 最終我們得到10折交叉驗證法進行對率迴歸得到的準確率為96%!

    #定義sigmoid函式
    def sigmoid(z):
    	return 1.0 / (1 + np.exp(-z))
    
    #計算正確率
    def testing(testset,w,testlabel):
        data = np.mat(testset).astype(float)
    
        y = sigmoid(np.dot(data, w))
        b, c = np.shape(y)  # 功能是檢視矩陣或者陣列的維數。
        rightcount = 0
    
        for i in range(b):
            flag = -1
            if y[i, 0] > 0.5:
                flag = 1
            elif y[i, 0] < 0.5:
                flag = 0
            if testlabel[i] == flag:
                rightcount += 1
    
        rightrate = rightcount / len(testset)
    
        return rightrate
    
    
    #迭代求w
    def training(dataset,labelset,testset,testlabel):
        # np.dot(a,b) a和b矩陣點乘
        # np.transpose()  轉置
        # np.ones((m,n))  建立一個m行n列的多維陣列
        data=np.mat(dataset).astype(float)
        label=np.mat(labelset).transpose()
        w = np.ones((len(dataset[0]),1))
    
        #步長
        n=0.0001
    
        # 每次迭代計算一次正確率(在測試集上的正確率)
        # 達到0.90的正確率,停止迭代
        rightrate=0.0
        count=0
        while count<5000:
            c=sigmoid(np.dot(data,w))
            b=c-label
            change = np.dot(np.transpose(data),b)
            w=w-change*n
            #預測,更新準確率
            if rightrate<testing(testset,w,testlabel):
                rightrate=testing(testset,w,testlabel)
            count+=1
        return rightrate
    
    def formdata(dataset,flag):#flag=1代表的是對一號資料集進行資料預處理,falg=2針對2號資料集
        #主要是將訓練集和測試集進行規範化處理,便於下一步進行正確率計算和迭代求w
        data=[]
        label=[]
        if flag==1:
            for row in dataset:
                label.append(copy.deepcopy(row[4]))
                row[4]=1
                data.append(copy.deepcopy(row))
        elif flag == 2:
            for row in dataset:
                label.append(copy.deepcopy(row[4]-1))
                row[4]=1
                data.append(copy.deepcopy(row))
    
        return data,label
    
    
    
    
    def changedata(dataset,train_index,test_index):#對資料集進行處理,增加最後一列為1
        trainset=[]
        testset=[]
        for i in train_index:
            trainset.append(copy.deepcopy(dataset[i]))
    
        for i in test_index:
            testset.append(copy.deepcopy(dataset[i]))
    
        return trainset,testset
        
    #10折交叉驗證法對資料集23進行分類
    def Flod_10(dataset):
        sam=KFold(n_splits=10)
        rightrate=0.0
        for train_index,test_index in sam.split(dataset):#得到訓練集和測試集的索引
    
            # 下面將索引轉化為所對應的元素,並將訓練集進行迭代,每次求出最大的正確率
            trainset,testset=changedata(dataset,train_index,test_index)
            #print(trainset)
            trainset,trainlabel=formdata(trainset,2)
            testset,testlabel=formdata(testset,2)
    
            rightrate+=training(trainset,trainlabel,testset,testlabel)
    
        print(rightrate/10)
    
    

    最終結果

[[-1.90048431]
 [-1.20567294]
 [ 2.31544454]
 [ 2.66095658]
 [-0.20997301]]
[[-1.86985439]
 [-1.3288315 ]
 [ 2.3427924 ]
 [ 2.64797632]
 [-0.16119412]]
[[-1.90055107]
 [-1.29322442]
 [ 2.37973509]
 [ 2.68461371]
 [-0.26297932]]
[[-2.00438577]
 [-1.18000688]
 [ 2.43352222]
 [ 2.65712983]
 [-0.15617894]]
[[-1.94737348]
 [-1.16692044]
 [ 2.35919664]
 [ 2.59038908]
 [-0.14542583]]
[[-1.91467144]
 [-1.22980709]
 [ 2.27891615]
 [ 2.74578832]
 [-0.23887025]]
[[-1.94810073]
 [-1.27450893]
 [ 2.37093425]
 [ 2.64955955]
 [-0.24649082]]
[[-1.99150258]
 [-1.25235181]
 [ 2.35312496]
 [ 2.75221192]
 [-0.20701229]]
[[-1.96302072]
 [-1.29024687]
 [ 2.31087635]
 [ 2.8008307 ]
 [-0.16047752]]
[[-1.9630222 ]
 [-1.35486554]
 [ 2.50563773]
 [ 2.44772595]
 [-0.25646535]]
0.96

針對1號資料集

【程式碼思路】我們使用留一法進行劃分,將資料集的75%作為訓練集,25%作為測試集,由於Iris-setosa、Iris-versicolor的個數為1:1因此採用分層抽樣的方法,我們將每種花的75%作為訓練集,25%作為測試集,然後進行迭代求準確率即可!

#留出法——對資料集12進行分類
#將75%的樣本作為訓練,其餘用作測試
def LeftOut(dataset):
    train12=[]
    test12=[]
    for i in range(len(dataset)):
        if i<=37:
            train12.append(copy.deepcopy(dataset[i]))
        elif i>50 and i<=88:
            train12.append(copy.deepcopy(dataset[i]))
        else:
            test12.append(copy.deepcopy(dataset[i]))

    trainset,trainlabel=formdata(train12,1)
    testset,testlabel=formdata(test12,1)
    rightrate=training(trainset,trainlabel,testset,testlabel)
    print(rightrate)

最終結果

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-YWyjAXfj-1620097001273)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20210504105611147.png)]
【完整原始碼】

import copy
import csv
import numpy as np
from sklearn.model_selection import KFold

#Iris-setosa標記為0,Iris-versicolor標記為1,Iris-virginica標記為2
def loadDataset(filename):
    dataset_12=[]
    dataset_23=[]

    with open(filename,'r',encoding='utf-8') as csvfile:
        csv_reader = csv.reader(csvfile)
        for row in csv_reader:
            if row[4] == 'Iris-setosa':
                row[4]=0
                dataset_12.append(copy.deepcopy(row))
            elif row[4]=='Iris-virginica':
                row[4]=2
                dataset_23.append(copy.deepcopy(row) )
            else:
                row[4]=1
                dataset_12.append(copy.deepcopy(row))
                dataset_23.append(copy.deepcopy(row))

    data_12 = [[float(x) for x in row] for row in dataset_12]
    data_23= [[float(x) for x in row] for row in dataset_23]
    # print(data_12)
    # print(data_23)
    return data_12,data_23

#定義sigmoid函式
def sigmoid(z):
	return 1.0 / (1 + np.exp(-z))

#計算正確率
def testing(testset,w,testlabel):
    data = np.mat(testset).astype(float)

    y = sigmoid(np.dot(data, w))
    b, c = np.shape(y)  # 功能是檢視矩陣或者陣列的維數。
    rightcount = 0

    for i in range(b):
        flag = -1
        if y[i, 0] > 0.5:
            flag = 1
        elif y[i, 0] < 0.5:
            flag = 0
        if testlabel[i] == flag:
            rightcount += 1

    rightrate = rightcount / len(testset)

    return rightrate


#迭代求w
def training(dataset,labelset,testset,testlabel):
    # np.dot(a,b) a和b矩陣點乘
    # np.transpose()  轉置
    # np.ones((m,n))  建立一個m行n列的多維陣列
    data=np.mat(dataset).astype(float)
    label=np.mat(labelset).transpose()
    w = np.ones((len(dataset[0]),1))

    #步長
    n=0.0001

    # 每次迭代計算一次正確率(在測試集上的正確率)
    # 達到0.90的正確率,停止迭代
    rightrate=0.0
    count=0
    while count<5000:
        c=sigmoid(np.dot(data,w))
        b=c-label
        change = np.dot(np.transpose(data),b)
        w=w-change*n
        #預測,更新準確率
        if rightrate<testing(testset,w,testlabel):
            rightrate=testing(testset,w,testlabel)
        count+=1
    print(w)
    return rightrate

def formdata(dataset,flag):#flag=1代表的是對一號資料集進行資料預處理,falg=2針對2號資料集
    #主要是將訓練集和測試集進行規範化處理,便於下一步進行正確率計算和迭代求w
    data=[]
    label=[]
    if flag==1:
        for row in dataset:
            label.append(copy.deepcopy(row[4]))
            row[4]=1
            data.append(copy.deepcopy(row))
    elif flag == 2:
        for row in dataset:
            label.append(copy.deepcopy(row[4]-1))
            row[4]=1
            data.append(copy.deepcopy(row))

    return data,label




def changedata(dataset,train_index,test_index):#對資料集進行處理,增加最後一列為1
    trainset=[]
    testset=[]
    for i in train_index:
        trainset.append(copy.deepcopy(dataset[i]))

    for i in test_index:
        testset.append(copy.deepcopy(dataset[i]))

    return trainset,testset


#留出法——對資料集12進行分類
#將75%的樣本作為訓練,其餘用作測試
def LeftOut(dataset):
    train12=[]
    test12=[]
    for i in range(len(dataset)):
        if i<=37:
            train12.append(copy.deepcopy(dataset[i]))
        elif i>50 and i<=88:
            train12.append(copy.deepcopy(dataset[i]))
        else:
            test12.append(copy.deepcopy(dataset[i]))

    trainset,trainlabel=formdata(train12,1)
    testset,testlabel=formdata(test12,1)
    rightrate=training(trainset,trainlabel,testset,testlabel)

    print(rightrate)

#10折交叉驗證法對資料集23進行分類
def Flod_10(dataset):
    sam=KFold(n_splits=10)
    rightrate=0.0
    for train_index,test_index in sam.split(dataset):#得到訓練集和測試集的索引

        # 下面將索引轉化為所對應的元素,並將訓練集進行迭代,每次求出最大的正確率
        trainset,testset=changedata(dataset,train_index,test_index)
        #print(trainset)
        trainset,trainlabel=formdata(trainset,2)
        testset,testlabel=formdata(testset,2)

        rightrate+=training(trainset,trainlabel,testset,testlabel)

    print(rightrate/10)

filename="iris.csv"
data_12,data_23=loadDataset(filename)
LeftOut(data_12)
Flod_10(data_23)

【結論】

10折交叉驗證法的錯誤率:0%(存在偶然性,需要進行多次隨機抽樣取平均值,我們未進行該操作

留一法所估計出的對率迴歸的錯誤率:4%