Python實現邏輯斯蒂回歸
Python實現邏輯斯蒂回歸
邏輯回歸與加正則化的邏輯斯蒂回歸介紹
本實驗室根據兩次考試成績與是否通過的數據,通過logistic回歸,最後獲得一個分類器。
邏輯斯蒂回歸
導入數據
import numpy as npdef loaddata(file, delimeter): #以delimeter為分隔符導入file數據 data = np.loadtxt(file, delimiter=delimeter) print(Dimensions: ,data.shape) # 列印數據前6行 print(data[1:6,:]) return(data)data = loaddata(data1.txt, ,)
結果為:
Dimensions: (100, 3)
[[30.28671077 43.89499752 0. ]
[35.84740877 72.90219803 0. ] [60.18259939 86.3085521 1. ] [79.03273605 75.34437644 1. ] [45.08327748 56.31637178 0. ]]可以看見data的數據結構為一個100*3的矩陣,第一列為exam1的成績,第二列為exam2的成績,第三列為是否最終通過(0為否,1為是)。
# 作圖顯示數據分布import matplotlib.pyplot as pltdef plotData(data, label_x, label_y, label_pos, label_neg, axes=None): # 獲得正負樣本的下標(即哪些是正樣本,哪些是負樣本) neg = data[:,2] == 0 pos = data[:,2] == 1 if axes == None: axes = plt.gca() axes.scatter(data[pos][:,0], data[pos][:,1], marker=+, c=k, s=60, linewidth=2, label=label_pos) axes.scatter(data[neg][:,0], data[neg][:,1], c=y, s=60, label=label_neg) axes.set_xlabel(label_x) axes.set_ylabel(label_y) axes.legend(frameon= True, fancybox = True)plotData(data, Exam 1 score, Exam 2 score, Pass, Fail)
讀取數據作為X與y向量:
X = np.c_[np.ones((data.shape[0],1)), data[:,0:2]]y = np.c_[data[:,2]]
則X為:
1 34.6237 78.0247
1 30.2867 43.895
1 35.8474 72.90221 60.1826 86.30861 79.0327 75.34441 45.0833 56.31641 61.1067 96.51141 75.0247 46.5541 76.0988 87.42061 84.4328 43.5334......y為:
0
001101111......邏輯斯蒂回歸
邏輯斯蒂回歸函數設為為:
# 定義sigmoid函數def sigmoid(z): return(1 / (1 + np.exp(-z)))
損失函數為:
向量化的損失函數(矩陣形式):
# 定義損失函數def costFunction(theta, X, y): m = y.size # 此處h為一個列向量 h = sigmoid(X.dot(theta)) J = -1.0*(1.0/m)*(np.log(h).T.dot(y)+np.log(1-h).T.dot(1-y)) return J[0]# 初始化theta為0向量initial_theta = np.zeros(X.shape[1])# 此時邏輯斯蒂回歸的損失函數值為cost = costFunction(initial_theta, X, y)print(Cost:
, cost)
Cost:
0.6931471805599452求偏導(梯度)
向量化的偏導(梯度)
# 求梯度def gradient(theta, X, y): m = y.size # 此處h為一100*1的列向量 h = sigmoid(X.dot(theta.reshape(-1,1))) # grad為一個3*100的矩陣 grad =(1.0/m)*X.T.dot(h-y) return(grad.flatten())#此時邏輯斯蒂函數在初始化theta處在Θ0、Θ1、Θ2處的梯度分別為:grad = gradient(initial_theta, X, y)print(Grad:
, grad)
Grad:
[ -0.1 -12.00921659 -11.26284221]最小化損失函數
minimize()函數的介紹
Jacobian矩陣和Hessian矩陣介紹
# 計算損失函數得最小值。from scipy.optimize import minimize# 規定最大迭代次數為400次res = minimize(costFunction, initial_theta, args=(X,y), jac=gradient, options={maxiter:400})res# minimize()返回的格式是固定的,fun為costFunction函數迭代求得的最小值,hess_inv和jac分別為求得最小值時海森矩陣和雅克比矩陣的值,小寫字母x為當costFunction函數最小時函數的解,在costFunction中即為theta的解。即計算損失函數最小時theta的值為[-25.16133284, 0.2062317 , 0.2014716 ]。Out[17]: fun: 0.20349770158944375 hess_inv: array([[ 3.31474479e+03, -2.63892205e+01, -2.70237122e+01], [ -2.63892205e+01, 2.23869433e-01, 2.02682332e-01], [ -2.70237122e+01, 2.02682332e-01, 2.35335117e-01]]) jac: array([ -9.52476821e-09, -9.31921318e-07, -2.82608930e-07]) message: Optimization terminated successfully. nfev: 31 nit: 23 njev: 31 status: 0 success: True x: array([-25.16133284, 0.2062317 , 0.2014716 ])
即損失函數最小時theta的值:
theta = res.x.TthetaOut[20]: array([-25.16133284, 0.2062317 , 0.2014716 ])
咱們來看看考試1得分45,考試2得分85的同學通過概率有多高
sigmoid(np.array([1,45,85]).dot(theta))Out[22]: 0.77629072405889421
即考試1得分45,考試2得分85的同學通過概率約為0.7763。
畫出決策邊界
# 標註考試1得分45,考試2得分85的同學plt.scatter(45, 85, s=60, c=r, marker=v, label=(45, 85))# plotData為之前定義的畫分類點的函數plotData(data, Exam 1 score, Exam 2 score, Admitted, Not admitted)# 生成網格數據x1_min, x1_max = X[:,1].min(), X[:,1].max(),x2_min, x2_max = X[:,2].min(), X[:,2].max(),xx1, xx2 = np.meshgrid(np.linspace(x1_min, x1_max), np.linspace(x2_min, x2_max))# 計算h的值h = sigmoid(np.c_[np.ones((xx1.ravel().shape[0],1)), xx1.ravel(), xx2.ravel()].dot(res.x))h = h.reshape(xx1.shape)# 作等高線,可理解為在這條線上h值為0.5plt.contour(xx1, xx2, h, [0.5], linewidths=1, colors=b)
加正則化項的邏輯斯蒂回歸
導入數據
data2 = loaddata(data2.txt, ,)
data2的格式為:
Dimensions: (118, 3)
[[-0.092742 0.68494 1. ] [-0.21371 0.69225 1. ] [-0.375 0.50219 1. ] [-0.51325 0.46564 1. ][-0.52477 0.2098 1. ]]
作分布圖
y = np.c_[data2[:,2]]X = data2[:,0:2]plotData(data2, Microchip Test 1, Microchip Test 2, y = 1, y = 0)
在上一個邏輯回歸試驗中,我們把sigmoid函數(即這裡的g函數)設置的為簡單的一次多項式。
在這個邏輯回歸實驗里,因為樣本的分布比較複雜,可以採用多次多項式來代替ΘTX。這裡取最高六次項。
取高階多項式放入sigmoid函數進行模擬
from sklearn.preprocessing import PolynomialFeatures# 生成一個六次多項式poly = PolynomialFeatures(6)# XX為生成的六次項的數據XX = poly.fit_transform(data2[:,0:2])# 六次項後有28個特徵值了。即,之前我們只有兩個特徵值x1、x2,取六次項多項式後我們會有x1、x2、x1^2、x2^2、x1*x2、x1^2*x2、……,總共28項。XX.shapeOut[12]: (118, 28)
正則化
因為取得的多項式最高項為6次,容易發生過擬合情況。將損失函數採取「正則化」處理,引入懲罰項。
正則化後損失函數:
向量化的損失函數(矩陣形式):
# 定義損失函數def costFunctionReg(theta,reg ,XX, y): m = y.size h = sigmoid(XX.dot(theta)) J = -1.0*(1.0/m)*(np.log(h).T.dot(y)+np.log(1-h).T.dot(1-y)) +(reg/(2.0*m))*np.sum(np.square(theta[1:])) if np.isnan(J[0]): return(np.inf) return(J[0])
與之對應的偏導(梯度):
向量化的偏導(梯度):
注意,我們另外自己加的參數 θ0 不需要被正則化
# 定義正則化損失函數的偏導def gradientReg(theta, reg, XX, y): m = y.size h = sigmoid(XX.dot(theta.reshape(-1,1))) grad = (1.0/m)*XX.T.dot(h-y) + (reg/m)*np.r_[[[0]],theta[1:].reshape(-1,1)] return(grad.flatten())
設初始化theta為0向量,計算此時初始損失值
initial_theta = np.zeros(XX.shape[1])costFunctionReg(initial_theta, 1, XX, y)Out[9]: 0.69314718055994529
畫出決策邊界
定義預測函數,用來統計準確率。分類的閾值定為0.5,即計算的h(x)>0.5則分到1類(即通過),h(x)<0.5則分到0類(即不通過):
def predict(theta, X, threshold=0.5): h = sigmoid(X.dot(theta.T)) >= threshold # 返回的h值只會有兩種,1或0 return(h.astype(int))
決策邊界,咱們分別來看看正則化係數lambda太大太小分別會出現什麼情況
- Lambda = 0 : 就是沒有正則化,這樣的話,就過擬合咯
- Lambda = 1 : 這才是正確的打開方式
- Lambda = 100 : 卧槽,正則化項太激進,導致基本就沒擬合出決策邊界
fig, axes = plt.subplots(1,3, sharey = True, figsize=(17,5))# 分別取lambda為0、1、100for i, C in enumerate([0.0, 1.0, 100.0]): # 最優化 costFunctionReg res2 = minimize(costFunctionReg, initial_theta, args=(C, XX, y), jac=gradientReg, options={maxiter:3000}) # 準確率 accuracy = 100.0*sum(predict(res2.x, XX) == y.ravel())/y.size # 對X,y的散列繪圖 plotData(data2, Microchip Test 1, Microchip Test 2, y = 1, y = 0, axes.flatten()[i]) # 畫出決策邊界 x1_min, x1_max = X[:,0].min(), X[:,0].max(), x2_min, x2_max = X[:,1].min(), X[:,1].max(), xx1, xx2 = np.meshgrid(np.linspace(x1_min, x1_max), np.linspace(x2_min, x2_max)) h = sigmoid(poly.fit_transform(np.c_[xx1.ravel(), xx2.ravel()]).dot(res2.x)) h = h.reshape(xx1.shape) axes.flatten()[i].contour(xx1, xx2, h, [0.5], linewidths=1, colors=g); axes.flatten()[i].set_title(Train accuracy {}% with Lambda = {}.format(np.round(accuracy, decimals=2), C))
推薦閱讀:
※想擴展知識,學一門新語言,該學 Python、Ruby,還是 C++ ?
※做網路工程師還是做大數據,請求大佬指路?
※怎樣用 rgb 三元組理解色相、亮度和飽和度?
※量化策略系列教程:09HANS123策略
※神奇的Numpy計算