5.0 方差与偏差
实现正则化线性回归,使用其来研究具有不同偏差-方差属性的模型。
在练习的前半部分,您将使用水库水位的变化实现正则化线性回归来预测大坝的出水量。在下半部分中,您将对调试学习算法进行诊断,并检查偏差和方差的影响。
本次的数据是以.mat格式储存的,x表示水位的变化,y表示大坝的出水量。数据集共分为三部分:训练集(X, y)、交叉验证集(Xval, yval)和测试集(Xtest, ytest)。
- 高偏差:欠拟合 高方差:过拟合
- 正则化(解决过拟合问题)
惩罚系数过大,欠拟合,惩罚系数过小,过拟合
- 学习曲线(用来判断过拟合/欠拟合)
- 训练集、验证集、测试集(6:2:2)
训练数据集:训练模型;(习得模型的参数)
验证数据集:验证模型的效果;如果模型的效果不好,则重新调整参数再次训练新的模型,直到找到了一组参数,使得模型针对验证数据集来说已经达到最优了;(将每个训练集训练好的参数在交叉验证集上计算交叉验证误差,选择误差最小的那个假设作为我们的模型,这里是用来选择lmd)
测试数据集:将此数据集传入由验证数据集得到的最佳模型,得到模型最终的性能;(作为衡量最终模型性能的数据集)
- 特征缩放
归一化和标准化的区别:
归一化(normalization):归一化是将样本的特征值转换到同一量纲下,把数据映射到[0,1]或者[-1, 1]区间内。
标准化(standardization):标准化是将样本的特征值转换为标准值(z值),每个样本点都对标准化产生影响。
- python语法
1 np.sum(a)
np.sum(a, axis=0) ------->列求和
np.sum(a, axis=1) ------->行求和
2 np.c_[ ]可以合并矩阵
3 flatten( )返回一个折叠成一维的数组。但该函数只适用于numpy对象,即array或者mat,普通的list列表不的。以将shape为(12, 1)的返回为(12, )。
4 np.std(x, axis=0, ddof=1) axis=0 计算每一列的标准差,ddof=1表示自由度为N-1
5 np.insert()函数 将向量插入某一行或列,axis=1表示增加的向量以列的形式
https://blog.csdn.net/Mxeron/article/details/113405004
6 X.shape[1] : X的列数
1 导入数据处理数据,可视化
import numpy as np
from scipy.io import loadmat
from scipy.optimize import minimize
import matplotlib.pyplot as plt
data = loadmat("ex5data1.mat")
# 训练集(12,1)
X_train, y_train = data['X'], data['y']
# 验证集 (21,1)
X_val, y_val = data['Xval'], data['yval']
# 测试集 (21,1
X_test, y_test = data['Xtest'], data['ytest']
# 加入截距项
X_train = np.insert(X_train, 0, 1, axis=1)
X_val = np.insert(X_val, 0, 1, axis=1)
X_test = np.insert(X_test, 0, 1, axis=1)
# 可视化
def plot_data():
fig, ax = plt.subplots()
ax.scatter(X_train[:, 1], y_train)
ax.set(xlabel="water level",
ylabel="water flowing out")
plot_data()
这犯了一个错误,可视化数据集的时候,全写的plt. 导致后续在数据集上画拟合曲线的时候,是两张图,错误代码如下
# #可视化训练集
# def plot_data():
# fig,ax = plt.subplots()
# plt.scatter(X_train[:, 1], y_train)#x取第二列
# plt.xlabel('Change in water level (x)')
# plt.ylabel('Water flowing out of the dam (y)')
# plt.show()
2 损失函数
def reg_cost(theta, X, y, lamda):
cost = np.sum(np.power((X @ theta - y.flatten()), 2))#对y进行降维
reg = np.sum(np.power(theta[1:], 2)) * lamda
return (cost + reg) / (2 * len(X))
# theta = np.ones(X_train.shape[1])#theta初始为一维
# lamda = 1
# print(reg_cost(theta, X_train, y_train, lamda))
3 梯度函数
def reg_gradient(theta, X, y, lamda):
grad = (X @ theta - y.flatten()) @ X
reg = lamda * theta
reg[0] = 0#第一项不做正则化
return (grad + reg) / (len(X))
# print(reg_gradient(theta, X_train, y_train, lamda))
4 神经网络训练参数并可视化lmd=0时拟合的线性方程
# 训练参数并可视化拟合曲线
from scipy.optimize import minimize
def train_model(X,y,lmd):
theta = np.ones(X.shape[1])
res = minimize(fun = reg_cost,
x0 = theta,
args = (X,y,lmd),
method = 'TNC',
jac = grad_reg)
return res.x #易错!!!!记得返回x!!!!!!
theta_final = train_model(X_train,y_train,lmd = 0)
print(theta_final) #[13.0879035 0.36777923]
#拟合图像
fig,ax = plt.subplots()
plt.scatter(X_train[:, 1], y_train, c='r', marker='x')
plt.xlabel('Change in water level (x)')
plt.ylabel('Water flowing out of the dam (y)')
plt.plot(X_train[:,1],X_train@theta_final,c = 'b')
plt.show()
拟合曲线显示拟合的不是很好,由第5步绘制出lamda = 0的学习曲线可以看出存在高偏差问题。若二者损失都大且差距不明显,则设定的模型过于简单,无法很好的拟合数据,存在欠拟合问题。
5 画学习曲线,判断高偏差、高方差问题
训练样本从开始递增进行训练,比较训练集和验证集上的损失函数的变化情况,
学习曲线用来判断样本存在高偏差/高方差的情况
若二者损失都大且差距不明显,则为高偏差,训练集低损失而验证集高损失,说明高偏差
利用选择不同的lamda来解决问题,高偏差减小lamda,高方差增大lamda
def plot_learning_curve(X_train,y_train,X_val,y_val,lmd):
train_cost = []
cv_cost = []
x = range(1,len(X_train+1))
for i in x:#x (1-13)
res = train_model(X_train[:i,:],y_train[:i,:],lmd)
# res0 = list(res.values()) #将优化后的参数字典中的数值转为列表
# res_ = res0[0]#提取参数字典中的theta
training_cost_i = reg_cost(res,X_train[:i,:],y_train[:i,:], lmd)
cv_cost_i = reg_cost(res,X_val,y_val, lmd)
train_cost.append(training_cost_i)
cv_cost.append(cv_cost_i)
plt.plot(x,train_cost,label = 'training cost')
plt.plot(x,cv_cost,label = 'cv cost')
plt.legend(loc = 1)
# plt.xlim(0, 12)
# plt.ylim(0)
plt.xlabel('number of training examples')
plt.ylabel('costs')
plt.show()
# 不考虑正则化的情况,出现欠拟合的情况
plot_learning_curve(X_train,y_train,X_val,y_val,lmd = 0)#lmd = 0,表示误差
可以看出,二者损失都较大,高偏差,因此特征映射创造多项式特征,进行多项式回归,用更复杂的函数去拟合
6 特征映射 均值归一化
# 增加多项式,从x的平方到x的多次方
def poly_feature(X, power):
for i in range(2, power + 1):#从二次方到power次方
X = np.insert(X, X.shape[1], np.power(X[:, 1], i), axis=1)#从第二列开始插入多列多项式
return X
#获取均值和方差
def get_standard(X):
# 按行计算,即求每一列的均值和方差
means = np.mean(X, axis=0)
stds = np.std(X, axis=0)
return means, stds
#标准化
def feature_normalize(X, means, stds):
X[:, 1:] = (X[:, 1:] - means[1:]) / stds[1:]#取所有行,去掉第一列
return X
# 测试,最大六次方
power = 6
#特征映射
X_train_poly = poly_feature(X_train, power)
X_val_poly = poly_feature(X_val, power)
X_test_poly = poly_feature(X_test, power)
#标准化
train_means, train_stds = get_standard(X_train_poly)
X_train_norm = feature_normalize(X_train_poly, train_means, train_stds)
X_val_norm = feature_normalize(X_val_poly, train_means, train_stds)
X_test_norm = feature_normalize(X_test_poly, train_means, train_stds)
theta_fit = train_model(X_train_norm, y_train, lamda=0)
print(theta_fit)
7 绘制多项式回归的拟合曲线
def plot_poly_fit():
plot_data()
x = np.linspace(-60, 60, 100)#生成网格数据(100,)
xReshape = x.reshape(100, 1)#从(100,)到(100,1)
xReshape = np.insert(xReshape, 0, 1, axis=1)#插入第一列1
xReshape = poly_feature(xReshape, power)#特征映射
xReshape = feature_normalize(xReshape, train_means, train_stds)#标准化
plt.plot(x, xReshape @ theta_fit, 'r--') #曲线 (x,xx@thte)
plt.show()
plot_poly_fit()
8 显示不同lamda取值下的学习曲线
# 高方差,次数太多,过拟合
plot_learning_curve(X_train_norm, y_train, X_val_norm, y_val, lamda=0)
# 加入正则化
plot_learning_curve(X_train_norm, y_train, X_val_norm, y_val, lamda=1)
# 正则化很大,欠拟合
plot_learning_curve(X_train_norm, y_train, X_val_norm, y_val, lamda=100)
9 用交叉验证集选择最佳lmd
# 用交叉验证集选择lmd
lamdas = [0, 0.001, 0.003, 0.01, 0.03, 0.1, 0.3, 1, 3, 10]
training_cost = []
cv_cost = []
for lamda in lamdas:
res = train_model(X_train_norm, y_train, lamda)
tc = reg_cost(res, X_train_norm, y_train, lamda=0)#计算训练集上的误差
cv = reg_cost(res, X_val_norm, y_val, lamda=0)#计算验证集上的误差
training_cost.append(tc)
cv_cost.append(cv)
plt.plot(lamdas, training_cost, label='training cost')#不同lamda取值下训练集的损失
plt.plot(lamdas, cv_cost, label='cv cost')
plt.legend()
plt.show()
#拿到最佳lamda
bestLamda = lamdas[np.argmin(cv_cost)]
print(bestLamda)#3
#用最佳lamda来训练测试集
res = train_model(X_train_norm, y_train, bestLamda)
print(reg_cost(res, X_test_norm, y_test, lamda=0))#4.3976161577441975