
简介本资源为基于机器学习的机票价格预测研究论文文档面向计算机、数据科学及民航相关专业的在校师生、毕业生与算法初学者可用于毕业设计、课程论文的写作参考与思路借鉴。论文围绕机票价格波动这一回归问题系统梳理了数据获取、特征工程、模型训练与融合的完整流程涉及随机森林、XGBoost等集成算法并探讨了爬虫反制、数据聚合、价格分布变换等实操细节。压缩包内仅含1个docx文件约78KB为规范完整的论文正文逻辑紧密、用语专业适合研读借鉴。目前已有113人学习下载。读者可从中获取机票价格预测的选题框架、特征选取思路、模型评价方法及改进方向为同类项目开发或论文撰写提供专业知识与参考启示但需注意不可完全照抄。1. 机票价格预测这件事为什么值得用机器学习认真做一遍同一趟航班提前三周看是 860提前一周变成 1240起飞前两天又掉回 990。做过机票相关系统的人都知道这种价格波动不是随机噪声背后是舱位库存、竞对调价、节假日需求、上座率预测几股力量在博弈。传统做法靠运营拍规则比如「低于 7 天涨价」「周二凌晨放低价」规则一多就互相打架维护成本高得离谱。机器学习切入的价值在于把历史价格序列和对应的日期、航线、提前天数、是否节假日这些特征喂给模型让它自己学出「什么条件下该是什么价」。这篇笔记面向正在做机器学习课程设计、毕设选题或者想拿真实业务数据练一遍完整回归流程的读者。我会按「数据怎么造 → 特征怎么搭 → 随机森林和 XGBoost 怎么跑 → 参数怎么调 → 坑在哪」的顺序把机票价格预测这个题目从选题到能跑出结果讲透代码可以直接抄。2. 先搞清楚预测目标回归还是分类特征从哪来2.1 机票价格预测到底预测什么很多人一上来就纠结用哪个模型其实第一步该定的是预测目标。机票价格预测在工业界有两种常见形态一种是回归直接预测某个航班在未来某天的具体票价输出是连续值比如 1080.5 元另一种是分类预测「未来 7 天价格会涨还是会跌」输出是涨/跌/持平三档。毕设和课设里回归更容易做出可量化的误差指标MAE、RMSE也更能体现特征工程的价值所以我一般建议先做回归把价格预测这条链路跑通再考虑加一个分类头做涨跌判断。回归目标确定后标签 y 就是历史成交价或抓取到的展示价。这里有个容易翻车的点如果你用的是公开数据集价格字段可能是区间价或者含税/不含税混在一起必须先统一口径。我见过有人把含税价和不含税价混着训练模型 MAE 看着还行一上线全错这就是典型的黑匣子没打开。2.2 特征工程把「提前天数」和「星期几」拆开喂给模型机票价格的核心特征可以分成四组特征组具体字段处理方式时间特征提前天数、星期几、月份、是否节假日数值化 独热编码航线特征出发地、目的地、飞行时长、经停次数类别编码或目标编码航班特征航空公司、机型、起飞时段类别编码市场特征同航线竞对最低价、历史同期均价数值标准化其中「提前天数」是最强特征没有之一。它和价格通常不是线性关系而是分段提前 30 天以上价格平稳7 到 30 天缓慢上升7 天以内剧烈波动。所以我会额外构造一个days_bucket特征把提前天数分成[0,3)、[3,7)、[7,30)、[30,)四档让树模型更容易切分。import pandas as pd import numpy as np def build_features(df): # 提前天数分桶树模型对分段特征更敏感 df[days_bucket] pd.cut(df[days_before], bins[-1, 3, 7, 30, 999], labels[urgent, short, mid, long]) # 星期几独热避免模型误以为周日和周一有大小关系 df pd.get_dummies(df, columns[day_of_week, days_bucket], drop_firstTrue) # 航线组合成一个类别字段 df[route] df[origin] _ df[dest] # 目标编码用历史均价替换航线类别注意要用训练集统计量 route_mean df.groupby(route)[price].mean() df[route_avg_price] df[route].map(route_mean) return df这段代码里pd.cut的分桶边界不是拍脑袋定的而是先画了价格随提前天数的散点图看到明显的拐点才这么切。get_dummies的drop_firstTrue是为了避免独热编码带来的共线性虽然树模型对共线性不敏感但后面如果换线性模型做对比这一步能省很多事。目标编码route_avg_price必须用训练集的均值如果用了全量数据就是典型的数据泄露验证集分数会虚高上线直接崩。2.3 数据集从哪来没有真实数据怎么造做课设最头疼的是没有真实机票数据。常见做法有三种一是用 Kaggle 上的航班价格数据集字段比较全但航线偏欧美二是自己写爬虫抓某条航线的历史价格注意频率别太高一天一次就够三是用模拟数据按「提前天数 星期几 节假日 随机扰动」生成价格虽然不够真实但足够把模型流程跑通。我一般会建议先用模拟数据把 pipeline 搭好再换成真实数据调参这样不会卡在数据获取上。def generate_simulated_data(n5000): np.random.seed(42) days_before np.random.randint(0, 60, n) base_price 800 # 提前天数越短价格越高7天内加价明显 price base_price np.where(days_before 7, 400, 0) \ np.where((days_before 7) (days_before 30), 150, 0) \ np.random.normal(0, 80, n) df pd.DataFrame({ days_before: days_before, price: np.maximum(price, 200).round(0), day_of_week: np.random.choice([Mon,Tue,Wed,Thu,Fri,Sat,Sun], n), origin: np.random.choice([PEK,SHA,CAN], n), dest: np.random.choice([SZX,CTU,HGH], n) }) return df模拟数据的逻辑很简单基础价 8007 天内加 4007 到 30 天加 150再叠一层正态噪声。这样生成的数据能保证模型学到「提前天数越短价格越高」这个主信号方便你验证代码是否正确。真实数据里还会有节假日、竞对调价等复杂因素但流程是一样的。3. 随机森林和 XGBoost 怎么选代码怎么跑通3.1 随机森林回归先跑一个 baseline随机森林回归算法在机票价格预测里是个很稳的 baseline。它的好处是几乎不用调参就能给出一个合理结果而且能输出特征重要性帮你验证特征工程有没有做对。我一般会先用默认参数跑一遍看看 MAE 大概在什么量级再决定要不要上 XGBoost。from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error, mean_squared_error # 假设 df 已经过特征工程处理 X df.drop(columns[price]) y df[price] X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) rf RandomForestRegressor( n_estimators200, # 树的数量200 通常够用再多收益递减 max_depth12, # 限制深度防止过拟合机票数据噪声大 min_samples_leaf5, # 叶子最少样本数太小容易记住噪声 n_jobs-1, # 用满 CPU 核 random_state42 ) rf.fit(X_train, y_train) pred_rf rf.predict(X_test) print(RF MAE:, mean_absolute_error(y_test, pred_rf)) print(RF RMSE:, np.sqrt(mean_squared_error(y_test, pred_rf)))n_estimators200是我在机票数据上试出来的经验值再往上加 MAE 下降不到 1%但训练时间翻倍。max_depth12和min_samples_leaf5是为了控制过拟合机票价格本身波动大树太深会把噪声也学进去。跑完记得看特征重要性import matplotlib.pyplot as plt feat_imp pd.Series(rf.feature_importances_, indexX.columns).sort_values(ascendingFalse) feat_imp.head(10).plot(kindbarh) plt.show()如果days_before和route_avg_price排在前两位说明特征工程方向对了如果某个独热编码字段排很高可能是编码方式有问题需要回头检查。3.2 XGBoost 回归模型调参和 early stoppingXGBoost 回归模型在结构化数据上通常比随机森林强尤其是特征有交互作用的时候。机票价格里「提前天数 × 是否节假日」这种交叉效应XGBoost 能自动捕捉。但它的参数多调不好反而不如随机森林。我一般会先用early_stopping_rounds防止过拟合再调max_depth和learning_rate。import xgboost as xgb dtrain xgb.DMatrix(X_train, labely_train) dtest xgb.DMatrix(X_test, labely_test) params { objective: reg:squarederror, # 回归任务用平方误差 eval_metric: mae, # 监控 MAE max_depth: 6, # 比随机森林浅XGBoost 靠 boosting 补 learning_rate: 0.05, # 学习率小一点配合更多轮数 subsample: 0.8, # 行采样防过拟合 colsample_bytree: 0.8, # 列采样 seed: 42 } model xgb.train( params, dtrain, num_boost_round1000, evals[(dtest, test)], early_stopping_rounds50, # 50 轮没提升就停 verbose_eval100 ) pred_xgb model.predict(dtest, iteration_range(0, model.best_iteration)) print(XGB MAE:, mean_absolute_error(y_test, pred_xgb))max_depth6是 XGBoost 在机票数据上的常用起点比随机森林浅是因为 boosting 会叠加多棵树单棵太深容易过拟合。learning_rate0.05配合num_boost_round1000和early_stopping_rounds50基本不用手动试太多轮数。subsample和colsample_bytree都设 0.8是防止模型太依赖某几个强特征。3.3 用 RandomizedSearchCV 自动搜超参数手动调参效率低XGBoost 超参数自动设置可以用RandomizedSearchCV。它比网格搜索快适合参数空间大的场景。from sklearn.model_selection import RandomizedSearchCV param_dist { max_depth: [4, 6, 8, 10], learning_rate: [0.01, 0.05, 0.1], subsample: [0.6, 0.8, 1.0], colsample_bytree: [0.6, 0.8, 1.0], n_estimators: [100, 200, 400] } xgb_reg xgb.XGBRegressor(objectivereg:squarederror, seed42) search RandomizedSearchCV( xgb_reg, param_dist, n_iter30, # 随机采 30 组太多太慢 cv3, # 3 折交叉验证 scoringneg_mean_absolute_error, n_jobs-1, random_state42 ) search.fit(X_train, y_train) print(Best params:, search.best_params_) print(Best MAE:, -search.best_score_)n_iter30是精度和时间的折中如果数据量小可以加到 50。cv3在数据量不大时够用数据多可以上 5 折。注意scoring用的是负 MAE因为 sklearn 的交叉验证默认分数越大越好。4. 避坑与排查机票价格预测里最容易翻车的 4 件事4.1 数据泄露验证集分数虚高上线就崩现象验证集 MAE 只有 50测试集 MAE 突然变成 300。原因特征工程里用了全量数据的统计量比如航线均价、全局归一化参数导致验证集信息泄露到训练过程。解决所有统计量必须只在训练集上计算再应用到验证集和测试集。用Pipeline封装可以避免手动出错。4.2 时间序列不能随机切分现象随机切分后模型表现很好但按时间顺序预测未来价格时误差巨大。原因机票价格有时间趋势随机切分让模型看到了未来数据。解决按时间切分比如前 80% 时间做训练后 20% 做测试。如果数据量够用滚动窗口验证。4.3 类别特征编码方式选错现象航线字段用 LabelEncoder 后模型认为 PEK_SZX 和 PEK_CTU 有大小关系。原因LabelEncoder 给类别赋了有序整数树模型会尝试按数值切分。解决类别特征用独热编码或目标编码不要用 LabelEncoder。高基数类别比如航线超过 50 个优先用目标编码。4.4 忽略价格分布的偏态现象模型对低价票预测准高价票误差很大。原因价格分布右偏高价样本少模型偏向预测均值。解决对价格取对数再训练预测后再指数还原。或者用分位数回归关注不同价格区间的误差。5. 进阶技巧用特征交叉和残差分析把 MAE 再压 15%5.1 手动构造交叉特征树模型能自动学交叉但手动构造能让它学得更快。机票场景里「提前天数 × 是否节假日」「航线 × 星期几」是最有效的两组交叉。df[is_holiday] df[date].isin(holiday_list).astype(int) df[days_x_holiday] df[days_before] * df[is_holiday] df[route_day] df[route] _ df[day_of_week].astype(str)days_x_holiday让模型直接看到「节假日 短提前天数」的组合效应不用自己从两棵树里拼。route_day把航线和星期几拼成一个新类别再用目标编码能捕捉「这条航线周末更贵」这种局部规律。5.2 残差分析找模型盲区跑完模型别只看 MAE画一张残差分布图。如果残差在某个价格区间明显偏大说明模型在那里学得不好。residuals y_test - pred_xgb plt.scatter(pred_xgb, residuals, alpha0.3) plt.axhline(0, colorred, linestyle--) plt.xlabel(Predicted Price) plt.ylabel(Residual) plt.show()如果残差图呈现喇叭形说明价格越高误差越大这时候可以对高价样本加权或者对价格取对数。如果残差在某个提前天数区间集中偏正说明模型低估了那个区间的价格需要检查特征里有没有漏掉节假日或竞对调价信息。5.3 模型融合随机森林 XGBoost 取平均单模型调到头之后简单平均往往能再降一点误差。pred_final 0.4 * pred_rf 0.6 * pred_xgb print(Ensemble MAE:, mean_absolute_error(y_test, pred_final))权重按验证集表现定XGBoost 通常给高一点。如果两个模型相关性很高融合收益有限如果随机森林偏保守、XGBoost 偏激进融合效果会明显。我做这类预测项目有个习惯每次调完参先把验证集上的预测结果按提前天数分桶看每个桶的 MAE。如果某个桶误差突然变大一定是那个区间的特征没做好。这个习惯帮我省了很多次「上线才发现某类样本全错」的后悔药。希望帮到你。本文还有配套的精品资源点击获取