【吴恩达】机器学习作业ex5-->偏差与方差(过拟合/欠拟合)Python
一.前言
這次的作業主要目的是研究偏差和方差也就是過擬合和欠擬合的關系,數據分別是水位的變化來預測大壩流出的水量,其實和房價預測相差不大,要說區別就是這次將X分為了三部分,分別是訓練集,交叉集,測試集(X,Xval,Xtest)
二.代碼部分
1.數據導入
還是導入scipy,numpy,matplotlib三個工具庫,分別是用于高級算法,加載數據,矩陣計算以及畫圖
import numpy as np import matplotlib.pyplot as plt from scipy.io import loadmat import scipy.optimize as opt2.獲取數據
這里先加載ex5data1.mat的數據,然后分別將訓練集,交叉集,測試集分別取出做備用,同樣在三個分類之后的數據集第一列插入1用來和theta進行矩陣運算
# 獲取路徑 data = loadmat('ex5data1.mat') # print(data) # 一共三組數據,分別是訓練集,交叉集以及測試集 X, y = data['X'], data['y'] # 訓練集 Xval, yval = data['Xval'], data['yval'] # 交叉集 Xtest, ytest = data['Xtest'], data['ytest'] # 測試集 # print(f"Xtest:{Xtest},ytest:{ytest}")# 下面在頭部插入一列1,方便與theta矩陣相乘從而得到常數項 X = np.insert(X, 0, 1, axis=1) # 此處的含義是在X矩陣中的第0列插入1,axis為0時為行,1為列 Xval = np.insert(Xval, 0, 1, axis=1) Xtest = np.insert(Xtest, 0, 1, axis=1)下面是已經插入1之后的X數據展示
?看一下每一部分的數據集的維度
# 查看一下矩陣維度 # print(f"X.shape:{X.shape} Xval.shape:{Xval.shape} Xtest.shape:{Xtest.shape}") # print(f"y.shape:{y.shape} yval.shape:{yval.shape} ytest.shape:{ytest.shape}")3.生成一下X,y對應的視圖
需要注意的是這里X并不是全部都取,我們剛剛插入了一列一,所以這里只取第二列的數據,也就是X訓練集的原生數據
# 原始圖片(數據的位置) def plotX():plt.figure(figsize=(13, 6))plt.scatter(X[:, 1], y, label='test')plt.xlabel('waterHeight')plt.xlabel('waterover')plt.legend()plt.show() plotX()4.代價函數
下面可以看出三部分數據集所用的代價函數都是相同的,但我們在寫的時候還是需要帶上正則化的一項,方便以后處理高階過擬合的狀態以及尋找最合適的theta。
別忘了這里加上正則項?
# 代價函數(erro) def costFunc(theta, X, y, lamda):theta.shape = (X.shape[1], 1)costFront = np.square(X @ theta - y).sum()cost_reg = lamda * (np.square(theta)).sum()return (costFront + cost_reg) / (2 * len(X))看一下第一次的代價函數值
theta = np.zeros(X.shape[1]) print(costFunc(theta,X,y,0))5.梯度偏導公式
以前我們用的都是梯度下降算法 ,用theta去對自身進行更新,但這次我們要用一下高級算法,只需要提供梯度的偏導公式即可,高級算法會自動幫我們找到最優解
?這里我的思路是先把theta設置為一個倆行一列的矩陣,梯度下降公式的前半部分會得到一個倆行一列的矩陣與后半部分的theta正則化相加,具體就是X.T(2 * 12) * (X(12 * 2) * theta(2 * 1) - y(12 * 1)) -->gradientFront(2*1)? + gradient_reg(2 * 1)
這里如果不明白我要這樣寫的,我建議自己在紙上把所有矩陣畫一下,再像上面一樣矩陣相乘就會懂啦
# 梯度偏導公式 def gradientFunc(theta, X, y, lamda):theta.shape = (X.shape[1], 1)gradientFront = X.T @ (X @ theta - y)gradient_reg = lamda * theta;gradient_reg[0] = 0return (gradientFront + gradient_reg) / (1 * len(X))看一下第一次梯度的值?
print(gradientFunc(theta,X,y,1))?6.使用高級算法來找出最優theta
注意,這里傳入的theta一定要是ndarray的類型,否則會報錯
簡單說一下這個算法,其實可以理解為你給他提供了一個代價函數(costFunc),同時又提供了一個梯度偏導函數(gradientFunc),那么這個opt.minimize方法就可以自行迭代然后找出最優的解返回給你,你可以把這個過程想象成梯度下降函數,只是他幫你實現了,當然minimize這個方法一定使用了比梯度下降更好的一些算法,只是我們不再需要深入了解了
# 使用優化方法自行找出優化后的theta def trainFindMin(theta, X, y, lamda):result = opt.minimize(fun=costFunc, x0=theta, args=(X, y, lamda), method='TNC',jac=gradientFunc)return result.x優化之后的theta值為:可以看出優化后的theta返回值也是ndarray的類型
train_theta = trainFindMin(theta,X,y,0) print(train_theta)?7.看一下第一次優化所得到的theta對應的圖像
需要注意的是,這里的橫坐標選取是有些說到的,雖然選取的是X的第一列也就是訓練集的原數據,但是在選取高階的特征值時就不能這樣寫橫坐標了,原因是X的里的數據是隨機分布的,并不是從左到右遞增的(可以看第2條獲取數據那里的X展示),這里能圖像沒出問題的原因是我們設置的函數是一個一元函數,也就是一條直線,所以無論X的數據如何排列,得到的都是一條線的值,如果是高階就會有很多彎路或者折返會顯的特別亂
# d為2時候欠擬合/高偏差的圖像 def plot_theta(train_theta, X):train_theta.shape = (X.shape[1], 1)predict_num = X @ train_theta # (12*1)plotX()plt.plot(X[:, 1:], predict_num)plt.show()return plot_theta(train_theta,X)?8.學習曲線(用于分析是否是欠擬合還是過擬合狀態)
學習曲線的X軸訓練集的數量,Y軸就是代價函數的值,就是看隨著訓練集數量的不斷增加,看訓練集的代價函數值與交叉集的代價函數值之間的關系,這里X_each是用來獲取訓練集的長度,依次增加數量,cost_train與cost_cv用來存儲訓練集與交叉集的代價函數值,for循環中分倆個步驟走:1)通過高級算法找出當前所給出的訓練集的最優theta 。2)用得到的theta分別帶入訓練集與交叉集的代價函數中
注意:這里在記錄訓練集與交叉集的代價函數時不需要懲罰,lamda置為0
# 看一下學習曲線是欠擬合還是過擬合 def learningCurve(theta, X, y, Xval, yval, lamda):x_each = np.array(range(1, len(X) + 1)) # 這里加一是因為右區間是開區間,所以這里x_each存的是(1-12)cost_train = []cost_cv = []# theta_temp = []for i in x_each:theta_temp = trainFindMin(theta, X[:i, :], y[:i, :], lamda) # 同樣這里右邊也是開區間,行一直取得是0-(i-1)cost_train.append(costFunc(theta_temp, X[:i, :], y[:i, :], 0))cost_cv.append(costFunc(theta_temp, Xval, yval, 0))plt.plot(x_each, cost_train, label='cost_train', c='r')plt.plot(x_each, cost_cv, label='cost_cv', c='b')plt.xlabel('nums of trainset')plt.ylabel('cost_erro')plt.legend()plt.show()學習曲線視圖如下:
下面圖片可以看出隨著訓練集數量增加,訓練集的代價函數與交叉集的代價函數都會偏高,這明顯是欠擬合的狀態(高偏差),因為無論怎么增加訓練集,倆個數據集誤差都很大,不擬合任何一方。如果是訓練集的代價函數為零,而交叉集偏高就是過擬合的狀態(高方差)
?9.修正欠擬合(高偏差)
通過學習曲線可得知是欠擬合的狀態,我們就可以提高特征值的維度的方法來修正
# 下面提高d的維度,用以修正欠擬合的狀態 def upDegree(X_upDegree, degree):for i in range(2, degree + 1):X_upDegree = np.insert(X_upDegree, i, np.power(X_upDegree[:, 1], i), axis=1)return X_upDegree?
?10.歸一化高維度的數據集
首先獲取訓練集的平均值與標準差為后面歸一化做準備,這里用到了numpy庫的倆個經典方法,mean->平均值,std->方差
這里補充一下為什么需要歸一化,如果這里不用歸一化來讓數據控制在更小的范圍內,就會出現有的數據集非常大,有的很小,導致計算機運算耗費更多資源,且用lamda在懲罰過擬合狀態時也會非常不明顯,比如4階的過擬合的訓練集,可能lamda需要跟到10多萬才能達到懲罰的效果,如果進行了歸一化,就算6階的過擬合訓練集也僅僅需要將lamda賦值10多就會達到修正的效果
# 取得平均值和標準差 def getMeanAndStd(X):X_mean = np.mean(X, axis=0)X_std = np.std(X, axis=0, ddof=1)return X_mean, X_std下面進行歸一化操作:
公式為:(數據集 - 平均值)/ 標準差,還是很直觀的
# 進行歸一化 def normalization(X, mean, std):X[:, 1:] = (X[:, 1:] - mean[1:]) / std[1:]return X11.進行高緯度測驗
# 高階測驗 X_degree = upDegree(X, 6) X_mean, X_std = getMeanAndStd(X_degree) X_normalize = normalization(X_degree, X_mean, X_std)X_val_degree = upDegree(Xval, 6) X_val_normalize = normalization(X_val_degree,X_mean,X_std)theta = np.zeros(X_degree.shape[1]) learningCurve(theta, X_normalize, y, X_val_normalize, yval, 1)這里讓訓練集達到6階并歸一化,lamda設為1,學習曲線如下:
?下面看一下6維訓練集得到的theta在XY軸的圖像(此時lamda設的為0,沒做懲罰,可以看出擬合度很高)
theta1 = trainFindMin(theta,X_normalize,y,0) def plot_fit_curve():x = np.linspace(-80,60,100)print("x:{}".format(x.shape))x1 = x.reshape(100,1)x1 = np.insert(x1,0,values=1,axis=1)x1 = upDegree(x1,6)x1 = normalization(x1,X_mean,X_std)y1 = x1 @ theta1plt.figure(figsize=(13,8),dpi=50)plt.scatter(X[:,1],y,marker='x',color = 'red')# 坐標軸范圍plt.xlim(-100,80)plt.ylim(-80,60)# 坐標軸刻度xtick = np.arange(-100,80,20)ytick = np.arange(-80,60,20)plt.xticks(xtick)plt.yticks(ytick)plt.xlabel("water height")plt.ylabel("Water overflow")plt.plot(x,y1,'b')plt.show() plot_fit_curve()?接下來看一下當lamda為100時,學習曲線的圖像,可以看出訓練集與交叉集的代價都非常大,出現了懲罰過大,欠擬合的狀態(高偏差)
?下面是lamda為100時的擬合曲線,更直觀了
?11.試驗不同的lamda來確定最合適的theta
我簡單說一下這里代碼的步驟,首先我們設置不同的lamda,我是按照吳恩達老師給的建議從0.01開始,每次乘2遞增,將這些lamda依次帶入算法中優化出最好的theta,用這些得到的theta帶入訓練集和交叉集的代價函數中去并存儲起來,然后我們在這一列交叉集的代價函數中找出最小值所對應的lamda值,調用高級算法帶入lamda得到對應theta(trainFindMin(theta,X_normalize,y,2.56)),注意這里是歸一化的訓練集,這個最后得到的theta再帶入測試集的代價函數中就得到最終結果了
一句話總結就是:在一群lamda當中,找出能讓交叉集代價函數值最小的那個lamda,此時這個lamda所對應的theta可能是最好的狀態,帶入測試集即可
注意:1.這里記錄不同lamda的訓練集和交叉集的代價函數值時,同樣不需要懲罰
? ? ? ? ? ?2.我選擇的是7階的訓練集,因為我發現7階所得到的測試集代價函數值和原題給的結果最接近
# 下面試驗10個lamda,看看哪一個最貼合 def diffLamda(theta, X_normalize, y, X_val_normalize, yval):lamda_list = []cost_train_lamda = []cost_val_lamda = []sum1 = 0.01;for i in range(1, 11):lamda_list.append(sum1)sum1 *= 2for i in lamda_list:theta_lamda_i = trainFindMin(theta, X_normalize, y, i)cost_train_lamda.append(costFunc(theta_lamda_i, X_normalize, y, 0))cost_val_lamda.append(costFunc(theta_lamda_i, X_val_normalize, yval, 0))plt.plot(lamda_list, cost_train_lamda, c='r')plt.plot(lamda_list, cost_val_lamda, c='b')plt.xlabel('lamda')plt.ylabel('cost_value')plt.show()print(cost_val_lamda) diffLamda(theta, X_normalize, y, X_val_normalize, yval)從下面代價集合中也可以看出當lamda等于2.56時,交叉集的代價函數值最小,所以我們選取lamda = 2.56來作為測試集的參數?
?下面是lamda與倆個訓練集的代價函數關系圖象,可以看出當lamda處在2-3之間時,cost_val(交叉集)有最小值
?12.獲取測試集的代價函數
這里選取上面最適合的lamda = 2.56最為參數,并求出測試集的代價函數,原題的答案為3.8599
# 求測試集的代價函數 theta2 = trainFindMin(theta,X_normalize,y,2.56) cost_test = costFunc(theta2,X_test_normalize,ytest,0) print(cost_test)?三.全部代碼
import numpy as np import matplotlib.pyplot as plt from scipy.io import loadmat import scipy.optimize as opt# 獲取路徑 data = loadmat('ex5data1.mat') # print(data) # 一共三組數據,分別是訓練集,交叉集以及測試集 X, y = data['X'], data['y'] # 訓練集 Xval, yval = data['Xval'], data['yval'] # 交叉集 Xtest, ytest = data['Xtest'], data['ytest'] # 測試集 # print(f"Xtest:{Xtest},ytest:{ytest}")# 下面在頭部插入一列1,方便與theta矩陣相乘從而得到常數項 X = np.insert(X, 0, 1, axis=1) # 此處的含義是在X矩陣中的第0列插入1,axis為0時為行,1為列 Xval = np.insert(Xval, 0, 1, axis=1) Xtest = np.insert(Xtest, 0, 1, axis=1)# print(X) # 查看一下矩陣維度 # print(f"X.shape:{X.shape} Xval.shape:{Xval.shape} Xtest.shape:{Xtest.shape}") # print(f"y.shape:{y.shape} yval.shape:{yval.shape} ytest.shape:{ytest.shape}")# 原始圖片(數據的位置) def plotX():plt.figure(figsize=(13, 6))plt.scatter(X[:, 1], y, label='test')plt.xlabel('waterHeight')plt.xlabel('waterover')plt.legend()# plt.show()# plotX()# 代價函數(erro) def costFunc(theta, X, y, lamda):theta.shape = (X.shape[1], 1)costFront = np.square(X @ theta - y).sum()cost_reg = lamda * (np.square(theta)).sum()return (costFront + cost_reg) / (2 * len(X))theta = np.zeros(X.shape[1]) # print(costFunc(theta,X,y,0))# 梯度偏導公式 def gradientFunc(theta, X, y, lamda):theta.shape = (X.shape[1], 1)gradientFront = X.T @ (X @ theta - y)gradient_reg = lamda * theta;gradient_reg[0] = 0return (gradientFront + gradient_reg) / (1 * len(X))# print(gradientFunc(theta,X,y,1))# 使用優化方法自行找出優化后的theta def trainFindMin(theta, X, y, lamda):result = opt.minimize(fun=costFunc, x0=theta, args=(X, y, lamda), method='TNC',jac=gradientFunc)return result.x# train_theta = trainFindMin(theta,X,y,0) # print(train_theta)# d為2時候欠擬合/高偏差的圖像 def plot_theta(train_theta, X):train_theta.shape = (X.shape[1], 1)predict_num = X @ train_theta # (12*1)plotX()plt.plot(X[:, 1:], predict_num)plt.show()return# plot_theta(train_theta,X) # 看一下學習曲線是欠擬合還是過擬合 def learningCurve(theta, X, y, Xval, yval, lamda):x_each = np.array(range(1, len(X) + 1)) # 這里加一是因為右區間是開區間,所以這里x_each存的是(1-12)cost_train = []cost_cv = []# theta_temp = []for i in x_each:theta_temp = trainFindMin(theta, X[:i, :], y[:i, :], lamda) # 同樣這里右邊也是開區間,行一直取得是0-(i-1)cost_train.append(costFunc(theta_temp, X[:i, :], y[:i, :], 0))cost_cv.append(costFunc(theta_temp, Xval, yval, 0))plt.plot(x_each, cost_train, label='cost_train', c='r')plt.plot(x_each, cost_cv, label='cost_cv', c='b')plt.xlabel('nums of trainset')plt.ylabel('cost_erro')plt.legend()plt.show()# plot_theta(theta_temp,X)# theta = np.zeros(X.shape[1]) # learningCurve(theta,X,y,Xval,yval,0) # 可以看出訓練集和交叉集的誤差都挺大,所以是高偏差也就是欠擬合的狀態# 下面提高d的維度,用以修正欠擬合的狀態 def upDegree(X_upDegree, degree):for i in range(2, degree + 1):X_upDegree = np.insert(X_upDegree, i, np.power(X_upDegree[:, 1], i), axis=1)return X_upDegree# 取得平均值和標準差 def getMeanAndStd(X):X_mean = np.mean(X, axis=0)X_std = np.std(X, axis=0, ddof=1)return X_mean, X_std# print(getMeanAndStd(X)) # 進行歸一化 def normalization(X, mean, std):X[:, 1:] = (X[:, 1:] - mean[1:]) / std[1:]return X# 高階測驗 X_degree = upDegree(X, 7) X_mean, X_std = getMeanAndStd(X_degree) X_normalize = normalization(X_degree, X_mean, X_std)X_val_degree = upDegree(Xval, 7) X_val_normalize = normalization(X_val_degree,X_mean,X_std)X_test_degree = upDegree(Xtest, 7) X_test_normalize = normalization(X_test_degree,X_mean,X_std)theta = np.zeros(X_degree.shape[1]) # learningCurve(theta, X_normalize, y, X_val_normalize, yval, 100) # 在這里可以調節lamda的值來看學習曲線圖像的狀態 # print(X_normalize)# plotUpdate(theta,X_normalize,y,1)# 下面試驗10個lamda,看看哪一個最貼合 def diffLamda(theta, X_normalize, y, X_val_normalize, yval):lamda_list = []cost_train_lamda = []cost_val_lamda = []sum1 = 0.01;for i in range(1, 11):lamda_list.append(sum1)sum1 *= 2for i in lamda_list:theta_lamda_i = trainFindMin(theta, X_normalize, y, i)cost_train_lamda.append(costFunc(theta_lamda_i, X_normalize, y, 0))cost_val_lamda.append(costFunc(theta_lamda_i, X_val_normalize, yval, 0))print(f"{i},",end="")plt.plot(lamda_list, cost_train_lamda, c='r',label = "cost_train")plt.plot(lamda_list, cost_val_lamda, c='b',label = "cost_val")plt.xlabel('lamda')plt.ylabel('cost_value')plt.legend()plt.show()print()print(cost_val_lamda)# diffLamda(theta, X_normalize, y, X_val_normalize, yval)# theta1 = trainFindMin(theta,X_normalize,y,0) # 在這里可以調節lamda的值來看擬合圖像的狀態 def plot_fit_curve():x = np.linspace(-80,60,100)print("x:{}".format(x.shape))x1 = x.reshape(100,1)x1 = np.insert(x1,0,values=1,axis=1)x1 = upDegree(x1,6)x1 = normalization(x1,X_mean,X_std)y1 = x1 @ theta1plt.figure(figsize=(13,8),dpi=50)plt.scatter(X[:,1],y,marker='x',color = 'red')# 坐標軸范圍plt.xlim(-100,80)plt.ylim(-80,60)# 坐標軸刻度xtick = np.arange(-100,80,20)ytick = np.arange(-80,60,20)plt.xticks(xtick)plt.yticks(ytick)plt.xlabel("water height")plt.ylabel("Water overflow")plt.plot(x,y1,'b')plt.show() # plot_fit_curve()# 求測試集的代價函數 theta2 = trainFindMin(theta,X_normalize,y,2.56) cost_test = costFunc(theta2,X_test_normalize,ytest,0) print(f"測試集的代價函數為:{cost_test}")總結
以上是生活随笔為你收集整理的【吴恩达】机器学习作业ex5-->偏差与方差(过拟合/欠拟合)Python的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 商号是知识产权客体吗
- 下一篇: TI_BLE软件开发者指导6——L2CA