一文概括風(fēng)控的遷移學(xué)習(xí)及實(shí)踐(Tabular Data)

一、遷移學(xué)習(xí)

機(jī)器學(xué)習(xí)有一個(gè)基本假設(shè):數(shù)據(jù)同分布。可參考之前這篇介紹:《數(shù)據(jù)不同分布,怎么整?》 然而,現(xiàn)實(shí)中的數(shù)據(jù)情況通常有點(diǎn)坎坷,數(shù)據(jù)不同分布的也很常見(jiàn),比如,已有的大量其他領(lǐng)域的數(shù)據(jù)如何適用以當(dāng)前任務(wù)的問(wèn)題,這也是遷移學(xué)習(xí)所要解決的!

遷移學(xué)習(xí),即Transfer learning 可以簡(jiǎn)單定義為:將在源域S的知識(shí)遷移到目標(biāo)域T任務(wù),提高在目標(biāo)域T的任務(wù)下模型預(yù)測(cè)的性能。

顧名思義就是遷移其他領(lǐng)域的數(shù)據(jù)經(jīng)驗(yàn),用于當(dāng)前的任務(wù),以解決當(dāng)前領(lǐng)域任務(wù)的數(shù)據(jù)積累不充分的問(wèn)題。而通常地,其他領(lǐng)域的數(shù)據(jù)和當(dāng)前目標(biāo)域任務(wù)的數(shù)據(jù)分布是有差異的。

在NLP、CV任務(wù)中很天然地適合做遷移學(xué)習(xí),各種的CV、NLP預(yù)訓(xùn)練模型層出不窮(如Bert、GPT、MAE)。一個(gè)例子如文本分類(lèi)任務(wù),我們可以借用在其他文本數(shù)據(jù)已經(jīng)訓(xùn)練好的預(yù)訓(xùn)練模型(word2vec、BERT、GPT等等),用于當(dāng)前的新聞分類(lèi)任務(wù)里面。這樣可以大大減少當(dāng)前任務(wù)需要的數(shù)據(jù),而且預(yù)訓(xùn)練模型是基于大樣本學(xué)習(xí)的經(jīng)驗(yàn),做下微調(diào),應(yīng)用效果也杠杠的。


二、風(fēng)控的遷移學(xué)習(xí)

回到金融風(fēng)控任務(wù),需要寄望于遷移學(xué)習(xí)的場(chǎng)景還是挺多的。很經(jīng)常的,業(yè)務(wù)有擴(kuò)展,引入了新的一個(gè)經(jīng)營(yíng)客群,而新的客群樣本量剛開(kāi)始肯定是很少的,這時(shí)就很需要借助下舊客群的數(shù)據(jù)。

而難點(diǎn)在于,風(fēng)控領(lǐng)域很難像NLP領(lǐng)域那樣的文字表示直接遷移,NLP中一個(gè)任務(wù)的文本表示可能就很適用另一文本任務(wù)。

風(fēng)控面對(duì)的主要是結(jié)構(gòu)化數(shù)據(jù)(Tabular Data),一個(gè)任務(wù)的數(shù)據(jù)組成、特征含義就很復(fù)雜多樣了,盡管可以抽取出同一組特征表示,數(shù)據(jù)分布可能也是天差地別。這種情況下怎么做遷移學(xué)習(xí)呢?

下面結(jié)合風(fēng)控的信用評(píng)分卡的任務(wù),具體介紹遷移學(xué)習(xí)方法及項(xiàng)目代碼實(shí)踐。

首先先做下任務(wù)的背景介紹。信用評(píng)分卡是風(fēng)控領(lǐng)域的核心任務(wù)之一,依據(jù)如個(gè)人基本信息、經(jīng)濟(jì)能力、貸款歷史信息,用于判斷借貸用戶(hù)的按時(shí)還款的概率。本文數(shù)據(jù)來(lái)源github.com/aialgorithm/Blog《一文梳理金融風(fēng)控建模全流程(Python)》


引用下遷移學(xué)習(xí)經(jīng)典論文《A survey on transfer learning》,遷移學(xué)習(xí)可以分為四種基本的方法:

  • 基于樣本的遷移
  • 基于特征的遷移
  • 基于模型的遷移
  • 基于關(guān)系的遷移(此項(xiàng)相關(guān)研究較少,本文略)。

2.1 基于樣本的遷移

基于樣本的遷移,是通過(guò)遷移源域的某些樣本或設(shè)定樣本權(quán)重到目標(biāo)域?qū)W習(xí)。



基于樣本應(yīng)該是結(jié)構(gòu)化數(shù)據(jù)任務(wù)應(yīng)用較為廣泛的遷移方法,但這類(lèi)方法通常只在領(lǐng)域間分布差異較小時(shí)有效。最簡(jiǎn)單直接的,可以把原領(lǐng)域的樣本直接加入目標(biāo)領(lǐng)域一起學(xué)習(xí)訓(xùn)練,但由于數(shù)據(jù)分布差異的問(wèn)題,這樣可能效果反而更差。

# 代碼來(lái)源:公眾號(hào)-算法進(jìn)階
# 完整代碼:github.com/aialgorithm/Blog 請(qǐng)見(jiàn)文章對(duì)應(yīng)項(xiàng)目代碼 

train_xa = pd.concat([train_x,train_source[filter_feas]]) # 直接把全量原領(lǐng)域數(shù)據(jù)加到目標(biāo)領(lǐng)域訓(xùn)練
train_ya = pd.concat([train_y,train_source['isDefault']])
lgb_source=lightgbm.LGBMClassifier(n_estimators=100, num_leaves=4,class_weight= 'balanced',metric = 'AUC',lambda_l1=1,lambda_l2=1) 

lgb_source.fit(train_xa, train_ya)
print(train_ya.value_counts())
# 以目標(biāo)領(lǐng)域的測(cè)試集判斷效果
print('train ',model_metrics(lgb_source,train_x, train_y))
print('test ',model_metrics(lgb_source,test_x,test_y))

對(duì)于本項(xiàng)目直接拼湊兩份領(lǐng)域數(shù)據(jù)用于訓(xùn)練,效果提升并不大。相比暴力拼湊,還有有兩種做法可以減少分布差異。

2.1.1 樣本選擇法

只從原領(lǐng)域遷移與目標(biāo)域分布一致(相似)的那部分樣本。

我們可以通過(guò)像one class-SVM 、孤立森林等異常檢測(cè)方法,以目標(biāo)域樣本為訓(xùn)練單分類(lèi)SVM,選取預(yù)測(cè)原領(lǐng)域的樣本中與目標(biāo)域相似概率(閾值)較高的樣本。

from sklearn.svm import OneClassSVM
from sklearn.ensemble import IsolationForest
ir = IsolationForest()#OneClassir()
ir.fit(train_x.fillna(0))
# 預(yù)測(cè)
train_source['isnormal'] = ir.predict(train_source[filter_feas].fillna(0))  #-1為分布異常的樣本
train_source['normal_score'] = ir.score_samples(train_source[filter_feas].fillna(0))+1 #正常程度數(shù)值
train_source['normal_score'].hist(bins=10)

#篩選出分布正常的樣本,拼接訓(xùn)練集

train_xa = pd.concat([train_x,train_source.loc[train_source['isnormal']==1,filter_feas]])
train_ya = pd.concat([train_y,train_source.loc[train_source['isnormal']==1,'isDefault']])

lgb_source.fit(train_xa, train_ya)
print(train_ya.value_counts())
# 以目標(biāo)領(lǐng)域的測(cè)試集判斷效果
print('train ',model_metrics(lgb_source,train_x, train_y))
print('test ',model_metrics(lgb_source,test_x,test_y))
  • 或者另一種方式, 結(jié)合我們的信用違約二分類(lèi)模型的任務(wù),也可以通過(guò)目標(biāo)域樣本訓(xùn)練一個(gè)二分類(lèi)模型,通過(guò)分類(lèi)模型對(duì)原領(lǐng)域樣本的預(yù)測(cè)結(jié)果,選取出在原領(lǐng)域中預(yù)測(cè)較準(zhǔn)的那部分樣本(原領(lǐng)域預(yù)測(cè)較準(zhǔn)的樣本說(shuō)明和目標(biāo)領(lǐng)域?qū)W習(xí)的樣本比較一致)。

其中預(yù)測(cè)概率的閾值是個(gè)經(jīng)驗(yàn)值,可以通過(guò)搜索不同閾值下實(shí)際效果確定。另外,可以結(jié)合實(shí)際效果,進(jìn)行多次迭代選擇。


2.1.2 樣本權(quán)重法

對(duì)與目標(biāo)域分布一致性較高的樣本,就給以較高的樣本學(xué)習(xí)權(quán)重。反之亦然。通過(guò)樣本權(quán)重控制對(duì)原領(lǐng)域樣本的有效利用。

  • 簡(jiǎn)單點(diǎn)的方法,可以通過(guò)異常檢測(cè)模型評(píng)價(jià)原領(lǐng)域的樣本的正常的程度作為權(quán)重;

import numpy as np

from sklearn.svm import OneClassSVM
from sklearn.ensemble import IsolationForest
ir = IsolationForest()#OneClassir()
ir.fit(train_x.fillna(0))
# 預(yù)測(cè)
train_source['isnormal'] = ir.predict(train_source[filter_feas].fillna(0))  #-1為與目標(biāo)域分布異常的樣本
train_source['normal_score'] = ir.score_samples(train_source[filter_feas].fillna(0))**2 #正常程度數(shù)值
train_source['normal_score'].hist(bins=10)

#拼接訓(xùn)練集

train_xa = pd.concat([train_x,train_source.loc[:,filter_feas]])
train_ya = pd.concat([train_y,train_source.loc[:,'isDefault']])
weights = np.append(np.array([1]*train_x.shape[0]) ,train_source['normal_score'])  #目標(biāo)域權(quán)重固定為1,原領(lǐng)域按照正常程度加權(quán)

lgb_source.fit(train_xa, train_ya,sample_weight=weights)
print(train_ya.value_counts())
# 以目標(biāo)領(lǐng)域的測(cè)試集判斷效果
print('train ',model_metrics(lgb_source,train_x, train_y))
print('test ',model_metrics(lgb_source,test_x,test_y))


  • 也可以對(duì)目標(biāo)域,原領(lǐng)域的樣本分別標(biāo)注為’1‘和’0‘標(biāo)簽,訓(xùn)練一個(gè)分類(lèi)模型,驗(yàn)證分類(lèi)模型的預(yù)測(cè)效果,模型效果越好說(shuō)明兩個(gè)領(lǐng)域的不一樣(區(qū)分)程度越明顯,越有必要做樣本選擇或樣本權(quán)重。 這里原領(lǐng)域的樣本權(quán)重可以用歸一化后的odds作為權(quán)重(odds是指一個(gè)事件的發(fā)生比,為該事件發(fā)生的概率與該事件不發(fā)生的概率的比值)。在這里,原領(lǐng)域的樣本權(quán)重就是歸一化后的【樣本屬于目標(biāo)域的概率p(t|x)除以不屬于目標(biāo)域的概率1-p(t|x)】。

  • 或者還有一種方式, 以我們的信用違約二分類(lèi)模型的任務(wù),可以通過(guò)目標(biāo)域樣本訓(xùn)練一個(gè)二分類(lèi)模型,以分類(lèi)模型對(duì)原領(lǐng)域樣本的預(yù)測(cè)結(jié)果的準(zhǔn)確性為權(quán)重。預(yù)測(cè)準(zhǔn)確的樣本權(quán)重越高。權(quán)重=1- abs(預(yù)測(cè)概率 - 實(shí)際標(biāo)簽值),如實(shí)際標(biāo)簽為1的樣本,模型預(yù)測(cè)的概率為0.9權(quán)重就為0.9,預(yù)測(cè)概率如為0.2,與標(biāo)簽差異比較大,權(quán)重就是0.2 也比較小。

#拼接訓(xùn)練集

train_xa = pd.concat([train_x,train_source.loc[:,filter_feas]])
train_ya = pd.concat([train_y,train_source.loc[:,'isDefault']])

train_source['proba'] = lgb_tar.predict_proba(train_source[filter_feas])[:,1] 
train_source['samplew']  = 1-abs(train_source['proba']-train_source['isDefault'])
weights = np.append(np.array([1]*train_x.shape[0]) ,train_source['samplew'])  #目標(biāo)域權(quán)重固定為1,原領(lǐng)域按照分類(lèi)準(zhǔn)確程度加權(quán)

lgb_source.fit(train_xa, train_ya,sample_weight=weights)
print(train_ya.value_counts())
# 以目標(biāo)領(lǐng)域的測(cè)試集判斷效果
print('train ',model_metrics(lgb_source,train_x, train_y))
print('test ',model_metrics(lgb_source,test_x,test_y))



以上幾種方法都可以在原領(lǐng)域樣本引入一個(gè)先驗(yàn)的樣本權(quán)重,加入到模型訓(xùn)練的sample_weights。其中,目標(biāo)領(lǐng)域的樣本就沒(méi)必要加樣本權(quán)重或加入個(gè)較大的權(quán)重。當(dāng)然,權(quán)重方法的好壞最終還是要結(jié)合實(shí)際效果。

  • TrAdaboost

不同于上面直接給一個(gè)固定的樣本權(quán)重,我們還可以在訓(xùn)練學(xué)習(xí)中動(dòng)態(tài)地調(diào)整好權(quán)重,這里有個(gè)現(xiàn)成的遷移學(xué)習(xí)boosting框架TrAdaboost 《Boosting for Transfer Learning 》

TrAdaboost可以通過(guò)動(dòng)態(tài)調(diào)整樣本權(quán)重,在目標(biāo)領(lǐng)域的任務(wù)上利用好原領(lǐng)域的數(shù)據(jù)。權(quán)重的調(diào)整的大概思路:以目標(biāo)域樣本評(píng)估訓(xùn)練損失,對(duì)于造成較大誤差的原領(lǐng)域的那部分樣本,則認(rèn)為其和目標(biāo)領(lǐng)域數(shù)據(jù)差異較大,不斷降低它的權(quán)重。而對(duì)于目標(biāo)領(lǐng)域分類(lèi)錯(cuò)誤的樣本則提升樣本權(quán)重(這個(gè)和Adaboost難樣本一脈相承)。核心代碼如下

"""" 附錄相關(guān)資料
論文地址:cse.hkust.edu.hk/~qyang/Docs/2007/tradaboost.pdf
代碼來(lái)源:github.com/loyalzc/transfer_learning/blob/master/TrAdaboost
論文解讀:by馬東什么zhuanlan.zhihu.com/p/109540481
""""
class TrAdaboost:
    def __init__(self, base_classifier=DecisionTreeClassifier(), N=10):
        self.base_classifier = base_classifier
        self.N = N
        self.beta_all = np.zeros([1, self.N])
        self.classifiers = []

    def fit(self, x_source, x_target, y_source, y_target):
        x_train = np.concatenate((x_source, x_target), axis=0)
        y_train = np.concatenate((y_source, y_target), axis=0)
        x_train = np.asarray(x_train, order='C')
        y_train = np.asarray(y_train, order='C')
        y_source = np.asarray(y_source, order='C')
        y_target = np.asarray(y_target, order='C')

        row_source = x_source.shape[0]
        row_target = x_target.shape[0]

        # 初始化權(quán)重
        weight_source = np.ones([row_source, 1]) / row_source
        weight_target = np.ones([row_target, 1]) / row_target
        weights = np.concatenate((weight_source, weight_target), axis=0)

        beta = 1 / (1 + np.sqrt(2 * np.log(row_source / self.N)))

        result = np.ones([row_source + row_target, self.N])
        for i in range(self.N):
            weights = self._calculate_weight(weights)
            self.base_classifier.fit(x_train, y_train, sample_weight=weights[:, 0])
            self.classifiers.append(self.base_classifier)

            result[:, i] = self.base_classifier.predict(x_train)
            error_rate = self._calculate_error_rate(y_target,
                                                    result[row_source:, i],
                                                    weights[row_source:, :])

            print("Error Rate in target data: ", error_rate, 'round:', i, 'all_round:', self.N)

            if error_rate > 0.5:
                error_rate = 0.5
            if error_rate == 0:
                self.N = i
                print("Early stopping...")
                break
            self.beta_all[0, i] = error_rate / (1 - error_rate)

            # 調(diào)整 target 樣本權(quán)重 錯(cuò)誤樣本權(quán)重變大
            for t in range(row_target):
                weights[row_source + t] = weights[row_source + t] * np.power(self.beta_all[0, i], -np.abs(result[row_source + t, i] - y_target[t]))
            # 調(diào)整 source 樣本 錯(cuò)分樣本變小
            for s in range(row_source):
                weights[s] = weights[s] * np.power(beta, np.abs(result[s, i] - y_source[s]))

    def predict(self, x_test):
        result = np.ones([x_test.shape[0], self.N + 1])
        predict = []

        i = 0
        for classifier in self.classifiers:
            y_pred = classifier.predict(x_test)
            result[:, i] = y_pred
            i += 1

        for i in range(x_test.shape[0]):
            left = np.sum(result[i, int(np.ceil(self.N / 2)): self.N] *
                          np.log(1 / self.beta_all[0, int(np.ceil(self.N / 2)):self.N]))

            right = 0.5 * np.sum(np.log(1 / self.beta_all[0, int(np.ceil(self.N / 2)): self.N]))

            if left >= right:
                predict.append(1)
            else:
                predict.append(0)
        return predict

    def predict_prob(self, x_test):
        result = np.ones([x_test.shape[0], self.N + 1])
        predict = []

        i = 0
        for classifier in self.classifiers:
            y_pred = classifier.predict(x_test)
            result[:, i] = y_pred
            i += 1

        for i in range(x_test.shape[0]):
            left = np.sum(result[i, int(np.ceil(self.N / 2)): self.N] *
                          np.log(1 / self.beta_all[0, int(np.ceil(self.N / 2)):self.N]))

            right = 0.5 * np.sum(np.log(1 / self.beta_all[0, int(np.ceil(self.N / 2)): self.N]))
            predict.append([left, right])
        return predict

    def _calculate_weight(self, weights):
        sum_weight = np.sum(weights)
        return np.asarray(weights / sum_weight, order='C')

    def _calculate_error_rate(self, y_target, y_predict, weight_target):
        sum_weight = np.sum(weight_target)
        return np.sum(weight_target[:, 0] / sum_weight * np.abs(y_target - y_predict))

2.2 基于特征的遷移

基于特征的遷移方法是指將源域和目標(biāo)域的數(shù)據(jù)特征變換到統(tǒng)一特征空間中,來(lái)減少源域和目標(biāo)域之間的差距。它基于這樣的假設(shè):“為了有效的遷移,良好的表征應(yīng)該是對(duì)主要學(xué)習(xí)任務(wù)的區(qū)別性,以及對(duì)源域和目標(biāo)域的不加區(qū)分。”

基于特征的遷移學(xué)習(xí)方法也是一大熱門(mén),大多是與神經(jīng)網(wǎng)絡(luò)的表示學(xué)習(xí)進(jìn)行結(jié)合,應(yīng)用于文本、圖像數(shù)據(jù)等任務(wù)。


對(duì)于結(jié)構(gòu)化數(shù)據(jù),個(gè)人理解基于特征的遷移通常是要結(jié)合樣本、模型層面的遷移。對(duì)應(yīng)著,可以通過(guò)特征加工轉(zhuǎn)換到同一分布減少些差異(如歸一化什么的),以及可以做下特征選擇,尋找既適用于源域又適用于目標(biāo)域的可遷移特征。比如可以通過(guò)特征重要性及穩(wěn)定性PSI進(jìn)行篩選,在原領(lǐng)域 與目標(biāo)領(lǐng)域的分布比較一致的特征。PSI表示的是實(shí)際分布與預(yù)期兩個(gè)分布間的差異,PSI=SUM( (實(shí)際占比 - 預(yù)期占比)* ln(實(shí)際占比 / 預(yù)期占比) )。特征選擇方法可以參考這篇《特征選擇》

特征處理完,再進(jìn)一步選擇合適的樣本或模型遷移方法。

2.3 基于模型的遷移

基于模型的方法是NLP、CV領(lǐng)域較為普遍的思路,通過(guò)大數(shù)據(jù)訓(xùn)一個(gè)預(yù)訓(xùn)練模型,然后下游任務(wù)只需要微調(diào)下就可以用了。


而對(duì)于結(jié)構(gòu)化數(shù)據(jù)任務(wù),這里我們可以采用熟悉的模型融合方式,簡(jiǎn)單高效地利用好原領(lǐng)域的這部分?jǐn)?shù)據(jù)信息,基于模型的方法很大程度也包含了特征層面的加工及遷移。

一個(gè)融合的思路是,我們可以先以原領(lǐng)域的數(shù)據(jù)訓(xùn)練模型A,將模型A的預(yù)測(cè)概率作為特征加入 以目標(biāo)領(lǐng)域訓(xùn)練的模型。這個(gè)思路還是和預(yù)訓(xùn)練微調(diào)很像的。

# 先從原領(lǐng)域?qū)W習(xí)一個(gè)模型
lgb_source=lightgbm.LGBMClassifier(n_estimators=100, num_leaves=6,class_weight= 'balanced',metric = 'AUC',lambda_l1=1,lambda_l2=1) 

lgb_source.fit(train_source.loc[:,filter_feas], train_source.loc[:,'isDefault'])


# 原領(lǐng)域模型評(píng)分作為特征輸入目標(biāo)領(lǐng)域模型
train_bank['source_score'] = lgb_source.predict_proba(train_bank.loc[:,filter_feas])[:,1]
train_x, test_x, train_y, test_y = train_test_split(train_bank[filter_feas+['source_score']], train_bank.isDefault,test_size=0.3, random_state=0)


lgb_tar=lightgbm.LGBMClassifier(n_estimators=100, num_leaves=6,class_weight= 'balanced',metric = 'AUC',lambda_l1=1,lambda_l2=1) 
lgb_tar.fit(train_x, train_y)
print(train_y.value_counts())
print('train ',model_metrics(lgb_tar,train_x, train_y))
print('test ',model_metrics(lgb_tar,test_x,test_y))
## 輸出特征重要性
from lightgbm import plot_importance

plot_importance(lgb_tar)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,401評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,011評(píng)論 3 413
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,263評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,543評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,323評(píng)論 6 404
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,874評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,968評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,095評(píng)論 0 286
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,605評(píng)論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,551評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,720評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,242評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,961評(píng)論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,358評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,612評(píng)論 1 280
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,330評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,690評(píng)論 2 370

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