西瓜书习题3.4 (交叉验证法)

西瓜书习题3.4 (交叉验证法):

​ 选择两个UCI数据集,比较10折交叉验证法和留一法所估计出的对率回归的错误率.

1.数据集长啥样?

​ 于是就下载了一组UCI数据集,它长这样:

西瓜书习题3.4 (交叉验证法)_第1张图片

至于这些数据是啥意思、UCI又是啥,咱也不知道咱也不敢问qwq~,只知道有748行、5列,在咱眼里它就是一个 (748 * 5)的矩阵。第5列数据是0和1,那它肯定是labels,属于二分类问题。

2.啥是十折交叉验证法?啥是留一法?

k折交叉验证法:

​ 我们将数据集随机分成k份,使用其中 k-1 份进行训练而将另外1份用作测试。该过程可以重复 k 次,每次使用的测试数据不同。

西瓜书习题3.4 (交叉验证法)_第2张图片

(1)每一次迭代中留存其中一个桶。第一次迭代中留存桶1,第二次留存桶2,其余依此类推。

(2)用其他 k-1 个桶的信息训练分类器(第一次迭代中利用从桶2到桶 k 的信息训练分类器)。

(3)最终返回这 k 次测试结果的accuracy的均值

十折交叉验证法就是 k=10 的情况

留一法则是 k=总样本数 的情况,即每次迭代从总样本取一条数据做测试集,剩余的全做训练集

3.以上一顿分析猛如虎,是时候该撸码实现啦 qwq

import numpy as np
import pandas as pd


# sigmoid函数
def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))


# 梯度上升算法
def grad(train_X, labels, iters=2000):
    m, n = train_X.shape
    # 步长alpha
    alpha = 0.05
    # 初始化权重,全设为1
    weights = np.ones((n, 1))

    # 2000次迭代
    for k in range(iters):
        # 沿着梯度方向,向前移动,并更新权重
        P = sigmoid(train_X.dot(weights))
        error = labels - P
        weights += alpha * np.dot(train_X.T, error)

    return weights


# predict function 返回一组预测结果,由0或1构成的 (m,1)矩阵
def predict(test_X, weights):
    m = test_X.shape[0]
    #由sigmoid函数的性质,z = w * x , z大于0时,sigmoid(Z)>0.5 即预测为1,反之预测为0 
    p = np.dot(test_X, weights)
    for k in range(m):
        if p[k] > 0:
            p[k] = 1
        else:
            p[k] = 0

    return p


# calculate accuracy 计算准确率,一列是预测结果,一列是真实结果,结果相同则计数
def accuracy(predict_Y, Y):
    m, n = Y.shape
    Matched = 0
    for k in range(m):
        if predict_Y[k] == Y[k]:
            Matched += 1
        else:
            Matched += 0
    return Matched / m

#数据矩阵化

df = pd.read_csv('Transfusion.txt')
df['one'] = 1
#print(df)

X = np.hstack((np.mat(df['one']).T,np.mat(df.iloc[:,0:4])))
Y = np.mat(df.iloc[:,4]).T
#print(X,Y)


# 留一法:有m个数据样本,k折交叉验证是把样本划分为10等份,留一法就是k=m时的场景,即每次留1个样本做测试集,剩余的全部做训练集
total = X.shape[0]
sum = 0
for k in range(total):
    test_index = k  # 测试集下标

    test_X = X[k]
    test_Y = Y[k]

    train_X = np.delete(X, test_index, axis=0)
    train_Y = np.delete(Y, test_index, axis=0)

    # 对率回归
    weights = grad(train_X, train_Y)

    # 统计正确率
    p = predict(test_X, weights)
    sum += accuracy(p, test_Y)

print('''LeaveOneOut's Accuracy: ''', sum / total)



#十折交叉验证,把样本分成10等分,在这10份数据中依次抽取一份做测试集,剩余9份做训练集,重复10次
total = X.shape[0]
num_split = int(total / 10)
sum = 0

for k in range(10):

    #选择测试集的下标
    test_index = range(k * num_split , (k+1) * num_split)
    
    test_X = X[test_index]
    test_Y = Y[test_index]
    
    train_X = np.delete(X,test_index,axis=0)
    train_Y = np.delete(Y,test_index,axis=0)
    
    #求对率回归最优参数
    weights = grad(train_X,train_Y)
    #print(weights)
    #统计每次组的正确率
    p = predict(test_X,weights)
    sum += accuracy(p,test_Y)
    #result += predict(test_X,weights)==test_Y ? 1:0

#正确次数 / 验证总次数 = 准确率
print('''10-foldCrossValidation's Accuracy: ''',sum/10)

结果是:

LeaveOneOut's Accuracy:  0.7831325301204819

10-foldCrossValidation's Accuracy:  0.7459459459459458

可以发现留一法准确率高十折交叉验证法将近4个百分点,其实差别不是很大,但使用留一法的运算时间却十分漫长

其原因是十折交叉验证法只需训练10个模型,而留一法则需要训练跟样本数量一样多的模型,而本次样本数量为748,速度慢一大截

可见,在数据集比较大时,留一法并不适用。(例如100万个样本,则需要训练100万个模型,编程几分钟,训练好几天?)

你可能感兴趣的:(ML)