小姐姐教我的 sklearn 逻辑回归
sklearn 邏輯回歸
Alex一晚上都沒睡好覺,被IEEE-CIS Fraud Detection折磨的死去活來,不管怎么調參,用什么樣的策略,評分就是上不去,這可不行,什么時候認過輸,生死看淡,不服就干!結果:
第二天,Alex打算去工作室問問Bachelor,這家伙肯定還藏了不少東西沒說,結果Bachelor不知道是因為心虛還是咋的,竟然沒來,工作室只有一個膚白貌美大長腿的實習生MM在,聽Bachelor說這個是實習生是個高手,也是MIT過來的,而且人家主修的就是人工智能這方面,Alex決定厚著臉皮去問問。
Alex:“Hi Coco, Do you know the credit card fraud detection we received recently?”
Coco:“你說啥?”
Alex:“我靠,你會說中文。”
Coco:“相較于你的英文,我還是聽懂了你的中文。”
ALex:“。。。。。。”
ALex:“我說你知道咱們工作室最近接的那個信用卡欺詐檢測的項目么?”
Coco:“知道啊,那個項目就是我接的,只不過現在是Bachelor負責,我跟進。”
Alex:“那太好了,Bachelor昨天給我講了講,但是我回去自己做的時候準確率出奇的低。”
Coco:“你跟我說說Bachelor講了啥,我幫你看看吧。”
于是Alex就把昨天的邏輯回歸又說了一遍…
Coco:“整體看來沒問題,只不過Bachelor只講了邏輯回歸的原理,應用在這個項目上還需要一些處理。”
于是,Alex一邊聽著Coco的講解,一邊擦口水…
Coco倒是沒多想,專心的給Alex講解:
這樣吧,我就給你捋一下工作室目前是怎么做的。
import numpy as np import pandas as pd import matplotlib.pyplot as plttrain_identity = pd.read_csv('/kaggle/input/ieee-fraud-detection/train_identity.csv') train_transaction = pd.read_csv('/kaggle/input/ieee-fraud-detection/train_transaction.csv') data = pd.merge(train_transaction, train_identity, on="TransactionID", how="left") data.drop("TransactionID", axis=1, inplace=True) # TransactionID說實話沒啥用 data.drop("TransactionDT", axis=1, inplace=True) # TransactionDT類似于時間戳,也沒啥用 del train_identity, train_transaction上來還是先把兩張表的數據讀進內存里,然后通過TransactionID連接起來制作一個train表,之后既可以把原來的兩張表刪了,不然會占用內存。
pd.set_option('display.max_columns',None) # 設置pandas顯示列不限制數量 pd.set_option('display.max_rows',None) # 設置pandas顯示行不限制數量 data.head()數據預處理
首先還是去掉缺失值超過50%的特征:
na_count = data.isnull().sum().sort_values(ascending=False) na_rate = na_count / len(data) na_data = pd.concat([na_count,na_rate],axis=1,keys=['count','ratio']) data.drop(na_data[na_data['ratio'] > 0.3].index, axis=1, inplace=True)將所有的離散型數據對應為數值型數據,缺失值用均值填充:
for col in data.columns:if data[col].dtypes == "object":data[col], uniques = pd.factorize(data[col])data[col].fillna(data[col].mean(), inplace=True) data.head()其實在這個項目里,重要的是數據預處理而不是邏輯回歸的算法。
拿到數據之后,先簡單看一下,isFraud表示有沒有沒詐騙,也就是我們的y值,0表示沒有被詐騙,1表示被詐騙了,但是在日常生活中,雖然詐騙比較多,但還是正常樣本占大多數,所以我們先看一下正負樣本的統計。
count_isFraud = pd.value_counts(data["isFraud"], sort=True) count_isFraud.plot(kind="bar") # 條形圖 plt.title("Fraud statistics") plt.xlabel("isFraud") plt.ylabel("Frequency") X = data.iloc[:, data.columns != "isFraud"] Y = data.iloc[:, data.columns == "isFraud"]通過條形圖我們可以發現,正負樣本量是非常不均衡的,這會對我們的模型計算產生很大的影響,我估計你昨天測試集的結果應該全都是0吧。
對于這種樣本不均衡的數據最常使用的有兩種解決方案:
1.過采樣:通過1樣本數據制造一些數據,讓兩種樣本數據保持平衡;
2.下采樣:在0樣本數據中隨機抽取跟1樣本數據一樣多的數據,讓兩種樣本數據保持均衡。
# 過采樣——通過第三方庫可以很方便的實現 from imblearn.over_sampling import SMOTE over_sample = SMOTE() X_over_sample_data, Y_over_sample_data = over_sample.fit_sample(X.values, Y.values) X_over_sample_data = pd.DataFrame(X_over_sample_data) Y_over_sample_data = pd.DataFrame(Y_over_sample_data) # 看一下過采樣數據集的長度 print("Total number of normal =", len(Y_over_sample_data[Y_over_sample_data == 0])) print("Total number of fraud =", len(Y_over_sample_data[Y_over_sample_data == 1])) # 下采樣 number_of_fraud = len(data[data.isFraud == 1]) # 統計被詐騙數據量 fraud_indices = np.array(data[data.isFraud == 1].index) # 被詐騙數據索引 normal_indices = np.array(data[data.isFraud == 0].index) # 正常數據索引# 在正常數據中隨機選擇與被詐騙數據量相等的正常數據的索引 random_normal_indices = np.array(np.random.choice(normal_indices, number_of_fraud, replace=False))# 將所有被詐騙的數據索引和隨機選擇的等量正常數據索引合并 under_sample_indices = np.concatenate([fraud_indices, random_normal_indices]) # 從原始數據中取出下采樣數據集 under_sample_data = data.iloc[under_sample_indices, :]X_under_sample = under_sample_data.iloc[:, under_sample_data.columns != "isFraud"] Y_under_sample = under_sample_data.iloc[:, under_sample_data.columns == "isFraud"] # 看一下下采樣數據集的長度 print("Total number of under_sample_data =", len(under_sample_data)) print("Total number of normal =", len(under_sample_data[data.isFraud == 0])) print("Total number of fraud =", len(under_sample_data[data.isFraud == 1]))標準化
在我們做機器學習模型的時候,要保證特征之間的分布數據是差不多,也就是保證初始情況下每一列特征的重要程度是相似的,比如說card1這一列,它的數據相比如其它的數據都非常大,在訓練模型的時候機器可能認為card1這行數據非常重要,但實際上并不能確定。
因此,我們需要對data進行標準化的處理,通過sklearn的preprocessing模塊可以快速的幫助我們隊數據做標準化:
from sklearn.preprocessing import StandardScalercols = X_under_sample.columnsfor col in cols:X[col] = StandardScaler().fit_transform(X[col].values.reshape(-1, 1))for col in cols:X_under_sample[col] = StandardScaler().fit_transform(X_under_sample[col].values.reshape(-1, 1))for col in cols:X_over_sample_data[col] = StandardScaler().fit_transform(X_over_sample_data[col].values.reshape(-1, 1))交叉驗證
為了在不使用測試集的情況下驗證模型的效果,通常在訓練一個機器學習的模型之前,會對train數據集進行切分:
我們把訓練集分成5份,然后進行五輪訓練:
第一輪:第1份數據留作驗證,2、3、4、5份數據用作訓練模型;第二輪:第2份數據留作驗證,1、3、4、5份數據用作訓練模型;第三輪:第3份數據留作驗證,1、2、4、5份數據用作訓練模型;第四輪:第4份數據留作驗證,1、2、3、5份數據用作訓練模型;第五輪:第5份數據留作驗證,1、2、3、4份數據用作訓練模型;最后針對每一輪訓練的結果取一個平均的效果,會讓我們的模型變得更加優秀,當然這個效果不用我們來實現,sklearn已經幫我們實現好了:
from sklearn.model_selection import train_test_split簡單來說,train_test_split就是輸入訓練集的X和Y,返回切分后的訓練集X、Y,驗證集X、Y。
# 對原始的全部數據進行切分 X_train, X_test, Y_train, y_test = train_test_split(X, Y, test_size=0.2)# 對下采樣數據集進行切分 X_under_sample_train, X_under_sample_test, Y_under_sample_train, Y_under_sample_test = train_test_split(X_under_sample, Y_under_sample, test_size=0.2)# 對上采樣數據集進行切分 X_over_sample_train, X_over_sample_test, Y_over_sample_train, Y_over_sample_test = train_test_split(X_over_sample_data, Y_over_sample_data, test_size=0.2)模型評估
一個模型的好壞不能只看它的準確率,舉一個簡單的例子:
假設1000個人中,有900個人是正常的,有100個人被詐騙。從這1000個人中抽出100人來測試某個模型,這100個人中有90個人是正常的,有10個人是被詐騙的。有一個非常粗暴的模型,將輸入的樣本都判斷為正常,那么這個模型在處理這100個數據的時候,會預測準那90個正常的,但剩下10個預測錯誤。模型的準確率為90%,但是這顯然不是一個好模型。所以,在樣本不均衡的情況下不能夠使用準確率作為模型評估的標準,而要使用recall,也就是召回率。
計算recall需要先看一個表格:
| 檢索到 | True Positive(TP),正類判斷為正類 | False Positive(FP),負類判斷為正類 |
| 未檢索到 | False Negative(FN),正類判斷為負類 | True Negative(TN),負類判斷為負類 |
recall=TPTP+FNrecall = \frac{TP}{TP+FN}recall=TP+FNTP?
這看起來不是很好理解,我們再來舉一個例子:
還是1000個人中,有900個人是正常的,有100個人被詐騙。又有一個不靠譜的模型,將輸入的樣本50%判斷為正常,50%判斷為異常,我們的目的是找出所有被詐騙的。經過這個模型的計算,得到500個模型認為的被詐騙人,但是500個人中只有50個人是被詐騙的,剩下450個都是正常的。檢索到的500個人中:50個異常數據被判斷為異常,TP=50;450個正常數據被判斷為異常,FP=450。未被檢索到的500個人中:50個異常數據被判斷為正常,FN=50;450個正常數據被判斷為正常,TN=450。recall = 50 / (50 + 50) = 0.5正則化懲罰項
我們訓練模型的目標其實就是求出參數θ,假設通過計算得到θ1和θ2兩個模型,盡管參數值截然不同,但在預測中有可能會得到相同的結果。
那么對于這兩個模型,我們到底要選擇哪一個呢?
在回答這個問題之前,我們要了解一個知識點,過擬合。
過擬合問題是機器學習中很讓人頭疼的一件事情,舉個例子:
暫時不用管這是什么算法,我們的目標是對紅綠色的點進行分類,可以看到對于大部分數據區分的還是比較完美的,但是綠色范圍在左下方突出了一個角,為了去擬合在紅色堆里那個按照正常的邏輯應該判定為紅色點的綠色點,但是,有可能那個離群的綠色點是個錯誤數據。
這就是過擬合,只能說我們的模型對于訓練集來說太過完美,這可并不是一件好事,我們的目的是想讓模型能夠匹配所有的數據,不僅僅局限于訓練集。
過擬合通常發生在訓練數據不夠多或者訓練過度(overtrainging)的情況下,而正則化方法就是為了解決過擬合的問題而誕生的,通過在損失函數中引入額外的計算項,可以有效的防止過擬合,提高模型的泛化能力。
目標函數=損失函數+正則化懲罰項目標函數 = 損失函數 + 正則化懲罰項目標函數=損失函數+正則化懲罰項
目前有兩種正則化懲罰項:
L1參數正則化:ω(θ)=∣∣w∣∣=∑i∣wi∣\omega(\theta)=||w||=\sum_i|w_i|ω(θ)=∣∣w∣∣=i∑?∣wi?∣
L2參數正則化:ω(θ)=12∣w∣2\omega(\theta)=\frac{1}{2}|w|^2ω(θ)=21?∣w∣2
邏輯回歸模型
我們已經學習過邏輯回歸算法的推導過程,能夠將計算過程由代碼實現,帶如果每次使用邏輯回歸都要再寫一遍代碼顯然是非常繁瑣的,sklearn包幫我們實現好了一個很優秀的邏輯回歸訓練器,只需要輸入相應的參數,就可以造出一個訓練器。
import time from sklearn.metrics import recall_score from sklearn.model_selection import KFold from sklearn.linear_model import LogisticRegression def kfold_scores(x_train_data, y_train_data):start_time = time.time()fold = KFold(3, shuffle=True) # 3折交叉驗證c_param_range = [10, 100, 1000] # 懲罰力度,正則化懲罰項的系數 # 做可視化展示results_table = pd.DataFrame(index=range(len(c_param_range), 2), columns=["C_parameter", "Mean recall scores"])results_table["C_parameter"] = c_param_range# 不確定哪一個正則化懲罰項的系數更好,因此采用循環確認index = 0for c_param in c_param_range:print('--------------------------------------------------------------------------------')print("If C parameter =", c_param, end="\n\n")# 做交叉驗證recall_accs = []lr = LogisticRegression(C=c_param, penalty='l1', solver='liblinear', max_iter=10000)for iteration, indices in enumerate(fold.split(x_train_data)):# 擬合訓練數據lr.fit(x_train_data.iloc[indices[0], :], y_train_data.iloc[indices[0], :].values.ravel())# 使用驗證集得出預測數據y_predicted_undersample = lr.predict(x_train_data.iloc[indices[1], :])# 計算recallrecall_acc = recall_score(y_train_data.iloc[indices[1], :], y_predicted_undersample)recall_accs.append(recall_acc)print('\tIteration ', iteration, ': recall score = ', recall_acc)index += 1# 計算recall的平均值results_table.loc[index, "Mean recall scores"] = np.mean(recall_accs)print('Mean recall score = ', results_table.loc[index, "Mean recall scores"], end="\n\n")print('--------------------------------------------------------------------------------')best_c_param = results_table.loc[results_table['Mean recall scores'].astype(float).idxmax()]['C_parameter']print('Best C parameter = ', best_c_param, "\t duration: ", time.time() - start_time)return lr, best_c_param # 對原始全部數據進行測試 # lr1, param1 = kfold_scores(X_train, Y_train) del X_train del Y_train # 對下采樣數據進行測試 lr2, param2 = kfold_scores(X_under_sample_train, Y_under_sample_train) del X_under_sample_train del Y_under_sample_train # 對上采樣數據進行測試 # lr3, param3 = kfold_scores(X_over_sample_train, Y_over_sample_train) del X_over_sample_train del Y_over_sample_train test_identity = pd.read_csv('/kaggle/input/ieee-fraud-detection/test_identity.csv') test_transaction = pd.read_csv('/kaggle/input/ieee-fraud-detection/test_transaction.csv') data = pd.merge(test_transaction, test_identity, on="TransactionID", how="left") test_ID = data[["TransactionID"]] data.drop("TransactionID", axis=1, inplace=True) # TransactionID說實話沒啥用 data.drop("TransactionDT", axis=1, inplace=True) # TransactionDT類似于時間戳,也沒啥用for col in data.columns:if col.startswith("id"):newcol = col.replace("-", "_")data.rename(columns={col: newcol},inplace=True)del test_identity, test_transactiondata.drop(na_data[na_data['ratio'] > 0.3].index, axis=1, inplace=True) for col in data.columns:if data[col].dtypes == "object":data[col], uniques = pd.factorize(data[col])data[col].fillna(data[col].mean(), inplace=True) for col in cols:data[col] = StandardScaler().fit_transform(data[col].values.reshape(-1, 1))test_predict = lr2.predict(data.values) submission = pd.concat([test_ID, pd.Series(test_predict)], axis=1, keys=["TransactionID", "isFraud"]) submission.to_csv("submission1.csv", index=False)好了,這就是通過sklearn來做這個邏輯回歸的項目,咱們可以吧結果提交評測網站IEEE-CIS Fraud Detection看看效果怎么樣:
Coco:“你看,這么做是不是效果比你之前那個好一點了。”
Alex:“我靠,真大。。。。”
Coco:“什么真大?”
Alex:“額,分數啊。”
Coco:“行,大概就是這個套路,剩下的是就是細節的問題了,你回去也可以試試過采樣和正常數據能達到什么效果。”
Alex:“不用回去了,我就在這試。”
總結
以上是生活随笔為你收集整理的小姐姐教我的 sklearn 逻辑回归的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 10.1 HTML介绍与开发环境的搭建
- 下一篇: 11.1 JavaScript介绍