Linear Regression
在學習李航《統計學習方法》的邏輯斯特迴歸時,正好coursera上相應的線性迴歸和邏輯斯特迴歸都學習完成,在此就一起進行總結,其中圖片多來自coursera課程上。
線性迴歸是機器學習中很好理解的一種算法。我們以常見的房屋銷售爲例來進行簡單分析:
假設我們統計的一個房屋銷售的數據如下:
在此,我們從單一變量談起,直觀上比較容易理解。訓練集定義爲
假設空間
實際線性迴歸假設能夠擬合各種不同的曲線,實際的房子價格可能與房間面積、房間廳室、房間朝向等多個變量有關,我們可以定義特徵
其中
代價函數
在線性迴歸中,我們定義代價函數爲:
其中,係數
從表達式我們可以看出,學習的最終目的就是優化代價函數,使代價函數變小了,預測值和真值的差異就越小,訓練出來的模型就越好。如何求解
梯度下降法
梯度下降法是求解無約束最優化問題的一種最常見的方法,其實現簡單,易於理解。如下圖所述帶有二元參數的目標函數
- Algorithm 6.1
- initialize
θ ,θ={0,0,…,0} - for k = 1 : NumIter do
-
θj=θj−α∂∂θjJ(θ) end for
在線性線性迴歸中:
∂∂θjJ(θ)=1m∑i=1m(hθ(x(i))−y(i))xij
其中xij 是第i 個樣本實例的第j 維特徵。由此我們就可以學習出每個特徵的參數。
在梯度下降法中有兩個個關鍵參數選擇:學習率α 和初始化θ 。對於合適的學習率
α ,目標函數J(θ) 在每次迭代中都會減小,因此可以通過J(θ) 的值檢測算法的正確性。在實際操作中,α 太小,算法的收斂速度會很慢,當α 太大時,則會出現震盪,學習不到最佳參數。- 對於初始參數
θ ,不同的起點,可能會得到不同的最優解,即陷入局部最優。
最小二乘法
梯度下降法需要不斷的迭代計算,一般來說,收斂速度都會比較慢,另一種快速求解最佳解的方法是最小二乘法,具體公式爲:
在自我編程實現中,矩陣逆的求解是一個難點。另外,也存在不可解的情況:一是特徵相互關聯,不獨立;二是樣本數少於特徵數,可能使得矩陣的逆不存在。
過擬合和正則化
過擬合是機器學習中很普遍的例子,指的是訓練模型在訓練集上有很好的分類迴歸效果,但是在新的測試數據集上表現卻很差,即模型的泛化能力差。
如下圖所示,依舊以“大小-房價”線性迴歸爲例來說明。房價與房屋大小可能是非線性關係,如圖1所示,假設模型爲
解決過擬合問題常有以下幾種方式:
- 減少特徵數量
- 人爲選擇特徵,去掉不必要的特徵
- 機器學習選擇特徵,主成分分析降維等 正則化
- 保持所有特徵,但是減小學習參數θ 的值。
如上圖3所示,通過懲罰項使最終的學習參數θ3,θ4 極小,則最終模型與圖2模型很相近。即:
min1m{∑i=1m(hθ(x(i))−y(i))+1000θ23+1000θ24}
通過將θ3,θ4 帶入到損失函數中,使函數考慮模型複雜度的影響。正則化的目的就是將模型的複雜度考慮到代價函數中,使模型趨於簡單,不易過擬合。對於線性迴歸,正則化代價函數爲:
J(θ)=12m[∑i=1m(hθ(x(i))−y(i))2+λ∑i=1nθ2j]
其中,前面一部分是對訓練數據集的擬合誤差,後一部分正則化項是對模型複雜度的約束,λ 是調節兩則之間的權重:當
λ 較小時,極限情況下λ=0 ,則不考慮模型的複雜度,是原有的損失函數- 當
λ 較大時,則訓練的參數很小,模型可能會欠擬合
Logistic Regression
迴歸問題一般是連續預測:如房價預測、銷售額預測,即輸出
分類問題則是離散預測:郵件分類(垃圾/正常),細胞檢測(正常/癌變),輸出一般對應有限狀態。
一般來說,線性迴歸不能直接用於分類問題,因爲迴歸是連續性模型,而且受噪音比較大,我們一般選擇logistic迴歸來進行分類。logistic本質是線性迴歸,只是在特徵到結果的映射中加入了一層映射函數。
邏輯斯特迴歸模型
對於二分類系統,我們希望學習模型的輸出爲0或1,對於固定的特徵,我們希望學習模型預測其屬於正例的概率。即:
如下圖所示,我們定義邏輯斯特迴歸的學習規則爲:
θTx≥0 ,則hθ(x)≥0.5 ,此時認爲樣本屬於正樣本的概率更大,即y=1 θTx<0 ,則hθ(x)<0.5 ,此時認爲樣本屬於正樣本的概率更大,即y=0
決策邊界
對於分類問題,最終就是得到一個分類邊界,使樣本能夠被準確區分開。如下圖所示的兩類樣本,我們假設紅色爲正樣本,即
- 當
−3+x1+x2≥0 時,即θTx≥0 ,此時有hθ(x)≥0.5 ,決策爲正樣本,從圖中我們可以看到−3+x1+x2=0 右上側爲正樣本 - 同理,當
−3+x1+x2<0 時,即θTx<0 有hθ(x)<0.5 ,決策爲負樣本。
通過該直線我們可以將二分類樣本正確區分開,這樣的邊界也稱爲決策邊界。如果樣本是非線性可分的,我們也可以通過複雜多項式進行分類。邏輯斯特迴歸最終學習到的模型就是這樣的邊界圖,在邊界的兩邊就是兩個不同的類別。
損失函數
邏輯斯特迴歸代價函數一般定義爲:
因爲
最終的代價函數爲:
如果考慮模型的複雜度,即加入正則項,則爲:
最終目標是最小化目標函數,用梯度下降法求解,則:
通過推導我們發現邏輯斯特迴歸的代價函數與線性迴歸形式上很像,不同之處在於模型假設不一樣,線性迴歸是
多分類問題
logistic迴歸也可用於擴展用於多分類問題,解決辦法常見的就是一對多。如下圖所示有三類樣本,我們可以先用一個分類器將類別一與另外兩類區分開(右圖1),然後用同樣的辦法訓練兩個分類器,將每個類別區分開。在得到的三個假設模型中,我們計算每個樣本在每個模型中的值,即概率,通過選取最大的概率,就能確定樣本所屬的類別。
Python實現
最後我們通過Python實現了簡單的logistic二分類問題,具體代碼如下:
讀取txt文件中的訓練數據,包含特徵和標籤,並給特徵加上偏置項1
# load training data set
def loadData(path):
dataMat = []; labelMat = [];
f = open(path)
data= f.read().split()
for datastring in data:
dataMat.append([1,float(datastring.split(',')[0]),float(datastring.split(',')[1])])
labelMat.append(int(datastring.split(',')[2]))
return dataMat,labelMat
從txt中讀取的特徵值很大,進行標準歸一化之後進行訓練。
def featureNormalize(dataMat):
dataMatrix = mat(dataMat)
data_norm = dataMatrix;
m,n = shape(dataMatrix)
mu = mean(dataMatrix[:,1:3],axis = 0)
sigma = std(dataMatrix[:,1:3],axis = 0)
data_norm[:,1:3]= [x/y for x,y in zip((dataMatrix[:,1:3]-tile(mu,(m,1))),tile(sigma,(m,1)))]
return data_norm,mu,sigma
繪製最終分類效果圖和損失函數的變化
# plot data set
def plotdata(theta,mu,sigma,dataMat,labelMat):
dataArr = array(dataMat)
positive_x =[]; positive_y = []
negtive_x =[]; negative_y = []
for i in range(len(labelMat)):
if 1 == int(labelMat[i]):
positive_x.append(dataArr[i,1]);positive_y.append(dataArr[i,2])
else:
negtive_x.append(dataArr[i,1]);negative_y.append(dataArr[i,2])
fig1 = plt.figure('fig1')
ax = fig1.add_subplot(111)
ax.scatter(positive_x,positive_y,s=30,c='red',marker='s')
ax.scatter(negtive_x,negative_y,s=30,c='green')
min_x = min(dataArr[:,1])
max_x = max(dataArr[:,1])
y_min_x = (-theta[0]-theta[1]*(min_x-mu[0,0])/sigma[0,0])*sigma[0,1]/theta[2]+mu[0,1]
y_max_x = (-theta[0]-theta[1]*(max_x-mu[0,0])/sigma[0,0])*sigma[0,1]/theta[2]+mu[0,1]
ax.plot([min_x,max_x],[y_min_x,y_max_x],'-g')
plt.xlabel('X1');plt.ylabel('X2');plt.legend();
plt.show()
# plot cost
def plotJ(J_history):
fig2 = plt.figure('fig2')
ax = fig2.add_subplot(111)
x = arange(0,len(J_history),1)
ax.plot(x,J_history)
plt.xlabel('Iter');plt.ylabel('cost');plt.legend();
plt.show()
梯度下降算法:
# sigmoid function
def sigmoid(z):
return 1.0 / (1+exp(-z))
# train
def gradientReg(dataMat,labelMat,alpha,lambda1,MaxIter):
dataMatrix = mat(dataMat)
labelMatrix = mat(labelMat).transpose()
m,n = shape(dataMatrix)
J = zeros((MaxIter,1))
theta = zeros((n,1))
for k in range(MaxIter):
h = sigmoid(dataMatrix*theta)
J[k] = 1.0/m*sum(-multiply(labelMatrix,log(h))-multiply((1-labelMatrix),log(1-h)))+\
lambda1/(2*m)*(sum(theta[2:n]**2))
error = (h-labelMatrix)
for i in range(n):
if 0 == i:
theta[i] = theta[i] - alpha*1.0/m*(error.transpose()*dataMatrix[:,i])
else:
theta[i] = theta[i] - alpha*1.0/m*(error.transpose()*dataMatrix[:,i]+lambda1*theta[i])
return theta,J
通過訓練模型進行分類預測
# predict
def predict(theta,dataMat):
prob = sigmoid(dataMat*theta)
p = double(prob>0.5)
return p;
主函數
# main
if __name__=="__main__":
dataMat = []; labelMat = [];
alpha = 0.1;lambda1 = 0; MaxIter = 1000;
datapath = 'F:\Program\Python\Machine_Learning\Logistic\src\ex2data1.txt'
dataMat,labelMat=loadData(datapath)
data_norm,mu,sigma =featureNormalize(dataMat)
theta,J_history = gradientReg(data_norm,labelMat,alpha,lambda1,MaxIter)
plotdata(theta,mu,sigma,dataMat,labelMat)
plotJ(J_history)
p = predict(theta,data_norm)
print "the classify accuracy is:%.3f%%" %(mean(double(p.transpose() == labelMat)) * 100)
當
當
當
通過對比圖1和圖2,可以發現當調節參數
PS:
本文主要參考了李航《統計學習方法》和斯坦福的在線課程,圖表也多引用自斯坦福課程,主要用於自我學習總結,代碼完整示例見此處。