1 線性目標的梯度優化
損失函數:
算法1 : 批量梯度下降BGD
每次迭代使用所有樣本來對參數進行更新。
損失函數:
代數形式:
矩陣形式:
更新:
代數形式偽代碼:
矩陣形式偽代碼:
算法2:隨機梯度下降SGD
每次迭代使用一個樣本來對參數進行更新。
一個樣本的損失函數:
代數形式偽代碼:
矩陣形式偽代碼
算法3:小批量梯度下降法
每次迭代使用n_batch個樣本對參數進行更新
代數形式偽代碼:
矩陣形式偽代碼
1. 線性目標的sgd優化,寫出偽代碼。mini_batch怎么更新?
2. SGD並行實現?
=============================
# 梯度優化代碼
import numpy as np import pandas as pdfrom sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression #基准模型 data_df = pd.read_csv('kc_house_data.csv') train_data, test_data = train_test_split( data_df, train_size=0.8, random_state=1234 ) # sklearn模型 X_train = train_data['sqft_living'].values.reshape(-1,1) y_train = train_data['price'].values.reshape(-1,1) X_test = test_data['sqft_living'].values.reshape(-1,1) y_test = test_data['price'].values.reshape(-1,1) model = LinearRegression(normalize=True).fit(X_train, y_train ) y_pred = model.predict(X_test) print('intercept:',model.intercept_) print('coel:', model.coef_) print('MSE: %.2f' % np.mean((y_pred - y_test) ** 2)) # 模型 def linear(X,w): X = np.hstack((np.ones((X.shape[0],1)), X)) y = np.dot(X,w) return y # 損失函數 def cost_function( X, y, w ): m = X.shape[0] e = np.dot(X,w) - y J = 0.5/m * np.dot(e.T, e) return J[0] # 標准化 def normalize( X ): mu = np.mean(X,axis =0) sigma = np.std(X,axis = 0) X = (X-mu)/sigma return X,mu,sigma # 均方誤差 def MSE( X, y, w ): return np.mean((linear(X,w) - y) ** 2) #----------------------------- # 批量梯度下降法 def batch_gradient_descent( X, y, eta=0.1, eps=1e-4, num_iters=2000 ): converged = False J = np.zeros( num_iters ) m = X.shape[0] i_iters = 0 w = np.zeros((X.shape[1],1)) while not converged and i_iters < num_iters: grad_w = np.dot( X.T, X.dot(w) - y ) w -= eta*grad_w/m dist_grad = np.sqrt(np.dot(grad_w.T,grad_w)) J[i_iters] = cost_function( X, y, w ) print('Iteration:', i_iters+1, ': J(w):', J[i_iters], "||J(w)||:", dist_grad) if dist_grad < eps: cur= True i_iters += 1 return w X, mu, sigma = normalize( X_train ) X = np.hstack( (np.ones((X_train.shape[0],1)), X) ) w = batch_gradient_descent( X, y, eta = 0.1, eps=1e-4 ) print ('Intercept:', w[0] - np.dot(mu/sigma,w[1:])) print( 'Slope :', w[1:]/sigma[:,np.newaxis]) print ("MSE : %.2f" % MSE( (X_test-mu)/sigma, y_test, w )) #---------------------------------------------- # 隨機梯度下降法 def stochastic_gradient_descent( X, y, eta=0.1, eps=1e-4, num_iters=2000 ): cur = False J = np.zeros( num_iters ) n_batch = 1 ## i_iters = 0 w = np.zeros((X.shape[1],1)) while not cur and i_iters < num_iters: batch = np.random.choice(X.shape[0], n_batch) X_batch, y_batch = X[batch], y[batch] grad_w = np.dot( X_batch.T, X_batch.dot(w) - y_batch ) w -= eta*grad_w/n_batch # dist_grad = np.sqrt(np.dot(grad_w.T,grad_w)) J[i_iters] = cost_function( X_batch, y_batch, w ) print('Iteration:', i_iters+1, ': J(w):', J[i_iters], "||J(w)||:", dist_grad) if dist_grad < eps: cur = True i_iters += 1 return w X, mu, sigma = normalize( X_train ) X = np.hstack( (np.ones((X_train.shape[0],1)), X) ) w = stochastic_gradient_descent( X, y, eta = 0.1, eps=1e-4 ) print ('Intercept:', w[0] - np.dot(mu/sigma,w[1:])) print( 'Slope :', w[1:]/sigma[:,np.newaxis]) print ("MSE : %.2f" % MSE( (X_test-mu)/sigma, y_test, w )) # --------------------------------------- # 小批量隨機梯度下降法 def mini_gradient_descent( X, y, eta=0.1, eps=1e-4, num_iters=2000 ): cur = False J = np.zeros( num_iters ) n_batch = 5000 ## i_iters = 0 w = np.zeros((X.shape[1],1)) while not cur and i_iters < num_iters: batch = np.random.choice(X.shape[0], n_batch) X_batch, y_batch = X[batch], y[batch] grad_w = np.dot( X_batch.T, X_batch.dot(w) - y_batch ) w -= eta*grad_w/n_batch # n_batch為所選擇的批量個數 dist_grad = np.sqrt(np.dot(grad_w.T,grad_w)) J[i_iters] = cost_function( X_batch, y_batch, w ) print('Iteration:', i_iters+1, ': J(w):', J[i_iters], "||J(w)||:", dist_grad) if dist_grad < eps: cur = True i_iters += 1 return w X, mu, sigma = normalize( X_train ) X = np.hstack( (np.ones((X_train.shape[0],1)), X) ) w = mini_gradient_descent( X, y, eta = 0.1, eps=1e-4 ) print ('Intercept:', w[0] - np.dot(mu/sigma,w[1:])) print( 'Slope :', w[1:]/sigma[:,np.newaxis]) print ("MSE : %.2f" % MSE( (X_test-mu)/sigma, y_test, w ))
代碼寫的再多,不如自己嘗試一遍,很多東西都是自己操作了才學得比較快。這里的數據以及大部分知識點多是從公眾號學習,這里就不具體說明。很多東西都是要自己找到的才會真正舍得去學。數據文件在網盤,http://pan.baidu.com/share/link?shareid=1375610667&uk=209896182。可以自己嘗試下載數據練習,里面也有各種jupyter,還有嶺回歸和拉索回歸的,感興趣可以自行去學習。
2 LR回歸原理推導
假設數據服從伯努利分布,通過極大似然函數的方法,用梯度下降來求解參數,來達到將數據分類,一般是二分類。
2.1 對率回歸推導
1. sigmoid函數推導
邏輯回歸解決的是二分類問題,首先通過訓練模型來學習x和y的映射關系,有了映射關系就容易對x和y進行預測分類。如何定義這種映射關系,這里假定y的取值為0或者1(也可以是其他類),兩種可能,自然而然服從伯努利分布,這種映射關系就可以通過條件概率p(y|x)來表示。p(y=1|x)和p(y=0|x),對於二分類就可設定一個閾值,0.5。
如何通過條件概率來描述x和y之間的關系呢?對率回歸是線性模型,但是無法直接表示成,那么就可以通過廣義線性模型來實現。一般廣義線性模型的一般形式,
,其中g(y)為單調函數,g(y)就可被作為聯系函數。(為什么選擇sigmoid函數,一般先考慮的是單位階躍函數將任意的實數轉換為0/1的概率值,用它來當成聯系函數來判斷屬於哪個類。但是單位階躍函數的一個缺點就是在零點不連續也不單調,而聯系函數需要單調連續。)而sigmoid能夠將(-無窮,+無窮)的值域映射到(0,1),這樣就可以得到合理的概率值,而且單調連續,可以作為聯系函數。
這樣就得到了,將
代入,得到
用條件概率表示就是
得到
2. 廣義線性模型推導
指數族分布:是一類分布的總稱,它的概率密度函數一般形式是:
其中,稱為該分布的自然參數;T(y)為充分統計量,通常為y本身;
為配分函數,保證概率表達式加和為1,保證式子是合格的概率密度函數;b(y)是關於隨機變量y的函數。常見伯努利和正態均為指數族分布。
證明:伯努利分布是指數族分布?
化成指數族的一般形式:
對應指數族分布的一般形式,
廣義線性模型三假設:
1. 給定x的條件下,假設隨機變量y服從指數族分布
2. 給定x條件下,目標是得到一個模型h(x)能預測出T(y)的期望值。
3. 假設該指數族分布中的自然參數和x呈線性關系,即
滿足這三條假設即為廣義線性模型。
對數幾率回歸是在對一個二分類進行建模,並假設被建模的隨機變量y取值為0或1,可以很自然地假設y服從伯努利分布。如果想要構建一個線性模型來預測在給定x的條件下y取值的話,可以考慮使用廣義線性模型來建模。
假設1:伯努利分布服從指數族分布。
假設2:,得到
假設3:
2.2 損失函數推導
損失函數:
由式子,
假設數據都是獨立的,由伯努利分布,得到一個樣本發生的條件概率表示為:
進而得到它們一起發生的概率(似然函數):
取對數,取負,得到損失函數
為什么用似然函數?
我們的目標是預測為某一類的出現概率最大,每個樣本預測都要得到最大的概率。前面我們得到了一個樣本的條件概率,極大似然估計就會將所有的樣本考慮進去,來使得觀測到的樣本的出現概率最大,所以有了累乘形式的似然函數。 (為什么累乘?)基於條件獨立的假設,總的條件概率就可以表示為每一個條件概率的累乘形式。
為什么用極大似然函數(交叉熵)作為損失函數而不用最小二乘(歐氏距離,均方損失)?
均方損失:假設誤差是正態分布,適合線性的輸出(回歸問題),特點是對於與真實值差別越大,懲罰力度越大,不適合分類問題。
交叉熵損失:假設誤差是伯努利分布,可以視為預測概率分布與真實概率分布的相似程度。多應用在分類的問題。
均方誤差對參數的偏導的結果都乘以了 sigmoid的導數,而sigmoid導數在其變量值很大或很小時趨近於0,所以均方誤差的偏導可能接近於0。
而參數更新公式: 參數 = 參數 – 學習率 * 損失函數對參數的偏導
在偏導很小時,參數更新速度會變得很慢,而在接近於0時,參數幾乎不更新,出現梯度消失的情況。反觀交叉熵對參數的偏導就沒有sigmoid導數,所以不存在梯度消失的問題。
均方損失:
使用梯度下降法的條件損失函數時凸函數。而對最小二乘的損失函數求導,
可以知道,J(w)對w不是凸函數,不能用代價函數。
交叉熵:
當越接近於1,越接近於0。預測值與真實值完全相同,其損失函數為0。
當越接近於0,越接近於。
當越接近於1,越接近於。
當越接近於0,越接近於0。預測值與真實值完全相同,其損失函數為0。
寫成矩陣形式:
2.3 梯度下降法
代數形式偽代碼:
矩陣形式偽代碼:
==================================
# LR代碼
import numpy as np #隨機生成樣本數據,二分類問題,每類樣本生成5000個 np.random.seed(43) num = 5000 x1 = np.random.multivariate_normal([0,0],[[1,0.75],[0.75,1]],num) #d多元正態分布矩陣,[0,0]為均值,然后是正定對稱半正定矩陣。 x2 = np.random.multivariate_normal([1, 4], [[1, .75],[.75, 1]], num) X = np.vstack((x1, x2)).astype(np.float32) X = np.hstack( (np.ones((X.shape[0],1)), X) ) y = np.hstack((np.zeros(num),np.ones(num))) # 模型 def sigmoid(x): return 1 / (1 + np.exp(-x)) # 損失函數 def log_likelihood(X, y, w): # 首先按照標簽來提取正樣本和負樣本的下標 pos, neg = np.where(y==1), np.where(y==0) pos_sum = np.sum(np.log(sigmoid(np.dot(X[pos], w)))) neg_sum = np.sum(np.log(1-sigmoid(np.dot(X[neg], w)))) return -(pos_sum + neg_sum) # 返回cross entropy loss # 實現邏輯回歸模型 def logistic_regression(X, y, num_steps, eta): m = X.shape[1] w = np.zeros(X.shape[1]) for step in range(num_steps): error = sigmoid(np.dot(X,w)) - y grad_w = np.matmul(X.T, error) w = w - eta/m * grad_w # 每隔一段時間,計算一下損失函數,看看有沒有變化 # 正常情況下, 它會慢慢變小,最后收斂 if step % 10000 == 0: print (log_likelihood(X, y, w)) return w w = logistic_regression(X, y, num_steps = 100000, eta = 5e-5) print ("參數w, b分別為: ", w[1:],w[0])# sklearn from sklearn.linear_model import LogisticRegression # C設置一個很大的值,意味着不想加入正則項 clf = LogisticRegression(fit_intercept=True, C = 1e15) clf.fit(X, y) print ("(sklearn)邏輯回歸的參數w, b分別為: ", clf.coef_, clf.intercept_, )
=============================================================================================
寫在后,這是第一次寫博客,平常喜歡在word或txt記錄,也習慣了word的公式,就不打latex了,公式直接截圖了。發出來的初衷有兩個,一個是怕萬一本地的文檔像上個電腦突然去世,里面的資料都丟失了。還一個也是希望,對上述公式或者知識點是以個人的見解來總結或者直接拿過來,希望能夠看到大家的看法,以及糾正,在這也對直接拿過來的這些原出處沒辦法做引用說明說聲抱歉。
也是在自學的路上,更新會比較慢,這也是對自己的一種鞭撻吧。
后續,還會有想對牛頓法,擬牛頓法進行補充,在能夠認為掌握之后。
願正在閱讀的你,以及仍熱愛的你,共勉。