邏輯回歸可以看做是被sigmoid進行歸一化的線性函數。

這裡首先規定,向量x是分類器的輸入數據,向量 	heta 就是我們要找到的最佳參數。

  • sigmoid函數的輸入:
  • 預測函數(其實就是概率值):(sigmoid函數)

注意:多分類的預測函數為:(softmax函數)

  • 二分類任務: 分類對應的概率為

損失函數為:

利用極大似然函數進行求解,對應似然函數為:

對數似然函數為:

下面參數更新的求解就是在原有的對數似然函數上乘上了-1/m,由於是負數,所以也由梯度上升轉換到了梯度下降),對數似然函數乘上了-1/m之後,對這個新的式子求偏導。

  • 參數更新:

計算過程:

y=1的概率為:

這裡θ是模型參數,也就是回歸係數,σ是sigmoid函數。實際上這個函數是由下面的對數幾率(也就是x屬於正類的可能性和負類的可能性的比值的對數)變換得到的:

所以說上面的logistic回歸就是一個線性分類模型,它與線性回歸的不同點在於:為了將線性回歸輸出的很大範圍的數,例如從負無窮到正無窮,壓縮到0和1之間。

似然函數:

對數似然函數:

對對數似然函數進行求導,看導數為0的時候可不可以解出來,也就是有沒有解析解

然後我們令該導數為0,你會很失望的發現,它無法解析求解。藉助迭代進行求最優解。

梯度下降(gradient descent)

Gradient descent 又叫 steepest descent,是利用一階的梯度信息找到函數局部最優解的一種方法,也是機器學習裡面最簡單最常用的一種優化方法。它的思想很簡單,和我開篇說的那樣,要找最小值,只需要每一步都往下走(也就是每一步都可以讓代價函數小一點),然後不斷的走,那肯定能走到最小值的地方。

而這個下坡最快的方向,就是梯度的負方向了。

利用梯度下降法求參數

  • 在開始梯度下降之前,sigmoid function有一個很好的性質,

梯度的負方向就是代價函數下降最快的方向,藉助泰勒展開,可以得到(函數可微,可導)

其中,f(x) 和δ為向量,那麼這兩者的內積就等於

當θ=π時,也就是在δ與f(x)的方向相反時,取得最小值, 也就是下降的最快的方向了

這裡也就是: f(x+δ) - f(x) = - ||δ||·||f(x)||。

其中,wj表示第j個特徵的權重,η為學習率,用來控制步長。

對損失函數J(θ)中的θ的第j個權重求偏導。

所以,在使用梯度下降法更新權重時,根據以下公式進行參數更新:

梯度下降演算法的偽代碼如下:

################################################

初始化回歸係數為1

重複下面步驟直到收斂{

計算整個數據集的梯度

使用alpha x gradient來更新回歸係數

}

返回回歸係數值

################################################

2.2、隨機梯度下降SGD (stochastic gradient descent)

梯度下降演算法在每次更新回歸係數的時候都需要遍歷整個數據集(計算整個數據集的回歸誤差),該方法對小數據集尚可。但當遇到有數十億樣本和成千上萬的特徵時,就有點力不從心了,它的計算複雜度太高。改進的方法是一次僅用一個樣本點(的回歸誤差)來更新回歸係數。這個方法叫隨機梯度下降演算法。由於可以在新的樣本到來的時候對分類器進行增量的更新(假設我們已經在資料庫A上訓練好一個分類器h了,那新來一個樣本x。對非增量學習演算法來說,我們需要把x和資料庫A混在一起,組成新的資料庫B,再重新訓練新的分類器。但對增量學習演算法,我們只需要用新樣本x來更新已有分類器h的參數即可),所以它屬於在線學習演算法。與在線學習相對應,一次處理整個數據集的叫「批處理」。

隨機梯度下降演算法的偽代碼如下:

################################################

初始化回歸係數為1

重複下面步驟直到收斂{

對數據集中每個樣本

計算該樣本的梯度

使用alpha x gradient來更新回歸係數

}

返回回歸係數值

################################################

2.3、改進的隨機梯度下降

對隨機梯度下降演算法,我們做兩處改進來避免上述的波動問題:

1)在每次迭代時,調整更新步長alpha的值。隨著迭代的進行,alpha越來越小,這會緩解係數的高頻波動(也就是每次迭代係數改變得太大,跳的跨度太大)。當然了,為了避免alpha隨著迭代不斷減小到接近於0(這時候,係數幾乎沒有調整,那麼迭代也沒有意義了),我們約束alpha一定大於一個稍微大點的常數項。

2)每次迭代,改變樣本的優化順序。也就是隨機選擇樣本來更新回歸係數。這樣做可以減少周期性的波動,因為樣本順序的改變,使得每次迭代不再形成周期性。

改進的隨機梯度下降演算法的偽代碼如下:

################################################

初始化回歸係數為1

重複下面步驟直到收斂{

對隨機遍歷的數據集中的每個樣本

隨著迭代的逐漸進行,減小alpha的值

計算該樣本的梯度

使用alpha x gradient來更新回歸係數

}

返回回歸係數值

################################################

3. 優缺點

(1)、優點

1. 形式簡單,模型的可解釋性非常好。從特徵的權重可以看到不同的特徵對最後結果的影響,某個特徵的權重值比較高,那麼這個特徵最後對結果的影響會比較大;

2. 模型效果不錯。在工程上是可以接受的(作為baseline),如果特徵工程做的好,效果不會太差,並且特徵工程可以大家並行開發,大大加快開發的速度;

3. 訓練速度較快。分類的時候,計算量僅僅只和特徵的數目相關。並且邏輯回歸的分散式優化sgd發展比較成熟,訓練的速度可以通過堆機器進一步提高,這樣我們可以在短時間內迭代好幾個版本的模型;

4. 資源佔用小,尤其是內存。因為只需要存儲各個維度的特徵值;

5. 方便輸出結果調整。邏輯回歸可以很方便的得到最後的分類結果,因為輸出的是每個樣本的概率分數,我們可以很容易的對這些概率分數進行cutoff,也就是劃分閾值(大於某個閾值的是一類,小於某個閾值的是一類)。

(2)、缺點

1. 準確率並不是很高。因為形式非常的簡單(非常類似線性模型),很難去擬合數據的真實分布;

2. 很難處理數據不平衡的問題。舉個例子:如果我們對於一個正負樣本非常不平衡的問題比如正負樣本比 10000:1.我們把所有樣本都預測為正也能使損失函數的值比較小。但是作為一個分類器,它對正負樣本的區分能力不會很好;

3. 處理非線性數據較麻煩。邏輯回歸在不引入其他方法的情況下,只能處理線性可分的數據,或者進一步說,處理二分類的問題;

4. 邏輯回歸本身無法篩選特徵。有時候,我們會用gbdt來篩選特徵,然後再上邏輯回歸。

4. 實例

(1)導入數據

import numpy as np
import pandas as pd
from sklearn import preprocessing
import matplotlib.pyplot as plt
plt.rc("font",size=14)
import seaborn as sns
sns.set(stylex="white")
sns.set(stylex="whitegrid",color_codes=True)
df=pd.read_csv("D:/Users/jl.qiao/Desktop/網上數據文件夾/titanic/titanic(1).csv")
print(df.head())
print("數據集包含的數據個數%d"%df.shape[0])

得到:

前5行數據:

pclass survived ... cabin embarked0 1 1 ... B5 S1 1 1 ... C22 C26 S2 1 0 ... C22 C26 S3 1 0 ... C22 C26 S4 1 0 ... C22 C26 S數據的行數:數據集包含的數據個數1309

(2)數據分析

1.查看數據的確實情況

print(df.isnull().sum())

得到:

pclass 0

survived 0name 0sex 0age 263sibsp 0parch 0ticket 0fare 1cabin 1014embarked 2dtype: int64

可以看出在總數據1309中,age、fare、cabin 、embarked 4個特徵存在缺失情況。

2. 特徵age的數據分布

#年齡數據缺失的百分比
print("age" 缺失的百分比 %.2f%% %((df[age].isnull().sum()/df.shape[0])*100))

得到:

"age" 缺失的百分比 20.09%

#通過直方圖看不同年齡的分布情況
ax=df[age].hist(bins=15,color=teal,alpha=0.6)
ax.set(xlabel=age)
ax.set(ylabel=number)
ax.set(title=age_number hist)
plt.xlim(-10,85) #限定x軸的取值範圍
plt.show()

得到:

#年齡的均值和中位數
print(The mean of Age is %.2f%(df[age].mean(skipna=True)))
print(The median of Age is %.2f%(df[age].median(skipna=True)))
#skipna=False表示有缺失值不可加總,=True表示可以存在缺失值

得到:

The mean of Age is 29.88

The median of Age is 28.00

結論:由於「年齡」的偏度不為0,即數據是有偏的, 使用均值替代缺失值不是最佳選擇, 這裡可以選擇使用中位數替代缺失值。

3. 倉位

#缺失的百分比
print("cabin"缺失的百分比 %.2f%%%((df[cabin].isnull().sum()/df.shape[0])*100))

結論:約 77% 的乘客的倉位都是缺失的, 最佳的選擇是不使用這個特徵的值.

4. 登船地點

#缺失百分比
print("embarked"缺失的百分比 %.2f%%%((df[embarked].isnull().sum()/df.shape[0])*100))
print(按照登船地點分組(C=Cherbourg,Q=Queenstown,S=Southampton))
print(df[embarked].value_counts()) #類別計數

得到:

"embarked"缺失的百分比 0.15%

按照登船地點分組(C=Cherbourg,Q=Queenstown,S=Southampton:S 914C 270Q 123

sns.countplot(x=embarked,data=df,palette=Set2)
#條形圖,palette是調色板
plt.show()

得到:

乘客登船最多的港口位於:

print(乘客登船地點的眾數為%s%df[embarked].value_counts().idxmax())

得到:

乘客登船地點的眾數為:S

結論:由於登船地點數據缺失較少,且大多數人是在南安普頓(Southhampton)登船, 故可以使用「S」替代缺失的數據值。

5. 船票的費用

from scipy import stats
print("fare"缺失的百分比 %.2f%%%((df[fare].isnull().sum()/df.shape[0])*100))
print(df[fare].min(),df[fare].max()) #船票費用的最大值和最小值

得到:

"fare"缺失的百分比 0.08%

0.0 512.3292

#分布直方圖
ax=df[fare].hist(bins=15, color=yellow, alpha=0.5)
ax.set(xlabel=fare)
plt.xlim(-10,600)
plt.show()

得到:

#船票費用的均值和中位數
print(The mean of fare is %.2f%(df[fare].mean(skipna=True)))
print(The median of fare is %.2f%(df[fare].median(skipna=True)))
print(The mode of fare is %.2f%(stats.mode(df[fare])[0][0]))

得到:

The mean of fare is 33.30The median of fare is 14.45The mode of fare is 8.05

結論:從數據可以看出,船票費用呈長尾分布,且數據缺失個數僅為1個,故可以用眾數進行替代

(3)根據缺失情況調整數據

根據以上分析的情況,對缺失數據進行填充或刪除

data=df.copy()
data[age].fillna(df[age].median(skipna=True),inplace=True)
data[embarked].fillna(df[embarked].value_counts().idxmax(),inplace=True)
data.drop(cabin,axis=1,inplace=True)
data[fare].fillna(stats.mode(df[fare])[0][0],inplace=True)
print(data.isnull().sum())
print(data.head())

得到:

pclass 0survived 0name 0sex 0age 0sibsp 0parch 0ticket 0fare 0embarked 0dtype: int64 pclass survived ... fare embarked0 1 1 ... 211.3375 S1 1 1 ... 151.5500 S2 1 0 ... 151.5500 S3 1 0 ... 151.5500 S4 1 0 ... 151.5500 S

可以看到,經過處理後的數據已經沒有缺失值出現。

(4) 特徵處理

#數據中的兩個特徵 「sibsp」 (一同登船的兄弟姐妹或者配偶數量)與「parch」(一同登船的父母或子女數量)都是代表是否有同伴同行. 為了預防這兩個逼啊了可能的多重共線性, 我們可以將這兩個變數轉為一個變數 「TravelAlone」 (是否獨自一人成行)
data[TravelAlone]=np.where((data[sibsp]+data[parch])>0,0,1)
data.drop(sibsp,axis=1,inplace=True)
data.drop(parch,axis=1,inplace=True)
print(data.head())

# 對 "embarked","sex"進行獨熱編碼, 丟棄 name, ticket
final=pd.get_dummies(data,columns=[embarked,sex])
final.drop(name,axis=1,inplace=True)
final.drop(ticket,axis=1,inplace=True)
print(final.head())
print(final.columns.values.tolist()) #獲取dataframe的列名

得到:

pclass survived age ... embarked_S sex_female sex_male

0 1 1 29.0000 ... 1 1 01 1 1 0.9167 ... 1 0 12 1 0 2.0000 ... 1 1 03 1 0 30.0000 ... 1 0 14 1 0 25.0000 ... 1 1 0[pclass, survived, age, fare, TravelAlone, embarked_C, embarked_Q, embarked_S, sex_female, sex_male]

(5)各特徵的數據分析

1.年齡

plt.figure(figsize=(15,8))
ax=sns.kdeplot(final[age][final.survived==1],color=darkturquoise,shade=True)
sns.kdeplot(final[age][final.survived==0],color=lightcoral,shade=True)
plt.legend([Survived,Died])
plt.title(Density Plot of age for surviving population and deceased population)
ax.set(xlabel=Age)
plt.xlim(-10,85)
plt.show()

得到:

結論:生還與遇難群體的分布相似, 唯一大的區別是生還群體中有一部分低年齡的乘客. 說明當時的人預先保留了孩子的生還機會.

2.票價

plt.figure(figsize=(15,8))
#核密度估計圖
ax=sns.kdeplot(final[fare][final.survived==1],color=darkturquoise,shade=True)
sns.kdeplot(final[fare][final.survived==0],color=lightcoral,shade=True)
plt.legend([Survived,Died])
plt.title(Density Plot of fare for surviving population and deceased population)
ax.set(xlabel=Fare)
plt.xlim(-20,200)
plt.show()

得到:

結論:生還與遇難群體的票價分布差異比較大, 說明這個特徵對預測乘客是否生還非常重要. 票價和倉位相關, 也許是倉位影響了逃生的效果, 我們接下來看倉位的分析.

3.倉位

sns.barplot(x=pclass,y=survived,data=df,color=darkturquoise) #箱形圖
plt.show()

得到:

結論:一等艙的乘客生還幾率最高。

4. 登船地點

sns.barplot(embarked, survived, data=df, color="teal")
plt.show()

得到:

結論:法國 Cherbourge 登錄的乘客生還率最高。

5.是否獨立旅行

sns.barplot(TravelAlone, survived, data=final, color="mediumturquoise")
plt.show()

得到:

結論:女性的生還率比較高。

(6)利用logistic進行預測

from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.metrics import recall_score,precision_score,f1_score
from sklearn.metrics import roc_auc_score
#使用一下特徵進行預測
col=[age,fare,TravelAlone,pclass,embarked_C,embarked_S,sex_male]
#創建x和y
x=final[col]
y=final[survived]
#劃分訓練集和測試集
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.2,random_state=40)
#利用logistic回歸進行訓練
logi=LinearRegression()
logi.fit(x_train,y_train)
#利用訓練模型進行預測
y_pred=logi.predict(x_test)
#y_pred為預測概率,需要轉換成類別,一般>0.5為1
y_pred=np.where(y_pred>0.5,1,0)
print(準確率為 %2.3f%accuracy_score(y_test,y_pred))
print(auc為 %2.3f%roc_auc_score(y_test,y_pred))
#準確率、召回率和F1score
print(recall_score(y_test,y_pred),precision_score(y_test,y_pred),f1_score(y_test,y_pred))

得到:

準確率為 0.760

auc為 0.743以下分別是召回率、查准率和f1score0.6571428571428571 0.71875 0.6865671641791045

推薦閱讀:

相关文章