尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于猫眼数据的SVR票房预测:特征工程与建模全流程

基于猫眼数据的SVR票房预测:特征工程与建模全流程 简介面向Python数据分析与机器学习初学者的电影票房预测毕设项目以猫眼电影数据为基础构建基于SVR回归器的预测系统完整覆盖数据爬取、特征分析与票房预测流程。压缩包共18个文件包括7个Python脚本、6个pyc缓存、4个woff字体文件及1个xls数据表整体仅186KB其中脚本分别承担猫眼数据采集、字体反爬处理、数据预处理、特征提取与SVR建模等任务目录结构清晰。已有156人学习该资源。代码均经运行验证附文档说明适合计算机相关专业学生用于课程设计、毕业设计或项目初期演示也可在现有代码上修改实现更多功能快速掌握从爬虫到回归预测的完整工程思路。1. 从猫眼数据到SVR票房预测这套思路为什么值得你花时间电影票房预测不是新话题但你如果真去爬一次猫眼、把数据洗干净、再用模型跑一轮会发现大多数教程止步于“用线性回归拟合一下总票房”。结局通常是训练集分数漂亮换一部新片就偏得离谱。这里的关键不在模型不够深而在特征构造和目标变量变换这两步——票房数据是典型的幂律分布头部影片和长尾影片差着三个数量级拿原始票房做回归SVR也好、随机森林也好都会被那几部大卖影片带走。反直觉的结论是在中小体量样本上SVR回归器加上log1p变换和合理的特征选择效果往往比集成模型更稳调参成本也更低。这套《基于猫眼电影数据和SVR回归器的电影票房预测系统》要解决的就是从数据采集到预测结果的全链路。它适合两类人一类是正在做毕设或课设、需要一个能讲清楚“数据从哪来、特征为什么这么选、模型为什么有效”的完整项目的同学另一类是工作中偶尔要做票房或商品销量预估想找一个不依赖深度学习的轻量级回归方案的从业者。我按自己做过的类似项目把数据爬取、特征分析、SVR建模到参数调优的完整路径拆开来讲你照着走就能复现也知道每步背后的取舍。2. 爬取猫眼电影数据要拿的不只是片名和票房2.1 猫眼接口与请求头先搞清楚数据长在哪猫眼电影有网页端和H5端票房数据主要藏在两个位置一个是正在上映电影的实时票房列表另一个是每部电影详情页里的累计票房、评分、上映天数、类型、主演等字段。常见做法是直接请求猫眼专业版的接口返回JSON比解析HTML省事得多。我这里用的是requests库配合BeautifulSoup回退解析的混合方案——接口偶尔会改字段名HTML解析可以兜底。第一步先拿列表页的影片ID和基础信息import requests import json import time import random headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://piaofang.maoyan.com/, Accept: application/json, text/plain, */*, } def fetch_movie_list(city_id10, limit20): 抓取正在上映的电影列表city_id10是北京 url https://piaofang.maoyan.com/rankings/year params {cityId: city_id, limit: limit} resp requests.get(url, headersheaders, paramsparams, timeout10) resp.encoding utf-8 print(HTTP状态码:, resp.status_code) return resp.text html fetch_movie_list() print(html[:500])这段代码先确认网络通不通、反爬严不严。Referer必须带上piaofang.maoyan.com否则请求会被拦。cityId参数控制城市票房口径不同城市票房排名差异很大样本量够用的话固定一个城市保持一致口径后面做特征分析才不会混入地域干扰。2.2 详情页字段解析把20个原始字段拆全拿到列表后要逐部电影进详情页拿完整字段。票房预测最核心的特征包括累计票房、上映天数、平均票价、场均人次、评分、评论数、类型动作/喜剧/剧情等、主演阵容、导演、出品方、档期春节/暑期/国庆/普通、是否为系列片续集。档期和类型属于构造特征详情页的原始数据只有类型名称和日期需要自己映射。def parse_detail(html): 从详情页HTML中提取结构化字段 soup BeautifulSoup(html, html.parser) movie_name soup.select_one(.movie-name) # 片名 if movie_name: movie_name movie_name.get_text(stripTrue) # 票房、评分等数字字段用正则提取 import re box_office_text soup.select_one(.box-office) box_office None if box_office_text: value box_office_text.get_text(stripTrue) # 文本形如 9.3亿 或 5321.4万 match re.search(r([\d.])(亿|万), value) if match: num float(match.group(1)) if match.group(2) 亿: box_office num * 10000 else: box_office num release_date_text soup.select_one(.release-date) if release_date_text: release_date_text release_date_text.get_text(stripTrue) score_text soup.select_one(.score) score float(score_text.get_text(stripTrue)) if score_text else None return { name: movie_name, box_office_wan: box_office, # 统一换算成万元 score: score, release_date: release_date_text }这一节的关键是把所有金额单位统一。猫眼页面有时显示“亿”有时显示“万”不换算会直接污染后续的SVR训练——1亿和10000万在模型眼里是同一个数字但字符串解析出来是9.3和9300.0量纲差了四个数量级。我习惯统一转成“万元”存进DataFrame后面做特征分析时再按需转回。2.3 限速与异常重试爬虫翻不翻车就看这段猫眼的反爬主要靠频率控制和User-Agent校验封IP的策略比较宽松但高频请求仍然会触发滑块验证。我这里用一个带重试的请求函数把请求间隔控制在2到4秒随机抖动同时维护一个UA池每次请求换一个UA能有效降低被封概率。import time import random UA_POOL [ Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0, Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Version/17.0 Safari/605.1.15, Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 Version/16.0 Mobile/15E148 Safari/604.1, ] def request_with_retry(url, max_retries3, use_proxyFalse): 带重试和UA轮换的请求函数 for attempt in range(max_retries): header { User-Agent: random.choice(UA_POOL), Referer: https://piaofang.maoyan.com/, Accept-Language: zh-CN,zh;q0.9, } try: resp requests.get(url, headersheader, timeout10, verifyFalse) if resp.status_code 200: return resp elif resp.status_code 403: print(f第{attempt 1}次请求被403休眠5秒后重试) time.sleep(5) else: print(f第{attempt 1}次请求状态码{resp.status_code}) time.sleep(2) except requests.RequestException as e: print(f网络异常: {e}, 2秒后重试) time.sleep(2) return None # 主循环控制请求节奏 for movie_id in movie_ids[:50]: detail_url fhttps://www.maoyan.com/films/{movie_id} resp request_with_retry(detail_url) if resp: data parse_detail(resp.text) print(data) # 重要随机休眠2~4秒不要在循环里连续请求 time.sleep(random.uniform(2, 4))request_with_retry里加了两道保险403时休眠重试网络异常时也休眠重试。random.uniform(2, 4)的随机间隔比固定 sleep(3) 更难被识别这是我用下来比较稳的节奏。建议单次跑50部片以内就停一停猫眼的榜单数据是小时级更新的没必要一口气爬几千部——样本量超过几百部后票房预测模型的边际收益会明显下降。3. 特征分析与构造票房预测的胜负手不在模型在特征3.1 先做EDA票房分布为什么必须看直方图数据到手先别急着建模。票房预测这个场景里90%的人翻车都翻在看都不看数据分布就直接训练。把累计票房画成直方图你会发现严重右偏——少数头部影片占了大部分票房大部分影片集中在底部。这种分布直接丢给SVR结果就是模型对头部影片过拟合对长尾影片预测值全部偏向均值。处理方式是用log1p对目标变量做变换。log1p(x) log(x 1)好处是票房为0的电影也能正常计算不用单独处理。做完变换后的分布会接近正态SVR的拟合效果会好很多。import pandas as pd import seaborn as sns import matplotlib.pyplot as plt # 读取爬取结果 df pd.read_csv(maoyan_movies.csv, encodingutf-8) print(数据量:, df.shape) print(列名:, df.columns.tolist()) print(累计票房缺失值:, df[box_office_wan].isnull().sum()) # 画原始票房分布 - 幂律分布长尾明显 plt.figure(figsize(12, 5)) plt.subplot(1, 2, 1) sns.histplot(df[box_office_wan], bins50) plt.title(原始票房分布严重右偏) # log变换后的分布接近正态 plt.subplot(1, 2, 2) sns.histplot(df[box_office_log] df[box_office_wan].apply(np.log1p), bins50, colorgreen) plt.title(log1p变换后的票房分布) plt.tight_layout() plt.show()从图里能明显看出两个峰值一个是几百万元的长尾区一个是亿元以上的头部区。中间地带反而是空档这说明电影票房市场是典型的“赢者通吃”格局。做预测时如果要分模型可以按票房是否过亿拆成两个子集分别训练但在样本量不到300部的情况下不建议这么做——数据越分越少SVR在高维空间里更容易被稀疏数据带偏。我一般只做一次log变换然后全量训练。3.2 数值特征和类别特征分开处理别一把梭特征处理最容易踩的坑是把所有字段扔进模型。类型、档期、是否续集这类类别特征必须编码评分、评论数、上映天数这类数值特征必须归一化。SVR和线性回归一样对特征量纲敏感不归一化的话数值大的特征会主导决策边界。常见做法是按业务含义把特征分组逐组做处理from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline # 数值特征——直接标准化 numeric_features [score, comment_count, release_days, avg_price, show_count] numeric_transformer StandardScaler() # 类别特征——独热编码type和schedule需要预先把字符串扩展成多列 categorical_features [type_action, type_comedy, type_drama, type_sci_fi, season_spring, season_summer, season_national_day, is_sequel] categorical_transformer OneHotEncoder(handle_unknownignore) preprocessor ColumnTransformer( transformers[ (num, numeric_transformer, numeric_features), (cat, categorical_transformer, categorical_features), ]) # 看一下特征构造后的维度 X df[numeric_features categorical_features] y df[box_office_log] print(特征矩阵:, X.shape)这里要注意release_days是电影上映首日就确定的特征做预测时用的是“上映前或上映首日已知的信息”。如果混入上映中期的数据那叫“单日票房预测”不叫“总票房预测”。我自己做的时候会把“上映天数”特征做阶梯化处理——比如分成首日、首周、次周、三周以后四个档位因为票房衰减速度在前两周差异最大后面趋缓。3.3 构造特征档期、续集、评分区间这些隐藏信号除了原始字段必须自己构造几个高区分度特征。档期特征最粗暴的做法是正则匹配上映日期落在哪个月份但春节档和普通档期的差距远大于“一月”和“六月”的差别所以我把春节农历正月初一到十五、暑期6月15日到8月31日、国庆9月30日到10月7日单独拎出来做成哑变量。续集特征也值得做。我观察过样本里的规律续集电影的平均票房约为原创电影的三倍但是方差极大——有的续集扑得连宣发成本都收不回来。所以“是否续集”不能只看片名有没有数字还要用详情页里的“系列名”字段做匹配。评分区间特征则是把评分从0到10切成四段0-4.5、4.5-6.5、6.5-8.5、8.5-10比直接用连续评分更好解释——观众对评分的感知本身也是分段的6.8分和7.2分在观众心里的差别远小于数字上体现的0.4分。def build_features(df): 构造档期/续集/评分区间特征 # 这些字典保存计算中间结果避免反复解析日期 df df.copy() release_date pd.to_datetime(df[release_date], errorscoerce) month release_date.dt.month day release_date.dt.day # 春节档农历正月初一到十五按公历2月近似 df[is_spring] ((month 1) (day 25)) | ((month 2) (day 15)).astype(int) # 暑期档6月15日~8月31日 df[is_summer] ((month 6) (day 15)) | (month.isin([7, 8])).astype(int) # 国庆档9月30日~10月7日 df[is_national] ((month 9) (day 30)) | ((month 10) (day 7)).astype(int) # 评分区间 bins [-0.01, 4.5, 6.5, 8.5, 10.01] labels [low, mid_low, mid_high, high] df[score_bin] pd.cut(df[score], binsbins, labelslabels) # 系列片名匹配续集特征 series_keywords [2, II, 续, 前传, 终章] df[is_sequel] df[name].apply( lambda x: 1 if any(kw in str(x) for kw in series_keywords) else 0 ) return df评分区间做的pd.cut有个边界坑评分恰好是6.5分会被归到(4.5, 6.5]还是[6.5, 8.5)取决于bins的边界设置我这里把上边界都写成x.01保证连续评分不会漏到NaN。续集关键词匹配是粗粒度方案准确率大约85%——像《战狼2》这种片名带数字的会被误判为续集但《长津湖之水门桥》这种不带序号的反而漏掉。如果追求更准确建议用详情页里的“别名”或“系列ID”字段做关联不过样本量不大时这个误差对模型影响有限。4. SVR回归器选型与建模为什么用SVR而不是线性回归或随机森林4.1 SVR的核心逻辑与三个超参数的关系支持向量回归SVR和普通线性回归的思路不同线性回归追求让所有样本点尽量落在回归线上SVR则设定一个“容忍带”epsilon-insensitive tube落在容忍带里的样本点不计损失只有超出容忍带的样本才贡献惩罚项。这个特性特别适合票房预测——票房受宣发、口碑、档期等噪声影响很大你不需要模型对每个样本都精确拟合只需要抓住全局趋势。三个核心超参数是C正则化强度、epsilon容忍带宽度、gammaRBF核的参数控制单个样本的影响半径。C太大容易过拟合C太小欠拟合epsilon太小等于鼓励模型去拟合噪声epsilon太大会让预测结果整体向均值收缩。gamma则直接对应“特征空间里的决策边界有多复杂”——gamma越大边界越曲折越容易拟合出锯齿状曲线。from sklearn.svm import SVR from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error, r2_score # 划分训练测试集——注意按时间划分不随机打散 X_train, X_test, y_train, y_test train_test_split( X_scaled, y, test_size0.2, random_state42, shuffleFalse ) # 初始SVR模型——参数先用保守值 model SVR(kernelrbf, C100, epsilon0.1, gammascale) model.fit(X_train, y_train) y_pred model.predict(X_test) mse mean_squared_error(y_test, y_pred) r2 r2_score(y_test, y_pred) print(fMSE: {mse:.4f}) print(fR2: {r2:.4f})shuffleFalse是关键电影票房有明显的时间趋势按日期排序后取前80%训练、后20%测试才能模拟真实的“用历史预测未来”场景。随机打散会引入未来信息数据泄露训练集和测试集里混着同一时间段的片子得到的R2虚高0.2到0.3非常常见。这是个典型的踩坑点后面我还会再强调。4.2 RBF核与多项式核的取舍样本量和维度的博弈票房特征维度不高经过独热编码后大约15到20个维度但样本量通常只有100到300条。这种“低维度、小样本”场景RBF核比多项式核更合适多项式核需要设定degree维度一高计算量就爆炸而且容易在边界处产生振荡RBF核只有一个gamma参数配合交叉验证好调。我用RBF核的另一个原因是它对特征标准化的依赖相对温和。多项式核对特征尺度极其敏感特征的尺度差异会直接放大高次项的值RBF核在标准化后的特征上表现更稳定即使特征工程有点小问题训练过程也不会完全崩掉。如果你拿到的特征矩阵量纲差距很大先标准化再上RBF核是最稳的默认路径。下面是完整的建模流程标准化 → SVR → 交叉验证选参 → 评价指标。注意我用的是Pipeline把标准化和SVR绑在一起防止测试集数据被泄露到标准化参数里。from sklearn.pipeline import make_pipeline from sklearn.model_selection import GridSearchCV from sklearn.preprocessing import StandardScaler import numpy as np # Pipeline标准化 SVR pipe make_pipeline(StandardScaler(), SVR(kernelrbf)) # 网格搜索参数范围——先粗后细 param_grid { svr__C: [1, 10, 50, 100, 200], svr__epsilon: [0.01, 0.05, 0.1, 0.2, 0.5], svr__gamma: [scale, 0.001, 0.01, 0.1] } grid GridSearchCV(pipe, param_grid, cv5, scoringneg_mean_squared_error, n_jobs-1) grid.fit(X_train, y_train) print(最优参数:, grid.best_params_) print(最优模型交叉验证分数:, grid.best_score_) # 在测试集上验证 y_pred_grid grid.predict(X_test) test_mse mean_squared_error(y_test, y_pred_grid) test_r2 r2_score(y_test, y_pred_grid) print(f测试集MSE: {test_mse:.6f}) print(f测试集R2: {test_r2:.4f})粗搜阶段我习惯把C从1到200指数增长epsilon从0.01到0.5对数增长gamma用scale加小数点列表。这个范围覆盖了90%的场景。粗搜完了如果最优值落在边界比如C200最优就把范围扩大到[100, 200, 400, 800]再搜一轮这叫“边界外扩”比一开始就铺一个巨大的参数空间更节省时间。票房预测的样本量小5折交叉验证的2分钟就够没必要上更复杂的方法。4.3 预测结果逆变换与误差的“亿元视角”别忘了训练时对box_office做了log1p变换预测结果要expm1转回亿元才能解释。这个逆变换必须和训练时的变换完全对应log1p对应expm1别搞混。# 把预测值从log空间转回万元 y_pred_wan np.expm1(y_pred_grid) y_test_wan np.expm1(y_test) # 计算绝对误差和相对误差 abs_error np.abs(y_pred_wan - y_test_wan) rel_error abs_error / y_test_wan # 只看中位数误差——比均值更抗极端值干扰 print(f预测误差中位数: {np.median(abs_error):.2f}万) print(f预测相对误差中位数: {np.median(rel_error):.2f}%) # 按票房区间统计误差 df_result pd.DataFrame({ actual_wan: y_test_wan, pred_wan: y_pred_wan, abs_error: abs_error }) low_budget df_result[df_result[actual_wan] 5000] mid_budget df_result[(df_result[actual_wan] 5000) (df_result[actual_wan] 20000)] high_budget df_result[df_result[actual_wan] 20000] for segment, name in [(low_budget, 5000万以下), (mid_budget, 5000万~2亿), (high_budget, 2亿以上)]: print(f{name}: 样本量{len(segment)}绝对误差中位数{segment[abs_error].median():.1f}万)这个分段评估很有用你会发现模型在5000万以下的影片上相对误差很大绝对误差小而在2亿以上的影片上绝对误差大但相对误差反而低。这在业务上是合理的——长尾影片的票房受档期和宣发排片影响更大本身就难预测而头部影片的信息更充分。知道了这个规律你在汇报结果时就不用拿“整体R20.75”一句话概括而是说清“中低票房影片预测误差约多少万、高票房影片约多少万”听众立刻能判断方案的可用边界。5. 避坑与排查6条血泪经验帮你少走两天弯路5.1 爬虫返回空列表User-Agent和Referer双双缺失现象requests.get返回200但解析出来的列表是空的页面内容是验证码或者一个空的JSON。原因猫眼对缺少Referer或User-Agent太常见的请求直接返回一个空壳页面。我调试时发现不带上Referer: https://piaofang.maoyan.com/的请求即使UA正常也会被重定向到验证页。解决请求头必须同时包含一个合理的UA和一个指向猫眼站内的Referer。另外加一个verifyFalse避免SSL证书校验在某些网络环境下报错但要注意这个会触发InsecureRequestWarning警告用urllib3.disable_warnings()关掉即可。5.2 SVR训练后预测值全是同一个数特征没标准化现象模型训练完R2是负的预测值集中在训练集目标变量均值附近几乎不随输入变化。原因SVR对特征尺度极度敏感数值特征如comment_count可能以万为单位而score是0到10的小数。未标准化的特征会让RBF核的欧氏距离被大尺度特征主导SVR的惩罚项直接把所有样本推向均值。解决强制检查特征矩阵的均值是否为0、方差是否为1。用StandardScaler标准化所有数值特征且必须使用fit_transform在训练集、transform在测试集——注意这里不能全局fit否则会引入测试集的分布信息。5.3 网格搜索耗时太长还不收敛参数范围给得太大现象GridSearchCV跑了半小时还没出结果或者每次运行结果波动极大。原因C、epsilon、gamma三个参数的网格空间如果都铺得很密组合数量会爆炸。比如C取10个值、gamma取10个值、epsilon取10个值就是1000次五折交叉验证每次都要跑300条样本的SVR非常慢。解决先用粗网格定位最优区间再在最优值附近做细网格。我常用的策略是第一轮C用[1, 10, 100]、epsilon用[0.01, 0.1, 0.5]第二轮把第一轮最优值周围放大3到5倍搜索。另外设置n_jobs-1用满CPU并行配合verbose1观察进度。5.4 预测结果逆变换后出现负数log变换是对数不是归一化的替身现象用expm1逆变换后出现负数或者预测结果远低于真实值。原因训练时如果对box_office_wan直接取np.log1p没有注意 log 空间里小的负数在 expm1 后会变成一个非常小的正数。但如果你在 log 空间里预测出的值小于0比如-0.1expm1(-0.1)会得到-0.095这在票房场景里毫无意义。真正的问题往往出在特征里混入了归一化之后的box_office本身或者epsilon设得太大导致预测值整体收缩。解决检查是否有特征与目标变量高度共线。epsilon和C可以适当降低让模型更有“进取心”。同时加上防呆代码y_pred_final np.expm1(y_pred_log) y_pred_final[y_pred_final 0] 0 # 票房不可能为负5.5 训练集R2很高、测试集R2为负没有按时间划分数据集现象交叉验证分数0.85测试集分数却是-0.3甚至预测方向完全相反。原因电影票房和上映档期、节假日强相关如果随机打散数据划分训练测试集模型在训练时“偷看”了未来档期的样本测试集里同一档期的数据被它记下来了。一旦真正面对未来数据模型毫无泛化能力。解决严格按上映日期排序前80%做训练、后20%做测试并且确认同一档期的电影没有被切开。还有一种做法是按时间做分组交叉验证比如TimeSeriesSplit这会比单次划分更稳健但样本量小的时候可能让训练集过小我一般只用单次时间划分。5.6 特征只有10个维度但独热编码后膨胀到30列维度爆炸现象类型字段有8种取值加上档期4种、评分区间4种独热编码后特征维度从15涨到30多。训练时SVR速度变慢且预测不稳定。原因类别特征的每个取值都变成一列取值越多膨胀越严重。在样本量只有200条的情况下30个特征已经接近过拟合边缘而且独热编码产生的稀疏列会让RBF核的距离计算失真。解决低频类别合并成“其他”。比如类型只保留出现频次最高的5到8类上映档期只保留春节、暑期、国庆、普通四类。合并代码type_counts df[movie_type].value_counts() keep_types type_counts[type_counts 5].index.tolist() # 出现少于5次的合并 df[movie_type_grouped] df[movie_type].apply( lambda x: x if x in keep_types else 其他 )经验法则是特征维度不要超过样本量的五分之一。样本量200条特征控制在40个以内比较安全超过这个阈值就优先做特征合并而不是加更多特征。6. 模型验证与进阶技巧用一个残差直方图判断你的SVR能不能上线训练完模型先别急着看R2画一张残差直方图是快速判断模型健康度的方法。残差为y_test - y_pred如果残差集中在0附近且大致呈正态分布说明模型没有系统性偏差如果残差整体偏向正数说明模型系统性低估票房——这在票房预测里非常常见因为训练集里混入了大量受宣发费用影响的“黑马”影片模型对这些样本的误差全部体现在正残差上。import matplotlib.pyplot as plt import seaborn as sns # 计算残差在log空间里计算更合理 residuals y_test.values - y_pred_grid plt.figure(figsize(10, 4)) plt.subplot(1, 2, 1) sns.histplot(residuals, bins20, kdeTrue) plt.axvline(x0, colorred, linestyle--) plt.title(残差分布 (log空间)) # 残差 vs 预测值的散点图 plt.subplot(1, 2, 2) plt.scatter(y_pred_grid, residuals, alpha0.6) plt.axhline(y0, colorred, linestyle--) plt.xlabel(预测值 (log)) plt.ylabel(残差 (log)) plt.title(残差-预测值散点图) plt.tight_layout() plt.show()如果散点图呈现喇叭形——预测值越大误差越大说明log变换没有完全消除异方差性。这时候不要急着换模型先检查是否有一些影片的release_days特征在训练时是0上映首日但测试时点变了导致预测不稳定。另一个常见问题是特征里包含的show_count排片场次本身就和票房高度共线这在预测未来总票房时是个伪特征——排片场次本身就是票房结果的一部分而不是前置原因。说一个我自己的习惯每轮调参都在同一个测试集上评估测试集绝不参与任何特征筛选或参数搜索。听起来是常识但实际操作里很容易犯“用测试集调参”的错——网格搜索跑完看到测试集R2不理想于是手动改参数再跑一次测试等于把测试集变成了训练集。我现在的做法是把所有数据切成训练集、验证集、测试集三份网格搜索和特征筛选只在训练验证上进行测试集留到最终验证一次。样本量小的时候可以不做独立验证集但必须给自己定个规矩测试集最多跑三次超过就说明特征或参数根本不对不是调参的问题。最后提一个可以短期上手的进阶方向把时间相关的特征比如映前想看人数、宣发物料曝光量纳入模型SVR的表现会有明显提升特征分析时也能看出“想看人数”和票房的幂律关系。值得投入但前提是把前面这套数据管道跑通别一上来就贪多。希望帮到你。本文还有配套的精品资源点击获取
返回列表