用Python+Scikit-learn轻松搞定鸢尾花分类:从数据探索到模型调优全流程

📅 发布时间:2026/7/13 16:43:16 👁️ 浏览次数:
用Python+Scikit-learn轻松搞定鸢尾花分类:从数据探索到模型调优全流程
用PythonScikit-learn轻松搞定鸢尾花分类从数据探索到模型调优全流程如果你刚开始接触机器学习可能会觉得那些复杂的算法和数学公式有点让人望而生畏。但我想告诉你的是机器学习并不总是那么高深莫测——有时候一个经典的数据集就能让你快速上手体验到构建智能模型的乐趣。鸢尾花数据集就是这样一个完美的起点它就像机器学习界的“Hello World”简洁却不简单。我在带新人学习机器学习时经常用这个数据集作为第一个实战项目。它只有150个样本4个特征3个类别数据量不大但足够让你理解整个机器学习流程从数据加载、探索分析到特征工程、模型训练再到评估优化。更重要的是你能在这个过程中直观地看到不同特征对分类结果的影响理解为什么有些特征比另一些更有区分度。今天我们就用Python的Scikit-learn库一步步走完这个完整的流程。我会分享一些在实际项目中积累的经验比如如何选择特征、如何调整模型参数、如何避免常见的陷阱。无论你是数据分析师、机器学习初学者还是想温故知新的开发者这篇文章都能给你带来实用的收获。1. 初识鸢尾花数据集不仅仅是加载数据那么简单鸢尾花数据集由统计学家Ronald Fisher在1936年引入已经成为机器学习领域最经典的多变量分析样本之一。数据集包含了三种鸢尾花山鸢尾、变色鸢尾、维吉尼亚鸢尾各50个样本每个样本有4个特征花萼长度、花萼宽度、花瓣长度、花瓣宽度单位都是厘米。在Scikit-learn中加载这个数据集非常简单from sklearn.datasets import load_iris import pandas as pd # 加载数据集 iris load_iris() # 查看数据集的基本信息 print(f特征名称: {iris.feature_names}) print(f目标类别: {iris.target_names}) print(f数据形状: {iris.data.shape}) print(f目标形状: {iris.target.shape}) # 转换为DataFrame便于分析 df pd.DataFrame(iris.data, columnsiris.feature_names) df[species] iris.target df[species_name] df[species].map({0: setosa, 1: versicolor, 2: virginica})但仅仅加载数据是不够的。在实际项目中我习惯先对数据有一个整体的了解。比如我们可以快速查看一下各类别的分布情况# 查看各类别样本数量 species_counts df[species_name].value_counts() print(各类别样本数量:) print(species_counts) # 查看基本统计信息 print(\n特征统计信息:) print(df.describe())注意虽然鸢尾花数据集是平衡的每个类别50个样本但在真实项目中数据不平衡是常见问题。如果遇到类别不平衡需要考虑采用过采样、欠采样或调整类别权重等方法。一个经常被忽视但很重要的步骤是检查数据集中是否存在缺失值。虽然鸢尾花数据集是完整的但养成检查的习惯对处理真实数据很有帮助# 检查缺失值 missing_values df.isnull().sum() print(缺失值统计:) print(missing_values)2. 数据可视化用眼睛发现模式在投入模型训练之前花时间可视化数据是值得的。可视化不仅能帮你发现数据中的模式还能让你对问题有更直观的理解。对于鸢尾花数据集我们可以从多个角度进行可视化分析。首先让我们看看特征的分布情况。不同类别的特征分布可能有明显差异import matplotlib.pyplot as plt import seaborn as sns # 设置图形风格 plt.style.use(seaborn-v0_8-darkgrid) fig, axes plt.subplots(2, 2, figsize(12, 10)) features iris.feature_names for idx, feature in enumerate(features): row, col divmod(idx, 2) ax axes[row, col] # 绘制每个类别的特征分布 for species_id, species_name in enumerate(iris.target_names): species_data df[df[species] species_id][feature] ax.hist(species_data, alpha0.5, labelspecies_name, bins15) ax.set_xlabel(feature) ax.set_ylabel(频数) ax.set_title(f{feature}分布) ax.legend() plt.tight_layout() plt.show()散点图矩阵是探索特征间关系的强大工具。它能一次性展示所有特征两两之间的关系# 散点图矩阵 sns.pairplot(df, huespecies_name, diag_kindkde, palettehusl, height2.5) plt.suptitle(鸢尾花特征散点图矩阵, y1.02) plt.show()从散点图中你可能会注意到一个有趣的现象花瓣特征花瓣长度和花瓣宽度比花萼特征花萼长度和花萼宽度有更好的类别区分能力。特别是山鸢尾setosa与其他两种鸢尾在花瓣特征上有明显的分离。为了量化这种观察我们可以计算特征与目标的相关性# 计算特征与目标的相关性 # 首先将类别转换为数值已经是数值 correlation_with_target df[[sepal length (cm), sepal width (cm), petal length (cm), petal width (cm), species]].corr() print(特征与目标类别的相关性:) print(correlation_with_target[species].sort_values(ascendingFalse))相关性分析通常会显示花瓣长度和花瓣宽度与目标类别有较高的相关性这解释了为什么基于花瓣特征的分类通常效果更好。3. 特征工程从原始数据到模型输入特征工程是机器学习中至关重要的一步好的特征能显著提升模型性能。对于鸢尾花分类我们可以尝试几种特征工程技巧。3.1 特征缩放许多机器学习算法对特征的尺度敏感。逻辑回归、支持向量机等基于距离的算法尤其需要特征缩放。常用的缩放方法有标准化Standardization和归一化Normalizationfrom sklearn.preprocessing import StandardScaler, MinMaxScaler # 分离特征和目标 X iris.data y iris.target # 标准化使特征均值为0方差为1 scaler_standard StandardScaler() X_standardized scaler_standard.fit_transform(X) # 归一化将特征缩放到[0,1]范围 scaler_minmax MinMaxScaler() X_normalized scaler_minmax.fit_transform(X) # 比较原始数据和缩放后数据的统计信息 original_stats pd.DataFrame(X).describe().loc[[mean, std, min, max]] standardized_stats pd.DataFrame(X_standardized).describe().loc[[mean, std, min, max]] normalized_stats pd.DataFrame(X_normalized).describe().loc[[mean, std, min, max]] print(原始数据统计:) print(original_stats) print(\n标准化后统计:) print(standardized_stats) print(\n归一化后统计:) print(normalized_stats)3.2 特征选择并非所有特征都同样重要。我们可以使用统计方法选择最有区分度的特征。对于鸢尾花数据集我们已经从可视化中观察到花瓣特征可能更重要。让我们用统计测试来验证from sklearn.feature_selection import SelectKBest, f_classif # 使用ANOVA F-value选择最好的k个特征 selector SelectKBest(score_funcf_classif, kall) selector.fit(X, y) # 查看每个特征的得分 feature_scores pd.DataFrame({ 特征: iris.feature_names, F值: selector.scores_, p值: selector.pvalues_ }).sort_values(F值, ascendingFalse) print(特征重要性排序基于ANOVA F检验:) print(feature_scores)3.3 创建新特征有时原始特征的组合或变换能提供更好的信息。对于鸢尾花数据集我们可以尝试创建一些新特征import numpy as np # 创建新特征花瓣面积近似值 petal_area X[:, 2] * X[:, 3] # 花瓣长度 * 花瓣宽度 # 创建新特征花萼面积近似值 sepal_area X[:, 0] * X[:, 1] # 花萼长度 * 花萼宽度 # 创建新特征花瓣长宽比 petal_ratio X[:, 2] / (X[:, 3] 1e-8) # 防止除零 # 创建新特征花萼长宽比 sepal_ratio X[:, 0] / (X[:, 1] 1e-8) # 将新特征添加到数据中 X_extended np.column_stack([X, petal_area, sepal_area, petal_ratio, sepal_ratio]) print(f原始特征数: {X.shape[1]}) print(f扩展后特征数: {X_extended.shape[1]})提示创建新特征时要确保它们有实际意义。花瓣面积和长宽比确实可能反映植物的生长特征但也要注意避免创建冗余特征。4. 逻辑回归实战深入理解多分类策略逻辑回归虽然名字中有回归但它实际上是一种分类算法。对于二分类问题逻辑回归使用sigmoid函数对于多分类问题则需要扩展。Scikit-learn提供了两种主要策略OvROne-vs-Rest和Multinomial多项逻辑回归。4.1 数据准备与分割在训练模型之前我们需要将数据分为训练集和测试集from sklearn.model_selection import train_test_split # 使用原始特征后续会对比不同特征集的效果 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) print(f训练集大小: {X_train.shape}) print(f测试集大小: {X_test.shape}) print(f训练集类别分布: {np.bincount(y_train)}) print(f测试集类别分布: {np.bincount(y_test)})注意我使用了stratifyy参数这确保了训练集和测试集中各类别的比例与原始数据集相同。在处理不平衡数据时这个参数特别重要。4.2 OvR与Multinomial策略对比让我们实现两种不同的多分类策略并比较它们的性能from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, classification_report, confusion_matrix # 策略1: One-vs-Rest (OvR) logreg_ovr LogisticRegression( multi_classovr, solverliblinear, # liblinear支持OvR max_iter1000, random_state42 ) # 策略2: Multinomial多项逻辑回归 logreg_multi LogisticRegression( multi_classmultinomial, solverlbfgs, # lbfgs、newton-cg、sag、saga支持Multinomial max_iter1000, random_state42 ) # 训练模型 logreg_ovr.fit(X_train, y_train) logreg_multi.fit(X_train, y_train) # 预测 y_pred_ovr logreg_ovr.predict(X_test) y_pred_multi logreg_multi.predict(X_test) # 评估 print(OvR策略性能:) print(f准确率: {accuracy_score(y_test, y_pred_ovr):.4f}) print(classification_report(y_test, y_pred_ovr, target_namesiris.target_names)) print(\nMultinomial策略性能:) print(f准确率: {accuracy_score(y_test, y_pred_multi):.4f}) print(classification_report(y_test, y_pred_multi, target_namesiris.target_names))在实际测试中你可能会发现两种策略的准确率相近但Multinomial策略通常更优因为它同时考虑所有类别而不是将多分类分解为多个二分类问题。4.3 深入理解multi_class参数multi_class参数的选择会影响可用的求解器solver。这是一个容易混淆的地方让我用一个表格来澄清multi_class 设置可用求解器说明ovrliblinear, newton-cg, lbfgs, sag, saga将多分类问题分解为多个二分类问题multinomialnewton-cg, lbfgs, sag, saga直接处理多分类考虑类别间关系auto自动选择默认值二分类或solverliblinear时选ovr否则选multinomial重要提示如果你选择multinomial就不能使用liblinear求解器。这是因为liblinear不直接支持多项逻辑回归损失函数。4.4 可视化决策边界理解模型如何做出决策的一个好方法是可视化决策边界。对于二维特征我们可以绘制决策区域import numpy as np def plot_decision_boundary(model, X, y, feature_indices[0, 1], title决策边界): 绘制二维决策边界 # 只使用两个特征 X_2d X[:, feature_indices] # 创建网格 x_min, x_max X_2d[:, 0].min() - 0.5, X_2d[:, 0].max() 0.5 y_min, y_max X_2d[:, 1].min() - 0.5, X_2d[:, 1].max() 0.5 xx, yy np.meshgrid(np.linspace(x_min, x_max, 200), np.linspace(y_min, y_max, 200)) # 训练只使用这两个特征的模型 model_2d LogisticRegression(multi_classmultinomial, solverlbfgs, max_iter1000) model_2d.fit(X_2d, y) # 预测网格点 Z model_2d.predict(np.c_[xx.ravel(), yy.ravel()]) Z Z.reshape(xx.shape) # 绘制 plt.figure(figsize(10, 8)) plt.contourf(xx, yy, Z, alpha0.3, cmapplt.cm.coolwarm) # 绘制数据点 scatter plt.scatter(X_2d[:, 0], X_2d[:, 1], cy, edgecolorblack, s50, cmapplt.cm.coolwarm) plt.xlabel(iris.feature_names[feature_indices[0]]) plt.ylabel(iris.feature_names[feature_indices[1]]) plt.title(title) plt.colorbar(scatter, ticks[0, 1, 2]) plt.show() # 比较不同特征组合的决策边界 print(花萼特征决策边界:) plot_decision_boundary(logreg_multi, X_train, y_train, feature_indices[0, 1], title基于花萼长度和宽度的决策边界) print(花瓣特征决策边界:) plot_decision_boundary(logreg_multi, X_train, y_train, feature_indices[2, 3], title基于花瓣长度和宽度的决策边界)从决策边界图中你可以清楚地看到为什么花瓣特征能提供更好的分类效果——类别间的边界更清晰重叠区域更少。5. 模型评估与调优不止于准确率准确率是评估分类模型的常用指标但对于多分类问题我们需要更全面的评估。特别是当类别不平衡或不同类别的误分类代价不同时。5.1 混淆矩阵分析混淆矩阵能告诉我们模型在哪里犯了错误from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay # 计算混淆矩阵 cm confusion_matrix(y_test, y_pred_multi) # 可视化 disp ConfusionMatrixDisplay(confusion_matrixcm, display_labelsiris.target_names) disp.plot(cmapplt.cm.Blues, values_formatd) plt.title(混淆矩阵) plt.show() # 分析混淆矩阵 print(混淆矩阵分析:) for i, true_class in enumerate(iris.target_names): for j, pred_class in enumerate(iris.target_names): if i ! j and cm[i, j] 0: print(f将 {true_class} 误分类为 {pred_class}: {cm[i, j]} 次)5.2 多维度评估指标对于多分类问题宏平均macro-average和微平均micro-average提供了不同的视角from sklearn.metrics import precision_score, recall_score, f1_score # 计算各种评估指标 precision_macro precision_score(y_test, y_pred_multi, averagemacro) precision_micro precision_score(y_test, y_pred_multi, averagemicro) recall_macro recall_score(y_test, y_pred_multi, averagemacro) recall_micro recall_score(y_test, y_pred_multi, averagemicro) f1_macro f1_score(y_test, y_pred_multi, averagemacro) f1_micro f1_score(y_test, y_pred_multi, averagemicro) print(多分类评估指标对比:) print(f{指标:15} {宏平均:10} {微平均:10}) print(f{-*40}) print(f{精确率:15} {precision_macro:.4f} {precision_micro:.4f}) print(f{召回率:15} {recall_macro:.4f} {recall_micro:.4f}) print(f{F1分数:15} {f1_macro:.4f} {f1_micro:.4f})5.3 超参数调优逻辑回归有几个重要的超参数通过调优可以提升模型性能。最常用的是正则化参数C和正则化类型from sklearn.model_selection import GridSearchCV # 定义参数网格 param_grid { C: [0.001, 0.01, 0.1, 1, 10, 100], penalty: [l1, l2], solver: [liblinear, saga] # 只有liblinear和saga支持l1正则化 } # 创建网格搜索对象 grid_search GridSearchCV( LogisticRegression(multi_classovr, max_iter1000, random_state42), param_grid, cv5, scoringaccuracy, n_jobs-1 ) # 执行网格搜索 grid_search.fit(X_train, y_train) # 输出最佳参数 print(最佳参数组合:) print(grid_search.best_params_) print(f最佳交叉验证准确率: {grid_search.best_score_:.4f}) # 在测试集上评估最佳模型 best_model grid_search.best_estimator_ y_pred_best best_model.predict(X_test) test_accuracy accuracy_score(y_test, y_pred_best) print(f测试集准确率: {test_accuracy:.4f})5.4 学习曲线分析学习曲线可以帮助我们判断模型是否过拟合或欠拟合以及增加数据是否有帮助from sklearn.model_selection import learning_curve def plot_learning_curve(estimator, title, X, y, cv5): 绘制学习曲线 train_sizes, train_scores, test_scores learning_curve( estimator, X, y, cvcv, n_jobs-1, train_sizesnp.linspace(0.1, 1.0, 10), scoringaccuracy ) train_scores_mean np.mean(train_scores, axis1) train_scores_std np.std(train_scores, axis1) test_scores_mean np.mean(test_scores, axis1) test_scores_std np.std(test_scores, axis1) plt.figure(figsize(10, 6)) plt.fill_between(train_sizes, train_scores_mean - train_scores_std, train_scores_mean train_scores_std, alpha0.1, colorr) plt.fill_between(train_sizes, test_scores_mean - test_scores_std, test_scores_mean test_scores_std, alpha0.1, colorg) plt.plot(train_sizes, train_scores_mean, o-, colorr, label训练得分) plt.plot(train_sizes, test_scores_mean, o-, colorg, label交叉验证得分) plt.xlabel(训练样本数) plt.ylabel(准确率) plt.title(title) plt.legend(locbest) plt.grid(True) plt.show() # 绘制学习曲线 plot_learning_curve( LogisticRegression(multi_classmultinomial, solverlbfgs, max_iter1000, C1), 逻辑回归学习曲线, X_train, y_train )6. 特征选择对比实验花瓣 vs 花萼我们在可视化阶段观察到花瓣特征可能比花萼特征更有区分力。现在让我们通过实验来验证这个假设6.1 不同特征组合的模型性能# 定义不同的特征组合 feature_sets { 仅花萼特征: [0, 1], # 花萼长度和宽度 仅花瓣特征: [2, 3], # 花瓣长度和宽度 花萼花瓣: [0, 1, 2, 3], # 所有特征 仅花瓣长度: [2], # 单个特征花瓣长度 仅花萼长度: [0], # 单个特征花萼长度 } # 比较不同特征组合的性能 results [] for name, indices in feature_sets.items(): # 提取特征子集 X_subset X[:, indices] # 分割数据 X_train_sub, X_test_sub, y_train_sub, y_test_sub train_test_split( X_subset, y, test_size0.2, random_state42, stratifyy ) # 训练模型 model LogisticRegression( multi_classmultinomial, solverlbfgs, max_iter1000, random_state42 ) model.fit(X_train_sub, y_train_sub) # 评估 train_acc model.score(X_train_sub, y_train_sub) test_acc model.score(X_test_sub, y_test_sub) results.append({ 特征组合: name, 特征数: len(indices), 训练准确率: train_acc, 测试准确率: test_acc }) # 创建结果表格 results_df pd.DataFrame(results) print(不同特征组合的性能对比:) print(results_df.to_string(indexFalse))6.2 可视化对比结果# 可视化对比结果 fig, (ax1, ax2) plt.subplots(1, 2, figsize(14, 6)) # 训练准确率对比 ax1.barh(results_df[特征组合], results_df[训练准确率], colorskyblue) ax1.set_xlabel(训练准确率) ax1.set_title(不同特征组合的训练准确率) ax1.set_xlim([0, 1.05]) # 测试准确率对比 ax2.barh(results_df[特征组合], results_df[测试准确率], colorlightcoral) ax2.set_xlabel(测试准确率) ax2.set_title(不同特征组合的测试准确率) ax2.set_xlim([0, 1.05]) plt.tight_layout() plt.show()这个实验通常会清楚地显示使用花瓣特征的模型性能优于使用花萼特征的模型而使用所有四个特征的模型性能最好。这验证了我们最初的观察。6.3 特征重要性分析逻辑回归的系数可以解释为特征重要性# 使用所有特征训练最终模型 final_model LogisticRegression( multi_classmultinomial, solverlbfgs, max_iter1000, random_state42 ) final_model.fit(X_train, y_train) # 获取特征系数 coefficients final_model.coef_ # 创建特征重要性表格 importance_df pd.DataFrame( coefficients.T, columnsiris.target_names, indexiris.feature_names ) print(特征系数重要性:) print(importance_df) # 可视化特征重要性 plt.figure(figsize(10, 6)) importance_df.plot(kindbar, figsize(12, 6)) plt.title(逻辑回归特征系数按类别) plt.xlabel(特征) plt.ylabel(系数值) plt.xticks(rotation45) plt.legend(title类别) plt.grid(True, alpha0.3) plt.tight_layout() plt.show()系数的大小和符号告诉我们每个特征对每个类别的影响。正系数表示该特征值增大会增加属于该类别的概率负系数则表示相反。7. 完整项目实战构建可复用的分类流程现在让我们将所有知识整合到一个完整的、可复用的机器学习流程中。这个流程可以轻松适配其他分类问题。7.1 创建模块化代码import numpy as np import pandas as pd from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split, cross_val_score from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.metrics import (accuracy_score, classification_report, confusion_matrix, ConfusionMatrixDisplay) import matplotlib.pyplot as plt import seaborn as sns class IrisClassifier: 鸢尾花分类器完整流程 def __init__(self, random_state42): self.random_state random_state self.scaler StandardScaler() self.model None self.feature_names None self.target_names None def load_and_explore_data(self): 加载并探索数据 iris load_iris() self.X iris.data self.y iris.target self.feature_names iris.feature_names self.target_names iris.target_names print(*50) print(数据集探索) print(*50) print(f样本数: {self.X.shape[0]}) print(f特征数: {self.X.shape[1]}) print(f特征名: {self.feature_names}) print(f类别数: {len(self.target_names)}) print(f类别名: {self.target_names}) print(f类别分布: {np.bincount(self.y)}) return self.X, self.y def prepare_data(self, test_size0.2): 准备训练和测试数据 # 分割数据 self.X_train, self.X_test, self.y_train, self.y_test train_test_split( self.X, self.y, test_sizetest_size, random_stateself.random_state, stratifyself.y ) # 特征缩放 self.X_train_scaled self.scaler.fit_transform(self.X_train) self.X_test_scaled self.scaler.transform(self.X_test) print(\n *50) print(数据准备完成) print(*50) print(f训练集大小: {self.X_train.shape}) print(f测试集大小: {self.X_test.shape}) return (self.X_train_scaled, self.X_test_scaled, self.y_train, self.y_test) def train_model(self, multi_classmultinomial, C1.0): 训练逻辑回归模型 self.model LogisticRegression( multi_classmulti_class, solverlbfgs, CC, max_iter1000, random_stateself.random_state ) self.model.fit(self.X_train_scaled, self.y_train) # 交叉验证 cv_scores cross_val_score( self.model, self.X_train_scaled, self.y_train, cv5, scoringaccuracy ) print(\n *50) print(模型训练完成) print(*50) print(f交叉验证准确率: {cv_scores.mean():.4f} (±{cv_scores.std():.4f})) print(f训练集准确率: {self.model.score(self.X_train_scaled, self.y_train):.4f}) return self.model def evaluate_model(self): 评估模型性能 if self.model is None: raise ValueError(请先训练模型) # 预测 self.y_pred self.model.predict(self.X_test_scaled) self.y_pred_proba self.model.predict_proba(self.X_test_scaled) # 计算指标 test_accuracy accuracy_score(self.y_test, self.y_pred) report classification_report(self.y_test, self.y_pred, target_namesself.target_names) print(\n *50) print(模型评估结果) print(*50) print(f测试集准确率: {test_accuracy:.4f}) print(\n分类报告:) print(report) # 混淆矩阵 self.plot_confusion_matrix() # 特征重要性 self.plot_feature_importance() return test_accuracy def plot_confusion_matrix(self): 绘制混淆矩阵 cm confusion_matrix(self.y_test, self.y_pred) plt.figure(figsize(8, 6)) disp ConfusionMatrixDisplay(confusion_matrixcm, display_labelsself.target_names) disp.plot(cmapplt.cm.Blues, values_formatd) plt.title(混淆矩阵) plt.tight_layout() plt.show() def plot_feature_importance(self): 绘制特征重要性 if self.model is None: raise ValueError(请先训练模型) coefficients self.model.coef_ fig, axes plt.subplots(1, 3, figsize(15, 5)) for idx, (ax, class_name) in enumerate(zip(axes, self.target_names)): ax.barh(self.feature_names, coefficients[idx]) ax.set_xlabel(系数值) ax.set_title(f{class_name} 的特征重要性) ax.axvline(x0, colorblack, linestyle-, linewidth0.5) plt.tight_layout() plt.show() def predict_new_sample(self, sample): 预测新样本 if self.model is None: raise ValueError(请先训练模型) # 缩放新样本 sample_scaled self.scaler.transform([sample]) # 预测 pred_class self.model.predict(sample_scaled)[0] pred_proba self.model.predict_proba(sample_scaled)[0] print(\n *50) print(新样本预测结果) print(*50) print(f输入特征: {sample}) print(f预测类别: {self.target_names[pred_class]}) print(\n类别概率:) for class_idx, class_name in enumerate(self.target_names): print(f {class_name}: {pred_proba[class_idx]:.4f}) return pred_class, pred_proba # 使用完整流程 def main(): # 创建分类器实例 classifier IrisClassifier(random_state42) # 1. 加载数据 classifier.load_and_explore_data() # 2. 准备数据 classifier.prepare_data(test_size0.2) # 3. 训练模型 classifier.train_model(multi_classmultinomial, C1.0) # 4. 评估模型 classifier.evaluate_model() # 5. 预测新样本 # 示例一个中等大小的鸢尾花 new_sample [5.5, 3.0, 3.5, 1.0] # 花萼长, 花萼宽, 花瓣长, 花瓣宽 classifier.predict_new_sample(new_sample) return classifier if __name__ __main__: trained_classifier main()7.2 模型部署考虑在实际项目中我们还需要考虑模型的部署。以下是一些关键考虑因素import joblib import json class ModelPipeline: 完整的模型管道包括训练、保存、加载和预测 def __init__(self): self.scaler StandardScaler() self.model None self.metadata {} def train(self, X, y, feature_names, target_names): 训练完整管道 # 保存元数据 self.metadata[feature_names] feature_names self.metadata[target_names] target_names self.metadata[n_samples] X.shape[0] self.metadata[n_features] X.shape[1] # 分割数据 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) # 特征缩放 X_train_scaled self.scaler.fit_transform(X_train) X_test_scaled self.scaler.transform(X_test) # 训练模型 self.model LogisticRegression( multi_classmultinomial, solverlbfgs, max_iter1000, random_state42 ) self.model.fit(X_train_scaled, y_train) # 评估 train_score self.model.score(X_train_scaled, y_train) test_score self.model.score(X_test_scaled, y_test) self.metadata[train_accuracy] float(train_score) self.metadata[test_accuracy] float(test_score) print(f训练完成 - 训练准确率: {train_score:.4f}, 测试准确率: {test_score:.4f}) return self def save(self, pathiris_model): 保存模型管道 # 创建保存目录 import os os.makedirs(path, exist_okTrue) # 保存scaler joblib.dump(self.scaler, f{path}/scaler.pkl) # 保存模型 joblib.dump(self.model, f{path}/model.pkl) # 保存元数据 with open(f{path}/metadata.json, w) as f: json.dump(self.metadata, f, indent2) print(f模型已保存到 {path}/) classmethod def load(cls, pathiris_model): 加载模型管道 pipeline cls() # 加载scaler pipeline.scaler joblib.load(f{path}/scaler.pkl) # 加载模型 pipeline.model joblib.load(f{path}/model.pkl) # 加载元数据 with open(f{path}/metadata.json, r) as f: pipeline.metadata json.load(f) print(f模型已从 {path}/ 加载) return pipeline def predict(self, X): 预测新样本 if self.model is None: raise ValueError(请先训练或加载模型) # 缩放特征 X_scaled self.scaler.transform(X) # 预测 predictions self.model.predict(X_scaled) probabilities self.model.predict_proba(X_scaled) # 转换为类别名 pred_classes [self.metadata[target_names][p] for p in predictions] return { predictions: pred_classes, probabilities: probabilities.tolist(), class_indices: predictions.tolist() } def get_feature_importance(self): 获取特征重要性 if self.model is None: raise ValueError(请先训练或加载模型) importance {} coefficients self.model.coef_ for class_idx, class_name in enumerate(self.metadata[target_names]): importance[class_name] {} for feat_idx, feat_name in enumerate(self.metadata[feature_names]): importance[class_name][feat_name] float(coefficients[class_idx, feat_idx]) return importance # 使用完整管道 def demo_pipeline(): # 加载数据 iris load_iris() X, y iris.data, iris.target feature_names iris.feature_names target_names iris.target_names # 创建和训练管道 print(训练模型管道...) pipeline ModelPipeline() pipeline.train(X, y, feature_names, target_names) # 保存管道 pipeline.save(my_iris_model) # 加载管道模拟部署环境 print(\n加载模型管道...) loaded_pipeline ModelPipeline.load(my_iris_model) # 使用管道进行预测 print(\n进行预测...) test_samples np.array([ [5.1, 3.5, 1.4, 0.2], # 应该是setosa [6.0, 2.7, 5.1, 1.6], # 应该是virginica [5.7, 2.8, 4.1, 1.3] # 应该是versicolor ]) results loaded_pipeline.predict(test_samples) print(\n预测结果:) for i, (sample, pred, probs) in enumerate(zip(test_samples, results[predictions], results[probabilities])): print(f\n样本 {i1}: {sample}) print(f 预测类别: {pred}) print(f 各类别概率:) for class_name, prob in zip(target_names, probs): print(f {class_name}: {prob:.4f}) # 特征重要性 print(\n特征重要性:) importance loaded_pipeline.get_feature_importance() for class_name, features in importance.items(): print(f\n{class_name}:) for feat_name, coeff in features.items(): print(f {feat_name}: {coeff:.4f}) return loaded_pipeline # 运行演示 if __name__ __main__: pipeline demo_pipeline()这个完整的管道展示了如何将机器学习模型产品化包括训练、保存、加载和预测的全流程。在实际项目中你可能会将这个管道封装为API服务或者集成到更大的应用程序中。通过这个完整的鸢尾花分类项目我们不仅学会了如何使用逻辑回归解决多分类问题还掌握了数据探索、特征工程、模型评估和部署的完整流程。这些技能可以迁移到其他分类任务中为你解决更复杂的机器学习问题打下坚实基础。记住机器学习项目的成功不仅取决于算法选择更取决于对数据的深入理解、合适的特征工程和严谨的评估方法。鸢尾花数据集虽然简单但它教会我们的方法论却是通用的。在实际工作中遇到新问题时不妨回想一下这个经典案例的处理流程它可能会给你带来启发。