简介:这是一份面向中小连锁超市运营管理与数据分析从业者的深度学习需求预测方案,聚焦如何基于DeepSeek模型优化零售库存决策。文档系统梳理了传统库存管理在需求预测不准确、缺乏灵活性、信息传递不及时等方面的局限,并给出从数据收集清洗、特征提取、模型搭建训练到评估验证的完整路径,尤其对安全库存确定、商品陈列与促销规划等实际场景有细化讲解。资源仅包含一个PDF文件,压缩包整体大小约1.61MB,文档共30页,目录结构清晰、图文完整,便于按章节研读。文档还重点解析了损失函数监控、过拟合与欠拟合处理、评估指标选择与交叉验证等调优细节,强调了数据质量对预测效果的决定性影响,并由实际应用案例展示补货决策与销售业绩的改善。目前已有98人学习下载,适合具备一定数据分析基础、希望借助深度学习提升超市运营效率的读者参考。
1. 库存预测落不了地的锅,不能都让算法背
中小连锁超市最典型的库存困境是“怕缺货就多下单”:生鲜、日配这类短保商品,采购员手上没有需求数字,只能按上周销量再乘一个经验系数,结果畅销 SKU 缺货、滞销 SKU 积压同时存在。需求预测不是新概念,但大多数门店连一张按“门店+SKU+日期”整理的销售明细都没有,模型自然无从落地。
这篇内容讲一条能跑的路径:用 DeepSeek 的 API(或内网本地部署)做未来 7 天需求预测,预测结果再换算成建议订货量,落到补货流程里。适合没有专职数据团队的中小连锁超市,也适合系统开发、运营、采购侧的同学判断这个方向值不值得自己搭一遍。
2. 做 DeepSeek 需求预测前,先把销售数据垫成“门店+SKU+日”宽表
2.1 数据不是“库存表”,而是一张干净的销售事实表
一上来就把 POS 系统的数据拿来喂给模型,第一道坎就是粒度混乱:有的门店一天导出一次,有的是订单行级,还有的把退货款算成负数混在销售里。常见做法是先落一张最朴素的销售事实表,五个字段够用:
store_id, sku_id, date, qty, amount
有几个字段容易漏。门店编码要能对应到面积、商圈类型,后面做特征才有意义;SKU 要有品类、包装规格,否则生鲜和饮料混在一起做预测,模型很难学出规律。如果原来的商品档案里有“是否赠品”“是否烟草”这类字段,先做一轮筛选,把不可预测的 SKU 摘出来。
2.2 生成滞后期与星期特征,先别急着上深度学习
DeepSeek 这类模型做预测,依赖的是你在提示词里给它的上下文。因此把“历史长什么样”编成数字特征,比模型本身更重要。我一般会先做下面这几列:
import pandas as pd sales = pd.read_csv("sales_fact.csv", parse_dates=["date"]) sales = ( sales.groupby(["store_id", "sku_id", "date"], as_index=False)["qty"] .sum() ) sales = sales.sort_values(["store_id", "sku_id", "date"]).reset_index(drop=True) g = sales.groupby(["store_id", "sku_id"])["qty"] sales["lag1"] = g.shift(1) # 昨天卖了多少 sales["lag7"] = g.shift(7) # 上个星期同期 sales["ma7"] = g.transform( lambda x: x.shift(1).rolling(7).mean() ) # 近7天均值,排除当天 sales["ma28"] = g.transform( lambda x: x.shift(1).rolling(28).mean() ) # 近28天均值 sales["dow"] = sales["date"].dt.dayofweek # 周一=0,周日=6这里的核心其实只有一列销量,加的都是时间特征。shift 的作用是让模型在看“今天”时不知道今天实际销量,只能依赖昨天的值(lag1)和上周同期的值(lag7),避免模型看到答案。ma7、ma28 表达最近销售走势的平滑趋势。dow 是星期几,周末型商品的规律基本靠它体现。
对于中小连锁超市,几十个门店、几千个 SKU 的量完全用不着分布式计算,pandas 就能跑完特征生产。注意g.shift(1)在 groupby 之后仍然保持按门店和 SKU 分组位移,不会出现跨门店错位。
2.3 促销与节假日:对需求预测影响最大的两个变量
如果在提示词里只给历史销量,模型无法解释“上周六突然多卖 50 件是因为买一送一,还是因为节假日”。所以要在特征里明确告诉模型这两类事件。
常见做法是维护一张促销计划表,然后按“门店+SKU+日期”展开成逐日标记:
promo = pd.DataFrame({ "store_id": ["S001", "S001"], "sku_id": ["SKU1001", "SKU1001"], "start_date": ["2025-07-12", "2025-08-01"], "end_date": ["2025-07-14", "2025-08-03"], }) promo["start_date"] = pd.to_datetime(promo["start_date"]) promo["end_date"] = pd.to_datetime(promo["end_date"]) rows = promo.apply( lambda r: pd.DataFrame({ "store_id": r["store_id"], "sku_id": r["sku_id"], "date": pd.date_range(r["start_date"], r["end_date"]), }), axis=1, ) promo_daily = pd.concat(rows.tolist(), ignore_index=True) promo_daily["is_promo"] = 1展开后的 promo_daily 再按 store_id、sku_id、date 合并回销售表,就把“当天有没有促销”变成 0/1 列。节假日可以不用写死在代码里,直接用chinese_calendar这类库生成,或者在数据库中维护一张节假日日历表更直白。未来 7 天是否促销、是否节假日、星期几,要跟着历史特征一起写进提示词,后面第 4 章的调优才有意义。
3. 调通 DeepSeek 预测接口:Prompt 模板、temperature 与 JSON 返回
3.1 安装与第一步请求:用 OpenAI 兼容 SDK 就够了
DeepSeek 的 API 兼容 OpenAI 的接口格式,不需要额外引入专用 SDK。装一个 openai 库,把 base_url 指过去就行:
pip install openai export DEEPSEEK_API_KEY="sk-..."from openai import OpenAI client = OpenAI( api_key="your_api_key", # 生产环境从环境变量读取,不要写死 base_url="https://api.deepseek.com/v1" ) resp = client.chat.completions.create( model="deepseek-chat", messages=[ {"role": "system", "content": "你是零售需求预测助手,输出 JSON。"}, {"role": "user", "content": "请预测未来 7 天销量。"} ], temperature=0.2, response_format={"type": "json_object"} ) print(resp.choices[0].message.content)这里关键的两个参数是 model 和 temperature。model 常见选择是deepseek-chat(普通对话,响应快)和deepseek-reasoner(带思考过程,适合复杂推理),具体可用模型名以官网当前文档为准。temperature 控制随机性,0.1~0.3 适合预测任务;取值太高会出现同一天预测值忽高忽低,短期销量预测我一般固定 0.2 上下。
response_format是让模型强制输出 JSON 的开关。没有它,模型经常在数字后面附带“这个预测是基于……”之类的解释,解析会很痛苦。对数据任务,这个参数基本属于必开。
提示:如果某些模型不支持
response_format,退而求其次是在提示词里写死“只输出 JSON,不要输出解释”,并在解析层做容错重试。
如果门店数据不能出内网,也可以把这套 prompt 放到本地部署的 DeepSeek 服务上,base_url换成内网地址即可,业务代码不用改。
3.2 提示词模板:把历史特征当成上下文
让模型做预测比让模型写诗简单得多,前提是把问题描述清楚。我会固定一个四段式模板,每次都复用同一套:角色与任务、特征说明、历史序列、输出要求。
def build_prompt(row, forecast_days=7): return f""" 你是中小连锁超市的需求预测助手。根据下面给出的 28 天日销售数据, 预测该门店该 SKU 未来 {forecast_days} 天每天的销量。 历史四周数据(星期、是否促销、是否节假日、销量): {row["history"]} 输出要求: 预测未来 {forecast_days} 天的每日销量,格式为 JSON。 字段名依次为 date, forecast, confidence_low, confidence_high。 不要输出其他文字。 """提示词里注意几点。历史序列只保留四周与相关标签,太长模型容易被噪音干扰;不要混入“每周总销量”“环比增长”这类由模型去理解的字段。confidence_low 和 confidence_high 是让模型在拿不准时用区间表达不确定性,后面算安全库存会用到。
3.3 拿到响应后的校验:先做类型检查,再进预测表
接口返回的结构是resp.choices[0].message.content,但模型即使开了 JSON 模式,也可能返回空字符串或字段缺失。需要包一层解析和校验函数:
import json def parse_forecast(content: str) -> dict: payload = json.loads(content) assert "forecast" in payload, "missing forecast" assert isinstance(payload["forecast"], list), "forecast must be list" assert len(payload["forecast"]) == 7, "expected 7 days" return payload这段代码的核心是“宁可这轮失败重试,也不要让脏数据流进库存系统”。模型输出的小数保留两位;如果某个 SKU 预测出负数,直接截断成 0,而不是报错,因为补货环节天然不接受负需求。
| temperature 取值 | 适用场景 | 特点 |
|---|---|---|
| 0 ~ 0.2 | 短期销量预测、库存补货 | 确定性高,多次运行结果接近 |
| 0.3 ~ 0.7 | 生成多版本预测做集成 | 结果有波动,需要多次采样取均值 |
| 0.8 以上 | 头脑风暴、探索性分析 | 一般不用于生产预测 |
4. 用 MAPE / WAPE 评估 DeepSeek 预测,先分快慢品再调 prompt
4.1 预测结果要与真实值对齐
DeepSeek 每轮预测的是“未来 7 天”,但到第 8 天,真实销售数据已经出来了。上线时最好把每次模型预测落一张表,字段包含 store_id、sku_id、date、pred_qty、pred_low、pred_high、model_version、created_at,等真实数据出来后再合并:
pred = pd.read_csv("deepseek_forecast.csv") actual = pd.read_csv("sales_fact.csv", usecols=["store_id", "sku_id", "date", "qty"]) merged = pred.merge( actual, on=["store_id", "sku_id", "date"], suffixes=("_pred", "_act") ) merged["abs_err"] = (merged["qty_act"] - merged["qty_pred"]).abs()merge 的结果就是后面计算指标的唯一依据。这里不需要复杂的时序对齐逻辑,只要保证两张表的日期格式一致,并且 actual 只取当天已落库的真实销量。
4.2 指标口径:不要只看 MAPE,也要看 WAPE 和 BIAS
| 指标 | 计算方式 | 说明 |
|---|---|---|
| MAPE | mean(abs_err / qty_act) × 100% | 容易被日销 0/1 件的慢动品带偏 |
| WAPE | sum(abs_err) / sum(qty_act) × 100% | 按销量加权,整体偏差看得准 |
| BIAS | sum(qty_act - qty_pred) / sum(qty_act) × 100% | 正值代表系统性低估,负值代表高估 |
total_act = merged["qty_act"].sum() wape = merged["abs_err"].sum() / total_act * 100 bias = (merged["qty_act"] - merged["qty_pred"]).sum() / total_act * 100注意这里 qty_act 可能为 0,MAPE 会被除零干扰,所以慢动品和快动品必须分开评估。日常销量大于 5 件的 SKU 用 WAPE 做口径;销量经常只有 1 件的 SKU,直接看区间覆盖比看百分比更有意义。
4.3 DeepSeek 预测调参的关键:few-shot 与上下文修剪
第一轮 WAPE 在 30% 以上时,不需要急着换模型,先检查上下文。两个前提条件:第一,历史特征是否用了 4 周以上;第二,提示词里有没有说明预测日是否为促销日。在此基础上,可以做 few-shot 调整,把过去一周的真实序列放进提示词:
few_shot = """ 最近7天实际销售: 周一 2件,周二 1件,周三 3件,周四 4件, 周五 6件(促销),周六 8件,周日 5件。 """模型会照着“周五促销销量约等于平时的 1.5 倍”这类规律推断未来。如果预测仍然明显偏差,就用 0.5 的 temperature 跑三到五次取中位数,压掉随机噪声。中小超市的预测任务请求量不大,成本完全在可接受范围内。
5. 把预测接进补货:建议订货量计算与回测脚本
5.1 从日预测换算成建议订货量
预测值本身不能直接发给门店,要转成“建议订货量”,公式如下:
建议订货量 = max(0, ceil((日均预测销量 × (到货天数 + 下单周期) + 安全库存 − 在库 − 在途) / 包装批量) × 包装批量)
import math def reorder_qty(pred_qty, lead_time=2, review_period=1, safety_stock=3, on_hand=10, on_order=0, batch_size=6): demand = pred_qty * (lead_time + review_period) need = demand + safety_stock - on_hand - on_order if need <= 0: return 0 return math.ceil(need / batch_size) * batch_size注意 pred_qty 是日平均预测销量,不是 7 天总量。lead_time 是从下单到到货的天数;review_period 是盘点补货间隔,比如每周日补货一次,这个值就是 7,不是 1。on_order 是已经在途的量,不扣掉会重复下单。安全库存可以用 95% 服务水平对应的 z=1.65,乘以近 28 天日销量的标准差,再乘以下单周期加采购提前期总天数的平方根:
import math safety_stock = round(1.65 * std_daily_qty * math.sqrt(lead_time + review_period))这个公式比采购员“多备三箱”要稳定得多。
5.2 回测脚本:看缺货率,而不是只看误差
预测误差降下来,不代表补货结果好。更直接的验证方法是回测:用过去 4 周的预测值,配上同样的补货参数,模拟每天库存变化,统计缺货次数。没有完整的库存模拟模型时,可以用一个粗略的代理指标——“预测是否覆盖实际”:
merged["hit"] = (merged["qty_pred"] >= merged["qty_act"]).astype(int) oos_rate = 1 - merged["hit"].mean()如果 oos_rate 高于 8%,说明安全库存设小了;低于 2%,说明库存积压风险高,可以适当下调补货系数。
5.3 一个能直接上手的技巧:用模型的置信区间调安全库存
最后补充一个立刻能落地的小技巧:不用统一安全库存,而是让 DeepSeek 在输出预测时同时输出 confidence_low 和 confidence_high,然后取区间宽度作为安全库存的调整量。
预测波动大的 SKU,比如生鲜、促销品,区间自然宽;稳定商品区间窄。这样安全库存会自动向高风险 SKU 倾斜,比所有 SKU 用同一个系数更合理。我在实践中把区间宽度乘以 0.3 再叠加到基本安全库存上,效果比统一调整参数来得直接。
本文还有配套的精品资源,点击获取