贊助 最新chease0ldman老人

機器學習-特征工程-Feature generation 和 Feature selection

  1. 概述:上節咱們說了特征工程是機器學習的一個核心內容。然后咱們已經學習了特征工程中的基礎內容,分別是missing value handling和categorical data encoding的一些方法技巧。但是光會前面的一些內容,還不足以應付實際的工作中的很多情況,例如如果咱們的原始數據的features太多,咱們應該選擇那些features作為咱們訓練的features?或者咱們的features太少了,咱們能不能利用現有的features再創造出一些新的與咱們的target有更加緊密聯系的features呢?這些都是咱們feature engineering中經常遇到的場景,這里涉及到的一些常用技術也是每一個做機器學習或是數據分析的工程師必須要掌握的。上面提到的技術,咱們通常叫做:feature generation和feature selection。下面咱們就來說一說這兩個技術點吧
  2. Feature generation。對于這個技術點,其實沒有什么訣竅,就是一個,深刻理解咱們的數據的意義再加上一點點創造力。大家是不是很懵逼,哈哈,這就完啦????哈哈當然不是啦,但是這一塊缺失沒有一個統一的模式,具有一定的隨機性。但是通過總結,咱們可以總結一下常用的模式,方便大家在應用的時候參考。                                                                                        2.1 Interaction。這個其實就是相當于交叉的意思,咱們可以將幾個features直接的拼接在一起,形成一個“有意思”的新的feature,記住一定要有意義的,否則你不但白搞了,甚至原來好好的數據都被你搞砸了,不要為了裝逼而裝逼,咱要裝逼于無形之中。那么這個有什么意義呢?首先它能將多個columns裝換成一個column,方便咱們的數據處理;其次在有些特定的數據中,這種interaction更加能反映出數據的本質。具體怎么操作了,咱們通過一個簡單的代碼來展示,注意我只截取了我代碼的一部分,默認數據都已經加載完畢,所以不要糾結我的代碼的變量和數據哈,大家主要看過程和思路
    interactions = data_raw["category"]+"_"+data_raw["country"]
    baseline_data = baseline_data.assign(category_country = label_encoder.fit_transform(interactions))

    上面的第一句代碼就是咱們interaction的部分,第二句是講interaction過后的數據label encoding并且加入到咱們的數據集里面,簡單明了。上面是將原始數據中的category 和 country連接在一起從而形成一個新的feature                                       2.2 numerical transforming。這是什么意思呢,對于有些numerical data的columns,他們的數據分布是很不均勻的,或者說他們的數值太大或者太小,有的時候不適合咱們的數據的訓練,可能會出現vanishing gradient或者gradient explode的情況。具體啥叫vanishing gradient和gradient exploding,咱們在后面的內容在慢慢解釋。暫時只需要知道這是個很麻煩的事情就好了,會導致咱們訓練的模型不那么牛逼就行了。那么咱們通過什么方法解決呢?這里主要通過一些常見的數學的方式來解決,例如用log 或者 sqrt等等方式。咱們可以通過下面的代碼來簡單的展示一下

    np.sqrt(baseline_data['goal'])
    np.log(baseline_data['goal'])

    從上面咱們可以看出,這里咱們主要還是通過numpy里面提供的API進行處理的,非常簡單,簡單跟1一樣,好了這里就說到這了。    對了,忘記一個事兒,就是numerical transforming在tree-based模型中沒有什么卵用的,因為tree-based的所有模型都是scale invariant的,就是tree-based模型都是不care數據的大小分布的。                                                                           2.3 rolling。這個就比較高級一點啦(相比前兩種方式),首先咱們先要明白rolling的概念,其實rolling就是相當于在咱們的數據(series)上面卡上一個fixed-size的小window,然后對于這個window覆蓋的數據進行一些簡單的計算,例如:counting,mean,sum等等。如果大家還是覺得不懂,我把它的官方鏈接貼在這里,大家自己去看看,里面還有很多實例:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.rolling.html#pandas.Series.rolling 。那么我先寫一個簡單的小例子給大家參考一下哈

    launched = pd.Series(data_raw.index,data_raw.launched,name="count_7_days").sort_index()
    count_7_days = launched.rolling('7d').count()
    count_7_days.index = launched.values
    count_7_days
    = count_7_days.reindex(data_raw.index)

    我先簡單解釋一下上面代碼的意思哈,第一句是讓時間作為這個series的index,第二句rolling是計算最近7天的的數量,第三第四句是講數據還原到之前的index的順序,房間重新join到原來的數據集中。那么rolling這種方式一般在什么情況下用呢?一般在你的數據有datetime的時候,或者前面數據會影響到后面的結果的時候,大家可以考慮一下下,但是這個是不一定的,還是需要大家有一定的creativity的。例如上面的例子就是統計最近7天一共上傳的APP的數量,來判斷某一個APP是否被下載的應用場景。一般情況下,最近上傳的APP數量太多,被下載的概率就越低,所以他們還是有一定關聯關系的。所以我generate一個新的feature還是有一定道理的。                                                                                                                                                       2.4 Time delta。從這個命名中咱們可以知道,這個跟time肯定有關系,這個大家猜的很對。time delta也是有一定隨機性的,有時需要有時也不需要,也是要根據實際的數據的特性來決定的,甚至是根據工程師自己來決定的,跟上面的rolling有點相似。為了方便解釋這其中的細節,我也是直接來一個例子然后慢慢解釋

    def time_since_last_project_same_category(series):
        return series.diff().dt.total_seconds()/3600
    df = data_raw[['category','launched']].sort_values('launched')
    group_category = df.groupby('category')
    timedeltas = group_category.transform(time_since_last_project_same_category)
    timedeltas = timedeltas.fillna(timedeltas.mean()).reindex(baseline_data.index)

    上面前兩行是一個計算相鄰datatime之間的相差多少個小時,第三行創建一個按照排序好的launched time為index的dataframe, 第四行是按照category的條件來group前面創建的df, 第五行是計算group里面相鄰數據之間的time delta,并且返回一個series, 第六行是填充這些空數據,并且按照原始數據的方式index重新排序方便加入到原始數據。流程就這樣結束了。上面的場景是計算同一個category相鄰app上傳的時間差,這個其實也是會影響到咱們的APP是否被下載的因素。所以這個也是咱們的一個creativity,實際情況中千變萬化,一定要根據實際情況來定,不能為了裝逼而裝逼,一定要根據實際的業務需要,否則適得其反。 好了,其實關于feature generation還有很多種方式,例如有些事計算兩個columns之間的差值,取模等等,這里沒有統一的標準,唯一的捷徑和key就是咱們一定得理解咱們每一個columns和dataset的實際業務的意思,否則再牛逼的generation也拯救不了你。下面咱們進入到這一章的最后一節feature selection吧。

  3. Feature selection。當咱們吧missing value, categorical data handling, feature generation這個繁雜的步驟都走完了,咱們就來到了feature engineering的最后一步了,那就是feature selection。根據意思就是咱們到底咱們最后選擇哪些數據來訓練咱們的模型,數據選的好,模型的適用范圍,效率,準確性都更好,否則咱們前面的努力可能會毀于一旦。關于feature selection我個人覺得是個人經驗和一些selection技術的結合,才能選出最好的features作為訓練的樣本。個人經驗,就是工程師自己對于數據的理解程度,有些features一看就和target沒有半毛錢的關系,咱們肯定直接排除這些features,例如咱們的手機設備號和手機價格一看就一點關系都沒有,咱們肯定直接刪除手機設備號這個feature;有些features一看就和target有很強的關系,例如手機內存大小和手機的價格一看就有很強的關聯性,所以咱們肯定要選擇這個內存的feature。個人經驗之外還有很多模棱兩可的features怎么辦呢?就跟我前面說的那樣,咱們還可以用一些技術手段來選擇。下面咱們來介紹兩種常用的feature selection的技術。                                                                                                                                                   3.1 F-classification method。這種方式是單獨計算每一個column和target的關聯性,然后選出關聯性最強的f個columns,這里f的值是咱們自定義的。具體的實現細節咱們不需要了解的那么深,因為sklearn已經幫助咱們造好輪子了,從下面的代碼,咱們來感受一下它的魅力吧
    from sklearn.feature_selection import SelectKBest, f_classif
    selector = SelectKBest(score_func = f_classif, k = 5)
    train,valid,test = get_data_splits(baseline_data, 0.1)
    feature_cols = train.columns.drop("outcome")
    X_new = selector.fit_transform(train[feature_cols],train["outcome"] )

    #get back to the features we kept
    features = pd.DataFrame(selector.inverse_transform(X_new), index = train.index, columns = feature_cols)
    #drop the columns that the values are all 0s
    feature_cols_final = features.columns[features.var()!=0]
    features_final = features[feature_cols_final]

    從上面的代碼咱們可以看出來,首先得從sklearn.feature_selection這個模塊中引進SelectKBest和f_classif兩個子模塊;第二步就是創建一個Selector實例對象,這個selector最終返回多少個features是通過咱們的參數K來控制的,selector的最終選擇哪些features也是通過f_classif這個函數來控制的;最后就是這個selector的transform了,將features和target作為參數分別傳遞給他,他會自動搞定,并且返回K個features, 然后再將numpy array返回到dataframe的格式。這種方式的只能計算出每一個feature和target的linear dependency,并不能一次性包括所有的features進行關聯性計算。                                                       3.2 L1 Regression。L1 Regression可以直接包括所有的features一次性的計算這個columns和target的關聯性。關聯性越強,數值越大。它不需要制定最后返回多少個features,它是根據L1的結果自動幫助咱們features。但是它的運行速度要遠遠慢于上面k-classif的方法,可是好處就是一般情況下L1 Regression的運行結果要好于K-classif, 但也不一定澳,只能說大部分情況是這樣的。

    from sklearn.linear_model import LogisticRegression
    from sklearn.feature_selection import SelectFromModel
    train,valid,test = get_data_splits(baseline_data, 0.1)
    X, y = train[train.columns.drop("outcome")], train["outcome"]
    logistic_model = LogisticRegression(C=1, penalty="l1", random_state=7).fit(X,y)
    selector = SelectFromModel(logistic_model,prefit=True)
    X_new = selector.transform(X)
    features = pd.DataFrame(selector.inverse_transform(X_new),index = train.index, columns = feature_cols)
    feature_cols_final = features.columns[features.var()!=0]

    總結:上面就是一般的的特征工程的feature selection和feature generation的一般方法,咱們上面講了很多種的方式,實際情況中具體選擇哪一種還是要根據實際情況,一定不要死讀書。feature generation一般就是interaction,numerical generation,rolling和time delta四種方式。feature selection一般用到的技術手段就是就是f-classif和L1 regression幾種方式。

posted @ 2020-01-19 12:09  HappyPuppy  閱讀(379)  評論(1編輯  收藏