数学建模竞赛实战:基于数据驱动的蔬菜定价与补货决策模型
2026/9/8 5:12:48 网站建设 项目流程

简介:本资源是2023年高教社杯全国大学生数学建模竞赛C题——蔬菜类商品的自动定价与补货决策的完整参赛成果,面向本科高年级学生、毕业设计选题者及数学建模初学者,聚焦生鲜零售场景下的动态定价、销量预测与库存优化等核心问题。压缩包共110个文件,含8个Python主程序(含详细注释)、12个h5与pkl格式训练模型(覆盖辣椒类、茄类、食用菌、花菜类等7大蔬菜品类)、16个xlsx原始与处理数据表、36张结果可视化png图,以及论文PDF、Word文档、技术说明txt等,整体大小72.74MB,结构清晰、模块解耦,便于理解与复用。已有844人学习下载,项目为作者手打高分方案(98分),经导师高度认可,可直接用于毕业设计、课程设计或期末大作业;所有代码均通过本地环境严格调试,部署后即可运行,配套模型已封装为即调即用接口,显著降低建模门槛与实施成本。

1. 项目概述:从赛题到实战的完整复盘

去年带队参加高教社杯数学建模竞赛,我们组选的就是C题——蔬菜类商品的自动定价与补货决策。这题目一出来,当时就在圈子里炸了锅,因为它太“实”了。不像一些纯理论推导题,这道题直接把一个生鲜零售行业的核心痛点,原封不动地搬到了我们面前:每天面对几百种蔬菜,进多少货?卖什么价?既要保证新鲜度减少损耗,又要最大化利润,还得考虑顾客满意度。这简直就是一个小型零售企业的CEO每天要做的决策。

这道题的价值在于,它完美地模拟了一个数据驱动的商业决策闭环。组委会提供了过去四年的销售流水、损耗记录,以及部分商品的批发价格。我们的任务,就是基于这些历史数据,构建数学模型,为未来一段时间(题目中是未来一周)的每一种蔬菜,制定出每天的补货量和销售定价策略。这不仅仅是一个数学问题,更是一个融合了统计分析、时间序列预测、运筹优化和商业理解的综合性项目。最终,我们团队凭借一套相对完整的解决方案,拿到了不错的成绩。今天,我就把当时的核心思路、模型构建的细节、编程实现的坑,以及那些论文里不会写的“实战心得”,毫无保留地分享出来。无论你是未来要参赛的同学,还是对数据分析和商业智能感兴趣的朋友,相信这篇近万字的复盘都能给你带来实实在在的启发。

2. 核心问题拆解与解题思路总览

面对这样一个庞大的问题,直接上手建模肯定会一头雾水。我们的第一步,也是最重要的一步,就是把赛题这个“黑盒子”拆解成一系列可量化、可建模的子问题。整个解题逻辑可以梳理成一条清晰的流水线。

2.1 问题一:销量与损耗的预测模型

这是所有决策的基础。如果连未来能卖多少、会坏多少都预测不准,后面的定价和补货就是空中楼阁。题目要求分别建立销售量和损耗量的预测模型。我们的思路是:

销售量预测:核心是时间序列分析。蔬菜销量有明显的规律:工作日和周末不同,节假日(特别是春节、国庆)会爆发,夏季和冬季的消费结构也不一样。我们首先进行了详尽的数据探索性分析(EDA),发现了以下几个关键特征:

  1. 趋势性:部分常备蔬菜(如土豆、西红柿)有缓慢的长期增长趋势。
  2. 季节性:年周期(季节性蔬菜如冬瓜、丝瓜)、周周期(周末采购高峰)非常明显。
  3. 节假日效应:节前采购高峰和节日期间的销售低谷。
  4. 价格弹性:销量受当日价格影响显著,这是后续定价模型的关键输入。

基于此,我们没有使用单一的模型,而是构建了一个组合预测框架。对于规律性强的单品,采用季节性分解(如STL)结合ARIMA或Prophet模型;对于波动大、规律性弱的单品,则尝试了LightGBM这类树模型,以历史销量、星期几、是否节假日、天气情况(可外部获取,但题目未提供,我们将其作为假设性扩展特征)、前N天的销量等作为特征。关键是,要对预测结果进行不确定性量化,给出预测区间(如90%置信区间),而不是一个孤零零的点估计,这为后续的鲁棒优化提供了基础。

损耗量预测:损耗与销量强相关,但并非简单的比例关系。我们将其建模为关于当前库存天数、商品品类、历史平均损耗率、季节性的函数。一个核心发现是,损耗率在库存量接近保质期临界点时会急剧上升,呈现非线性关系。我们采用了梯度提升树(如XGBoost)来捕捉这种复杂关系,特征包括:当日期初库存、前几日销量、商品易腐等级(叶菜类、果菜类、根茎类等)、仓库平均温度(假设数据)等。

注意:预测模型没有“银弹”。我们花了大量时间在特征工程上,比如创造“过去3天平均销量”、“相对于上周同期的销量变化率”等特征,这些往往比直接用原始数据更有效。另外,一定要用滚动预测的方式验证模型,模拟真实环境中每天用新数据更新模型预测明天的场景,而不是用全部历史数据一次性预测未来,后者会导致严重的过拟合和乐观偏差。

2.2 问题二与三:定价与补货的联合优化模型

这是本题的精华和难点。定价和补货不是两个独立决策,而是相互耦合、相互影响的。定高价可能减少销量导致库存积压和损耗,定低价可能清空库存但损失利润;补货多需要低价促销来保证售罄,补货少则可能错过销售机会。

我们将其构建为一个动态规划随机规划问题。目标是最大化未来计划期(如一周)的总期望利润。决策变量是每天的补货量和定价。约束条件包括:

  • 库存平衡约束(上日库存+补货-销量-损耗=本日库存)。
  • 库存容量约束。
  • 补货量上下限约束(基于供应商和物流)。
  • 价格变动幅度约束(不能每天价格波动太大,影响顾客感受)。
  • 需求与价格的函数关系(需求预测模型)。

这里的核心是需求函数。我们假设销量D(p)是价格p的函数,通常是一个向下倾斜的曲线。我们通过历史数据,对每个单品拟合了简单的线性或指数型需求-价格弹性模型。例如,D(p) = D0 * (p / p0)^(-e),其中D0是基准价格p0下的预测销量,e是价格弹性系数(通过历史数据回归估计)。

于是,单日利润可表示为:Profit = p * min(D(p), 实际库存) - 成本 * 补货量 - 损耗成本。其中min(D(p), 实际库存)体现了库存不能超过需求销售的现实。

求解这个优化模型是个挑战。因为涉及整数变量(补货量)、非线性目标(价格乘以需求函数),我们采用了分解和启发式算法

  1. 先补货,后定价的迭代算法:先固定一组价格,求解最优补货量(变成一个线性/整数规划);再固定补货量,优化价格(变成一个非线性规划,可用梯度下降)。迭代几次直至收敛。
  2. 模拟退火或遗传算法:将补货量和价格编码为染色体,直接优化总利润目标函数。这种方法更通用,但计算量大,需要精心设计编码和邻域搜索策略。

我们最终选择的是第一种迭代算法,因为它更直观,在有限竞赛时间内更容易实现和调试。对于价格优化,我们甚至对某些单品采用了更简单的分段定价策略:根据预测销量和库存水平,将价格设定在“高利润”、“平衡”、“促销”三档之一,大大简化了计算。

2.3 问题四:分析模型对数据的敏感性

任何模型都不是万能的,必须检验其健壮性。题目要求分析模型对销售和损耗预测的敏感性。我们做了以下几件事:

  1. 扰动分析:在预测的销量和损耗值上,人工添加±10%, ±20%的随机扰动,重新运行定价补货模型,观察总利润、平均库存等关键指标的变化幅度。如果利润波动剧烈,说明模型对预测误差非常敏感,决策风险高。
  2. 关键参数敏感性:重点分析价格弹性系数e损耗率参数。通过改变这些参数的值,观察最优定价策略的变化。例如,当价格弹性变大(顾客对价格更敏感),模型会倾向于给出更低的价格。
  3. 场景分析:模拟极端场景,如“突然的销量暴增”(类似促销活动)或“连续的阴雨天气导致客流减少”,看模型给出的决策是否合理,是否需要引入人工干预规则。

这部分内容在论文中能极大体现团队的思考深度,表明我们不仅建了模型,还知道模型的局限在哪里,什么情况下会“失灵”。

3. 数据预处理与特征工程实战细节

拿到数据后,千万别急着跑模型。原始数据就像未经雕琢的玉石,数据预处理和特征工程就是雕刻刀,这一步直接决定了模型性能的上限。我们当时的数据大致包含:商品编码、日期、销售量、损耗量、批发价(部分)、是否打折等字段。

3.1 数据清洗与整合

  1. 缺失值处理:批发价数据大量缺失。我们采用了多种方法填补:
    • 同类商品均值法:对于土豆、西红柿等常见菜,用同一品类下其他日期的均价填补。
    • 前后插值法:对于连续几天缺失的,用前后日期的价格线性插值。
    • 建立简单预测模型:用历史价格序列,对每个单品建立一个简单的时序模型(如移动平均)来预测缺失日期的价格。我们专门写了一小段Python代码批量处理。
    # 示例:使用前向填充(ffill)和后向填充(bfill)结合处理批发价缺失 import pandas as pd # 假设df是包含`wholesale_price`的DataFrame,按商品和日期排序 df['wholesale_price_filled'] = df.groupby('product_id')['wholesale_price'].apply( lambda x: x.ffill().bfill() # 先向前填充,再向后填充 ) # 对于填充后仍为NaN的(如开头数据),用品类均价填充 category_avg_price = df.groupby('product_category')['wholesale_price_filled'].transform('mean') df['wholesale_price_filled'] = df['wholesale_price_filled'].fillna(category_avg_price)
  2. 异常值检测与处理:销量为0但损耗奇高?可能是盘点错误或数据录入问题。我们使用箱线图基于移动标准差的方法来识别异常值。对于明显的异常值(如销量是平时100倍),我们将其视为“特殊事件”(可能是团购)并单独分析,或直接用前后正常值的均值替换。
  3. 数据聚合:原始数据可能是流水记录,我们需要将其按商品-日期聚合为日度数据,得到每个单品每天的总销量、总损耗、平均销售价格等。

3.2 核心特征构造

这是特征工程的重头戏,我们构造了四大类特征:

1. 时间特征:

  • 基础周期:day_of_week,month,quarter,is_weekend
  • 节假日:is_holiday(标记国家法定节假日),days_to_holiday(距离最近节假日的天数,节前为正,节后为负),这个特征对捕捉节前囤货行为非常有效。
  • 时序特征:lag_1,lag_7,lag_30(前1天、7天、30天的销量),rolling_mean_7,rolling_std_7(过去7天的移动均值和标准差)。

2. 商品特征:

  • 品类编码:category_id(叶菜、果菜、根茎、菌菇等)。
  • 物理属性:shelf_life(假设的保质期天数,根据品类设定),is_perishable(是否易腐,0/1)。
  • 价格特征:current_price,discount_rate(折扣率),price_change(相较于昨日价格变化)。

3. 交互特征与衍生特征:

  • 库存相关inventory_turnover(过去7天平均销量/当日期初库存),这是一个关键指标,反映周转速度。
  • 损耗风险特征inventory_age(基于先进先出假设估算的库存货龄),estimated_spoilage_risk(库存量 / (过去3天平均销量 * 保质期)),比值越高,风险越大。
  • 竞争/互补特征:计算同一品类内其他商品的总销量(作为市场热度指标),或与主要搭配商品(如西红柿和鸡蛋)的销量关联度。

4. 外部特征(假设性扩展):

  • 天气数据:temperature,is_rainy。高温可能增加冷饮蔬菜销量,雨天可能减少客流。
  • 宏观经济标记:is_payday(是否发薪日附近)。

实操心得:特征不是越多越好。我们先用所有特征训练一个简单的树模型(如LightGBM),然后通过特征重要性排序递归特征消除,筛选出最重要的20-30个特征。这不仅能加快训练速度,还能防止过拟合,提高模型泛化能力。特别是对于时序预测,要警惕“数据泄露”,绝对不能使用未来的信息(如明天的价格)来预测今天的销量。

4. 预测模型的选择、实现与调优

我们为销量预测和损耗预测分别搭建了模型流水线。

4.1 销量预测模型栈

我们采用了“轻量级时序模型 + 机器学习模型”的混合策略。

1. 基准模型 - Prophet:Facebook Prophet 非常适合具有强季节性和节假日效应的商业序列。它几乎开箱即用,能提供不错的基线预测和预测区间。

from prophet import Prophet import pandas as pd # 准备数据,列名必须是 ds (日期) 和 y (销量) df_prophet = df[['date', 'sales']].rename(columns={'date': 'ds', 'sales': 'y'}) # 添加节假日数据 holidays_df = pd.DataFrame({ 'holiday': 'chinese_holiday', 'ds': pd.to_datetime(['2022-01-01', '2022-02-01', ...]), # 节假日日期列表 'lower_window': -2, # 节前2天开始影响 'upper_window': 1, # 节后1天结束影响 }) model = Prophet(holidays=holidays_df, yearly_seasonality=True, weekly_seasonality=True) model.fit(df_prophet) future = model.make_future_dataframe(periods=7) # 预测未来7天 forecast = model.predict(future) # forecast 中包含预测值 yhat 以及置信区间

Prophet的结果作为我们第一个基准,特别是其捕捉到的周季节性和节假日效应,非常直观。

2. 主力模型 - LightGBM:我们将预测问题转化为监督学习问题。对于要预测的某一天t,我们使用t-1, t-2, ..., t-30天的特征(包括构造的时序特征、价格特征、品类特征等)来预测t天的销量。这样,我们得到了一个庞大的特征数据集。

import lightgbm as lgb from sklearn.model_selection import TimeSeriesSplit # 假设 X 是特征矩阵,y 是销量目标值 tscv = TimeSeriesSplit(n_splits=5) # 时序交叉验证 params = { 'objective': 'regression', 'metric': 'rmse', 'boosting_type': 'gbdt', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9, } for train_idx, val_idx in tscv.split(X): X_train, X_val = X.iloc[train_idx], X.iloc[val_idx] y_train, y_val = y.iloc[train_idx], y.iloc[val_idx] lgb_train = lgb.Dataset(X_train, y_train) lgb_eval = lgb.Dataset(X_val, y_val, reference=lgb_train) gbm = lgb.train(params, lgb_train, num_boost_round=1000, valid_sets=[lgb_train, lgb_eval], callbacks=[lgb.early_stopping(stopping_rounds=50)])

LightGBM能很好地捕捉非线性关系和特征交互,对于受多种因素影响的销量预测非常有效。

3. 模型融合:我们简单地将Prophet的预测结果和LightGBM的预测结果进行加权平均(如 0.3 * Prophet + 0.7 * LightGBM),或者将Prophet的预测值及其置信区间宽度作为新的特征,输入到LightGBM中进行第二轮训练。融合后的模型通常比单一模型更稳定、更准确。

4.2 损耗预测模型

损耗预测我们主要使用LightGBM,将其作为一个回归问题。目标变量是损耗量损耗率。特征除了销量预测中用到的,特别加入了:

  • current_inventory:当日开始时的库存。
  • inventory_age:库存新鲜度。
  • category_spoil_rate:该品类历史平均损耗率。
  • sales_in_last_3days:过去3天销量,反映周转速度。

一个关键技巧是,对于损耗预测,我们更关注高库存、低周转的商品。因此,在训练时,我们对这部分样本给予了更高的权重,让模型更专注于学习高风险场景下的损耗规律。

4.3 模型评估与调优

我们坚决不使用简单的训练集-测试集随机划分,而是采用时序交叉验证。模拟滚动预测的过程:用前N天数据训练,预测下一天,然后加入真实数据,再预测下一天,如此往复。用整个滚动窗口上的平均误差(如RMSE, MAPE)来评估模型。

调参主要针对LightGBM。我们使用了贝叶斯优化(如hyperopt库)来搜索最优参数组合,目标是最小化验证集上的RMSE。主要调整的参数包括:num_leaves,learning_rate,feature_fraction,min_data_in_leaf等。

踩坑实录:一开始我们直接用全部数据训练,然后预测未来,结果在测试集上表现“虚高”。这是因为未来信息通过全局统计量(如全局均值、方差)泄露到了训练中。时序交叉验证是解决这个问题的金科玉律。另外,对于销量为0的天数很多(长尾商品),直接预测原始销量RMSE会被大值主导,我们尝试预测log(1+sales),效果有所改善。

5. 定价与补货联合优化模型的求解

预测模型给出的是未来需求的“可能性”,优化模型则是在此基础上做“决策”。我们构建的联合优化模型本质是一个带约束的随机优化问题。

5.1 模型数学表述

设规划期为T天(如T=7),商品种类为I

决策变量:

  • x_{i,t}:第i种商品在第t天的补货量。
  • p_{i,t}:第i种商品在第t天的销售单价。
  • I_{i,t}:第i种商品在第t天结束时的库存量。

目标函数:最大化总期望利润

Maximize Σ_{t=1 to T} Σ_{i=1 to I} E[ p_{i,t} * min(D_{i,t}(p_{i,t}), I_{i,t-1}+x_{i,t}) - c_i * x_{i,t} - h * I_{i,t} - s * S_{i,t} ]

其中:

  • D_{i,t}(p):需求函数,表示在价格p下的随机需求量。我们通常用预测的期望销量d_{i,t}乘以一个价格弹性因子来建模,例如D(p) = d * (p/p_ref)^(-e)
  • c_i:商品i的单位进货成本。
  • h:单位库存持有成本(资金占用、仓储费)。
  • s:单位损耗成本。
  • S_{i,t}:第t天的损耗量,是库存和销量的函数,由损耗预测模型给出。

约束条件:

  1. 库存平衡I_{i,t} = I_{i,t-1} + x_{i,t} - Sales_{i,t} - S_{i,t}。其中Sales_{i,t} = min(D_{i,t}(p_{i,t}), I_{i,t-1}+x_{i,t})
  2. 补货能力0 <= x_{i,t} <= X_max_i
  3. 库存容量0 <= I_{i,t} <= I_max_i
  4. 价格变动限制|p_{i,t} - p_{i,t-1}| / p_{i,t-1} <= δ(例如,日价格波动不超过10%)。
  5. 价格范围P_min_i <= p_{i,t} <= P_max_i(基于成本、市场接受度)。

5.2 求解策略:近似与启发式

上述模型包含非线性(min函数,需求函数)、随机性,直接求解是NP-Hard的。在竞赛的有限时间内,我们采用了实用化的近似求解策略。

策略一:分步迭代优化(我们采用的主要方法)这是一个坐标下降法的思想,固定一组变量,优化另一组。

  1. 初始化:设定初始价格(如历史平均价)和初始补货计划(如预测销量)。
  2. 补货子问题:固定价格p,需求D(p)可视为已知。此时目标函数简化为一个线性规划(LP)整数规划(IP),因为min(D, I+x)在已知需求分布下可以线性化或通过场景法处理。我们可以用PuLPortools库快速求解未来T天的最优补货量x
  3. 定价子问题:固定补货量x和库存路径I,优化价格p。此时每个商品每天的价格决策相对独立。目标函数是关于p的单变量非线性函数(考虑需求函数D(p))。我们可以对每个(i,t),在价格区间[P_min, P_max]内,用一维搜索(如黄金分割法)或梯度法快速找到最优价格。
  4. 迭代:重复步骤2和3,直到总利润的变化小于某个阈值,或达到最大迭代次数。

策略二:模拟退火算法我们将所有决策变量(x_{i,t}p_{i,t})编码成一个长向量作为“状态”。

  1. 邻域移动:随机选择一个商品和一天,对其补货量或价格进行一个小的随机扰动。
  2. 接受准则:计算新状态下的利润(需要通过模拟需求随机实现来计算,计算量较大)。如果新利润更高,则接受新状态;如果更低,则以一定概率(随时间衰减)接受,以避免陷入局部最优。
  3. 冷却:按照冷却计划表降低“温度”。

模拟退火能探索更广的解空间,但计算非常慢,需要对问题进行大量简化(如减少商品种类或天数)才能运行。

策略三:基于规则的策略(用于对比和快速生成基线)为了验证我们优化模型的有效性,我们实现了几个简单的规则策略作为基线:

  • 经验补货法:补货量 = 未来7天预测销量之和 - 当前库存 + 安全库存(如3天预测销量)。
  • 成本加成定价法:价格 = 进货成本 * (1 + 固定毛利率)。
  • 动态定价法:根据库存水平调整价格。高库存时降价促销,低库存时提价保利润。例如:价格 = 基准价 * (1 + α * (安全库存 - 当前库存)/安全库存),其中α是敏感系数。

核心技巧:在迭代优化中,需求函数D(p)的估计至关重要。我们不是用一个固定的弹性系数e,而是针对不同商品、不同季节甚至不同库存水平,拟合了不同的弹性系数。例如,对于绿叶菜,临近保质期时价格弹性会变大(顾客对临期品价格更敏感),此时模型会自动给出更大的折扣。这需要我们将预测模型和弹性系数估计模型紧密耦合。

6. 系统实现、代码结构与关键模块

我们使用Python作为主要实现语言,其丰富的数据科学生态系统(pandas, numpy, scikit-learn, lightgbm, pulp)完美支撑了整个项目。代码结构遵循模块化设计,便于协作和调试。

6.1 项目目录结构

math_modeling_c/ ├── data/ # 原始数据与处理后的数据 │ ├── raw/ # 赛题提供的原始CSV文件 │ └── processed/ # 清洗、特征工程后的数据 ├── src/ # 源代码 │ ├── data_preprocessing.py │ ├── feature_engineering.py │ ├── demand_forecast.py # 销量预测模型 │ ├── spoilage_forecast.py # 损耗预测模型 │ ├── price_inventory_optimization.py # 定价补货优化模型 │ └── utils.py # 工具函数(评估指标、绘图等) ├── configs/ # 配置文件(模型参数、路径等) ├── outputs/ # 模型输出、预测结果、决策方案 │ ├── forecasts/ │ ├── decisions/ │ └── figures/ # 分析图表 └── main.py # 主程序入口,串联整个流程

6.2 关键模块代码片段

1. 需求预测模块 (demand_forecast.py) 核心:

class DemandForecaster: def __init__(self, model_type='lgb'): self.model_type = model_type self.model = None self.scaler = StandardScaler() def prepare_features(self, df): """构造时序特征、价格特征、节假日特征等""" # ... 特征构造逻辑 ... return feature_df, target_series def train(self, X_train, y_train): if self.model_type == 'lgb': self.model = lgb.LGBMRegressor(**self.lgb_params) self.model.fit(X_train, y_train) elif self.model_type == 'prophet': # ... Prophet训练逻辑 ... # 保存模型和scaler joblib.dump({'model': self.model, 'scaler': self.scaler}, 'model.pkl') def predict(self, X_future): """预测未来需求,并返回置信区间(模拟)""" if self.model_type == 'lgb': pred = self.model.predict(X_future) # 通过计算验证集残差的标准差来模拟预测区间 pred_interval = self._calculate_prediction_interval(pred) return pred, pred_interval

2. 优化求解模块 (price_inventory_optimization.py) 核心:

import pulp class InventoryOptimizer: def __init__(self, demand_forecast, cost_dict, capacity_dict): self.demand = demand_forecast # 未来各期需求预测(均值) self.cost = cost_dict self.capacity = capacity_dict def solve_lp(self, prices): """给定价格,求解最优补货计划(线性规划)""" prob = pulp.LpProblem('Inventory_Optimization', pulp.LpMaximize) # 定义决策变量:补货量x,库存量I x_vars = pulp.LpVariable.dicts('x', [(i,t) for i in products for t in periods], lowBound=0, upBound=self.capacity['max_order']) I_vars = pulp.LpVariable.dicts('I', [(i,t) for i in products for t in periods], lowBound=0, upBound=self.capacity['max_storage']) # 设置目标函数:利润 = 收入 - 进货成本 - 持有成本 revenue = pulp.lpSum([prices[i][t] * pulp.lpMin(self.demand[i][t], I_vars[i,t-1] + x_vars[i,t]) for i in products for t in periods]) # ... 简化处理min函数,此处需线性化或近似 ... cost = pulp.lpSum([self.cost['purchase'][i] * x_vars[i,t] for i in products for t in periods]) prob += revenue - cost # 添加库存平衡约束等 for i in products: for t in periods: prob += I_vars[i,t] == I_vars[i,t-1] + x_vars[i,t] - self.demand[i][t], f"库存平衡_{i}_{t}" prob.solve(pulp.PULP_CBC_CMD(msg=False)) return {('x', i, t): pulp.value(x_vars[i,t]) for i in products for t in periods}

3. 主流程串联 (main.py):

def main(): # 1. 加载和预处理数据 raw_df = load_data('data/raw/sales.csv') clean_df = preprocess_data(raw_df) # 2. 特征工程 feature_df, target_df = engineer_features(clean_df) # 3. 训练预测模型 forecaster = DemandForecaster(model_type='lgb') forecaster.train(feature_df, target_df) future_demand, demand_interval = forecaster.predict(future_features) # 4. 优化求解 optimizer = InventoryOptimizer(future_demand, cost_data, capacity_data) # 初始化价格 initial_prices = get_historical_avg_prices() best_solution = None best_profit = -float('inf') for iteration in range(MAX_ITER): # 固定价格,优化补货 replenishment_plan = optimizer.solve_lp(initial_prices) # 固定补货,优化价格 optimized_prices = optimize_prices_given_replenishment(replenishment_plan, future_demand) # 计算当前方案总利润 current_profit = evaluate_profit(optimized_prices, replenishment_plan, future_demand) if current_profit > best_profit: best_profit = current_profit best_solution = (optimized_prices.copy(), replenishment_plan.copy()) # 判断收敛 if profit_improvement < TOLERANCE: break initial_prices = optimized_prices # 5. 输出最终决策方案 output_decisions(best_solution[0], best_solution[1], 'outputs/decisions/final_plan.csv') # 6. 生成分析图表和报告 generate_report(best_solution, future_demand)

开发心得:一定要边写代码边验证。我们为每个模块都编写了单元测试,例如,检查库存平衡约束是否被满足,检查优化后的利润是否确实比简单规则高。使用pandas进行数据操作时,要时刻注意索引对齐,这是最常见的bug来源之一。另外,优化问题的求解时间可能很长,对于上百种商品、7天的规划期,迭代求解可能需要几分钟。在代码中关键位置添加日志和进度条(tqdm库)能极大提升调试体验。

7. 结果分析、模型评估与策略解读

模型跑出来了,但数字本身没有意义。关键在于如何解读结果,并评估其商业合理性。

7.1 评估指标

我们不仅看最终的总利润预测值,更关注一系列过程指标:

  • 财务指标:总利润、毛利率、收入、总成本(进货成本+损耗成本+持有成本)。
  • 库存指标:平均库存水平、库存周转天数、缺货率(需求大于库存的天数占比)、滞销率(期末库存大于某个阈值的商品占比)。
  • 运营指标:平均价格变动频率、平均折扣幅度、高损耗商品清单。

我们将优化模型的输出与基准策略(如经验补货+成本加成定价)进行对比。一个成功的优化模型应该能在利润上有显著提升(例如10%-20%),同时保持或改善库存和缺货指标。

7.2 策略解读与可视化

通过可视化,让决策变得可解释:

  1. 补货决策热力图:横轴是日期,纵轴是商品,颜色深浅表示补货量。一眼就能看出哪些商品需要持续补货,哪些是间歇性补货。
  2. 价格决策曲线:对于某个重点商品(如西红柿),画出其未来一周的建议价格曲线、库存水平曲线和预测销量曲线。分析价格如何随库存下降而上升,或在预测销量低迷时如何通过促销拉动需求。
  3. 利润贡献分析:计算每种商品对总利润的贡献度。你会发现可能80%的利润来自20%的商品(帕累托法则)。这指导我们在实际运营中,应对高利润商品给予更精细的管理。
  4. 敏感性分析图:用柱状图或折线图展示当预测误差(±10%, ±20%)时,总利润的波动范围。这给了决策者一个风险范围的直观感受。

7.3 模型局限性与改进方向

在论文中,坦诚地讨论模型的局限性是加分项。我们的模型主要有以下局限:

  • 需求预测误差:所有决策建立在预测之上,预测不准,优化就是“垃圾进,垃圾出”。我们通过提供预测区间和进行敏感性分析来部分应对。
  • 简化假设:我们假设需求函数是确定性的,且价格弹性是常数。现实中需求是随机的,弹性也可能随库存、竞争环境变化。
  • 单商品优化:模型虽然考虑了所有商品,但目标函数是简单加总,没有考虑商品之间的替代和互补效应(西红柿降价可能减少黄瓜的销量)。
  • 计算复杂度:对于大规模商品(成千上万种),我们的迭代算法可能太慢。在实际工业界,可能需要更高效的分布式优化算法。

可行的改进方向

  • 引入鲁棒优化随机规划,显式地考虑需求的不确定性,寻找最坏情况下的最优解(或期望最优解)。
  • 使用强化学习来建模这个序列决策问题。将状态定义为库存、价格、时间等,动作定义为补货量和定价,奖励定义为当日利润。让AI智能体通过与环境的交互来学习最优策略。
  • 引入联合需求模型,使用多元时间序列或图神经网络来建模商品之间的关联关系。

8. 参赛实战经验与避坑指南

最后,分享一些纯实战的、在技术文档和论文里不会写的经验。

1. 时间管理是生命线。三天三夜,时间分配建议:第一天下午+晚上:彻底读懂题目,完成数据探索和预处理方案设计。第二天全天:完成预测模型的构建、训练和初步评估。第三天上午:完成优化模型的构建和求解。第三天下午:结果分析、可视化、撰写论文初稿。第三天晚上至凌晨:反复修改论文,打磨摘要,检查模型假设和结果,定稿。绝对不要前松后紧,最后一天通宵写论文质量会惨不忍睹。

2. 论文写作高于模型复杂度。评委看论文的时间很短。一个清晰、逻辑自洽、图表美观的论文,比一个用了极其复杂模型但讲不清楚的论文,得分高得多。摘要一定要精炼,突出问题、方法、结果和亮点。模型部分,多用公式和流程图来说明。结果部分,多用对比图表。

3. 可视化是王道。一图胜千言。趋势图、热力图、柱状对比图、桑基图(展示库存流向)都能极大提升论文表现力。使用matplotlibseaborn绘图时,注意配色专业、坐标轴清晰、有图例和标题。

4. 一定要做敏感性分析。这是体现思维严谨性的关键。问问自己:如果我的预测偏差了20%,我的决策会失效吗?如果价格弹性估计不准,会有什么后果?把这些分析写进论文。

5. 代码要整洁,可复现。虽然不提交代码,但混乱的代码会影响你的调试效率。写好注释,使用函数封装,关键步骤输出中间结果到文件以便检查。

6. 团队协作要明确分工。最好一人主攻建模和算法(编程能力强),一人主攻论文写作和可视化(逻辑表达好),一人负责数据预处理、查资料和统筹协调。每天固定时间开会同步进度,阻塞问题及时提出一起解决。

7. 最后检查清单:

  • 摘要是否涵盖了问题、方法、结果、结论?
  • 模型假设是否合理且明确列出?
  • 所有公式符号是否有说明?
  • 图表是否都有编号和标题?
  • 参考文献格式是否统一?
  • 全文是否有明显的语法和拼写错误?

这道C题是一个绝佳的数据科学和运筹学实战案例。它教会我们的,不仅仅是如何使用Prophet或LightGBM,更是如何将一个模糊的商业问题,拆解成具体的数据问题,并通过建模、求解、评估的完整流程给出决策支持。这个过程,和你在互联网公司做一个定价策略产品,或者在供应链公司做一个库存优化系统,在方法论上是一脉相承的。希望这篇超详细的复盘,能帮你不仅赢得比赛,更赢得解决真实世界问题的思维和能力。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询