《Python數據分析與機器學習實戰-唐宇迪》讀書筆記第6章--邏輯回歸項目實戰 ——信用卡欺詐檢測


python數據分析個人學習讀書筆記-目錄索引

第6章--邏輯回歸項目實戰 ——信用卡欺詐檢測

   本章從實戰的角度出發,以真實數據集為背景,一步步講解如何使用Python工具包進行實際數據分析與建模工作。

6.1數據分析與預處理

   假設有一份信用卡交易記錄,遺憾的是數據經過了脫敏處理,只知道其特征,卻不知道每一個字段代表什么含義,沒關系,就當作是一個個數據特征。在數據中有兩種類別,分別是正常交易數據和異常交易數據,字段中有明確的標識符。要做的任務就是建立邏輯回歸模型,以對這兩類數據進行分類。

  在機器學習建模任務中,包括數據預處理、特征提取、模型調參等,每一步都會對最終的結果產生影響。

6.1.1數據讀取與分析

   先把任務所需的工具包導入進來

1 import pandas as pd
2 import matplotlib.pyplot as plt
3 import numpy as np
4 
5 %matplotlib inline

  信用卡交易記錄數據是一個.csv文件,里面包含約28萬條數據,規模很大,原始文件:

邀月工作室

  首先使用Pandas工具包讀取數據:

1 data = pd.read_csv("creditcard.csv")
2 data.head()

 邀月工作室

  如上,原始數據為個人交易記錄,該數據集總共有31列,其中數據特征有30列,Time列暫時不考慮,Amount列表示貸款的金額,Class列表示分類結果,若Class為0代表該條交易記錄正常,若Class為1代表交易異常。

 邀月工作室

  拿到這樣一份原始數據之后,直觀感覺就是數據已經是處理好的特征,只需要對其進行建模任務即可。但是,上述輸出結果只展示了前5條交易記錄並且發現全部是正常交易數據,在實際生活中似乎正常交易也占絕大多數,異常交易僅占一少部分,那么,在整個數據集中,樣本分布是否均衡呢?也就是說,在Class列中,正常數據和異常數據的比例是多少?繪制一份圖表更能清晰說明:

1 count_classes = pd.value_counts(data['Class'], sort = True).sort_index()
2 count_classes.plot(kind = 'bar')
3 plt.title("Fraud class histogram")
4 plt.xlabel("Class")
5 plt.ylabel("Frequency")

  邀月工作室

  上述代碼首先計算出Class列中各個指標的個數,也就是0和1分別有多少個。為了更直觀地顯示,數據繪制成條形圖,從上圖中可以發現,似乎只有0沒有1(仔細觀察,其實是1的比例太少),說明數據中絕大多數是正常數據,異常數據極少。

  這個問題看起來有點嚴峻,數據極度不平衡會對結果造成什么影響呢?模型會不會一邊倒呢?認為所有數據都是正常的,完全不管那些異常的,因為異常數據微乎其微,這種情況出現的可能性很大。我們的任務目標就是找到異常數據,如果模型不重視異常數據,結果就沒有意義了,所以,首先要做的就是改進不平衡數據。

  在機器學習任務中,加載數據后,首先應當觀察數據是否存在問題,先把問題處理掉,再考慮特征提取與建模任務

6.1.2樣本不均衡解決方案

   那么,如何解決數據標簽不平衡問題呢?首先,造成數據標簽不平衡的最根本的原因就是它們的個數相差懸殊,如果能讓它們的個數相差不大,或者比例接近,這個問題就解決了。基於此,提出以下兩種解決方案。

  • (1)下采樣。既然異常數據比較少,那就讓正常樣本和異常樣本一樣少。例如正常樣本有30W個,異常樣本只有500個,若從正常樣本中隨機選出500個,它們的比例就均衡了。雖然下采樣的方法看似很簡單,但是也存在瑕疵,即使原始數據很豐富,下采樣過后,只利用了其中一小部分,這樣對結果會不會有影響呢?
  • (2)過采樣。不想放棄任何有價值的數據,只能讓異常樣本和正常樣本一樣多,怎么做到呢?異常樣本若只有500個,此時可以對數據進行變換,假造出來一些異常數據,數據生成也是現階段常見的一種套路。雖然數據生成解決了異常樣本數量的問題,但是異常數據畢竟是造出來的,會不會存在問題呢?

  這兩種方案各有優缺點,到底哪種方案效果更好呢?需要進行實驗比較。

  在開始階段,應當多提出各種解決和對比方案,盡可能先把全局規划制定完整,如果只是想一步做一步,會做大量重復性操作,降低效率。

6.1.3特征標准化

   既然已經有了解決方案,是不是應當按照制訂的計划准備開始建模任務呢?千萬別心急,還差好多步呢,首先要對數據進行預處理,可能大家覺得機器學習的核心就是對數據建模,其實建模只是其中一部分,通常更多的時間和精力都用於數據處理中,例如數據清洗、特征提取等,這些並不是小的細節,而是十分重要的核心內容。目的都是使得最終的結果更好,該書作者經常說:“數據特征決定結果的上限,而模型的調優只決定如何接近這個上限。”

  觀察圖6-2可以發現,Amount列的數值變化幅度很大,而V1~V28列的特征數據的數值都比較小,此時Amount列的數值相對來說比較大。這會產生什么影響呢?模型對數值是十分敏感的,它不像人類能夠理解每一個指標的物理含義,可能會認為數值大的數據相對更重要(此處僅是假設)。但是在數據中,並沒有強調Amount列更重要,而是應當同等對待它們,因此需要改善一下。

  特征標准化就是希望數據經過處理后得到的每一個特征的數值都在較小范圍內浮動,公式如下:

 邀月工作室

  其中,Z為標准化后的數據;X為原始數據;Xmean為原始數據的均值;std(X)為原始數據的標准差。

  如果把式(6.1)的過程進行分解,就會更加清晰明了。首先將數據的各個維度減去其各自的均值,這樣數據就是以原點為中心對稱。其中數值浮動較大的數據,其標准差也必然更大;數值浮動較小的數據,其標准差也會比較小。再將結果除以各自的標准差,就相當於讓大的數據壓縮到較小的空間中,讓小的數據能夠伸張一些,對於圖6-3所示的二維數據,就得到其標准化之后的結果,以原點為中心,各個維度的取值范圍基本一致。

 邀月工作室

  圖6-3 數據標准化過程

  接下來,很多數據處理和機器學習建模任務都會用到sklearn工具包,這里先做簡單介紹,該工具包提供了幾乎所有常用的機器學習算法,僅需一兩行代碼,即可完成建模工作,計算也比較高效。不僅如此,還提供了非常豐富的數據預處理與特征提取模塊,方便大家快速上手處理數據特征。它是Python中非常實用的機器學習建模工具包,在后續的實戰任務中,都會出現它的身影。

  Sklearn工具包提供了在機器學習中最核心的三大模塊(Classification、Regression、Clustering)的實現方法供大家調用,還包括數據降維(Dimensionality reduction)、模型選擇(Model selection)、數據預處理(Preprocessing)等模塊,功能十分豐富,如圖6-4所示。在初學階段,大家還可以參考Examples模塊,基本上所有算法和函數都配套相應的實例程序供大家參考。

API:  https://scikit-learn.org/stable/modules/classes.html

  Sklearn工具包還提供了很多實際應用的例子,並且配套相應的代碼與可視化展示方法。

示例:https://scikit-learn.org/stable/auto_examples/index.html

  使用sklearn工具包來完成特征標准化操作,代碼如下:

1 from sklearn.preprocessing import StandardScaler
2 
3 data['normAmount'] = StandardScaler().fit_transform(data['Amount'].values.reshape(-1, 1))
4 data = data.drop(['Time','Amount'],axis=1)
5 data.head()

  上述代碼使用 StandardScaler方法對數據進行標准化處理,調用時需先導入該模塊,然后進行fit_transform操作,相當於執行公式(6.1)。reshape(−1,1)的含義是將傳入數據轉換成一列的形式(需按照函數輸入要求做)。最后用drop操作去掉無用特征。上述輸出結果中的normAmount列就是標准化處理后的結果,可見數值都在較小范圍內浮動。

  數據預處理過程非常重要,絕大多數任務都需要對特征數據進行標准化操作(或者其他預處理方法,如歸一化等)。

 

6.2下采樣方案

   下采樣方案的實現過程比較簡單,只需要對正常樣本進行采樣,得到與異常樣本一樣多的個數即可,代碼如下:

 1 X = data.iloc[:, data.columns != 'Class']
 2 y = data.iloc[:, data.columns == 'Class']
 3 
 4 # 得到所有異常樣本的索引
 5 number_records_fraud = len(data[data.Class == 1])
 6 fraud_indices = np.array(data[data.Class == 1].index)
 7 
 8 # 得到所有正常樣本的索引
 9 normal_indices = data[data.Class == 0].index
10 
11 # 在正常樣本中隨機采樣出指定個數的樣本,並取其索引
12 random_normal_indices = np.random.choice(normal_indices, number_records_fraud, replace = False)
13 random_normal_indices = np.array(random_normal_indices)
14 
15 # 有了正常和異常樣本后把它們的索引都拿到手
16 under_sample_indices = np.concatenate([fraud_indices,random_normal_indices])
17 
18 # 根據索引得到下采樣所有樣本點
19 under_sample_data = data.iloc[under_sample_indices,:]
20 
21 X_undersample = under_sample_data.iloc[:, under_sample_data.columns != 'Class']
22 y_undersample = under_sample_data.iloc[:, under_sample_data.columns == 'Class']
23 
24 # 下采樣 樣本比例
25 print("正常樣本所占整體比例: ", len(under_sample_data[under_sample_data.Class == 0])/len(under_sample_data))
26 print("異常樣本所占整體比例: ", len(under_sample_data[under_sample_data.Class == 1])/len(under_sample_data))
27 print("下采樣策略總體樣本數量: ", len(under_sample_data))

  整體流程比較簡單,首先計算異常樣本的個數並取其索引,接下來在正常樣本中隨機選擇指定個數樣本,最后把所有樣本索引拼接在一起即可。上述輸出結果顯示,執行下采樣方案后,一共有984條數據,其中正常樣本和異常樣本各占50%,此時數據滿足平衡標准。

正常樣本所占整體比例:  0.5
異常樣本所占整體比例:  0.5
下采樣策略總體樣本數量:  984

6.2.1交叉驗證

  得到輸入數據后,接下來划分數據集,在機器學習中,使用訓練集完成建模后,還需知道這個模型的效果,也就是需要一個測試集,以幫助完成模型測試工作。不僅如此,在整個模型訓練過程中,也會涉及一些參數調整,所以,還需要驗證集,幫助模型進行參數的調整與選擇。

  突然出現很多種集合,感覺很容易弄混,再來總結一下。

  首先把數據分成兩部分,左邊是訓練集,右邊是測試集,如圖6-8所示。訓練集用於建立模型,例如以梯度下降來迭代優化,這里需要的數據就是由訓練集提供的。測試集是當所有建模工作都完成后使用的,需要強調一點,測試集十分寶貴,在建模的過程中,不能加入任何與測試集有關的信息,否則就相當於透題,評估結果就不會准確。可以自己設定訓練集和測試集的大小和比例,8︰2、9︰1都是常見的切分比例。

 邀月工作室

  圖6-8 訓練集與測試集

  接下來需要對數據集再進行處理,如圖6-9所示,可以發現測試集沒有任何變化,僅把訓練集划分成很多份。這樣做的目的在於,建模嘗試過程中,需要調整各種可能影響結果的參數,因此需要知道每一種參數方案的效果,但是這里不能用測試集,因為建模任務還沒有全部完成,所以驗證集就是在建模過程中評估參數用的,那么單獨在訓練集中找出來一份做驗證集(例如fold5)不就可以了嗎,為什么要划分出來這么多小份呢?

 邀月工作室

  圖6-9 驗證集划分

  如果只是單獨找出來一份,恰好這一份數據比較簡單,那么最終的結果可能會偏高;如果選出來的這一份里面有一些錯誤點或者離群點,得到的結果可能就會偏低。無論哪種情況,評估結果都會出現一定偏差。

  為了解決這個問題,可以把訓練集切分成多份,例如將訓練集分成10份,如圖6-10所示。在驗證某一次結果時,需要把整個過程分成10步,第一步用前9份當作訓練集,最后一份當作驗證集,得到一個結果,以此類推,每次都依次用另外一份當作驗證集,其他部分當作訓練集。這樣經過10步之后,就得到10個結果,每個結果分別對應其中每一小份,組合在一起恰好包含原始訓練集中所有數據,再對最終得到的10個結果進行平均,就得到最終模型評估的結果。這個過程就叫作交叉驗證。

 邀月工作室

  圖6-10 交叉驗證

  交叉驗證看起來有些復雜,但是能對模型進行更好的評估,使得結果更准確,從后續的實驗中,大家會發現,用不同驗證集評估的時候,結果差異很大,所以這個套路是必須要做的。在sklearn工具包中,已經實現好數據集切分的功能,這里需先將數據集划分成訓練集和測試集,切分驗證集的工作等到建模的時候再做也來得及,代碼如下:

 1 # from sklearn.cross_validation import train_test_split
 2 #ModuleNotFoundError: No module named 'sklearn.cross_validation'
 3 #改為model_selection,邀月注
 4 from sklearn.model_selection import train_test_split
 5 
 6 # 整個數據集進行划分
 7 X_train, X_test, y_train, y_test = train_test_split(X,y,test_size = 0.3, random_state = 0)
 8 
 9 print("原始訓練集包含樣本數量: ", len(X_train))
10 print("原始測試集包含樣本數量: ", len(X_test))
11 print("原始樣本總數: ", len(X_train)+len(X_test))
12 
13 # 下采樣數據集進行划分
14 X_train_undersample, X_test_undersample, y_train_undersample, y_test_undersample = train_test_split(X_undersample
15                                                                                                    ,y_undersample
16                                                                                                    ,test_size = 0.3
17                                                                                                    ,random_state = 0)
18 print("")
19 print("下采樣訓練集包含樣本數量: ", len(X_train_undersample))
20 print("下采樣測試集包含樣本數量: ", len(X_test_undersample))
21 print("下采樣樣本總數: ", len(X_train_undersample)+len(X_test_undersample))

  通過輸出結果可以發現,在切分數據集時做了兩件事:首先對原始數據集進行划分,然后對下采樣數據集進行划分。我們最初的目標不是要用下采樣數據集建模嗎,為什么又對原始數據進行切分操作呢?

原始訓練集包含樣本數量:  199364
原始測試集包含樣本數量:  85443
原始樣本總數:  284807

下采樣訓練集包含樣本數量:  688
下采樣測試集包含樣本數量:  296
下采樣樣本總數:  984

 

6.2.2模型評估方法

  接下來,沒錯,還沒到實際建模任務,還需要考慮模型的評估方法,為什么建模之前要考慮整個過程呢?因為建模是一個過程,需要優先考慮如何評估其價值,而不是僅僅提供一堆模型參數值。

  准確率是分類問題中最常使用的一個參數,用於說明在整體中做對了多少。下面舉一個與這份數據集相似的例子:醫院中有1000個病人,其中10個患癌,990個沒有患癌,需要建立一個模型來區分他們。假設模型認為病人都沒有患癌,只有10個人分類有錯,因此得到的准確率高達990/1000,也就是0.99,看起來是十分不錯的結果。但是建模的目的是找出患有癌症的病人,即使一個都沒找到,准確率也很高。這說明對於不同的問題,需要指定特定的評估標准,因為不同的評估方法會產生非常大的差異。

  選擇合適的評估方法非常重要,因為評估方法是為整個實驗提供決策的服務的,所以一定要基於實際任務與數據集進行選擇。

  在這個問題中,癌症患者與非癌症患者人數比例十分不均衡,那么,該如何建模呢?既然已經明確建模的目標是為了檢測到癌症患者(異常樣本),應當把關注點放在他們身上,可以考慮模型在異常樣本中檢測到多少個。對於上述問題來說,一個癌症病人都沒檢測到,意味着召回率(Recall)為0。這里提到了召回率,先通俗理解一下:就是觀察給定目標,針對這個目標統計你取得了多大成績,而不是針對整體而言。

  如果直接給出計算公式,理解起來可能有點吃力,現在先來解釋一下在機器學習以及數據科學領域中常用的名詞,理解了這些名詞,就很容易理解這些評估方法。

  下面還是由一個問題來引入,假如某個班級有男生80人,女生20人,共計100人,目標是找出所有女生。現在某次實驗挑選出50個人,其中20人是女生,另外還錯誤地把30個男生也當作女生挑選出來(這里把女生當作正例,男生當作負例)。

  表6-1列出了TP、TN、FP、FN四個關鍵詞的解釋,這里告訴大家一個竅門,不需要死記硬背,從詞表面的意思上也可以理解它們。

  表6-1 TP、TN、FP、FN解釋

 邀月工作室

  • (1)TP。首先,第一個詞是True,這就表明模型預測結果正確,再看Positive,指預測成正例,組合在一起就是首先模型預測正確,即將正例預測成正例。返回來看題目,選出來的50人中有20個是女生,那么TP值就是20,這20個女生被當作女生選出來。
  • (2)FP。FP表明模型預測結果錯誤,並且被當作Positive(也就是正例)。在題目中,就是錯把男生當作女生選出來。在這里目標是選女生,選出來的50人中有30個卻是男的,因此FP等於30。
  • (3)FN。同理,首先預測結果錯誤,並且被當作負例,也就是把女生錯當作男生選出來,題中並沒有這個現象,所以FN等於0。
  • (4)TN。預測結果正確,但把負例當作負例,將男生當作男生選出來,題中有100人,選出認為是女生的50人,剩下的就是男生了,所以TN等於50。
  • 上述評估分析中常見的4個指標只需要掌握其含義即可。下面來看看通過這4個指標能得出什么結論。

•准確率(Accuracy):表示在分類問題中,做對的占總體的百分比。

 邀月工作室

•召回率(Recall):表示在正例中有多少能預測到,覆蓋面的大小。

 邀月工作室

•精確度(Precision):表示被分為正例中實際為正例的比例。

 邀月工作室

  上面介紹了3種比較常見的評估指標,下面回到信用卡分類問題,想一想在這份檢測任務中,應當使用哪一個評估指標呢?由於目的是查看有多少異常樣本能被檢測出來,所以應當使用召回率進行模型評估。

6.2.3正則化懲罰

   本小節討論的是正則化懲罰,這個名字看起來有點別扭,好好的模型為什么要懲罰呢?先來解釋一下過擬合的含義。

  建模的出發點就是盡可能多地滿足樣本數據,在圖6-11中,圖6-11(a)中直線看起來有點簡單,沒有滿足大部分數據樣本點,這種情況就是欠擬合,究其原因,可能由於模型本身過於簡單所導致。再來看圖6-11(b),比圖6-11(a)所示模型稍微復雜些,可以滿足大多數樣本點,這是一個比較不錯的模型。但是通過觀察可以發現,還是沒有抓住所有樣本點,這只是一個大致輪廓,那么如果能把模型做得更復雜,豈不是更好?再來看圖6-11(c),這是一個非常復雜的回歸模型,竟然把所有樣本點都抓到了,給人的第一感覺是模型十分強大,但是也會存在一個問題—模型是在訓練集上得到的,測試集與訓練集卻不完全一樣,一旦進行測試,效果可能不盡如人意。

 邀月工作室

  圖6-11 過擬合問題

  在機器學習中,通常都是先用簡單的模型進行嘗試,如果達不到要求,再做復雜一點的,而不是先用最復雜的模型來做,雖然訓練集的准確度可以達到99%甚至更高,但是實際應用的效果卻很差,這就是過擬合。

  我們在機器學習任務中經常會遇到過擬合現象,最常見的情況就是隨着模型復雜程度的提升,訓練集效果越來越好,但是測試集效果反而越來越差,如圖6-12所示。

  對於同一算法來說,模型的復雜程度由誰來控制呢?當然就是其中要求解的參數(例如梯度下降中優化的參數),如果在訓練集上得到的參數值忽高忽低,就很可能導致過擬合,所以正則化懲罰就是為解決過擬合准備的,即懲罰數值較大的權重參數,讓它們對結果的影響小一點。

 邀月工作室

  圖6-12 過擬合現象

  還是舉一個例子來看看其作用,假設有一條樣本數據是x:[1,1,1,1],現在有兩個模型:

•θ1:[1,0,0,0]

•θ2:[0.25,0.25,0.25,0.25]

  可以發現,模型參數θ1、θ2與數據x組合之后的結果都為1(也就是對應位置相乘求和的結果)。這是不是意味着兩個模型的效果相同呢?再觀察發現,兩個參數本身有着很大的差異,θ1只有第一個位置有值,相當於只注重數據中第一個特征,其他特征完全不考慮;而θ2會同等對待數據中的所有特征。雖然它們的結果相同,但是,如果讓大家來選擇,大概都會選擇第二個,因為它比較均衡,沒有那么絕對。

  在實際建模中,也需要進行這樣的篩選,選擇泛化能力更強的也就是都趨於穩定的權重參數。那么如何把控參數呢?此時就需要一個懲罰項,以懲罰那些類似θ1模型的參數,懲罰項會與目標函數組合在一起,讓模型在迭代過程中就開始重視這個問題,而不是建模完成后再來調整,常見的有L1和L2正則化懲罰項:

•L1正則化:

 邀月工作室

•L2正則化:

 邀月工作室

  兩種正則化懲罰方法都對權重參數進行了處理,既然加到目標函數中,目的就是不讓個別權重太大,以致對局部產生較大影響,也就是過擬合的結果。在L1正則化中可以對|w|求累加和,但是只直接計算絕對值求累加和的話,例如上述例子中θ1和θ2的結果仍然相同,都等於1,並沒有作出區分。這時候L2正則化就登場了,它的懲罰力度更大,對權重參數求平方和,目的就是讓大的更大,相對懲罰也更多。θ1的L2懲罰為1,θ2的L2懲罰只有0.25,表明θ1帶來的損失更大,在模型效果一致的前提下,當然選擇整體效果更優的θ2組模型。

  細心的讀者可能還會發現,在懲罰項的前面還有一個α系數,它表示正則化懲罰的力度。以一種極端情況舉例說明:如果α值比較大,意味着要非常嚴格地對待權重參數,此時正則化懲罰的結果會對整體目標函數產生較大影響。如果α值較小,意味着懲罰的力度較小,不會對結果產生太大影響。

  最終結果的定論是由測試集決定的,訓練集上的效果僅供參考,因為過擬合現象十分常見。

 

6.3邏輯回歸模型

   歷盡千辛萬苦,現在終於到建模的時候了,這里需要把上面考慮的所有內容都結合在一起,再用工具包建立一個基礎模型就非常簡單,難點在於怎樣得到最優的結果,其中每一環節都會對結果產生不同的影響。

6.3.1參數對結果的影響

   在邏輯回歸算法中,涉及的參數比較少,這里僅對正則化懲罰力度進行調參實驗,為了對比分析交叉驗證的效果,對不同驗證集分別進行建模與評估分析,代碼如下:

 

 1 #Recall = TP/(TP+FN)
 2 from sklearn.linear_model import LogisticRegression
 3 # from sklearn.cross_validation import KFold, cross_val_score
 4 from sklearn.metrics import confusion_matrix,recall_score,classification_report 
 5 from sklearn.model_selection import cross_val_predict, KFold, cross_val_score
 6 
 7 #print(help(KFold))
 8 
 9 def printing_Kfold_scores(x_train_data,y_train_data):
10     #fold = KFold(len(y_train_data),5,shuffle=False) 
11     fold = KFold(5,shuffle=False) 
12 
13     # 定義不同力度的正則化懲罰力度
14     c_param_range = [0.01,0.1,1,10,100]
15     # 展示結果用的表格
16     results_table = pd.DataFrame(index = range(len(c_param_range),2), columns = ['C_parameter','Mean recall score'])
17     results_table['C_parameter'] = c_param_range
18 
19     # k-fold 表示K折的交叉驗證,這里會得到兩個索引集合: 訓練集 = indices[0], 驗證集 = indices[1]
20     j = 0
21     #循環遍歷不同的參數
22     for c_param in c_param_range:
23         print('-------------------------------------------')
24         print('正則化懲罰力度: ', c_param)
25         print('-------------------------------------------')
26         print('')
27 
28         recall_accs = []
29         
30         #一步步分解來執行交叉驗證
31         #for iteration, indices in enumerate(fold,start=1):
32         for iteration, indices in enumerate(fold.split(x_train_data)):
33 
34             # 指定算法模型,並且給定參數
35             #lr = LogisticRegression(C = c_param, penalty = 'l1')
36             lr = LogisticRegression(C = c_param, penalty = 'l1',solver='liblinear')
37 
38             # 訓練模型,注意索引不要給錯了,訓練的時候一定傳入的是訓練集,所以X和Y的索引都是0
39             lr.fit(x_train_data.iloc[indices[0],:],y_train_data.iloc[indices[0],:].values.ravel())
40    
41             # 建立好模型后,預測模型結果,這里用的就是驗證集,索引為1
42             y_pred_undersample = lr.predict(x_train_data.iloc[indices[1],:].values)
43 
44             # 有了預測結果之后就可以來進行評估了,這里recall_score需要傳入預測值和真實值。
45             recall_acc = recall_score(y_train_data.iloc[indices[1],:].values,y_pred_undersample)
46             # 一會還要算平均,所以把每一步的結果都先保存起來。
47             recall_accs.append(recall_acc)
48             print('Iteration ', iteration,': 召回率 = ', recall_acc)
49 
50         
51         # 當執行完所有的交叉驗證后,計算平均結果
52         results_table.loc[j,'Mean recall score'] = np.mean(recall_accs)
53         j += 1
54         print('')
55         print('平均召回率 ', np.mean(recall_accs))
56         print('')
57         
58     #找到最好的參數,哪一個Recall高,自然就是最好的了。
59     best_c = results_table.loc[results_table['Mean recall score'].astype('float32').idxmax()]['C_parameter']
60     
61     # 打印最好的結果
62     print('*********************************************************************************')
63     print('效果最好的模型所選參數 = ', best_c)
64     print('*********************************************************************************')
65     
66     return best_c
67 
68 #交叉驗證與不同參數結果
69 best_c = printing_Kfold_scores(X_train_undersample,y_train_undersample)

 

      先來喘口氣,迪哥的這段代碼稍微有點年代了,所以sklearn的版本是個問題,邀月引用的是最新的0.22版本。

 1 >>pip show Scikit-learn
 2 Name: scikit-learn
 3 Version: 0.22
 4 Summary: A set of python modules for machine learning and data mining
 5 Home-page: http://scikit-learn.org
 6 Author: None
 7 Author-email: None
 8 License: new BSD
 9 Location: d:\tools\python37\lib\site-packages
10 Requires: joblib, numpy, scipy
11 Required-by: sklearn, imbalanced-learn

  所以報了4個錯:

Tips:ModuleNotFoundError: No module named 'sklearn.cross_validation'
原因:
新版已棄用sklearn.cross_validation,
# from sklearn.cross_validation import KFold, cross_val_score修改為
from sklearn.model_selection import KFold,cross_val_score


Tips:TypeError: __init__() got multiple values for argument 'shuffle'
原因:sklearn版本升級,引用路徑修改后,參數也有變化。
#fold = KFold(len(y_train_data),5,shuffle=False) 
改為:
fold = KFold(5,shuffle=False) 

Tips:'KFold' object is not iterable
原因:上述修改引發的聯動問題。
#for iteration, indices in enumerate(fold,start=1):
改為:
for iteration, indices in enumerate(fold.split(x_train_data)): 
    
Tips:
ValueError: Solver lbfgs supports only 'l2' or 'none' penalties, got l1 penalty.

參看:https://blog.csdn.net/qq_22592457/article/details/103504796

原因:
solver這個參數上,這個參數定義的是分類器,‘newton-cg’,‘sag’和‘lbfgs’等solvers僅支持‘L2’regularization,‘liblinear’ solver同時支持‘L1’、‘L2’regularization,
若dual=Ture,則僅支持L2 penalty。 決定懲罰項選擇的有2個參數:dual和solver,如果要選L1范數,dual必須是False,solver必須是liblinear 把上面代碼改成: lr = LogisticRegression(C = c_param, penalty = 'l1',solver='liblinear')

  上述代碼中,KFold用於選擇交叉驗證的折數,這里選擇5折,即把訓練集平均分成5份。C_param是正則化懲罰的力度,也就是正則化懲罰公式中的a。為了觀察不同懲罰力度對結果的影響,在建模的時候,嵌套兩層for循環,首先選擇不同的懲罰力度參數,然后對於每一個參數都進行5折的交叉驗證,最后得到其驗證集的召回率結果。在sklearn工具包中,所有算法的建模調用方法都是類似的,首先選擇需要的算法模型,然后.fit()傳入實際數據進行迭代,最后用.predict()進行預測。

  上述代碼可以生成如下的輸出。先來單獨看正則化懲罰的力度C為0.01時,通過交叉驗證分別得到5次實驗結果,可以發現,即便在相同參數的情況下,交叉驗證結果的差異還是很大,其值在0.93~1.0之間浮動,但是千萬別小看這幾個百分點,建模都是圍繞着一步步小的提升逐步優化的,所以交叉驗證非常有必要。

-------------------------------------------
正則化懲罰力度:  0.01
-------------------------------------------

Iteration  0 : 召回率 =  0.958904109589041
Iteration  1 : 召回率 =  0.9452054794520548
Iteration  2 : 召回率 =  1.0
Iteration  3 : 召回率 =  0.972972972972973
Iteration  4 : 召回率 =  0.9696969696969697

平均召回率  0.9693559063422077

-------------------------------------------
正則化懲罰力度:  0.1
-------------------------------------------

Iteration  0 : 召回率 =  0.8493150684931506
Iteration  1 : 召回率 =  0.863013698630137
Iteration  2 : 召回率 =  0.9322033898305084
Iteration  3 : 召回率 =  0.9459459459459459
Iteration  4 : 召回率 =  0.8939393939393939

平均召回率  0.8968834993678272

-------------------------------------------
正則化懲罰力度:  1
-------------------------------------------

Iteration  0 : 召回率 =  0.8493150684931506
Iteration  1 : 召回率 =  0.8904109589041096
Iteration  2 : 召回率 =  0.9491525423728814
Iteration  3 : 召回率 =  0.9459459459459459
Iteration  4 : 召回率 =  0.9090909090909091

平均召回率  0.9087830849613994

-------------------------------------------
正則化懲罰力度:  10
-------------------------------------------

Iteration  0 : 召回率 =  0.863013698630137
Iteration  1 : 召回率 =  0.8904109589041096
Iteration  2 : 召回率 =  0.9661016949152542
Iteration  3 : 召回率 =  0.9594594594594594
Iteration  4 : 召回率 =  0.9090909090909091

平均召回率  0.9176153441999739

-------------------------------------------
正則化懲罰力度:  100
-------------------------------------------

Iteration  0 : 召回率 =  0.863013698630137
Iteration  1 : 召回率 =  0.8904109589041096
Iteration  2 : 召回率 =  0.9661016949152542
Iteration  3 : 召回率 =  0.9459459459459459
Iteration  4 : 召回率 =  0.9090909090909091

平均召回率  0.9149126414972711

*********************************************************************************
效果最好的模型所選參數 =  0.01
*********************************************************************************

 

  下采樣數據集邏輯回歸評估分析

  在sklearn工具包中,C參數的意義正好是倒過來的,例如C=0.01表示正則化力度比較大,而C=100則表示力度比較小。看起來有點像陷阱,但既然工具包這樣定義了,也只好按照其要求做,所以一定要參考其API文檔(見圖6-14)。

   https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html?highlight=logisticregression#sklearn.linear_model.LogisticRegression

Cfloat, default=1.0

    Inverse of regularization strength; must be a positive float. Like in support vector machines, smaller values specify stronger regularization.

  該頁面同時也可看到剛才那個報錯的原因:

solver{‘newton-cg’, ‘lbfgs’, ‘liblinear’, ‘sag’, ‘saga’}, default=’lbfgs’

    Algorithm to use in the optimization problem.

        For small datasets, ‘liblinear’ is a good choice, whereas ‘sag’ and ‘saga’ are faster for large ones.

        For multiclass problems, only ‘newton-cg’, ‘sag’, ‘saga’ and ‘lbfgs’ handle multinomial loss; 
        ‘liblinear’ is limited to one-versus-rest schemes. ‘newton-cg’, ‘lbfgs’, ‘sag’ and ‘saga’ handle L2 or no penalty ‘liblinear’ and ‘saga’ also handle L1 penalty ‘saga’ also supports ‘elasticnet’ penalty ‘liblinear’ does not support setting penalty='none' Note that ‘sag’ and ‘saga’ fast convergence is only guaranteed on features with approximately the same scale.
     You can preprocess the data with a scaler from sklearn.preprocessing. New in version 0.17: Stochastic Average Gradient descent solver. New in version 0.19: SAGA solver. Changed in version 0.22: The default solver changed from ‘liblinear’ to ‘lbfgs’ in 0.22.

  再來對比分析不同參數得到的結果,直接觀察交叉驗證最后的平均召回率值就可以,不同參數的情況下,得到的結果各不相同,差異還是存在的,所以在建模的時候調參必不可少,可能大家都覺得應該按照經驗值去做,但更多的時候,經驗值只能提供一個大致的方向,具體的探索還是通過大量的實驗進行分析。

  現在已經完成建模和基本的調參任務,只看這個90%左右的結果,感覺還不錯,但是,如果想知道模型的具體表現,需要再深入分析。

6.3.2混淆矩陣

   預測結果明確之后,還可以更直觀地進行展示,這時候混淆矩陣就派上用場了(見圖6-15)。

 邀月工作室

  圖6-15 混淆矩陣

  混淆矩陣中用到的指標值前面已經解釋過,既然已經訓練好模型,就可以展示其結果,這里用到Matplotlib工具包,大家可以把下面的代碼當成一個混淆矩陣模板,用的時候,只需傳入自己的數據即可:

 1 def plot_confusion_matrix(cm, classes,
 2                           title='Confusion matrix',
 3                           cmap=plt.cm.Blues):
 4     """
 5     繪制混淆矩陣
 6     """
 7     plt.imshow(cm, interpolation='nearest', cmap=cmap)
 8     plt.title(title)
 9     plt.colorbar()
10     tick_marks = np.arange(len(classes))
11     plt.xticks(tick_marks, classes, rotation=0)
12     plt.yticks(tick_marks, classes)
13 
14     thresh = cm.max() / 2.
15     for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
16         plt.text(j, i, cm[i, j],
17                  horizontalalignment="center",
18                  color="white" if cm[i, j] > thresh else "black")
19 
20     plt.tight_layout()
21     plt.ylabel('True label')
22     plt.xlabel('Predicted label')

  定義好混淆矩陣的畫法之后,需要傳入實際預測結果,調用之前的邏輯回歸模型,得到測試結果,再把數據的真實標簽值傳進去即可:

 1 import itertools
 2 # lr = LogisticRegression(C = best_c, penalty = 'l1')
 3 lr = LogisticRegression(C = best_c, penalty = 'l1', solver='liblinear')
 4 lr.fit(X_train_undersample,y_train_undersample.values.ravel())
 5 y_pred_undersample = lr.predict(X_test_undersample.values)
 6 
 7 # 計算所需值
 8 cnf_matrix = confusion_matrix(y_test_undersample,y_pred_undersample)
 9 np.set_printoptions(precision=2)
10 
11 print("召回率: ", cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1]))
12 
13 # 繪制
14 class_names = [0,1]
15 plt.figure()
16 plot_confusion_matrix(cnf_matrix
17                       , classes=class_names
18                       , title='Confusion matrix')
19 plt.show()

 

 邀月工作室

  在這份數據集中,目標任務是二分類,所以只有0和1,主對角線上的值就是預測值和真實值一致的情況,深色區域代表模型預測正確(真實值和預測值一致),其余位置代表預測錯誤。數值10代表有10個樣本數據本來是異常的,模型卻將它預測成為正常,相當於“漏檢”。數值30代表有30個樣本數據本來是正常的,卻把它當成異常的識別出來,相當於“誤殺”。

  最終得到的召回率值約為0.9387,看起來是一個還不錯的指標,但是還有沒有問題呢?用下采樣的數據集進行建模,並且測試集也是下采樣的測試集,在這份測試集中,異常樣本和正常樣本的比例基本均衡,因為已經對數據集進行過處理。但是實際的數據集並不是這樣的,相當於在測試時用理想情況來代替真實情況,這樣的檢測效果可能會偏高,所以,值得注意的是,在測試的時候,需要使用原始數據的測試集,才能最具代表性,只需要改變傳入的測試數據即可,代碼如下:

 1 # lr = LogisticRegression(C = best_c, penalty = 'l1')
 2 lr = LogisticRegression(C = best_c, penalty = 'l1', solver='liblinear')
 3 lr.fit(X_train_undersample,y_train_undersample.values.ravel())
 4 y_pred = lr.predict(X_test.values)
 5 
 6 # 計算所需值
 7 cnf_matrix = confusion_matrix(y_test,y_pred)
 8 np.set_printoptions(precision=2)
 9 
10 print("召回率: ", cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1]))
11 
12 # 繪制
13 class_names = [0,1]
14 plt.figure()
15 plot_confusion_matrix(cnf_matrix
16                       , classes=class_names
17                       , title='Confusion matrix')
18 plt.show()

 

 邀月工作室

  還記得在切分數據集的時候,我們做了兩手准備嗎?不僅對下采樣數據集進行切分,而且對原始數據集也進行了切分。這時候就派上用場了,得到的召回率值為0.9319,雖然有所下降,但是整體來說還是可以的。

  在實際的測試中,不僅需要考慮評估方法,還要注重實際應用情況,再深入混淆矩陣中,看看還有哪些實際問題。上圖中左下角的數值為10,看起來沒有問題,說明有10個漏檢的,只比之前的9個多1個而已。但是,右上角有一個數字格外顯眼——3600,意味着有3600個樣本被誤殺。好像之前用下采樣數據集進行測試的時候沒有注意到這一點,因為只有30個樣本被誤殺。但是,在實際的測試集中卻出現了這樣的事:整個測試集一共只有100多個異常樣本,模型卻誤殺掉3600個,有點誇張了,根據實際業務需求,后續肯定要對檢測出來的異常樣本做一些處理,比如凍結賬號、電話詢問等,如果誤殺掉這么多樣本,實際業務也會出現問題。

  在測試中還需綜合考慮,不僅要看模型具體的指標值(例如召回率、精度等),還需要從實際問題角度評估模型到底可不可取。

  問題已經很嚴峻,模型現在出現了大問題,該如何改進呢?是對模型調整參數,不斷優化算法呢?還是在數據層面做一些處理呢?一般情況下,建議大家先從數據下手,因為對數據做變換要比優化算法模型更容易,得到的效果也更突出。不要忘了之前提出的兩種方案,而且過采樣方案還沒有嘗試,會不會發生一些變化呢?下面就來揭曉答案。

6.3.3分類閾值對結果的影響

  回想一下邏輯回歸算法原理,通過Sigmoid函數將得分值轉換成概率值,那么,怎么得到具體的分類結果呢?默認情況下,模型都是以0.5為界限來划分類別:

 邀月工作室

  可以說0.5是一個經驗值,但是並不是固定不變的,實踐時可以根據自己的標准來指定該閾值大小。如果閾值設置得大一些,相當於要求變得嚴格,只有非常異常的樣本,才能當作異常;如果閾值設置得比較小,相當於寧肯錯殺也不肯放過,只要有一點異常就通通抓起來。

  在sklearn工具包中既可以用.predict()函數得到分類結果,相當於以0.5為默認閾值,也可以用.predict_proba()函數得到其概率值,而不進行類別判斷,代碼如下:

 

 1 # 用之前最好的參數來進行建模
 2 # lr = LogisticRegression(C = 0.01, penalty = 'l1')
 3 lr = LogisticRegression(C = 0.01, penalty = 'l1',solver='liblinear')
 4 
 5 # 訓練模型,還是用下采樣的數據集
 6 lr.fit(X_train_undersample,y_train_undersample.values.ravel())
 7 
 8 # 得到預測結果的概率值
 9 y_pred_undersample_proba = lr.predict_proba(X_test_undersample.values)
10 
11 #指定不同的閾值
12 thresholds = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9]
13 
14 plt.figure(figsize=(10,10))
15 
16 j = 1
17 
18 # 用混淆矩陣來進行展示
19 for i in thresholds:
20     y_test_predictions_high_recall = y_pred_undersample_proba[:,1] > i
21     
22     plt.subplot(3,3,j)
23     j += 1
24     
25     cnf_matrix = confusion_matrix(y_test_undersample,y_test_predictions_high_recall)
26     np.set_printoptions(precision=2)
27 
28     print("給定閾值為:",i,"時測試集召回率: ", cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1]))
29 
30     class_names = [0,1]
31     plot_confusion_matrix(cnf_matrix
32                           , classes=class_names
33                           , title='Threshold >= %s'%i) 
給定閾值為: 0.1 時測試集召回率:  1.0
給定閾值為: 0.2 時測試集召回率:  1.0
給定閾值為: 0.3 時測試集召回率:  1.0
給定閾值為: 0.4 時測試集召回率:  0.9931972789115646
給定閾值為: 0.5 時測試集召回率:  0.9387755102040817
給定閾值為: 0.6 時測試集召回率:  0.8979591836734694
給定閾值為: 0.7 時測試集召回率:  0.8367346938775511
給定閾值為: 0.8 時測試集召回率:  0.7482993197278912
給定閾值為: 0.9 時測試集召回率:  0.5782312925170068

邀月工作室

  代碼中設置0.1~0.9多個閾值,並且確保每一次建模都使用相同的參數,將得到的概率值與給定閾值進行比較來完成分類任務。

  現在觀察一下輸出結果,當閾值比較小的時候,可以發現召回率指標非常高,第一個子圖竟然把所有樣本都當作異常的,但是誤殺率也是很高的,實際意義並不大。隨着閾值的增加,召回率逐漸下降,也就是漏檢的逐步增多,而誤殺的慢慢減少,這是正常現象。當閾值趨於中間范圍時,看起來各有優缺點,當閾值等於0.5時,召回率偏高,但是誤殺的樣本個數有點多。當閾值等於0.6時,召回率有所下降,但是誤殺樣本數量明顯減少。那么,究竟選擇哪一個閾值比較合適呢?這就需要從實際業務的角度出發,看一看實際問題中,到底需要模型更符合哪一個標准。

 

6.4過采樣方案

   在下采樣方案中,雖然得到較高的召回率,但是誤殺的樣本數量實在太多了,下面就來看看用過采樣方案能否解決這個問題。

6.4.1SMOTE數據生成策略

   如何才能讓異常樣本與正常樣本一樣多呢?這里需要對少數樣本進行生成,這可不是復制粘貼,一模一樣的樣本是沒有用的,需要采用一些策略,最常用的就是SMOTE算法(見圖6-16),其流程如下。

第①步:對於少數類中每一個樣本x,以歐式距離為標准,計算它到少數類樣本集中所有樣本的距離,經過排序,得到其近鄰樣本。

第②步:根據樣本不平衡比例設置一個采樣倍率N,對於每一個少數樣本x,從其近鄰開始依次選擇N個樣本。

第③步:對於每一個選出的近鄰樣本,分別與原樣本按照如下的公式構建新的樣本數據。

   邀月工作室

邀月工作室

  圖6-16 SMOTE算法

  總結一下:對於每一個異常樣本,首先找到離其最近的同類樣本,然后在它們之間的距離上,取0~1中的一個隨機小數作為比例,再加到原始數據點上,就得到新的異常樣本。對於SMOTE算法,可以使用imblearn工具包完成這個操作,首先需要安裝該工具包,可以直接在命令行中使用pip install imblearn完成安裝操作。再把SMOTE算法加載進來,只需要將特征數據和標簽傳進去,接下來就得到20W+個異常樣本,完成過采樣方案。

1 import pandas as pd
2 from imblearn.over_sampling import SMOTE
3 from sklearn.metrics import confusion_matrix
4 from sklearn.model_selection import train_test_split

 

6.4.2過采樣應用效果

   過采樣方案的效果究竟怎樣呢?同樣使用邏輯回歸算法來看看:

 1 import pandas as pd
 2 from imblearn.over_sampling import SMOTE
 3 from sklearn.metrics import confusion_matrix
 4 from sklearn.model_selection import train_test_split
 5 
 6 credit_cards=pd.read_csv('creditcard.csv')
 7 
 8 columns=credit_cards.columns
 9 # 在特征中去除掉標簽
10 features_columns=columns.delete(len(columns)-1)
11 
12 features=credit_cards[features_columns]
13 labels=credit_cards['Class']
14 
15 
16 features_train, features_test, labels_train, labels_test = train_test_split(features,
17                                                                             labels,
18                                                                             test_size=0.3,
19                                                                             random_state=0)
20 
21 oversampler=SMOTE(random_state=0)
22 os_features,os_labels=oversampler.fit_sample(features_train,labels_train)
23 
24 len(os_labels[os_labels==1])
25 #199019
26 
27 os_features = pd.DataFrame(os_features)
28 os_labels = pd.DataFrame(os_labels)
29 best_c = printing_Kfold_scores(os_features,os_labels)

 

-------------------------------------------
正則化懲罰力度:  0.01
-------------------------------------------

Iteration  0 : 召回率 =  0.9142857142857143
Iteration  1 : 召回率 =  0.88
Iteration  2 : 召回率 =  0.9716742539200809
Iteration  3 : 召回率 =  0.9622395015513711
Iteration  4 : 召回率 =  0.9619254588164358

平均召回率  0.9380249857147204

-------------------------------------------
正則化懲罰力度:  0.1
-------------------------------------------

Iteration  0 : 召回率 =  0.9142857142857143
Iteration  1 : 召回率 =  0.88
Iteration  2 : 召回率 =  0.9728882144663632
Iteration  3 : 召回率 =  0.9640483877045989
Iteration  4 : 召回率 =  0.9640986345421885

平均召回率  0.939064190199773

-------------------------------------------
正則化懲罰力度:  1
-------------------------------------------

Iteration  0 : 召回率 =  0.9142857142857143
Iteration  1 : 召回率 =  0.88
Iteration  2 : 召回率 =  0.9732928679817906
Iteration  3 : 召回率 =  0.9640735111233937
Iteration  4 : 召回率 =  0.9643121836019446

平均召回率  0.9391928553985686

-------------------------------------------
正則化懲罰力度:  10
-------------------------------------------

Iteration  0 : 召回率 =  0.9142857142857143
Iteration  1 : 召回率 =  0.88
Iteration  2 : 召回率 =  0.9730905412240769
Iteration  3 : 召回率 =  0.9640986345421885
Iteration  4 : 召回率 =  0.9644126772771239

平均召回率  0.9391775134658207

-------------------------------------------
正則化懲罰力度:  100
-------------------------------------------

Iteration  0 : 召回率 =  0.9142857142857143
Iteration  1 : 召回率 =  0.88
Iteration  2 : 召回率 =  0.9730905412240769
Iteration  3 : 召回率 =  0.9643624304395342
Iteration  4 : 召回率 =  0.9642996218925471

平均召回率  0.9392076615683745

*********************************************************************************
效果最好的模型所選參數 =  100.0
*********************************************************************************
View Code

  在訓練集上的效果還不錯,再來看看其測試結果的混淆矩陣:

 1 # lr = LogisticRegression(C = best_c, penalty = 'l1')
 2 lr = LogisticRegression(C = best_c, penalty = 'l1', solver='liblinear')
 3 lr.fit(os_features,os_labels.values.ravel())
 4 y_pred = lr.predict(features_test.values)
 5 
 6 # 計算混淆矩陣
 7 cnf_matrix = confusion_matrix(labels_test,y_pred)
 8 np.set_printoptions(precision=2)
 9 
10 print("召回率: ", cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1]))
11 
12 # 繪制
13 class_names = [0,1]
14 plt.figure()
15 plot_confusion_matrix(cnf_matrix
16                       , classes=class_names
17                       , title='Confusion matrix')
18 plt.show()

邀月工作室

  得到的召回率值與之前的下采樣方案相比有所下降,畢竟在異常樣本中,很多都是假冒的,不能與真實數據相媲美。值得欣慰的是,這回模型的誤殺比例大大下降,原來誤殺比例占到所有測試樣本的10%,現在只占不到1%,實際應用效果有很大提升。

  經過對比可以明顯發現,過采樣的總體效果優於下采樣(還得依據實際應用效果具體分析),因為可利用的數據信息更多,使得模型更符合實際的任務需求。但是,對於不同的任務與數據源來說,並沒有一成不變的答案,任何結果都需要通過實驗證明,所以,當大家遇到問題時,最好的解決方案是通過大量實驗進行分析。

   

6.5用原始數據直接建模

1 best_c = printing_Kfold_scores(X_train,y_train)
-------------------------------------------
正則化懲罰力度:  0.01
-------------------------------------------

Iteration  0 : 召回率 =  0.4925373134328358
Iteration  1 : 召回率 =  0.6027397260273972
Iteration  2 : 召回率 =  0.6833333333333333
Iteration  3 : 召回率 =  0.5692307692307692
Iteration  4 : 召回率 =  0.45

平均召回率  0.5595682284048672

-------------------------------------------
正則化懲罰力度:  0.1
-------------------------------------------

Iteration  0 : 召回率 =  0.5671641791044776
Iteration  1 : 召回率 =  0.6164383561643836
Iteration  2 : 召回率 =  0.6833333333333333
Iteration  3 : 召回率 =  0.5846153846153846
Iteration  4 : 召回率 =  0.525

平均召回率  0.5953102506435158

-------------------------------------------
正則化懲罰力度:  1
-------------------------------------------

Iteration  0 : 召回率 =  0.5522388059701493
Iteration  1 : 召回率 =  0.6164383561643836
Iteration  2 : 召回率 =  0.7166666666666667
Iteration  3 : 召回率 =  0.6153846153846154
Iteration  4 : 召回率 =  0.5625

平均召回率  0.612645688837163

-------------------------------------------
正則化懲罰力度:  10
-------------------------------------------

Iteration  0 : 召回率 =  0.5522388059701493
Iteration  1 : 召回率 =  0.6164383561643836
Iteration  2 : 召回率 =  0.7333333333333333
Iteration  3 : 召回率 =  0.6153846153846154
Iteration  4 : 召回率 =  0.575

平均召回率  0.6184790221704963

-------------------------------------------
正則化懲罰力度:  100
-------------------------------------------

Iteration  0 : 召回率 =  0.5522388059701493
Iteration  1 : 召回率 =  0.6164383561643836
Iteration  2 : 召回率 =  0.7333333333333333
Iteration  3 : 召回率 =  0.6153846153846154
Iteration  4 : 召回率 =  0.575

平均召回率  0.6184790221704963

*********************************************************************************
效果最好的模型所選參數 =  10.0
*********************************************************************************
#原始數據直接建模結果
# lr = LogisticRegression(C = best_c, penalty = 'l1')
lr = LogisticRegression(C = best_c, penalty = 'l1', solver='liblinear')
lr.fit(X_train,y_train.values.ravel())
y_pred_undersample = lr.predict(X_test.values)

# Compute confusion matrix
cnf_matrix = confusion_matrix(y_test,y_pred_undersample)
np.set_printoptions(precision=2)

print("Recall metric in the testing dataset: ", cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1]))

# Plot non-normalized confusion matrix
class_names = [0,1]
plt.figure()
plot_confusion_matrix(cnf_matrix
                      , classes=class_names
                      , title='Confusion matrix')
plt.show()

邀月工作室

 

項目過程小結:

  • 1.在做任務之前,一定要檢查數據,看看數據有什么問題。在此項目中,通過對數據進行觀察,發現其中有樣本不均衡的問題,針對這些問題,再來選擇解決方案。
  • 2.針對問題提出兩種方法:下采樣和過采樣。通過兩條路線進行對比實驗,任何實際問題出現后,通常都是先得到一個基礎模型,然后對各種方法進行對比,找到最合適的,所以在任務開始之前,一定要多動腦筋,做多手准備,得到的結果才有可選擇的余地。
  • 3.在建模之前,需要對數據進行各種預處理操作,例如數據標准化、缺失值填充等,這些都是必要的,由於數據本身已經給定特征,此處還沒有涉及特征工程這個概念,后續實戰中會逐步引入,其實數據預處理工作是整個任務中最重、最苦的一個工作階段,數據處理得好壞對結果的影響最大。
  • 4.先選好評估方法,再進行建模實驗。建模的目的就是為了得到結果,但是不可能一次就得到最好的結果,肯定要嘗試很多次,所以一定要有一個合適的評估方法,可以選擇通用的,例如召回率、准確率等,也可以根據實際問題自己指定合適的評估指標。
  • 5.選擇合適的算法,本例中選擇邏輯回歸算法,詳細分析其中的細節,之后還會講解其他算法,並不一定非要用邏輯回歸完成這個任務,其他算法效果可能會更好。但是有一點希望大家能夠理解,就是在機器學習中,並不是越復雜的算法越實用,反而越簡單的算法應用越廣泛。邏輯回歸就是其中一個典型的代表,簡單實用,所以任何分類問題都可以把邏輯回歸當作一個待比較的基礎模型。
  • 6.模型的調參也是很重要的,通過實驗發現,不同的參數可能會對結果產生較大的影響,這一步也是必須的,后續實戰中還會再來強調調參的細節。使用工具包時,建議先查閱其API文檔,知道每一個參數的意義,再來進行實驗。
  • 7.得到的預測結果一定要和實際任務結合在一起,有時候雖然得到的評估指標還不錯,但是在實際應用中卻出現問題,所以測試環節也是必不可少的。

 

 

第6章完。

python數據分析個人學習讀書筆記-目錄索引

 

該書資源下載,請至異步社區:https://www.epubit.com

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM