機器學習之邏輯回歸

  • 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