鸢尾花线性回归:sklearn实现回归模型训练与评估
简介鸢尾花数据集是机器学习入门最经典的多元分类数据之一由花萼长度、花萼宽度、花瓣长度、花瓣宽度四个属性构成用来区分Setosa、Versicolour和Virginica三个鸢尾花品种。这份资源面向正在学习Python机器学习与基础统计建模的初学者通过线性回归方式对特征进行拟合演示从数据读取、模型训练到分类预测的完整流程能帮助读者理解特征与标签之间的关系以及回归模型在判别任务中的实际用法。压缩包只有2KB共包含1个数据文件和1个Python脚本结构非常精简适合快速上手、对照学习代码写法也可作为课堂演示或自主练习的小型案例。目前已有11537人学习下载对刚接触机器学习的同学来说省去了搜寻和整理数据集的麻烦拿到后即可运行脚本复现实验。通过该资源还能学习基础的数据预处理、模型拟合和评价思路并在现成代码基础上调整参数或更换算法进一步巩固机器学习基础。1. 鸢尾花线性回归一个被误当分类的入门项目第一次看到“鸢尾花线性回归”这个标题时很多人的第一反应是“鸢尾花不是分类问题吗用 KNN 或者逻辑回归才对”。这恰恰是这个项目最有意思的地方——它把那个所有教程都在用的经典分类数据集硬生生做成了回归任务。你以为自己拿错了数据其实没有只是切入角度变了把花瓣长度当特征去回归预测花萼宽度或者在四个特征之间互相回归。这个项目适合两类人一类是刚入门机器学习、想搞懂数据到底怎么变成模型的初学者另一类是已经跑过分类教程、想换个视角看看同一份数据能挖掘出什么信息的开发者。它会带你走完一条从加载数据到训练模型再到可视化评估的完整链路而且全程只用 Python 和 sklearn代码量不到 60 行。2. 项目定位与技术选型为什么鸢尾花常被拿来做线性回归2.1 鸢尾花数据集到底长什么样鸢尾花数据集诞生于 1936 年统计学界的老古董了但到今天依然是机器学习入门的第一站。它内置在 sklearn 的 datasets 模块里不需要单独下载数据文件。数据一共 150 条样本每条样本有 4 个数值型特征花萼长度、花萼宽度、花瓣长度、花瓣宽度标签是 3 种鸢尾花Setosa、Versicolour、Virginica每种 50 条。# 在 Python 中快速查看数据集基本信息 from sklearn.datasets import load_iris iris load_iris() print(iris.data.shape) # (150, 4) print(iris.feature_names) # [sepal length (cm), sepal width (cm), petal length (cm), petal width (cm)] print(iris.target_names) # [setosa versicolor virginica]输出结果里(150, 4)告诉你这是 150 行、4 列的数据。特征名分别是花萼长度、花萼宽度、花瓣长度、花瓣宽度单位是厘米。target_names 是三种鸢尾花的品种名。这里有一个容易被忽略的细节iris.data是二维数组iris.target是一维数组它们按行一一对应——第 i 行特征就对应第 i 个标签。数据集的数值范围差异不算大都在 0.1 到 7.9 之间所以做线性回归时通常不需要做标准化。这一点和做 SVM 或 KNN 不一样那些算法对特征尺度敏感而线性回归的系数会天然适应特征的尺度。2.2 技术栈sklearn pandas matplotlib这个项目选型非常克制核心就三个库各有各的活。scikit-learn负责加载数据、划分训练集测试集、提供线性回归模型LinearRegression、计算评估指标。项目最主要的逻辑都在这个库里。pandas把数据转换成 DataFrame 去做探索性分析。虽然直接用 numpy 数组也能跑但项目里一般会转成 DataFrame这样列名清晰、画图时也好指定 x 轴和 y 轴。matplotlib画散点图和回归线。机器学习项目里可视化是刚需没有图你很难直观判断回归效果到底怎么样。看一个典型的加载过程import pandas as pd from sklearn.datasets import load_iris iris load_iris() df pd.DataFrame(iris.data, columnsiris.feature_names) df[species] iris.target_names[iris.target] print(df.head())转成 DataFrame 之后df.head()会输出前 5 行数据列名就是前面打印的四个特征名外加品种列。如果你用df.describe()还能看到每个特征的均值、标准差、最小值、最大值。pandas 在这条链路里的角色是数据探索和可视化准备真正建模时喂给模型的仍然是 numpy 数组。2.3 线性回归和逻辑回归的分水岭很多初学者在这个项目上会问一个问题明明目标是预测花的品种为什么要用线性回归这个问题的本质是混淆了两个名字里都有“回归”的算法。线性回归LinearRegression做的是拟合连续数值比如根据花瓣长度预测花萼宽度预测结果是一个浮点数比如 3.26 厘米。逻辑回归LogisticRegression虽然名字带回归但做的是分类输出的是一个概率值。鸢尾花原始数据集是一个三分类任务目标是预测花的品种所以如果你拿品种当 y那确实应该用逻辑回归而不是线性回归。但当这个项目把回归目标改成了“用花瓣长度预测花萼宽度”时问题就变成了连续值预测线性回归就是正确选择。这里有个判断技巧看你的 y 是连续数值还是离散类别。连续数值身高、房价、温度用线性回归离散类别是/否、品种 A/B/C用逻辑回归或其他分类算法。把这个分清了你就不会在跑这个项目时怀疑自己拿错了代码。3. 从数据到模型的完整链路数据集加载、划分与回归拟合3.1 加载鸢尾花数据集项目的第一步永远是加载数据。用 sklearn 内置的数据集有一个好处数据已经清洗好了没有缺失值、没有乱码你只需要调用load_iris()就能拿到一份可以直接用于训练的干净数据。from sklearn.datasets import load_iris # 加载鸢尾花数据集 iris load_iris() # 提取特征和标签 X iris.data # 150x4 的特征矩阵 y iris.target # 150 个标签值 # 打印数据结构信息 print(特征矩阵形状:, X.shape) print(标签数组形状:, y.shape)X是特征矩阵形状为(150, 4)每一行是一朵鸢尾花的四个测量值y是目标变量也是一维数组。在回归场景里这个y可以是品种编码吗不建议。因为品种是离散的类别用连续回归去拟合一个 0/1/2 的编码模型会认为 1 和 2 之间的距离是有意义的实际情况并非如此。这个项目更合理的做法是从四个特征里选一个当目标其余当特征或者直接只取两个特征做一元回归方便在二维平面上画图。3.2 数据探索与特征选择数据加载完先别急着训练。用pd.DataFrame把数据转换成表格然后用一行df.describe()查看分布情况。做这一步不是为了走形式而是为了选对特征做回归。import pandas as pd from sklearn.datasets import load_iris iris load_iris() df pd.DataFrame(iris.data, columnsiris.feature_names) # 查看每个特征的分布情况 print(df.describe()) # 查看特征之间的相关性 print(df.corr())df.describe()给出每个特征的均值、标准差、最小最大值df.corr()给出特征之间的相关系数矩阵。鸢尾花数据集里花瓣长度和花瓣宽度的相关性极高相关系数通常在 0.94 左右花萼长度和花瓣长度的相关性也接近 0.87。相关性强意味着回归效果好散点图上是明显的线性趋势。所以常见的做法是选花瓣长度作为 X花瓣宽度作为 y因为这两个特征线性关系最明显拟合出来的回归线看起来最“像样”。如果你的目标是用多个特征做多元回归那可以留下花萼长度、花萼宽度、花瓣宽度来共同预测花瓣长度。特征选择决定了模型效果的天花板这一步值得花几分钟看一眼相关性矩阵再做决定。3.3 训练集测试集划分与模型训练数据准备好了接下来要划分训练集和测试集。这个操作是机器学习流程里绝对不能省的环节你用训练集去拟合模型用测试集去评估模型在未见数据上的表现。如果把全部数据都拿去训练再拿去评估得到的指标会虚高因为模型已经“背下”了那 150 条数据。from sklearn.model_selection import train_test_split # X 是花瓣长度y 是花萼宽度reshape(-1, 1) 把一维数组变成列向量 X iris.data[:, 2].reshape(-1, 1) # 取花瓣长度特征 y iris.data[:, 1] # 取花萼宽度作为回归目标 # 划分训练集和测试集80% 训练20% 测试 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) print(训练集样本数:, X_train.shape[0]) print(测试集样本数:, X_test.shape[0])reshape(-1, 1)是把一维数组转成二维列向量的标准写法。sklearn 的LinearRegression要求特征是二维的如果你直接传一维数组会报错。test_size0.2表示取 20% 的数据作为测试集random_state42是随机种子固定之后每次跑代码划分结果都一样。这个细节很重要不固定随机种子你每次跑代码都会得到不同的划分模型的评估结果也会跟着波动你就没法稳定复现实验了。然后就是训练模型核心代码只有三行from sklearn.linear_model import LinearRegression # 创建线性回归模型 model LinearRegression() # 用训练数据拟合模型 model.fit(X_train, y_train) # 查看模型系数和截距 print(系数斜率:, model.coef_) print(截距:, model.intercept_)fit方法会计算出一条直线y w*x b其中w就是斜率b是截距。打印出来的系数如果是负数说明特征与目标负相关如果是正数就是正相关。以花瓣长度预测花萼宽度为例斜率大约在 0.4 左右意味着花瓣每增长 1 厘米花萼宽度大约增加 0.4 厘米。3.4 模型评估MSE、R² 与可视化模型训练完关键一步是评估效果好不好。这里用三个手段均方误差MSE、R² 分数、散点图加回归线可视化。from sklearn.metrics import mean_squared_error, r2_score # 在测试集上做预测 y_pred model.predict(X_test) # 计算评估指标 mse mean_squared_error(y_test, y_pred) r2 r2_score(y_test, y_pred) print(均方误差 (MSE):, mse) print(R² 分数:, r2)MSE 是预测值与真实值差距的平方的平均值越小越好。R² 的取值范围通常在 0 到 1 之间越接近 1 说明模型解释了越多的数据变化。用花瓣长度预测花萼宽度时R² 大约在 0.35 到 0.45 之间——听起来不高但这在真实数据里其实是正常的。如果换成用花瓣长度预测花瓣宽度R² 能上到 0.93效果会明显好得多。很多初学者看到 R² 0.4 就觉得项目失败了其实不是这取决于你预测的目标本身和特征的相关性有多强。可视化这一步用 matplotlib 就能搞定。import matplotlib.pyplot as plt # 绘制测试集样本点 plt.scatter(X_test, y_test, colorblue, label真实数据) # 绘制回归线按特征值从小到大排序保证线条连续 import numpy as np X_line np.linspace(X_test.min(), X_test.max(), 100).reshape(-1, 1) y_line model.predict(X_line) plt.plot(X_line, y_line, colorred, label回归线) plt.xlabel(花瓣长度 (cm)) plt.ylabel(花萼宽度 (cm)) plt.title(鸢尾花花瓣长度 vs 花萼宽度 线性回归) plt.legend() plt.show()散点图上的每个蓝点是一朵真实的鸢尾花红线是学到的回归直线。这条线不一定穿过所有点但它的位置是让所有点到这条线的垂直距离之和最小的解——这就是“最小二乘法”的直观含义。看到这张图你对回归拟合的理解会比背十遍公式都深刻。4. 核心模块拆解训练脚本中每一段代码的作用4.1 完整代码结构与逐段讲解把前几章的代码拼起来就是一个完整的 60 行不到的训练脚本。放进单个文件iris_regression.py就能直接运行。脚本结构分为六段导入库、加载数据、特征选择、划分数据集、训练模型、评估与可视化。# iris_regression.py # 依赖numpy, pandas, matplotlib, scikit-learn import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, r2_score # 1. 加载数据 iris load_iris() df pd.DataFrame(iris.data, columnsiris.feature_names) # 2. 特征选择用花瓣长度预测花瓣宽度相关性高达 0.96 X df[[petal length (cm)]].values # 返回 (150, 1) 的二维数组 y df[petal width (cm)].values # 返回 (150,) 的一维数组 # 3. 划分训练集和测试集 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) # 4. 创建并训练线性回归模型 model LinearRegression() model.fit(X_train, y_train) # 5. 在测试集上预测 y_pred model.predict(X_test) # 6. 评估与输出 mse mean_squared_error(y_test, y_pred) r2 r2_score(y_test, y_pred) print(f系数斜率: {model.coef_[0]:.4f}) print(f截距: {model.intercept_:.4f}) print(f均方误差 (MSE): {mse:.6f}) print(fR² 分数: {r2:.4f}) # 7. 可视化 plt.scatter(X_test, y_test, colorblue, alpha0.7, label真实数据) x_line np.linspace(X_test.min(), X_test.max(), 100).reshape(-1, 1) y_line model.predict(x_line) plt.plot(x_line, y_line, colorred, linewidth2, label回归线) plt.xlabel(花瓣长度 (cm)) plt.ylabel(花瓣宽度 (cm)) plt.title(鸢尾花数据集线性回归结果) plt.legend() plt.grid(alpha0.3) plt.show()这个脚本里有两处容易翻车的地方。第一处是df[[petal length (cm)]].values用了双层方括号取出的是 DataFrame转成数组后是(150, 1)的二维结构如果改成df[petal length (cm)].values取出的是 Series转成数组后是(150,)的一维结构。LinearRegression在fit时接受一维的 y但不接受一维的 X所以 X 必须保持二维。第二处是可视化部分的np.linspace它的作用是在最小值和最大值之间均匀生成 100 个点这样回归线画出来是一条平滑直线而不是连成一团的锯齿线。4.2 参数说明与可调点脚本里有几个参数值得专门列出来讲清楚它们各自影响着模型的行为和最终效果。model LinearRegression(fit_interceptTrue) # 默认就是 Truefit_intercept控制模型是否计算截距 b。如果设为 False模型会强制直线过原点0, 0也就是说当花瓣长度为 0 时花萼宽度也为 0。实际情况下这个假设通常不成立所以这个参数一般保持默认。X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 )test_size控制测试集比例常见取值有 0.2、0.3数据量小的时候也有用 0.1 的。random_state是随机种子换成任何整数都行但同一个项目里要固定住否则每次跑的结果都不一样没法对比实验。model.coef_ # 回归系数 model.intercept_ # 截距这两个属性是训练完之后的模型参数。在fit之前访问它们会得到None或直接报错因为它们是在训练过程中被计算出来的。训练完成后它们就是这条回归线的分子分母y coef_ * x intercept_。你可以拿这个公式手动算一个预测结果跟model.predict的输出对比应该完全一致这也是理解线性回归内部机制的好方法。4.3 常见报错与处理跑代码时最容易碰到的报错我来盘一盘基本都是初学者必踩的ValueError: Expected 2D array, got 1D array instead这是最经典的报错几乎每个跑 sklearn 的新手都会遇到一次。原因是你传给fit的 X 是一维数组而 sklearn 要求 X 是二维矩阵。解决方法是把X df[petal length (cm)].values改成X df[[petal length (cm)]].values或者加一句X X.reshape(-1, 1)。AttributeError: LinearRegression object has no attribute coef_这出现在你还没训练模型就直接访问coef_的时候。coef_是在fit之后才生成的属性不是初始化时就有的。训练之前检查一下是否调用了model.fit(X_train, y_train)。matplotlib 画的图没有显示出来脚本最后单独跑的时候plt.show()会阻塞进程等待关图窗口。如果在 Jupyter Notebook 里你需要先执行%matplotlib inline否则图形会弹到独立窗口甚至不显示。如果在无图形界面的服务器上跑把plt.show()改成plt.savefig(result.png)图形会保存到本地文件。5. 避坑指南线性回归做鸢尾花的四个典型坑5.1 坑一把三种鸢尾花的品种标签当作回归目标现象用iris.target0、1、2 三个值作为 y跑线性回归打印出来的 R² 还挺高但把预测结果拿去和实际品种对比时完全对不上。原因iris.target是离散类别编码0 代表 Setosa1 代表 Versicolour2 代表 Virginica。线性回归拟合的是连续数值它会把 1.5 这种不存在的中间值当成合理输出。分类问题用回归模型方向就错了。解决确认你的任务是“预测连续数值”还是“预测类别”。预测连续值用LinearRegression预测种类请换LogisticRegression或KNeighborsClassifier。跑这个项目时回归目标应该从四个特征里选一列而不是从target里拿。5.2 坑二特征与目标相关性太低导致 R² 几乎为 0现象拿花萼宽度去预测花萼长度训练完发现 R² 只有 0.08 左右散点图上的点糊成一片没有明显的线性趋势开始怀疑代码写错了。原因线性回归只能捕捉线性关系。花萼宽度和花萼长度在鸢尾花数据集里相关性确实很低相关系数只有 0.1 上下。两个变量之间本来就没有线性关系回归模型再有本事也白搭。解决先做数据探索用df.corr()查看特征之间的相关系数选择相关性高的特征对来做回归。鸢尾花数据集里花瓣长度和花瓣宽度相关性高达 0.96用这一对特征效果最好。如果你跑出来的模型效果奇差先看特征选对没有再怀疑代码。5.3 坑三随机种子不固定每次跑出来的模型和指标都不一样现象同一份数据、同一个脚本今天跑 R² 是 0.93明天跑变成了 0.89甚至斜率都不一样。想记录实验结果却发现前后对不上。原因train_test_split默认是随机切分。不固定随机种子每次划分的训练集和测试集都不一样模型在不同数据子集上训练自然得到不同参数评估指标也会上下波动。解决划分数据时固定random_state比如random_state42。固定之后每次运行脚本都会得到完全一样的划分、一样的训练结果、一样的评估指标实验可复现。这是机器学习工程里的基本素养不是可选项。5.4 坑四把全部数据拿去训练然后用同一批数据评估现象X_train Xy_train y直接fit全部数据再在同样的数据上predict得到的 R² 高得惊人但换到真实场景一用就露馅。原因模型已经“见过”全部数据相当于考试前把答案背下来然后试卷出的还是原题分数当然好看。这种评估方式衡量的是模型的记忆能力不是泛化能力。解决坚持划分训练集和测试集。标准做法是留出 20% 到 30% 的数据不参与训练只用来评估。如果数据量紧张可以改用交叉验证cross_val_score把数据切成几折轮流做训练集和验证集这是小数据集上更稳妥的评估方式。6. 二次开发把鸢尾花回归改成你自己的项目6.1 换数据集从鸢尾花切换到波士顿房价鸢尾花跑通之后把这个脚本改到其他数据集上只需要动两行代码。以波士顿房价数据为例特征换成房价相关的 13 个指标之一目标换成房价MEDV其他流程完全不动。from sklearn.datasets import fetch_openml # 加载波士顿房价数据集sklearn 1.2 之后不再内置改用 openml 获取 boston fetch_openml(nameboston, version1, as_frameTrue) df boston.frame # 找一个和目标相关性高的特征 X df[[LSTAT]].values # 低收入人口比例与房价负相关 y df[MEDV].values # 房价中位数fetch_openml需要联网下载数据as_frameTrue让它以 DataFrame 格式返回。换数据集之后重点关注两件事第一形状对不对——X 必须是二维的y 必须是一维的和数据规模无关第二特征和目标的相关性高不高——先用df.corr()里的矩阵挑一条相关性最强的特征避免换完数据后回归线变成一条平线。6.2 加特征组合与多项式回归鸢尾花的四个特征单独做回归效果最好的也只有花瓣长对花瓣宽这一对。如果你想把多个特征一起喂进模型做多元回归直接多加几列就行如果想捕捉非线性关系可以做多项式回归。这是同一个算法思路的两种延伸。from sklearn.preprocessing import PolynomialFeatures from sklearn.pipeline import make_pipeline # 用多项式特征扩展现有特征degree2 会生成 x^2 和 x1*x2 这类组合 poly_model make_pipeline( PolynomialFeatures(degree2, include_biasFalse), LinearRegression() ) # X_multi 是多个原始特征的组合 X_multi iris.data[:, [2, 3]] # 花瓣长度 花瓣宽度 poly_model.fit(X_train, y_train)PolynomialFeatures(degree2)会把原来的一个特征x扩展成x, x^2等多个特征让线性回归能拟合曲线。但要注意多项式特征的维度会随着 degree 和原始特征数量迅速爆炸鸢尾花数据量只有 150 条degree 超过 3 就非常容易过拟合训练集上 R² 极高、测试集上一塌糊涂。这是个经典教训——模型复杂度不是越高越好。6.3 从线性回归到逻辑回归的分类改造如果你最终想把鸢尾花项目改回分类任务——预测花的品种——那只需要把模型换掉数据分割逻辑兼容。这个改造能让你同时掌握回归和分类两条技术路线。from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, confusion_matrix # 分类任务特征用全部 4 列目标用品种标签 X iris.data y iris.target X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) clf LogisticRegression(max_iter1000) clf.fit(X_train, y_train) y_pred clf.predict(X_test) print(准确率:, accuracy_score(y_test, y_pred)) print(混淆矩阵: , confusion_matrix(y_test, y_pred))逻辑回归这里有个参数要专门说明max_iter1000。鸢尾花数据集特征不多但默认的max_iter100偶尔会触发收敛警告把迭代次数调大到 1000 就稳定了。混淆矩阵是分类任务里比准确率更重要的评估手段它告诉你三类花各自被预测对多少、错分到哪一类。改造完成后你会看到同一个数据集回归视角和分类视角的模型、评估指标、画图方式完全不同——这才是机器学习项目最值钱的体验。从鸢尾花回归到分类的这次切换让我养成一个习惯拿到任何数据集先问一句“目标变量是连续值还是离散值”再决定算法方向。这个习惯帮我少走了很多弯路。希望这份拆解对你有用下载代码后先从特征相关性和回归可视化开始跑跑通了再动手改你会看到数据在不同算法手里呈现出完全不同的面貌。本文还有配套的精品资源点击获取