不管是XGBoost仍是其餘的Boosting Tree,使用的Tree都是cart迴歸樹,這也就意味着該類提高樹算法只接受數值特徵輸入,不直接支持類別特徵,默認狀況下,xgboost會把類別型的特徵當成數值型。事實上,對於類別特徵的處理,參考XGBoost PPT以下:node
xgboost 樹模型實際上是不建議使用one-hot編碼,在xgboost上面的 issue 也提到過,相關的說明以下python
I do not know what you mean by vector. xgboost treat every input feature as numerical, with support for missing values and sparsity. The decision is at the user So if you want ordered variables, you can transform the variables into numerical levels(say age). Or if you prefer treat it as categorical variable, do one hot encoding.
在另外一個issues上也提到過(tqchen commented on 8 May 2015):算法
One-hot encoding could be helpful when the number of categories are small( in level of 10 to 100). In such case one-hot encoding can discover interesting interactions like (gender=male) AND (job = teacher). While ordering them makes it harder to be discovered(need two split on job). However, indeed there is not a unified way handling categorical features in trees, and usually what tree was really good at was ordered continuous features anyway..
總結起來的結論,大至兩條:編程
xgboost是不支持category特徵的,在訓練模型以前,須要咱們進行預處理,能夠根據特徵的具體形式來選擇:數組
Label encoding是使用字典的方式,將每一個類別標籤與不斷增長的整數相關聯,即生成一個名爲class_的實例數組的索引。瀏覽器
from sklearn.preprocessing import LabelEncoder le = LabelEncoder() city_list = ["paris", "paris", "tokyo", "amsterdam"] le.fit(city_list) print(le.classes_) # 輸出爲:['amsterdam' 'paris' 'tokyo'] city_list_le = le.transform(city_list) # 進行Encode print(city_list_le) # 輸出爲:[1 1 2 0] city_list_new = le.inverse_transform(city_list_le) # 進行decode print(city_list_new) # 輸出爲:['paris' 'paris' 'tokyo' 'amsterdam']
若是是多列數據如何進行方便的編碼。網絡
方案一:session
from sklearn.preprocessing import LabelEncoder from collections import defaultdict import pandas as pd d = defaultdict(LabelEncoder) df = pd.DataFrame({ 'pets': ['cat', 'dog', 'cat', 'monkey', 'dog', 'dog'], 'owner': ['Champ', 'Ron', 'Brick', 'Champ', 'Veronica', 'Ron'], 'location': ['San_Diego', 'New_York', 'New_York', 'San_Diego', 'San_Diego', 'New_York'] }) # Encoding the variable fit = df.apply(lambda x: d[x.name].fit_transform(x)) # Inverse the encoded fit.apply(lambda x: d[x.name].inverse_transform(x)) # Using the dictionary to label future data df.apply(lambda x: d[x.name].transform(x))
方案2:app
import pandas as pd from sklearn.preprocessing import LabelEncoder from sklearn.pipeline import Pipeline # Create some toy data in a Pandas dataframe fruit_data = pd.DataFrame({ 'fruit': ['apple','orange','pear','orange'], 'color': ['red','orange','green','green'], 'weight': [5,6,3,4] }) class MultiColumnLabelEncoder: def __init__(self,columns = None): self.columns = columns # array of column names to encode def fit(self,X,y=None): return self # not relevant here def transform(self,X): ''' Transforms columns of X specified in self.columns using LabelEncoder(). If no columns specified, transforms all columns in X. ''' output = X.copy() if self.columns is not None: for col in self.columns: output[col] = LabelEncoder().fit_transform(output[col]) else: for colname,col in output.iteritems(): output[colname] = LabelEncoder().fit_transform(col) return output def fit_transform(self,X,y=None): return self.fit(X,y).transform(X)
參考連接: https://stackoverflow.com/questions/24458645/label-encoding-across-multiple-columns-in-scikit-learn框架
這種方法很簡單,在許多狀況下效果很好,但他有一個缺點:全部的標籤都變成了數字,而後算法模型直接將根據其距離來考慮類似的數字,而不考慮標籤的具體含義。所以,一般優選獨熱編碼(one-hot encoding)將數據二進制化。
from sklearn.preprocessing import LabelBinarizer lb = LabelBinarizer() city_list = ["paris", "paris", "tokyo", "amsterdam"] lb.fit(city_list) print(lb.classes_) # 輸出爲:['amsterdam' 'paris' 'tokyo'] city_list_le = lb.transform(city_list) # 進行Encode print(city_list_le) # 輸出爲: # [[0 1 0] # [0 1 0] # [0 0 1] # [1 0 0]] city_list_new = lb.inverse_transform(city_list_le) # 進行decode print(city_list_new) # 輸出爲:['paris' 'paris' 'tokyo' 'amsterdam']
當類別的特徵被構形成相似於字典的列表時,列表中的值僅僅須要時幾個特徵的值而不須要很密集,此時可採用另外一種分類方法。DictVectorizer能夠用於將各列使用標準的Python dict對象表示的特徵數組,轉換成sklearn中的estimators可用的NumPy/SciPy表示的對象。Python的dict的優勢是,很方便使用,稀疏,能夠存儲feature名和值。DictVectorizer實現了一個稱爲one-of-K或者」one-hot」編碼的類別特徵。類別特徵是「屬性-值」對,它的值嚴格對應於一列無序的離散機率(好比:topic id, 對象類型,tags, names…)
下例中,」city」是類別的屬性,而」temperature」是一個傳統的數值型feature:
from sklearn.feature_extraction import DictVectorizer measurements = [ {'city': 'Dubai', 'temperature': 33.}, {'city': 'London', 'temperature': 12.}, {'city': 'San Fransisco', 'temperature': 18.}, ] vec = DictVectorizer() measurements_vec = vec.fit_transform(measurements) print(measurements_vec) # 輸出內容: # (0, 0) 1.0 # (0, 3) 33.0 # (1, 1) 1.0 # (1, 3) 12.0 # (2, 2) 1.0 # (2, 3) 18.0 print(measurements_vec.toarray()) # 輸出內容 # [[ 1. 0. 0. 33.] # [ 0. 1. 0. 12.] # [ 0. 0. 1. 18.]] feature_names = vec.get_feature_names() print(feature_names) # 輸出:['city=Dubai', 'city=London', 'city=San Fransisco', 'temperature']
通常的vectorizer是爲訓練過程當中遇到的特徵構建一個hash table,而FeatureHasher類則直接對特徵應用一個hash函數來決定特徵在樣本矩陣中的列索引。這樣的作法使得計算速度提高而且節省了內存,the hasher沒法記住輸入特徵的樣子,並且不遜在你想變換操做:inverse_transform。
由於哈希函數可能會致使原本不相關的特徵之間發生衝突,因此使用了有符號的hash函數。對一個特徵,其hash值的符號決定了被存儲到輸出矩陣中的值的符號。經過這種方式就可以消除特徵hash映射時發生的衝突而不是累計衝突。並且任意輸出的值的指望均值是0。sklearn中的FeatureHasher使用了MurmurHash 3做爲其Hash算法。
FeatureHasher的輸出一般是CSR格式的scipy.sparse matrix。Feature hashing 可被用於文檔分類中去,可是與text.CountVectorizer不一樣,FeatureHasher不作單詞切分或其餘的預處理操做,除了Unicode-to-UTF-8編碼之外。
在實際的機器學習的應用任務中,特徵有時候並不老是連續值,有多是一些分類值,如性別可分爲「male」和「female」。在機器學習任務中,對於這樣的特徵,一般咱們須要對其進行特徵數字化,好比有以下三個特徵屬性:
對於某一個樣本,如[「male」,」US」,」Internet Explorer」],咱們須要將這個分類值的特徵數字化,最直接的方法,咱們能夠採用序列化的方式:[0,1,3]。可是,即便轉化爲數字表示後,上述數據也不能直接用在咱們的分類器中。由於,分類器每每默認數據是連續的,而且是有序的。按照上述的表示,數字並非有序的,而是隨機分配的。這樣的特徵處理並不能直接放入機器學習算法中。
爲了解決上述問題,其中一種可能的解決方法是採用獨熱編碼(One-Hot Encoding)。獨熱編碼,又稱爲一位有效編碼。其方法是使用N位狀態寄存器來對N個狀態進行編碼,每一個狀態都由他獨立的寄存器位,而且在任意時候,其中只有一位有效。能夠這樣理解,對於每個特徵,若是它有m個可能值,那麼通過獨熱編碼後,就變成了m個二元特徵。而且,這些特徵互斥,每次只有一個激活。所以,數據會變成稀疏的。
對於上述的問題,性別的屬性是二維的,同理,地區是三維的,瀏覽器則是四維的,這樣,咱們能夠採用One-Hot編碼的方式對上述的樣本「[「male」,」US」,」Internet Explorer」]」編碼,「male」則對應着[1,0],同理「US」對應着[0,1,0],「Internet Explorer」對應着[0,0,0,1]。則完整的特徵數字化的結果爲:[1,0,0,1,0,0,0,0,1]。
一、基於sklearn 的one hot encoding:
import pandas as pd from sklearn.preprocessing import LabelEncoder from sklearn.preprocessing import OneHotEncoder df = pd.DataFrame([ ['green', 'Chevrolet', 2017], ['blue', 'BMW', 2015], ['yellow', 'Lexus', 2018], ]) df.columns = ['color', 'make', 'year'] le_color = LabelEncoder() le_make = LabelEncoder() df['color_encoded'] = le_color.fit_transform(df.color) df['make_encoded'] = le_make.fit_transform(df.make) color_ohe = OneHotEncoder() make_ohe = OneHotEncoder() X = color_ohe.fit_transform(df.color_encoded.values.reshape(-1, 1)).toarray() Xm = make_ohe.fit_transform(df.make_encoded.values.reshape(-1, 1)).toarray() dfOneHot = pd.DataFrame(X, columns=["Color_" + str(int(i)) for i in range(X.shape[1])]) df = pd.concat([df, dfOneHot], axis=1) dfOneHot = pd.DataFrame(Xm, columns=["Make" + str(int(i)) for i in range(X.shape[1])]) df = pd.concat([df, dfOneHot], axis=1)
參考連接: http://www.insightsbot.com/blog/McTKK/python-one-hot-encoding-with-scikit-learn
二、基於pandas的one hot encoding:
其實若是咱們跳出 scikit-learn, 在 pandas 中能夠很好地解決這個問題,用 pandas 自帶的get_dummies函數便可
import pandas as pd df = pd.DataFrame([ ['green', 'Chevrolet', 2017], ['blue', 'BMW', 2015], ['yellow', 'Lexus', 2018], ]) df.columns = ['color', 'make', 'year'] df_processed = pd.get_dummies(df, prefix_sep="_", columns=df.columns[:-1]) print(df_processed)
get_dummies的優點在於:
get_dummies雖然有這麼多優勢,但畢竟不是 sklearn 裏的transformer類型,因此獲得的結果得手動輸入到 sklearn 裏的相應模塊,也沒法像 sklearn 的transformer同樣能夠輸入到pipeline中進行流程化地機器學習過程。
參考連接: https://blog.cambridgespark.com/robust-one-hot-encoding-in-python-3e29bfcec77e
Embedding的起源和火爆都是在NLP中的,經典的word2vec都是在作word embedding這件事情,而真正首先在結構數據探索embedding的是在kaggle上的《Rossmann Store Sales》中的rank 3的解決方案,做者在比賽完後爲此方法整理一篇論文放在了arXiv,文章名:《 Entity Embeddings of Categorical Variables 》。
Embedding也被稱爲嵌套,是將大型稀疏矢量映射到一個保留語義關係的低維空間。在此模塊的隨後幾個部分中,咱們將從直觀角度、概念角度和編程角度來詳細探討嵌套。
要解決稀疏輸入數據的核心問題,您能夠將高維度數據映射到低維度空間。即使是小型多維空間,也能自由地將語義上類似的項歸到一塊兒,並將相異項分開。矢量空間中的位置(距離和方向)可對良好的嵌套中的語義進行編碼。例如,下面的真實嵌套可視化圖所展現的幾何關係圖捕獲了國家與其首都之間的語義關係。
嵌套是一個矩陣,每列表示您詞彙中的一項所對應的矢量。要得到某個詞彙項的密集矢量,您能夠檢索該項所對應的列。可是,如何轉換字詞矢量的稀疏包呢?要得到表示多個詞彙項(例如,一句或一段中的全部字詞)的稀疏矢量的密集矢量,您能夠檢索各項的嵌套,而後將它們相加。若是稀疏矢量包含詞彙項的計數,則您能夠將每項嵌套與其對應項的計數相乘,而後再求和。這些運算可能看起來很眼熟吧。
咱們剛剛闡述的查詢、乘法和加法程序等效於矩陣乘法。假設有一個的稀疏表示 S 和一個的嵌套表 E,矩陣乘法能夠得出密集矢量。這個概念用神經網絡圖來表示以下:
但首要問題是,如何獲取 E 呢?咱們將在下一部分介紹如何獲取嵌套。
Network Embedding,即將網絡節點、community投影到低維向量空間,用於node classification、link prediction、community detection、visualization等任務。
核心假設:節點間距離越近,embedding向量越接近,定義LOSS爲:
目標是將類似性高的兩個節點,映射到低維空間後依然保持距離相近,其損失函數定義爲:
主要思想:將節點的類似性向量直接做爲模型的輸入,經過 Auto-encoder 對這個向量進行降維壓縮,獲得其向量化後的結果 $Z_i#。其損失函數定義爲:
模型框架爲:
主要思想:將節點自己及其鄰居節點的屬性(好比文本信息)或特徵(好比統計信息)編碼進向量中,引入了更多特徵信息,而且在鄰居節點間共享了一些特徵或參數,基於最終的目標(如節點分類)作總體優化。
模型框架示意和計算流程:
更多參考:
Keras對Tensorflow又進行了一層封裝,操做簡單,功能強大。
# 構造輸入數據 # 輸入數據是320*6,320個樣本,6個類別特徵,且類別特徵的可能值是0到36之間(37個)。 # 對這6個特徵作one-hot的話,應該爲37*6, # embedding就是使1個特徵本來應該one-hot的37維變爲3維(手動設定,也能夠是其它),由於有36個類別特徵 # 這樣輸出的結果就應該是3*6 # 參考連接:https://keras.io/zh/layers/embeddings/ # 建議降維的維度爲 math.ceil(category_count ** 0.25) import numpy as np np.random.seed(42) input_array = np.random.randint(37, size=(320, 6)) print(input_array) import tensorflow as tf from keras import backend as K from keras.models import Sequential from keras.layers.embeddings import Embedding with tf.Session() as sess: K.set_session(sess) model = Sequential() model.add(Embedding(37, 3, input_length=6)) model.compile('rmsprop', 'mse') output_array = model.predict(input_array) print(output_array) # weight = model.get_weights() # print(weight)
在上述的代碼中,咱們能夠看到6個類別特徵的值都在0到37,而且咱們沒有對模型進行訓練,而是直接就搭建了一個網絡,就輸出結果了。在真實的應用中,不是這樣。有2點須要改進:
爲了解決上述的2個問題,咱們這裏仍是人工構建訓練集,咱們搭建的模型如圖:
從模型中,咱們能夠看到,這是符合現實世界的數據集的:即既有分類特徵,又有連續特徵。咱們先訓練一個網絡,embedding_3和embedding_4層的輸出結果就是用embedding處理類別特徵後的結果。
import numpy as np import tensorflow as tf from keras.models import Model from keras.layers import Input, Dense, Concatenate, Reshape, Dropout from keras.layers.embeddings import Embedding from keras import backend as K from keras.utils import plot_model session_conf = tf.ConfigProto(intra_op_parallelism_threads=1, inter_op_parallelism_threads=1) sess = tf.Session(graph=tf.get_default_graph(), config=session_conf) K.set_session(sess) def build_embedding_network(): """ 以網絡結構embeddding層在前,dense層在後。即訓練集的X必須以分類特徵在前,連續特徵在後。 """ inputs = [] embeddings = [] input_cate_feature_1 = Input(shape=(1,)) embedding = Embedding(10, 3, input_length=1)(input_cate_feature_1) embedding = Reshape(target_shape=(3,))(embedding) # embedding後是10*1*3,爲了後續計算方便,所以使用Reshape轉爲10*3 inputs.append(input_cate_feature_1) embeddings.append(embedding) input_cate_feature_2 = Input(shape=(1,)) embedding = Embedding(4, 2, input_length=1)(input_cate_feature_2) embedding = Reshape(target_shape=(2,))(embedding) inputs.append(input_cate_feature_2) embeddings.append(embedding) input_numeric = Input(shape=(1,)) embedding_numeric = Dense(16)(input_numeric) inputs.append(input_numeric) embeddings.append(embedding_numeric) x = Concatenate()(embeddings) x = Dense(10, activation='relu')(x) x = Dropout(.15)(x) output = Dense(1, activation='sigmoid')(x) model = Model(inputs, output) model.compile(loss='binary_crossentropy', optimizer='adam') return model """ 構造訓練數據 輸入數據是320*3,320個樣本,2個類別特徵,1個連續特徵。 對類別特徵作entity embedding,第一個類別特徵10個,第二個類別特徵4個。對這2個特徵作one-hot的話,應該爲10+4, 對第一個類別特徵作embedding使其爲3維,對第二個類別特徵作embedding使其爲2維。3對連續特徵不作處理。這樣理想輸出的結果就應該是3+2+1。 維和2維的設定是根據實驗效果和交叉驗證設定。 """ sample_num = 320 # 樣本數爲32 cate_feature_num = 2 # 類別特徵爲2 contious_feature_num = 1 # 連續特徵爲1 rng = np.random.RandomState(0) # 保證了訓練集的復現 cate_feature_1 = rng.randint(10, size=(sample_num, 1)) cate_feature_2 = rng.randint(4, size=(sample_num, 1)) contious_feature = rng.rand(sample_num, 1) X = [cate_feature_1, cate_feature_2, contious_feature] Y = np.random.randint(2, size=(sample_num, 1)) # 二分類 cate_embedding_dimension = {'0': 3, '1': 2} # 記錄類別特徵embedding後的維度。key爲類別特徵索引,value爲embedding後的維度 """ 訓練和預測 """ NN = build_embedding_network() plot_model(NN, to_file='NN.png') # 畫出模型,須要GraphViz包。另外須要安裝 pip install pydot NN.fit(X, Y, epochs=3, batch_size=4, verbose=0) y_preds = NN.predict(X)[:, 0] """ 讀embedding層的輸出結果 """ model = NN # 建立原始模型 for i in range(cate_feature_num): layer_name = NN.get_config()['layers'][cate_feature_num + i]['name'] # cate_feature_num+i就是全部embedding層 intermediate_layer_model = Model(inputs=NN.input, outputs=model.get_layer(layer_name).output) intermediate_output = intermediate_layer_model.predict(X) intermediate_output.resize([sample_num, cate_embedding_dimension[str(i)]]) if i == 0: X_embedding_trans = intermediate_output else: X_embedding_trans = np.hstack((X_embedding_trans, intermediate_output)) # 水平拼接 for i in range(contious_feature_num): if i == 0: X_contious = X[cate_feature_num + i] else: X_contious = np.hstack((X_contious, X[cate_feature_num + i])) X_trans = np.hstack((X_embedding_trans, X_contious)) # 在類別特徵作embedding後的基礎上,拼接連續特徵,造成最終矩陣,也就是其它學習器的輸入。 print(X_trans[:5]) # 其中,類別特徵維度爲5(前5個),連續特徵維度爲1(最後1個) weight = NN.trainable_weights[0].eval(session=sess) # embedding_1層的參數。 print(weight[:5])
參考連接: https://blog.csdn.net/h4565445654/article/details/78998444