機器學習之邏輯回歸
- 2019 年 10 月 4 日
- 筆記
文中的所有數據集鏈接:https://pan.baidu.com/s/1TV4RQseo6bVd9xKJdmsNFw
提取碼:8mm4
什麼是邏輯回歸
回想一下線性回歸,它被用於確定一個連續因變量的值。邏輯回歸通常用於分類目的。與線性回歸不同,因變量只能採用有限數量的值,即因變量是分類的。當可能結果的數量只有兩個時,它被稱為二元邏輯回歸。
從大的類別上來說,邏輯回歸是一種有監督的統計學習方法,主要用於對樣本進行分類
。
邏輯回歸與線性回歸的關係
邏輯回歸也被稱為廣義線性回歸模型,它與線性回歸模型的形式基本上相同,都具有 ax+b,其中a和b是待求參數,其區別在於他們的因變量不同,多重線性回歸直接將ax+b作為因變量,即y = ax+b,而logistic回歸則通過函數S將ax+b對應到一個隱狀態p,p = S(ax+b),然後根據p與1-p的大小決定因變量的值。這裡的函數S就是Sigmoid函數,就是因為這個函數的使用才能分類

在這裡插入圖片描述
該函數具有如下的特性:當x趨近於負無窮時,y趨近於0;當x趨近於正無窮時,y趨近於1;當x= 0時,y=0.5.
假設現在有一個函數 y = a*x1 +b *x2 +c*x3 +b
x1,x2,x3 帶入sigmoid函數中 得到y 如果大於0.5,分類為1,小於0.5,分類為0
德國信用卡欺詐數據集
歐洲的信用卡持卡人在2013年9月2天時間裏的284807筆交易數據,其中有492筆交易是欺詐交易,佔比0.172%。數據採用PCA變換映射為V1,V2,…,V28 數值型屬性,只有交易時間和金額這兩個變量沒有經過PCA變換。輸出變量為二值變量,1為正常,0為欺詐交易。

在這裡插入圖片描述 讀取excel文件
data = pd.read_csv("../credit-a.csv", header=None) data.head()

在這裡插入圖片描述 我們可以從圖上可以看到有15個特徵,一個分類結果 相當於y = a1*x1 +…… +a15 *x15 +b
邏輯回歸和分類數據集
from sklearn.model_selection import train_test_split from sklearn.linear_model.logistic import LogisticRegression
將x 作為特徵數據,y作為分類結果,因為輸出變量為二值變量,1為正常,0為欺詐交易,而我這是-1代表欺詐和交易,所以用replace方法把-1,變為0
x = data[data.columns[:-1]] # y = data[data.columns[15]].replace(-1,0)
將x和y分為訓練集和測試集
x_train,x_test,y_train,y_test = train_test_split(x,y) len(x_train),len(x_test) OUT: (489, 164)
和線性回歸一樣的操作
model = LogisticRegression() #用邏輯回歸建立模型 model.fit(x_train,y_train) #fit 和predict model.predict(x_test) OUT: array([1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1], dtype=int64) #這裡有164個結果
predict_proba 方法可以得到概率(Sigmoid的x和y)
model.predict_proba(x_test)

在這裡插入圖片描述
第一個數[1.42575343e-02, 9.85742466e-01] 也就是x = 0.014, y=0.9>0.5 分類結果為1
對於精確性如何,導入accuracy_score
from sklearn.metrics import accuracy_score accuracy_score(model.predict(x_test), y_test) OUT: 0.8719512195121951
也就是在168個測試樣本中168*0.8719512195121951個是正確的
預測Titanic乘客是否生還
來源kaggle
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(style="white") #設置seaborn畫圖的背景為白色 sns.set(style="whitegrid", color_codes=True) # 將數據讀入 DataFrame df = pd.read_csv("./titanic_data.csv") # 預覽數據 df.head()

print('數據集包含的數據個數 {}.'.format(df.shape[0]))
數據集包含的數據個數 1310.
2. 查看缺失數據
# 查看數據集中各個特徵缺失的情況 df.isnull().sum() out: pclass 1 survived 1 name 1 sex 1 age 264 sibsp 1 parch 1 ticket 1 fare 2 cabin 1015 embarked 3 dtype: int64
2.1. 年齡
# "age" 缺失的百分比 print('"age" 缺失的百分比 %.2f%%' %((df['age'].isnull().sum()/df.shape[0])*100)) "age" 缺失的百分比 20.15%
約 20% 的乘客的年齡缺失了. 看一看年齡的分別情況.
ax = df["age"].hist(bins=15, color='teal', alpha=0.6) ax.set(xlabel='age') plt.xlim(-10,85) plt.show()

在這裡插入圖片描述 由於「年齡」的偏度不為0, 使用均值替代缺失值不是最佳選擇, 這裡可以選擇使用中間值替代缺失值
在數量上,偏度為負(負偏態)就意味着在概率密度函數左側的尾部比右側的長,絕大多數的值(不一定包括中位數在內)位於平均值的右側。偏度為正(正偏態)就意味着在概率密度函數右側的尾部比左側的長,絕大多數的值(不一定包括中位數)位於平均值的左側。偏度為零就表示數值相對均勻地分佈在平均值的兩側,但不一定意味着其為對稱分佈。
# 年齡的均值 print('The mean of "Age" is %.2f' %(df["age"].mean(skipna=True))) # 年齡的中間值 print('The median of "Age" is %.2f' %(df["age"].median(skipna=True)))
The mean of "Age" is 29.88 The median of "Age" is 28.00
2.2. 倉位
# 倉位缺失的百分比 print('"Cabin" 缺失的百分比 %.2f%%' %((df['cabin'].isnull().sum()/df.shape[0])*100))
"Cabin" 缺失的百分比 77.48%
約 77% 的乘客的倉位都是缺失的, 最佳的選擇是不使用這個特徵的值.
2.3. 登船地點
# 登船地點的缺失率 print('"Embarked" 缺失的百分比 %.2f%%' %((df['embarked'].isnull().sum()/df.shape[0])*100))
"Embarked" 缺失的百分比 0.23%
只有 0.23% 的乘客的登船地點數據缺失, 可以使用眾數替代缺失的值.
print('按照登船地點分組 (C = Cherbourg, Q = Queenstown, S = Southampton):') print(df['embarked'].value_counts()) sns.countplot(x='embarked', data=df, palette='Set2') plt.show()
按照登船地點分組 (C = Cherbourg, Q = Queenstown, S = Southampton): S 914 C 270 Q 123 Name: embarked, dtype: int64

在這裡插入圖片描述
print('乘客登船地點的眾數為 %s.' %df['embarked'].value_counts().idxmax())
乘客登船地點的眾數為 S.
由於大多數人是在南安普頓(Southhampton)登船, 可以使用「S」替代缺失的數據值
2.4. 根據缺失數據情況調整數據
基於以上分析, 我們進行如下調整:
- 如果一條數據的 "Age" 缺失, 使用年齡的中位數 28 替代.
- 如果一條數據的 "Embarked" 缺失, 使用登船地點的眾數 「S」 替代.
- 由於太多乘客的 「Cabin」 數據缺失, 從所有數據中丟棄這個特徵的值.
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.isnull().sum()
pclass 1 survived 1 name 1 sex 1 age 0 sibsp 1 parch 1 ticket 1 fare 2 embarked 0 dtype: int64
#處理仍然存在缺失數據的情況 final.dropna(inplace=True)
# 預覽調整過的數據 data.head()

查看年齡在調整前後的分佈
plt.figure(figsize=(15,8)) ax = df["age"].hist(bins=15, normed=True, stacked=True, color='teal', alpha=0.6) df["age"].plot(kind='density', color='teal') ax = data["age"].hist(bins=15, normed=True, stacked=True, color='orange', alpha=0.5) data["age"].plot(kind='density', color='orange') ax.legend(['Raw Age', 'Adjusted Age']) ax.set(xlabel='Age') plt.xlim(-10,85) plt.show()

在這裡插入圖片描述
[
2.4.1. 其它特徵的處理
數據中的兩個特徵 「sibsp」 (一同登船的兄弟姐妹或者配偶數量)與「parch」(一同登船的父母或子女數量)都是代表是否有同伴同行. 為了預防這兩個特徵具有多重共線性, 我們可以將這兩個變量轉為一個變量 「TravelAlone」 (是否獨自一人成行)
注: 多重共線性(multicollinearity)是指多變量線性回歸中,變量之間由於存在高度相關關係而使回歸估計不準確。比如虛擬變量陷阱(英語:Dummy variable trap)即有可能觸發多重共線性問題。
## 創建一個新的變量'TravelAlone'記錄是否獨自成行, 丟棄「sibsp」 (一同登船的兄弟姐妹或者配偶數量)與「parch」(一同登船的父母或子女數量) 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)
對類別變量(categorical variables)使用獨熱編碼(One-Hot Encoding), 將字符串類別轉換為數值
# 對 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) final.head()

3. 數據分析
3.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()

在這裡插入圖片描述
生還與遇難群體的分佈相似, 唯一大的區別是生還群體中用一部分低年齡的乘客. 說明當時的人預先保留了孩子的生還機會.
3.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.3. 倉位
sns.barplot('pclass', 'survived', data=df, color="darkturquoise") plt.show()

在這裡插入圖片描述 如我們所料, 一等艙的乘客生還幾率最高.
3.4. 登船地點
sns.barplot('embarked', 'survived', data=df, color="teal") plt.show()

在這裡插入圖片描述
從法國 Cherbourge 登錄的乘客生還率最高
3.5. 是否獨自成行
sns.barplot('TravelAlone', 'survived', data=final, color="mediumturquoise") plt.show()

在這裡插入圖片描述
獨自成行的乘客生還率比較低. 當時的年代, 大多數獨自成行的乘客為男性居多.
3.6. 性別
sns.barplot('sex', 'survived', data=df, color="aquamarine") plt.show()

在這裡插入圖片描述 很明顯, 女性的生還率比較高
4. 使用Logistic Regression做預測
from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score # 使用如下特徵做預測 cols = ["age","fare","TravelAlone","pclass","embarked_C","embarked_S","sex_male"] # 創建 X (特徵) 和 y (類別標籤) X = final[cols] y = final['survived'] # 將 X 和 y 分為兩個部分 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=2) # 訓練模型, model = LogisticRegression() model.fit(X_train,y_train.values.reshape(-1,1)) # 根據模型, 以 X_test 為輸入, 生成變量 y_pred print('Train/Test split results:') y_pred = model.predict(X_test) print("準確率為 %2.3f" % accuracy_score(y_test, y_pred))
Train/Test split results: 準確率為 0.817