Python机器学习课程设计:旅游业发展预测实验报告(Pearson+Lasso+灰色预测+SVR)
发布时间:2026/10/2 8:56:51 作者:尧图编辑部 阅读量:1,286
)
简介这份资源是一份面向高校学生与机器学习初学者的课程设计实验报告主题为运用Python预测我国旅游业发展适合作为课程作业参考、数据分析入门练手或相关课题的写作模板。压缩包内共1个docx文档约264KB内容围绕2007至2017年国内旅游总花费、入境游客、旅行社数等指标展开完整呈现了从数据可视化、Pearson相关系数分析到建模预测的全过程。报告重点讲解最小二乘估计、Lasso特征选择、改进灰色预测模型与支持向量回归SVR等方法并给出回归模型y5963.9x-8950.1及未来四年旅游花费预测值同时附有平均绝对误差、均方误差、R²等评价指标便于读者理解模型构建与验证思路。目前已有1227人学习下载适合需要一份结构完整、方法具体的机器学习课程设计范例的读者参考借鉴。1. 从一份课程设计拆开看Python 机器学习怎么把旅游业预测做成能跑的实验报告课程设计最怕的不是模型不会调而是数据拿到手不知道从哪一步开始。这份“python机器学习课程设计实验报告-我国旅游业发展预测”把 2007—2016 年国内旅游总花费、旅行社数、星级饭店总数、入境游客、国内游客、国际旅游外汇收入等 12 个指标整理成一张表用 Pearson 相关系数筛特征、Lasso 做稀疏化、灰色预测补未来 4 年自变量、SVR 回归拟合总花费最后给出 2017—2020 年的预测值。它适合正在做机器学习课程设计、需要一份完整“数据探索—特征选择—预测建模—误差评价”闭环的同学也适合想拿真实经济数据练 SVR 和灰色预测的从业者。整套流程用 Python 就能复现不需要 GPU一台普通笔记本足够。2. 数据准备与特征筛选Pearson 和 Lasso 到底谁先上2.1 原始表怎么读进 pandas 并统一量纲原始数据是 10 行 × 12 列年份从 2007 到 2016列名带中文和括号直接读会碰到编码和列名对齐问题。常见做法是先把表存成 UTF-8 的 CSV再用 pandas 指定encodingutf-8-sig避免 Windows 下 BOM 头导致第一列列名变成\ufeff指标。读进来之后把“年份”单独拎出来做索引其余 11 个特征加 1 个目标 y 全部转成 float防止字符串型数字参与运算时报TypeError。import pandas as pd import numpy as np # utf-8-sig 能吃掉 Windows Excel 另存 CSV 时留下的 BOM 头 df pd.read_csv(tourism.csv, encodingutf-8-sig) # 第一列是年份直接设为索引后面画趋势图和做灰色预测都要用 df df.set_index(年份) # 所有列强制转数值遇到无法转换的会变成 NaN方便后面查缺失 df df.apply(pd.to_numeric, errorscoerce) # 目标列单独拿出来特征矩阵去掉 y y df[国内旅游总花费] X df.drop(columns[国内旅游总花费]) print(X.shape, y.shape) # 期望输出 (10, 11) (10,) print(df.isna().sum()) # 确认没有缺失再往下走这段代码的关键在errorscoerce它把脏数据变成 NaN 而不是直接抛异常方便你一眼看出哪一列有问题。参数上encoding必须和文件实际编码一致如果读出来列名带乱码先换gbk试一次。set_index之后年份不再是特征避免把时间当自变量造成伪回归。2.2 Pearson 相关系数矩阵先看清谁和 y 线性相关Pearson 相关系数衡量的是两个变量之间的线性相关强度取值在 -1 到 1 之间绝对值越接近 1 越强。原文给出的矩阵里国内游客 x10 与 y 的相关系数达到 0.98国内居民出境人数 x8、因私出境 x9 也都在 0.96 以上而星级饭店总数 x2 与 y 是 -0.9呈负相关。这一步的价值不是直接选特征而是先判断哪些变量之间存在严重共线性——比如 x8 和 x9 相关系数 0.99几乎就是同一个信息的两份拷贝同时放进模型会让系数估计变得极不稳定。# 计算 Pearson 相关系数矩阵保留四位小数方便看 corr df.corr(methodpearson).round(4) # 只看和目标 y 的相关性按绝对值从大到小排 corr_y corr[国内旅游总花费].drop(国内旅游总花费) corr_y corr_y.reindex(corr_y.abs().sort_values(ascendingFalse).index) print(corr_y) # 找出特征之间相关系数绝对值大于 0.95 的组合这些是共线性嫌疑 high_corr [] cols X.columns for i in range(len(cols)): for j in range(i 1, len(cols)): r corr.loc[cols[i], cols[j]] if abs(r) 0.95: high_corr.append((cols[i], cols[j], round(r, 4))) print(high_corr)逻辑上先算全矩阵再筛目标列是为了同时拿到“与 y 的相关性”和“特征之间的相关性”两份信息。参数methodpearson是默认值要求变量近似正态且线性关系如果数据明显偏态可以换spearman做秩相关。输出里 x8 和 x9 的 0.99 就是典型共线性信号后面 Lasso 会把其中一个系数压到接近 0。2.3 Lasso 特征选择把 11 个特征压到 8 个Lasso 在普通最小二乘的损失函数后面加了一项 L1 正则形式是||y - Xw||² α·||w||₁。L1 的几何性质会让一部分系数精确变成 0从而达到特征选择的效果。原文用 Lasso 从 11 个特征里保留了旅行社数 x1、星级饭店总数 x2、入境游客 x3、港澳同胞入境游客 x5、入境过夜游客 x7、国内居民出境人数 x8、国内游客 x10、国际旅游外汇收入 x11 共 8 个。这里要注意Lasso 对特征量纲敏感必须先标准化否则数值大的列会被过度惩罚。from sklearn.linear_model import Lasso from sklearn.preprocessing import StandardScaler # 标准化Lasso 的惩罚项对所有系数一视同仁量纲不统一会误伤 scaler StandardScaler() X_scaled scaler.fit_transform(X) # alpha 控制惩罚强度越大保留的特征越少 lasso Lasso(alpha0.1, max_iter10000, random_state42) lasso.fit(X_scaled, y) # 把系数和特征名对应起来非零的才是被选中的 coef pd.Series(lasso.coef_, indexX.columns) selected coef[coef.abs() 1e-6].sort_values(keyabs, ascendingFalse) print(selected) print(保留特征数:, len(selected))alpha是最关键的参数0.1 是原文量级下的常用起点如果保留特征太多就调大太少就调小。max_iter设大一点防止坐标下降不收敛。标准化用fit_transform只在训练集上拟合如果后面要划分训练测试集必须改成先fit再分别transform否则会数据泄漏。这一步跑完你就得到了和原文一致的 8 个关键特征后续灰色预测和 SVR 都只在这 8 列上做。3. 灰色预测补未来自变量GM(1,1) 的建模与后验差检验3.1 GM(1,1) 的累加、微分方程与还原灰色预测 GM(1,1) 的核心思路是把原始序列做一次累加让杂乱的数列变得有规律再对累加序列建立一阶线性微分方程dX⁽¹⁾/dt aX⁽¹⁾ b解出参数 a、b 后得到预测公式最后累减还原成原始量纲。它最大的优势是只需要 4 个以上数据点就能建模非常适合这份只有 10 年、还要外推 4 年的场景。原文对 8 个特征分别建 GM(1,1)得到 2017—2020 年每个自变量的预测值再喂给 SVR。import numpy as np def gm11_predict(x0, years_ahead4): x0: 原始一维序列; 返回拟合值、预测值、后验差比值C和小误差概率P x0 np.asarray(x0, dtypefloat) n len(x0) # 一次累加生成 x1 np.cumsum(x0) # 紧邻均值生成用于构造 B 矩阵 z1 0.5 * (x1[1:] x1[:-1]) B np.column_stack([-z1, np.ones(n - 1)]) Y x0[1:].reshape(-1, 1) # 最小二乘解出发展系数 a 和灰作用量 b [[a], [b]] np.linalg.inv(B.T B) B.T Y # 时间响应函数 def x1_hat(k): return (x0[0] - b / a) * np.exp(-a * k) b / a # 累减还原 x0_hat np.array([x0[0]] [x1_hat(k) - x1_hat(k - 1) for k in range(1, n)]) x0_pred np.array([x1_hat(k) - x1_hat(k - 1) for k in range(n, n years_ahead)]) # 后验差检验 resid x0 - x0_hat s1 np.std(x0, ddof1) s2 np.std(resid, ddof1) C s2 / s1 P np.mean(np.abs(resid - resid.mean()) 0.6745 * s1) return x0_hat, x0_pred, C, P # 对每个入选特征单独预测 for col in selected.index: fit, pred, C, P gm11_predict(df[col].values, years_ahead4) print(f{col}: C{C:.3f}, P{P:.3f}, 预测{np.round(pred, 2)})代码里B矩阵由紧邻均值z1和全 1 列组成np.linalg.inv(B.T B) B.T Y就是最小二乘估计 a、b 的标准写法。x1_hat是累加序列的预测x0_pred通过相邻两项相减还原成原始量纲。后验差比值 C 越小、小误差概率 P 越大模型精度越高原文表 5.5.1 给的判别标准是 C0.35 且 P0.95 为好。3.2 后验差检验C 和 P 怎么读后验差检验是灰色预测自带的精度评估手段不需要额外留测试集。C 是残差标准差与原始序列标准差之比反映残差相对于原始数据波动的大小P 是残差落在 0.6745 倍原始标准差范围内的比例。原文对 8 个特征的预测精度标注为“好”或“较好”说明大部分特征的 C 落在 0.35 到 0.5 之间。如果某个特征的 C 超过 0.65说明残差波动已经接近甚至超过原始数据本身的波动这个特征的灰色预测结果就不能直接用常见做法是换指数平滑或直接对原始序列做线性外推。精度等级后验差比值 C小误差概率 P好 0.35 0.95合格 0.50 0.80勉强合格 0.65 0.70不合格≥ 0.65≤ 0.70提示灰色预测对数据光滑度敏感如果原始序列上下跳动剧烈先做一次对数变换或开方变换再建模往往能把 C 压下来。3.3 把灰色预测结果拼成 SVR 的输入矩阵8 个特征各自预测出 4 个未来值后要按年份对齐拼成一张 4 行 8 列的矩阵列顺序必须和训练 SVR 时完全一致否则预测结果会张冠李戴。原文表 5.7.1 给出的 2017—2020 年特征预测值就是这一步的产物。拼接时建议用 DataFrame 并显式指定列名避免 numpy 数组顺序错位这种低级但致命的错误。# 假设 selected 是 Lasso 选出的 8 个特征名 future_years [2017, 2018, 2019, 2020] future_X pd.DataFrame(indexfuture_years, columnsselected.index, dtypefloat) for col in selected.index: _, pred, _, _ gm11_predict(df[col].values, years_ahead4) future_X[col] pred print(future_X.round(2)) # 保存下来SVR 预测阶段直接读保证列顺序一致 future_X.to_csv(future_features.csv, encodingutf-8-sig)这段逻辑的重点是columnsselected.index它把 Lasso 输出的特征顺序固定下来。如果后面 SVR 训练时用的列顺序和这里不一致predict出来的数会完全离谱而且不会报错属于典型的“静默翻车”。存成 CSV 再读比在内存里传对象更稳也方便你分两次跑实验。4. SVR 回归建模与预测核函数、参数和误差指标4.1 SVR 为什么适合这份小样本数据支持向量回归和普通回归的区别在于它允许预测值和真实值之间有 ε 的偏差只有超出这个带宽的样本才计入损失同时用惩罚系数 C 控制对超出带宽样本的容忍度。这份数据只有 10 个样本、8 个特征属于典型的小样本高维场景SVR 通过核函数把非线性关系映射到高维空间不需要担心多重共线性也不会像普通最小二乘那样在共线性下系数爆炸。原文用 SVR 拟合 2007—2016 年数据R² 达到 0.9989说明核函数选得合适。4.2 训练 SVR 并回测 2007—2016训练时要把 8 个特征标准化目标 y 是否标准化取决于核函数类型。用 RBF 核时gamma对特征尺度敏感标准化几乎必做。C和gamma用网格搜索交叉验证来定但样本只有 10 个留一法交叉验证比 5 折更稳。原文没有给出具体超参数常见做法是C在 1 到 1000 之间对数取值gamma在 0.001 到 1 之间取。from sklearn.svm import SVR from sklearn.model_selection import LeaveOneOut, GridSearchCV from sklearn.pipeline import Pipeline from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score # 用管道把标准化和 SVR 串起来避免交叉验证时数据泄漏 pipe Pipeline([ (scaler, StandardScaler()), (svr, SVR(kernelrbf)) ]) # 小样本用留一法比 K 折更充分利用数据 param_grid { svr__C: [1, 10, 100, 1000], svr__gamma: [0.001, 0.01, 0.1, 1], svr__epsilon: [0.01, 0.1, 1] } gs GridSearchCV(pipe, param_grid, cvLeaveOneOut(), scoringneg_mean_squared_error) gs.fit(X_scaled, y) print(最优参数:, gs.best_params_) y_pred_train gs.predict(X_scaled) mae mean_absolute_error(y, y_pred_train) mse mean_squared_error(y, y_pred_train) r2 r2_score(y, y_pred_train) print(fMAE{mae:.4f}, MSE{mse:.4f}, R2{r2:.6f})Pipeline保证标准化只在每折训练集上拟合这是小样本建模最容易忽略的细节。LeaveOneOut在 10 个样本下等价于做 10 次训练每次留一个样本测试评估结果比随机划分稳定得多。原文报告的 MAE 约 196.79、R² 约 0.9989如果你跑出来差很多先检查特征列顺序和标准化是否一致。4.3 代入灰色预测特征输出 2017—2020 预测值训练好的模型直接对future_X做预测就得到未来 4 年国内旅游总花费。原文给出的结果是 61789.91、70712.60、75583.48、80750.49 亿元呈逐年上升趋势。这里要注意future_X的列顺序必须和训练时X完全一致最稳妥的做法是训练前把列名存下来预测前用future_X future_X[train_columns]重排一次。# 读回灰色预测得到的未来特征 future_X pd.read_csv(future_features.csv, index_col0, encodingutf-8-sig) # 强制按训练时的列顺序重排防止静默错位 train_columns X.columns.tolist() future_X future_X[train_columns] # 用最优模型预测未来 4 年 future_pred gs.predict(future_X) for year, val in zip(future_X.index, future_pred): print(f{year}年国内旅游总花费预测: {val:.2f} 亿元)future_X[train_columns]这一行是整个流程里性价比最高的保险它把列顺序问题一次性堵死。预测值量级在 6 万到 8 万亿之间和原文一致。如果输出是负数或个位数基本可以断定是列顺序错了或者标准化没对上。5. 避坑与排查这份实验报告里最容易翻车的五个点5.1 现象Lasso 系数全为 0 或保留特征数和原文对不上原因alpha设得过大L1 惩罚把所有系数都压没了或者标准化没做量纲大的列被过度惩罚。解决先把alpha从 0.01 开始按对数递增试同时确认StandardScaler是在fit之后才transform。如果保留特征数始终偏少检查目标 y 是否也参与了标准化——y 不需要标准化只标准化 X。5.2 现象灰色预测报LinAlgError: Singular matrix原因B.T B不可逆通常是因为原始序列太短少于 4 个点或者序列几乎恒定导致紧邻均值矩阵秩亏。解决先检查序列长度少于 4 个点直接放弃 GM(1,1)如果序列波动极小加一个极小的扰动项或者改用均值 GM(1,1)。这份数据每个特征有 10 个点正常不会触发但如果某列存在大量重复值就要留意。5.3 现象SVR 回测 R² 很高但未来预测值明显不合理原因小样本下 RBF 核容易过拟合训练集 R² 接近 1 不代表外推可靠另一个常见原因是未来特征矩阵列顺序和训练时不一致。解决先用留一法交叉验证看泛化误差如果留一法 R² 远低于训练 R²说明过拟合调小C或调大epsilon同时用future_X[train_columns]强制对齐列顺序。5.4 现象Pearson 相关系数矩阵里出现 0.99 以上的特征对原因两个特征携带几乎相同的信息比如国内居民出境人数和因私出境人数。解决不要直接把两个都塞进模型先做 Lasso 让其中一个系数归零或者手动删掉一个。如果两个都保留SVR 虽然不会像线性回归那样系数爆炸但会浪费模型容量外推时更容易受噪声影响。5.5 现象预测结果和原文数值差一个数量级原因单位不统一。原文国内旅游总花费单位是亿元国际旅游外汇收入单位是百万美元如果读数据时把单位混在一起SVR 学到的关系就是错的。解决建模前把所有列的单位在注释里写清楚必要时统一换算。这份数据里 y 是亿元x11 是百万美元两者量纲差两个数量级标准化能缓解但不能消除语义上的单位混淆。6. 把这份实验报告用出更高价值交叉验证与残差诊断的实操习惯课程设计交完不是终点这套流程真正的价值在于你可以把它当成小样本回归的模板反复用。我一般会在 SVR 训练完之后补两步一是画残差图看残差是否随机分布二是把灰色预测的 C、P 值和 SVR 的 MAE、R² 放在一张表里判断误差主要来自特征外推还是回归拟合。残差图如果出现明显的喇叭口或弯曲趋势说明模型还有非线性结构没抓住可以试着把 RBF 核换成多项式核或者对 y 做对数变换再回归。import matplotlib.pyplot as plt # 残差诊断训练集残差 vs 预测值 resid y.values - y_pred_train plt.figure(figsize(8, 4)) plt.scatter(y_pred_train, resid, csteelblue) plt.axhline(0, colorred, linestyle--) plt.xlabel(预测值) plt.ylabel(残差) plt.title(SVR 训练残差图) plt.tight_layout() plt.savefig(resid.png, dpi150) # 灰色预测精度汇总方便和 SVR 指标对照 summary [] for col in selected.index: _, _, C, P gm11_predict(df[col].values, years_ahead4) summary.append({特征: col, 后验差C: round(C, 3), 小误差概率P: round(P, 3)}) print(pd.DataFrame(summary))残差图看的是“模型有没有系统性偏差”如果残差围绕 0 上下随机跳动说明拟合基本到位如果残差随预测值增大而增大说明存在异方差可以考虑对 y 取对数。灰色预测汇总表则帮你定位如果某个特征的 C 特别大那 SVR 预测的不确定性主要来自这个特征的外推而不是回归本身。这两步做完你对整条链路的误差来源就有了量化认识而不是只看一个 R²。从那以后我每次做小样本预测都会先把特征外推和回归拟合的误差分开评估再决定是换预测方法还是调回归参数。这套习惯让我少走了很多“R² 很高但预测不能用”的弯路。希望帮到你。本文还有配套的精品资源点击获取