RandomForest:随机森林
隨機森林:RF
隨機森林是一種一決策樹為基學習器的Bagging算法,但是不同之處在于RF決策樹的訓練過程中還加入了隨機屬性選擇(特征上的子采樣)
- 傳統的決策樹在選擇劃分的屬性時,會選擇最優屬性
- RF
參數
使用這些方法時要調整的參數主要是 n_estimators 和 max_features。 前者(n_estimators)是森林里樹的數量,通常數量越大,效果越好,但是計算時間也會隨之增加。 此外要注意,當樹的數量超過一個臨界值之后,算法的效果并不會很顯著地變好。 后者(max_features)是分割節點時考慮的特征的隨機子集的大小。 這個值越低,方差減小得越多,但是偏差的增大也越多。 根據經驗,回歸問題中使用 max_features = n_features, 分類問題使用max_features = sqrt(n_features (其中 n_features 是特征的個數)是比較好的默認值。max_depth = None和 min_samples_split = 2 結合通常會有不錯的效果(即生成完全的樹)。 請記住,這些(默認)值通常不是最佳的,同時還可能消耗大量的內存,最佳參數值應由交叉驗證獲得。 另外,請注意,在隨機森林中,默認使用自助采樣法(bootstrap = True), 然而 extra-trees 的默認策略是使用整個數據集(bootstrap = False)。 當使用自助采樣法方法抽樣時,泛化精度是可以通過剩余的或者袋外的樣本來估算的,設置 oob_score = True 即可實現。
提示:
默認參數下模型復雜度是:O(M*N*log(N)) , 其中 M 是樹的數目, N 是樣本數。 可以通過設置以下參數來降低模型復雜度: min_samples_split ,?min_samples_leaf ,?max_leaf_nodes?和?max_depth 。
偏差與方差問題
理論部分
實驗部分
此處我們對sklearn中的Single estimator versus bagging: bias-variance decomposition示例進行稍微的修改,用來展示極限隨機森林與隨機森林,普通決策樹在同一數據集上的方差-偏差分解
import numpy as np import matplotlib.pyplot as pltplt.figure(figsize=(20, 10))from sklearn.ensemble import ExtraTreesRegressor, RandomForestRegressor from sklearn.tree import DecisionTreeRegressor# Settings n_repeat = 50 # Number of iterations for computing expectations n_train = 50 # Size of the training set n_test = 1000 # Size of the test set noise = 0.1 # Standard deviation of the noise np.random.seed(0)estimators = [("Tree", DecisionTreeRegressor()),("RandomForestRegressor", RandomForestRegressor(random_state=100,bootstrap = True)),("ExtraTreesClassifier", ExtraTreesRegressor(random_state=100,bootstrap = True)), ]n_estimators = len(estimators)# Generate data def f(x):x = x.ravel()return np.exp(-x ** 2) + 1.5 * np.exp(-(x - 2) ** 2)def generate(n_samples, noise, n_repeat=1):X = np.random.rand(n_samples) * 10 - 5X = np.sort(X)if n_repeat == 1:y = f(X) + np.random.normal(0.0, noise, n_samples)else:y = np.zeros((n_samples, n_repeat))for i in range(n_repeat):y[:, i] = f(X) + np.random.normal(0.0, noise, n_samples)X = X.reshape((n_samples, 1))return X, yX_train = [] y_train = []for i in range(n_repeat):X, y = generate(n_samples=n_train, noise=noise)X_train.append(X)y_train.append(y)X_test, y_test = generate(n_samples=n_test, noise=noise, n_repeat=n_repeat)# Loop over estimators to compare for n, (name, estimator) in enumerate(estimators):# Compute predictionsy_predict = np.zeros((n_test, n_repeat))for i in range(n_repeat):estimator.fit(X_train[i], y_train[i])y_predict[:, i] = estimator.predict(X_test)# Bias^2 + Variance + Noise decomposition of the mean squared errory_error = np.zeros(n_test)for i in range(n_repeat):for j in range(n_repeat):y_error += (y_test[:, j] - y_predict[:, i]) ** 2y_error /= (n_repeat * n_repeat)y_noise = np.var(y_test, axis=1)y_bias = (f(X_test) - np.mean(y_predict, axis=1)) ** 2y_var = np.var(y_predict, axis=1)print("{0}: {1:.4f} (error) = {2:.4f} (bias^2) "" + {3:.4f} (var) + {4:.4f} (noise)".format(name,np.mean(y_error),np.mean(y_bias),np.mean(y_var),np.mean(y_noise)))# Plot figuresplt.subplot(2, n_estimators, n + 1)plt.plot(X_test, f(X_test), "b", label="$f(x)$")plt.plot(X_train[0], y_train[0], ".b", label="LS ~ $y = f(x)+noise$")for i in range(n_repeat):if i == 0:plt.plot(X_test, y_predict[:, i], "r", label="$\^y(x)$")else:plt.plot(X_test, y_predict[:, i], "r", alpha=0.05)plt.plot(X_test, np.mean(y_predict, axis=1), "c",label="$\mathbb{E}_{LS} \^y(x)$")plt.xlim([-5, 5])plt.title(name)if n == 0:plt.legend(loc="upper left", prop={"size": 11})plt.subplot(2, n_estimators, n_estimators + n + 1)plt.plot(X_test, y_error, "r", label="$error(x)$")plt.plot(X_test, y_bias, "b", label="$bias^2(x)$"),plt.plot(X_test, y_var, "g", label="$variance(x)$"),plt.plot(X_test, y_noise, "c", label="$noise(x)$")plt.xlim([-5, 5])plt.ylim([0, 0.1])if n == 0:plt.legend(loc="upper left", prop={"size": 11})plt.show() Tree: 0.0255 (error) = 0.0003 (bias^2) + 0.0152 (var) + 0.0098 (noise) RandomForestRegressor: 0.0202 (error) = 0.0004 (bias^2) + 0.0098 (var) + 0.0098 (noise) ExtraTreesClassifier: 0.0175 (error) = 0.0011 (bias^2) + 0.0065 (var) + 0.0098 (noise)由實驗結果我們可以很好地看出,相對于一般的決策樹,隨機森林雖然增加了模型的偏差,但是大幅度降低了方差,因而在整體上獲取了更好的結果;而相比之下,在剛剛實驗中的RF算法方差仍然遠遠大于偏差,這個時候我們就可以采用極限隨機森林,正因為一般而言極限隨機森林相對于隨機森林進一步增加了偏差,同時進一步下降了方差,因為在該實驗中極限隨機森林應當獲取要優于隨機森林的效果(不過這種趨勢并不一定是百分之百的)
特征重要程度評估
特征對于目標變量的相對重要程度,可以根據特征使用的相對順序進行評估。決策樹頂部使用的特征對更大一部分輸入樣本的最終預測結果做出貢獻;因此,可以使用接受每個特征對最終預測的貢獻的樣本比例來評估該 特征的相對重要性 。
在RF中,通過歲多個隨機數中的預測貢獻率進行平均,降低了方差,因此可用于特征選擇。不過要注意的是隨機森林與極限隨機森林對于同一個數據集根除的重要程度不一定相同,而且即使是一個模型在參數不同的情況下,最終結果也并不一定相同
因為極限隨機森林的特殊性質,所以請不要采用極限隨機森林進行特征重要程度的排名,建議使用RF.
import numpy as np from sklearn.datasets import make_classification from sklearn.ensemble import ExtraTreesClassifier, RandomForestClassifier import matplotlib.pyplot as pltplt.figure(figsize=(8, 4))estimators = [("RandomForest", RandomForestClassifier(random_state=100)),("ExtraTrees", ExtraTreesClassifier(random_state=100)), ]n_estimators = len(estimators)# Build a classification task using 3 informative features X, y = make_classification(n_samples=1000,n_features=10,n_informative=3,n_redundant=0,n_repeated=0,n_classes=2,random_state=0,shuffle=False)# Build a forest and compute the feature importances forest = ExtraTreesClassifier(n_estimators=250,random_state=0)forest.fit(X, y)for n, (name, estimator) in enumerate(estimators):estimator.fit(X, y)importances = estimator.feature_importances_std = np.std([tree.feature_importances_ for tree in forest.estimators_],axis=0)indices = np.argsort(importances)[::-1]# Print the feature ranking # print(name +" Feature ranking:") # for f in range(X.shape[1]): # print("%d. feature %d (%f)" % (f + 1, indices[f], importances[indices[f]]))# Plot the feature importances of the forestplt.subplot(1, n_estimators, n + 1)plt.title(name + " Feature importances")plt.bar(range(X.shape[1]), importances[indices],color="r", yerr=std[indices], align="center")plt.xticks(range(X.shape[1]), indices)plt.xlim([-1, X.shape[1]]) plt.show()完全隨機樹嵌入
sklearn中還實現了隨機森林的一種特殊用法,即完全隨機樹嵌入(RandomTreesEmbedding)。RandomTreesEmbedding 實現了一個無監督的數據轉換。 通過由完全隨機樹構成的森林,RandomTreesEmbedding 使用數據最終歸屬的葉子節點的索引值(編號)對數據進行編碼。 該索引以 one-of-K 方式編碼,最終形成一個高維的稀疏二進制編碼。 這種編碼可以被非常高效地計算出來,并且可以作為其他學習任務的基礎。 編碼的大小和稀疏度可以通過選擇樹的數量和每棵樹的最大深度來確定。對于集成中的每棵樹的每個節點包含一個實例(校對者注:這里真的沒搞懂)。 編碼的大小(維度)最多為 n_estimators * 2 ** max_depth,即森林中的葉子節點的最大數。
其作用一共有兩種:
對于功能一
下面是一個驗證完全隨機樹嵌入作用的兩個例子:
例子一:使用完全隨機樹嵌入進行散列特征轉換
RandomTreesEmbedding提供了一種將數據映射到非常高維稀疏表示的方法,這可能有助于分類。該映射是完全無監督的,非常有效。
這個例子顯示了由幾棵樹給出的分區,并且顯示了變換如何也可以用于非線性降維或者非線性分類。
相鄰的點經常共享同一個樹的葉節點并且因此共享大部分的散列表示,這允許截斷奇異值分解(truncated SVD)可以分離數據轉換后的兩個同心圓。
在高維空間中,線性分類器通常達到極好的精度。對于稀疏的二進制數據,BernoulliNB特別適合。最下面一行將BernoulliNB在變換空間中獲得的決策邊界與在原始數據上學習的ExtraTreesClassifier森林進行比較。
from sklearn.datasets import make_circles from sklearn.ensemble import RandomTreesEmbedding, ExtraTreesClassifier from sklearn.decomposition import TruncatedSVD from sklearn.naive_bayes import BernoulliNB# make a synthetic dataset X, y = make_circles(factor=0.5, random_state=0, noise=0.05)# use RandomTreesEmbedding to transform data hasher = RandomTreesEmbedding(n_estimators=10, random_state=0, max_depth=3) X_transformed = hasher.fit_transform(X)# Visualize result after dimensionality reduction using truncated SVD svd = TruncatedSVD(n_components=2) X_reduced = svd.fit_transform(X_transformed)# Learn a Naive Bayes classifier on the transformed data nb = BernoulliNB() nb.fit(X_transformed, y)# Learn an ExtraTreesClassifier for comparison trees = ExtraTreesClassifier(max_depth=3, n_estimators=10, random_state=0) trees.fit(X, y)# scatter plot of original and reduced data fig = plt.figure(figsize=(9, 8))ax = plt.subplot(221) ax.scatter(X[:, 0], X[:, 1], c=y, s=50, edgecolor='k') ax.set_title("Original Data (2d)") ax.set_xticks(()) ax.set_yticks(())ax = plt.subplot(222) ax.scatter(X_reduced[:, 0], X_reduced[:, 1], c=y, s=50, edgecolor='k') ax.set_title("Truncated SVD reduction (2d) of transformed data (%dd)" %X_transformed.shape[1]) ax.set_xticks(()) ax.set_yticks(())# Plot the decision in original space. For that, we will assign a color # to each point in the mesh [x_min, x_max]x[y_min, y_max]. h = .01 x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5 y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5 xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))# transform grid using RandomTreesEmbedding transformed_grid = hasher.transform(np.c_[xx.ravel(), yy.ravel()]) y_grid_pred = nb.predict_proba(transformed_grid)[:, 1]ax = plt.subplot(223) ax.set_title("Naive Bayes on Transformed data") ax.pcolormesh(xx, yy, y_grid_pred.reshape(xx.shape)) ax.scatter(X[:, 0], X[:, 1], c=y, s=50, edgecolor='k') ax.set_ylim(-1.4, 1.4) ax.set_xlim(-1.4, 1.4) ax.set_xticks(()) ax.set_yticks(())# transform grid using ExtraTreesClassifier y_grid_pred = trees.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]ax = plt.subplot(224) ax.set_title("ExtraTrees predictions") ax.pcolormesh(xx, yy, y_grid_pred.reshape(xx.shape)) ax.scatter(X[:, 0], X[:, 1], c=y, s=50, edgecolor='k') ax.set_ylim(-1.4, 1.4) ax.set_xlim(-1.4, 1.4) ax.set_xticks(()) ax.set_yticks(())plt.tight_layout() plt.show()[關于生成新特征這一作用的實驗](Feature transformations with ensembles of trees)
完全隨機樹嵌入,可以將特征轉化為更高維度,更稀疏的空間,方式為首先在數據集上訓練模型(極限隨機森林,隨機森林,GBT系列皆可)然后將新的特征空間中每個葉節點都會分配一個固定的特征索引,然后將所有的葉節點進行獨熱編碼,通過將樣本所在的葉子設置為1,其他特征設置為0,來對樣本進行編碼,將其轉轉換到稀疏的,高維度的空間.
下面的代碼展示了,不同轉換模型轉換出的特征最終得到特征在LR上的分類效果,第二幅圖是第一幅圖左上角的放大,可以看出在本數據集上似乎還是GBT系列的轉換效果好一些(你也可以使用lightGBM與XGBoot中的sklearn借口,實現代碼中GradientBoostingClassifier類似的效果)
import numpy as np np.random.seed(10)import matplotlib.pyplot as pltfrom sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.ensemble import (RandomTreesEmbedding, RandomForestClassifier,GradientBoostingClassifier) from sklearn.preprocessing import OneHotEncoder from sklearn.model_selection import train_test_split from sklearn.metrics import roc_curve from sklearn.pipeline import make_pipelinen_estimator = 10 X, y = make_classification(n_samples=80000) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5) # It is important to train the ensemble of trees on a different subset # of the training data than the linear regression model to avoid # overfitting, in particular if the total number of leaves is # similar to the number of training samples X_train, X_train_lr, y_train, y_train_lr = train_test_split(X_train,y_train,test_size=0.5)# Unsupervised transformation based on totally random trees rt = RandomTreesEmbedding(max_depth=3, n_estimators=n_estimator,random_state=0)rt_lm = LogisticRegression() pipeline = make_pipeline(rt, rt_lm) pipeline.fit(X_train, y_train) y_pred_rt = pipeline.predict_proba(X_test)[:, 1] fpr_rt_lm, tpr_rt_lm, _ = roc_curve(y_test, y_pred_rt)# Supervised transformation based on random forests rf = RandomForestClassifier(max_depth=3, n_estimators=n_estimator) rf_enc = OneHotEncoder() rf_lm = LogisticRegression() rf.fit(X_train, y_train) rf_enc.fit(rf.apply(X_train)) rf_lm.fit(rf_enc.transform(rf.apply(X_train_lr)), y_train_lr)y_pred_rf_lm = rf_lm.predict_proba(rf_enc.transform(rf.apply(X_test)))[:, 1] fpr_rf_lm, tpr_rf_lm, _ = roc_curve(y_test, y_pred_rf_lm)grd = GradientBoostingClassifier(n_estimators=n_estimator) grd_enc = OneHotEncoder() grd_lm = LogisticRegression() grd.fit(X_train, y_train) grd_enc.fit(grd.apply(X_train)[:, :, 0]) grd_lm.fit(grd_enc.transform(grd.apply(X_train_lr)[:, :, 0]), y_train_lr)y_pred_grd_lm = grd_lm.predict_proba(grd_enc.transform(grd.apply(X_test)[:, :, 0]))[:, 1] fpr_grd_lm, tpr_grd_lm, _ = roc_curve(y_test, y_pred_grd_lm)# The gradient boosted model by itself y_pred_grd = grd.predict_proba(X_test)[:, 1] fpr_grd, tpr_grd, _ = roc_curve(y_test, y_pred_grd)# The random forest model by itself y_pred_rf = rf.predict_proba(X_test)[:, 1] fpr_rf, tpr_rf, _ = roc_curve(y_test, y_pred_rf)plt.figure(1) plt.plot([0, 1], [0, 1], 'k--') plt.plot(fpr_rt_lm, tpr_rt_lm, label='RT + LR') plt.plot(fpr_rf, tpr_rf, label='RF') plt.plot(fpr_rf_lm, tpr_rf_lm, label='RF + LR') plt.plot(fpr_grd, tpr_grd, label='GBT') plt.plot(fpr_grd_lm, tpr_grd_lm, label='GBT + LR') plt.xlabel('False positive rate') plt.ylabel('True positive rate') plt.title('ROC curve') plt.legend(loc='best') plt.show()plt.figure(2) plt.xlim(0, 0.2) plt.ylim(0.8, 1) plt.plot([0, 1], [0, 1], 'k--') plt.plot(fpr_rt_lm, tpr_rt_lm, label='RT + LR') plt.plot(fpr_rf, tpr_rf, label='RF') plt.plot(fpr_rf_lm, tpr_rf_lm, label='RF + LR') plt.plot(fpr_grd, tpr_grd, label='GBT') plt.plot(fpr_grd_lm, tpr_grd_lm, label='GBT + LR') plt.xlabel('False positive rate') plt.ylabel('True positive rate') plt.title('ROC curve (zoomed in at top left)') plt.legend(loc='best') plt.show()
除此之外,隨機森林還可以進行異常檢測,sklearn中也實現了該算法IsolationForest.更多內容請閱讀我的博客。
參考
- sklearn官方文檔:ensemble
- sklearn官方文檔:使用完全隨機數進行散列特征轉換
- sklearn官方文檔:Hashing feature transformation using Totally Random Trees
- sklearn ApacheCN中文官方文檔:集成算法
總結
以上是生活随笔為你收集整理的RandomForest:随机森林的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 通过模型进行特征选择
- 下一篇: Gradient Tree Boosti