機器學習-線性回歸與邏輯回歸
變數名介紹
- m = 訓練集個數
- n = 特徵個數
- x = 輸入變數 / 特徵
- y = 輸出變數 / 目標變數
- = 參數
- = 學習速率
- (x , y) = 一個樣本(訓練集中的一個)
- = 第 i 個樣本
- = 第 i 個樣本中的第 j 個特徵值(或與 對應的第 j 個輸入值)
線性回歸定義假設
假設 h 由訓練集經過學習演算法訓練得到,可以通過輸入特徵得到相應的預測。
由於是線性回歸,我們假設:
為了簡化形式,我們假設 ,那麼
建立損失函數
現在我們擁有的是訓練集{ }與假設函數 ,我們需要做的是改變 的值讓 盡量小,使預測值與真值接近。
但是絕對值符號在打開的時候,存在判斷正負的問題,所以採用 更好。
最後整合所有訓練集,建立損失函數 (乘了一個1/2是方便後面運算),此時我們需要做的就是求 的最小值點。
梯度下降
在求函數 最小值點的開始,我們設置了 的初始值,然後我們改變 值的策略就是向著梯度相反的方向,因為梯度的方向永遠是上升最快的方向。所以我們得到了更新 的方法:
為了更好的理解,可以舉一個不太靠譜的例子:
我們假設 ,我們同樣是求 的最小值點,我們當然知道,就是 點。那麼當 時,我們利用更新 的方法得到:
其中 ,所以:
由於 ,所以只要 ,那麼 的值就會更加靠近0,同時 也會變小。經過不斷的迭代後就會收斂到最小值點 。
在這個例子里,我們也可以理解 為什麼叫學習速率。
在 的條件下,在 越小,迭代次數就會越多, 越大,迭代次數就會越少,並且在 時一步就收斂。
在 的條件下,將會發生震蕩。
在 的條件下,將會發散。
所以可以看到 的大小在訓練過程中也很重要。
書接上文,我們得到了更新 的方法,把 帶入,得到:
所以整合所有訓練集,我們得到了完成的更新 的方法:
(for every j)
當數據量太大時,我們可以採取隨機梯度下降(SGD)的方法:
for i to m { (for every j)}
矩陣求解
針對線性回歸,我們可以直接求解 的值:
由於存在矩陣的逆,所以不容易計算,並且可能不存在。
局部加權回歸
局部加權回歸可以看作時線性回歸的一種推廣,他可以擬合除了線性之外其他的模型。
首先引入兩個變數:
- = 第 i 組訓練數據對應的權值
- = 波長(控制權值隨距離下降的速率,越小下降越快,越大下降越慢)
改變損失函數為:
其中:
(也可以採用其他函數,只要滿足下面的性質即可,常用這個)
所以在 的時候,
當 的時候,
並且由 來控制 隨距離大小下降的速率。
在 中是存在輸入變數 x 的,在每一次擬合之前都需要知道輸入變數 x 的大小,即在每次預測之前都需要重新訓練。並且離輸入變數 x 越遠的樣本對於損失函數的貢獻就越小,若我們把貢獻小的樣本去掉,其實局部線性回歸就是利用預測值附近的樣本擬合了一條直線之後,再進行預測的。
線性回歸的概率解釋
首先我們假設:
其中 可以看作誤差,並且該誤差服從高斯分布: ,即:
然後我們利用極大似然估計相同的方法,選擇 值使得概率最大:
為了使整個函數值最大化,我們需要函數中如下圖的部分的值盡量小:
即得到了之前設計得損失函數,也說明該損失函數是有概率意義的。
邏輯回歸
由於邏輯回歸用來分類,所以 , 。
設置激活函數: ,函數圖像如下圖:
可以看到函數值的範圍在 之間,並且在 處值 0.5 。
同時定義函數 ,計算結果時,以0.5為區分界限,大於0.5即為1,小於0.5即為0。
梯度上升
接下來計算我們需要設置目標函數,並且得到 的更新方法,我們採用概率的方法實現。
首先得到概率解釋:
由於 ,所以可以整合在一起:
同時利用與極大似然估計相同的方法:
為了方便操作,進行如下操作:
為了改變 使 取得最大值,我們採用梯度上升的方法:
化簡操作如下:
得到最後的 更新方法:
實際上這個更新方法與線性回歸看似是一樣的,但是實際上是不同的,因為 是不同的。
感知器
我們可以把上節中的 變成感知器函數:
可以得到與激活函數相同的結果,這可以看作邏輯回歸的特性,即:
牛頓方法
牛頓方法是如同梯度上升一樣相同的,迭代更新 的方法,但是它的限制條件會多一點,但是迭代速度一般都比梯度上升方法更快。
為了介紹牛頓方法,首先假設函數 ,為了找到 的點。在選取了 的初始值後,我們可以利用如下的方法更新 :
,直至收斂。
其實際的物理意義如下圖所示:
即每次更新 的值為, 點處的切線與 x 軸相交的點,迭代至收斂後就會得到 的點。
但是我們需要利用牛頓方法來替代梯度上升方法,之前我們利用梯度上升的方法來尋找 的最大值,實際上也是求 的點。這樣我們可以利用牛頓方法來求解,即將 看作上面的 。
所以在邏輯回歸里利用牛頓方法,我們就可以利用如下方法更新 :
我們可以如下化簡:
其中 H 為Hessian Matrix,如下所示:
牛頓方法迭代的速度一般遠遠快於梯度下降方法。
廣義線性模型
廣義線性模型可以看作線性回歸與邏輯回歸的一種普通情況,並且我們可以知道只要我們假設概率模型為指數分布簇,我們就可以利用廣義線性模型解決。(同時也是利用極大似然估計來計算)
指數分布簇如下:
,通過設置T(y),b(y),a( )來得到不同的概率分布。
主要流程如下:
- 假設概率模型 ,服從ExpFamily( ),即指數分布簇。
- 給定x,目標輸出為 ,大多數情況下 。
- 。
總結
針對判別式學習演算法,我們需要做的主要是以下幾個步驟:
- 假設 概率分布(若是線性模型,則為指數分布)。
- 得到目標輸出 (廣義線性模型中為 )。
- 利用極大似然估計求得 。
- 利用函數 預測結果。
參考
- Andrew Ng的機器學習公開課。
線性回歸代碼
房屋數據(data.txt):
2104,3,3999001600,3,3299002400,3,3690001416,2,2320003000,4,5399001985,4,2999001534,3,3149001427,3,1989991380,3,2120001494,3,2425001940,4,2399992000,3,3470001890,3,3299994478,5,6999001268,3,2599002300,4,4499001320,2,2999001236,3,1999002609,4,4999983031,4,5990001767,3,2529001888,2,2550001604,3,2429001962,4,2599003890,3,5739001100,3,2499001458,3,4645002526,3,4690002200,3,4750002637,3,2999001839,2,3499001000,1,1699002040,4,3149003137,3,5799001811,4,2859001437,3,2499001239,3,2299002132,4,3450004215,4,5490002162,4,2870001664,2,3685002238,3,3299002567,4,3140001200,3,299000852,2,1799001852,4,2999001203,3,239500
代碼:
# -*- coding: utf-8 -*-"""第一個版本:1.按照學習筆記實現"""import numpy as npdef load_data(path): """ 功能:讀取訓練數據 參數:數據文件路徑 返回:訓練集與標籤矩陣 """ x = [] y = [] file_in = open(path) for line in file_in.readlines(): s_data = line.strip().split(,) # strip()函數去除換行符,split()函數分割數據 temp = [] temp.append(1.0) # 設置x0 = 1 if len(s_data) < 2: print "輸入數據錯誤" for i in range(len(s_data) - 1): temp.append(float(s_data[i])) x.append(temp) y.append(float(s_data[len(s_data) - 1])) return np.mat(x), np.mat(y).transpose() # 轉化為矩陣def J(x, y, theta): """ 功能:損失函數 參數:數據集,參數 返回:損失值 """ delta = x * theta - y ws = 0.5 * delta.transpose() * delta return wsdef train(x, y, alpha, maxIter): """ 功能:訓練函數 參數:訓練集,學習速率,最大迭代次數 返回:參數 """ theta = np.ones((x.shape[1], 1)) for i in range(maxIter): delta = x.transpose() * (x * theta - y) theta = theta - alpha * delta print J(x, y, theta) return thetaif __name__ == __main__: train_x, train_y = load_data(data.txt) theta = train(train_x, train_y, 0.0000000005, 200) print train_x * theta
在設置學習速率時,是從 1 開始不斷減小測試的,到大約為0.0000000005時開始收斂,學習速率非常難修改,且收斂後預測效果並不好,主要因為數據沒有預處理。(注意最後測試時用的也是訓練集,是不對的,但是這裡數據量太小。)
增加數據預處理,SGD訓練方法,epoch的概念:
# -*- coding: utf-8 -*-"""第二個版本:1.由於學習速率不好設置,所以增加數據預處理,即標準化2.增加SGD訓練"""import numpy as npdef load_data(path): """ 功能:讀取訓練數據 參數:數據文件路徑 返回:訓練集與標籤矩陣 """ x = [] y = [] file_in = open(path) for line in file_in.readlines(): s_data = line.strip().split(,) temp = [] temp.append(1.0) if len(s_data) < 2: print "輸入數據錯誤" for i in range(len(s_data) - 1): temp.append(float(s_data[i])) x.append(temp) y.append(float(s_data[len(s_data) - 1])) return np.mat(x), np.mat(y).transpose()def data_preprocessing(x): """ 功能:數據預處理 參數:數據集 返回:處理後數據,均值,標準差 """ m, n = x.shape miu = np.zeros((n, 1)) sigma = np.zeros((n, 1)) for i in range(n): miu[i] = np.mean(x[:, i]) # 計算均值 sigma[i] = np.std(x[:, i]) # 計算標準差 for j in range(m): x[j, 1:] = np.divide(x[j, 1:] - miu[1:].transpose(), sigma[1:].transpose()) return x, miu, sigmadef J(x, y, theta): """ 功能:損失函數 參數:數據集,參數 返回:損失值 """ delta = x * theta - y ws = 0.5 * delta.transpose() * delta return wsdef train(x, y, alpha, maxIter): """ 功能:訓練函數 參數:訓練集,標籤,學習速率,最大迭代次數 返回:參數 """ theta = np.ones((x.shape[1], 1)) for i in range(maxIter): delta = x.transpose() * (x * theta - y) theta = theta - alpha * delta print "Iter:" + str(i+1), " J:" + str(J(x, y, theta).tolist()[0][0]) return thetadef train_SGD(x, y, alpha, maxEpoch): """ 功能:訓練函數(SGD) 參數:訓練集,標籤,學習速率,最大迭代次數 返回:參數 """ theta = np.ones((x.shape[1], 1)) for i in range(maxEpoch): # 每次迭代 for j in range(x.shape[0]): # 每個參數 delta = x[j, :].transpose() * (x[j, :] * theta - y[j, :]) theta = theta - alpha * delta print "Epoch:" + str(i + 1), " Iter:" + str(i * maxEpoch + j), " J:" + str(J(x, y, theta).tolist()[0][0]) return thetaif __name__ == __main__: train_x, train_y = load_data(data.txt) pre_train_x, miu, sigma = data_preprocessing(train_x) # theta = train(train_x, train_y, 0.02, 20) theta = train_SGD(train_x, train_y, 0.02, 20) print pre_train_x * theta
邏輯回歸代碼
數據(data.txt):
34.62365962451697,78.0246928153624,030.28671076822607,43.89499752400101,035.84740876993872,72.90219802708364,060.18259938620976,86.30855209546826,179.0327360507101,75.3443764369103,145.08327747668339,56.3163717815305,061.10666453684766,96.51142588489624,175.02474556738889,46.55401354116538,176.09878670226257,87.42056971926803,184.43281996120035,43.53339331072109,195.86155507093572,38.22527805795094,075.01365838958247,30.60326323428011,082.30705337399482,76.48196330235604,169.36458875970939,97.71869196188608,139.53833914367223,76.03681085115882,053.9710521485623,89.20735013750205,169.07014406283025,52.74046973016765,167.94685547711617,46.67857410673128,070.66150955499435,92.92713789364831,176.97878372747498,47.57596364975532,167.37202754570876,42.83843832029179,089.67677575072079,65.79936592745237,150.534788289883,48.85581152764205,034.21206097786789,44.20952859866288,077.9240914545704,68.9723599933059,162.27101367004632,69.95445795447587,180.1901807509566,44.82162893218353,193.114388797442,38.80067033713209,061.83020602312595,50.25610789244621,038.78580379679423,64.99568095539578,061.379289447425,72.80788731317097,185.40451939411645,57.05198397627122,152.10797973193984,63.12762376881715,052.04540476831827,69.43286012045222,140.23689373545111,71.16774802184875,054.63510555424817,52.21388588061123,033.91550010906887,98.86943574220611,064.17698887494485,80.90806058670817,174.78925295941542,41.57341522824434,034.1836400264419,75.2377203360134,083.90239366249155,56.30804621605327,151.54772026906181,46.85629026349976,094.44336776917852,65.56892160559052,182.36875375713919,40.61825515970618,051.04775177128865,45.82270145776001,062.22267576120188,52.06099194836679,077.19303492601364,70.45820000180959,197.77159928000232,86.7278223300282,162.07306379667647,96.76882412413983,191.56497449807442,88.69629254546599,179.94481794066932,74.16311935043758,199.2725269292572,60.99903099844988,190.54671411399852,43.39060180650027,134.52451385320009,60.39634245837173,050.2864961189907,49.80453881323059,049.58667721632031,59.80895099453265,097.64563396007767,68.86157272420604,132.57720016809309,95.59854761387875,074.24869136721598,69.82457122657193,171.79646205863379,78.45356224515052,175.3956114656803,85.75993667331619,135.28611281526193,47.02051394723416,056.25381749711624,39.26147251058019,030.05882244669796,49.59297386723685,044.66826172480893,66.45008614558913,066.56089447242954,41.09209807936973,040.45755098375164,97.53518548909936,149.07256321908844,51.88321182073966,080.27957401466998,92.11606081344084,166.74671856944039,60.99139402740988,132.72283304060323,43.30717306430063,064.0393204150601,78.03168802018232,172.34649422579923,96.22759296761404,160.45788573918959,73.09499809758037,158.84095621726802,75.85844831279042,199.82785779692128,72.36925193383885,147.26426910848174,88.47586499559782,150.45815980285988,75.80985952982456,160.45555629271532,42.50840943572217,082.22666157785568,42.71987853716458,088.9138964166533,69.80378889835472,194.83450672430196,45.69430680250754,167.31925746917527,66.58935317747915,157.23870631569862,59.51428198012956,180.36675600171273,90.96014789746954,168.46852178591112,85.59430710452014,142.0754545384731,78.84478600148043,075.47770200533905,90.42453899753964,178.63542434898018,96.64742716885644,152.34800398794107,60.76950525602592,094.09433112516793,77.15910509073893,190.44855097096364,87.50879176484702,155.48216114069585,35.57070347228866,074.49269241843041,84.84513684930135,189.84580670720979,45.35828361091658,183.48916274498238,48.38028579728175,142.2617008099817,87.10385094025457,199.31500880510394,68.77540947206617,155.34001756003703,64.9319380069486,174.77589300092767,89.52981289513276,1
邏輯回歸與線性回歸相比需要修改損失函數為極大似然估計中的 l(theta) ,利用梯度上升求得使 l(theta) 取得最大值時的參數。代碼如下:
# -*- coding: utf-8 -*-"""第一個版本"""import numpy as npdef load_data(path): """ 功能:讀取訓練數據 參數:數據文件路徑 返回:訓練集與標籤矩陣 """ x = [] y = [] file_in = open(path) for line in file_in.readlines(): s_data = line.strip().split(,) temp = [] temp.append(1.0) if len(s_data) < 2: print "輸入數據錯誤" for i in range(len(s_data) - 1): temp.append(float(s_data[i])) x.append(temp) y.append(float(s_data[len(s_data) - 1])) return np.mat(x), np.mat(y).transpose()def data_preprocessing(x): """ 功能:數據預處理 參數:數據集 返回:處理後數據,均值,標準差 """ m, n = x.shape miu = np.zeros((n, 1)) sigma = np.zeros((n, 1)) for i in range(n): miu[i] = np.mean(x[:, i]) sigma[i] = np.std(x[:, i]) for j in range(m): x[j, 1:] = np.divide(x[j, 1:] - miu[1:].transpose(), sigma[1:].transpose()) return x, miu, sigmadef l(x, y, theta): """ 功能:極大似然估計概率函數l(theta) 參數:數據集,參數 返回:損失值 """ delta = sigmoid(x * theta) costs = y.transpose() * np.log(delta) + (1.0 - y).transpose() * np.log(1.0 - delta) return sum(costs)def train(x, y, alpha, maxIter): """ 功能:訓練函數(變為梯度上升) 參數:訓練集,標籤,學習速率,最大迭代次數 返回:參數 """ theta = np.ones((x.shape[1], 1)) for i in range(maxIter): delta = y - sigmoid(x * theta) theta = theta + alpha * x.transpose() * delta print "Iter:" + str(i+1), " Alpha:" + str(alpha), " l:" + str(l(x, y, theta).tolist()[0][0]) return thetadef train_SGD(x, y, alpha, maxEpoch): """ 功能:訓練函數(SGD) 參數:訓練集,標籤,學習速率,最大迭代次數 返回:參數 """ theta = np.ones((x.shape[1], 1)) for i in range(maxEpoch): # 每次迭代 for j in range(x.shape[0]): #每個參數 delta = x[j, :].transpose() * (y[j, :] - sigmoid(x[j, :] * theta)) theta = theta + alpha * delta print "Epoch:" + str(i + 1), " Iter:" + str(i * maxEpoch + j), " l:" + str(l(x, y, theta).tolist()[0][0]) return thetadef sigmoid(z): """ 功能:sigmoid函數 參數:輸入矩陣 返回:輸出矩陣 """ return 1.0 / (1 + np.exp(-z))def test_accuracy(x, y, theta): """ 功能:計算測試準確率 參數:訓練集,標籤,參數 返回:準確率 """ m = x.shape[0] num = 0 predict_y = sigmoid(x * theta) # 預測值 difference = np.abs(y - predict_y).tolist() # 計算預測值與標籤的差值的絕對值,小於0.5則預測正確,否則預測錯誤 for i in range(m): if difference[i][0] < 0.5: num += 1 return num / float(m)if __name__ == __main__: train_x, train_y = load_data(data.txt) pre_train_x, miu, sigma = data_preprocessing(train_x) theta = train(train_x, train_y, 0.01, 100) # theta = train_SGD(train_x, train_y, 0.02, 20) print theta print test_accuracy(pre_train_x, train_y, theta)
在進行測試時,由於數據少,我們還是訓練集與測試集用的同一個數據集。在學習速率為0.01時,我們訓練到收斂後,得到的準確率為0.89。但是當我們將學習速率設置為0.0001時,在收斂後我們得到的準確率為0.91。在參數值接近最優解的時候,我們需要減小學習速率才會更加接近最優解,學習速率太大可能產生震蕩。所以應該隨著迭代次數的增加不斷減小學習速率。修改如下:
# -*- coding: utf-8 -*-"""第二個版本:1.修改學習速率隨迭代次數而減小,盡量少的迭代次數得到更高的準確率"""import numpy as npdef load_data(path): """ 功能:讀取訓練數據 參數:數據文件路徑 返回:訓練集與標籤矩陣 """ x = [] y = [] file_in = open(path) for line in file_in.readlines(): s_data = line.strip().split(,) temp = [] temp.append(1.0) if len(s_data) < 2: print "輸入數據錯誤" for i in range(len(s_data) - 1): temp.append(float(s_data[i])) x.append(temp) y.append(float(s_data[len(s_data) - 1])) return np.mat(x), np.mat(y).transpose()def data_preprocessing(x): """ 功能:數據預處理 參數:數據集 返回:處理後數據,均值,標準差 """ m, n = x.shape miu = np.zeros((n, 1)) sigma = np.zeros((n, 1)) for i in range(n): miu[i] = np.mean(x[:, i]) sigma[i] = np.std(x[:, i]) for j in range(m): x[j, 1:] = np.divide(x[j, 1:] - miu[1:].transpose(), sigma[1:].transpose()) return x, miu, sigmadef l(x, y, theta): """ 功能:極大似然估計概率函數l(theta) 參數:數據集,參數 返回:損失值 """ delta = sigmoid(x * theta) costs = y.transpose() * np.log(delta) + (1.0 - y).transpose() * np.log(1.0 - delta) return sum(costs)def train(x, y, alpha, maxIter, alpha_rate): """ 功能:訓練函數(變為梯度上升) 參數:訓練集,標籤,學習速率,最大迭代次數,學習速率下降速度 返回:參數 """ theta = np.ones((x.shape[1], 1)) for i in range(maxIter): delta = y - sigmoid(x * theta) theta = theta + alpha * x.transpose() * delta alpha -= alpha_rate * alpha print "Iter:" + str(i+1), " l:" + str(l(x, y, theta).tolist()[0][0]) return thetadef train_SGD(x, y, alpha, maxEpoch, alpha_rate): """ 功能:訓練函數(SGD) 參數:訓練集,標籤,學習速率,最大迭代次數,學習速率下降速度 返回:參數 """ theta = np.ones((x.shape[1], 1)) for i in range(maxEpoch): for j in range(x.shape[0]): delta = x[j, :].transpose() * (y[j, :] - sigmoid(x[j, :] * theta)) theta = theta + alpha * delta alpha -= alpha_rate * alpha print "Epoch:" + str(i + 1), " Iter:" + str(i * maxEpoch + j), " l:" + str(l(x, y, theta).tolist()[0][0]) return thetadef sigmoid(z): """ 功能:sigmoid函數 參數:輸入矩陣 返回:輸出矩陣 """ return 1.0 / (1 + np.exp(-z))def test_accuracy(x, y, theta): """ 功能:計算測試準確率 參數:訓練集,標籤,參數 返回:準確率 """ m = x.shape[0] num = 0 predict_y = sigmoid(x * theta) difference = np.abs(y - predict_y).tolist() for i in range(m): if difference[i][0] < 0.5: num += 1 return num / float(m)if __name__ == __main__: train_x, train_y = load_data(data.txt) pre_train_x, miu, sigma = data_preprocessing(train_x) theta = train(train_x, train_y, 0.01, 100, 0.2) # theta = train_SGD(train_x, train_y, 0.02, 15, 0.02) print theta print test_accuracy(pre_train_x, train_y, theta)
在每次迭代後alpha的大小都會減去本身的alpha_rate倍,實現alpha隨著迭代次數增加而減小。最後使用正常的梯度下降達到了0.92的準確率,使用SGD達到了0.93的準確率。
推薦閱讀:
※導數、偏導、次梯度關係
※正經機器學習之小巧的流程可視化機器學習工具
※kaggle實戰-房價預測
※通俗講解P(查准率),R(查全率),F1值
※深度森林(deep forest)
TAG:機器學習 |