
大家好这一篇我带你用最基础也最好懂的一元线性回归做一个能真正跑起来的毕业设计身高体重回归预测系统。这有一些练手的项目完整项目https://pan.quark.cn/s/1e54aa2ae950先说清楚这个项目是干嘛的。道理大家都懂个子高的人一般偏重个子矮的人一般偏轻身高和体重之间有明显的正相关。我们就把身高当成输入体重当成输出收集一堆身高体重的数据让模型自己学出这两者之间的关系。学完之后你输入一个身高比如 175 厘米它立刻告诉你体重约为 64.3 公斤。听起来很玄乎其实就是一根直线在干活。为什么要选线性回归来做因为它是所有机器学习算法里最基础、最好讲清楚的一个从最小二乘法到误差指标答辩老师随便问你都能接住。再加上一个 Tkinter 界面把散点图、回归直线、预测结果都放进窗口里演示效果直接拉满。所有图统一用 SimHei 黑体显示中文Windows 系统一般自带不会出乱码。一、项目结构16_身高体重回归/ ├── data/ │ └── height_weight.csv # 120 条身高体重数据 ├── train_model.py # 训练回归模型 评估 画图 ├── app.py # Tkinter 图形界面 ├── 模型/ │ ├── model.pkl # 训练好的回归模型 │ └── x_mean.npy # 身高均值用于校验输入范围 ├── 图/ │ ├── 01_回归拟合.png # 散点图 回归直线 │ └── 02_残差图.png # 预测误差分布 └── requirements.txt运行顺序和上一个项目一样先train_model.py训练再app.py开界面下面逐块拆开讲。二、数据是怎么造的身高体重这种数据网上没有现成特别规整的小数据集自己生成反而更干净。我按一个真实存在的规律来造体重约等于 0.72 倍的身高减去 62再叠加一点随机噪声模拟人体本身的差异。生成的代码很简单import numpy as np np.random.seed(42) n 120 heights np.round(np.random.uniform(148, 192, n), 1) weights np.round(0.72 * heights - 62 np.random.normal(0, 3.2, n), 1)np.random.seed(42)是固定随机种子这样不管谁跑这段代码生成的 120 条数据都一样答辩时前后能对上号。身高从 148 到 192 厘米随机取体重按上面的公式算再加一个标准差 3.2 的正态噪声。生成完存成 CSV长这样节选身高(cm)体重(kg)164.554.2189.873.6180.266.5174.358.8154.950.5150.646.4真实数据的基本统计如下统计项数值样本总数120身高均值169.0 cm身高中位数169.2 cm体重均值59.9 kg体重中位数59.0 kg注意 CSV 要用utf-8-sig编码保存带 BOM这样 Excel 打开不乱码pandas 读取也稳定。三、线性回归到底在做什么一句话在数据点之间找一条误差最小的直线。数学上就是 y ax bx 是身高y 是体重a 是斜率b 是截距。那怎么算 a 和 b用最小二乘法把所有样本点离这条直线的竖直距离的平方加起来想办法让这个总距离最小。道理很直观——直线离所有点越近就说明它越能代表数据的整体趋势。sklearn 里两行代码就搞定了model LinearRegression() model.fit(X_train, y_train)fit就相当于在内部解最小二乘的方程求出最合适的 a 和 b。你甚至不用懂它的推导过程但答辩时能说出最小二乘法让预测误差的平方和最小这句话就已经是及格线往上了。四、训练代码逐段讲解完整的脚本是train_model.py拆成几块看。第一部分字体和工具准备import matplotlib matplotlib.use(Agg) import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split from sklearn.metrics import (r2_score, mean_absolute_error, mean_squared_error) plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] Falsematplotlib.use(Agg)表示用无界面模式画图适合纯训练脚本画完直接存文件不弹窗。两行rcParams是中文显示的关键SimHei黑体 让负号正常显示否则图里全是小方块。第二部分加载数据并划分训练测试集df pd.read_csv(DATA_FILE, encodingutf-8-sig) df df.dropna().reset_index(dropTrue) X df[[身高(cm)]].values y df[体重(kg)].values X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42)这里有个小坑值得注意X必须是二维的所以写成df[[身高(cm)]]而不是df[身高(cm)]后者是一维 Seriessklearn 会直接报错。test_size0.2表示 120 条里拿 96 条训练、24 条测试random_state42固定切分保证每次运行切出来的测试集一样。第三部分训练和评估model LinearRegression() model.fit(X_train, y_train)训练完模型就把 a 和 b 算出来了直接打印回归方程print(回归方程体重(kg) {:.3f} × 身高(cm) {:.3f}.format( model.coef_[0], model.intercept_))评估用了三个指标一个都不能省p model.predict(X_test) r2 r2_score(y_test, p) mae mean_absolute_error(y_test, p) rmse np.sqrt(mean_squared_error(y_test, p))R²决定系数模型解释了多少比例的数据变化越接近 1 说明拟合越好0.89 已经是很强的线性关系了。MAE平均绝对误差预测值和真实值平均差多少公斤单位直观好理解。RMSE均方根误差和 MAE 类似但大误差会被放大更能反映预测的抖动程度。第四部分画图line_x np.linspace(X.min() - 2, X.max() 2, 100).reshape(-1, 1) line_y model.predict(line_x) plt.scatter(X, y, color#4C8BF5, alpha0.7, s40, label实际数据点) plt.plot(line_x, line_y, color#E53935, linewidth2.2, label回归直线)一张图把数据点蓝点和回归直线红线叠在一起模型好不好一眼就看出来。再画一张残差图——把每个样本的真实值减预测值画出来如果残差均匀散布在零线上下说明模型没有明显偏差。第五部分保存模型joblib.dump(model, os.path.join(MODEL_DIR, model.pkl))训练完把模型存成 pkl 文件界面启动时直接加载不需要重新训练打开就是秒开。五、真实运行结果数据展示这是我跑出来的真实输出样本总数 120 身高均值169.0 cm中位数169.2 cm 体重均值59.9 kg中位数59.0 kg 回归方程体重(kg) 0.704 × 身高(cm) -58.869 训练集 R20.895 MAE2.33 RMSE3.12 测试集 R20.893 MAE2.74 RMSE3.21测试集 24 个样本的前 8 条对照身高(cm)真实体重(kg)预测体重(kg)误差(kg)159.450.353.3-3.0170.957.161.4-4.3154.950.550.20.3188.669.673.9-4.3156.854.651.53.1160.459.554.05.5183.971.770.61.1148.944.146.0-1.9几个看点R² 高达 0.89说明身高确实能解释约 89% 的体重变化两者的线性关系非常强。回归方程 y 0.704·身高 - 58.869意思就是身高每多 1 厘米体重平均多约 0.7 公斤这个系数解释起来非常自然。RMSE 约 3.2 公斤也就是说预测体重和真实体重平均误差在 3 公斤左右对身高体重这种本身就因人而异的量来说已经相当不错。六、图形界面是怎么做的Tkinter 界面把三样东西放到一个窗口身高输入框、预测结果、回归拟合图。布局是这样顶部输入身高的文本框和一个预测体重按钮中间大字显示预测结果旁边实时显示回归方程下方一个标签页放着回归拟合图另一个标签页是数据预览表。预测的核心逻辑很简单text self.entry.get().strip() height float(text) # 字符串转数字 weight float(self.model.predict([[height]])[0])注意predict的输入也得是二维的所以写成[[height]]。预测完不但改结果文字还会在图上点一个红点标出你输入的这条数据落在回归线的哪个位置交互感很强。另外我做了两个输入的边界检查防止用户乱填if not (100 height 250): messagebox.showerror(范围错误, 身高请填 100~250 之间的数值)数字格式不对会弹请输入数字超出合理范围会弹范围错误这就是写代码时考虑的边界情况答辩时可以主动提一句很加分。图是用FigureCanvasTkAgg嵌进窗口的配合 SimHei 字体标题、坐标轴、图例全是正常中文。七、怎么运行环境需要这几个库numpy pandas scikit-learn matplotlib joblib两步走先训练在项目目录下运行python train_model.py会打印回归方程和评估指标同时生成模型里的 pkl 文件和图里的两张图片。再开界面运行python app.py输入身高点预测就行。如果打开app.py提示找不到模型就是第一步还没执行先回去把训练脚本跑一遍。八、答辩常问问题问为什么用线性回归而不用别的答身高和体重本身就是近似线性关系散点图一看就是直线趋势线性回归最简单、可解释性最强。如果数据明显非线性再考虑多项式回归。问R² 是什么意思答决定系数衡量模型解释了多少数据波动。取值 0 到 1越接近 1 拟合越好。这里 0.89说明身高解释了约 89% 的体重变化。问MAE 和 RMSE 有什么区别答都是衡量预测误差的MAE 是误差的平均绝对值RMSE 先平方再平均再开根号会把大的误差放大所以 RMSE 一般不小于 MAE。问怎么判断模型有没有过拟合答看训练集和测试集的指标是否接近。这里训练 R²0.895测试 R²0.893几乎一样说明没有过拟合。问还能怎么改进答把性别也加进去做多元回归或者引入 BMI 相关的身高平方项能进一步提升准确率。