自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

機器學習隨機森林算法實戰(zhàn)解析

譯文 精選
人工智能 機器學習
本文將通過具體的Python編程代碼試圖透徹解析機器學習中隨機森林這個美麗的算法的內在實現原理。

譯者 | 朱先忠

審校 | 孫淑娟

在經典的機器學習中,隨機森林(Random Forests)算法可謂是一種“銀彈”類型的算法模型。

該模型之所以很棒,有如下幾個原因:

  • 與其他許多算法相比,需要更少的數據預處理,這使得該算法設置起來更容易
  • 可以充當分類或回歸模型
  • 不易過度擬合
  • 能夠輕松計算出特征的重要程度

在這篇文章中,我想更好地分析一下組成隨機森林算法的各個組成部分。為了實現這一點,我將把隨機森林算法分解為最基本的組成部分,并解釋每一組成部分計算任務的情況。到文章最后,我們就能夠對隨機森林算法如何工作以及如何以更直觀的方式使用它們有更深入的理解。需要說明的是,本文中我們將使用的示例將側重于分類功能,但其中的許多原則也同樣適用于回歸場景的應用。

運行隨機森林算法

讓我們首先從調用一個經典的隨機森林模式開始。這是最高級別的,也是許多人在使用Python語言訓練隨機森林時所采用的方案。

模擬的數據

如果我想運行一個隨機森林算法來預測我的目標列,那么我只需要執(zhí)行以下操作:

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(df.drop('target', axis=1), df['target'], test_size=0.2, random_state=0)

# 訓練隨機森林算法并計算得分
simple_rf_model = RandomForestClassifier(n_estimators=100, random_state=0)
simple_rf_model.fit(X_train, y_train)
print(f"accuracy: {simple_rf_model.score(X_test, y_test)}")

# accuracy: 0.93

運行隨機森林分類器非常簡單。如上面代碼所示,我只是定義了n_estimators參數,并將參數random_state設置為0。我可以從個人經驗告訴你,很多人都會盯著0.93這個精確度不放松。他們似乎感覺非常滿意,于是輕易地開始了瘋狂的部署工作。但是,我們今天不會這么做。

首先,讓我們重新審視下面這一行“無傷大雅”的代碼:

simple_rf_model = RandomForestClassifier(n_estimators=100, random_state=0)

隨機狀態(tài)是大多數數據科學模型的一個特征,它可以確保其他人可以復制您的工作。因此,我們不會太擔心random_state這個參數。

但是,讓我們深入研究一下n_estimators這個參數。如果我們查看一下scikit-learn中的有關文檔,會找到其如下的簡潔定義:

“森林中的樹木數量。”

樹木數量研究

現在,讓我們更具體地定義一個隨機森林。隨機森林是一個集成模型,是許多決策樹的共識內容。這個定義可能是不完整的,但我們稍后還會回來討論它的。

許多樹木間相互通信并達成共識

這可能會讓你認為,如果你把它分解成以下內容,你可能會得到一個隨機森林:

#創(chuàng)建決策樹
tree1 = DecisionTreeClassifier().fit(X_train, y_train)
tree2 = DecisionTreeClassifier().fit(X_train, y_train)
tree3 = DecisionTreeClassifier().fit(X_train, y_train)

# 預測X_test上的每一棵決策樹
predictions_1 = tree1.predict(X_test)
predictions_2 = tree2.predict(X_test)
predictions_3 = tree3.predict(X_test)
print(predictions_1, predictions_2, predictions_3)

# 采取優(yōu)先級策略
final_prediction = np.array([np.round((predictions_1[i] + predictions_2[i] + predictions_3[i])/3) for i in range(len(predictions_1))])
print(final_prediction)

在上面的例子中,我們在X_train上訓練了3棵決策樹,這意味著n_estimators=3。在訓練了3棵樹之后,我們在同一測試集上預測了每棵樹,然后最終得到3棵樹中有2棵選擇的預測。

這是有道理的,但這看起來并不完全正確。如果所有的決策樹都是在相同的數據上訓練的,那么它們不會得出相同的結論,從而導致否定整體的優(yōu)勢嗎?

放回抽樣詳解

讓我們在前面的定義的基礎上再補充上這樣一句:“隨機森林是一個集成模型,它是許多不相關決策樹的共識。”

決策樹可以通過兩種方式變得不相關:

1. 您有足夠大的數據集大小,可以將數據的獨特部分采樣到每個決策樹中。這種方式并不流行,因為通常需要大量數據。

2. 您可以利用一種叫做放回抽樣(sampling with replacement)的技術。放回抽樣是指從總體中抽取的樣本在抽取下一個樣本之前返回到樣本總體中。

為了解釋放回抽樣,假設我有5個3種顏色的彈珠,所以總體看起來是這樣的:

blue, blue, red, green, red

如果我想取樣一些彈珠,我通常會從中抽出幾顆,也許最后會得到:

blue, red

這是因為一旦我拿起紅色,我就沒有把它放回原來的彈珠堆中。

然而,如果我用放回方式進行采樣,我實際上可以兩次拾取任何彈珠。因為紅色回到了我的堆中,所以我仍然有機會再次撿起它。

red, red

在隨機森林算法中,默認值是構建約為原始樣本總體大小2/3的樣本。如果我的原始訓練數據是1000行,那么我輸入到樹中的訓練數據樣本可能大約是670行。也就是說,在構建隨機森林時,嘗試不同的采樣率將是一個很好的參數。

與上一段代碼不同,下面的代碼更接近隨機森林,其中參數n_estimators=3。

import numpy as np
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split

# 對于每一棵樹從X_train中采用3次放回抽樣
df_sample1 = df.sample(frac=.67, replace=True)
df_sample2 = df.sample(frac=.67, replace=True)
df_sample3 = df.sample(frac=.67, replace=True)

X_train_sample1, X_test_sample1, y_train_sample1, y_test_sample1 = train_test_split(df_sample1.drop('target', axis=1), df_sample1['target'], test_size=0.2)
X_train_sample2, X_test_sample2, y_train_sample2, y_test_sample2 = train_test_split(df_sample2.drop('target', axis=1), df_sample2['target'], test_size=0.2)
X_train_sample3, X_test_sample3, y_train_sample3, y_test_sample3 = train_test_split(df_sample3.drop('target', axis=1), df_sample3['target'], test_size=0.2)

#生成決策樹
tree1 = DecisionTreeClassifier().fit(X_train_sample1, y_train_sample1)
tree2 = DecisionTreeClassifier().fit(X_train_sample2, y_train_sample2)
tree3 = DecisionTreeClassifier().fit(X_train_sample3, y_train_sample3)

# 在X_test上預測每一棵決策樹
predictions_1 = tree1.predict(X_test)
predictions_2 = tree2.predict(X_test)
predictions_3 = tree3.predict(X_test)
df = pd.DataFrame([predictions_1, predictions_2, predictions_3]).T
df.columns = ["tree1", "tree2", "tree3"]

# 采取優(yōu)先級策略
final_prediction = np.array([np.round((predictions_1[i] + predictions_2[i] + predictions_3[i])/3) for i in range(len(predictions_1))])
preds = pd.DataFrame([predictions_1, predictions_2, predictions_3, final_prediction, y_test]).T.head(20)
preds.columns = ["tree1", "tree2", "tree3", "final", "label"]
preds

我們用放回抽樣,把這些樣本輸送給樹,產生結果,并達成共識。

袋裝分類器(Bagging Classifier)

早期的架構實際上就是一個裝袋分類器

我們現在將引入一種新的算法,一種稱為自助聚集(Bootstrap Aggregation,也稱為“Bagging”)的有監(jiān)督的學習算法。但請放心,這又會與隨機森林算法聯系起來。我們引入這個新概念的原因是,正如我們將要在文章后面的圖中看到的,我們到目前為止所做的一切實際上都是裝袋分類器所做的!

在下面的代碼中,裝袋分類器使用了一個名為bootstrap的參數,它實際上執(zhí)行了我們剛才手動執(zhí)行的放回抽樣步驟。其實,sklearn庫的隨機森林算法實現也存在相同的參數。如果bootstrap參數的值是false,那么我們將為每個分類器使用整個總體。

import numpy as np
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import BaggingClassifier

# 集合中所使用的樹的數量
n_estimators = 3

# 初始化裝袋分類器
bag_clf = BaggingClassifier(
DecisionTreeClassifier(), n_estimators=n_estimators, bootstrap=True)

# 根據訓練數據擬合裝袋分類器
bag_clf.fit(X_train, y_train)

# 對測試數據進行預測
y_pred = bag_clf.predict(X_test)
pd.DataFrame([y_pred, y_test]).T

裝袋分類器BaggingClassifier非常棒,因為您可以將它們與未命名為決策樹的評估器一起使用!您可以插入許多算法,Bagging算法會將其轉化為集成解決方案。隨機森林算法實際上擴展了裝袋算法(如果bootstrapping = true),因為它部分地利用Bagging算法來形成不相關的決策樹。

然而,即使bootstrapping=false,隨機森林算法也需要額外一步來確保樹之間的不相關性——特征采樣。

特征采樣詳解

特征采樣(Feature sampling)意味著不僅對行進行采樣,還對列進行采樣。與行不同,隨機森林的列在沒有放回的情況下被采樣,這意味著我們不會有重復的列來訓練1棵樹。

有許多方法可以對特征進行采樣。您可以指定要采樣的固定最大特征數量,獲取特征總數的平方根,或者嘗試使用日志數據。這些方法中的每一種都有各自的利弊,并將取決于您的數據和具體使用場景。

通過特征采樣擴展了Bagging算法

下面的代碼片段使用sqrt技術對列進行采樣,對行進行采樣,訓練3個決策樹,并使用優(yōu)先級規(guī)則進行預測。我們首先使用放回進行采樣,他們對列進行采樣,訓練我們的單個樹,讓我們的樹根據測試數據進行預測,然后采用優(yōu)先級規(guī)則實現共識。

import numpy as np
import pandas as pd
import math
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
#對于每一棵樹從X_train中取3個樣本
df_sample1 = df.sample(frac=.67, replace=True)
df_sample2 = df.sample(frac=.67, replace=True)
df_sample3 = df.sample(frac=.67, replace=True)

# 分割訓練集
X_train_sample1, y_train_sample1 = df_sample1.drop('target', axis=1), df_sample1['target']
X_train_sample2, y_train_sample2 = df_sample2.drop('target', axis=1), df_sample2['target']
X_train_sample3, y_train_sample3 = df_sample3.drop('target', axis=1), df_sample3['target']

# 使用sqrt獲取訓練和測試的采樣特征,現在注意replace如何等于False的
num_features = len(X_train.columns)
X_train_sample1 = X_train_sample1.sample(n=int(math.sqrt(num_features)), replace=False, axis = 1)
X_train_sample2 = X_train_sample2.sample(n=int(math.sqrt(num_features)), replace=False, axis = 1)
X_train_sample3 = X_train_sample3.sample(n=int(math.sqrt(num_features)), replace=False, axis = 1)

# 創(chuàng)建決策樹,這次我們對列進行采樣
tree1 = DecisionTreeClassifier().fit(X_train_sample1, y_train_sample1)
tree2 = DecisionTreeClassifier().fit(X_train_sample2, y_train_sample2)
tree3 = DecisionTreeClassifier().fit(X_train_sample3, y_train_sample3)

# 預測X_test上的每個決策樹
predictions_1 = tree1.predict(X_test[X_train_sample1.columns])
predictions_2 = tree2.predict(X_test[X_train_sample2.columns])
predictions_3 = tree3.predict(X_test[X_train_sample3.columns])
preds = pd.DataFrame([predictions_1, predictions_2, predictions_3]).T
preds.columns = ["tree1", "tree2", "tree3"]

# 使用優(yōu)先級規(guī)則
final_prediction = np.array([np.round((predictions_1[i] + predictions_2[i] + predictions_3[i])/3) for i in range(len(predictions_1))])
preds = pd.DataFrame([predictions_1, predictions_2, predictions_3, final_prediction, y_test]).T.head(20)
preds.columns = ["tree1", "tree2", "tree3", "final", "label"]

當我運行這段代碼時,我發(fā)現我的決策樹開始預測不同的事情,這表明我們已經刪除了樹之間的許多相關性。

我的測試結果樹之間不再總是彼此保持一致了

決策樹基礎

到目前為止,我們已經剖析了數據是如何被送入大量決策樹的。在前面的代碼示例中,我們使用DecisionTreeClassifier函數來訓練決策樹,但為了完全理解隨機森林,我們需要先來解釋一下什么是決策樹。

一棵名副其實的決策樹看起來像一棵倒掛的樹。從一種高級別角度上看,該算法試圖提出問題,并將數據分割成不同的節(jié)點。下圖顯示了決策樹的形象示意。

決策樹示例

決策樹根據前一個問題的答案提出一系列問題。對于它提出的每一個問題,可能都有多個答案,我們不妨可以將其想象為分割節(jié)點。上一個問題的答案將決定樹將詢問的下一個問題。在詢問了一系列問題之后的某個時刻,你得到了答案。

但是你怎么知道你的答案是準確的,或者你詢問了正確的問題呢?實際上,您可以用幾種不同的方法來評估決策樹,我們當然也會對這些方法加以解釋。

熵與信息增益

介紹到現在,我們需要討論一個叫做熵(entropy)的新術語。從一種高角度來看,熵是衡量節(jié)點中雜質或隨機性水平的一種方法。順便說一句,還有另一種流行的方法來測量節(jié)點的雜質,稱為基尼系數(Gini impurity),但我們不會在本文中解析該方法,因為它與許多關于熵的概念重疊,盡管計算略有不同。一般的想法是,熵或基尼系數越高,節(jié)點中的方差越大,我們的目標是減少這種不確定性。

決策樹試圖通過將所詢問的節(jié)點拆分為更小、更同質的節(jié)點來最小化熵。熵的實際公式是:

為了進一步解釋熵的概念,讓我們回到那個彈珠的例子:

假設我有10個彈珠。其中5個是藍色的,5個是綠色的。我的總體數據集的熵為1.0,那么計算熵的代碼如下:

from collections import Counter
from math import log2

#我的預測分類為:0或者1。其中,0代表藍色彈珠,1代表是綠色彈珠。
data = [0, 0, 0, 1, 1, 1, 1, 0, 1, 0]
# 獲取標簽的長度
len_labels = len(data)
def calculate_entropy(data, len_labels):
# 對每一種分類進行計數
counts = Counter(labels)
# 我們計算分數,這個例子的輸出應該是[.5.5]
probs = [count / num_labels for count in counts.values()]
# 實際熵計算
return - sum(p * log2(p) for p in probs)

calculate_entropy(labels, num_labels)

如果數據完全充滿綠色彈珠,熵將為0,并且熵將隨著我們接近50%的分割而增加。

每次減少熵,我們都會獲得一些關于數據集的信息,因為我們減少了隨機性。信息增益告訴我們哪個特征相對來說最能讓我們最小化熵。計算信息增益的方法是:

entropy(parent)  [weighted_average_of_entropy(children)]

在這種情況下,父節(jié)點是原始節(jié)點,子節(jié)點是拆分節(jié)點的結果。

拆分一個節(jié)點

為了計算信息增益,我們執(zhí)行以下操作:

  • 計算父節(jié)點的熵
  • 將父節(jié)點拆分為子節(jié)點
  • 為每個子節(jié)點創(chuàng)建權重。這是通過number_of_samples_in_child_node/number_of_ssamples_in_parent_node測量的
  • 計算每個子節(jié)點的熵
  • 通過計算weight*entropy_of_child1+weight*entropy_of_child2創(chuàng)建[weighted_average_of_entropy(children)]
  • 從父節(jié)點的熵中減去此加權熵

下面的代碼實現了將父節(jié)點拆分為兩個子節(jié)點的簡單信息增益:

def information_gain(left_labels, right_labels, parent_entropy):
"""計算拆分的信息增益"""
#計算左側節(jié)點的權重
proportion_left_node = float(len(left_labels)) / (len(left_labels) + len(right_labels))
#計算右節(jié)點的權重
proportion_right_node = 1 - proportion_left_node
# 計算子節(jié)點的加權平均值
weighted_average_of_child_nodes = ((proportion_left_node * entropy(left_labels)) + (proportion_right_node * entropy(right_labels)))
#返回父節(jié)點熵——子節(jié)點的加權熵
return parent_entropy - weighted_average_of_child_nodes

決策樹詳解

考慮到上述這些概念,我們現在已經準備好實現一棵小型決策樹了!

在沒有任何指導的情況下,決策樹將繼續(xù)拆分節(jié)點,直到所有最終的葉節(jié)點都是純的??刂茦涞膹碗s性的想法被稱為修剪(pruning),我們可以在樹完全建成后修剪它,也可以在生長階段之前使用特定參數對樹進行預修剪。預修剪樹復雜度的一些方法是控制拆分的數量、限制最大深度(從根節(jié)點到葉節(jié)點的最長距離)或設置信息增益。

以下代碼將所有這些概念聯系在一起:

  • 從一個數據集開始,其中有一個要預測的目標變量
  • 計算原始數據集(根節(jié)點)的熵(或基尼系數)
  • 查看每個特征并計算信息增益
  • 選擇具有最佳信息增益的最佳特征,這與導致熵降低最多的特征相同

保持增長,直到滿足停止條件——在這種情況下,這是我們的最大深度限制,節(jié)點的熵為0。

import pandas as pd
import numpy as np
from math import log2

def entropy(data, target_col):
# calculate the entropy of the entire dataset
values, counts = np.unique(data[target_col], return_counts=True)
entropy = np.sum([-count/len(data) * log2(count/len(data)) for count in counts])
return entropy

def compute_information_gain(data, feature, target_col):
parent_entropy = entropy(data, target_col)
# 計算在給定特征上拆分的信息增益
split_values = np.unique(data[feature])
# initialize at 0
weighted_child_entropy = 0
# 計算加權熵,記住這與新節(jié)點中的點數有關
for value in split_values:
sub_data = data[data[feature] == value]
node_weight = len(sub_data)/len(data)
weighted_child_entropy += node_weight * entropy(sub_data, target_col)
#與之前相同的計算,我們只是從父節(jié)點熵中減去加權熵
return parent_entropy - weighted_child_entropy

def grow_tree(data, features, target_col, depth=0, max_depth=3):
# 我們將最大深度設置為3以“預修剪”或限制樹的復雜性
if depth >= max_depth or len(np.unique(data[target_col])) == 1:
# 如果達到最大深度或所有標簽都相同,則停止生長樹。所有標簽相同意味著熵為0
return np.unique(data[target_col])[0]
# 我們根據信息增益計算最佳特征(或最佳問題)
node = {}
gains = [compute_information_gain(data, feature, target_col) for feature in features]
best_feature = features[np.argmax(gains)]

for value in np.unique(data[best_feature]):
sub_data = data[data[best_feature] == value]
node[value] = grow_tree(sub_data, features, target_col, depth+1, max_depth)

return node


# 模擬一些數據并制作一個數據幀,注意我們是如何建立一個目標的
data = {
'A': [1, 2, 1, 2, 1, 2, 1, 2],
'B': [3, 3, 4, 4, 3, 3, 4, 4],
'C': [5, 5, 5, 5, 6, 6, 6, 6],
'target': [0, 0, 0, 1, 1, 1, 1, 0]
}
df = pd.DataFrame(data)

# 定義我們的特征和標簽
features = ["A", "B", "C"]
target_col = "target"

# 成長樹
tree = grow_tree(df, features, target_col, max_depth=3)
print(tree)

預測這棵樹意味著,用新數據遍歷生長的樹,直到它到達葉節(jié)點。最后一個葉節(jié)點是預測。

關于隨機森林的一些有趣的事情

我們在上一節(jié)中討論的所有內容都是有關單棵決策樹如何做出決策。下圖將這些概念與我們之前討論的隨機森林采樣概念聯系起來。

具有解構決策樹的隨機森林架構

因為決策樹實際上檢查每個特征的信息增益,所以您可以計算隨機森林中的特征重要性。特征重要性的計算通常被視為所有樹中雜質的平均減少。隨機森林不像Logistic回歸模型那樣可解釋,因此特征重要性為我們提供了一點關于樹如何生長的知識。

最后,有幾種方法可以測試你訓練過的隨機森林。您可以始終使用經典的機器學習方法,并使用測試集來衡量模型對未知數據的概括程度。然而,這通常需要第二次計算。隨機森林有一個獨特的屬性,稱為袋外錯誤或OOB錯誤。還記得我們如何僅對數據集的一部分進行采樣以構建每個樹嗎?

實際上,您可以在訓練時使用其余的樣本來進行驗證,這實際上只有在算法存在集成特性的情況下才是可能的。這意味著,在一次試驗中,我們就可以了解我們的模型如何很好地推廣到未知數據。

總結

總結一下,我們在本文中所學到的內容:

  • 隨機森林實際上是一組不相關的決策樹,它們做出預測并達成共識。這種共識是回歸問題的平均分數和分類問題的優(yōu)先級規(guī)則。
  • 隨機森林通過利用裝袋算法和特征采樣減輕相關性。通過利用這兩種技術,單棵決策樹可以查看我們集合的特定維度,并根據不同的因素進行預測。
  • 決策樹是通過在產生最高信息增益的特征上分割數據來生長的。信息增益被測量為雜質的最高減少。雜質通常通過熵或基尼系統來測量。
  • 隨機森林能夠通過特征重要性實現有限程度的可解釋性,這是特征的平均信息增益的度量。
  • 隨機森林也有能力在訓練時進行交叉驗證,這是一種被稱為OOB錯誤的獨特技術。這是可能的,得益于算法對上游數據進行采樣的方式。
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(df.drop('target', axis=1), df['target'], test_size=0.2, random_state=0)

# 訓練和評分隨機森林
simple_rf_model = RandomForestClassifier(n_estimators=100, random_state=0)
simple_rf_model.fit(X_train, y_train)
print(f"accuracy: {simple_rf_model.score(X_test, y_test)}")

# accuracy: 0.93

當查看訓練隨機森林的原始代碼時,這幾行代碼中發(fā)生了多少不同的計算和評估,這讓我感到驚訝。為了防止過度擬合,在樹木和森林層面上進行評估,并實現一些基本的可解釋性,需要考慮很多因素,此外,由于現有的所有框架,很容易進行設置。

我希望下次你訓練隨機森林模型時,你能夠查看隨機森林的??scikit學習文檔頁面??,并更好地了解你的所有選項。雖然有一些直觀的默認設置,但應該清楚您可以進行多少不同的調整,以及這些技術中有多少可以擴展到其他模型。

我在寫這篇文章時很開心,并且親自了解了很多關于這個漂亮算法的工作原理。我希望你也能從中學習到一些東西!

譯者介紹

朱先忠,51CTO社區(qū)編輯,51CTO專家博客、講師,濰坊一所高校計算機教師,自由編程界老兵一枚。

原文標題:??Demystifying the Random Forest??,作者:Siddarth Ramesh

責任編輯:華軒 來源: 51CTO
相關推薦

2023-02-17 08:10:58

2014-07-07 10:05:57

機械學習

2017-10-18 14:11:20

機器學習決策樹隨機森林

2023-03-13 08:00:00

機器學習算法

2023-09-22 10:34:19

學習算法隨機森林Java

2020-02-17 15:05:28

機器學習人工智能計算機

2023-08-18 09:18:47

信貸違約機器學習

2023-10-07 13:13:24

機器學習模型數據

2015-09-14 13:41:47

隨機森林入門攻略

2016-11-15 15:02:00

機器學習算法

2020-12-16 15:56:26

機器學習人工智能Python

2019-03-20 07:50:47

機器學習算法線性回歸

2024-05-27 00:05:00

2020-06-18 16:05:20

機器學習人工智能算法

2014-06-17 09:55:24

機器學習

2022-12-21 14:39:35

機器學習案發(fā)決策樹

2017-08-04 14:28:40

決策樹隨機森林CART模型

2021-05-28 17:18:44

TensorFlow數據機器學習

2023-11-28 12:08:56

機器學習算法人工智能

2017-08-25 14:05:01

機器學習算法模型
點贊
收藏

51CTO技術棧公眾號