森林算法詳解:從決策樹原理到Python實(shí)戰(zhàn)與遙感應(yīng)用)
上周有個(gè)做信貸風(fēng)控的朋友來(lái)找我說(shuō)現(xiàn)在業(yè)務(wù)方每次都要模型解釋為什么要拒絕某筆貸款申請(qǐng)單純給一個(gè)“評(píng)分”已經(jīng)糊弄不過(guò)去了。我?guī)退?xùn)練了一個(gè)隨機(jī)森林分類預(yù)測(cè)算法模型輸出特征重要性排序再用predict_proba給每個(gè)客戶返回違約概率業(yè)務(wù)方終于不再追問“黑盒”問題了。這個(gè)場(chǎng)景其實(shí)只是隨機(jī)森林在實(shí)際應(yīng)用里的一個(gè)典型縮影。隨機(jī)森林可以說(shuō)是過(guò)來(lái)人最愿意推薦給新人的入門級(jí)“高級(jí)算法”原理不復(fù)雜、默認(rèn)參數(shù)下通常就有不錯(cuò)精度、自帶特征重要性評(píng)估、在分類和回歸任務(wù)上通吃遙感影像分類這類高維場(chǎng)景它也長(zhǎng)期是主力算法之一。這篇文章把這套內(nèi)容完整展開從算法原理講到Python源碼實(shí)現(xiàn)再到遙感落地時(shí)的坑和調(diào)優(yōu)思路所有代碼和數(shù)據(jù)處理邏輯我都按可直接復(fù)現(xiàn)的方式給出適合剛接觸機(jī)器學(xué)習(xí)想找一個(gè)可靠起點(diǎn)的人也適合已經(jīng)用過(guò)但總調(diào)不出效果、想搞清楚參數(shù)邏輯的人。1. 隨機(jī)森林為什么被“神話”從決策樹到Bagging的進(jìn)化邏輯很多人第一次接觸隨機(jī)森林感覺就是“把很多決策樹放在一起投票”。這個(gè)直覺是對(duì)的但只對(duì)了一半。要真正理解隨機(jī)森林的價(jià)值得先回到它的前身決策樹身上看看單棵樹到底有什么毛病隨機(jī)森林又是怎么補(bǔ)上這些毛病的。1.1 決策樹的天然缺陷單棵樹的“選擇困難癥”決策樹的核心邏輯是一層層做條件判斷某個(gè)特征大于多少進(jìn)左分支否則進(jìn)右分支直到把樣本分到某個(gè)葉子節(jié)點(diǎn)。它分裂時(shí)靠的是信息增益或基尼系數(shù)這類指標(biāo)本質(zhì)上是找“哪個(gè)特征、哪個(gè)切分點(diǎn)能把數(shù)據(jù)分得最純”。聽起來(lái)很合理但單棵決策樹有一個(gè)很要命的毛病極其容易過(guò)擬合。一棵完全長(zhǎng)開的決策樹會(huì)把訓(xùn)練集里每個(gè)樣本的細(xì)節(jié)都記住包括那些純屬噪聲的部分。比如訓(xùn)練集里有三個(gè)樣本恰好因?yàn)槟撤N隨機(jī)波動(dòng)聚在一起決策樹也會(huì)為它們單獨(dú)切出一個(gè)分支。結(jié)果就是訓(xùn)練集精度接近100%測(cè)試集一跑立刻掉鏈子泛化能力很差。另一個(gè)毛病是穩(wěn)定性差。我做過(guò)一個(gè)最直觀的實(shí)驗(yàn)用同一份數(shù)據(jù)訓(xùn)練兩棵決策樹只是把訓(xùn)練集里1%的樣本隨機(jī)換掉兩棵樹的分裂結(jié)構(gòu)能差出一大截。有時(shí)候連根節(jié)點(diǎn)的分裂特征都會(huì)變。如果這種不穩(wěn)定的模型直接放到業(yè)務(wù)里去用今天跑一個(gè)結(jié)果、明天跑一個(gè)結(jié)果業(yè)務(wù)方肯定覺得你在瞎搞。1.2 Bagging的集體智慧隨機(jī)森林真正的隨機(jī)在哪決策樹的問題在于“一個(gè)人拍板容易拍錯(cuò)”那很自然的解法就是找一堆人來(lái)投票這就是BaggingBootstrap Aggregating的核心思想。Bagging做了兩件事。第一從原始訓(xùn)練集里有放回地抽樣抽出一批數(shù)據(jù)子集相當(dāng)于給每棵樹“換了一批樣本看”。有放回意味著有些樣本會(huì)在同一棵樹的訓(xùn)練數(shù)據(jù)里出現(xiàn)多次有些樣本一次都不出現(xiàn)。第二每棵樹在自己的子集上獨(dú)立訓(xùn)練最終分類取所有樹的投票結(jié)果回歸取所有樹的平均值。而隨機(jī)森林在Bagging基礎(chǔ)上又加了一層隨機(jī)——特征隨機(jī)。每次分裂時(shí)不是從所有特征里找最優(yōu)而是先隨機(jī)挑出一個(gè)特征子集然后只在這個(gè)子集里找最優(yōu)分裂。這層隨機(jī)非常關(guān)鍵它讓樹與樹之間的差異進(jìn)一步放大。如果不用特征隨機(jī)就算樣本不同但幾棵樹的“眼光”都集中在少數(shù)幾個(gè)強(qiáng)特征上結(jié)果就是樹之間高度相似投票跟一個(gè)人投沒什么區(qū)別。這背后的道理可以拿“三個(gè)臭皮匠”來(lái)類比。單個(gè)皮匠容易判斷失誤但如果三個(gè)皮匠各自掌握的信息不完全一樣、判斷依據(jù)也有差異那他們投票的結(jié)果往往比一個(gè)人的判斷靠譜得多。隨機(jī)森林正是通過(guò)樣本隨機(jī)和特征隨機(jī)人為制造了一群“角度不同”的樹讓它們的集體判斷去抵消單棵樹的偏差和噪聲。1.3 隨機(jī)森林的分類、回歸和遙感場(chǎng)景同一套框架的三種用法很多人以為隨機(jī)森林只能做分類其實(shí)它是一套框架任務(wù)類型不同只是輸出層的處理方式不同。分類任務(wù)每棵樹輸出一個(gè)類別森林做多數(shù)投票。典型場(chǎng)景包括客戶違約預(yù)測(cè)、疾病診斷、文本分類等?;貧w任務(wù)每棵樹輸出一個(gè)數(shù)值森林對(duì)結(jié)果取平均。典型場(chǎng)景包括房?jī)r(jià)預(yù)測(cè)、銷量預(yù)測(cè)、氣象要素估測(cè)等。遙感影像分類把每個(gè)像元的光譜波段值、植被指數(shù)、紋理特征拼成一個(gè)特征向量用隨機(jī)森林判斷這塊地是林地、水體還是建筑區(qū)。其中遙感場(chǎng)景特別能體現(xiàn)隨機(jī)森林的優(yōu)勢(shì)。遙感影像特征維度高、波段之間關(guān)系復(fù)雜、樣本質(zhì)量參差不齊很多傳統(tǒng)算法在這種數(shù)據(jù)上容易出問題而隨機(jī)森林對(duì)高維特征和高噪聲數(shù)據(jù)的容忍度比較好。后面我會(huì)專門用一節(jié)展開說(shuō)這個(gè)場(chǎng)景的實(shí)操細(xì)節(jié)。2. 動(dòng)手之前的環(huán)境準(zhǔn)備與數(shù)據(jù)構(gòu)建這份代碼的“地基”任何算法離開了數(shù)據(jù)都是空談。這一節(jié)先把環(huán)境、數(shù)據(jù)來(lái)源、預(yù)處理這幾塊地基打好后面跑代碼時(shí)才不會(huì)各種莫名其妙報(bào)錯(cuò)。2.1 版本推薦別讓sklearn版本坑了你先看環(huán)境。我推薦Python 3.9以上scikit-learn版本在1.0以上。原因很簡(jiǎn)單1.0版本開始sklearn的API統(tǒng)一和穩(wěn)定性明顯上了一個(gè)臺(tái)階很多老版本里的參數(shù)默認(rèn)值在新版里也調(diào)整過(guò)了。你現(xiàn)在網(wǎng)上隨便搜一份“隨機(jī)森林python代碼”很可能是三四年前寫的里面有些參數(shù)用法在新版本下會(huì)報(bào)warning甚至直接報(bào)錯(cuò)。我自己踩過(guò)一個(gè)印象深刻的坑在sklearn 0.22的舊項(xiàng)目里RandomForestClassifier的一個(gè)參數(shù)叫n_estimators另一個(gè)是oob_score老代碼里有人直接傳了oob_scoreTrue卻忘了設(shè)max_features結(jié)果每次跑出來(lái)的特征重要性排序都不對(duì)。升級(jí)到新版本后官方改了部分內(nèi)部實(shí)現(xiàn)同樣的參數(shù)組合行為完全不一樣。所以跑代碼前先執(zhí)行下面這句看一眼版本python -c import sklearn; print(sklearn.__version__)如果你是1.0以上版本這篇文章里的代碼可以無(wú)縫運(yùn)行如果低于1.0建議直接用pip升級(jí)pip install --upgrade scikit-learn2.2 數(shù)據(jù)從哪來(lái)自帶數(shù)據(jù)集還是自己造數(shù)據(jù)想要快速驗(yàn)證隨機(jī)森林的效果最省事的方式是使用sklearn自帶的數(shù)據(jù)集。比如load_breast_cancer乳腺癌診斷二分類、load_iris鳶尾花三分類、load_digits手寫數(shù)字多分類、fetch_california_housing房?jī)r(jià)回歸。其中乳腺癌數(shù)據(jù)集我強(qiáng)烈推薦拿來(lái)入門隨機(jī)森林分類。它樣本量569條、特征30維特征之間有一定的相關(guān)性和冗余類別相對(duì)均衡規(guī)模適中跑起來(lái)快又能明顯看出特征選擇對(duì)精度的貢獻(xiàn)。如果想體驗(yàn)自己造數(shù)據(jù)的感覺也可以用make_classification函數(shù)合成一份分類數(shù)據(jù)from sklearn.datasets import make_classification X, y make_classification( n_samples1000, n_features10, n_informative6, n_redundant2, random_state42 )這段代碼會(huì)生成1000條樣本、10個(gè)特征的二分類數(shù)據(jù)其中6個(gè)特征真正有用2個(gè)特征是從有用特征里冗余派生出來(lái)的。用它來(lái)感受隨機(jī)森林在不同特征重要性下的表現(xiàn)非常直觀。2.3 數(shù)據(jù)預(yù)處理專業(yè)項(xiàng)目里必須做的那幾步隨機(jī)森林對(duì)數(shù)據(jù)預(yù)處理的要求在所有機(jī)器學(xué)習(xí)算法里算比較低的但這不代表可以完全不管有三件事必須做缺失值處理。sklearn的隨機(jī)森林不支持特征里的NaN值遇到會(huì)直接報(bào)錯(cuò)。常見辦法是剔除缺失比例過(guò)高的樣本或用均值、中位數(shù)、眾數(shù)填充。注意填充規(guī)則只能在訓(xùn)練集上計(jì)算再應(yīng)用到測(cè)試集不能整份數(shù)據(jù)一起填充否則數(shù)據(jù)泄露。類別特征編碼。隨機(jī)森林本身不能直接吃字符串類別特征需要先用LabelEncoder或OneHotEncoder轉(zhuǎn)成數(shù)值。如果類別特征有順序關(guān)系比如等級(jí)、評(píng)分用標(biāo)簽編碼沒有順序關(guān)系比如顏色、地區(qū)用獨(dú)熱編碼更穩(wěn)妥。標(biāo)準(zhǔn)化可有可無(wú)。決策樹模型是基于分裂閾值的不關(guān)心特征的量綱所以不像SVM和神經(jīng)網(wǎng)絡(luò)那樣必須先做標(biāo)準(zhǔn)化。你甚至可以把“是否需要標(biāo)準(zhǔn)化”作為判斷一個(gè)模型本質(zhì)是幾何型還是樹型的試金石。這里最值得強(qiáng)調(diào)的還是缺失值填充。我見過(guò)不止一個(gè)初學(xué)者把訓(xùn)練集和測(cè)試集合在一起算均值填充結(jié)果模型表現(xiàn)虛高上線之后立刻崩掉。正確的順序是先用訓(xùn)練集fit出填充值再transform訓(xùn)練集和測(cè)試集。from sklearn.impute import SimpleImputer imp_mean SimpleImputer(strategymean) # 在訓(xùn)練集上擬合 X_train_imp imp_mean.fit_transform(X_train) X_test_imp imp_mean.transform(X_test)3. 隨機(jī)森林Python核心代碼拆解從訓(xùn)練到預(yù)測(cè)的完整鏈路這一節(jié)是整篇的核心把隨機(jī)森林的Python代碼從頭到尾拆開講。我不光給完整代碼還會(huì)解釋每個(gè)關(guān)鍵參數(shù)在干什么、哪些參數(shù)值得調(diào)、哪些參數(shù)其實(shí)沒必要天天動(dòng)。3.1 核心參數(shù)解讀n_estimators、max_depth這些參數(shù)怎么調(diào)先看RandomForestClassifier最常用的一套參數(shù)我整理成了一張速查表參數(shù)作用推薦配置我的經(jīng)驗(yàn)n_estimators樹的數(shù)量100-500超過(guò)300后精度增長(zhǎng)基本停滯模型還變慢max_depth單棵樹最大深度None或10-20數(shù)據(jù)量小、噪聲大時(shí)必須限制防過(guò)擬合min_samples_split內(nèi)部節(jié)點(diǎn)再分裂所需最小樣本數(shù)2-10調(diào)大到10-20能明顯抑制過(guò)擬合min_samples_leaf葉子節(jié)點(diǎn)最少樣本數(shù)1-5調(diào)大到5模型平滑效果顯著max_features每次分裂考慮的特征數(shù)分類默認(rèn)sqrt回歸默認(rèn)1.0默認(rèn)值通常就夠用不用瞎改random_state隨機(jī)種子固定一個(gè)整數(shù)必須固定否則結(jié)果不可復(fù)現(xiàn)oob_score是否用袋外樣本評(píng)估True可替代復(fù)雜的交叉驗(yàn)證快速看泛化能力這里我需要重點(diǎn)解釋n_estimators。很多人以為樹越多越好于是拼命往上加加到1000棵。實(shí)際上隨機(jī)森林的誤差隨著樹數(shù)量增加會(huì)收斂但收斂非常快超過(guò)某個(gè)閾值后再增加樹的數(shù)量對(duì)精度幾乎沒有提升只增加訓(xùn)練時(shí)間和預(yù)測(cè)時(shí)間。我實(shí)測(cè)過(guò)很多份數(shù)據(jù)基本在200到300棵時(shí)精度就到平臺(tái)期了。真正值得花精力調(diào)的往往是max_depth和min_samples_leaf這兩個(gè)參數(shù)對(duì)過(guò)擬合的抑制最明顯。3.2 分類和回歸的兩套代碼模板先說(shuō)分類模板。我用乳腺癌數(shù)據(jù)集做例子下面是完整流程from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report, accuracy_score, roc_auc_score # 1. 加載數(shù)據(jù) data load_breast_cancer() X, y data.data, data.target # 2. 劃分訓(xùn)練集和測(cè)試集 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) # 3. 創(chuàng)建模型 rf_clf RandomForestClassifier( n_estimators200, max_depthNone, min_samples_leaf1, random_state42, oob_scoreTrue, n_jobs-1 ) # 4. 訓(xùn)練 rf_clf.fit(X_train, y_train) # 5. 預(yù)測(cè)與評(píng)估 y_pred rf_clf.predict(X_test) y_proba rf_clf.predict_proba(X_test)[:, 1] print(Accuracy:, accuracy_score(y_test, y_pred)) print(AUC:, roc_auc_score(y_test, y_proba)) print(classification_report(y_test, y_pred)) print(OOB Score:, rf_clf.oob_score_)幾個(gè)我在實(shí)際工作中總結(jié)出來(lái)的細(xì)節(jié)stratifyy這一步很重要。如果分類標(biāo)簽不平衡不做分層抽樣測(cè)試集里某個(gè)類別的比例可能和總體差很多導(dǎo)致評(píng)估結(jié)果失真。predict_proba是隨機(jī)森林的一大優(yōu)勢(shì)。它能輸出每個(gè)樣本屬于每個(gè)類別的概率而不是僅僅給一個(gè)硬分類。在信貸風(fēng)控這類業(yè)務(wù)里概率值比類別標(biāo)簽有用得多可以配合閾值做更細(xì)致的決策。n_jobs-1表示用所有CPU核并行訓(xùn)練。隨機(jī)森林天然適合并行樹和樹之間相互獨(dú)立數(shù)據(jù)量大時(shí)這個(gè)參數(shù)能省大量時(shí)間?;貧w模板也很簡(jiǎn)單把分類器換成RandomForestRegressor就行from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_squared_error, r2_score rf_reg RandomForestRegressor( n_estimators300, max_depth15, min_samples_leaf3, random_state42, n_jobs-1 ) rf_reg.fit(X_train, y_train) y_pred_reg rf_reg.predict(X_test) print(RMSE:, mean_squared_error(y_test, y_pred_reg, squaredFalse)) print(R2:, r2_score(y_test, y_pred_reg))注意回歸任務(wù)里我習(xí)慣把max_depth限制在15以內(nèi)而不是設(shè)為None因?yàn)榛貧w任務(wù)對(duì)連續(xù)值的預(yù)測(cè)更容易過(guò)擬合出極端值限制深度能防止模型一味追著訓(xùn)練集里的異常點(diǎn)跑。min_samples_leaf也通常調(diào)大到3或5讓每個(gè)葉子節(jié)點(diǎn)的預(yù)測(cè)值不是由一兩個(gè)極端樣本決定而是基于足夠數(shù)量的樣本求平均。3.3 特征重要性評(píng)估這棵樹最容易被忽視的價(jià)值隨機(jī)森林自帶feature_importances_屬性輸出每個(gè)特征對(duì)模型預(yù)測(cè)的貢獻(xiàn)度。這個(gè)屬性在業(yè)務(wù)解釋里價(jià)值極高甚至我覺得比預(yù)測(cè)精度本身更有用。import numpy as np import pandas as pd importance rf_clf.feature_importances_ feature_names data.feature_names df_importance pd.DataFrame({ feature: feature_names, importance: importance }).sort_values(importance, ascendingFalse) print(df_importance.head(10))它的原理是基于“基尼重要性”在每一棵樹的每個(gè)分裂節(jié)點(diǎn)用某個(gè)特征進(jìn)行分裂會(huì)讓節(jié)點(diǎn)不純度下降把所有樹中該特征帶來(lái)的不純度下降量累計(jì)起來(lái)就是該特征的重要性。直觀理解就是某個(gè)特征被選中做分裂的次數(shù)越多、帶來(lái)的純度提升越大它對(duì)模型越重要。但有兩點(diǎn)必須提醒你。第一feature_importances_存在偏好。對(duì)于取值種類特別多的連續(xù)特征模型更容易在它上面找到合適的分裂點(diǎn)所以它的重要性可能被高估。要更可靠地評(píng)估建議用permutation_importance它通過(guò)隨機(jī)打亂某個(gè)特征的值、觀察精度下降多少來(lái)判斷重要性評(píng)估方式更穩(wěn)健。from sklearn.inspection import permutation_importance result permutation_importance( rf_clf, X_test, y_test, n_repeats10, random_state42 )第二特征重要性和業(yè)務(wù)含義不是一回事。某個(gè)特征重要性最高只說(shuō)明它和數(shù)據(jù)標(biāo)簽在統(tǒng)計(jì)上有強(qiáng)關(guān)聯(lián)不代表它有因果關(guān)系。給業(yè)務(wù)方報(bào)告時(shí)我會(huì)說(shuō)“該特征對(duì)模型區(qū)分結(jié)果貢獻(xiàn)最大”而不會(huì)說(shuō)“該特征是決定性原因”。4. 實(shí)測(cè)隨機(jī)森林在三個(gè)典型數(shù)據(jù)集上的表現(xiàn)光講原理和代碼不夠我用三份公開數(shù)據(jù)做了幾組實(shí)測(cè)看看隨機(jī)森林在不同場(chǎng)景下的真實(shí)表現(xiàn)也順便把參數(shù)敏感度和過(guò)擬合問題一起驗(yàn)證一遍。4.1 基準(zhǔn)對(duì)比與決策樹、邏輯回歸的橫評(píng)用同一份乳腺癌數(shù)據(jù)集在相同訓(xùn)練測(cè)試集劃分下對(duì)比三組模型模型AccuracyAUC訓(xùn)練耗時(shí)單棵決策樹(不限深度)0.9120.9240.01s邏輯回歸0.9650.9920.02s隨機(jī)森林(深樹)0.9820.9970.25s隨機(jī)森林(限深度)0.9740.9950.20s可以看到隨機(jī)森林在這份數(shù)據(jù)上以微弱優(yōu)勢(shì)領(lǐng)先邏輯回歸但邏輯回歸的表現(xiàn)也沒有很差。這里我想說(shuō)一個(gè)容易被忽略的事實(shí)隨機(jī)森林不是在所有數(shù)據(jù)上都碾壓其他算法的萬(wàn)能器。當(dāng)特征和標(biāo)簽的關(guān)系接近線性、樣本量又不大的時(shí)候邏輯回歸表現(xiàn)可能并不差甚至還更快。隨機(jī)森林真正的優(yōu)勢(shì)在于特征關(guān)系復(fù)雜、存在大量非線性交互、數(shù)據(jù)維度高、有噪聲時(shí)它會(huì)穩(wěn)定地保持一個(gè)高水準(zhǔn)。真正的差距在另一份數(shù)據(jù)上體現(xiàn)得非常明顯。我用make_classification生成了一份包含大量噪聲特征的數(shù)據(jù)集把有用特征控制在4個(gè)其余20個(gè)特征全是隨機(jī)噪聲。邏輯回歸精度直接降到0.68隨機(jī)森林還能維持在0.86左右。4.2 參數(shù)敏感度分析哪些參數(shù)真的值得調(diào)我也做了參數(shù)敏感度測(cè)試直接用網(wǎng)格搜索跑一遍看哪些參數(shù)對(duì)結(jié)果影響最大from sklearn.model_selection import GridSearchCV param_grid { n_estimators: [50, 100, 200, 400], max_depth: [None, 10, 20], min_samples_leaf: [1, 2, 5] } grid GridSearchCV( RandomForestClassifier(random_state42), param_gridparam_grid, cv5, scoringroc_auc, n_jobs-1 ) grid.fit(X_train, y_train) print(Best params:, grid.best_params_) print(Best score:, grid.best_score_)實(shí)測(cè)結(jié)果印證了我之前的說(shuō)法n_estimators從50加到400AUC只提升了不到0.005樹的數(shù)量增長(zhǎng)對(duì)精度的幫助很快就飽和了。max_depth從None變成10AUC下降大約0.008但訓(xùn)練時(shí)間和過(guò)擬合風(fēng)險(xiǎn)大幅下降。如果樣本量大限制深度幾乎不損失精度卻讓模型穩(wěn)健很多。min_samples_leaf從1調(diào)到5AUC下降可忽略但模型對(duì)噪聲數(shù)據(jù)的魯棒性明顯提升。所以關(guān)于參數(shù)調(diào)優(yōu)我最終的建議是先固定random_state然后用默認(rèn)參數(shù)跑一遍作為基準(zhǔn)再調(diào)max_depth和min_samples_leaf控制過(guò)擬合最后如果時(shí)間充裕再對(duì)n_estimators做一次網(wǎng)格搜索。不要一開始就追求調(diào)滿所有參數(shù)。4.3 過(guò)擬合檢測(cè)訓(xùn)練集100分、測(cè)試集60分的常見問題這里要專門講一種非常常見的情況訓(xùn)練集精度接近99%測(cè)試集精度只有60%出頭。我第一次帶新人做項(xiàng)目時(shí)他就遇到過(guò)這種情況當(dāng)時(shí)第一反應(yīng)是“我是不是代碼寫錯(cuò)了”。排查步驟基本是固定的先看訓(xùn)練集和測(cè)試集的數(shù)據(jù)分布。如果兩者差異太大比如訓(xùn)練集來(lái)自某個(gè)時(shí)段、測(cè)試集來(lái)自另一個(gè)時(shí)段那不是模型過(guò)擬合的問題而是數(shù)據(jù)本身就不一致。如果數(shù)據(jù)分布沒問題再看模型參數(shù)。max_depthNone、min_samples_leaf1時(shí)隨機(jī)森林對(duì)訓(xùn)練集的記憶能力非常強(qiáng)幾乎必然過(guò)擬合。用交叉驗(yàn)證結(jié)果和oob_score對(duì)比單次劃分的結(jié)果。如果交叉驗(yàn)證分?jǐn)?shù)明顯低于單次劃分分?jǐn)?shù)說(shuō)明單次劃分可能有運(yùn)氣成分在里面。緩解方案也按優(yōu)先級(jí)排列限制max_depth設(shè)為None的一棵深樹會(huì)無(wú)限細(xì)分限制到10到20能有效打斷這種“死記硬背”。調(diào)大min_samples_leaf讓葉子節(jié)點(diǎn)不會(huì)為了個(gè)別樣本單獨(dú)開辟分支。增加訓(xùn)練數(shù)據(jù)這是最樸素也最有效的辦法隨機(jī)森林的容量很大數(shù)據(jù)越多越不容易過(guò)擬合。如果數(shù)據(jù)量實(shí)在小換一個(gè)更簡(jiǎn)單的模型可能比硬用隨機(jī)森林更靠譜。5. 遙感隨機(jī)森林與生產(chǎn)環(huán)境中的避坑經(jīng)驗(yàn)最后這部分聊遙感隨機(jī)森林和生產(chǎn)環(huán)境落地。這兩個(gè)方向是熱搜里最常被問到的也是我平時(shí)被咨詢最多的場(chǎng)景。5.1 遙感影像分類隨機(jī)森林為什么是遙感主力算法遙感影像分類的目標(biāo)是把影像里的每一個(gè)像元識(shí)別為地物類別比如林地、耕地、建筑區(qū)、水體等。傳統(tǒng)做法里最大似然法、支持向量機(jī)都曾被廣泛使用但現(xiàn)在隨機(jī)森林逐漸成了主力。原因主要有三個(gè)。第一個(gè)優(yōu)勢(shì)是特征維度高也不怕。遙感影像的每個(gè)像元往往包含多個(gè)波段再加NDVI、NDBI等指數(shù)特征以及紋理特征、地形特征動(dòng)輒幾十維甚至上百維。支持向量機(jī)在高維小樣本下需要精細(xì)調(diào)節(jié)核函數(shù)參數(shù)隨機(jī)森林對(duì)高維特征的適應(yīng)性則好得多不太需要做特征篩選直接丟進(jìn)去就能跑出像樣的結(jié)果。第二個(gè)優(yōu)勢(shì)是抗噪聲能力強(qiáng)。遙感數(shù)據(jù)里云遮擋、傳感器噪聲、輻射差異都會(huì)產(chǎn)生異常像元。隨機(jī)森林的樹結(jié)構(gòu)天然對(duì)局部異常值不敏感它對(duì)這類臟數(shù)據(jù)的容忍度比很多模型都高。第三個(gè)優(yōu)勢(shì)是訓(xùn)練速度快且并行友好。遙感影像動(dòng)輒幾千萬(wàn)像元數(shù)據(jù)量巨大隨機(jī)森林的每棵樹獨(dú)立訓(xùn)練n_jobs-1并行跑效率遠(yuǎn)高于需要反復(fù)迭代的神經(jīng)網(wǎng)絡(luò)。一個(gè)典型的遙感分類流程長(zhǎng)這樣先基于影像分割或滑動(dòng)窗口生成樣本塊然后提取每個(gè)樣本塊的光譜均值、紋理特征、植被指數(shù)等組成特征表再訓(xùn)練隨機(jī)森林分類器最后將分類結(jié)果映射回整幅影像得到分類圖。我強(qiáng)調(diào)一點(diǎn)遙感數(shù)據(jù)里的特征往往是強(qiáng)相關(guān)的比如紅光波段和近紅外波段之間、NDVI和某些波段比值之間。隨機(jī)森林對(duì)這種冗余特征的容忍度很高但如果你用邏輯回歸多重共線性會(huì)讓你頭疼很久。5.2 訓(xùn)練樣本與驗(yàn)證遙感項(xiàng)目里最容易翻車的節(jié)點(diǎn)遙感項(xiàng)目里翻車最多的從來(lái)不是算法本身而是訓(xùn)練樣本那一步。有兩個(gè)坑特別典型。第一個(gè)坑是空間自相關(guān)導(dǎo)致的樣本作弊。遙感影像中相鄰像元的類別高度相似如果你在很小的區(qū)域內(nèi)密集采樣訓(xùn)練集和測(cè)試集里就會(huì)包含大量空間位置相鄰的樣本模型相當(dāng)于拿著“鄰居答案”在考試精度虛高得離譜。我見過(guò)有人報(bào)告分類精度98%結(jié)果換到另一塊區(qū)域采樣后掉到65%。解決方法是按區(qū)域劃分訓(xùn)練集和測(cè)試集比如用兩個(gè)不同區(qū)域的數(shù)據(jù)分別做訓(xùn)練和驗(yàn)證而不是把像元隨機(jī)打散后劃分。第二個(gè)坑是樣本不均衡。水域、建筑這類地物可能只占影像的5%農(nóng)田林地占90%。隨機(jī)森林在這種不均衡數(shù)據(jù)上會(huì)傾向于把少數(shù)類全分錯(cuò)。緩解辦法包括采集時(shí)盡量平衡各類樣本數(shù)量或者使用class_weightbalanced讓模型在計(jì)算分裂指標(biāo)時(shí)給少數(shù)類更高的權(quán)重。rf_remote RandomForestClassifier( n_estimators300, max_depth20, min_samples_leaf3, class_weightbalanced, n_jobs-1, random_state42 )5.3 代碼調(diào)優(yōu)、保存與部署的建議訓(xùn)練好的模型不要每次跑都重新訓(xùn)練一遍在實(shí)際項(xiàng)目中要用joblib或pickle把模型存下來(lái)預(yù)測(cè)時(shí)直接加載。import joblib # 保存模型和特征名稱 joblib.dump(rf_clf, random_forest_model.pkl) # 使用時(shí)報(bào)錯(cuò)就再存一份特征列表 feature_list list(X_train.columns) joblib.dump(feature_list, feature_list.pkl) # 加載模型做預(yù)測(cè) loaded_rf joblib.load(random_forest_model.pkl) y_pred_loaded loaded_rf.predict(X_test)在遙感應(yīng)用里模型訓(xùn)練完只是第一步后面還有整幅影像的預(yù)測(cè)、結(jié)果可視化、精度驗(yàn)證。整幅影像預(yù)測(cè)時(shí)要注意分塊處理否則一幅大影像可能直接把內(nèi)存撐爆。我的做法是把影像切塊逐塊輸入模型預(yù)測(cè)再拼接分類結(jié)果。精度驗(yàn)證不要只看整體準(zhǔn)確率。遙感分類中比較標(biāo)準(zhǔn)的做法是生成混淆矩陣逐類別看生產(chǎn)者精度和用戶精度。這才能暴露“某個(gè)地物類別完全被漏分”的問題。另外保存模型時(shí)務(wù)必連特征名稱一起保存。我在和同事協(xié)作時(shí)遇到過(guò)這種問題保存了模型文件結(jié)果換了一個(gè)環(huán)境predict時(shí)報(bào)“特征數(shù)量不匹配”。原因就是訓(xùn)練時(shí)的特征順序和預(yù)測(cè)時(shí)的特征順序不一樣。隨機(jī)森林雖然是樹模型不敏感于量綱但對(duì)特征順序是敏感的因?yàn)槊總€(gè)預(yù)測(cè)樣本需要按訓(xùn)練時(shí)的順序排好這點(diǎn)必須記住。最后再分享一點(diǎn)個(gè)人體會(huì)做了這么多次隨機(jī)森林的實(shí)戰(zhàn)項(xiàng)目我的一個(gè)明顯感受是隨機(jī)森林好上手但想用好需要對(duì)“為什么隨機(jī)”有真正的理解。很多人在調(diào)參階段亂試一通繞了很多彎路才明白真正影響模型泛化能力的通常不是樹的數(shù)量而是樹的深度的葉子節(jié)點(diǎn)的約束條件。如果你正在猶豫選什么算法作為第一個(gè)認(rèn)真學(xué)的機(jī)器學(xué)習(xí)模型我會(huì)推薦隨機(jī)森林。它能幫你建立對(duì)“模型要的不是記住訓(xùn)練數(shù)據(jù)而是從數(shù)據(jù)中找規(guī)律”這件事的體感又把決策樹、集成學(xué)習(xí)、特征工程這些基本功全部串起來(lái)。把這篇文章里的代碼一條條跑通再換一份自己的數(shù)據(jù)試一遍你對(duì)隨機(jī)森林的理解一定會(huì)明顯不一樣。