轉(zhuǎn)載--結(jié)合Scikit-learn介紹幾種常用的特征選擇方法

作者: Edwin Jarvis

特征選擇(排序)對于數(shù)據(jù)科學(xué)家寨昙、機器學(xué)習(xí)從業(yè)者來說非常重要。好的特征選擇能夠提升模型的性能,更能幫助我們理解數(shù)據(jù)的特點吼砂、底層結(jié)構(gòu),這對進一步改善模型鼎文、算法都有著重要作用渔肩。

特征選擇主要有兩個功能:

  1. 減少特征數(shù)量、降維拇惋,使模型泛化能力更強周偎,減少過擬合
  2. 增強對特征和特征值之間的理解

拿到數(shù)據(jù)集,一個特征選擇方法撑帖,往往很難同時完成這兩個目的蓉坎。通常情況下,我們經(jīng)常不管三七二十一磷仰,選擇一種自己最熟悉或者最方便的特征選擇方法(往往目的是降維袍嬉,而忽略了對特征和數(shù)據(jù)理解的目的)。

在許多機器學(xué)習(xí)相關(guān)的書里灶平,很難找到關(guān)于特征選擇的內(nèi)容伺通,因為特征選擇要解決的問題往往被視為機器學(xué)習(xí)的一種副作用,一般不會單獨拿出來討論逢享。

本文將結(jié)合 Scikit-learn提供的例子 介紹幾種常用的特征選擇方法罐监,它們各自的優(yōu)缺點和問題。

1 去掉取值變化小的特征 Removing features with low variance

這應(yīng)該是最簡單的特征選擇方法了:假設(shè)某特征的特征值只有0和1瞒爬,并且在所有輸入樣本中弓柱,95%的實例的該特征取值都是1,那就可以認為這個特征作用不大侧但。如果100%都是1矢空,那這個特征就沒意義了。當(dāng)特征值都是離散型變量的時候這種方法才能用禀横,如果是連續(xù)型變量屁药,就需要將連續(xù)變量離散化之后才能用,而且實際當(dāng)中柏锄,一般不太會有95%以上都取某個值的特征存在酿箭,所以這種方法雖然簡單但是不太好用复亏。可以把它作為特征選擇的預(yù)處理缭嫡,先去掉那些取值變化小的特征缔御,然后再從接下來提到的的特征選擇方法中選擇合適的進行進一步的特征選擇。

2 單變量特征選擇 Univariate feature selection

單變量特征選擇能夠?qū)γ恳粋€特征進行測試妇蛀,衡量該特征和響應(yīng)變量之間的關(guān)系耕突,根據(jù)得分扔掉不好的特征。對于回歸和分類問題可以采用卡方檢驗等方式對特征進行測試评架。

這種方法比較簡單有勾,易于運行,易于理解古程,通常對于理解數(shù)據(jù)有較好的效果(但對特征優(yōu)化、提高泛化能力來說不一定有效)喊崖;這種方法有許多改進的版本挣磨、變種。

2.1 Pearson相關(guān)系數(shù) Pearson Correlation

皮爾森相關(guān)系數(shù)是一種最簡單的荤懂,能幫助理解特征和響應(yīng)變量之間關(guān)系的方法茁裙,該方法衡量的是變量之間的線性相關(guān)性,結(jié)果的取值區(qū)間為[-1节仿,1]晤锥,-1表示完全的負相關(guān)(這個變量下降,那個就會上升)廊宪,+1表示完全的正相關(guān)矾瘾,0表示沒有線性相關(guān)。

Pearson Correlation速度快箭启、易于計算壕翩,經(jīng)常在拿到數(shù)據(jù)(經(jīng)過清洗和特征提取之后的)之后第一時間就執(zhí)行。Scipy的 pearsonr 方法能夠同時計算相關(guān)系數(shù)和p-value傅寡,

import numpy as np
from scipy.stats import pearsonr
np.random.seed(0)
size = 300
x = np.random.normal(0, 1, size)
print "Lower noise", pearsonr(x, x + np.random.normal(0, 1, size))
print "Higher noise", pearsonr(x, x + np.random.normal(0, 10, size))

Lower noise (0.71824836862138386, 7.3240173129992273e-49)
Higher noise (0.057964292079338148, 0.31700993885324746)

這個例子中放妈,我們比較了變量在加入噪音之前和之后的差異。當(dāng)噪音比較小的時候荐操,相關(guān)性很強芜抒,p-value很低。

Scikit-learn提供的 f_regrssion 方法能夠批量計算特征的p-value托启,非常方便宅倒,參考sklearn的 pipeline

Pearson相關(guān)系數(shù)的一個明顯缺陷是,作為特征排序機制驾中,他只對線性關(guān)系敏感唉堪。如果關(guān)系是非線性的模聋,即便兩個變量具有一一對應(yīng)的關(guān)系,Pearson相關(guān)性也可能會接近0唠亚。

x = np.random.uniform(-1, 1, 100000)
print pearsonr(x, x**2)[0]

-0.00230804707612

更多類似的例子參考 sample plots 链方。另外,如果僅僅根據(jù)相關(guān)系數(shù)這個值來判斷的話灶搜,有時候會具有很強的誤導(dǎo)性祟蚀,如 Anscombe’s quartet ,最好把數(shù)據(jù)可視化出來割卖,以免得出錯誤的結(jié)論前酿。

2.2 互信息和最大信息系數(shù) Mutual information and maximal information coefficient (MIC)

以上就是經(jīng)典的互信息公式了。想把互信息直接用于特征選擇其實不是太方便:

  1. 它不屬于度量方式鹏溯,也沒有辦法歸一化罢维,在不同數(shù)據(jù)及上的結(jié)果無法做比較;
  2. 對于連續(xù)變量的計算不是很方便(X和Y都是集合丙挽,x肺孵,y都是離散的取值),通常變量需要先離散化颜阐,而互信息的結(jié)果對離散化的方式很敏感平窘。

最大信息系數(shù)克服了這兩個問題。它首先尋找一種最優(yōu)的離散化方式凳怨,然后把互信息取值轉(zhuǎn)換成一種度量方式瑰艘,取值區(qū)間在[0,1]肤舞。 minepy 提供了MIC功能紫新。

反過頭來看y=x^2這個例子,MIC算出來的互信息值為1(最大的取值)萨赁。

from minepy import MINE
m = MINE()
x = np.random.uniform(-1, 1, 10000)
m.compute_score(x, x**2)
print m.mic()

1.0

MIC的統(tǒng)計能力遭到了 一些質(zhì)疑 弊琴,當(dāng)零假設(shè)不成立時,MIC的統(tǒng)計就會受到影響杖爽。在有的數(shù)據(jù)集上不存在這個問題敲董,但有的數(shù)據(jù)集上就存在這個問題。

2.3 距離相關(guān)系數(shù) (Distance correlation)

距離相關(guān)系數(shù)是為了克服Pearson相關(guān)系數(shù)的弱點而生的慰安。在x和x^2這個例子中腋寨,即便Pearson相關(guān)系數(shù)是0,我們也不能斷定這兩個變量是獨立的(有可能是非線性相關(guān))化焕;但如果距離相關(guān)系數(shù)是0萄窜,那么我們就可以說這兩個變量是獨立的。

R的 energy 包里提供了距離相關(guān)系數(shù)的實現(xiàn),另外這是 Python gist 的實現(xiàn)查刻。

#R-code
>  x  =  runif  (1000,  -1,  1)
>  dcor(x,  x**2)
[1] 0.4943864

盡管有MIC和距離相關(guān)系數(shù)在了键兜,但當(dāng)變量之間的關(guān)系接近線性相關(guān)的時候,Pearson相關(guān)系數(shù)仍然是不可替代的穗泵。第一普气、Pearson相關(guān)系數(shù)計算速度快,這在處理大規(guī)模數(shù)據(jù)的時候很重要佃延。第二现诀、Pearson相關(guān)系數(shù)的取值區(qū)間是[-1,1]履肃,而MIC和距離相關(guān)系數(shù)都是[0仔沿,1]。這個特點使得Pearson相關(guān)系數(shù)能夠表征更豐富的關(guān)系尺棋,符號表示關(guān)系的正負封锉,絕對值能夠表示強度。當(dāng)然膘螟,Pearson相關(guān)性有效的前提是兩個變量的變化關(guān)系是單調(diào)的烘浦。

2.4 基于學(xué)習(xí)模型的特征排序 (Model based ranking)

這種方法的思路是直接使用你要用的機器學(xué)習(xí)算法,針對每個單獨的特征和響應(yīng)變量建立預(yù)測模型萍鲸。其實Pearson相關(guān)系數(shù)等價于線性回歸里的標(biāo)準(zhǔn)化回歸系數(shù)。假如某個特征和響應(yīng)變量之間的關(guān)系是非線性的擦俐,可以用基于樹的方法(決策樹脊阴、隨機森林)、或者擴展的線性模型等蚯瞧『倨冢基于樹的方法比較易于使用,因為他們對非線性關(guān)系的建模比較好埋合,并且不需要太多的調(diào)試备徐。但要注意過擬合問題,因此樹的深度最好不要太大甚颂,再就是運用交叉驗證蜜猾。

波士頓房價數(shù)據(jù)集 上使用sklearn的 隨機森林回歸 給出一個單變量選擇的例子:

from sklearn.cross_validation import cross_val_score, ShuffleSplit
from sklearn.datasets import load_boston
from sklearn.ensemble import RandomForestRegressor

#Load boston housing dataset as an example
boston = load_boston()
X = boston["data"]
Y = boston["target"]
names = boston["feature_names"]

rf = RandomForestRegressor(n_estimators=20, max_depth=4)
scores = []
for i in range(X.shape[1]):
     score = cross_val_score(rf, X[:, i:i+1], Y, scoring="r2",
                              cv=ShuffleSplit(len(X), 3, .3))
     scores.append((round(np.mean(score), 3), names[i]))
print sorted(scores, reverse=True)

[(0.636, ‘LSTAT’), (0.59, ‘RM’), (0.472, ‘NOX’), (0.369, ‘INDUS’), (0.311, ‘PTRATIO’), (0.24, ‘TAX’), (0.24, ‘CRIM’), (0.185, ‘RAD’), (0.16, ‘ZN’), (0.087, ‘B’), (0.062, ‘DIS’), (0.036, ‘CHAS’), (0.027, ‘AGE’)]

3 線性模型和正則化

單變量特征選擇方法獨立的衡量每個特征與響應(yīng)變量之間的關(guān)系,另一種主流的特征選擇方法是基于機器學(xué)習(xí)模型的方法振诬。有些機器學(xué)習(xí)方法本身就具有對特征進行打分的機制蹭睡,或者很容易將其運用到特征選擇任務(wù)中,例如回歸模型赶么,SVM肩豁,決策樹,隨機森林等等。說句題外話清钥,這種方法好像在一些地方叫做wrapper類型琼锋,大概意思是說,特征排序模型和機器學(xué)習(xí)模型是耦盒在一起的祟昭,對應(yīng)的非wrapper類型的特征選擇方法叫做filter類型缕坎。

下面將介紹如何用回歸模型的系數(shù)來選擇特征。越是重要的特征在模型中對應(yīng)的系數(shù)就會越大从橘,而跟輸出變量越是無關(guān)的特征對應(yīng)的系數(shù)就會越接近于0念赶。在噪音不多的數(shù)據(jù)上,或者是數(shù)據(jù)量遠遠大于特征數(shù)的數(shù)據(jù)上恰力,如果特征之間相對來說是比較獨立的叉谜,那么即便是運用最簡單的線性回歸模型也一樣能取得非常好的效果。

from sklearn.linear_model import LinearRegression
import numpy as np
np.random.seed(0)
size = 5000
#A  dataset  with  3  features
X = np.random.normal(0, 1, (size, 3))
#Y  =  X0  +  2*X1  +  noise
Y = X[:,0] + 2*X[:,1] + np.random.normal(0, 2, size)
lr = LinearRegression()
lr.fit(X, Y)
#A  helper  method  for  pretty-printing  linear  models
def  pretty_print_linear(coefs,  names  =  None,  sort  =  False):
    if names == None:
        names = ["X%s" % x for x in range(len(coefs))]
    lst = zip(coefs, names)
    if sort:
        lst = sorted(lst,  key = lambda x:-np.abs(x[0]))
    return " + ".join("%s * %s" % (round(coef, 3), name)
                                   for coef, name in lst)
print "Linear model:", pretty_print_linear(lr.coef_)

Linear model: 0.984 * X0 + 1.995 * X1 + -0.041 * X2

在這個例子當(dāng)中踩萎,盡管數(shù)據(jù)中存在一些噪音停局,但這種特征選擇模型仍然能夠很好的體現(xiàn)出數(shù)據(jù)的底層結(jié)構(gòu)。當(dāng)然這也是因為例子中的這個問題非常適合用線性模型來解:特征和響應(yīng)變量之間全都是線性關(guān)系香府,并且特征之間均是獨立的董栽。

在很多實際的數(shù)據(jù)當(dāng)中,往往存在多個互相關(guān)聯(lián)的特征企孩,這時候模型就會變得不穩(wěn)定锭碳,數(shù)據(jù)中細微的變化就可能導(dǎo)致模型的巨大變化(模型的變化本質(zhì)上是系數(shù),或者叫參數(shù)勿璃,可以理解成W)擒抛,這會讓模型的預(yù)測變得困難,這種現(xiàn)象也稱為多重共線性补疑。例如歧沪,假設(shè)我們有個數(shù)據(jù)集,它的真實模型應(yīng)該是Y=X1+X2莲组,當(dāng)我們觀察的時候诊胞,發(fā)現(xiàn)Y’=X1+X2+e,e是噪音锹杈。如果X1和X2之間存在線性關(guān)系撵孤,例如X1約等于X2,這個時候由于噪音e的存在竭望,我們學(xué)到的模型可能就不是Y=X1+X2了早直,有可能是Y=2X1,或者Y=-X1+3X2市框。

下邊這個例子當(dāng)中霞扬,在同一個數(shù)據(jù)上加入了一些噪音,用隨機森林算法進行特征選擇。

from sklearn.linear_model import LinearRegression

size = 100
np.random.seed(seed=5)

X_seed = np.random.normal(0, 1, size)
X1 = X_seed + np.random.normal(0, .1, size)
X2 = X_seed + np.random.normal(0, .1, size)
X3 = X_seed + np.random.normal(0, .1, size)

Y = X1 + X2 + X3 + np.random.normal(0,1, size)
X = np.array([X1, X2, X3]).T

lr = LinearRegression()
lr.fit(X,Y)
print "Linear model:", pretty_print_linear(lr.coef_)

Linear model: -1.291 * X0 + 1.591 * X1 + 2.747 * X2

系數(shù)之和接近3喻圃,基本上和上上個例子的結(jié)果一致萤彩,應(yīng)該說學(xué)到的模型對于預(yù)測來說還是不錯的。但是斧拍,如果從系數(shù)的字面意思上去解釋特征的重要性的話雀扶,X3對于輸出變量來說具有很強的正面影響,而X1具有負面影響肆汹,而實際上所有特征與輸出變量之間的影響是均等的愚墓。

同樣的方法和套路可以用到類似的線性模型上,比如邏輯回歸昂勉。

3.1 正則化模型

正則化就是把額外的約束或者懲罰項加到已有模型(損失函數(shù))上浪册,以防止過擬合并提高泛化能力。損失函數(shù)由原來的E(X,Y)變?yōu)镋(X,Y)+alpha||w||岗照,w是模型系數(shù)組成的向量(有些地方也叫參數(shù)parameter村象,coefficients),||·||一般是L1或者L2范數(shù)攒至,alpha是一個可調(diào)的參數(shù)厚者,控制著正則化的強度。當(dāng)用在線性模型上時迫吐,L1正則化和L2正則化也稱為Lasso和Ridge库菲。

3.2 L1正則化/Lasso

L1正則化將系數(shù)w的l1范數(shù)作為懲罰項加到損失函數(shù)上,由于正則項非零志膀,這就迫使那些弱的特征所對應(yīng)的系數(shù)變成0蝙昙。因此L1正則化往往會使學(xué)到的模型很稀疏(系數(shù)w經(jīng)常為0),這個特性使得L1正則化成為一種很好的特征選擇方法梧却。

Scikit-learn為線性回歸提供了Lasso,為分類提供了L1邏輯回歸败去。

下面的例子在波士頓房價數(shù)據(jù)上運行了Lasso放航,其中參數(shù)alpha是通過grid search進行優(yōu)化的。

from sklearn.linear_model import Lasso
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_boston

boston = load_boston()
scaler = StandardScaler()
X = scaler.fit_transform(boston["data"])
Y = boston["target"]
names = boston["feature_names"]

lasso = Lasso(alpha=.3)
lasso.fit(X, Y)

print "Lasso model: ", pretty_print_linear(lasso.coef_, names, sort = True)

Lasso model: -3.707 * LSTAT + 2.992 * RM + -1.757 * PTRATIO + -1.081 * DIS + -0.7 * NOX + 0.631 * B + 0.54 * CHAS + -0.236 * CRIM + 0.081 * ZN + -0.0 * INDUS + -0.0 * AGE + 0.0 * RAD + -0.0 * TAX

可以看到圆裕,很多特征的系數(shù)都是0广鳍。如果繼續(xù)增加alpha的值,得到的模型就會越來越稀疏吓妆,即越來越多的特征系數(shù)會變成0赊时。

然而,L1正則化像非正則化線性模型一樣也是不穩(wěn)定的行拢,如果特征集合中具有相關(guān)聯(lián)的特征祖秒,當(dāng)數(shù)據(jù)發(fā)生細微變化時也有可能導(dǎo)致很大的模型差異。

3.3 L2正則化/Ridge regression

L2正則化將系數(shù)向量的L2范數(shù)添加到了損失函數(shù)中。由于L2懲罰項中系數(shù)是二次方的竭缝,這使得L2和L1有著諸多差異房维,最明顯的一點就是,L2正則化會讓系數(shù)的取值變得平均抬纸。對于關(guān)聯(lián)特征咙俩,這意味著他們能夠獲得更相近的對應(yīng)系數(shù)。還是以Y=X1+X2為例湿故,假設(shè)X1和X2具有很強的關(guān)聯(lián)阿趁,如果用L1正則化,不論學(xué)到的模型是Y=X1+X2還是Y=2X1坛猪,懲罰都是一樣的脖阵,都是2 alpha。但是對于L2來說砚哆,第一個模型的懲罰項是2 alpha独撇,但第二個模型的是4*alpha≡晁可以看出纷铣,系數(shù)之和為常數(shù)時,各系數(shù)相等時懲罰是最小的战转,所以才有了L2會讓各個系數(shù)趨于相同的特點搜立。

可以看出,L2正則化對于特征選擇來說一種穩(wěn)定的模型槐秧,不像L1正則化那樣啄踊,系數(shù)會因為細微的數(shù)據(jù)變化而波動。所以L2正則化和L1正則化提供的價值是不同的刁标,L2正則化對于特征理解來說更加有用:表示能力強的特征對應(yīng)的系數(shù)是非零颠通。

回過頭來看看3個互相關(guān)聯(lián)的特征的例子,分別以10個不同的種子隨機初始化運行10次膀懈,來觀察L1和L2正則化的穩(wěn)定性顿锰。

from sklearn.linear_model import Ridge
from sklearn.metrics import r2_score
size = 100
#We run the method 10 times with different random seeds
for i in range(10):
    print "Random seed %s" % i
    np.random.seed(seed=i)
    X_seed = np.random.normal(0, 1, size)
    X1 = X_seed + np.random.normal(0, .1, size)
    X2 = X_seed + np.random.normal(0, .1, size)
    X3 = X_seed + np.random.normal(0, .1, size)
    Y = X1 + X2 + X3 + np.random.normal(0, 1, size)
    X = np.array([X1, X2, X3]).T
    lr = LinearRegression()
    lr.fit(X,Y)
    print "Linear model:", pretty_print_linear(lr.coef_)
    ridge = Ridge(alpha=10)
    ridge.fit(X,Y)
    print "Ridge model:", pretty_print_linear(ridge.coef_)
    print

Random seed 0 Linear model: 0.728 * X0 + 2.309 * X1 + -0.082 * X2 Ridge model: 0.938 * X0 + 1.059 * X1 + 0.877 * X2

Random seed 1 Linear model: 1.152 * X0 + 2.366 * X1 + -0.599 * X2 Ridge model: 0.984 * X0 + 1.068 * X1 + 0.759 * X2

Random seed 2 Linear model: 0.697 * X0 + 0.322 * X1 + 2.086 * X2 Ridge model: 0.972 * X0 + 0.943 * X1 + 1.085 * X2

Random seed 3 Linear model: 0.287 * X0 + 1.254 * X1 + 1.491 * X2 Ridge model: 0.919 * X0 + 1.005 * X1 + 1.033 * X2

Random seed 4 Linear model: 0.187 * X0 + 0.772 * X1 + 2.189 * X2 Ridge model: 0.964 * X0 + 0.982 * X1 + 1.098 * X2

Random seed 5 Linear model: -1.291 * X0 + 1.591 * X1 + 2.747 * X2 Ridge model: 0.758 * X0 + 1.011 * X1 + 1.139 * X2

Random seed 6 Linear model: 1.199 * X0 + -0.031 * X1 + 1.915 * X2 Ridge model: 1.016 * X0 + 0.89 * X1 + 1.091 * X2

Random seed 7 Linear model: 1.474 * X0 + 1.762 * X1 + -0.151 * X2 Ridge model: 1.018 * X0 + 1.039 * X1 + 0.901 * X2

Random seed 8 Linear model: 0.084 * X0 + 1.88 * X1 + 1.107 * X2 Ridge model: 0.907 * X0 + 1.071 * X1 + 1.008 * X2

Random seed 9 Linear model: 0.714 * X0 + 0.776 * X1 + 1.364 * X2 Ridge model: 0.896 * X0 + 0.903 * X1 + 0.98 * X2

可以看出,不同的數(shù)據(jù)上線性回歸得到的模型(系數(shù))相差甚遠启搂,但對于L2正則化模型來說硼控,結(jié)果中的系數(shù)非常的穩(wěn)定,差別較小胳赌,都比較接近于1牢撼,能夠反映出數(shù)據(jù)的內(nèi)在結(jié)構(gòu)。

4 隨機森林

隨機森林具有準(zhǔn)確率高疑苫、魯棒性好熏版、易于使用等優(yōu)點纷责,這使得它成為了目前最流行的機器學(xué)習(xí)算法之一。隨機森林提供了兩種特征選擇的方法:mean decrease impurity和mean decrease accuracy纳决。

4.1 平均不純度減少 mean decrease impurity

隨機森林由多個決策樹構(gòu)成碰逸。決策樹中的每一個節(jié)點都是關(guān)于某個特征的條件,為的是將數(shù)據(jù)集按照不同的響應(yīng)變量一分為二阔加。利用不純度可以確定節(jié)點(最優(yōu)條件)饵史,對于分類問題,通常采用 基尼不純度 或者 信息增益 胜榔,對于回歸問題胳喷,通常采用的是 方差 或者最小二乘擬合。當(dāng)訓(xùn)練決策樹的時候夭织,可以計算出每個特征減少了多少樹的不純度吭露。對于一個決策樹森林來說,可以算出每個特征平均減少了多少不純度尊惰,并把它平均減少的不純度作為特征選擇的值讲竿。

下邊的例子是sklearn中基于隨機森林的特征重要度度量方法:

from sklearn.datasets import load_boston
from sklearn.ensemble import RandomForestRegressor
import numpy as np
#Load boston housing dataset as an example
boston = load_boston()
X = boston["data"]
Y = boston["target"]
names = boston["feature_names"]
rf = RandomForestRegressor()
rf.fit(X, Y)
print "Features sorted by their score:"
print sorted(zip(map(lambda x: round(x, 4), rf.feature_importances_), names), 
             reverse=True)

Features sorted by their score: [(0.5298, ‘LSTAT’), (0.4116, ‘RM’), (0.0252, ‘DIS’), (0.0172, ‘CRIM’), (0.0065, ‘NOX’), (0.0035, ‘PTRATIO’), (0.0021, ‘TAX’), (0.0017, ‘AGE’), (0.0012, ‘B’), (0.0008, ‘INDUS’), (0.0004, ‘RAD’), (0.0001, ‘CHAS’), (0.0, ‘ZN’)]

這里特征得分實際上采用的是 Gini Importance 。使用基于不純度的方法的時候弄屡,要記滋赓鳌:1、這種方法存在 偏向 膀捷,對具有更多類別的變量會更有利迈嘹;2、對于存在關(guān)聯(lián)的多個特征全庸,其中任意一個都可以作為指示器(優(yōu)秀的特征)秀仲,并且一旦某個特征被選擇之后,其他特征的重要度就會急劇下降壶笼,因為不純度已經(jīng)被選中的那個特征降下來了神僵,其他的特征就很難再降低那么多不純度了,這樣一來覆劈,只有先被選中的那個特征重要度很高保礼,其他的關(guān)聯(lián)特征重要度往往較低。在理解數(shù)據(jù)時墩崩,這就會造成誤解,導(dǎo)致錯誤的認為先被選中的特征是很重要的侯勉,而其余的特征是不重要的鹦筹,但實際上這些特征對響應(yīng)變量的作用確實非常接近的(這跟Lasso是很像的)。

特征隨機選擇 方法稍微緩解了這個問題址貌,但總的來說并沒有完全解決铐拐。下面的例子中徘键,X0、X1遍蟋、X2是三個互相關(guān)聯(lián)的變量吹害,在沒有噪音的情況下,輸出變量是三者之和虚青。

size = 10000
np.random.seed(seed=10)
X_seed = np.random.normal(0, 1, size)
X0 = X_seed + np.random.normal(0, .1, size)
X1 = X_seed + np.random.normal(0, .1, size)
X2 = X_seed + np.random.normal(0, .1, size)
X = np.array([X0, X1, X2]).T
Y = X0 + X1 + X2

rf = RandomForestRegressor(n_estimators=20, max_features=2)
rf.fit(X, Y);
print "Scores for X0, X1, X2:", map(lambda x:round (x,3),
                                    rf.feature_importances_)

Scores for X0, X1, X2: [0.278, 0.66, 0.062]

當(dāng)計算特征重要性時它呀,可以看到X1的重要度比X2的重要度要高出10倍,但實際上他們真正的重要度是一樣的棒厘。盡管數(shù)據(jù)量已經(jīng)很大且沒有噪音纵穿,且用了20棵樹來做隨機選擇,但這個問題還是會存在奢人。

需要注意的一點是谓媒,關(guān)聯(lián)特征的打分存在不穩(wěn)定的現(xiàn)象,這不僅僅是隨機森林特有的何乎,大多數(shù)基于模型的特征選擇方法都存在這個問題句惯。

4.2 平均精確率減少 Mean decrease accuracy

另一種常用的特征選擇方法就是直接度量每個特征對模型精確率的影響。主要思路是打亂每個特征的特征值順序支救,并且度量順序變動對模型的精確率的影響抢野。很明顯,對于不重要的變量來說搂妻,打亂順序?qū)δP偷木_率影響不會太大蒙保,但是對于重要的變量來說,打亂順序就會降低模型的精確率欲主。

這個方法sklearn中沒有直接提供邓厕,但是很容易實現(xiàn),下面繼續(xù)在波士頓房價數(shù)據(jù)集上進行實現(xiàn)扁瓢。

from sklearn.cross_validation import ShuffleSplit
from sklearn.metrics import r2_score
from collections import defaultdict
X = boston["data"]
Y = boston["target"]
rf = RandomForestRegressor()
scores = defaultdict(list)
#crossvalidate the scores on a number of different random splits of the data
for train_idx, test_idx in ShuffleSplit(len(X), 100, .3):
    X_train, X_test = X[train_idx], X[test_idx]
    Y_train, Y_test = Y[train_idx], Y[test_idx]
    r = rf.fit(X_train, Y_train)
    acc = r2_score(Y_test, rf.predict(X_test))
    for i in range(X.shape[1]):
        X_t = X_test.copy()
        np.random.shuffle(X_t[:, i])
        shuff_acc = r2_score(Y_test, rf.predict(X_t))
        scores[names[i]].append((acc-shuff_acc)/acc)
print "Features sorted by their score:"
print sorted([(round(np.mean(score), 4), feat) for
              feat, score in scores.items()], reverse=True)

Features sorted by their score: [(0.7276, ‘LSTAT’), (0.5675, ‘RM’), (0.0867, ‘DIS’), (0.0407, ‘NOX’), (0.0351, ‘CRIM’), (0.0233, ‘PTRATIO’), (0.0168, ‘TAX’), (0.0122, ‘AGE’), (0.005, ‘B’), (0.0048, ‘INDUS’), (0.0043, ‘RAD’), (0.0004, ‘ZN’), (0.0001, ‘CHAS’)]

在這個例子當(dāng)中详恼,LSTAT和RM這兩個特征對模型的性能有著很大的影響,打亂這兩個特征的特征值使得模型的性能下降了73%和57%引几。注意昧互,盡管這些我們是在所有特征上進行了訓(xùn)練得到了模型,然后才得到了每個特征的重要性測試伟桅,這并不意味著我們?nèi)拥裟硞€或者某些重要特征后模型的性能就一定會下降很多敞掘,因為即便某個特征刪掉之后,其關(guān)聯(lián)特征一樣可以發(fā)揮作用楣铁,讓模型性能基本上不變玖雁。

5 兩種頂層特征選擇算法

之所以叫做頂層,是因為他們都是建立在基于模型的特征選擇方法基礎(chǔ)之上的盖腕,例如回歸和SVM赫冬,在不同的子集上建立模型浓镜,然后匯總最終確定特征得分。

5.1 穩(wěn)定性選擇 Stability selection

穩(wěn)定性選擇是一種基于二次抽樣和選擇算法相結(jié)合較新的方法劲厌,選擇算法可以是回歸膛薛、SVM或其他類似的方法。它的主要思想是在不同的數(shù)據(jù)子集和特征子集上運行特征選擇算法补鼻,不斷的重復(fù)哄啄,最終匯總特征選擇結(jié)果,比如可以統(tǒng)計某個特征被認為是重要特征的頻率(被選為重要特征的次數(shù)除以它所在的子集被測試的次數(shù))辽幌。理想情況下增淹,重要特征的得分會接近100%。稍微弱一點的特征得分會是非0的數(shù)乌企,而最無用的特征得分將會接近于0虑润。

sklearn在 隨機lasso隨機邏輯回歸 中有對穩(wěn)定性選擇的實現(xiàn)。

from sklearn.datasets import load_boston
boston = load_boston()

#using the Boston housing data. 
#Data gets scaled automatically by sklearn's implementation
X = boston["data"]
Y = boston["target"]
names = boston["feature_names"]

rlasso = RandomizedLasso(alpha=0.025)
rlasso.fit(X, Y)

print "Features sorted by their score:"
print sorted(zip(map(lambda x: round(x, 4), rlasso.scores_), 
                 names), reverse=True)

Features sorted by their score: [(1.0, ‘RM’), (1.0, ‘PTRATIO’), (1.0, ‘LSTAT’), (0.62, ‘CHAS’), (0.595, ‘B’), (0.39, ‘TAX’), (0.385, ‘CRIM’), (0.25, ‘DIS’), (0.22, ‘NOX’), (0.125, ‘INDUS’), (0.045, ‘ZN’), (0.02, ‘RAD’), (0.015, ‘AGE’)]

在上邊這個例子當(dāng)中加酵,最高的3個特征得分是1.0脖祈,這表示他們總會被選作有用的特征(當(dāng)然毕源,得分會收到正則化參數(shù)alpha的影響掺栅,但是sklearn的隨機lasso能夠自動選擇最優(yōu)的alpha)馏颂。接下來的幾個特征得分就開始下降,但是下降的不是特別急劇陋葡,這跟純lasso的方法和隨機森林的結(jié)果不一樣亚亲。能夠看出穩(wěn)定性選擇對于克服過擬合和對數(shù)據(jù)理解來說都是有幫助的:總的來說,好的特征不會因為有相似的特征腐缤、關(guān)聯(lián)特征而得分為0捌归,這跟Lasso是不同的。對于特征選擇任務(wù)岭粤,在許多數(shù)據(jù)集和環(huán)境下惜索,穩(wěn)定性選擇往往是性能最好的方法之一。

5.2 遞歸特征消除 Recursive feature elimination (RFE)

遞歸特征消除的主要思想是反復(fù)的構(gòu)建模型(如SVM或者回歸模型)然后選出最好的(或者最差的)的特征(可以根據(jù)系數(shù)來選)剃浇,把選出來的特征放到一遍巾兆,然后在剩余的特征上重復(fù)這個過程,直到所有特征都遍歷了虎囚。這個過程中特征被消除的次序就是特征的排序角塑。因此,這是一種尋找最優(yōu)特征子集的貪心算法淘讥。

RFE的穩(wěn)定性很大程度上取決于在迭代的時候底層用哪種模型圃伶。例如,假如RFE采用的普通的回歸适揉,沒有經(jīng)過正則化的回歸是不穩(wěn)定的留攒,那么RFE就是不穩(wěn)定的;假如采用的是Ridge嫉嘀,而用Ridge正則化的回歸是穩(wěn)定的炼邀,那么RFE就是穩(wěn)定的。

Sklearn提供了 RFE 包剪侮,可以用于特征消除拭宁,還提供了 RFECV ,可以通過交叉驗證來對的特征進行排序瓣俯。

from sklearn.feature_selection import RFE
from sklearn.linear_model import LinearRegression

boston = load_boston()
X = boston["data"]
Y = boston["target"]
names = boston["feature_names"]

#use linear regression as the model
lr = LinearRegression()
#rank all features, i.e continue the elimination until the last one
rfe = RFE(lr, n_features_to_select=1)
rfe.fit(X,Y)

print "Features sorted by their rank:"
print sorted(zip(map(lambda x: round(x, 4), rfe.ranking_), names))

Features sorted by their rank: [(1.0, ‘NOX’), (2.0, ‘RM’), (3.0, ‘CHAS’), (4.0, ‘PTRATIO’), (5.0, ‘DIS’), (6.0, ‘LSTAT’), (7.0, ‘RAD’), (8.0, ‘CRIM’), (9.0, ‘INDUS’), (10.0, ‘ZN’), (11.0, ‘TAX’), (12.0, ‘B’), (13.0, ‘AGE’)]

6 一個完整的例子

下面將本文所有提到的方法進行實驗對比杰标,數(shù)據(jù)集采用Friedman #1 回歸數(shù)據(jù)( 這篇論文 中的數(shù)據(jù))。數(shù)據(jù)是用這個公式產(chǎn)生的:

X1到X5是由 單變量分布 生成的彩匕,e是 標(biāo)準(zhǔn)正態(tài)變量 N(0,1)腔剂。另外,原始的數(shù)據(jù)集中含有5個噪音變量 X5,…,X10驼仪,跟響應(yīng)變量是獨立的掸犬。我們增加了4個額外的變量X11,…X14,分別是X1,…,X4的關(guān)聯(lián)變量绪爸,通過f(x)=x+N(0,0.01)生成湾碎,這將產(chǎn)生大于0.999的關(guān)聯(lián)系數(shù)。這樣生成的數(shù)據(jù)能夠體現(xiàn)出不同的特征排序方法應(yīng)對關(guān)聯(lián)特征時的表現(xiàn)奠货。

接下來將會在上述數(shù)據(jù)上運行所有的特征選擇方法介褥,并且將每種方法給出的得分進行歸一化,讓取值都落在0-1之間递惋。對于RFE來說柔滔,由于它給出的是順序而不是得分,我們將最好的5個的得分定為1丹墨,其他的特征的得分均勻的分布在0-1之間廊遍。

from sklearn.datasets import load_boston
from sklearn.linear_model import (LinearRegression, Ridge, 
                                  Lasso, RandomizedLasso)
from sklearn.feature_selection import RFE, f_regression
from sklearn.preprocessing import MinMaxScaler
from sklearn.ensemble import RandomForestRegressor
import numpy as np
from minepy import MINE
np.random.seed(0)
size = 750
X = np.random.uniform(0, 1, (size, 14))
#"Friedamn #1” regression problem
Y = (10 * np.sin(np.pi*X[:,0]*X[:,1]) + 20*(X[:,2] - .5)**2 +
     10*X[:,3] + 5*X[:,4] + np.random.normal(0,1))
#Add 3 additional correlated variables (correlated with X1-X3)
X[:,10:] = X[:,:4] + np.random.normal(0, .025, (size,4))
names = ["x%s" % i for i in range(1,15)]
ranks = {}
def  rank_to_dict(ranks,  names,  order=1):
    minmax = MinMaxScaler()
    ranks = minmax.fit_transform(order*np.array([ranks]).T).T[0]
    ranks = map(lambda x: round(x, 2), ranks)
    return dict(zip(names, ranks ))
lr = LinearRegression(normalize=True)
lr.fit(X, Y)
ranks["Linear reg"] = rank_to_dict(np.abs(lr.coef_), names)
ridge = Ridge(alpha=7)
ridge.fit(X, Y)
ranks["Ridge"] = rank_to_dict(np.abs(ridge.coef_), names)
lasso = Lasso(alpha=.05)
lasso.fit(X, Y)
ranks["Lasso"] = rank_to_dict(np.abs(lasso.coef_), names)
rlasso = RandomizedLasso(alpha=0.04)
rlasso.fit(X, Y)
ranks["Stability"] = rank_to_dict(np.abs(rlasso.scores_), names)
#stop the search when 5 features are left (they will get equal scores)
rfe = RFE(lr, n_features_to_select=5)
rfe.fit(X,Y)
ranks["RFE"] = rank_to_dict(map(float, rfe.ranking_), names, order=-1)
rf = RandomForestRegressor()
rf.fit(X,Y)
ranks["RF"] = rank_to_dict(rf.feature_importances_, names)
f, pval  = f_regression(X, Y, center=True)
ranks["Corr."] = rank_to_dict(f, names)
mine = MINE()
mic_scores = []
for i in range(X.shape[1]):
    mine.compute_score(X[:,i], Y)
    m = mine.mic()
    mic_scores.append(m)
ranks["MIC"] = rank_to_dict(mic_scores, names)
r = {}
for name in names:
    r[name] = round(np.mean([ranks[method][name] 
                             for method in ranks.keys()]), 2)
methods = sorted(ranks.keys())
ranks["Mean"] = r
methods.append("Mean")
print "\t%s" % "\t".join(methods)
for name in names:
    print "%s\t%s" % (name, "\t".join(map(str, 
                         [ranks[method][name] for method in methods])))

從以上結(jié)果中可以找到一些有趣的發(fā)現(xiàn):

特征之間存在 線性關(guān)聯(lián) 關(guān)系,每個特征都是獨立評價的贩挣,因此X1,…X4的得分和X11,…X14的得分非常接近喉前,而噪音特征X5,…,X10正如預(yù)期的那樣和響應(yīng)變量之間幾乎沒有關(guān)系。由于變量X3是二次的王财,因此X3和響應(yīng)變量之間看不出有關(guān)系(除了MIC之外卵迂,其他方法都找不到關(guān)系)。這種方法能夠衡量出特征和響應(yīng)變量之間的線性關(guān)系绒净,但若想選出優(yōu)質(zhì)特征來提升模型的泛化能力见咒,這種方法就不是特別給力了,因為所有的優(yōu)質(zhì)特征都不可避免的會被挑出來兩次挂疆。

Lasso能夠挑出一些優(yōu)質(zhì)特征改览,同時讓其他特征的系數(shù)趨于0下翎。當(dāng)如需要減少特征數(shù)的時候它很有用,但是對于數(shù)據(jù)理解來說不是很好用宝当。(例如在結(jié)果表中视事,X11,X12,X13的得分都是0,好像他們跟輸出變量之間沒有很強的聯(lián)系庆揩,但實際上不是這樣的)

MIC對特征一視同仁俐东,這一點上和關(guān)聯(lián)系數(shù)有點像,另外订晌,它能夠找出X3和響應(yīng)變量之間的非線性關(guān)系虏辫。

隨機森林基于不純度的排序結(jié)果非常鮮明,在得分最高的幾個特征之后的特征锈拨,得分急劇的下降砌庄。從表中可以看到,得分第三的特征比第一的小4倍奕枢。而其他的特征選擇算法就沒有下降的這么劇烈鹤耍。

Ridge將回歸系數(shù)均勻的分攤到各個關(guān)聯(lián)變量上,從表中可以看出验辞,X11,…,X14和X1,…,X4的得分非常接近稿黄。

穩(wěn)定性選擇常常是一種既能夠有助于理解數(shù)據(jù)又能夠挑出優(yōu)質(zhì)特征的這種選擇,在結(jié)果表中就能很好的看出跌造。像Lasso一樣杆怕,它能找到那些性能比較好的特征(X1,X2壳贪,X4陵珍,X5),同時违施,與這些特征關(guān)聯(lián)度很強的變量也得到了較高的得分互纯。

總結(jié)

  1. 對于理解數(shù)據(jù)、數(shù)據(jù)的結(jié)構(gòu)磕蒲、特點來說留潦,單變量特征選擇是個非常好的選擇。盡管可以用它對特征進行排序來優(yōu)化模型辣往,但由于它不能發(fā)現(xiàn)冗余(例如假如一個特征子集兔院,其中的特征之間具有很強的關(guān)聯(lián),那么從中選擇最優(yōu)的特征時就很難考慮到冗余的問題)站削。
  2. 正則化的線性模型對于特征理解和特征選擇來說是非常強大的工具坊萝。L1正則化能夠生成稀疏的模型,對于選擇特征子集來說非常有用;相比起L1正則化十偶,L2正則化的表現(xiàn)更加穩(wěn)定菩鲜,由于有用的特征往往對應(yīng)系數(shù)非零,因此L2正則化對于數(shù)據(jù)的理解來說很合適惦积。由于響應(yīng)變量和特征之間往往是非線性關(guān)系睦袖,可以采用basis expansion的方式將特征轉(zhuǎn)換到一個更加合適的空間當(dāng)中,在此基礎(chǔ)上再考慮運用簡單的線性模型荣刑。
  3. 隨機森林是一種非常流行的特征選擇方法,它易于使用伦乔,一般不需要feature engineering厉亏、調(diào)參等繁瑣的步驟,并且很多工具包都提供了平均不純度下降方法烈和。它的兩個主要問題爱只,1是重要的特征有可能得分很低(關(guān)聯(lián)特征問題),2是這種方法對特征變量類別多的特征越有利(偏向問題)招刹。盡管如此恬试,這種方法仍然非常值得在你的應(yīng)用中試一試。
  4. 特征選擇在很多機器學(xué)習(xí)和數(shù)據(jù)挖掘場景中都是非常有用的疯暑。在使用的時候要弄清楚自己的目標(biāo)是什么训柴,然后找到哪種方法適用于自己的任務(wù)。當(dāng)選擇最優(yōu)特征以提升模型性能的時候妇拯,可以采用交叉驗證的方法來驗證某種方法是否比其他方法要好幻馁。當(dāng)用特征選擇的方法來理解數(shù)據(jù)的時候要留心,特征選擇模型的穩(wěn)定性非常重要越锈,穩(wěn)定性差的模型很容易就會導(dǎo)致錯誤的結(jié)論仗嗦。對數(shù)據(jù)進行二次采樣然后在子集上運行特征選擇算法能夠有所幫助,如果在各個子集上的結(jié)果是一致的甘凭,那就可以說在這個數(shù)據(jù)集上得出來的結(jié)論是可信的稀拐,可以用這種特征選擇模型的結(jié)果來理解數(shù)據(jù)。

Tips

  • 什么是 卡方檢驗 丹弱?用方差來衡量某個觀測頻率和理論頻率之間差異性的方法

  • 什么是 皮爾森卡方檢驗 德撬?這是一種最常用的卡方檢驗方法,它有兩個用途:1是計算某個變量對某種分布的擬合程度躲胳,2是根據(jù)兩個觀測變量的 Contingency table 來計算這兩個變量是否是獨立的砰逻。主要有三個步驟:第一步用方差和的方式來計算觀測頻率和理論頻率之間卡方值;第二步算出卡方檢驗的自由度(行數(shù)-1乘以列數(shù)-1)泛鸟;第三步比較卡方值和對應(yīng)自由度的卡方分布蝠咆,判斷顯著性。

  • 什么是 p-value ?簡單地說刚操,p-value就是為了驗證假設(shè)和實際之間一致性的統(tǒng)計學(xué)意義的值闸翅,即假設(shè)檢驗。有些地方叫右尾概率菊霜,根據(jù)卡方值和自由度可以算出一個固定的p-value坚冀,

  • 什么是 響應(yīng)變量(response value) ?簡單地說鉴逞,模型的輸入叫做explanatroy variables记某,模型的輸出叫做response variables,其實就是要驗證該特征對結(jié)果造成了什么樣的影響

  • 什么是 統(tǒng)計能力(statistical power) ?

  • 什么是 度量(metric) ?

  • 什么是 零假設(shè)(null hypothesis) ?在相關(guān)性檢驗中构捡,一般會取“兩者之間無關(guān)聯(lián)”作為零假設(shè)液南,而在獨立性檢驗中,一般會取“兩者之間是獨立”作為零假設(shè)勾徽。與零假設(shè)相對的是備擇假設(shè)(對立假設(shè))滑凉,即希望證明是正確的另一種可能。

  • 什么是 多重共線性 喘帚?

  • 什么是 grid search 畅姊?

That’s it

References

  1. http://blog.datadive.net/selecting-good-features-part-i-univariate-selection/
  2. http://blog.datadive.net/selecting-good-features-part-ii-linear-models-and-regularization/
  3. http://scikit-learn.org/stable/modules/feature_selection.html#univariate-feature-selection
  4. http://www.quora.com/What-are-some-feature-selection-methods
  5. http://www.quora.com/What-are-some-feature-selection-algorithms
  6. http://www.quora.com/What-are-some-feature-selection-methods-for-SVMs
  7. http://www.quora.com/What-is-the-difference-between-principal-component-analysis-PCA-and-feature-selection-in-machine-learning-Is-PCA-a-means-of-feature-selection
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市吹由,隨后出現(xiàn)的幾起案子若未,更是在濱河造成了極大的恐慌,老刑警劉巖倾鲫,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件陨瘩,死亡現(xiàn)場離奇詭異,居然都是意外死亡级乍,警方通過查閱死者的電腦和手機舌劳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玫荣,“玉大人甚淡,你說我怎么就攤上這事⊥背В” “怎么了贯卦?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長焙贷。 經(jīng)常有香客問我撵割,道長,這世上最難降的妖魔是什么辙芍? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任啡彬,我火速辦了婚禮羹与,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘庶灿。我一直安慰自己纵搁,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布往踢。 她就那樣靜靜地躺著腾誉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪峻呕。 梳的紋絲不亂的頭發(fā)上利职,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機與錄音瘦癌,去河邊找鬼猪贪。 笑死,一個胖子當(dāng)著我的面吹牛佩憾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播干花,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼妄帘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了池凄?” 一聲冷哼從身側(cè)響起抡驼,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肿仑,沒想到半個月后致盟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡尤慰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年馏锡,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伟端。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡杯道,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出责蝠,到底是詐尸還是另有隱情党巾,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布霜医,位于F島的核電站齿拂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏肴敛。R本人自食惡果不足惜署海,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一吗购、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧叹侄,春花似錦巩搏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至撒强,卻和暖如春禽捆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背飘哨。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工胚想, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芽隆。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓浊服,卻偏偏與公主長得像,于是被迫代替她去往敵國和親胚吁。 傳聞我的和親對象是個殘疾皇子牙躺,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

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