1. 程式人生 > >機器學習 | 吳恩達機器學習第三週程式設計作業(Python版)

機器學習 | 吳恩達機器學習第三週程式設計作業(Python版)

實驗指導書  下載密碼:fja4

本篇部落格主要講解,吳恩達機器學習第三週的程式設計作業,作業內容主要是利用邏輯迴歸演算法(正則化)進行二分類。實驗的原始版本是用Matlab實現的,本篇部落格主要用Python來實現。

 

目錄

1.實驗包含的檔案

2.使用邏輯迴歸演算法(不帶正則化)進行二分類

3.邏輯迴歸演算法(不帶正則化)進行二分類完整專案程式碼

4.利用邏輯迴歸演算法(帶正則化)進行二分類

5.邏輯迴歸演算法(正則化)進行二分類完整專案程式碼


1.實驗包含的檔案

檔名稱 含義
ex2.py 邏輯迴歸演算法(不帶正則化)主程式
ex2_reg.py 邏輯迴歸演算法(帶正則化)主程式
ex2data1.txt 第一個實驗的訓練資料集
ex2data2txt 第二個實驗的訓練資料集
mapFeature.py 在原始輸入特徵基礎上生成新的多項式特徵的程式
plotDecisionBoundary.py 繪製決策邊界的程式
plotData.py
視覺化待分類資料的程式
sigmoid.py Sigmoid函式
costFunction.py 計算邏輯迴歸(不帶正則化)代價函式的程式
predict.py 邏輯迴歸測試函式
costFunctionReg.py 計算邏輯迴歸(帶正則化)代價函式的程式

實驗任務:編寫紅色部分程式的關鍵程式碼。

2.使用邏輯迴歸演算法(不帶正則化)進行二分類

  • 開啟主程式ex2.py
data = np.loadtxt('ex2data1.txt', delimiter=',') #讀取txt檔案 每一行以','分隔
X = data[:, 0:2] #前兩列為原始輸入特徵   分別兩門考試的成績
y = data[:, 2]   #第三列是輸出變數(標籤)  二分類 0/1  1代表通過 0代表未通過

'''第1部分 視覺化訓練資料集'''
print('Plotting Data with + indicating (y = 1) examples and o indicating (y = 0) examples.')

plot_data(X, y)

plt.axis([30, 100, 30, 100])  #設定x,y軸的取值範圍
plt.legend(['Admitted', 'Not admitted'], loc=1)  #設定圖例
plt.xlabel('Exam 1 score')   #x軸標題  考試1成績
plt.ylabel('Exam 2 score')   #y軸標題  考試2成績
  • 編寫視覺化程式plotData.py
def plot_data(X, y):
    plt.figure()

    postive=X[y==1]  #分離正樣本
    negtive=X[y==0]  #分離負樣本
    
    plt.scatter(postive[:,0],postive[:,1],marker='+',c='red',label='Admitted') #畫出正樣本
    plt.scatter(negtive[:,0],negtive[:,1],marker='o',c='blue',label='Not Admitted') #畫出負樣本
  • 檢視視覺化效果

  • 計算邏輯迴歸的代價函式和梯度
'''第2部分 計算代價函式和梯度'''


(m, n) = X.shape #m樣本數 n原始輸入特徵數


X = np.c_[np.ones(m), X] #特徵矩陣X前加一列1  方便矩陣運算

#初始化模型引數為0
initial_theta = np.zeros(n + 1)

# 計算邏輯迴歸的代價函式和梯度
cost, grad = cf.cost_function(initial_theta, X, y)

np.set_printoptions(formatter={'float': '{: 0.4f}\n'.format}) #設定輸出格式

#與期望值進行比較 驗證程式的正確性
print('Cost at initial theta (zeros): {:0.3f}'.format(cost)) #0引數下的代價函式值
print('Expected cost (approx): 0.693')
print('Gradient at initial theta (zeros): \n{}'.format(grad)) #0引數下的梯度值
print('Expected gradients (approx): \n-0.1000\n-12.0092\n-11.2628')

# 用非零引數值計算代價函式和梯度
test_theta = np.array([-24, 0.2, 0.2])
cost, grad = cf.cost_function(test_theta, X, y)
#與期望值進行比較 驗證程式的正確性
print('Cost at test theta (zeros): {}'.format(cost))#非0引數下的代價函式值
print('Expected cost (approx): 0.218')
print('Gradient at test theta: \n{}'.format(grad))
print('Expected gradients (approx): \n0.043\n2.566\n2.647')#非0引數下的代價函式值
  • 編寫sigmoid函式sigmoid.py

def sigmoid(z):
    g = np.zeros(z.size)
    
    g=1/(1+np.exp(-z))

    return g
  • 編寫計算代價函式和梯度的程式costFunction.py

def h(theta,X): #假設函式
    return sigmoid(np.dot(X,theta))

def cost_function(theta, X, y):
    m = y.size #樣本數

   
    cost = 0
    grad = np.zeros(theta.shape)
    
    myh=h(theta,X)  #得到假設函式值
    term1=-y.dot(np.log(myh))
    term2=(1-y).dot(np.log(1-myh))
    cost=(term1-term2)/m
    
    grad=(myh-y).dot(X)/m

    return cost, grad

證明我們的程式碼是正確的。

  • 訓練分類器,並用高階優化方法fmin_bfgs求解最優引數
'''第3部分 用高階優化方法fmin_bfgs求解最優引數'''

#可以把高階優化想像成梯度下降法 只不過不用人工設定學習率
'''
    fmin_bfgs優化函式 第一個引數是計算代價的函式 第二個引數是計算梯度的函式 引數x0傳入初始化的theta值
    maxiter設定最大迭代優化次數
'''
def cost_func(t):  #單獨寫一個計算代價的函式  返回代價函式值
    return cf.cost_function(t, X, y)[0]


def grad_func(t): #單獨寫一個計算梯度的函式 返回梯度值
    return cf.cost_function(t, X, y)[1]


# 執行高階優化方法
theta, cost, *unused = opt.fmin_bfgs(f=cost_func, fprime=grad_func, x0=initial_theta, maxiter=400, full_output=True, disp=False)

#列印最優的代價函式值和引數值  與期望值比較 驗證正確性
print('Cost at theta found by fmin: {:0.4f}'.format(cost))
print('Expected cost (approx): 0.203')
print('theta: \n{}'.format(theta))
print('Expected Theta (approx): \n-25.161\n0.206\n0.201')

# 畫出決策邊界
pdb.plot_decision_boundary(theta, X, y)

plt.xlabel('Exam 1 score')
plt.ylabel('Exam 2 score')

可以發現我們的結果和期望值差不多:

呼叫已經寫好的plotDecisionBoundary.py畫出決策邊界:

  • 用訓練好的分類器進行預測,並計算在訓練集上的準確率
'''第4部分 用訓練好的分類器進行預測,並計算分類器在訓練集上的準確率'''

#假設一個學生 考試1成績45 考試2成績85  預測他通過的概率
prob = sigmoid(np.array([1, 45, 85]).dot(theta))
#與期望值進行比較 驗證正確性
print('For a student with scores 45 and 85, we predict an admission probability of {:0.4f}'.format(prob))
print('Expected value : 0.775 +/- 0.002')

# 計算分類器在訓練集上的準確率
p = predict.predict(theta, X)
#與期望值進行比較 驗證正確性
print('Train accuracy: {}'.format(np.mean(y == p) * 100))
print('Expected accuracy (approx): 89.0')
  • 編寫預測程式predict.py
def predict(theta, X):
    m = X.shape[0] #樣本數

    p = np.zeros(m) #每個樣本預測的標籤

    p=sigmoid(X.dot(theta))  #每個樣本屬於正類的概率
    p[p>=0.5]=1  #概率大於等於0.5 認為屬於正類 標籤為1 否則為0
    p[p<0.5]=0
    return p

發現我們的結果和期望值差不多:

 

3.邏輯迴歸演算法(不帶正則化)進行二分類完整專案程式碼

下載連結   下載密碼:546j

4.利用邏輯迴歸演算法(帶正則化)進行二分類

使用邏輯迴歸進行分類時,一種方案是直接使用原始輸入特徵進行運算;另一種是當輸入特徵比較少或分類效果不理想時時,可以考慮在原始輸入特徵的基礎上擴充一些新特徵,再進行邏輯迴歸。本小節的實驗就屬於第二種情況,該資料集視覺化後會發現線性不可分,所以僅用兩個原始輸入特徵是不可行的,需要擴充套件一些新特徵。

擴充的新特徵多一些沒關係,訓練過程中會自動篩選對分類效果貢獻大的特徵,體現在求解的最優引數上,一般不重要的特徵,前面的引數都接近於0.

  • 開啟主程式ex2_reg.py
data = np.loadtxt('ex2data2.txt', delimiter=',') #載入txt格式訓練資料集 每一行用','分隔 
X = data[:, 0:2]  #前兩列是原始輸入特徵(2)
y = data[:, 2]  #最後一列是標籤 0/1

plot_data(X, y)  #視覺化訓練集

plt.xlabel('Microchip Test 1') 
plt.ylabel('Microchip Test 2')
plt.legend(['y = 1', 'y = 0'])#圖例

input('Program paused. Press ENTER to continue')

'''第1部分 增加新的多項式特徵,計算邏輯迴歸(正則化)代價函式和梯度'''
X = mf.map_feature(X[:, 0], X[:, 1])


initial_theta = np.zeros(X.shape[1])


lmd = 1 #正則化懲罰項係數

# 計算引數為0時的代價函式值和梯度
cost, grad = cfr.cost_function_reg(initial_theta, X, y, lmd)

#與期望值比較 驗證正確性
np.set_printoptions(formatter={'float': '{: 0.4f}\n'.format})
print('Cost at initial theta (zeros): {}'.format(cost))
print('Expected cost (approx): 0.693')
print('Gradient at initial theta (zeros) - first five values only: \n{}'.format(grad[0:5]))
print('Expected gradients (approx) - first five values only: \n 0.0085\n 0.0188\n 0.0001\n 0.0503\n 0.0115')

input('Program paused. Press ENTER to continue')


test_theta = np.ones(X.shape[1])
# 計算引數非0(1)時的代價函式值和梯度
cost, grad = cfr.cost_function_reg(test_theta, X, y, lmd)
#與期望值比較 驗證正確性
print('Cost at test theta: {}'.format(cost))
print('Expected cost (approx): 2.13')
print('Gradient at test theta - first five values only: \n{}'.format(grad[0:5]))
print('Expected gradients (approx) - first five values only: \n 0.3460\n 0.0851\n 0.1185\n 0.1506\n 0.0159')
  • 編寫視覺化程式plotData.py
def plot_data(X, y):
    plt.figure()

    postive=X[y==1] #取出正樣本
    negtive=X[y==0] #取出負樣本
    
    plt.scatter(postive[:,0],postive[:,1],marker='x',c='red',label='y=1')
    plt.scatter(negtive[:,0],negtive[:,1],marker='o',c='blue',label='y=0')
  • 檢視已經寫好的特徵對映程式mapFeature.py

在原始輸入特徵(兩個)基礎上增加新的多項式特徵:

def map_feature(x1, x2): #生成新的多項式特徵
    degree = 6

    x1 = x1.reshape((x1.size, 1))
    x2 = x2.reshape((x2.size, 1))
    result = np.ones(x1[:, 0].shape) #result初始為一個列向量 值全為1

    for i in range(1, degree + 1):
        for j in range(0, i + 1):
            result = np.c_[result, (x1**(i-j)) * (x2**j)]  #不斷拼接新的列 擴充特徵矩陣

    return result
  • 編寫邏輯迴歸(正則化)的代價函式和梯度計算程式costFunctionReg.py

注意不懲罰第一個引數。

def h(theta,X): #假設函式
    return sigmoid(X.dot(theta))

def cost_function_reg(theta, X, y, lmd):
    m = y.size

    cost = 0
    grad = np.zeros(theta.shape)
    
    myh=h(theta,X) #假設函式值
    term1=-y.dot(np.log(myh))
    term2=(1-y).dot(np.log(1-myh))
    term3=(lmd/(2*m))*(theta[1:].dot(theta[1:])) #不懲罰第一項
    cost=(term1-term2)/m+term3

    grad=(myh-y).dot(X)/m
    grad[1:]+=(lmd/m)*theta[1:]

    return cost, grad

與期望值進行比較,差不多,說明我們的程式是正確的:

  • 資料集視覺化效果

  • 訓練與預測
'''第2部分 嘗試不同的懲罰係數[0,1,10,100],分別利用高階優化演算法求解最優引數,分別計算訓練好的分類器在訓練集上的準確率,
並畫出決策邊界
 '''

initial_theta = np.zeros(X.shape[1])

# Set regularization parameter lambda to 1 (you should vary this)
lmd = 1 #需要改變這個值

# Optimize
def cost_func(t):
    return cfr.cost_function_reg(t, X, y, lmd)[0]

def grad_func(t):
    return cfr.cost_function_reg(t, X, y, lmd)[1]

theta, cost, *unused = opt.fmin_bfgs(f=cost_func, fprime=grad_func, x0=initial_theta, maxiter=400, full_output=True, disp=False)


print('Plotting decision boundary ...')
pdb.plot_decision_boundary(theta, X, y)
plt.title('lambda = {}'.format(lmd))

plt.xlabel('Microchip Test 1')
plt.ylabel('Microchip Test 2')


p = predict.predict(theta, X)

print('Train Accuracy: {:0.4f}'.format(np.mean(y == p) * 100))
print('Expected accuracy (with lambda = 1): 83.1 (approx)')
  • 編寫預測函式
def predict(theta, X):
    m = X.shape[0]

    p = np.zeros(m)

    p=sigmoid(X.dot(theta))
    p[p>=0.5]=1
    p[p<0.5]=0

    return p
  • 嘗試不同的懲罰係數值

\lambda =1時:

視覺化決策邊界,檢視分類效果:

與期望值進行比較,差不多,說明我們的程式是正確的:

\lambda =0時:

相當於不進行正則化。

視覺化決策邊界,檢視分類效果:

雖然此時分類效果看起來更好,在訓練集上的準確率更高,但是很有可能出現過擬合,模型泛化能力比較差。

\lambda =10時:

視覺化決策邊界,檢視分類效果:

此時,正則化懲罰係數有些大,分類效果不太好以及在訓練集上的準確率不太高,應該稍微減小一下。

\lambda =100時:

視覺化決策邊界,檢視分類效果:

此時,正則化懲罰係數有些過大,分類效果很不好以及在訓練集上的準確率很低,應該大幅度減小一下。

 

綜上,在訓練過程中,需要新增正則化懲罰項防止過擬合,但懲罰係數要合理設定,過大過小都不行。

5.邏輯迴歸演算法(正則化)進行二分類完整專案程式碼

下載連結  下載密碼:73we