1. 项目背景及解决问题的方案
1.1 项目背景
鸢尾花(Iris)数据集是机器学习领域经典的多分类基准数据集,由Fisher于1936年提出,常被用于验证分类算法的有效性。该数据集包含3类鸢尾花(山鸢尾、变色鸢尾、维吉尼亚鸢尾),共150条样本,每条样本包含4个数值型特征:花萼长度、花萼宽度、花瓣长度、花瓣宽度。
本项目的核心背景与目标:
- 算法验证:以鸢尾花分类问题为载体,验证随机森林(Random Forest)——作为Bagging集成学习的改进算法——在多分类任务中的性能;
- 关键特性验证:测试随机森林的“袋外样本(OOB)评分”(无需额外验证集即可评估模型泛化能力);
- 特征价值分析:通过随机森林的特征重要性输出,明确哪些特征对鸢尾花分类最关键;
- 算法对比(可选):对比基础Bagging算法与随机森林的分类效果,体现随机森林“特征随机”的优势。
1.2 解决问题的方案
针对“鸢尾花多分类”问题,本项目采用**“数据预处理→模型构建→模型评估→特征分析”** 四步解决方案:
| 步骤 | 具体操作 |
|---|---|
| 数据预处理 | 1. 加载鸢尾花数据集; 2. 选取特征(先选花萼长宽,后用全部4个特征); 3. 按7:3拆分训练集/测试集(保证随机可复现) |
| 模型构建 | 1. 初始化随机森林分类器,设置树数量、最大叶节点数、开启OOB评分; 2. (可选)初始化基础Bagging分类器作为对比 |
| 模型评估 | 1. 输出OOB评分(袋外样本准确率); 2. 用测试集预测,计算分类准确率; 3. (可选)对比Bagging的测试集准确率 |
| 特征重要性分析 | 用全特征训练随机森林,输出4个特征的重要性得分,明确核心分类特征 |
2. 详细注释版代码(含中文可视化/打印)
# 导入必要的库fromsklearn.ensembleimportRandomForestClassifier# 随机森林分类器fromsklearn.model_selectionimporttrain_test_split# 数据集拆分fromsklearn.ensembleimportBaggingClassifier# 基础Bagging分类器(对比用)fromsklearn.treeimportDecisionTreeClassifier# 决策树基学习器fromsklearn.metricsimportaccuracy_score# 分类准确率评估指标fromsklearn.datasetsimportload_iris# 加载鸢尾花数据集importmatplotlib.pyplotasplt# 可视化库importnumpyasnp# 数值计算库# ========== 解决matplotlib中文显示问题 ==========plt.rcParams['font.sans-serif']=['SimHei']# 用黑体显示中文plt.rcParams['axes.unicode_minus']=False# 正常显示负号# ========== 第一步:数据加载与预处理 ==========# 加载鸢尾花数据集,包含特征、标签、特征名、标签名等信息iris=load_iris()# 选取前2个特征:花萼长度(第0列)、花萼宽度(第1列),用于简化模型验证X=iris.data[:,:2]# 标签:0=山鸢尾,1=变色鸢尾,2=维吉尼亚鸢尾y=iris.target# 拆分训练集(67%)和测试集(33%),random_state=42保证结果可复现# stratify=y:分层拆分,保证训练/测试集的类别分布与原数据一致X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.33,random_state=42,stratify=y)print("="*50)print("数据预处理完成:")print(f"训练集样本数:{len(X_train)},测试集样本数:{len(X_test)}")print(f"选取的特征:{iris.feature_names[0]}、{iris.feature_names[1]}")# ========== 第二步:随机森林模型训练与评估 ==========# 初始化随机森林分类器# n_estimators=15:决策树数量为15# max_leaf_nodes=16:每棵树的最大叶节点数(限制树复杂度,防止过拟合)# n_jobs=1:单线程运行(便于调试)# oob_score=True:开启袋外样本评分(用未被抽样的样本评估模型)rnd_clf=RandomForestClassifier(n_estimators=15,max_leaf_nodes=16,n_jobs=1,oob_score=True,random_state=42# 随机种子,保证结果可复现)# 用训练集训练模型rnd_clf.fit(X_train,y_train)# 输出OOB评分(袋外样本准确率):无需测试集即可评估模型泛化能力print("="*50)print(f"随机森林袋外(OOB)样本准确率:{rnd_clf.oob_score_:.4f}")# 用训练好的模型预测测试集y_pred_rf=rnd_clf.predict(X_test)# 计算测试集分类准确率rf_acc=accuracy_score(y_test,y_pred_rf)print(f"随机森林测试集分类准确率:{rf_acc:.4f}")# ========== 可选:基础Bagging分类器对比(解除注释可运行) ==========# 初始化Bagging分类器(基学习器为随机拆分的决策树)# bag_clf = BaggingClassifier(# DecisionTreeClassifier(splitter="random", max_leaf_nodes=16), # 随机拆分的决策树# n_estimators=15, # 基学习器数量(与随机森林一致)# max_samples=1.0, # 每个基学习器使用100%的抽样样本# bootstrap=True, # 开启有放回抽样(Bootstrap)# n_jobs=1,# random_state=42# )# bag_clf.fit(X_train, y_train) # 训练Bagging模型# y_pred_bag = bag_clf.predict(X_test) # 测试集预测# bag_acc = accuracy_score(y_test, y_pred_bag) # 计算准确率# print(f"基础Bagging分类器测试集准确率:{bag_acc:.4f}")# ========== 第三步:全特征随机森林 + 特征重要性分析 ==========print("="*50)print("全特征随机森林特征重要性分析:")# 重新初始化随机森林(更多树,更高稳定性),用全部4个特征训练rnd_clf_full=RandomForestClassifier(n_estimators=500,# 决策树数量增加到500,提升特征重要性的稳定性n_jobs=-1,# 使用全部CPU核心,加速训练random_state=42)# 用全部特征(4个)训练模型rnd_clf_full.fit(iris["data"],iris['target'])# 遍历特征名和对应的重要性得分,打印输出feature_importance=[]forname,scoreinzip(iris['feature_names'],rnd_clf_full.feature_importances_):print(f"特征:{name},重要性得分:{score:.4f}")feature_importance.append((name,score))# ========== 第四步:特征重要性可视化(柱状图) ==========# 提取特征名和得分features=[x[0]forxinfeature_importance]scores=[x[1]forxinfeature_importance]# 创建画布,设置大小plt.figure(figsize=(10,6))# 绘制柱状图bars=plt.bar(features,scores,color=['#1f77b4','#ff7f0e','#2ca02c','#d62728'])# 添加数值标签(在柱子上方显示得分)forbarinbars:height=bar.get_height()plt.text(bar.get_x()+bar.get_width()/2.,height+0.01,f'{height:.4f}',ha='center',va='bottom',fontsize=10)# 设置标题和坐标轴标签plt.title('鸢尾花分类 - 随机森林特征重要性',fontsize=14)plt.xlabel('特征名称',fontsize=12)plt.ylabel('重要性得分',fontsize=12)# 旋转x轴标签,避免重叠plt.xticks(rotation=15)# 显示网格(仅y轴)plt.grid(axis='y',linestyle='--',alpha=0.7)# 保存图片(可选)plt.savefig('特征重要性.png',dpi=300,bbox_inches='tight')# 显示图表plt.show()# ========== 第五步:二维特征分类结果可视化(花萼长宽) ==========print("="*50)print("绘制二维特征(花萼长宽)的分类决策边界...")# 生成网格点,用于绘制决策边界x_min,x_max=X[:,0].min()-0.5,X[:,0].max()+0.5y_min,y_max=X[:,1].min()-0.5,X[:,1].max()+0.5xx,yy=np.meshgrid(np.arange(x_min,x_max,0.02),np.arange(y_min,y_max,0.02))# 用随机森林模型预测网格点的类别Z=rnd_clf.predict(np.c_[xx.ravel(),yy.ravel()])Z=Z.reshape(xx.shape)# 创建画布plt.figure(figsize=(10,6))# 绘制决策边界(等高线填充)plt.contourf(xx,yy,Z,alpha=0.3,cmap=plt.cm.RdYlBu)# 绘制训练集样本点plt.scatter(X_train[:,0],X_train[:,1],c=y_train,edgecolors='k',cmap=plt.cm.RdYlBu,label='训练集')# 绘制测试集样本点(加空心圈区分)plt.scatter(X_test[:,0],X_test[:,1],c=y_test,edgecolors='k',cmap=plt.cm.RdYlBu,marker='o',facecolors='none',s=100,label='测试集')# 设置标题和坐标轴标签plt.title('随机森林分类决策边界(花萼长度 vs 花萼宽度)',fontsize=14)plt.xlabel(iris.feature_names[0],fontsize=12)plt.ylabel(iris.feature_names[1],fontsize=12)# 添加图例plt.legend(loc='upper left')# 显示网格plt.grid(alpha=0.3)# 保存图片plt.savefig('分类决策边界.png',dpi=300,bbox_inches='tight')# 显示图表plt.show()3. 简洁版代码(保留核心逻辑)
fromsklearn.ensembleimportRandomForestClassifierfromsklearn.model_selectionimporttrain_test_splitfromsklearn.ensembleimportBaggingClassifierfromsklearn.treeimportDecisionTreeClassifierfromsklearn.metricsimportaccuracy_scorefromsklearn.datasetsimportload_irisimportmatplotlib.pyplotaspltimportnumpyasnp plt.rcParams['font.sans-serif']=['SimHei']plt.rcParams['axes.unicode_minus']=Falseiris=load_iris()X=iris.data[:,:2]y=iris.target X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.33,random_state=42,stratify=y)print("="*50)print(f"训练集样本数:{len(X_train)},测试集样本数:{len(X_test)}")rnd_clf=RandomForestClassifier(n_estimators=15,max_leaf_nodes=16,n_jobs=1,oob_score=True,random_state=42)rnd_clf.fit(X_train,y_train)print("="*50)print(f"随机森林袋外(OOB)样本准确率:{rnd_clf.oob_score_:.4f}")y_pred_rf=rnd_clf.predict(X_test)rf_acc=accuracy_score(y_test,y_pred_rf)print(f"随机森林测试集分类准确率:{rf_acc:.4f}")print("="*50)print("全特征随机森林特征重要性分析:")rnd_clf_full=RandomForestClassifier(n_estimators=500,n_jobs=-1,random_state=42)rnd_clf_full.fit(iris["data"],iris['target'])feature_importance=[]forname,scoreinzip(iris['feature_names'],rnd_clf_full.feature_importances_):print(f"特征:{name},重要性得分:{score:.4f}")feature_importance.append((name,score))features=[x[0]forxinfeature_importance]scores=[x[1]forxinfeature_importance]plt.figure(figsize=(10,6))bars=plt.bar(features,scores,color=['#1f77b4','#ff7f0e','#2ca02c','#d62728'])forbarinbars:height=bar.get_height()plt.text(bar.get_x()+bar.get_width()/2.,height+0.01,f'{height:.4f}',ha='center',va='bottom',fontsize=10)plt.title('鸢尾花分类 - 随机森林特征重要性',fontsize=14)plt.xlabel('特征名称',fontsize=12)plt.ylabel('重要性得分',fontsize=12)plt.xticks(rotation=15)plt.grid(axis='y',linestyle='--',alpha=0.7)plt.savefig('特征重要性.png',dpi=300,bbox_inches='tight')plt.show()print("="*50)print("绘制二维特征(花萼长宽)的分类决策边界...")x_min,x_max=X[:,0].min()-0.5,X[:,0].max()+0.5y_min,y_max=X[:,1].min()-0.5,X[:,1].max()+0.5xx,yy=np.meshgrid(np.arange(x_min,x_max,0.02),np.arange(y_min,y_max,0.02))Z=rnd_clf.predict(np.c_[xx.ravel(),yy.ravel()])Z=Z.reshape(xx.shape)plt.figure(figsize=(10,6))plt.contourf(xx,yy,Z,alpha=0.3,cmap=plt.cm.RdYlBu)plt.scatter(X_train[:,0],X_train[:,1],c=y_train,edgecolors='k',cmap=plt.cm.RdYlBu,label='训练集')plt.scatter(X_test[:,0],X_test[:,1],c=y_test,edgecolors='k',cmap=plt.cm.RdYlBu,marker='o',facecolors='none',s=100,label='测试集')plt.title('随机森林分类决策边界(花萼长度 vs 花萼宽度)',fontsize=14)plt.xlabel(iris.feature_names[0],fontsize=12)plt.ylabel(iris.feature_names[1],fontsize=12)plt.legend(loc='upper left')plt.grid(alpha=0.3)plt.savefig('分类决策边界.png',dpi=300,bbox_inches='tight')plt.show()关键输出说明
- OOB评分:通常在0.85~0.95之间,代表模型在未参与训练的“袋外样本”上的准确率,反映泛化能力;
- 测试集准确率:基于花萼长宽两个特征,准确率约0.7~0.8(若用全部4个特征,准确率接近1.0);
- 特征重要性:花瓣长度(petal length)和花瓣宽度(petal width)的重要性得分远高于花萼特征,是鸢尾花分类的核心特征;
- 可视化图表:
- 特征重要性柱状图:直观展示各特征的贡献度;
- 决策边界图:可看到随机森林对二维特征的分类边界,以及训练/测试样本的分布。