news 2026/5/23 8:19:02

从LIKE暴力匹配到LLM智能分类——遗留系统数据分析实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从LIKE暴力匹配到LLM智能分类——遗留系统数据分析实战

从LIKE暴力匹配到LLM智能分类——遗留系统数据分析实战

文章目录

  • 从LIKE暴力匹配到LLM智能分类——遗留系统数据分析实战
    • 一、故事的起点
    • 二、LIKE暴力分类:笨但能跑
    • 三、包厢利用率:把时间轴展开
    • 四、如果现在重做:用LLM做分类
      • 4.1 不需要7B,3B就够了
      • 4.2 处理边界情况
      • 4.3 效率对比
    • 五、教训与反思
    • 六、如果现在从头做

一、故事的起点

接手一个餐饮老系统的数据分析任务。打开数据库一看,流水表里几万条记录,只有商品名称、数量、单价、金额、日期。没有分类字段,没有品类标签。

老板说:帮我看一下,哪个品类贡献了最多营收?哪个单品卖得最好?

几千个不同的商品名称,人工分类?不现实。当时用的办法,说出来有点笨——用SQL的LIKE逐个品类往里套

二、LIKE暴力分类:笨但能跑

看看当时写的SQL(保留原样,包括那些注释掉的条件):

insertintocf_flselectdistinct'海鲜类'fl,namesfromcf_product awherenameslike'%虾%'andnamesnotlike'%虾片%'andnamesnotlike'%龙虾味%'

就这?对,就这。

每一类都是一轮"试错→排除→再试错"的循环。注释里密密麻麻全是被推翻的条件:

-- names like '%蟹%' and names not like '%蟹肉棒%'-- names like '%鸡%' and names not like '%鸡蛋%'-- names like '%牛%' and names not like '%牛油%' and names not like '%牛奶%'

为什么这么多排除条件?因为中餐菜名太坑了:

  • “蚂蚁上树”——没有蚂蚁,是肉末粉条(猪肉类)
  • “夫妻肺片”——没有夫妻,是牛肉牛杂(牛肉类)
  • “鱼香肉丝”——没有鱼,是猪肉类
  • “狮子头”——没有狮子,是猪肉类

LIKE只能做字面匹配,理解不了语义。每遇到一个误分类,就得加一个排除条件。几千条数据,光分类就调了两三天。

最终效果是这样的:逐条INSERT进分类表(cf_fl),两个字段——类别和商品名称。一个品类一个品类地攒出来的。

-- 查某个品类的销售情况selecta.names,count(*)as销售次数,sum(numbers)as销售总量,sum(costs)as总营收,max(price)as最高单价,min(price)as最低单价,round(sum(costs)/sum(numbers),2)as客单价fromcf_product a,cf_fl bwherea.names=b.namesandb.fl='海鲜类'groupbya.namesorderby4desc;

跑出来的结果很有意思:同一个菜,最高单价和最低单价能差出好几倍。比如一道"蒜蓉大虾",最高卖过128,最低卖过68。这不是折扣,是定价混乱——不同时段、不同服务员可能录入的价格不一样。这种问题,不分析数据根本发现不了。

三、包厢利用率:把时间轴展开

分类问题解决了,接下来是包厢分析。

数据长这样:

tablename(包厢号)btime(开台时间)etime(结账时间)billid(账单号)
3012022-01-08 18:302022-01-08 22:15XS-220108-0023
3022022-01-08 19:002022-01-09 01:30XS-220108-0025

老板想知道:每个包厢,每天每个小时,是空闲还是占用?

原始数据只有开台和结账时间,要变成"每小时的状态",得自己算。当时的做法——PL/SQL暴力遍历:

declaren_count number;ctimedate;date1date;hour1 number :=0;begindate1 :=to_date('20220101','yyyymmdd');while(date1<to_date('20220401','yyyymmdd'))loopforrecin(select*frombx)loophour1 :=0;while(hour1<24)loopctime :=date1+hour1/24;selectcount(*)inton_countfrombxlogwheretablename=rec.tablenameandctimebetweenbtimeandetime;ifn_count>0theninsertintobxrq(rq,tablename,chour,kyzt)values(date1,rec.tablename,hour1,'有客');elseinsertintobxrq(rq,tablename,chour,kyzt)values(date1,rec.tablename,hour1,'无客');endif;hour1 :=hour1+1;commit;endloop;endloop;date1 :=date1+1;endloop;end;

三个嵌套循环:90天 × 十几间包厢 × 24小时 = 三万多条记录。跑了好几分钟才出结果。

然后用Oracle的PIVOT展开成矩阵:

select*from(selecttablename,rq,chour,decode(kyzt,'无客',1,0)kyfromBXRQ)pivot(sum(ky)forchourin('0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23'))

1表示空闲,0表示占用。一张热力图就出来了。

再按星期汇总:

selectdecode(to_char(rq,'d'),'7','星期六','1','星期日','2','星期一','3','星期二','4','星期三','5','星期四','6','星期五')as星期,count(distinctrq)as总天数,sum(casewhenhour22=1andhour23=1andhour0=1then1else0end)as"晚10点后全满天数"frombxkygroupbydecode(to_char(rq,'d'),'7','星期六','1','星期日','2','星期一','3','星期二','4','星期三','5','星期四','6','星期五')orderby3;

结论很清晰:周六晚上10点到凌晨,几乎所有包厢都满员。周一到周五的下午,大面积空闲。

这不是什么高深的洞察,但它是真实的。不做数据分析,老板只能凭感觉说"周末忙、平时闲",但到底多忙、多闲,哪个时段最空,不知道。有了数据,决策才有依据——空闲时段推低价套餐引流,高峰时段提高最低消费门槛。

四、如果现在重做:用LLM做分类

时隔几年再看这些SQL,最想改的就是分类那块。

LIKE暴力匹配的痛点很明确:

  1. 维护成本高。每新增一个菜品,都要手动判断归哪类。新品上架了,分类表没更新,分析就漏了
  2. 误分类多。"鱼香肉丝"含"鱼"字但不是鱼类,"鸡蛋灌饼"含"鸡"字但不是禽类。排除条件越加越多,越来越难维护
  3. 无法泛化。换一个餐厅,菜名体系完全不同,所有条件得重写

现在用LLM,事情简单得多。

4.1 不需要7B,3B就够了

菜品分类是短文本分类任务:输入一个菜名(通常2-10个字),输出一个类别标签。这在NLP领域是最基础的任务之一。

方案适用场景本任务是否需要
BERT/TextCNN有标注数据,需训练够用,但要标注
LLM 1.5B-3B少样本,零训练最推荐
LLM 7B更复杂的推理杀鸡用牛刀
LLM 70B+通用任务完全没必要

用Qwen2.5-3B甚至更小的模型,本地部署,几行代码搞定:

importjsonfromopenaiimportOpenAI client=OpenAI(base_url="http://localhost:8080/v1",api_key="empty")categories=["海鲜类","牛肉类","猪肉类","禽类","蔬菜类","主食类","酒水类","饮品甜品类","其他"]defclassify_dish(name):resp=client.chat.completions.create(model="qwen2.5-3b",messages=[{"role":"system","content":f"你是餐饮菜品分类助手。将菜名归入以下类别之一:{', '.join(categories)}。只输出类别名称,不要解释。"},{"role":"user","content":f"菜名:{name}"}],temperature=0)returnresp.choices[0].message.content.strip()dishes=["蒜蓉大虾","蚂蚁上树","夫妻肺片","鱼香肉丝","狮子头","青岛啤酒","芒果布丁"]fordindishes:print(f"{d}->{classify_dish(d)}")

预期输出:

蒜蓉大虾 -> 海鲜类 蚂蚁上树 -> 猪肉类 夫妻肺片 -> 牛肉类 鱼香肉丝 -> 猪肉类 狮子头 -> 猪肉类 青岛啤酒 -> 酒水类 芒果布丁 -> 饮品甜品类

LIKE做不到的事,LLM轻松搞定。"蚂蚁上树"它知道是肉末粉条,不是虫类;"鱼香肉丝"它知道是川菜猪肉,不是鱼类。这是语义理解,不是字面匹配。

4.2 处理边界情况

当然,LLM也不是万能的。遇到本地特色菜、自创菜名,可能也会犯错。比如"九星招牌虾"——如果"九星"是店名,这应该归海鲜类,但LLM可能不确定。

解决方法:少样本提示(Few-shot)。在prompt里给几个例子:

messages=[{"role":"system","content":f"你是餐饮菜品分类助手。将菜名归入以下类别之一:{', '.join(categories)}。只输出类别名称。\n\n示例:\n九星招牌虾 -> 海鲜类\n秘制烤羊排 -> 牛羊肉类\n特色素三鲜 -> 蔬菜类"},{"role":"user","content":f"菜名:{name}"}]

加上几个本店特色菜的例子,准确率能从85%拉到95%以上。剩下5%的异常值,人工过一遍就行。

4.3 效率对比

维度LIKE暴力匹配LLM分类
开发时间2-3天(反复调条件)半天(写prompt+跑批)
新增菜品手动加条件自动分类
误分类率10%-15%(语义陷阱)< 5%(加few-shot后)
可迁移性换店重写改几个few-shot例子
维护成本持续累积排除条件基本无需维护

几千条数据,本地3B模型跑完不到半小时。加上人工抽查,整个过程半天搞定。当年用LIKE,光"虾"这个关键字就调了半天。

五、教训与反思

回头看这次数据分析,最大的教训不是技术选型,而是:遗留系统的数据质量,决定了分析的上限。

  • 菜名不统一:同一个菜,有的写"蒜蓉虾",有的写"蒜蓉大虾",有的写"蒜蓉开背虾"。LIKE和LLM都会把它们当成不同的菜
  • 价格混乱:同菜不同价,可能是时段定价,也可能是录错了
  • 包厢数据有脏数据:有些记录的开台时间晚于结账时间,PL/SQL遍历时会漏算

这些问题不是SQL或LLM能解决的,需要在数据采集端就做好规范化。

第二个体会是:数据分析的价值不在于技术多酷,而在于能不能回答业务问题。

包厢分析那段PL/SQL,写法很丑——三层嵌套循环,逐行INSERT,跑好几分钟。但跑出来的那张热力图,直接告诉老板"周六晚10点以后一房难求,周三下午全空"。这个结论值多少钱?如果据此调整定价策略,一个月多出来的营收可能覆盖整个数据分析的成本。

LIKE分类也很丑。但当年没有更好的工具,它解决了问题。现在有了LLM,同样的任务可以用更优雅的方式做。技术的进步就在于此:不是让不可能变成可能,而是让可能的成本从三天降到半天。

六、如果现在从头做

我会这样设计:

  1. 分类:LLM(3B本地模型),自动分类 + 人工抽查异常值
  2. 包厢分析:不再用PL/SQL暴力遍历,改用SQL窗口函数或Python pandas处理时间区间
  3. 可视化:包厢热力图直接用Python(matplotlib/seaborn),不用在数据库里PIVOT
  4. 数据清洗:先用LLM做菜名标准化("蒜蓉虾"和"蒜蓉大虾"合并为同一道菜),再分类

技术栈从"Oracle + 纯SQL"变成"Python + LLM + 可视化工具",但分析思路是一样的:先解决分类问题,再做聚合统计,最后回答业务问题。

工具换了,方法论没变。这大概就是数据分析的本质。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/23 8:11:31

AI写论文的高效秘籍!4款AI论文写作工具,让写论文变得更简单!

AI论文写作工具推荐 是不是还在为写期刊论文而烦恼不已&#xff1f;面对无尽的文献资料、繁琐的格式要求和不断的修改&#xff0c;许多学术人员都感到写作效率低下。别担心&#xff0c;今天我为大家带来了四款经过实测的AI论文写作工具&#xff0c;它们将从论文文献检索、论文…

作者头像 李华
网站建设 2026/5/23 8:05:44

Keil C51函数指针调用中的递归警告解析与优化

1. 问题现象解析在Keil C51开发环境中&#xff0c;当使用函数指针调用包含字符串常量的函数时&#xff0c;BL51链接器会抛出"Warning L13: Recursive Call to Segment"警告。这个看似晦涩的警告实际上揭示了嵌入式C编程中一个重要的内存管理机制。让我们通过一个典型…

作者头像 李华
网站建设 2026/5/23 8:02:45

C++智能指针实例详解

由于 C 语言没有自动内存回收机制&#xff0c;程序员每次 new 出来的内存都要手动 delete。程序员忘记 delete&#xff0c;流程太复杂&#xff0c;最终导致没有 delete&#xff0c;异常导致程序过早退出&#xff0c;没有执行 delete 的情况并不罕见。 用智能指针便可以有效缓解…

作者头像 李华