之前參加了一個螞蟻金服的數(shù)據挖掘比賽,最后初賽拿到了37名,全是靠的stacking呀,不過懶癌晚期患者直到現(xiàn)在才把學到的東西整理出來,簡直無藥可救了。
1、Stacking原理
stacking 就是當用初始訓練數(shù)據學習出若干個基學習器后,將這幾個學習器的預測結果作為新的訓練集,來學習一個新的學習器。
他具體是怎么實現(xiàn)的呢,我們來通過代碼了解一下吧,代碼來源于github
https://github.com/log0/vertebral/blob/master/stacked_generalization.py
2、Stacking分類應用
這里我們用二分類的例子做介紹。
例如我們用 RandomForestClassifier, ExtraTreesClassifier, GradientBoostingClassifier 作為第一層學習器(當然這里我們可以添加更多的分類器,也可以用不同的特征組合但是同樣的學習方法作為基分類器):
clfs = [
RandomForestClassifier(n_estimators = n_trees, criterion = 'gini'),
ExtraTreesClassifier(n_estimators = n_trees * 2, criterion = 'gini'),
GradientBoostingClassifier(n_estimators = n_trees),
]
接著要訓練第一層學習器,并得到第二層學習器所需要的數(shù)據,這里會用到 k 折交叉驗證。我們首先會將數(shù)據集進行一個劃分,比如使用80%的訓練數(shù)據來訓練,20%的數(shù)據用來測試,
dev_cutoff = len(Y) * 4/5
X_dev = X[:dev_cutoff]
Y_dev = Y[:dev_cutoff]
X_test = X[dev_cutoff:]
Y_test = Y[dev_cutoff:]
然后對訓練數(shù)據通過交叉驗證訓練 clf,并得到第二層的訓練數(shù)據 blend_train,同時,在每個基分類器的每一折交叉驗證中,我們都會對測試數(shù)據進行一次預測,以得到我們blend_test,二者的定義如下:
blend_train = np.zeros((X_dev.shape[0], len(clfs))) # Number of training data x Number of classifiers
blend_test = np.zeros((X_test.shape[0], len(clfs))) # Number of testing data x Number of classifiers
按照上面說的,blend_train基于下面的方法得到,注意,下圖是對于一個分類器來說的,所以每個分類器得到的blend_train的行數(shù)與用于訓練的數(shù)據一樣多,所以blend_train的shape為X_dev.shape[0]*len(clfs),即訓練集長度 * 基分類器個數(shù):
而對于第二輪的測試集blend_test來說,由于每次交叉驗證的過程中都要進行一次預測,假設我們是5折交叉驗證,那么對于每個分類器來說,得到的blend_test的shape是測試集行數(shù) * 交叉驗證折數(shù),此時的做法是,對axis=1方向取平均值,以得到測試集行數(shù) * 1 的測試數(shù)據,所以總的blend_test就是測試集行數(shù) * 基分類器個數(shù),可以跟blend_train保持一致:
得到blend_train 和 blend_test的代碼如下:
for j, clf in enumerate(clfs):
print 'Training classifier [%s]' % (j)
blend_test_j = np.zeros((X_test.shape[0], len(skf))) # Number of testing data x Number of folds , we will take the mean of the predictions later
for i, (train_index, cv_index) in enumerate(skf):
print 'Fold [%s]' % (i)
# This is the training and validation set
X_train = X_dev[train_index]
Y_train = Y_dev[train_index]
X_cv = X_dev[cv_index]
Y_cv = Y_dev[cv_index]
clf.fit(X_train, Y_train)
# This output will be the basis for our blended classifier to train against,
# which is also the output of our classifiers
blend_train[cv_index, j] = clf.predict(X_cv)
blend_test_j[:, i] = clf.predict(X_test)
# Take the mean of the predictions of the cross validation set
blend_test[:, j] = blend_test_j.mean(1)
接著我們就可以用 blend_train, Y_dev 去訓練第二層的學習器 LogisticRegression(當然也可以是別的分類器,比如lightGBM,XGBoost):
bclf = LogisticRegression()
bclf.fit(blend_train, Y_dev)
最后,基于我們訓練的二級分類器,我們可以預測測試集 blend_test,并得到 score:
Y_test_predict = bclf.predict(blend_test)
score = metrics.accuracy_score(Y_test, Y_test_predict)
print 'Accuracy = %s' % (score)
如果是多分類怎么辦呢,我們這里就不能用predict方法啦,我么要用的是predict_proba方法,得到基分類器對每個類的預測概率代入二級分類器中訓練,修改的部分代碼如下:
blend_train = np.zeros((np.array(X_dev.values.tolist()).shape[0], num_classes*len(clfs)),dtype=np.float32) # Number of training data x Number of classifiers
blend_test = np.zeros((np.array(X_test.values.tolist()).shape[0], num_classes*len(clfs)),dtype=np.float32) # Number of testing data x Number of classifiers
# For each classifier, we train the number of fold times (=len(skf))
for j, clf in enumerate(clfs):
for i, (train_index, cv_index) in enumerate(skf):
print('Fold [%s]' % (i))
# This is the training and validation set
X_train = X_dev[train_index]
Y_train = Y_dev[train_index]
X_cv = X_dev[cv_index]
X_train = np.concatenate((X_train, ret_x),axis=0)
Y_train = np.concatenate((Y_train, ret_y),axis=0)
clf.fit(X_train, Y_train)
blend_train[cv_index, j*num_classes:(j+1)*num_classes] = clf.predict_proba(X_cv)
blend_test[:, j*num_classes:(j+1)*num_classes] += clf.predict_proba(X_test)
blend_test = blend_test / float(n_folds)
上面的代碼修改的主要就是blend_train和blend_test的shape,可以看到,對于多分類問題來說,二者的第二維的shape不再是基分類器的數(shù)量,而是class的數(shù)量*基分類器的數(shù)量,這是大家要注意的,否則可能不會得到我們想要的結果。