一、實驗目的
深入理解決策樹、預剪枝和後剪枝的演算法原理,能夠使用 Python 語言實現帶有預剪枝 和後剪枝的決策樹演算法 C4.5 演算法的訓練與測試,並且使用五折交叉驗證演算法進行模型訓練 與評估。
二、實驗內容
(1)從 scikit-learn 庫中載入 iris 資料集,使用留出法留出 1/3 的樣本作為測試集(注意同分布取樣);
(2)使用訓練集訓練分類帶有預剪枝和後剪枝的 C4.5 演算法;
(3)使用五折交叉驗證對模型效能(準確度、精度、召回率和 F1 值)進行評估和選 擇;
(4)使用測試集,測試模型的效能,對測試結果進行分析,完成實驗報告中實驗三的部分。
三、演算法步驟、程式碼、及結果
1. 演算法虛擬碼
輸入: 資料集 D, 屬性集 A, 剪枝策略 P(預剪枝或後剪枝)
輸出: 決策樹 T
1. 若 D 中所有樣本同屬一個類別,返回葉節點;
2. 若 A 為空集,返回葉節點,其類別為 D 中樣本最多的類別;
3. 對 A 中每個屬性,計算其資訊增益率,選擇增益率最高的屬性作為分裂節點;
4. 根據分裂屬性將 D 分成子集,遞迴呼叫構建子樹;
5. 預剪枝策略:若分裂導致驗證集效能下降,則停止分裂;
6. 後剪枝策略:
a. 對生成的樹進行修剪;
b. 比較剪枝前後的效能,若剪枝後效能無顯著下降,則保留剪枝;
7. 返回構建的決策樹 T。
2. 演算法主要程式碼
完整原始碼\呼叫庫方法(函式引數說明)
import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import classification_report, make_scorer, precision_score, recall_score, f1_score
# 資料載入與分割
def load_and_split_data():
iris = load_iris()
X, y = iris.data, iris.target
# 留出法:1/3 的資料作為測試集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=1/3, random_state=42, stratify=y
)
return X_train, X_test, y_train, y_test
# 帶預剪枝的決策樹
def train_pre_pruned_tree(X_train, y_train, max_depth=3, min_samples_split=5):
model = DecisionTreeClassifier(
criterion='entropy', # 基於資訊增益率
max_depth=max_depth, # 最大深度限制
min_samples_split=min_samples_split, # 節點最小樣本數
random_state=42 # 隨機種子
)
model.fit(X_train, y_train)
return model
# 後剪枝實現
def post_prune_tree(model, X_val, y_val):
tree = model.tree_
# 遞迴修剪函式
def prune(node_id):
# 如果是葉子節點,直接返回
if tree.children_left[node_id] == -1 and tree.children_right[node_id] == -1:
return
# 遞迴修剪子節點
if tree.children_left[node_id] != -1:
prune(tree.children_left[node_id])
if tree.children_right[node_id] != -1:
prune(tree.children_right[node_id])
# 檢查是否可以合併子節點
if (
tree.children_left[node_id] != -1
and tree.children_right[node_id] != -1
):
# 模擬剪枝:移除子節點
left_child = tree.children_left[node_id]
right_child = tree.children_right[node_id]
# 儲存當前節點的類別和樣本權重
original_class = tree.value[node_id]
tree.children_left[node_id] = -1
tree.children_right[node_id] = -1
# 重新評估效能
y_pred = model.predict(X_val)
acc_after_prune = np.mean(y_pred == y_val)
# 如果剪枝後效能下降,恢復子節點
if acc_after_prune < np.mean(model.predict(X_val) == y_val):
tree.children_left[node_id] = left_child
tree.children_right[node_id] = right_child
# 從根節點開始修剪
prune(0)
return model
# 五折交叉驗證
def cross_validate_model(model, X_train, y_train):
# 定義評分指標
scorers = {
'accuracy': 'accuracy',
'precision': make_scorer(precision_score, average='macro'),
'recall': make_scorer(recall_score, average='macro'),
'f1': make_scorer(f1_score, average='macro')
}
# 交叉驗證
results = {}
for metric, scorer in scorers.items():
scores = cross_val_score(model, X_train, y_train, cv=5, scoring=scorer)
results[metric] = scores.mean()
print(f"{metric.capitalize()} 平均值: {scores.mean():.4f}")
return results
# 測試模型效能
def evaluate_model(model, X_test, y_test):
y_pred = model.predict(X_test)
report = classification_report(y_test, y_pred, output_dict=True, zero_division=0)
print("測試集效能:")
print(classification_report(y_test, y_pred, zero_division=0))
return report
# 主函式
if __name__ == "__main__":
# 資料載入與分割
X_train, X_test, y_train, y_test = load_and_split_data()
# 帶預剪枝的決策樹訓練
pre_pruned_model = train_pre_pruned_tree(X_train, y_train)
# 五折交叉驗證評估
print("\n五折交叉驗證評估:")
cross_validate_model(pre_pruned_model, X_train, y_train)
# 後剪枝
print("\n開始後剪枝:")
pruned_model = post_prune_tree(pre_pruned_model, X_test, y_test)
# 測試集評估
print("\n測試集評估:")
evaluate_model(pruned_model, X_test, y_test)
1.train_test_split
來源: sklearn.model_selection.train_test_split
功能: 將資料集劃分為訓練集和測試集。
引數說明:
X: 特徵資料集。
y: 目標標籤資料集。
test_size: 測試集所佔比例(0-1 之間的浮點數)。
train_size: 訓練集所佔比例(可選,與 test_size 互斥)。
random_state: 隨機種子,用於結果復現。
stratify: 如果為 y,則按目標變數比例分層抽樣。
2. DecisionTreeClassifier
來源: sklearn.tree.DecisionTreeClassifier
功能: 使用決策樹對資料進行分類。
引數說明:
criterion: 劃分標準,預設值為 "gini";本程式碼中使用 "entropy"(基於資訊增益率)。
max_depth: 決策樹的最大深度,用於防止過擬合。
min_samples_split: 每個節點劃分所需的最小樣本數。
class_weight: 類別權重,可以設定為 "balanced",根據資料集自動調整類別權重。
random_state: 隨機種子,用於結果復現。
3. cross_val_score
來源: sklearn.model_selection.cross_val_score
功能: 使用交叉驗證評估模型效能。
引數說明:
estimator: 需要評估的模型。
X: 特徵資料集。
y: 目標標籤資料集。
cv: 交叉驗證的折數,預設為 5。
scoring: 評分方法,可選 "accuracy", "precision", "recall", "f1" 等。
4. classification_report
來源: sklearn.metrics.classification_report
功能: 生成分類任務的詳細評估報告,包括準確率、精度、召回率和 F1 值。
引數說明:
y_true: 真實標籤。
y_pred: 模型預測結果。
output_dict: 如果為 True,返回字典格式的報告。
zero_division: 預設值為 "warn"。當分母為零時,控制返回的值(0 或 1)。
5. make_scorer
來源: sklearn.metrics.make_scorer
功能: 將自定義的評分方法轉換為可用於 cross_val_score 的格式。
引數說明:
score_func: 自定義評分函式,如 precision_score, recall_score, f1_score 等。
greater_is_better: 是否更高的分數更好,預設 True。
average: 當用於多分類問題時,控制指標計算方式,例如 "macro" 或 "weighted"。
6. np.unique
來源: numpy.unique
功能: 查詢陣列中的唯一值,並返回每個值的計數。
引數說明:
return_counts: 如果為 True,同時返回每個唯一值的計數。
7. model.tree_ 屬性
來源: sklearn.tree.DecisionTreeClassifier.tree_
功能: 獲取決策樹的底層資料結構。
主要屬性:
children_left: 每個節點的左子節點索引,葉節點為 -1。
children_right: 每個節點的右子節點索引,葉節點為 -1。
feature: 每個節點分裂時使用的特徵索引。
threshold: 每個節點分裂時使用的閾值。
value: 每個節點的類別分佈(數量)。