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

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

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

1.数据集长啥样?

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

1562751598858

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

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

k折交叉验证法:

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

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

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

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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)

结果是:

1
2
3
LeaveOneOut's Accuracy:  0.7831325301204819

10-foldCrossValidation's Accuracy: 0.7459459459459458

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

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

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