2025/10/24日 每日总结 机器学习入门实践:数据准备与模型评估(Iris数据集实战)
机器学习入门实践:数据准备与模型评估(Iris数据集实战)
作为机器学习入门的基础实验,数据准备和模型评估是构建可靠模型的核心步骤。这次我以经典的Iris鸢尾花数据集为对象,实操了数据集加载、五折交叉验证和多指标评估的完整流程,整理了一份详细的实践笔记,适合刚入门的小伙伴参考~
一、实验核心目标
-
熟悉Python数据处理和机器学习库的基本操作(pandas、scikit-learn)
-
掌握数据集的多种加载方式(本地文件、库内置数据)
-
理解训练集/测试集划分、五折交叉验证的原理与实现
-
学会计算模型的准确率、精度、召回率和F1值等核心评估指标
二、实验核心内容
本次实验围绕Iris数据集展开,主要完成4件事:
-
用pandas读取本地存储的Iris数据集(CSV格式、UCI标准格式)
-
直接从scikit-learn库加载内置Iris数据集
-
基于随机森林模型实现五折交叉验证训练
-
计算并对比不同加载方式下的模型评估指标
三、算法设计思路
1. 核心逻辑
通过五折交叉验证充分利用有限数据,避免单一划分带来的结果偶然性,同时通过多指标全面评估模型泛化能力。
2. 算法伪代码
def EvaluateRF-CV(X, y, target_names, model, random_state=42):
# 1. 定义5折交叉验证(打乱数据,固定随机种子保证可重复)
kf = KFold(n_splits=5, shuffle=True, random_state=random_state)
2. 计算每折准确率
acc_scores = cross_val_score(model, X, y, cv=kf, scoring='accuracy')
3. 得到交叉验证的预测标签
y_pred = cross_val_predict(model, X, y, cv=kf)
4. 计算多维度评估指标(加权平均处理多分类)
avg_acc = mean(acc_scores)
std_acc = std(acc_scores)
precision = precision_score(y, y_pred, average='weighted', zero_division=0)
recall = recall_score(y, y_pred, average='weighted', zero_division=0)
f1 = f1_score(y, y_pred, average='weighted', zero_division=0)
5. 输出结果
print(f"五折交叉验证准确率: {avg_acc:.4f} (±{std_acc:.4f})")
print(f"Precision: {precision:.4f} Recall: {recall:.4f} F1: {f1:.4f}")
print(classification_report(y, y_pred, target_names=target_names))
return avg_acc, precision, recall, f1
### 3. 关键参数说明
| 参数 | 含义 | 作用 |
|------|------|------|
| n_estimators=100 | 随机森林中决策树数量 | 平衡模型性能与计算成本 |
| random_state=42 | 随机种子 | 保证实验结果可重复 |
| max_depth=3 | 单棵决策树最大深度 | 防止模型过拟合 |
| cv=5 | 交叉验证折数 | 充分利用数据,提升评估可靠性 |
| average='weighted' | 多分类指标平均方式 | 处理类别平衡问题 |
## 四、完整代码实现
```python
"""
机器学习入门:数据准备与模型评估(Iris数据集)
"""
import pandas as pd
import numpy as np
import os
import warnings
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score, cross_val_predict, KFold
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, classification_report
from sklearn.preprocessing import LabelEncoder
warnings.filterwarnings("ignore")
# ---------------------- 数据加载函数 ----------------------
def load_iris_local():
"""加载本地CSV格式Iris数据集(UTF-8-sig编码)"""
try:
df = pd.read_csv("iris.csv", encoding="utf-8-sig")
X = df.drop("species", axis=1).values
y = df["species"].values
le = LabelEncoder()
y_encoded = le.fit_transform(y)
print("✅ 成功从本地 iris.csv 加载数据")
return X, y_encoded, le.classes_
except FileNotFoundError:
print("❌ 本地 iris.csv 未找到")
return None, None, None
def load_iris_data_file():
"""加载UCI标准格式 iris.data"""
try:
cols = ["sepal_length", "sepal_width", "petal_length", "petal_width", "class"]
df = pd.read_csv("iris.data", header=None, names=cols)
X, y = df.iloc[:, :-1].values, df.iloc[:, -1].values
le = LabelEncoder()
y_encoded = le.fit_transform(y)
print("✅ 成功从 iris.data 加载数据")
return X, y_encoded, le.classes_
except Exception as e:
print(f"❌ 读取 iris.data 失败: {e}")
return None, None, None
def load_bezdek_iris_data():
"""加载Iris数据集变体 bezdekIris.data"""
try:
cols = ["sepal_length", "sepal_width", "petal_length", "petal_width", "class"]
df = pd.read_csv("bezdekIris.data", header=None, names=cols)
X, y = df.iloc[:, :-1].values, df.iloc[:, -1].values
le = LabelEncoder()
y_encoded = le.fit_transform(y)
print("✅ 成功从 bezdekIris.data 加载数据")
return X, y_encoded, le.classes_
except Exception as e:
print(f"❌ 读取 bezdekIris.data 失败: {e}")
return None, None, None
def load_iris_sklearn():
"""直接从scikit-learn加载内置数据集"""
iris = load_iris()
print("✅ 成功从 scikit-learn 加载数据")
return iris.data, iris.target, iris.target_names
# ---------------------- 工具函数 ----------------------
def list_available_files():
"""列出本地可加载的数据集文件"""
files = []
desc = {
"iris.data": "UCI标准Iris数据集",
"bezdekIris.data": "Iris数据集变体",
"iris.csv": "CSV格式Iris数据集",
}
print("\n📂 本地可用数据集文件:")
for f in desc:
if os.path.exists(f):
size = os.path.getsize(f)
files.append(f)
print(f" {f:<20} {size:>6} B {desc[f]}")
return files
def create_local_iris_file():
"""自动生成CSV格式Iris数据集(方便本地使用)"""
try:
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df["species"] = [iris.target_names[i] for i in iris.target]
df.to_csv("iris.csv", index=False, encoding="utf-8-sig")
print("✅ 已自动创建 iris.csv 文件")
return True
except Exception as e:
print(f"❌ 创建 iris.csv 失败: {e}")
return False
def display_dataset_info(X, y, target_names, source):
"""展示数据集基本信息"""
print(f"\n📊 {source} 数据集信息:")
print(f" 数据形状: {X.shape} 类别数: {len(target_names)}")
unique, counts = np.unique(y, return_counts=True)
for cls, cnt in zip(unique, counts):
print(f" {target_names[cls]:<15} {cnt:>3} 条样本")
feature_names = ["花萼长度", "花萼宽度", "花瓣长度", "花瓣宽度"]
for i, name in enumerate(feature_names):
print(f" {name}: 均值={X[:, i].mean():.2f} 标准差={X[:, i].std():.2f}")
def evaluate_model(X, y, target_names, model, model_name="模型"):
"""五折交叉验证 + 多指标评估"""
kf = KFold(n_splits=5, shuffle=True, random_state=42)
acc_scores = cross_val_score(model, X, y, cv=kf, scoring="accuracy")
y_pred = cross_val_predict(model, X, y, cv=kf)
avg_acc, std_acc = acc_scores.mean(), acc_scores.std()
prec = precision_score(y, y_pred, average="weighted", zero_division=0)
rec = recall_score(y, y_pred, average="weighted", zero_division=0)
f1 = f1_score(y, y_pred, average="weighted", zero_division=0)
print(f"\n🏆 {model_name} 评估结果:")
print(f" 五折交叉验证准确率: {avg_acc:.4f} (±{std_acc:.4f})")
print(f" Precision: {prec:.4f} Recall: {rec:.4f} F1: {f1:.4f}")
print("\n详细分类报告:")
print(classification_report(y, y_pred, target_names=target_names, digits=4))
return avg_acc, prec, rec, f1
# ---------------------- 主程序 ----------------------
def main():
# 初始化随机森林模型(固定参数保证可重复)
rf = RandomForestClassifier(n_estimators=100, random_state=42, max_depth=3)
print("=" * 60)
print("机器学习实践:数据准备与模型评估(Iris数据集)")
print("=" * 60)
# 列出本地可用文件
available_files = list_available_files()
# 构建加载方式菜单
loader_map = {
"1": (load_iris_sklearn, "scikit-learn内置"),
"2": (load_iris_local, "本地iris.csv"),
"3": (load_iris_data_file, "iris.data"),
"4": (load_bezdek_iris_data, "bezdekIris.data"),
}
menu = {"1": "从 scikit-learn 库直接加载"}
if "iris.csv" in available_files:
menu["2"] = "从本地CSV文件加载"
if "iris.data" in available_files:
menu["3"] = "从 iris.data 加载"
if "bezdekIris.data" in available_files:
menu["4"] = "从 bezdekIris.data 加载"
if not any(f in available_files for f in ["iris.csv", "iris.data", "bezdekIris.data"]):
menu["5"] = "创建本地 iris.csv 文件"
menu["0"] = "退出程序"
# 交互选择加载方式
while True:
print("\n🔧 请选择数据加载方式:")
for k, v in menu.items():
print(f"{k}. {v}")
choice = input(">>> ").strip()
if choice == "0":
print("👋 程序已退出")
break
# 自动创建本地文件
if choice == "5" and "5" in menu:
if create_local_iris_file():
X, y, names = load_iris_local()
if X is not None:
display_dataset_info(X, y, names, "新建本地CSV")
evaluate_model(X, y, names, rf, "新建本地CSV数据集")
continue
# 加载选中的数据集
if choice not in loader_map or choice not in menu:
print("❌ 无效选择,请重新输入")
continue
loader, desc = loader_map[choice]
X, y, names = loader()
if X is None:
continue
# 展示信息并评估模型
display_dataset_info(X, y, names, desc)
evaluate_model(X, y, names, rf, desc)
# 是否继续尝试其他加载方式
if input("\n🔄 是否继续尝试其他加载方式? (y/n) ").lower() != "y":
print("🎉 实验完成!")
break
print("=" * 60)
if __name__ == "__main__":
main()
五、实验运行结果
1. 不同加载方式的性能对比
| 数据加载方式 | 五折交叉验证准确率 | 精度 | 召回率 | F1值 |
|---|---|---|---|---|
| scikit-learn内置 | 0.9600 (±0.0249) | 0.9600 | 0.9600 | 0.9600 |
| 本地iris.data | 0.9600 (±0.0249) | 0.9600 | 0.9600 | 0.9600 |
| 本地bezdekIris.data | 0.9600 (±0.0249) | 0.9600 | 0.9600 | 0.9600 |
| 本地CSV文件 | 0.9600 (±0.0249) | 0.9600 | 0.9600 | 0.9600 |
2. 关键结论
-
不同加载方式下,模型性能基本一致,说明数据加载逻辑可靠
-
随机森林在Iris数据集上表现优异,平均准确率达96%
-
三类鸢尾花中,Iris-setosa的识别准确率达100%,另外两类准确率约94%
六、实验心得与收获
- 数据准备是基础:实际项目中数据格式多样(CSV、原始文本等),需要掌握灵活的加载和预处理方法。这次实现了多种格式的兼容加载,还添加了自动创建文件的容错机制,深刻体会到"数据干净,模型才靠谱"。
- 交叉验证的重要性:五折交叉验证比单一训练测试分割更能反映模型真实性能,避免了数据划分的偶然性。通过标准差可以看到,模型性能波动很小(±0.0249),说明模型稳定性较好。
- 多指标评估更全面:单一准确率在类别不平衡时会误导判断,而精度、召回率、F1值能从不同角度评估模型。比如这次实验中,虽然整体准确率很高,但通过分类报告能看到具体类别的表现差异。
- 工程细节不能忽视:随机种子的固定、标签编码的一致性、异常处理(文件不存在等情况),这些细节决定了实验的可重复性和代码的健壮性。
作为机器学习入门的第一个实验,这次实践不仅巩固了Python和scikit-learn的使用,更建立了"数据-模型-评估"的完整思维框架。后续可以尝试调整模型超参数(比如决策树数量、深度),或者更换其他模型(如逻辑回归、SVM)进行对比,进一步深化理解~

浙公网安备 33010602011771号