實驗指導書 下載下傳密碼:ovyt
本篇部落客要講解,吳恩達機器學習第六周的程式設計作業,作業内容主要是實作一個正則化的線性回歸算法,涉及本周講的模型選擇問題,繪制學習曲線判斷高偏差/高方差問題。原始實驗使用Matlab實作,本篇部落格提供Python版本。
目錄
1.實驗包含的檔案
2.正則化線性回歸算法
3.完整項目代碼
1.實驗包含的檔案
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0DOXl1b5YFZr50MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL1ATN2IDOyUTM1ETMxgTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
檔案名稱 | 含義 |
ex5.py | 實驗主程式 |
ex5data1.mat | 實驗資料集 |
featureNormalize.py | 特征縮放程式 |
learningCurve.py | 繪制學習曲線程式 |
plotFit.py | 繪制拟合曲線 |
linearRegCostFunction.py | 線性回歸(正則化)代價函數 |
trainLinearReg.py | 訓練程式 |
ployFeatures.py | 為原始特征增加多項式特征 |
validationCurve.py | 驗證曲線 |
實驗任務:完成紅色部分程式的關鍵代碼
2.正則化線性回歸算法
- 打開主程式ex5.py
'''第1部分 加載并可視化資料集'''
print('Loading and Visualizing data ...')
data = scio.loadmat('ex5data1.mat') #讀取矩陣格式的資料集
#資料集已經被分成了訓練集、驗證集、測試集三部分
X = data['X'] #提取訓練集原始輸入特征
y = data['y'].flatten() #提取訓練集輸出變量 并轉換成一維數組
Xval = data['Xval'] #提取驗證集原始輸入特征
yval = data['yval'].flatten()#提取驗證集輸出變量 并轉換成一維數組
Xtest = data['Xtest']#提取測試集原始輸入特征
ytest = data['ytest'].flatten()#提取測試集輸出變量 并轉換成一維數組
m = y.size #訓練樣本數
# 可視化訓練集
plt.figure()
plt.scatter(X, y, c='r', marker="x")
plt.xlabel('Change in water level (x)')
plt.ylabel('Water folowing out of the dam (y)')
input('Program paused. Press ENTER to continue')
- 訓練集可視化效果
- 計算正則化線性回歸算法的代價函數
'''第2-1部分 編寫正則化線性回歸的代價函數'''
theta = np.ones(2) #初始化參數為1 隻有一個原始輸入特征 是以兩個參數
cost, _ = lrcf.linear_reg_cost_function(theta, np.c_[np.ones(m), X], y, 1) #為原始輸入特征矩陣前面加一列1 正則化系數為1
#傳回計算的代價并與期望進行比較 驗證程式正确性
print('Cost at theta = [1 1]: {:0.6f}\n(this value should be about 303.993192'.format(cost))
input('Program paused. Press ENTER to continue')
'''第2-2部分 計算正則化線性回歸的梯度'''
theta = np.ones(2)#初始化參數為1 隻有一個原始輸入特征 是以兩個參數
cost, grad = lrcf.linear_reg_cost_function(theta, np.c_[np.ones(m), X], y, 1) #為原始輸入特征矩陣前面加一列1 正則化系數為1
#傳回計算的代價和梯度,并将梯度與期望進行比較 驗證程式正确性
print('Gradient at theta = [1 1]: {}\n(this value should be about [-15.303016 598.250744]'.format(grad))
- 編寫linearRegCostFunction.py
def h(theta,x): #假設函數
return x.dot(theta)
def linear_reg_cost_function(theta, x, y, lmd):
m = y.size #訓練樣本數
cost = 0
grad = np.zeros(theta.shape)
myh=h(theta,x) #假設函數值
cost=(myh-y).dot(myh-h)/(2*m)+theta[1:].dot(theta[1:])*(lmd/(2*m)) #注意不懲罰第一個參數
grad=(myh-h).dot(x)/m
grad[1:]+=(lmd/m)*theta[1:]
return cost, grad
與期望值一樣,我們的程式是正确的:
- 訓練線性回歸
'''第3部分 訓練線性回歸'''
lmd = 0 #相當于不使用正則化
theta = tlr.train_linear_reg(np.c_[np.ones(m), X], y, lmd) #傳回訓練後的最優參數
#畫出拟合的曲線
plt.plot(X, np.dot(np.c_[np.ones(m), X], theta))
- 檢視訓練程式trainLinearReg.py
def train_linear_reg(x, y, lmd):
initial_theta = np.ones(x.shape[1]) #初始化參數為1
def cost_func(t): #計算代價
return lrcf.linear_reg_cost_function(t, x, y, lmd)[0]
def grad_func(t): #計算梯度
return lrcf.linear_reg_cost_function(t, x, y, lmd)[1]
#調用進階優化方法
theta, *unused = opt.fmin_cg(cost_func, initial_theta, grad_func, maxiter=200, disp=False,
full_output=True)
#傳回最優的參數
return theta
- 在訓練集上的拟合效果
由于原始輸入特征隻有1個,是以模型的拟合效果不是很好,之後我們在原始輸入特征的基礎上增加多項式特征。
- 繪制此時的學習曲線
'''第4部分 繪制線性回歸學習曲線'''
lmd = 0 #相當于不使用正則化
#傳回不同訓練樣本下的訓練誤差和驗證誤差
error_train, error_val = lc.learning_curve(np.c_[np.ones(m), X], y, np.c_[np.ones(Xval.shape[0]), Xval], yval, lmd)
#繪制學習曲線
plt.figure()
plt.plot(np.arange(m), error_train, np.arange(m), error_val)
plt.title('Learning Curve for Linear Regression')
plt.legend(['Train', 'Cross Validation'])
plt.xlabel('Number of Training Examples')
plt.ylabel('Error')
plt.axis([0, 13, 0, 150])
- 編寫learningCurve.py
def learning_curve(X, y, Xval, yval, lmd):
m = X.shape[0] #訓練樣本數
error_train = np.zeros(m) #不同訓練樣本對應的訓練誤差
error_val = np.zeros(m)#不同訓練樣本對應的驗證誤差
for i in range(m):
x=X[:i+1,:]
y1=y[:i+1]
theta=tlr.train_linear_reg(x, y1, lmd)
error_train[i]=lrcf.linear_reg_cost_function(theta, x, y1, lmd)[0]
error_val[i]=lrcf.linear_reg_cost_function(theta, Xval, yval, lmd)[0]
return error_train, error_val
- 可視化學習曲線
曲線特點:驗證誤差随樣本增加不斷減小,并趨于平緩;訓練誤差随樣本增加不斷增大,最後也趨于平緩;并且二者非常接近,交界處對應的誤差比較大。
根據學習曲線的特點,此時模型出現了高偏差的情況,也就是欠拟合。此時增加更多的訓練樣本用處不大,應該增加更多的輸入特征。
- 增加多項式特征
'''第5部分 增加多項式特征'''
p = 5 #多項式的最高次數
#分别對訓練集、驗證集、測試集的原始輸入特征矩陣增加新的多項式特征,傳回新的輸入特征矩陣 再加一列特征1 友善矩陣運算
#并對新的輸入特征矩陣進行特征縮放 使各個特征的取值範圍相近 加快優化速度
#驗證集和測試集特征縮放使用的均值和方差 使用訓練集計算的均值和方差
X_poly = pf.poly_features(X, p)
X_poly, mu, sigma = fn.feature_normalize(X_poly)
X_poly = np.c_[np.ones(m), X_poly]
X_poly_test = pf.poly_features(Xtest, p)
X_poly_test -= mu
X_poly_test /= sigma
X_poly_test = np.c_[np.ones(X_poly_test.shape[0]), X_poly_test]
X_poly_val = pf.poly_features(Xval, p)
X_poly_val -= mu
X_poly_val /= sigma
X_poly_val = np.c_[np.ones(X_poly_val.shape[0]), X_poly_val]
print('Normalized Training Example 1 : \n{}'.format(X_poly[0]))
- 編寫特征映射程式ployFeatures.py
def poly_features(X, p):
X_poly=X[:]#第一列為原始輸入特征
#第2到p列 是原始輸入特征的平方到p次方
for i in range(2,p+1):
X_poly=np.c_[X_poly,X**i]
return X_poly
- 檢視特征縮放程式featureNormalize.py
def feature_normalize(X):
mu = np.mean(X, 0) #求特征矩陣每一列的均值
sigma = np.std(X, 0, ddof=1)#求特征矩陣每一列的标準差
X_norm = (X - mu) / sigma #對特征矩陣每一列進行縮放
return X_norm, mu, sigma
- 增加特征後進行訓練,可視化拟合效果和學習曲線
'''第6部分 增加多項式特征後進行訓練 可視化拟合效果和學習曲線'''
lmd = 0 #不進行正則化
theta = tlr.train_linear_reg(X_poly, y, lmd) #訓練得到最優參數
# 可視化訓練集和拟合曲線
plt.figure()
plt.scatter(X, y, c='r', marker="x")
plotft.plot_fit(np.min(X), np.max(X), mu, sigma, theta, p)
plt.xlabel('Change in water level (x)')
plt.ylabel('Water folowing out of the dam (y)')
plt.ylim([0, 60])
plt.title('Polynomial Regression Fit (lambda = {})'.format(lmd))
#繪制學習曲線
error_train, error_val = lc.learning_curve(X_poly, y, X_poly_val, yval, lmd)
plt.figure()
plt.plot(np.arange(m), error_train, np.arange(m), error_val)
plt.title('Polynomial Regression Learning Curve (lambda = {})'.format(lmd))
plt.legend(['Train', 'Cross Validation'])
plt.xlabel('Number of Training Examples')
plt.ylabel('Error')
plt.axis([0, 13, 0, 150])
print('Polynomial Regression (lambda = {})'.format(lmd))
print('# Training Examples\tTrain Error\t\tCross Validation Error')
for i in range(m):
print(' \t{}\t\t{}\t{}'.format(i, error_train[i], error_val[i]))
- 拟合效果
- 學習曲線
可以在增加多項式特征後,驗證誤差随訓練樣本的增加先不斷減小,到達一個最優值後,又開始上升;而訓練誤差一直都非常小,幾乎為0.可以判斷此時出現了過拟合的情況,可以進行正則化,調整一下lambda的值。
- 模型選擇,通過驗證集選擇一個最優的lambda值
'''第7部分 通過驗證集選擇一個最優的lambda值,模型選擇'''
lambda_vec, error_train, error_val = vc.validation_curve(X_poly, y, X_poly_val, yval)
plt.figure()
plt.plot(lambda_vec, error_train, lambda_vec, error_val)
plt.legend(['Train', 'Cross Validation'])
plt.xlabel('lambda')
plt.ylabel('Error')
print('驗證誤差')
print(error_val)
print('使驗證誤差最小的lambda取值:')
print(lambda_vec[np.argmin(error_val)]) #使驗證誤差最小的lambda取值
- 編寫validationCurve.py
def validation_curve(X, y, Xval, yval):
# 嘗試不同的lambda值
lambda_vec = np.array([0., 0.001, 0.003, 0.01, 0.03, 0.1, 0.3, 1, 3, 10])
# 每設定一個lambda值,進行訓練,傳回此時的訓練誤差和驗證誤差
error_train = np.zeros(lambda_vec.size)
error_val = np.zeros(lambda_vec.size)
i=0
for lmd in lambda_vec:
print(lmd)
theta=tlr.train_linear_reg(X, y, lmd)
error_train[i]=lrcf.linear_reg_cost_function(theta, X, y,0)[0]#注意計算誤差時lmd=0
error_val[i]=lrcf.linear_reg_cost_function(theta, Xval, yval,0)[0]#注意計算誤差時lmd=0
i+=1
print(error_train)
return lambda_vec, error_train, error_val
- 可視化不同lambda取值情況下,訓練誤差和驗證誤差曲線
是以正則化懲罰系數lambda的最佳選擇時1.0。
3.完整項目代碼
完整項目 提取碼:hbbq