1. 程式人生 > >機器學習-特徵工程-Feature generation 和 Feature selection

機器學習-特徵工程-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幾種方式。