信用卡欺詐檢測

項目目的

通過信用卡的歷史交易數(shù)據(jù)绘趋,利用機(jī)器學(xué)習(xí)算法構(gòu)建信用卡反欺詐預(yù)測模型,提前發(fā)現(xiàn)客戶信用卡被盜刷的事件颗管。

項目介紹

該數(shù)據(jù)集包含由歐洲持卡人于2013年9月使用信用卡進(jìn)行交易的數(shù)據(jù)陷遮。此數(shù)據(jù)集顯示兩天內(nèi)發(fā)生的交易記錄,其中284,807筆交易中有492筆被盜刷垦江。數(shù)據(jù)集非常不平衡帽馋,積極的類(被盜刷)占所有交易的0.172%。因為其中涉及到了隱私的內(nèi)容比吭,數(shù)據(jù)集已經(jīng)進(jìn)行了PCA的處理绽族,將特征數(shù)據(jù)提取出來了,特征V1衩藤,V2吧慢,... V28是使用PCA獲得的主要特征變量。

  • 模型選擇:該項目要解決的問題是預(yù)測持卡人是否會發(fā)生信用卡被盜刷赏表。信用卡持卡人是否會發(fā)生被盜刷只有兩種可能检诗,所以這是一個二分類問題,意味著可以通過二分類相關(guān)的算法來找到具體的解決辦法瓢剿,本項目選用的算法模型是邏輯回歸模型(Logistic Regression)逢慌。

  • 數(shù)據(jù)處理

    1. 數(shù)據(jù)是結(jié)構(gòu)化數(shù)據(jù) ,不需要做特征抽象间狂。特征V1至V28是經(jīng)過PCA處理攻泼,而特征Time和Amount的數(shù)據(jù)規(guī)格與其他特征差別較大,需要對其做特征縮放前标,將特征縮放至同一個規(guī)格坠韩。在數(shù)據(jù)質(zhì)量方面 距潘,沒有出現(xiàn)亂碼或空字符的數(shù)據(jù)炼列,可以確定字段Class為目標(biāo)列,其他列為特征列音比。
    2. 該數(shù)據(jù)集非常不平衡俭尖,被盜刷占所有交易的0.172%,需要進(jìn)行平衡樣本處理。

分析思路:

  • 一:數(shù)據(jù)預(yù)處理
    1:導(dǎo)入數(shù)據(jù)
    2:標(biāo)準(zhǔn)化處理
    3:樣本不均衡處理
  • 二:模型訓(xùn)練
    1:欠采樣平衡樣本
    1.1 數(shù)據(jù)拆分:訓(xùn)練集稽犁、驗證集與測試集
    1.2 交叉驗證
    1.3 模型評估方法
    1.4 正則化懲罰項
    1.5 構(gòu)建模型
    1.5.1K折交叉驗證-尋找較優(yōu)參數(shù)*
    1.5.2使用下采樣數(shù)據(jù)進(jìn)行訓(xùn)練焰望,下采樣數(shù)據(jù)進(jìn)行測試*
    1.5.3不同閾值對結(jié)果的影響*
    1.5.4使用下采樣數(shù)據(jù)進(jìn)行訓(xùn)練,原始數(shù)據(jù)進(jìn)行測試*
    1.5.5使用原始數(shù)據(jù)進(jìn)行訓(xùn)練與測試*
    2:過采樣平衡樣本
    2.1 SMOTE算法
    2.2 生成過采樣數(shù)據(jù)
    2.3 K折交叉驗證獲取較優(yōu)參數(shù)
    2.5 使用過采樣數(shù)據(jù)訓(xùn)練和測試模型
  • 三:總結(jié):過采樣和欠采樣模型對比

項目實現(xiàn)步驟

一:數(shù)據(jù)預(yù)處理

1:導(dǎo)入數(shù)據(jù)

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
# 導(dǎo)入數(shù)據(jù)
data = pd.read_csv("creditcard.csv")
data.head()

2:標(biāo)準(zhǔn)化處理

Amount這一列的數(shù)據(jù)已亥,沒有進(jìn)行特征縮放熊赖,所以接下來對其進(jìn)行標(biāo)準(zhǔn)化處理

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

3:樣本不平衡

count_classes = pd.value_counts(data['Class'], sort = True).sort_index() # 查看Class字段值得類別
count_classes.plot(kind = 'bar',rot=0)
plt.xlabel("Class")
plt.ylabel("頻數(shù)")
for a, b in zip(count_classes.index, count_classes.values):
    plt.text(a, b,b,ha='center', va='bottom', fontsize=10) 

響應(yīng)變量Class的取值中,0是指正常交易虑椎,1是指被盜刷震鹉。通過上面的圖和數(shù)據(jù)可知,存在492例盜刷,這是一個明顯樣本不平衡問題捆姜。

如何平衡樣本传趾?

在進(jìn)行不平衡樣本的數(shù)據(jù)預(yù)處理以前,先來談?wù)劮瞧胶鈽颖镜挠绊懸约俺S玫囊恍┢胶鈽颖镜姆椒嗉迹斑m用場景浆兰。

  • 不平衡樣本
    不平衡的樣本會影響模型的評估效果,嚴(yán)重的會帶來過擬合或欠擬合的結(jié)果珊豹。所以我們需要讓正負(fù)樣本在訓(xùn)練過程中擁有相同話語權(quán)或權(quán)重簸呈。在這里,稱數(shù)據(jù)集中樣本較多的一類稱為“大眾類”,樣本較少的一類稱為“小眾類”。
  • 處理方法
    1:下采樣(Undersampling,欠采樣):以小眾類的樣本數(shù)為標(biāo)準(zhǔn)叉寂,在大眾類的樣本中取得樣本數(shù)和小眾類的樣本數(shù)一樣(如讓0和1兩個樣本同樣少)尊剔。
    2:上采樣(Oversampling,過采樣):以大眾類的樣本數(shù)為標(biāo)準(zhǔn)叹哭,生成一些樣本使得小眾類的樣本數(shù)和大眾類的樣本數(shù)一樣多(對1號樣本進(jìn)行生成,讓 0 和 1 這兩個樣本一樣多)。

但是這樣簡單處理也會有相應(yīng)的弊端出現(xiàn)吃溅。因為上采樣是復(fù)制多份小眾類,所以上采樣中小眾類會反復(fù)出現(xiàn)一些樣本鸯檬,這會導(dǎo)致過擬合决侈;下采樣是選取部分大眾類,下采樣會由于丟失信息而導(dǎo)致欠擬合喧务。

  • 改進(jìn)方法
    1:下采樣:下采樣信息丟失的問題赖歌,有EasyEnsemble,與BalanceCascade兩種改進(jìn)方法功茴。
    (1)EasyEnsemble:多次下采樣選擇不同的數(shù)據(jù)集庐冯,訓(xùn)練不同的分類器。
    (2)BalanceCascade:先通過一次下采樣產(chǎn)生數(shù)據(jù)集坎穿,訓(xùn)練一個分類器展父,然后將那些分類正確的大眾樣本不放回返劲,然后對剩余的大眾樣本采樣產(chǎn)生數(shù)據(jù)集,訓(xùn)練多個分類器栖茉。
    2:上采樣
    (1)數(shù)據(jù)合成:SMOTE是最為常見過數(shù)據(jù)合成方法來基于已有的數(shù)據(jù)生成更多的樣本
    (2)數(shù)據(jù)加權(quán):其難點(diǎn)在于如何合理設(shè)置權(quán)重篮绿。
    3:異常值檢測
    可以換一種角度,對于正負(fù)樣本極不平衡的情況下吕漂,我們也可以視其為異常值檢測或一分類問題亲配。
  • 方法選擇
    所以解決不平衡樣本的問題有很多種方法,那如何選擇惶凝?
    1:在正負(fù)樣本都非常之少的情況下, 采用數(shù)據(jù)合成的方式:
    2:在負(fù)樣本足夠多弃榨,正樣本非常之少且比例及其懸殊的情況下, 考慮一分類方法
    3:在正負(fù)樣本都足夠多且比例不是特別懸殊的情況下, 應(yīng)該考慮采樣或者加權(quán)的方法
    4:同時也有適用于不平衡樣本的模型比如XGBoost 。

二:模型訓(xùn)練

針對樣本不平衡本項目將依次采用欠采樣和過采樣兩種方法進(jìn)行數(shù)據(jù)處理梨睁,并進(jìn)行模型訓(xùn)練鲸睛。

1:欠采樣平衡樣本

X = data.loc[:, data.columns != 'Class']  # 特征變量數(shù)據(jù)集
y = data.loc[:, data.columns == 'Class']  # 響應(yīng)變量數(shù)據(jù)集

number_records_fraud = len(data[data.Class == 1])       # 欺詐記錄的數(shù)量
fraud_indices = np.array(data[data.Class == 1].index)   #欺詐記錄的索引
normal_indices = data[data.Class == 0].index            # 正常記錄的索引

# 從正常記錄中隨機(jī)抽取與欺詐記錄相同數(shù)量的數(shù)據(jù)
random_normal_indices = np.random.choice(normal_indices, number_records_fraud, replace = False)  
random_normal_indices = np.array(random_normal_indices)
under_sample_indices = np.concatenate([fraud_indices,random_normal_indices])  # 將正常記錄索引和欺詐記錄的索引合并

under_sample_data = data.iloc[under_sample_indices,:]  # 下采樣得到的的樣本數(shù)據(jù)
X_undersample = under_sample_data.iloc[:, under_sample_data.columns != 'Class']  # 下采樣的特征變量數(shù)據(jù)集
y_undersample = under_sample_data.iloc[:, under_sample_data.columns == 'Class']  # 下采樣的相應(yīng)變量數(shù)據(jù)集

print("正常交易的數(shù)量占比: ", len(under_sample_data[under_sample_data.Class == 0])/len(under_sample_data))
print("欺詐交易的數(shù)量占比 ", len(under_sample_data[under_sample_data.Class == 1])/len(under_sample_data))
print("下采樣的交易數(shù)據(jù)數(shù)量: ", len(under_sample_data))
正常交易的數(shù)量占比:  0.5
欺詐交易的數(shù)量占比  0.5
下采樣的交易數(shù)據(jù)數(shù)量:  984

(1)數(shù)據(jù)拆分:訓(xùn)練集、驗證集與測試集

   a)訓(xùn)練集:直接參與了模型調(diào)參的過程坡贺,顯然不能用來反映模型真實的能力(防止課本死記硬背的學(xué)生擁有最好的成績官辈,即防止過擬合)
   b)驗證集:參與了人工調(diào)參(超參數(shù))的過程,也不能用來最終評判一個模型(刷題庫的學(xué)生不能算是學(xué)習(xí)好的學(xué)生)
   c)測試集:所以要通過最終的測試來考察一個模型真正的效果

這里僅僅將數(shù)據(jù)分成了訓(xùn)練集和測試集遍坟,之后介紹交叉驗證拳亿,會把訓(xùn)練集再次分成訓(xùn)練集與測試集。

from sklearn.model_selection import train_test_split  
# 將整個樣本數(shù)據(jù)集劃分:訓(xùn)練數(shù)據(jù)和測試數(shù)據(jù)
## test_size=0.3愿伴,表示測試集占總樣本數(shù)據(jù)集30%
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size = 0.3, random_state = 0)  
print("訓(xùn)練數(shù)據(jù)集長度: ", len(X_train))
print("測試數(shù)據(jù)集長度: ", len(X_test))
print("整個數(shù)據(jù)集長度: ", len(X_train)+len(X_test))

# 下采樣數(shù)據(jù)集劃分:訓(xùn)練數(shù)據(jù)和測試數(shù)據(jù)
X_train_undersample, X_test_undersample, y_train_undersample, y_test_undersample = train_test_split(X_undersample
                                                                                                   ,y_undersample
                                                                                                   ,test_size = 0.3
                                                                                                   ,random_state = 0)
print("")
print("下采樣訓(xùn)練數(shù)據(jù)集長度: ", len(X_train_undersample))
print("下采樣測試數(shù)據(jù)集長度: ", len(X_test_undersample))
print("整個下采樣數(shù)據(jù)集長度: ", len(X_train_undersample)+len(X_test_undersample))
> 訓(xùn)練數(shù)據(jù)集長度:  199364
測試數(shù)據(jù)集長度:  85443
整個數(shù)據(jù)集長度:  284807
    
>下采樣訓(xùn)練數(shù)據(jù)集長度:  688
下采樣測試數(shù)據(jù)集長度:  296
整個下采樣數(shù)據(jù)集長度:  984

說明:為什么進(jìn)行了下采樣肺魁,還要把原始數(shù)據(jù)進(jìn)行切分呢?對數(shù)據(jù)集的訓(xùn)練是通過下采樣的訓(xùn)練集隔节,對數(shù)據(jù)的測試的是通過原始的數(shù)據(jù)集的測試集鹅经,下采樣的測試集可能沒有原始部分當(dāng)中的一些特征,不能充分進(jìn)行測試怎诫。

(2)交叉驗證

交叉驗證是在機(jī)器學(xué)習(xí)建立模型和驗證模型參數(shù)時常用的辦法瘾晃,就是重復(fù)的使用數(shù)據(jù),把得到的樣本數(shù)據(jù)進(jìn)行切分幻妓,組合為不同的訓(xùn)練集和測試集蹦误,用訓(xùn)練集來訓(xùn)練模型,用測試集來評估模型預(yù)測的好壞肉津。在此基礎(chǔ)上可以得到多組不同的訓(xùn)練集和測試集强胰,某次訓(xùn)練集中的某樣本在下次可能成為測試集中的樣本,即所謂“交叉”妹沙。

交叉驗證方法
1:簡單交叉驗證:首先隨機(jī)將已給數(shù)據(jù)分成兩份偶洋,一份作為訓(xùn)練集,另一份作為測試集(比如: 70%的訓(xùn)練集初烘,30%的測試集)涡真。然后用訓(xùn)練集來訓(xùn)練模型,在測試集上驗證模型及參數(shù)肾筐。接著哆料,我們再把樣本打亂,重新選擇訓(xùn)練集和測試集吗铐,繼續(xù)訓(xùn)練數(shù)據(jù)和檢驗?zāi)P投唷W詈笪覀冞x擇損失函數(shù)評估最優(yōu)的模型和參數(shù)。
2:K折交叉驗證:會把樣本數(shù)據(jù)隨機(jī)的分成S份唬渗,每次隨機(jī)的選擇S-1份作為訓(xùn)練集典阵,剩下的1份做測試集。當(dāng)這一輪完成后镊逝,重新隨機(jī)選擇S-1份來訓(xùn)練數(shù)據(jù)壮啊。若干輪(小于S)之后,選擇損失函數(shù)評估最優(yōu)的模型和參數(shù)撑蒜。
3:留一交叉驗證:它是第二種情況的特例歹啼,此時S等于樣本數(shù)N,這樣對于N個樣本座菠,每次選擇N-1個樣本來訓(xùn)練數(shù)據(jù)狸眼,留一個樣本來驗證模型預(yù)測的好壞。此方法主要用于樣本量非常少的情況浴滴,比如對于普通適中問題拓萌,N小于50時,一般采用留一交叉驗證升略。

對于普通適中問題微王,如果數(shù)據(jù)樣本量小于一萬條,我們就會采用交叉驗證來訓(xùn)練優(yōu)化選擇模型品嚣。 如果樣本大于一萬條的話骂远,我們一般隨機(jī)的把數(shù)據(jù)分成三份,一份為訓(xùn)練集腰根,一份為驗證集激才,最后一份為測試集。用訓(xùn)練集來訓(xùn)練模型额嘿,用驗證集來評估模型預(yù)測的好壞和選擇模型及其對應(yīng)的參數(shù)瘸恼。把最終得到的模型再用于測試集,最終決定使用哪個模型以及對應(yīng)參數(shù)册养。


(3)模型評估方法

混淆矩陣:以分類模型中最簡單的二分類為例东帅,對于這種問題,我們的模型最終需要判斷樣本的結(jié)果是0還是1球拦,或者說是negative還是positive靠闭。

準(zhǔn)確率: 所有預(yù)測結(jié)果中帐我,預(yù)測正確的樣本數(shù)量占比。
1)公式:Acc=(TN+TF) / S愧膀。
2)由于樣本不平衡的問題拦键,導(dǎo)致了得到的高準(zhǔn)確率結(jié)果含有很大的水分。即如果樣本不平衡檩淋,準(zhǔn)確率就會失效芬为。正因為如此,也就衍生出了其它兩種指標(biāo):精準(zhǔn)率和召回率
精確率: 預(yù)測為正中蟀悦,實際為正占比媚朦。
1)公式:PRE=TP / Mt
2)精準(zhǔn)率代表對正樣本結(jié)果中的預(yù)測準(zhǔn)確程度,而準(zhǔn)確率則代表整體的預(yù)測準(zhǔn)確程度日戈,既包括正樣本询张,也包括負(fù)樣本。
召回率: 實際為正中浙炼,預(yù)測為正的占比瑞侮。
1)公式:Recall=TP / Nt
2)召回率越高,代表實際壞用戶被預(yù)測出來的概率越高鼓拧,它的含義類似:寧可錯殺一千半火,絕不放過一個。
漏檢率: 實際為正中季俩,預(yù)測為反的占比钮糖,漏檢率與召回率相反。
1)公式:MR=1-Recall
誤檢率: 預(yù)測為真中實際為負(fù)酌住,占所有負(fù)樣本比例店归。
1)公式:FPR=FP / Nf
F1-score
1)公式:F1-score=2 * 精準(zhǔn)率*召回率/ (精準(zhǔn)率+召回率)
2)我們希望精準(zhǔn)率和召回率同時都非常高,但是精準(zhǔn)率和召回率是兩個向矛盾的指標(biāo)酪我,如果其中一個非常高消痛,另一個肯定會非常低,如當(dāng)召回率很高時都哭,精準(zhǔn)率往往很低秩伞。這需要綜合考慮他們情況,F(xiàn)1-score同時考慮了兩個指標(biāo)欺矫,讓二者同時達(dá)到最高纱新,取一個平衡,當(dāng)F1-score較高時說明模型有效穆趴。
ROC曲線
1)縱坐標(biāo):召回率TPR/Recall
2)橫坐標(biāo):誤檢率FPR
3)ROC曲線也是通過遍歷所有閾值來繪制整條曲線的脸爱。如果不斷的遍歷所有閾值,預(yù)測的正樣本和負(fù)樣本是在不斷變化的未妹,相應(yīng)的精準(zhǔn)率和召回率在ROC曲線圖中也會沿著曲線滑動簿废,但是曲線本身是不會變的空入。判斷一個模型的ROC曲線是好的呢?我們當(dāng)然是希望精準(zhǔn)率越高族檬,誤檢率越低(即ROC曲線越陡峭)那么模型的性能越好歪赢。
AUC:ROC曲線下的面積
1:ROC曲線越陡越好,所以理想值就是1导梆,一個正方形,而最差的隨機(jī)判斷都有0.5迂烁,所以一般AUC的值是介于0.5到1之間的看尼。
2:AUC的一般判斷標(biāo)準(zhǔn)
0.5 - 0.7:效果較低
0.7 - 0.85:效果一般
0.85 - 0.95:效果很好
0.95 - 1:效果非常好,但一般不太可能

(4)正則化懲罰項

正則化是結(jié)構(gòu)風(fēng)險最小化策略的實現(xiàn)盟步,是在經(jīng)驗風(fēng)險上加上一個正則化項或懲罰項藏斩。正則化項一般是負(fù)責(zé)模型復(fù)雜度的單調(diào)遞增函數(shù),模型越復(fù)雜却盘,正則化值就越大狰域。正則化的作用是選擇經(jīng)驗風(fēng)險與模型復(fù)雜度同時較小的模型。正則化符合奧卡姆剃刀原理黄橘,在所有可能選擇的模型中兆览,能夠很好地解釋數(shù)據(jù)并且十分簡單的才是最好的模型。

  • 下面損失函數(shù)中塞关,第一項是經(jīng)驗風(fēng)險抬探,第二項為正則化項。正則化項可以取不同的形式帆赢,可以是參數(shù)向量的L2范數(shù)小压,也可以是L1范數(shù)。




L1正則化和L2正則化對比
  • L1正則化中可以對|w|求累加和椰于,但是只直接計算絕對值求累加和的話怠益,[1,0,0,0,0]和[0.25,0.25,0.25,0.25,0.25]的結(jié)果是相同的,無法做出區(qū)分瘾婿。
  • L2正則化懲罰力度更大蜻牢,對權(quán)重參數(shù)求平方和,目的是讓大的更大偏陪,相對懲罰更多孩饼。
  • λ系數(shù),表示正則化懲罰力度竹挡。λ值越大镀娶,表示懲罰力度越大,對模型影響越大揪罕。如果λ值較小梯码,意味著懲罰力度較小宝泵,不會對結(jié)果產(chǎn)生太大影響。
選擇L1正則化的模型還是L2正則化轩娶?
  • 如果數(shù)據(jù)集中有很多特征儿奶,而這些特征中并不是每一個都對結(jié)果有重要影響,那么就應(yīng)該選擇L1正則化的模型鳄抒。
  • 但如果數(shù)據(jù)集中特征本來就不多闯捎,而且每一個都有很重要的作用,那么就應(yīng)該選擇L2正則化的模型许溅。

(5)構(gòu)建模型

K折交叉驗證——使用下采樣訓(xùn)練數(shù)據(jù)集瓤鼻,訓(xùn)練和測試模型,尋找較優(yōu)模型參數(shù)
  • 模型正則化L1參數(shù)分別設(shè)置為[0.01,0.1,1,10,100]贤重;使用K折交叉驗證將下采樣訓(xùn)練數(shù)據(jù)分拆為5份茬祷,逐一遍歷數(shù)據(jù)訓(xùn)練模型,尋找較優(yōu)的L1參數(shù)并蝗。
  • 在異常檢測問題中祭犯,精準(zhǔn)率不是衡量模型好壞的指標(biāo),因為我們的目的是找出可能的異常值滚停,所以用召回率衡量模型的好壞沃粗。
from sklearn.linear_model import LogisticRegression  
from sklearn.model_selection import KFold, cross_val_score
from sklearn.metrics import confusion_matrix,recall_score,classification_report 

def printing_Kfold_scores(x_train_data,y_train_data):
    #k折交叉驗證
    # 會得到一個可迭代對象(可以用 for 循環(huán)遍歷取出),可以遍歷5次键畴,每次遍歷出來的會是一個2值列表陪每,
    # 存放每一次的訓(xùn)練集和驗證集的索引
    fold = KFold(n_splits=5,shuffle=False)  
    #不同的C參數(shù)
    c_param_range = [0.01,0.1,1,10,100]
    results_table = pd.DataFrame(index = range(len(c_param_range)), columns = ['C_parameter','Mean recall score'])
    results_table['C_parameter'] = c_param_range
    #k折操作將會給出兩個列表:train_indices = indices[0], test_indices = indices[1]
    j = 0
    for c_param in c_param_range:
        print('-------------------------------------------')
        print('C parameter: ', c_param)
        print('-------------------------------------------')
        print('')
        recall_accs = [] # 存放召回率
        # enumerate() 函數(shù)用于將一個可遍歷的數(shù)據(jù)對象(如列表、元組或字符串)組合為一個索引序列镰吵,同時列出數(shù)據(jù)和數(shù)據(jù)下標(biāo)檩禾,一般用在 for 循環(huán)當(dāng)中。
        for iteration,indices in enumerate(fold.split(x_train_data)):
            #把c_param_range代入到邏輯回歸模型中疤祭,并使用了l1正則化
            lr = LogisticRegression(C = c_param,penalty = 'l1',solver='liblinear')
            #使用indices[0]的數(shù)據(jù)進(jìn)行擬合曲線盼产,
            lr.fit(x_train_data.iloc[indices[0],:],y_train_data.iloc[indices[0],:].values.ravel()) #ravel()將多維數(shù)組轉(zhuǎn)換為一維數(shù)組
            #在indices[1]數(shù)據(jù)上預(yù)測值
            y_pred_undersample = lr.predict(x_train_data.iloc[indices[1],:].values)
            #根據(jù)不同的c_parameter計算召回率
            recall_acc = recall_score(y_train_data.iloc[indices[1],:].values,y_pred_undersample)
            recall_accs .append(recall_acc)
            print('Iteration ', iteration,': recall score = ', recall_acc)
        #求出我們想要的召回平均值
        results_table.loc[j,'Mean recall score'] = np.mean(recall_accs)
        j += 1
        print('')
        print('Mean recall score ', np.mean(recall_accs))
        print('')
    best_c = results_table.loc[results_table['Mean recall score'].values.argmax()]['C_parameter']
    #最后選擇最好的 C parameter
    print('*********************************************************************************')
    print('Best model to choose from cross validation is with C parameter = ', best_c)
    print('*********************************************************************************')
    return best_c
# 下采樣-訓(xùn)練數(shù)據(jù)集-召回率
best_c = printing_Kfold_scores(X_train_undersample,y_train_undersample) 
-------------------------------------------
C parameter:  0.01
-------------------------------------------
Iteration  0 : recall score =  0.9315068493150684
Iteration  1 : recall score =  0.9178082191780822
Iteration  2 : recall score =  1.0
Iteration  3 : recall score =  0.9864864864864865
Iteration  4 : recall score =  0.9696969696969697

Mean recall score  0.9610997049353213

-------------------------------------------
C parameter:  0.1
-------------------------------------------
Iteration  0 : recall score =  0.8493150684931506
Iteration  1 : recall score =  0.863013698630137
Iteration  2 : recall score =  0.9322033898305084
Iteration  3 : recall score =  0.9459459459459459
Iteration  4 : recall score =  0.8939393939393939

Mean recall score  0.8968834993678272

-------------------------------------------
C parameter:  1
-------------------------------------------
Iteration  0 : recall score =  0.863013698630137
Iteration  1 : recall score =  0.8904109589041096
Iteration  2 : recall score =  0.9830508474576272
Iteration  3 : recall score =  0.9459459459459459
Iteration  4 : recall score =  0.9090909090909091

Mean recall score  0.9183024720057457

-------------------------------------------
C parameter:  10
-------------------------------------------
Iteration  0 : recall score =  0.863013698630137
Iteration  1 : recall score =  0.8904109589041096
Iteration  2 : recall score =  0.9830508474576272
Iteration  3 : recall score =  0.9459459459459459
Iteration  4 : recall score =  0.9242424242424242

Mean recall score  0.9213327750360488

-------------------------------------------
C parameter:  100
-------------------------------------------
Iteration  0 : recall score =  0.863013698630137
Iteration  1 : recall score =  0.8904109589041096
Iteration  2 : recall score =  0.9830508474576272
Iteration  3 : recall score =  0.9459459459459459
Iteration  4 : recall score =  0.9242424242424242

Mean recall score  0.9213327750360488

*********************************************************************************
Best model to choose from cross validation is with C parameter =  0.01
*********************************************************************************
  • 上述結(jié)果可以得知:當(dāng)參數(shù)C為0.01時,模型的平均召回率最高勺馆,即模型的預(yù)測效果最好戏售。同時,我們可以發(fā)現(xiàn)草穆,對于相同的參數(shù)值灌灾,用交叉驗證每次迭代的召回率相差較大,這個現(xiàn)象說明交叉驗證是非常必要的悲柱,交叉驗證可以盡量減少訓(xùn)練數(shù)據(jù)對結(jié)果的影響锋喜。
繪制混淆矩陣
import itertools
def plot_confusion_matrix(cm,classes,title='Confusion matrix',cmap=plt.cm.Blues):
    '''這個方法用來輸出和畫出混淆矩陣的'''
    #cm為混淆矩陣數(shù)據(jù),interpolation='nearest'使用最近鄰插值,cmap顏色圖譜(colormap), 默認(rèn)繪制為RGB(A)顏色空間
    plt.imshow(cm,interpolation='nearest',cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    #xticks(刻度下標(biāo)嘿般,刻度標(biāo)簽)
    plt.xticks(tick_marks, classes, rotation=0)
    plt.yticks(classes)
    plt.ylim([1.5,-0.5])
    #text()命令可以在任意的位置添加文字
    thresh = cm.max() / 2
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, cm[i, j],
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")  
    plt.tight_layout() #自動緊湊布局
    plt.ylabel('True label')
    plt.xlabel('Predicted label')

計算模型指標(biāo)

# 計算模型指標(biāo)
from sklearn.metrics import f1_score,precision_score,recall_score,roc_auc_score,accuracy_score,roc_curve
def metrics_score(y_test,y_pred):
    Recall=recall_score(y_test,y_pred)  # 召回率
    Pre=precision_score(y_test,y_pred)  # 精準(zhǔn)率
    F1_score=f1_score(y_test,y_pred)    # F1_score
    AUC=roc_auc_score(y_test,y_pred)    # roc面積
    return Recall,Pre,F1_score,AUC

使用下采樣數(shù)據(jù)進(jìn)行訓(xùn)練段标,下采樣數(shù)據(jù)進(jìn)行測試

lr = LogisticRegression(C = best_c, penalty = 'l1',solver='liblinear')
lr.fit(X_train_undersample,y_train_undersample.values.ravel())
y_pred_undersample = lr.predict(X_test_undersample.values)
# 計算混淆矩陣:使用下采樣測試數(shù)據(jù)
cnf_matrix = confusion_matrix(y_test_undersample,y_pred_undersample) 
np.set_printoptions(precision=2) #輸出精度為小數(shù)點(diǎn)后兩位

#畫出非標(biāo)準(zhǔn)化的混淆矩陣
class_names = [0,1]
plt.figure()
plot_confusion_matrix(cnf_matrix,classes=class_names,title='Confusion matrix')
plt.show()

# 計算指標(biāo)得分
Recall,Pre,F1_score,AUC=metrics_score(y_test_undersample,y_pred_undersample)
print(' 召回率:{:.2%} | 精準(zhǔn)率:{:.2%} | F1_score:{:.2f} | AUC:{:.2f}'.format(Recall,Pre,F1_score,AUC))
# ROC曲線圖
y_pred_proba=lr.predict_proba(X_test_undersample.values)
lr_fpr0,lr_tpr0,lr_threasholds0=roc_curve(y_test_undersample,y_pred_proba[:,1]) # 計算ROC的值,lr_threasholds為閾值
plt.title("ROC曲線(AUC={:.2f})".format(AUC))
plt.xlabel('誤檢率')
plt.ylabel('召回率')
plt.plot(lr_fpr,lr_tpr)
plt.show()
 召回率:93.20% | 精準(zhǔn)率:92.57% | F1_score:0.93 | AUC:0.93
  • 使用下采樣數(shù)據(jù)訓(xùn)練和測試模型,得到的模型召回率為93.19%,精確率為91.33%炉奴,看起來效果好像非常好逼庞;但這只是對下采樣數(shù)據(jù)的測試集的預(yù)測效果,我們應(yīng)該用原始數(shù)據(jù)的測試集來衡量這個模型的優(yōu)劣瞻赶。

不同閾值對結(jié)果的影響

  • 使用下采樣數(shù)據(jù)來訓(xùn)練模型并作出不同閾值條件下下采樣數(shù)據(jù)的測試集的混淆矩陣赛糟,以便觀察閾值對模型的影響,從而選擇合適的閾值獲得最優(yōu)模型.
lr = LogisticRegression(C =  best_c, penalty = 'l1',solver='liblinear')
lr.fit(X_train_undersample,y_train_undersample.values.ravel())
y_pred_undersample_proba = lr.predict_proba(X_test_undersample.values)
thresholds = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9]
plt.figure(figsize=(10,10))
j = 1
for i in thresholds:
    y_test_predictions_high_recall = y_pred_undersample_proba[:,1] > i
    plt.subplot(3,3,j)
    j += 1
    #計算混淆矩陣
    cnf_matrix = confusion_matrix(y_test_undersample,y_test_predictions_high_recall)
    #輸出精度為小數(shù)點(diǎn)后兩位
    np.set_printoptions(precision=2)
     
    #畫出非標(biāo)準(zhǔn)化的混淆矩陣
    class_names = [0,1]
    plot_confusion_matrix(cnf_matrix,classes=class_names,title='Threshold >= %s'%i)
    # 計算指標(biāo)得分
    Recall,Pre,F1_score,AUC=metrics_score(y_test_undersample,y_test_predictions_high_recall)
    print('閾值:{} | 召回率:{:.2%} | 精準(zhǔn)率:{:.2%} | F1_score:{:.2f} AUC{:.2f}'.format(i,Recall,Pre,F1_score,AUC))
閾值:0.1 | 召回率:100.00% | 精準(zhǔn)率:49.66% | F1_score:0.66 | AUC:0.50
閾值:0.2 | 召回率:100.00% | 精準(zhǔn)率:49.66% | F1_score:0.66 | AUC:0.50
閾值:0.3 | 召回率:100.00% | 精準(zhǔn)率:49.66% | F1_score:0.66 | AUC:0.50
閾值:0.4 | 召回率:96.60% | 精準(zhǔn)率:61.74% | F1_score:0.75 | AUC:0.69
閾值:0.5 | 召回率:93.20% | 精準(zhǔn)率:92.57% | F1_score:0.93 | AUC:0.93
閾值:0.6 | 召回率:87.07% | 精準(zhǔn)率:97.71% | F1_score:0.92 | AUC:0.93
閾值:0.7 | 召回率:81.63% | 精準(zhǔn)率:99.17% | F1_score:0.90 | AUC:0.90
閾值:0.8 | 召回率:76.19% | 精準(zhǔn)率:100.00% | F1_score:0.86 | AUC:0.88
閾值:0.9 | 召回率:57.82% | 精準(zhǔn)率:100.00% | F1_score:0.73 | AUC:0.79
  • 由上可知砸逊,閾值的設(shè)置極大的影響了模型的泛化效果璧南,隨著閾值的改變,模型的召回率和精確率都會隨之變化痹兜;當(dāng)閾值設(shè)置為0.5和0.6時穆咐,得到的AUC相同颤诀,但綜合考慮召回率和F1_score指標(biāo)字旭,應(yīng)該選擇閾值為0.5。

使用下采樣數(shù)據(jù)進(jìn)行訓(xùn)練崖叫,原始數(shù)據(jù)進(jìn)行測試

lr = LogisticRegression(C = best_c, penalty = 'l1',solver='liblinear')
lr.fit(X_train_undersample,y_train_undersample.values.ravel())
y_pred = lr.predict(X_test.values)
#計算混淆矩陣
cnf_matrix = confusion_matrix(y_test,y_pred)
#輸出精度為小數(shù)點(diǎn)后兩位
np.set_printoptions(precision=2)
np.set_printoptions(precision=2)
Recall=cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1])  # 召回率
Pre=cnf_matrix[1,1]/(cnf_matrix[0,1]+cnf_matrix[1,1])     #精準(zhǔn)率
F1_score=2*Recall*Pre/(Recall+Pre)                        # F1_score
print('召回率:{:.2%}'.format(cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1])), '|',
      '精確率:{:.2%}'.format(cnf_matrix[1,1]/(cnf_matrix[0,1]+cnf_matrix[1,1])),'|',
      'F1_score:{:.2%}'.format(F1_score))
#畫出非標(biāo)準(zhǔn)化的混淆矩陣
class_names = [0,1]
plt.figure()
plot_confusion_matrix(cnf_matrix,classes=class_names,title='Confusion matrix')
plt.show()
召回率:91.16% | 精準(zhǔn)率:1.41% | F1_score:0.03 | AUC:0.90
  • 使用下采樣數(shù)據(jù)進(jìn)行訓(xùn)練遗淳,原始數(shù)據(jù)進(jìn)行測試,得到的召回率:92.52% ,精確率:1.40%心傀。雖然召回率很高屈暗,但模型出現(xiàn)了錯將9573個正常值誤判為異常值的情況,這說明該模型具有較大的誤殺率脂男,導(dǎo)致精確率很低养叛,這是下采樣方法的缺陷。

使用原始數(shù)據(jù)進(jìn)行訓(xùn)練與測試

  • 對原始數(shù)據(jù)進(jìn)行K折交叉驗證
best_c2 = printing_Kfold_scores(X_train,y_train)
-------------------------------------------
C parameter:  0.01
-------------------------------------------
Iteration  0 : recall score =  0.4925373134328358
Iteration  1 : recall score =  0.6027397260273972
Iteration  2 : recall score =  0.6833333333333333
Iteration  3 : recall score =  0.5692307692307692
Iteration  4 : recall score =  0.45

Mean recall score  0.5595682284048672

-------------------------------------------
C parameter:  0.1
-------------------------------------------
Iteration  0 : recall score =  0.5671641791044776
Iteration  1 : recall score =  0.6164383561643836
Iteration  2 : recall score =  0.6833333333333333
Iteration  3 : recall score =  0.5846153846153846
Iteration  4 : recall score =  0.525

Mean recall score  0.5953102506435158

-------------------------------------------
C parameter:  1
-------------------------------------------
Iteration  0 : recall score =  0.5522388059701493
Iteration  1 : recall score =  0.6164383561643836
Iteration  2 : recall score =  0.7166666666666667
Iteration  3 : recall score =  0.6153846153846154
Iteration  4 : recall score =  0.5625

Mean recall score  0.612645688837163

-------------------------------------------
C parameter:  10
-------------------------------------------
Iteration  0 : recall score =  0.5522388059701493
Iteration  1 : recall score =  0.6164383561643836
Iteration  2 : recall score =  0.7333333333333333
Iteration  3 : recall score =  0.6153846153846154
Iteration  4 : recall score =  0.575

Mean recall score  0.6184790221704963

-------------------------------------------
C parameter:  100
-------------------------------------------
Iteration  0 : recall score =  0.5522388059701493
Iteration  1 : recall score =  0.6164383561643836
Iteration  2 : recall score =  0.7333333333333333
Iteration  3 : recall score =  0.6153846153846154
Iteration  4 : recall score =  0.575

Mean recall score  0.6184790221704963

*********************************************************************************
Best model to choose from cross validation is with C parameter =  10.0
*********************************************************************************
  • 構(gòu)建模型
lr = LogisticRegression(C = best_c2, penalty = 'l1',solver='liblinear')
lr.fit(X_train,y_train.values.ravel())
y_pred_undersample = lr.predict(X_test.values)
  • 畫混淆矩陣圖
#計算混淆矩陣
cnf_matrix = confusion_matrix(y_test,y_pred_undersample)
np.set_printoptions(precision=2)  #輸出精度為小數(shù)點(diǎn)后兩位
# 畫混淆矩陣圖
class_names = [0,1]
plt.figure()
plot_confusion_matrix(cnf_matrix
                      , classes=class_names
                      , title='Confusion matrix')
plt.show()
#計算指標(biāo)得分
Recall,Pre,F1_score,AUC=metrics_score(y_test,y_pred_undersample)
print(' 召回率:{:.2%} | 精準(zhǔn)率:{:.2%} | F1_score:{:.2f} | AUC:{:.2f}'.format(Recall,Pre,F1_score,AUC))
# 畫ROC曲線
y_pred_proba=lr.predict_proba(X_test.values)
lr_fpr,lr_tpr,lr_threasholds=roc_curve(y_test,y_pred_proba[:,1]) # 計算ROC的值,lr_threasholds為閾值
plt.title("ROC曲線(AUC={:.2f})".format(AUC))
plt.xlabel('誤檢率')
plt.ylabel('召回率')
plt.plot(lr_fpr,lr_tpr)
plt.show()
召回率:61.90% | 精準(zhǔn)率:88.35% | F1_score:0.73 | AUC:0.81
  • 單純使用未處理過的原始數(shù)據(jù)訓(xùn)練和測試模型宰翅,得到的模型召回率僅為61.90%,精確率為88.35%弃甥,并沒有下采樣模型效果好。

2:過采樣平衡樣本

SMOTE算法

  • 由于隨機(jī)采樣采取簡單素質(zhì)樣本的策略來增加少數(shù)類樣本汁讼,這樣容易產(chǎn)生模型過擬合的問題淆攻。
  • SMOTE算法它是基于采樣算法的一種改進(jìn)方案。用SMOTE算法生成小眾類樣本嘿架,使得大眾類樣本和小眾類樣本數(shù)量相同瓶珊,用生成后的數(shù)據(jù)來做模型訓(xùn)練和測試。
算法流程:
  • 設(shè)一個少數(shù)類樣本數(shù)為T耸彪,那么SMOTE算法將為這個少數(shù)類合成NT個新樣本伞芹。這里要求N必須是正整數(shù),如果給定的N<1蝉娜,那么算法認(rèn)為少數(shù)類的樣本數(shù)T=NT丑瞧,并將強(qiáng)制N=1柑土。考慮該少數(shù)類的一個樣本i绊汹,其特征向量為Xi,i∈{1,…,T}

    • Step1:首先從該少數(shù)類的全部T個樣本中找到樣本Xi的k個近鄰(例如歐式距離)稽屏,記為:xi(near),near∈{1,…,k}。
    • Step2:然后從這k個近鄰中隨機(jī)選擇一個樣本Xi(nn)西乖,再生成一個0到1之間的隨機(jī)數(shù)random狐榔,從而合成一個新樣本Xi1:Xi1=Xi+random*(Xi(nn)-Xi);
    • Step3:將步驟2重復(fù)進(jìn)行N次获雕,從而可以合成N個新樣本: Xinew,new∈{1,…,k}薄腻。
  • 那么,對全部的T個少數(shù)類樣本進(jìn)行上述操作届案,便可為該少數(shù)類合成NT個新樣本庵楷。如果樣本的特征維數(shù)是2維,那么每個樣本都可以用二維平面的一個點(diǎn)來表示楣颠。SMOTE算法所合成出的一個新樣本xi1相當(dāng)于是表示樣本xi的點(diǎn)和表示樣本xi(nn)的點(diǎn)之間所連線段上的一個點(diǎn)尽纽,所以說該算法是基于“差值”來合成新樣本。



# 導(dǎo)入相關(guān)庫
import pandas as pd
from imblearn.over_sampling import SMOTE
from sklearn.ensemble import RandomForestClassifier  # 隨機(jī)森林分類器
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split

SMOTE算法生成過采樣數(shù)據(jù)

#導(dǎo)入數(shù)據(jù)
credit_cards=pd.read_csv('creditcard.csv')
columns=credit_cards.columns

features_columns=columns.delete(len(columns)-1) # 為了獲得特征列童漩,移除最后一列標(biāo)簽列
features = credit_cards[features_columns]  # 特征數(shù)據(jù)
labels=credit_cards['Class']               #響應(yīng)變量

# 劃分訓(xùn)練集合測試集
features_train, features_test, labels_train, labels_test = train_test_split(features, labels, test_size=0.2, random_state=0)
# 過采樣
oversampler = SMOTE(random_state=0)
os_features,os_labels = oversampler.fit_sample(features_train,labels_train)
print('過采樣后 1 的樣本的個數(shù)為:',len(os_labels[os_labels==1]))
過采樣后 1 的樣本的個數(shù)為: 227454

K折交叉驗證獲取較優(yōu)參數(shù)L1

os_features = pd.DataFrame(os_features)  # 特征數(shù)據(jù)
os_labels = pd.DataFrame(os_labels)      # 響應(yīng)變量
best_c = printing_Kfold_scores(os_features,os_labels)
-------------------------------------------
C parameter:  0.01
-------------------------------------------

Iteration  0 : recall score =  0.8903225806451613
Iteration  1 : recall score =  0.8947368421052632
Iteration  2 : recall score =  0.9687506916011951
Iteration  3 : recall score =  0.9555181851155736
Iteration  4 : recall score =  0.9584308811729921

Mean recall score  0.9335518361280369

-------------------------------------------
C parameter:  0.1
-------------------------------------------

Iteration  0 : recall score =  0.8903225806451613
Iteration  1 : recall score =  0.8947368421052632
Iteration  2 : recall score =  0.9704105344694036
Iteration  3 : recall score =  0.9559688286565327
Iteration  4 : recall score =  0.9528912630109583

Mean recall score  0.9328660097774637

-------------------------------------------
C parameter:  1
-------------------------------------------

Iteration  0 : recall score =  0.8903225806451613
Iteration  1 : recall score =  0.8947368421052632
Iteration  2 : recall score =  0.9698351222750913
Iteration  3 : recall score =  0.9598707422428859
Iteration  4 : recall score =  0.9605082379837548

Mean recall score  0.9350547050504312

-------------------------------------------
C parameter:  10
-------------------------------------------

Iteration  0 : recall score =  0.8903225806451613
Iteration  1 : recall score =  0.8947368421052632
Iteration  2 : recall score =  0.9705433218988603
Iteration  3 : recall score =  0.9597718204899924
Iteration  4 : recall score =  0.9608819423835746

Mean recall score  0.9352513015045704

-------------------------------------------
C parameter:  100
-------------------------------------------

Iteration  0 : recall score =  0.8903225806451613
Iteration  1 : recall score =  0.8947368421052632
Iteration  2 : recall score =  0.9705433218988603
Iteration  3 : recall score =  0.9603543597014761
Iteration  4 : recall score =  0.9602884118662138

Mean recall score  0.9352491032433949

*********************************************************************************
Best model to choose from cross validation is with C parameter =  10.0
*********************************************************************************

使用過采樣數(shù)據(jù)訓(xùn)練和測試模型

lr = LogisticRegression(C = best_c, penalty = 'l1',solver='liblinear')
lr.fit(os_features,os_labels.values.ravel())
y_pred = lr.predict(features_test.values)
# 計算混淆矩陣
cnf_matrix = confusion_matrix(labels_test,y_pred)
np.set_printoptions(precision=2)
# 畫出非規(guī)范化的混淆矩陣
class_names = [0,1]
plt.figure()
plot_confusion_matrix(cnf_matrix
                      , classes=class_names
                      , title='Confusion matrix')
plt.show()
#計算指標(biāo)得分
Recall,Pre,F1_score,AUC=metrics_score(labels_test,y_pred)
print('召回率:{:.2%} | 精準(zhǔn)率:{:.2%} | F1_score:{:.2f} | AUC:{:.2f}'.format(Recall,Pre,F1_score,AUC))
# 畫出ROC曲線圖
y_pred_proba=lr.predict_proba(features_test.values)
lr_fpr,lr_tpr,lr_threasholds=roc_curve(labels_test,y_pred_proba[:,1]) # 計算ROC的值,lr_threasholds為閾值
plt.title("ROC曲線(AUC={:.2f})".format(AUC))
plt.xlabel('誤檢率')
plt.ylabel('召回率')
plt.plot(lr_fpr,lr_tpr)
plt.show()
召回率:91.09% | 精準(zhǔn)率:14.63% | F1_score:0.25 | AUC:0.95

總結(jié):過采樣和欠采樣模型對比

  • 使用欠采樣數(shù)據(jù)訓(xùn)練出的模型弄贿,召回率:91.16% ,精準(zhǔn)率:1.41% 矫膨,誤殺數(shù):9369差凹,F(xiàn)1_score:0.03 , AUC:0.90
    使用過采樣數(shù)據(jù)訓(xùn)練出的模型侧馅,召回率:91.09% 危尿,精準(zhǔn)率:14.63% ,誤殺數(shù):537馁痴,F(xiàn)1_score:0.25 谊娇,AUC:0.95
  • 通過對比兩個模型得分,在召回率相差不大的情況下弥搞,使用過采樣數(shù)據(jù)訓(xùn)練出的模型邮绿,誤殺數(shù)量明顯更少,模型精準(zhǔn)率更高攀例,所以使用過采樣處理數(shù)據(jù)的方法更好船逮。
  • 另外不同的懲罰力度c和不同的閾值會對模型的泛化能力有很大影響漠畜,所以需要反復(fù)調(diào)試以得到最合適的參數(shù)匕累,從而獲得最優(yōu)模型。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載坞古,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末酱鸭,一起剝皮案震驚了整個濱河市吗垮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌凹髓,老刑警劉巖烁登,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蔚舀,居然都是意外死亡饵沧,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門赌躺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來狼牺,“玉大人,你說我怎么就攤上這事礼患∈窃浚” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵缅叠,是天一觀的道長悄泥。 經(jīng)常有香客問我,道長痪署,這世上最難降的妖魔是什么码泞? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任兄旬,我火速辦了婚禮狼犯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘领铐。我一直安慰自己悯森,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布绪撵。 她就那樣靜靜地躺著瓢姻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪音诈。 梳的紋絲不亂的頭發(fā)上幻碱,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機(jī)與錄音细溅,去河邊找鬼褥傍。 笑死,一個胖子當(dāng)著我的面吹牛喇聊,可吹牛的內(nèi)容都是我干的恍风。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼朋贬!你這毒婦竟也來了凯楔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤锦募,失蹤者是張志新(化名)和其女友劉穎摆屯,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體糠亩,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鸥拧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了削解。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片富弦。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖氛驮,靈堂內(nèi)的尸體忽然破棺而出腕柜,到底是詐尸還是另有隱情,我是刑警寧澤矫废,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布盏缤,位于F島的核電站,受9級特大地震影響蓖扑,放射性物質(zhì)發(fā)生泄漏唉铜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一律杠、第九天 我趴在偏房一處隱蔽的房頂上張望潭流。 院中可真熱鬧,春花似錦柜去、人聲如沸灰嫉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽讼撒。三九已至,卻和暖如春股耽,著一層夾襖步出監(jiān)牢的瞬間根盒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工物蝙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留炎滞,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓茬末,卻偏偏與公主長得像厂榛,于是被迫代替她去往敵國和親盖矫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容