☰
GSP算法详解:从候选生成到剪枝的完整序列模式挖掘实战
2026/10/19 6:04:02 网站建设 项目流程

拿到这个项目标题,我第一反应就是:终于有人写GSP了。这个算法在“数据挖掘”这堂课上几乎是必讲内容,但真正把它嚼碎了讲透的教程不多。序列模式挖掘稍不留神就容易讲成一堆数学符号的堆砌,初学者看完只会背三个关键词:支持度、候选生成、剪枝,一上手还是懵。所以我打算换个讲法,用一个具体到能用手算的电商购物场景,把GSP从第一轮到最后一轮完整走一遍,顺便把那些书本上不会写明的坑也一并交代清楚。

这个内容不是什么炫技黑科技,但它是理解更高级序列挖掘算法(比如PrefixSpan、SPADE)的基石。适合正在啃数据挖掘教材的学生、准备算法面试的求职者,以及实际业务里要分析用户行为路径但不知道从何下手的从业者。读完你可以做到两件事:第一,能用人话讲清楚GSP干了什么;第二,能照着我给的示例数据,在纸上完整复现整个挖掘过程,包括候选是怎么生成的、剪枝剪掉了什么、支持度是怎么数出来的。

1. 先从“买完A再买B”说起:序列模式挖掘到底在挖什么

1.1 关联规则与序列模式的本质区别

学过数据挖掘的人对Apriori肯定不陌生,它解决的是“一个订单里同时买了哪些商品”的问题。比如“啤酒和尿布”这个故事,本质上是一个项集内部的共现关系:在同一张购物小票上,啤酒和尿布经常同时出现。注意,这里假设所有商品都是同一时刻发生的,事务之间的先后顺序被完全抹掉了。

但真实业务里大量问题其实带着鲜明的时间顺序。用户浏览一个网页,是先看首页还是先看详情页;用户购买商品,是先加购还是先直接下单;用户开通服务,是先试用还是先付费。这些行为天然是排好队的序列。序列模式挖掘干的事情,就是从一大堆这样的行为序列里,找出高频出现的、有先后顺序的子序列。

关联规则里的“啤酒→尿布”,表达的是“同一时刻啤酒和尿布一起出现”。序列模式里的规则可以表达为“买了啤酒的用户,在接下来的三天内又去买了尿布”,这两者信息量完全不一样。前者只能告诉你“这两个东西经常同框”,后者能告诉你“先发生了A,再发生了B”,这才是真正的行为路径。

1.2 序列模式的应用场景和业务价值

说到应用场景,序列模式挖掘在业界几乎是无处不在。电商平台做用户路径分析时,要看用户从搜索页到商品详情页再到下单页的转化路径,哪条路径最通畅、哪条路径最容易流失。这在本质上就是在一大堆会话序列里找高频率的序列子串。内容平台做推荐时,用户在同一个Session里的阅读顺序也构成序列,把高频阅读序列挖掘出来,就能针对性地优化内容推荐顺序。甚至在医疗领域,病人症状的出现顺序、银行里可疑交易的先后顺序,都可以用序列模式挖掘来发现规律。

序列模式挖掘的输出,不是一条条孤立的“A->B”规则,而是一棵包含不同长度、不同细节层次的序列模式树。有了这棵树,业务方可以挑出与业务目标相关的模式,做针对性优化。

2. GSP算法核心原理:从Apriori到序列化

2.1 一些必须提前约定好的概念

在进入GSP的流程之前,先把基础概念讲清楚。这里很多术语看起来跟Apriori很像,但概念上有微妙区别,理解错一个后面全乱。

项(Item)是最基础的元素,比如一个商品、一个页面。项集(Itemset)是多个项的集合,在GSP里面用花括号表示,如{b,c}表示b和c出现在同一个事务中,它们之间没有先后之分。序列(Sequence)是多个项集的排列,如<{a},{b,c},{d}>表示先发生a,然后b和c同时发生,最后d发生。注意,这跟<{a},{b},{c},{d}>是两码事,后者表示b和c是两个独立事务,前者表示b和c挤在一个事务里。

这里标注一下序列的“粒度”:无论是<{b,c}>还是<{b},{c}>,只要项的个数为2,都被称为2-序列。GSP里的k-序列指的是“包含k个项的序列”,而不是“包含k个事务的序列”,这个口径很容易被忽略,但在候选生成时特别要紧。

支持度(Support)的定义是:整个数据库中有多少条序列“包含”这个子序列。什么叫包含?以序列S=<{a},{b,c},{d}>举例,它包含<{a},{c}>吗?包含,因为a在第1个位置,c在第2个位置(虽然c旁边还有b,但c是存在的,而且出现在a之后)。它包含<{b},{c}>吗?不包含,因为b和c位于同一个项集内部,两者的时间戳相同,不存在“b先于c”的先后关系。包含关系的判断,是GSP实现中最容易出错的地方,后文我会专门说。

2.2 GSP的四阶段主循环

GSP的全称是Generalized Sequential Patterns,翻译过来就是“通用序列模式”,它由Apriori算法的作者在1996年提出。你可以把GSP理解成“Apriori的序列升级版”,它保留了Apriori最核心的逐层搜索思想,把操作对象从“项集”换成了“序列”。

GSP的完整流程分四步:

第一,扫描全库,统计所有只包含单个项的1-序列的出现次数,把支持度不低于阈值min_sup的1-序列挑出来,构成频繁1-序列集合L1。这是整个迭代的地基。

第二,进入候选生成阶段。基于上一轮得到的频繁(k-1)-序列集合L(k-1),用自连接的方式生成候选k-序列集合Ck。候选集是“可能频繁”的序列集合,是下一轮统计支持度的对象。

第三,剪枝。对Ck中的每一个候选序列,检查它的每一个“真子序列”(即删掉一个项后得到的(k-1)-序列)是否都在L(k-1)里。只要有一个真子序列不频繁,这个候选序列就可以直接扔掉。这个操作的合法性来自支持度的反单调性:一个序列的支持度不可能大于它的任何一个子序列的支持度。子序列都不频繁,超序列必然不频繁。

第四,再次扫描数据库,统计Ck中所有候选序列的真实支持度,保留不低于min_sup的候选,构成频繁k-序列集合Lk。

循环执行上述四步,直到Lk为空,或者Lk里的序列长度达到用户设定的上限。整个过程中,每一轮都要完整扫描一次数据库,这是GSP最大的性能瓶颈,也是后文选型时要重点权衡的地方。

2.3 候选生成:GSP理解起来最费劲的环节

候选生成是整个GSP里看起来最绕的部分,我还是放慢速度讲透。

它的目标很简单:已知所有频繁(k-1)-序列,想生成所有可能的频繁k-序列的候选。做法借鉴Apriori的自连接,但因为是序列,连接时的处理比Apriori多一些分支。

具体来说,取两个频繁(k-1)-序列S1和S2。先各自丢掉一个项,让它们能够对接上。用官方一点的说法:如果S1去掉第一个项后得到的序列,恰好等于S2去掉最后一个项后得到的序列,那这两个序列就可以拼接成一个新的k-序列。拼接的方式取决于S1的最后一个项和S2的第一个项分别落在哪个位置:

第一种情况是S1和S2的最后一个项、第一个项都是独立的单个项(孤零零地占一个事务),这时把S2的最后一个项追加到S1的队尾,生成一个新的k-序列,这个新项自己占一个新的事务位置。比如<{a},{b}>和<{b},{c}>拼成<{a},{b},{c}>。

第二种情况是S1的最后一个项与S2的第一个项中,至少有一个位于一个多项项集内部。这时拼接规则就要把项并入原有的项集。比如<{a},{b,c}>和<{b,c},{d}>拼接时,可能生成<{a},{b,c,d}>这样包含四项项集的序列。实际操作中这一块最容易写错,所以大多数教学实现通常只处理单项拼接,把多项项集的情况交由剪枝和计数去兜底。

2.4 反什么单调性?可着这一条定理使劲儿

剪枝为什么成立,值得单独拎出来说一句。支持度是单调递减的:一个序列的支持度不会超过它自身任何一个子序列的支持度。如果你挖出一个5-序列是频繁的,那它蕴含的所有1-序列、2-序列一直到4-序列都频繁。反过来看,如果某个候选5-序列里存在一个不频繁的2-子序列,那这个5-序列的支持度不可能达到阈值,直接删除毫无风险。

这条性质是Apriori家族的立身之本。GSP继承了它,所以能用“剪枝”这一招提前砍掉大量候选,否则每一轮的支持度统计会直接把性能拖垮。

3. 手把手跑通一个完整例子:四次序列,三轮迭代

3.1 构造一个小到能心算的数据集

理论讲再多不如手算一遍,我们构造一个小型数据库D。为了便于通用理解,这里用字母a、b、c、d代表行为事件,实际业务里你可以把a理解为“访问首页”、b理解为“搜索商品”、c理解为“加入购物车”、d理解为“支付成功”之类的任何业务动作。

假设数据库里有4条序列,这是我们所有统计的来源:

序列ID事务1事务2事务3
S1{a}{b}{c}
S2{a}{b}{d}
S3{a}{b,c}{d}
S4{a}{c}{d}

事先说明一个核心规则:一条序列无论里面匹配到多少次模式,在统计支持度时都只算一次。比如S3=<{a},{b,c},{d}>里同时存在a→b和a→c,但S3对<{a},{b}>和<{a},{c}>的支持度贡献都是1次,对<{a},{b,c}>的贡献也是1次,互相不重复计算。

设定最小支持度阈值min_sup=2,也就是说一个模式至少要在2条不同序列里出现,才算频繁。

3.2 第1轮:得到频繁1-序列L1

第一次扫描数据库,逐一检查只有单个项的序列出现次数。这里统计的是“这个项在几条序列里出现过”,而不是“这个项总共出现了几次”。为什么?因为支持度的计数单位是序列条数,不是项的出现次数。同一序列里出现两次a也只算一次。

统计结果:

1-序列出现序列支持度判断
<{a}>S1,S2,S3,S44频繁
<{b}>S1,S2,S33频繁
<{c}>S1,S3,S43频繁
<{d}>S2,S3,S43频繁

得到L1={<{a}>, <{b}>, <{c}>, <{d}>}。到这里我们已知:a单独出现在所有4条序列中,b、c、d各出现在3条序列中。这为下一步提供了全部“素材”。

3.3 第2轮:候选2-序列的生成与剪枝

从L1自连接生成候选2-序列C2。2-序列有两种形态:一种是两个单项分处两个事务,比如<{a},{b}>;另一种是两个项处于同一个事务,比如<{a,b}>。所以C2的总量不是简单的4×4,而是跨事务的组合加上事务内的组合。

按Apriori家族的规则,跨事务型候选有所有有序对,共4×4=16个;事务内型候选则是无重复的项集组合,共C(4,2)=6个。剪枝阶段检查每个候选2-序列的1-子序列是否都在L1里。因为L1包含了全部4个单项,所以这里的剪枝不会剪掉任何候选,C2保持22个。

然后扫描数据库,统计每个候选的支持度。这里我就把完全不可能出现的候选直接略过,在真实代码里依然会在遍历时全部统计,只不过结果为零的都被剪枝逻辑处理掉了。列一下有正数支持度的候选:

候选2-序列S1是否包含S2是否包含S3是否包含S4是否包含支持度判断
<{a},{b}>是是是否3频繁
<{a},{c}>是否是是3频繁
<{a},{d}>否是是是3频繁
<{b},{d}>否是是否2频繁
<{c},{d}>否否是是2频繁
<{b},{c}>是否否否1剪枝
<{a,b}>否否否否0剪枝

剩下那些<{b},{a}>、<{c},{a}>、<{d},{a}>之类全部是0,直接扔掉。得到的L2={<{a},{b}>, <{a},{c}>, <{a},{d}>, <{b},{d}>, <{c},{d}>}。

这里重点看两个细节,第一个是<{b},{c}>:S1里的顺序是a→b→c,b确实在c前面,所以S1包含<{b},{c}>。但S3里b和c挤在同一个事务里,同一事务内的两个项没有先后顺序,所以S3不包含<{b},{c}>。很多初写者在这里把S3也算进去,导致支持度错算成2,这是典型的“同一项集不等于序列先后”的坑,后文会再次强调。

第二个看<{a,b}>这个同事务型候选:整个数据库里没有任何一个事务同时包含a和b,所以支持度是0。这也是GSP和Apriori在情感上最像的地方,Apriori挖的是频繁项集,GSP在某一轮里也会顺带挖出一些同事务的组合,但要是同事务组合支持度不高,它跟跨事务序列一样会被淘汰。

3.4 第3轮:候选3-序列的生成、剪枝与计数

现在L2里有5个频繁2-序列。生成候选3-序列时,我们拿L2里的序列两两连接,连接条件是S1去掉第一个项后的序列等于S2去掉最后一个项后的序列。

具体看<{a},{b}>和<{b},{d}>:S1去掉第一个项<{a}>得到<{b}>,S2去掉最后一个项<{d}>也得到<{b}>,两者吻合,拼接成<{a},{b},{d}>。同理,<{a},{c}>和<{c},{d}>拼成<{a},{c},{d}>。其他组合如<{a},{b}>和<{c},{d}>,去掉首尾后分别得到<{b}>和<{c}>,对不上,不能拼接。

所以候选3-序列只有两个:<{a},{b},{d}>和<{a},{c},{d}>。

剪枝阶段开始发挥作用。检查<{a},{b},{d}>的所有2-真子序列:<{a},{b}>频繁、<{a},{d}>频繁、<{b},{d}>频繁,三个都在L2里,通过。检查<{a},{c},{d}>:<{a},{c}>频繁、<{a},{d}>频繁、<{c},{d}>频繁,也全部通过。如果某个候选的2-子序列中任意一个不在L2里,这个候选会在进扫描前就被扔出去,避免浪费一次全库扫描。

随后再次扫描数据库,统计这两个候选的支持度:

对<{a},{b},{d}>,S2从a到b再到d,包含;S3也从a到b再到d,包含;S1没有d,S4没有b,所以支持度是2,刚好压线,频繁。

对<{a},{c},{d}>,S3从a到c再到d,包含;S4从a到c再到d,包含;S1没有d,S2没有c,所以支持度也是2,频繁。

L3={<{a},{b},{d}>, <{a},{c},{d}>}。

3.5 第4轮:算法终止与最终结果

继续尝试生成候选4-序列。拿两个3-序列做连接,<{a},{b},{d}>去掉首项得到<{b},{d}>,<{a},{c},{d}>去掉末项得到<{a},{c}>,两者不相等,无法连接。候选4-序列为空,算法在这里自然终止。

最终算法输出的所有频繁序列是这些:

长度频繁序列
频繁1-序列<{a}>、<{b}>、<{c}>、<{d}>
频繁2-序列<{a},{b}>、<{a},{c}>、<{a},{d}>、<{b},{d}>、<{c},{d}>
频繁3-序列<{a},{b},{d}>、<{a},{c},{d}>

这个结果能给出很直观的业务提示:所有用户的行为都从a开始,然后要么经过b、要么经过c,最终走到d。如果a代表进入首页、b代表浏览商品列表、c代表直接搜索、d代表下单,那么挖掘结果说明存在“首页→商品列表→下单”和“首页→搜索→下单”两条主流转化路径。中间偶尔有人走“商品列表→搜索”这种绕路行为,但因为支持度只有1,被阈值过滤掉了。

3.6 换个角度验证:支持度计数时必须坚持的“一序列一票”

我在实战里吃过一个暗亏,在这里提前说出来。当一条序列里匹配到多次模式时,支持度只加1,不能加多次。举个极端例子,如果某条序列是<a,a,a>,而我们在统计<{a},{a}>的支持度,这条序列里其实存在两种匹配方法,分别拿第1个a和第2个a配对、拿第2个a和第3个a配对。但在GSP的定义下,这条序列仍然只贡献1的支持度,因为我们统计的是“包含该模式的序列条数”,不是“该模式在所有序列中捻到的次数”。

这个规则在数据量小的时候看不出差别,一旦数据量大、单序列又很长,如果实现错误,计数会变成正常值的数倍,最后挖掘出的模式失真得离谱。我在早期实现里就吃过这个亏,那时候一个模式的支持度几乎翻了一倍,排查了很久才发现统计口径错了。

4. 实战要点:从纸上谈兵到跑真实数据

4.1 最小支持度阈值怎么定

GSP里最让人头疼的超参数就是min_sup。调太小,候选序列数量爆炸,跑一轮要几十分钟;调太大,什么模式都挖不出来,输出结果干瘪得可怜。

我的经验是先站在业务角度估算。比如电商平台有10万条用户会话序列,如果只对覆盖超过500条会话的行为模式感兴趣,那min_sup就是500,对应相对支持度0.5%。这个值没有标准答案,得结合业务对“罕见但重要”的定义来定。做异常检测时可能希望挖一些超低频模式,阈值就要压得很低;做主流路径分析时阈值可以适度提高。

实际操作中我习惯先跑一个较高的阈值(比如5%),迅速看一轮结果找到感觉,再逐步降低阈值观察候选数量增长曲线。如果从2%降到1.5%导致候选数量翻了十倍,就要警惕计算资源是否跟得上,必要时混用抽样策略做初步探索。

4.2 数据预处理:把原始日志整理成干净的序列

真实的原始数据极少是现成的序列格式。用户在同一个页面上产生一堆点击事件,有些点击是无效的、有些是重复的,必须先做清理。以网页日志为例,一条会话序列通常需要按“用户ID + 会话ID + 时间戳”分组,把同一个用户在同一个会话里的行为按时间排序,压缩掉重复点击,再决定是否要按时间窗口切分成更细粒度的序列。

这里有一个经常被忽略的问题:时间窗口的粒度。同一个用户连续半小时的行为要合成一条序列,还是要按30分钟窗口拆成多条?这没有标准答案,要看业务场景。做路径分析时,两个行为隔了3小时可能已经不算同一段路径了;做长期购买行为分析时,跨天的序列反而更有意义。GSP本身不感知时间间隔,它只看到先后顺序,所以时间语义一定要在预处理阶段处理好。

4.3 实现层面的三个加速思路

实现GSP要比想象中容易写出“正确但慢到跑不动”的版本。如果只在课程作业里跑几千条数据倒无所谓,一旦面对真实数据,下面三个点很值得关注。

第一个是候选的存储结构。我强烈建议用哈希树(hash tree)来存储候选序列,这跟Apriori的优化思路一脉相承。在支持度计数阶段,把候选序列存入哈希树,遍历数据库里的每一条序列时,只需要在哈希树上做快速查找,而不是把这条序列跟所有候选做一次完整的暴力匹配,复杂度可以从“候选数×序列数”降到近似“序列数×模式长度”。

第二个是子序列匹配的判断。判断一条序列是否包含某个候选模式时,最容易实现的算法是拿候选模式的项集去逐个匹配目标序列的事务位置,匹配不上就回溯。更快的做法是把每个项在目标序列里的所有出现位置预先建立索引,然后按候选序列的顺序找递增的位置序列,一次线性扫描就能判断。

第三个是利用剪枝减少无效扫描。候选生成阶段可以想尽办法多做剪枝,比如记录每个候选的所有(k-1)-子序列,如果其中任何一个不在上一轮频繁集里,马上标记为无效。多付出一些内存开销来降低扫描次数,通常是很划算的。

4.4 处理时间的扩展:GSP的变体与约束

基础GSP只考虑事件的先后关系,但很多真实业务对“时间间隔”有硬需求。比如电商想要“下单后7天内是否退款”这样的模式,如果把退款时间拉长到一年后,早就不算同一段行为了。

GSP有专门的扩展机制来应对这类需求,最常见的是在候选生成和计数阶段引入最小间隙min_gap和最大间隙max_gap。所谓间隙,指的是相邻两个项集之间的时间差。设置max_gap=3天,就要求模式里的相邻行为间隔不能超过3天。设置min_gap=1小时,就要求相邻行为间隔至少满1小时,防止把同一会话里的瞬时点击误认为有先后因果。窗口大小window size则用来控制同一个项集内部多个项之间的最大时间跨度。

这些时间约束一旦开启,支持度计数的复杂度会比基础版本高不少,因为并不是“满足先后顺序”就算包含,还得满足时间间隔限制。但这是让序列模式挖掘真正落地的关键,否则挖出来的模式可能在业务上毫无意义。

5. 常见问题与排查技巧实录

5.1 候选爆炸,数量大到内存直接爆

这个问题太经典了。当数据集中频繁项很多时,L1动辄有几百上千个项,连接生成的C2数量会接近项数量的平方,几十万甚至上百万个候选并不稀奇。接着每轮生成的候选数量还会继续膨胀。遇到这种情况,第一件事不是优化代码,而是重新审视min_sup。在很多业务数据里,阈值设得够高是能控制候选规模的。如果业务确实要求低阈值,那就要做好心理准备:要么改用PrefixSpan这类不生成候选的算法,要么对数据库做抽样,先在小样本上调参。

5.2 长序列模式挖不出来

GSP是逐层迭代的,挖一个长度为k的模式需要挨过k轮扫描。如果业务里的目标模式特别长,比如一个“搜索→浏览→加购→下单→支付→评价”的六步路径,GSP需要跑六轮,而且越靠后的轮次候选数量越多、扫描成本越高。我在实践中发现,当序列平均长度超过10个事件时,GSP的表现就不太好看了。这时候通常考虑换成PrefixSpan,它通过递归投影数据库来挖模式,不需要每轮重新扫描全量数据。

5.3 同一项集与前后序列的误判

支持度计数最隐蔽的bug就是前文提到的<{b},{c}>和<{b,c}>混淆。在处理带多项项集的序列比如S3=<{a},{b,c},{d}>时,统计<{b},{c}>时要认为不包含,但统计<{b,c}>时要认为包含。很多新手在实现子序列匹配时,图省事把同一个事务里的项遍历一遍就当作顺序匹配了,结果把不支持的模式数成了超高频模式。排查这类问题的办法很简单:找一个明确包含、一个明确不包含的边界用例做单元测试,把同一项集的事件和跨事务的事件分别验证。

5.4 多趟扫描数据库带来的I/O瓶颈

GSP每一轮迭代都要把全库扫一遍。代码实现时如果直接从磁盘反复读原始文件,数据量大时I/O时间会比计算时间还长。我的习惯是第一次读取后就把数据加载进内存,并转换成便于匹配的内部结构(比如链表或数组),后续各轮都在内存里操作。如果内存吃紧,至少也应该是把数据压缩后驻留,而不是每轮重复读文件。

6. 与主流序列挖掘算法的对比及选型建议

6.1 GSP与Apriori的差异一句话讲清

GSP是Apriori思想的序列化改造。Apriori的候选单位是项集,候选生成时直接做并集操作;GSP的候选单位是序列,候选生成时多了一层“顺序维度”的连接。在Apriori里“{a,b}”和“{a,c}”是独立候选,在GSP里还要区分<{a},{b}>和<{b},{a}>。因此GSP的候选空间比Apriori更大,剪枝条件也更复杂,但本质上的“逐层搜索+反单调剪枝”框架是完全一致的。

6.2 GSP与PrefixSpan的核心分歧在哪

PrefixSpan走的是截然不同的路线,它不生成候选序列,而是在挖掘过程中不断把数据库投影成更小的子库,在每个子库上递归挖掘。它的调性更适合长序列和低支持度场景,因为不需要花费大量时间维护一个庞大的候选集。但PrefixSpan也有自己的毛病:递归投影会创建大量中间数据库,内存占用可能高得吓人。具体取舍要看数据形态,如果序列普遍较短、项总数不大,GSP反而更好写、更好调。

对比维度GSPPrefixSpan
基本策略候选生成-测试前缀投影递归挖掘
扫描数据库每轮全库扫描投影过程中扫描投影库
候选集大规模候选不产生候选
长模式支持较弱较强
低支持度场景容易候选爆炸相对稳健
实现难度中等较简单(伪代码简洁)

6.3 到底怎么选:我的选型建议

面对一个具体序列挖掘任务,我的习惯是先回答三个问题:序列平均长度有多长?目标模式大概多长?能接受的阈值有多低?

如果序列平均长度在5个事件以内、目标模式也就三到五步、阈值可以设到1%以上,我会用GSP,图一个实现简单、控制力强。如果序列动辄几十个事件、要挖十步以上的长模式、阈值还得压到零点几,那直接上PrefixSpan,不要硬用GSP。如果数据量特别大但项数量有限,还可以考虑SPADE这类垂直格式方法,它把数据库转成每个项对应的序列ID列表,用集合交集来快速计数,内存开销比较大的同时速度通常很快。

6.4 扩展到其他场景的几个启发

学完GSP的意义不只在会跑这个算法本身。我在做Web日志分析时,经常会把序列模式挖掘的结果当作特征工程的一环,把高频序列是否出现当作用户行为特征,喂给下游推荐模型。在金融领域,也有人用它来做欺诈路径识别,正常用户高频走固定路径,异常路径才会暴露异常用户。序列模式的深度应用还不止于此,它和聚类、分类模型结合的方式五花八门,思路一旦打开,你会发现GSP只是工具箱里的第一把扳手。

最后再分享两个小技巧

第一,初学阶段一定要在纸上跑一遍完整的小例子,哪怕只是我把这个a、b、c、d的例子抄一遍,你也会瞬间搞懂候选生成和剪枝为什么那么设计。纸上跑通之后,再去看开源实现或者自己写代码,都会顺畅很多。

第二,用GSP挖完结果,别急着直接看最长的频繁序列,先看一眼频繁1-序列的分布。如果L1里的项互相之间的支持度差异巨大,整个L2、L3很有可能被头部几个大项主导,挖出来的模式基本围绕少数热门项展开,其他项几乎参与不进来。这时候要考虑把头部项单独剔掉再挖一轮,往往能挖出一些被“掩盖”的细分行为模式。我在电商数据上试过这个操作,效果出乎意料地好。

数据挖掘路上,GSP既是门槛也是垫脚石,把它吃透,后面遇到再复杂的序列算法,你心里都会有底。

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

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

立即咨询