AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


AI 世界生存手册(一):从LR到DeepSeek,模型慢慢变大了,也变强了

发布日期:2025-03-06 08:44:00 浏览次数: 1775 来源:阿里云开发者
推荐语

深入解析大模型如何引领AI走向通用人工智能(AGI)。

核心内容:
1. 大模型之前的算法演变与关键发展
2. 机器学习基础与深度学习范式
3. 预训练+微调范式与BERT和GPT的对比故事

杨芳贤
53A创始人/腾讯云(TVP)最具价值专家


大部分人都已经清楚什么是大模型(what),怎么用大模型(how),本文将尝试解答为什么是大模型(why)大家都可以通过写 prompt 来和大模型对话,那大模型之前的算法是怎样的,算法世界经过了哪些比较关键的发展,最后为什么是大模型这条路线走向了 AGI,我会用两篇文章共5.7万字详细探索一下。

剧透一下本文的知识图谱如下:



东西稍稍有点多,为了方便理解,我会把它们分到三个章节并作为正文部分,分别对应上边的颜色逐渐加深的三簇。

文章结构如下:


文章结构

本文会按照下边四个章节展开:



图 0 章节目录

  • 我们会在机器学习章节借助小程序分模型,介绍机器学习的基础工作比如特征工程、模型选择、训练、评价等等,这些工作类似第一行代码 hello world。

  • 第二章我们会将机器学习平滑引入到深度学习的领域, 总结并深入深度学习的一个重要范式 embedding+MLP,比如因为RAG 的火热被人人熟知的 embedding,其实在深度学习中无处不在,同理还有MLP。本章会用一个具体的推荐场景介绍具体的模型细节,但是不慌,会搭积木就可以看的懂?。

  • 第三章深度学习进入到预训练+微调的范式,算法工作变得简单且可迁移,介绍双胞胎 bert 和 gpt 的相爱相杀的故事.

  • 最后附赠一个彩蛋。起因是农历新年和老婆看了一个电影,电影结束了老婆还在等彩蛋,用她的话说,“好电影都有彩蛋”。所以我也准备了一个,也可以直接跳到彩蛋,然后回来看前三章。

所以开始我们的边做边学之旅了。

所以我们第一个要学是“机器学习”。

机器学习:我的PD也是算法工程师


场景

一切从一杯咖啡开始。

今年 8 月份和 pd 一起喝了一杯咖啡,聊了下接下来要做的一个业务场景-如何给支付宝小程序打分,满分 100 分。

简单来说需要给优质的小程序打上高分,给低质量的打低分。小程序的质量有了客观评价分数后,运营同学就可以依此做分层,借助运营手段,促进开发者优化小程序质量,进而提升整体小程序的质量。

PD的方案

pd 已经想到了一种解法,选了几个和小程序质量相关的特征(为了方便理解我们只选取报错率和功能最佳体验两个特征,比较符合直觉:功能越齐备且报错越少的小程序体验更佳),并为每个特征设置了几个档位如下图:我们称之为评分表。

特征(feature)

档位(bin)

分数(score)

报错率

档位 1 (报错率<=0.01)

50


档位 2 (报错率>0.01)

0

最佳实践

档位 1 最佳实践(如提供更多功能)

50


档位 2 中档体验 (如提供常见功能)

30


档位 3 较差体验 (功能不全)

0

表 1  小程序评分表

通过这个评分表,我们可以根据具体小程序的特征及其对应的档位,将每个特征的得分进行汇总,从而计算出最终的总分。

我们用代表分数,x 代表特征(若命中某分桶则为 1,不命中则为 0),w 代表分档后的分数。简单公式如下:,真实的特征可能是 n 个,公式可能稍微复杂点的求和公式如下:


模型自动化

然而实际设计的时候可能会遇到一些问题,比如:档位怎么拆分更科学,会很吃专家经验。 另外随着特征的增多,因为本质是一个求和公式,多个特征之间的档位得分怎么平分这 100 分,保证最后的分数更合理。

既然手动设计规则公式出力还不一定合理,那么这个我们不去干预,让机器(为了好理解,可以认为是运行的一段代码)帮我们自己产出一份更科学的算法公式替换我们的规则公式,或者换句话说,让模型识别到这个潜在的规则公式( 模式、规则 ),我们称这种偷懒的方式叫机器学习

机器学习的概念非常广泛,我们将最基础的机器学习方法称为传统机器学习。随着神经网络的发展,深度学习(神经元足够多层数足够深的神经网络)作为机器学习的一个重要分支应运而生。而我们现在所熟知的大模型,更是深度学习的一个细分领域,后边我们会细讲。

所以他们的关系是机器学习> 人工神经网络>深度学习> 大模型是一个层级包含的关系。


认识模型

从数学角度来看,pd 的公式是一个线性方程,一般记为 f(x)=wx+b,为了方便理解我们将截距(也叫偏置)b隐藏。线性方程非常直观,假如f(x)=2x(w=2,b=0), 就是下图的那条红线:



图 2 线性函数

在这公式中有两个变量 x 和 w,数学中称x 为 自变量;w 为权重参数也叫参数,对,大模型中常说的 70b 参数就是指的就是有 700 亿个这种数据。

这里发现了第一个问题,即 x 和 w 一定是数字,不然怎么相乘呢。因为 x 本质上是代表了小程序的特征(feature),假如这个原始特征不是一个数字怎么办,我们了解自己的业务,所以可以提前做点工作将其转换成数字, 我们将这个转换成数字的过程可以称之为特征工程

第二个问题接踵而至,机器怎么才能为每个特征x找到正确的w权重参数。假如啥也不告诉机器,机器肯定是无从下手的。我们把机器当做我们的助理,需要给它传输点经验,然后它才能自主的处理工作。不过这个机器学习助手要求不高,不太需要我们告诉他 why,只需要告诉它我们之前做过的 N 个具体的case 及我们对 case 的处理结果即可,他会自己总结出我们的做事逻辑(模型训练),听起来还不错对吧,有点 ai 的感觉了。

我们称这些 case 为样本,每条样本包含了case 的细节(我们做事的背景)+一个结果(我们做了什么,并称这个为 label)。 大模型场景下我们常听说的人工打标,打的就是这个 label。

假设上边两个问题都解决了,貌似我们就能拿到一个摸得着的具体的公式了比如f(x)=2x,怎么判断这个公式是不是准的呢,还需要一个专业测评阶段。测完发现 ok,可以上线了。部署上线后,后续用户的真实数据请求进来,只需要用相同的特征工程方法(比如训练用 0-男 1-女,此时也用相同方式)处理得到输入 x,直接代入公式就能求到结果,这个就是推理过程

ok 万事具备开搞,我们已经知道了自己需要做:

1.准备样本
2.设计模型比如 y=wx
3.特征工程计算出 x
4.训练模型学到 w ,得到一个完整的公式
5.并测评一下这个公式准不准

6.上线,提供推理服务

模型视角下(我们假设每个特征只占据一个节点),模型及参数的变化过程:



图 3 参数随模型训练变化过程

现在我们 get 到了上边的流程图,训练现在就像一个黑盒,后边会详细解开其中的秘密。


算法思维切换



图 4 最简化的算法模式

上边的例子其实pd 已经潜移默化做了很多算法相关的工作,简单来说,分档(分档的本质是将连续特征转换离散特征)=特征工程,规则公式=算法模型。如果说机器学习=特征工程+算法模型,这么一看 pd 其实也是算法工程师了,事实上我们大家都离算法的世界很近。

借助上边的例子我们看一下算法的做法和常规的工程的做法的区别,借此切换我们的思维模式到算法模式。



图 5 算法和工程的区别

万物皆是 IO 系统,纯工程的逻辑是,理解需求然后写一个代码在线上 run 起来,针对用户的不同的请求给出不同的输出响应。前边我们总结出来算法逻辑是需要根据以往用户的输入(用我们的例子就是不同小程序的特征,做一下特征工程后得到数字化特征)+输出(不同小程序的分数)组成的样本一起给到算法模型进行训练,模型会分析出隐藏在数据背后的业务逻辑,代替了pd 设计规则需求+开发分析需求写代码的过程。

可以很容易看的出来,和工程的做法,算法逻辑稍微有点倒转了。

现在我们要去把这个算法模型的工作具体化了,之前我们分析出开发一个算法模型,需要特征工程、模型定义、模型训练、模型测评等步骤,接下来是时候细致的解开他们四个的面纱了。


开始实质算法工作

上边我们分析出的算法开发步骤对比下工程开发:

图 6 算法开发和工程开发的类比

1.选择模型:根据任务需求选择适合的模型,从很多模型中选一个适合的,比如我们之前选了一个线性模型。工程同学视角就是技术选型。

2.特征工程:准备和处理特征数据,包括特征选择、特征转换、归一化等,以适应并提高模型的表现。

3.训练模型:将处理好的特征数据输入模型进行训练,希望模型学习到数据中的规律和知识。 工程同学视角这两个步骤可以认为是我们的开发和自测阶段

4.评估模型:使用专业的评价标准来评测模型的表现。如果评测结果不令人满意,需要调整。 工程同学视角这个阶段可以认为是测试阶段

5.调整与优化:根据评估结果调整特征、模型架构、超参数等,反复进行训练和评估,直到达到预期的性能。工程同学视角,质量同学测试出来了 bug,我们修改下代码。

6.应用模型:一旦模型的表现ok,就可以发一个算法服务了。工程同学视角是相同的,终于完成质量验收了,上线生产。

再贴一下人类学习和机器学习的类比图,方便理解。



特征工程

算法的世界其实一直只有数据和模型两个主角,特征工程就是我们为了让模型怎么更好的理解数据,中间做的一个桥梁。

我们用支付宝当面付的产品描述介绍下文本怎么提取特征,为了让模型更好的理解,所以我们要提前做下分词,方便模型学习。另外为了方便理解,分词我用我们熟知的汉语词组进行。



图 7 文本特征的常见方法

其中one-hot (图中扫码的表示是[1,0,0,0,0,0,0,0],这种形式在数学上称之为向量,向量中有一个位置是 1,所以叫 one-hot;如果多个位置是 1 则称之为 muti-hot,muti-hot一般用在同一主体多标签的场景,比如小程序可能有多个标签表达活跃度、是否 KA 等)方式目的是为了让模型感知到每个索引后的分词是不同的,不然 出示=1,付款码=2, 支付=3,模型会以为支付的含义=付款码+出示,它可最擅长做加法了。

如图 ont-hot 有一个致命问题就是,词表如果是 5w,那么每一个分词(token) 都会存在 49999个 0,这不仅导致存储空间的浪费,同时也使得计算效率低下。one-hot 避免了语义混淆, 但是做的太彻底,不同词之间毫无关系,就失去了表达语义的可能,比如猫和狗是相似的关系。

初识embedding

回到问题的本质,我们担心不同词之间的表达在相加或计算时,可能无法准确地反映其语义关系。但是如果可以呢?比如国王 - 男 + 女 ≈女王。

   

图 8 embedding 例子

这个例子体现了embedding的基本思想:我们不再只用 0 和 1 来表达数据,换成了更精确的数字,依此引入了丰富的语义表示。并显著压缩了向量的维度,比如上边的例子向量只有三维,对,embedding 本质是一个向量,重命名是为了区别普通向量并体现出这个压缩过程。

通过简单的数学计算,例如矩阵分解的方法,我们就可以拆分和压缩向量,得到 embedding,这个思路层广泛应用在协同过滤算法中,首先构建一个用户和商品的共现矩阵,通过矩阵分解就可以生成user和item的embedding,有了这个向量我们就可以做相似度计算了。当然 embedding也可以通过一个专门的算法模型来学习得到,后边会介绍 Word2Vec 和sentence embedding本质上都学习了大量的文本语料后,模型可以生成embedding表征(特征表示)。

embedding 虽好,通过上边的介绍也要知道其本质是对现实世界的压缩,凡压缩就可能有损耗,所以并不一定 100% 准确。

特征提取后

在这里,我们只介绍了文本模态数据的特征提取方式,而对于图像、视频等不同模态的数据,特征提取的方法会有所不同,多模态的特征提取会在第二章节详细介绍。

在提取完特征后,我们还可以进行重要特征的选择,例如,假设我们有 1000 个特征,可以从中挑选出与当前任务强相关的 100 个特征。

如果在第一步计算得出的特征在数值表达上不够显著,我们可以进行一些特征转换,如标准化、归一化、对数变换以及多项式变换(例如立方和平方,比如早期 youtube 会将用户的观看时间做平方,让模型更容易关注到这个特征,另外平方还带来了非线性)。此外,如果特征向量的维度过大,可以通过主成分分析(PCA)来提取主要成分,进一步压缩特征维度,以优化后续的模型训练和性能。

当然还可以通过对多个特征相乘等计算方式,无中生有一个新的特征出来,比如特征有年龄、收入,可以把年龄乘收入,用来捕捉到年龄和收入之间的复合关系,这种方式称之为特征交叉。

在我们的例子中,小程序具有多个特征。经过特征工程提取后,我们可以将这些特征转换为向量,并将它们简单拼接成一个更长的向量(比如城市2 维+类别 4 维 =最后就是一个 6 维的向量),作为一个整体表达整个小程序。

有了这个特征就可以作为输入给模型了。

算法模型

任务类别

在我们上一步的特征数据上加上一列 label ,我们称之为一条样本,当然也可以不加。加了 label 让模型照葫芦画瓢的叫监督学习,没有 label 让模型自己悟的叫无监督学习,比如聚类,把相似的数据聚集成一个的簇。偷个懒只给少部分打标,大部分不打标的叫半监督学习or 弱监督。

我们的场景监督学习,因为有 label 嘛。一开始,我们并不会自己设计模型,而是从机器学习的监督学习算法模型库中挑选出适合自己业务场景的模型进行使用,还好我们提前了解了下模型任务分为回归和分类两种,可以根据任务类型进一步缩减一下选择的范围。

  • 分类很好理解,比如给了一堆图片,我们去区分哪些是猫,哪些是狗,哪些是卡皮巴拉。如推荐算法中的点击率预估就是一个比较常见的二分类场景,判断用户点击 1 不点击 0。如逻辑回归、svm、树模型都可以做。不同模型分类的方式不同,目标是一致的,想一个办法把两种数据隔离开。



图 9 不同分类算法的可视化

  • 回归概念略抽象,其来自一个统计学的故事。数学家弗朗西斯·高尔顿通过分析 1078 对父亲与儿子的身高数据,发现了一个有趣的现象,儿子的身高倾向于“回归”到父亲身高的平均值,而不是完全继承父亲的极端身高。他将这种现象称为回归效应(Regression to the Mean),并基于此建立了线性关系模型 y=33.73+0.516x,用于预测子女的身高。其中,x表示父亲的身高,y表示儿子的身高。回归问题很多模型都已做,比如如线性回归、svm、树模型。

这里可以更通俗理解:可枚举的任务通常是分类,输出为离散标签;不可枚举的任务通常是回归,输出为连续数值。

选模型

我们确认了我们的场景是有监督,有监督算法模型很多,但是可以更细的看下。

  • 线性关系:

在某些情况下,多个特征之间可能符合线性的关系(线性关系指的就是多个特征之间能否用线性方程描述,比如简单的一元一次方程),这种情况下可以使用线性模型,如线性回归、逻辑回归、岭回归等。按照pd 的假设我们可以选择这类模型。

  • 决策树与集成方法

有时,我们可以通过 n 层 if-else 结构组成的决策树来实现所需的结果,这种树状结构直观地呈现为梯形。在更复杂的情况下,单棵树可能无法精准地解决问题,因此我们采用多棵树的集成方法。根据“三个臭皮匠顶个诸葛亮”的思路,使用多棵树的组合可以选择 Bagging(Bootstrap Aggregating)和 Boosting 两种策略来构建集成模型。前者生成随机森林,而后者涵盖了从 GBDT 到 XGBoost,再到 LightGBM 的演进。

  • 非线性关系:

当特征之间的关系不符合线性假设时,线性模型可能无法很好地捕捉数据的复杂模式。在这种情况下,可以使用非线性模型,如支持向量机(SVM)中的核方法、K近邻(KNN)等。这些模型能够处理更复杂的决策边界,适合处理非线性可分的数据。

机器学习模型种类丰富,比如还有利用概率的贝叶斯算法,许多模型能够应对不同的场景。例如,树模型可以用于分类也可以用于回归,我们可以通过实验分析来比较不同模型的性能,以选择最优解。

我们的场景

粗看我们的场景是一个回归场景,通过小程序的不同特征然后决定最后的得分。但是问题是假如我们把场景设置成回归,就需要打标同学提前打标 N份数据,假设是 1000 个小程序+每个小程序的分数。这对打标同学来说是困难的,怎么凭直觉判断出每个小程序可能 1分甚至更小的差距在哪里。

所以我们换了个思路,打标数据变成了质量体感好的小程序和体感差的小程序,然后让模型去学习小程序成为优质小程序的概率(0,1二分类),从一个回归任务转成了分类任务。

我们选了逻辑回归模型作为算法模型,其公式可以理解:

  • 左图:一开始 pd 的规则模型(算法化后是线性回归模型),但是线性回归计算出来数据可能是-n到 m 的,怎么将数据控制一下限制在[0,1],不要溢出。

  • 右图:我们可以将上一步的结果再做一次数据变化,将最终数据压缩到 0-1,这个数据转换的工具叫做 sigmoid, 也叫logistic函数,sigmoid的作用就是把一个钢铁直男生生掰弯了,从 LR(Linear Regression)到 LR(Logistic Regression)仿佛什么都没变,仿佛什么都变了?。

在后续的深度学习章节,我们将根据神经网络的特性,为sigmoid 这类工具赋予一个更为广为人知的名称——激活函数。



图 10 从线性回归到逻辑回归

模型训练

之前两步我们1.对打标的样本数据的输入部分做了特征工程,得到了数字化后的数据集。 2.然后选择一个线性模型,比如 LR 模型。接下来是时候让模型从这个数据集中总结学习到知识了。

模型训练的过程是有效学习输入特征与目标变量(label)之间的关系,并使模型具备良好的泛化能力。训练的目标是通过优化模型参数,让它能够准确预测新数据。通俗一点说,如果我们给定的样本,标签是 1,模型会对特征值进行一通计算(不管做什么吧,加减乘除想做什么做什么),最终得到结果也应该尽量接近 1。

这种无限接近的现象被称为拟合,但如果模型只是在记忆训练数据本身,而没有找到隐藏在数据背后的规律,我们称之为过拟合。比如学会了 1+1=2,我们问它 1+2 等于多少,它不会了。相对应地,泛化是指模型在未见过的新数据上也能保持良好预测性能的能力。训练后一个优秀的模型不仅要在训练数据上拟合得好,还应能够泛化到新样本,真正的捕捉到了数据的本质规律。

那么怎么评价模型是不是有泛化能力,数据只让他学一部分,剩下的看看它也能不能预测的准确,这里就涉及到了样本数据的拆分。

数据集拆分

与工程测试不同,数据本身就是测试用例。为了评估模型的性能,我们需要将数据初步划分为训练集和测试集。训练集用于模型开发,而测试集用于上线前的最终测试(测试集是一定不能让模型学习的,不然就是提前泄题了)。进一步地,从训练集中再划分出验证集,用于模型开发过程中的自测和调优。

经过这两次拆分,最后数据集通常被分为三个部分:训练集、验证集和测试集。下图表达了这个拆分过程。



图 11 训练集、验证集和测试集

参数选择

选择好训练集和验证集后,我们就可以训练模型了。训练之前,我们有些参数我们可以干预下,这些参数可以控制学习的速度和性能,我们称之为超参数。超参数可以理解为非模型通过学习到的参数(比如我们一开始提到的权重参数w),是人工可以干预的参数集合。

为了简化理解只举 4个超参数:

  • 学习轮次:学习轮次可以类比为练习某个技能的次数。练习的次数越多,我们可能掌握得越熟练,不过不是越多越好,就像我们幼时背古诗背 100 遍,并没有理解其中的意境,直接脱口而出,可能是过拟合了。

  • 批次大小:学习知识时,每次分章节学习,不会一次学完。机器学习一般都是小批次多 epoch 的训练方式

  • 学习率:学习新技能时,我们的学习速度就是学习率。如果学习速度太快,可能学得不够扎实,容易犯错;如果学习速度太慢,可能需要更长的时间才能掌握技能。学习率是和优化算法配合一起的。

  • 优化算法:优化算法就像是学习新技能时,我们使用的学习方法。不同的方法可以有不同的效果。例如,有的方法可能会让我们更快地掌握技能,有的方法则可能让我们更扎实地掌握技能。有很多的优化算法,优化过程更形象化的样子是,更快的走到谷底,不同的优化算法有不同的路线,优化速度也不尽相同。



图 12  优化算法

损失函数:损失函数一般不认为是超参数,但是为了方便整体理解,我也贴在这里了。损失函数可以类比为考试或测验,每次考试的结果都可以反映出我们与正确答案之间的差距。这个差距(loss)越小,说明学习效果越好。不同的场景一般选择不同的损失函数。比如回归场景常用均方误差(MSE),二分类常用交叉熵损失(Cross-Entropy Loss)。



图 13  配置超参数训练模型

学习的过程其实就像教小孩做题:我们把题目(数据)分成一小堆一小堆的(batch)。模型每学完一堆,就会做个“小测验”(计算 loss),看看自己错哪儿了。然后,优化算法就像个“老师”,根据错误的大小(loss)和学习进度(学习率),告诉模型该怎么调整自己的“思路”(参数)。学完一堆,再学下一堆,直到把所有题目都学完一遍(1 epoch)。如果觉得学得还不够,就再来几轮(n epoch),直到模型学明白。

在机器学习领域,模型通过迭代学习过程不断优化其内部参数,以减少预测误差。超参数的调整,包括但不限于上边的几个参数,是算法同学的比较重要的工作。调参费时费力,因为多个参数本质上是一个笛卡尔积,有些自动调参技术如网格搜索和随机搜索等也被辅助用于寻找最优的超参数组合。

怎么叫学的好

具体来说,一个理想的模型应该能够在训练集上表现好,也要通过测试集的考验。不然就有可能偷懒--欠拟合,死记硬背--过拟合。



图 14 真实数据分布、欠拟合、拟合、过拟合

k-fold 验证

既然训练会有各种问题,比如过拟合和欠拟合,需要在训练阶段要科学的全面测试。没有好的办法,多实验几遍,当然重复可以以更科学的方式进行---交叉验证(Cross-Validation),特别是k-fold交叉验证,是一种统计学方法,类似于我们熟知的滑动窗口。

通过k-折交叉验证,我们可以观察模型在不同训练集上的性能变化。如果这些性能度量相差不大,那么我们就可以认为模型具有好的泛化能力。



图 15  k 折训练,分片后遍历训练,最后评价

假设我们已经训练好了模型,并且观察到损失函数(loss)在不断降低,这时可能会产生一个疑问:仅仅依靠损失值来评估模型的好坏是否足够科学?

loss 表达了模型自己觉得自己好了,我们要用更直观的方式去看下。例如,在工程角度,质量同学会提前准备比如 100 个测试用例(test case),测试过程中通过 bug 率来判断本次需求的开发质量。我们之前提到过,在算法场景下测试用例实际上就是提前准备的一些样本,因此从具体样本的角度来看,模型的表现如何,可以通过计算正确预测和错误预测的比例来更直观地反映模型的性能。

这样,我们可以更全面地假设模型在实际应用中的表现。

测评

没有人比质量同学更懂测试了,所以我们用质量同学的角度来看算法的几个核心指标是怎么来的。

在做测试分析阶段,质量对当前需求的细致分析后,用思维导图分析出来 N 个 case。但是这些 case 可以继续拆分一下,比如根据是不是正向的 case,分成正向用例和负向用例。前者表达了期望成功的的正常用例,后者期望失败的例如边界异常用例,系统能拦截并提醒报错。

用质量同学用例的预期情况(称为实际)和工程开发的代码处理(称为预测)就有了四种可能(T和 F 代表 true 和 false 是否预测成功,P 和 N 代表Positive和Negative 预测的是正负,比如 TP 代表一个正样本正确被预测了,FN 代表错误的预测成了负样本,代表一个原本正样本被错误预测了,这块稍微有点绕,可以自己稍微思考一下),我们按照下边的图组织下,直观表达预测有没有混淆真实情况,所以称之为混淆矩阵。



图 16 混淆矩阵(Confusion matrix)

机器学习指标

QA 测试用例类比

解释

真阳性 (TP)

正向测试用例,代码通过(Pass)

正向测试用例按预期通过,表示代码正确处理了这些正常输入。

真阴性 (TN)

负向测试用例,代码拦截(Fail)

负向测试用例按预期失败,表示代码正确地拒绝或处理了异常或非法输入。

假阳性 (FP)

负向测试用例,代码未拦截(Pass)

负向测试用例意外通过,表示代码未能正确处理异常或非法输入,存在缺陷。

假阴性 (FN)

正向测试用例,代码拦截(Fail)

正向测试用例意外失败,表示代码未能正确处理正常逻辑,存在缺陷。

单指标

准确率(Accuracy)

准确率是指模型预测正确的样本数与总样本数的比例,一般简称为 ACC。在测试场景,1-acc 就是质量比较关注的 bug 率,代表了本次提测的质量。

我们也可以借助 acc 和 loss 来识别下模型是否过拟合。如果在训练集上表现越来越好,验证集不怎么升高或者降低了,那么就要考虑模型训练过拟合了,其后边的 AUC 等指标也是一样的道理,要多个指标一起看一下。



图 17 acc 和 loss 一起判断模型的表现

不足:假设质量同学准备了 100 个 case,其中有 95 个正向的只有 5 个 负向的。负向的即使不符合预期,正向流程都表现的很好,最后这个指标也会很高,但是负向的 case一旦没拦住,发上线又会影响较大,还是想办法关注到这个指标,比如可以用特异性。

特异性(Specificity)

有的场景我们很关注负向的 case,希望异常情况都能被提前识别。那么特异性这个指标,专注于看负向的测试用例中,真正被识别拦截的概率。

既然可以专注负向用例是不是可以专注正向用例,后者就是我们常听说的召回率。

召回率(Recall) 

召回率 (Recall) 是实际为正类别中被正确预测为正类别的比例。

我们的小程序场景就是质量高小程序被识别称高质量的比例,较高的召回率意味着我们没有漏掉太多实际高质量的小程序。高召回率在内容推荐场景,可以理解为用户提供尽可能全面的内容,确保他们不错过任何可能感兴趣的内容,比如抖音内容的推荐。

单看这个公式,如果我们只需要把 FN 降低,同时保持住 TP,就可以带来召回的提升。努力让正向的用例通过,同时把没有通过的正向用例修复掉,改了 bug 不带来新的 bug,这个指标就提升了。

仅关注召回率可能导致系统变得过于宽松,因为没关注负向用例,可能允许大量负向测试用例通过(FP),即无法正确识别和拦截这些异常或非法输入。如果考虑一下 FP,拦截负向异常case,先不管失败的正向 case  FN 。这就是精确率。

精确率(Precision)

精确率 (Precision) 是预测为正类别中实际为正类别的比例

解释:要想提高精确率就要想办法降低 FP,在我们的小程序分场景高精确率意味着模型会尽量避免将低质量的小程序误判为高质量。

不足:在某些情况下,追求更高的精确率可能导致召回率下降,因为模型可能会更保守,不愿犯错(不愿意将某些不太确定的小程序标记为高质量)。

我们会发现依赖单一指标总会有不足,很容易拆东墙补西墙。所以为了更全面的性能评估,会用一些综合性的指标来看。

综合指标

精确和召回的综合指标(F1)

既然召回率和精确率都挺重要的,那么和个稀泥。F1 为了调和精准率和召回率, 其思想来源于数学里的调和平均数。F1分数高即模型在这两个方面表现都比较均衡。



样本分布不均的综合指标(MCC)

与F1-Score(只考虑了TP、FP、FN,召回率和精确率只用了这三个指标)相比,MCC考虑了所有四个混淆矩阵的方面(TP、FP、TN、FN),提供了一个更全面、更均衡的衡量。因此,在需要综合考虑所有类型的预测结果和处理不平衡数据集时,MCC可以说是一种比F1-Score更稳健的性能度量方法。

除此之外 auc 可以综合的来看模型区分正负样本的能力,另外 auc 对正负样本的比例不敏感,只关注区分能力。
ROC 曲线下面积(AUC)
上边的玩法稍稍有点无聊, 有一个质量同学喜欢这么花样搞测试:
1.取一对样本:一个来自正类,一个来自负类。
2.比较模型评分:模型为每个样本打分(例如,预测为正类的概率)。
3.记录结果:

a.如果正样本的分数高于负样本,计为✅。

b.如果负样本的分数高于正样本,计为❌

c.如果两者相等,可以视为“中立”或按一定规则处理,比如偏向于❌。

4.重复以上步骤,遍历所有可能的正负样本对。
5.计算比例:将✅的比例作为 AUC 值。
通过上边描述,我们会发现,AUC 可以看作是所有可能的正负样本对中,模型能够正确区分的比例。
其具象化的表现是ROC曲线下的面积,曲线的横坐标为假正例率(FPR),纵坐标为TPR(真正例率,其实就是我们之前说的 recall)。



AUC (Area under Curve):即ROC曲线下的面积,介于0和1之间,作为数值可以直观的评价分类器的好坏,一般认为值越大越好。

图 18 auc=0.93 的图
当模型的表现尽可能接近完美分类(即完美区分正类和负类),其 ROC 曲线下的面积将达到最大值 1,这表明模型的性能达到最佳水平。而no skill辅助线则表示预测接近随机,此时曲线下的面积为0.5,表示模型未能掌握区分正负样本的能力。
上边提到的是比较通用的指标,当然评测指标还有很多,比如大模型常用的困惑度、BLEU、ROUGE,以及推荐算法中常用的GAUC、NDCG、 MRR 等,选择适合的评测指标对于不同任务的模型优化至关重要。

完整代码

按照我们上边介绍的内容,将每个模块转化成真实的代码。
1 特征工程后,准备了样本,并且拆分训练集和验证集,比例 8:2。
2 选择了模型逻辑回归LR
3 选择随机梯度下降优化器,学习率 0.01 训练轮次 5 批次大小32
4 评估用 acc  F1进行评价
import torchimport torch.nn as nnimport torch.optim as optimimport pandas as pdfrom sklearn.model_selection import train_test_splitfrom sklearn.preprocessing import StandardScalerfrom torch.utils.data import DataLoader, TensorDataset# 使用GPU 如果无使用 CPUdevice = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 加载小程序数据集data = pd.read_csv('mini_program_data.csv')# 提取特征和标签X = data[['completeness', 'error_rate']].values  # 特征列y = data['label'].values  # 标签列(优质与否)# 特征标准化scaler = StandardScaler()X = scaler.fit_transform(X)# 划分训练集和验证集X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)# 转换为 PyTorch 张量X_train_tensor = torch.tensor(X_train, dtype=torch.float32).to(device)y_train_tensor = torch.tensor(y_train, dtype=torch.float32).view(-1, 1).to(device)X_val_tensor = torch.tensor(X_val, dtype=torch.float32).to(device)y_val_tensor = torch.tensor(y_val, dtype=torch.float32).view(-1, 1).to(device)# 创建 TensorDataset 和 DataLoaderbatch_size = 32  # 设置批次大小train_dataset = TensorDataset(X_train_tensor, y_train_tensor)train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)# 定义逻辑回归模型class LogisticRegressionModel(nn.Module):    def __init__(self, input_dim):        super(LogisticRegressionModel, self).__init__()        self.linear = nn.Linear(input_dim, 1)    def forward(self, x):        # 使用sigmoid        return torch.sigmoid(self.linear(x))# 初始化模型、损失函数和优化器input_dim = X_train.shape[1]model = LogisticRegressionModel(input_dim).to(device)criterion = nn.BCELoss()  # 二元交叉熵损失optimizer = optim.SGD(model.parameters(), lr=0.01)  # 随机梯度下降优化器,学习率 0.01# 训练模型num_epochs = 100for epoch in range(num_epochs):    model.train()  # 设定为训练模式    for batch_X, batch_y in train_loader:  # 使用 DataLoader 进行批次训练        optimizer.zero_grad()  # 清零梯度        # 前向传播        outputs = model(batch_X)        loss = criterion(outputs, batch_y)        # 反向传播和优化        loss.backward()        optimizer.step()    if (epoch + 1) % 10 == 0:  # 每10个epoch输出一次损失        print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}')model.eval()  # 设定为评估模式with torch.no_grad():    val_outputs = model(X_val_tensor)    predicted = (val_outputs > 0.5).float()  # 使用0.5作为阈值进行分类    # 计算准确率和F1分数    accuracy = accuracy_score(y_val_tensor.cpu(), predicted.cpu())      f1 = f1_score(y_val_tensor.cpu(), predicted.cpu())    print(f'Validation Accuracy: {accuracy:.2f}')    print(f'Validation F1 Score: {f1:.2f}')

上线

模型给出的是小程序是一个优质小程序的概率,我们需要继续做一个尺度对齐,将概率转换成分数,比如芝麻分场景,会用概率表示违约率,然后利用基础分数 +违约概率转换成的分数(可能正可能负)作为最终的分数。为方便理解,我们假设这个概率就是最终的分数,比如模型输出是 0.98,代表 98% 是一个好的小程序,那么分数就是 98 分。
通过前几步的开发,我们的模型通过了测评,主要看了 f1,acc,auc 等指标,表现 ok,现在可以上线了。
上线后会产生一些负反馈的 bad case,还需要继续把上边的流程重新走一遍,是一个持续优化升级的过程。


总结

  • 传统机器学习、深度学习、大模型等等都属于机器学习
  • 机器学习 =  特征工程+算法模型。其中特征工程是模型的输入,训练阶段模型会对特征(除标签列)进行各种计算期望得到的结果最大可能的接近样本的标签列。训练完成后,固化下来的参数,将用在后续的模型预测阶段中。
  • 特征工程是提升模型性能的关键环节,通过合理的特征设计,可以显著提高算法的效果上限。
  • 拥有好的特征后,算法模型能够更接近其理论效果上限,从而实现更高的预测准确性。
  • 使用算法进行建模和预测是为了更好地理解和解决问题,而标签正是定义问题的关键,体现了算法同学对当前问题的理解。
  • 在测评阶段,我们格局放大一点,假设我们准备了一份在特定场景下具有最高质量和最全面的数据集,就可以提供出去,举行比赛了,让开发者看到训练集并隐藏测试集的 label。假如这个数据集行业内影响力十足,就可以依此做一套基准测试。例如,LiveCodeBeach,从LeetCode等平台收集来 500 多个代表性的题目,用来评测大模型的代码能力,比如代码生成、代码执行。

图 19  LiveCodeBeach 榜单


美好假设

回到我们说的那个不严谨的公式传统机器学习=算法模型+特征工程。如果我们要偷懒改怎么减少工作量呢
1.急需一个神奇的模型可以模拟各种各样的公式---万能公式,代替我们算法模型选择的工作。
2.特征工程比较费人,在使用线性模型时,比如特征和标签之间并不符合线性关系,我们需要对特征进行各种变换,强迫它适应线性关系。有一天我们换了其他模型,可能要重新适配搞一下。特征工程这么苦,可不可以少做点,或者干脆不做了。
我们要结束传统机器学习这一章了,带着这两个问题或者目标,接下来我们会借助一个真实的例子在深度学习的领域穿梭。

深度学习:会搭积木就可以开发算法模型


场景


图 20 服务商场景下的搜推
在 2024上半年,我们希望为支付宝的服务商提供个性化推荐,重点围绕用户搜索和投放推荐两个场景,期望在提升用户体验并推动业务转化。
搜索与投放推荐可以视为相似的应用场景,两者均依赖于服务商(支付宝的商家服务商,user)和内容(item)的标签信息。主要区别在于,搜索场景中增加了对用户查询(query)的响应。
在搜索场景中,模型需要根据服务商的查询和内容标签来预测并排序潜在的点击行为,以帮助用户快速找到感兴趣的内容。相比之下,主动投放场景则是在没有明确用户查询的情况下,依据服务商和内容的特征,主动向服务商推荐可能感兴趣的内容。这要求算法能够准确捕捉服务商的兴趣变化,并进行个性化推荐,挑战更大点。
比如之前的小程序分的场景我们需要对用户解释他分数怎么来的,让用户明确怎么做可以提高分数。但是推荐场景不需要强解释性,比如我们给用户推荐一个广告,搜索推荐一个内容,只需要提供给用户更可能点击的内容就好了,算法此时的贡献可能不专注于具体的 case 而是最终业务整体的点击率提升。所以我们想办法改造一下我们的 LR,让其放弃可解释的执念,变得更强。


从机器学习到深度学习

重新认识 LR 模型

在上一个场景我们利用 LR ,那么我们重新认识下这个模型,稍微抽象下,其特征节点是输入,中间求和节点是计算节点,最后的数据转换是输出节点。

图 21 抽象模型
它的问题是什么
1.仅有线性能力:本质只在做求和后做了一次数据转换,只能表达特征间的线性关系,缺少非线性的能力。
2.参数少:我们可以把节点的连线数量作为参数量,参数的具象表达就是这个线(真实情况其实是一个向量)。 参数有点少,比如上图只有三个参数(w1,w2,w3),参数少会显著限制模型的表达能力,这个非常符合直觉,比如人类之所以超脱于万物就是因为大脑神经元多,且得到了相对有效的利用。

一起发明全新模型

有没有办法增加一下参数量,并且增加一下非线性。
输入我们没法加了因为其用来接受特征向量=特征向量的维度,输出节点代表了我们最终要的预测结果,也是固定的。那么只能在计算节点来扩展了,我们头脑风暴下,可能的扩展结构如下:

图 22 可能的方案
图 1 我们利用图设计了中间的计算层,假设这个节点可以随便两两连接,图 2 假设节点按照一定层次排序,n-1 层的节点只可以和 n 层的节点连接。其实两图参数量差不多,但是明显图2更清晰简洁。所以我们选择分层方案作为我们的模型架构。
参数量提升了,看起来很完美。
按照之前 LR 模型的经验,参数会从白色节点传进来,然后在绿色节点做求和,做后计算出来一个结果作为输出。
我们简化这个推导下这个过程(为了好理解,继续隐藏了偏置 b):

图 23 简化的模型

其表达还是一个线性方程,我们得到的还会一个复杂一点的LR。 面对非线性问题,怎么办呢,我们想到了 LR 模型最后的输出 sigmoid 将数据做了非线性变化,把结果压缩到了(0,1),如果把它前移,用在特征计算阶段,那么不就可以表达特征间的非线性关系了。
这么随意的吗,其实算法领域还真的就是这么随意,想到了就去试,别人没试过就是我们的paper了?,所以我们的假设如下图中上半部分:

图 24 神经网络可能的样子
人类大脑的思考过程可以理解是一个多个神经元的放电过程,如果信号不强烈,会在中间某个神经元,信号中断,成为没有激活该神经元(上图中间部分)。转换成我们的模型,我们希望在某个节点,数据变成 0,不再往下传播数据。sigmoid 很明显可以模拟这个效果,因为其模拟了神元激活的激活动作,所以称为激活函数。但是在训练的过程中,sigmoid会存在一些问题,数据过小或过大的时候,求导都是数字很小比如 0.001,梯度消失了。
还好有聪明的人发明了 relu(Rectified Linear Unit)(上图下半部分)。 后来发现 relu 也有问题,就是小于0的情况下,求导都是 0,神经元就死亡了,不往下传递了。后来出来一堆修复这个 bug,比如给它小于 0 的部分加个斜率(Leaky ReLU),这个斜率不是写死可以学习就是(PReLU),relu 系列应用非常广泛,比如经典的 transformer 中,这个激活函数可以随便替换的,比如 bert 换成了GELU,有的大模型换成了swishGLU 
激活函数非常多,有人统计30 年来激活函数的发展,发现有 400 多种激活函数。来源论文《Three Decades of Activations: A Comprehensive Survey of 400 Activation Functions for Neural Networks》:https://arxiv.org/pdf/2402.09092

图25 激活函数
一般神经网络都会用一种激活函数,用在隐藏层的每一层后。假如我们将这个激活函数放在神经元连接的那条线上,并不去根据经验提前指定,让模型在训练过程中,自由的选择和调整这个激活函数。这就是 24 年比较火的 KAN,曾经通稿满天飞,要替换MLP,现在已经没啥人提了,MLP 简单且有效很难被替换掉。
除了激活函数,而 w1 w2 权重参数又像是模拟了信号的强弱。这里先泼冷水,人类大脑不长这样的,神经元首先就不是层次连接的(这么设计的最主要原因是为了更方便应用反向传播算法),然后信号的激活涉及到非常复杂的生物现象,不是一个 relu 能表达的。拙劣的模仿也是模仿,所以这种建模的方式被称为人工神经网络(ANN)。

图 26 来源女儿的科普书《 dk拆解万物大百科》
ok 我们摸到了一个全新领域的门槛,我们在机器学习算法基础上拓展出来新的模型结构,所以,人工神经网络很自然是其一个重要的分支。


万能的MLP


图 27 MLP
回到我们一起发明的分层方案,把特征输入作为输入层,计算层作为隐藏层(我们直觉感受下它在干嘛,其实就是它在偷偷学习,对特征进行各种计算, MLP这个家离不开它),输出层代表我们想要的结果(比如我们要对图片可能属于猫或者狗进行分类,那么就是有两个节点,分别表达是猫还是狗的概率),这种模型称之为Multi-Layer Perceptron,简称 MLP,因为数据是向输出节点单方向传播的,又叫前馈神经网络(FFN,是不是很眼熟,这是大模型的核心组件)。与前馈神经网络对应的是反馈神经网络(如循环神经网络 RNN),信息可以在网络中循环传播。
MLP 是最简单的神经网络模型,用工程视角来看,其特别像经典的 MVC 架构。在MVC 架构中,M 关注数据类似 MLP 中的特征输入;C 负责业务逻辑处理,类似 MLP 中的隐藏层对用户特征进行加工处理;V 是面向用户的视图,在 MLP 中输出节层负责输出预测结果。

图 28 MLP 和 MVC
不过我们也发现了这样画不太表现出每一层之后用了什么激活函数(假设隐藏层我们用了 relu,输出层用了sigmoid 来做二分类)。所以MLP 的图一般这么画,并且旋转了 90 度,变身一个蛋糕,后续我们图都会沿用这种新的画法。

图 29  MLP 的画法
就当我们发明的 MLP吧,其初出茅庐我们还不知道它有多强。先说结论它超级强,超级有用,以至于随处可见。

代码

MLP 的代码非常简单,比如下边这个隐藏层用了 relu,最后一层用了sigmoid,可以用来做二分类任务。
class MLPModel(nn.Module):    def __init__(self, input_dim):        super(MLPModel, self).__init__()        self.fc1 = nn.Linear(input_dim, 64)  # 第一层,输入到隐藏层        self.fc2 = nn.Linear(64, 32)          # 隐藏层        self.fc3 = nn.Linear(32, 1)           # 隐藏层到输出层        self.activation = nn.ReLU()           # 使用 ReLU 激活函数
   def forward(self, x):        x = self.activation(self.fc1(x))  # 第一层前向传播        x = self.activation(self.fc2(x))  # 第二层前向传播        return torch.sigmoid(self.fc3(x)) # 最后一层Sigmoid激活

万能函数

还记得我们提到过,如果能够有一个“万能公式”就好了,这样每次都不需要选择模型。
在深度学习领域,刚好有一个重要的理论——万能逼近定理。该定理表明,只要具备至少一个隐藏层并配备足够数量的神经元,多层感知器(MLP)能够以任意所需的精度模拟任何复杂的连续函数。因此,MLP的有效性在于它将传统机器学习算法的单一函数能力提升到了接近“万能函数”的水平(尽管实际上并不完全“万能”,但至少是相当有效的)。
MLP 做了什么:
1. 任何函数,都可以分成很多段的线性函数
2. 激活函数,让线性的神经网络具备了“分段”表达的能力。
既然一整个函数不好模拟,那么分成 N 段,一段一段的模拟呢,分而治之。每当看到这个定理我都想到海底捞的扯面,虽然不是很相似,但是我也很无奈,就立马浮现画面?,扯面在徐峥手里可以被拉成各种形状,像极了我们 MLP 的灵活的拟合各种函数。

图 30 MLP 之父徐峥
既然万能,那就回归和分类都可以做喽。我们借助服务商的例子(为了方便理解,我们假设每个特征都可以用一个数字表示,刚好可以放在一个节点上)来了解下 MLP 怎么做分类和回归。
  • 回归:服务商每月可以提取一定数量的 iot 设备去铺设,我们会根据作业能力和一些指标,决定他下个月可以提多少。
  • 二分类:判断服务商当前喜欢哪些内容,并会发生点击的概率
  • 多分类:服务商线下作业有很多类型,通过算法给他匹配最适合做的几种类型的任务。这里还是我们之前的思路哈,sigmoid 可以表达 (0,1),多分类不太适合,换一个表达多种概率的激活函数就好了,这里可以选 softmax,(大模型最后的词表匹配也是它,其计算了词表中所有 token 作为下一个 token 输出的概率)

图31 MLP 处理分类、回归问题
只靠 MLP 我们就实现了模型的可扩展性(可以自由配置不同深度、不同参数量的隐藏层) 和可复用性(只用它,解决了分类和回归所有类型的任务),太强了太强了,有了 MLP ,我们要天下无敌,bushi?
但是不管怎样 MLP 如一个好友,我们遇到任何问题都可以想起它。


搞科研

其实MLP 是深度学习领域最简单的模型,接下来我们会借助它打开了一个全新的世界。
还记得刚刚我们将 sigmoid 应用在每一层后的数据激活转化,本质上我们在尝试各种可能性,既然开始搞了,我们就贯彻到底,开始假装搞科研了?。
可是我们并不会啊啊啊啊,假装自己会吧,想到科学家首先想到了杂交水稻之父-袁隆平院士,我们学一下吧,发现基因好的东西就往水稻(MLP)上拼。

图 32 生物杂交

baseline

科研一般会选一个模型作为基线然后才好看我们后续的优化是不是正向优化,其作为一个参照物,我们就选 MLP 作为 baseline。输入的特征的线不要较真,为了美观我随便连了一下线,然后这个特征会经过一个 embedding 层进行转换,将特征压缩成 embedding,然后经过一个 concat 层将 embedding 从左到右连接在一起。

图 33 MLP4Rec


交叉模型

Wide&Deep

我有一个铁牛牛头面,bushi,我有一个好点子。
还记得我们一开始搞的 LR 模型吗,其特点是可解释性强,MLP 的特点是泛化性好,可以模拟各种函数。我们要不取两者所长,拼在一起,左手 LR,右手 MLP, 机器学习+深度学习两手一起抓。
其实这就是大名鼎鼎的wide&deep。
Google于2016年提出了Wide&Deep模型,应用在 google play 应用商店的应用推荐。Wide&Deep模型的主要思路正如其名,是由单层的Wide部分和多层的Deep部分组成的混合模型(混合模型更早的是 facebook 2014 年的 GBDT+LR)。其中,Wide部分的主要作用是让模型具有较强的“记忆能力”(memorization),LR擅长总结浅层规律;Deep部分的主要作用是让模型具有“泛化能力”(generalization),善于挖掘潜在的深层规律,正是这样的结构特点,使模型兼具了逻辑回归和深度神经网络的优点——能够快速处理并记忆大量历史行为特征,并且具有强大的表达能力。
用我们的服务商内容推荐的场景来展示一下缝合的这个模型长啥样:

图 34 wide&deep
wide&deep很强,我们也很强,毕竟这么牛的模型其实只领先我们 10 年不到,写到这的时候时间是(2025 年 2月13日  21:30 )
wide&deep很强,但是也有缺点,主要缺陷是 LR 模型不能做特征的交叉,我们可以理解为他只能表达 wa* a 特征+wb*b 特征,无法表达比如 a特征 和 b 特征交叉的情况,若要做也行,需要我们自己去手动搞出笛卡尔积那种特征组合出来。
我们可以将LR 换成能天生自带特征交叉能力的 FM 模型,我们可以称之为 fm&deep?。

DeepFM


图35 deepfm
真实情况:2017年华为将Wide&Deep模型的 wide 部分从 LR 升级成为 FM,核心改进在于利用FM自动学习特征组合的能力(FM 的核心思想是通过对特征进行分解,生成低维的 embedding 来表示每个特征,然后利用这些 embedding 的内积来捕捉特征之间的交互关系),带来了 DeepFM模型。其实这个模型影响力真的很大,现在还会被用在各种场景,其是和Wide&Deep一样的经典模型。

DCN

对于Wide&Deep,作者 google也做了优化,不过没用现成的模型来代替 LR,而是直面问题本质,既然 LR 不能特征交叉,ok,重新设计一下 wide 部分,Google 称之为Deep & Cross Network 简称 dcn。

图36 dcn
cross 是一个向量,为了提升表达能力,Google 在 2020 年将其提升为一个矩阵,包含信息更丰富。带来了 V2 ,简称 dcn v2版本。

DCN-V2


图 37 dcnv2

基准测试

大模型有很多基准测试,其实推荐算法也有的,BARS是华为提出来的一个针对推荐算法基准测试,包含三个数据集。我们上文提到的很多模型都出现在里面了。其评测指标--对数损失和 auc,在该测试标准下,ONN 表现亮眼,其核心是一个 PNN 模型+FFM( 升级的 FM),说到 PNN,又是一个 MLP+乘积层 ,还是我们说的那种杂交实验。我们上边提到的dcn v2也表现优异。

图38 BARS 测评

杂交成果展示


图39 一些显著的交叉推荐模型
有些模型我没细写,比如微软的 deep crossing 会出现在彩蛋里面。微软也优化了DeepFM 提出了 xDeepFM,核心是设计了一个新的组件 CIN,拼接在了原来 deepfm 上,这里我们就不展开讲了,有兴趣可以去探索下。
反正拼来拼去,我们变成了科学狂人的同时(按照这个思路去搞生物实验,简直不敢想,甚至有点害怕,感觉有生之年能见到活的哥斯拉,哈哈哈哈哈),模型搞出来一堆。
但是他们有一个共性就是,将用户的特征,内容的特征一通交叉计算,最后搞出来一个结果,用这个莫名其妙的数字表达了用户的点击意愿程度。


突然不那么万能的MLP

MLP 只是理论上可以拟合所有的函数,但是复杂场景,可能需要提前设计超级深的隐藏层为代价。这样无异加重了计算量,所以一般不这么直接搞。一般会提前提取一下特征,让MLP模型学精华就好了。

表征学习,我们与embedding 的再次相遇

还记得我们之前提到的是不是可以用一个模型自动化掉特征工程,然后把这个模型拼接在 MLP 上,是不是就同时解决了特征工程苦并且可以不用设计很深的 MLP 了。因为我们是用了一个新的模型做特征的学习,期望它自己学习到特征的表示,所以我们称之为表征学习(Representation Learning), 表征的直观形式就是一个 embedding,所以我们再次和老熟人见面了。
此时我们现在不明白怎么就学到表征了,只能假设是一个专门的神经网络在做,可能是一个 MLP?我们构想的开发模式从上演变成下边:

图40 表征学习的设想

图片表征

假设原始数据是一张图片,模型怎么学到图片里面的特征呢,比如有天空正在下着雪,雪下有一个湖,湖心有个亭子,湖中人鸟声俱绝,而我们正在亭中一边看雪,一边拿着毛笔写文章《湖心亭看雪》。
这其实一直都是 CNN(卷积神经网络) 在解决的问题,其模型很多, LeCun 早在 1998 年就发明了 LeNet-5,它的模型如下:

图41 cnn 表征学习
我们不用去理解每一层做了什么,只明白一个道理,原始图片到最后的全连接层(MLP) 中间的这几层是在做特征的提取,最终学到特征的正确表达,就是我们说的表征学习。
那 CNN 怎么进行的表征学习呢,核心是卷积层+汇聚层。还记得我们尝试的分词提取一句话的特征吗,句子本质是一个 N*1的数据,有一个滑动窗口在提取 token,图片可以理解为扩展成了 N*M,我们把这个窗口扩展成a*b(一个方块) ,让它像一个滚筒在图片上滚动,不停刷下特征来,称之为特征图,最终用一个汇聚层从特征图中提取显著特征。N 个卷积层+1 个汇聚层可以看成一个整体,我们可以把他们左手右手重复很多遍。
比如 alexnet vgg 都是如此,有一个主要思路是怎么像堆积积木一样把模型搭起来。

图42 从 alexnet 到 VGG
最后的特征数据(embedding)就可以会作为 MLP 的输入进行学习训练学习了。上图可以看到模型在最后的输出层是 1000 个节点,是因为当时alexnet vgg 两个模型参加了 ImageNet 大赛,模型需要从 1000 个分类中分辨出照片真实可能的类别。

文本表征

那文本又如何?
在前文我们剧透了 word2vec 就是将文本中学习到 embedding 表征。因为词不会脱离句子存在,我们可以把想要计算的词作为 loss,去掉这个词的句子作为输入。还是老样子,第一步先做特征工程,我们把词(token)进行one-hot 编码。
接下来借助一个一个滑动窗口,比如每次选择 2N+1 个 token,中间词作为需要预测的词,窗口内的其他词作为上下文。窗口不停的滑动,模型学不止。(这是一种方式称为CBOW,还有一种方式和这个是倒转关系,通过中心词来预测上下文,称之为Skip-gram)这里有一个细节上下文所有词的 embedding 做了平均,用来表达要预测的词。
就这样用大量的语料去训练,模型最后通过做这个填空题,明白所有词具体的意思是什么,为什么出现在这个位置。

图 43 词表征
如图中的例子,表达我每天喝咖啡,经过大量训练后的模型会通过 softmax准确的匹配到咖啡的概率是 62%。Wv*n 代表了模型学习到所有词的embedding表示,其中 V 是全量词表的大小,N 是我们想要最终 embedding 的维度,比如 512 维。
有了这个向量就可以做我们之前说的国王和女王的embedding计算了。 除了加减法还可以做相似度计算,方式有很多,比如余弦相似度,皮尔逊相关系数,欧几里得距离。
我们本次只介绍最简单的余弦相似度,余弦相似度的取值范围是 [-1, 1],其中 1 表示两个向量方向完全相同(夹角为0°),-1 表示完全相反(夹角为180°),0 表示两个向量正交,即无关联(夹角为90°)比如猫和狗是比较相近的词,余弦相似度表达不出来两个向量真正的距离,如果需要就要用欧几里得距离了。



图44 相似度计算

图谱表征

对于社交属性比较强的场景,我们可以构建领域的知识图谱。图谱的优势可以表达出不同用户之间的关系,并且可以依次洞察到潜在的关联。
我们之前提取一个用户特征的方式是分别看他的年龄、性别、出生地等等特征,如果把这个人放在图谱里,通过图谱表征,这个表征就可以表达出他的社交信息,一个人的表示突然就更立体了,听起来真不错。
那怎么提取呢?有点难,不过我们已经知道了 word2vec 从文本中提取了文本表征,通过 CNN 提取到图片表征。思路打开分别借鉴一下。前者称为随机游走DeepWalk,后者称为 GCN。
DeepWalk(论文:DeepWalk: Online Learning of Social Representations)核心思想是将网络中的节点看作是单词,将节点随机游走序列看作是句子,依赖这个图就可以产生无数的句子后作为样本,然后用类似于word2vec的技术来学习,最终获得节点的 embedding表示。

图45 从图到 embedding
图卷积网络(Graph Convolutional Networks 论文: Semi-Supervised Classification with Graph Convolutional Networks )用于从图结构数据中学习节点表示。这些表示不仅包含了节点的内在特征,还捕捉了其图上下文——即节点与其邻居的关联。GCN通过集合每个节点和它周围节点的信息来提取图的数据特征,这个过程有点像卷积神经网络(CNN)提取特征的方式。在 GCN 中,节点的特征不仅看自己的信息,还会受到邻居节点特征的影响,像是使用一个滑动窗口来聚合周围的信息。通过堆叠多个卷积层,GCN 能够逐步学习到更深层次的图结构信息,最后提取出重要的特征。

图46 GCN
GCN 这种利用了神经网络的算法统称为Graph Neural Networks,GNN。 GNN 领域算法很多,比如加了注意力机制的 GNN就是 GAT(Graph Attention Networks),具体可以参考一下阿灯老师的《知识图谱算法综述》,非常全面。综述论文可以看下《Graph neural networks: A review of methods and applications》

如何处理时序

单独用 MLP 其实处理不了时序数据。如下图,我们分词后输入模型其实破坏了本身时序行,隐藏层不会先处理卡再处理皮,无法理解卡在皮之前(其实 word2vec 也是如此,没有考虑词的顺序)。有没有办法,让模型按照时序处理特征呢。
   
图47 MLP 能处理时序吗?

RNN

那就一个一个 token 喂给模型,比如先给卡再给皮。
嗯,说干就干

图48 我们的假设
不过还要想办法把两个图连接起来,让模型先学了第一个 token 之后,在学第二个 token 的时候,仍然有上下文记忆。我们可以用一个状态记住数据,然后每一步学习的时候去更新就好了,我们称这个状态为隐状态h(hidden state)。
假设我们要模型把卡皮巴拉每一个汉字分别翻译成汉语拼音。h0 初始为 0。RNN 根据输入 "卡" 和隐藏状态 h0 计算出当前的隐藏状态 h1,并产生输出 "ka" 。
   
图49 RNN
我们发现模型只能一个输入对应一个输出,常见的翻译任务其实不太能做,因为不同语言分词后不是一一对应的,比如我输入汉字卡皮巴拉翻译成英文“capybara”
工程同学在架构中有一个很有趣且有用的方式,遇事不决加一个中间层,让它缓冲一下问题,比如消息中间件、API 网关。这种思路我们可以用一下,具体是我们让之前的 RNN 只做学习,学完后统一输出一份 embedding,新增一个模型去专门解码。
我们的例子,卡皮巴拉让模型先理解一波,先别着急输出,把最终的整体理解作为h0输入,让另外一个模型做输出。这其实就是 encoder-decoder 的思想,比较早的模型 seq2seq 就是在解决这个问题。

图50 seq2seq
但是 RNN 也有缺陷,比如输入超级长,比如输入 10000 个字,最后一个字的上一个隐状态,可能已经把第一字忘记了,记性不太好。有两种解决思路,1 选择性遗忘,把一些不重要的信息忘记,只记一些关键信息 2 增强记忆,长序列也能够访问过去的输入。前者是 LSTM 和 GRU 后者是自注意力机制。

LSTM


图51 lstm
LSTM单元包含三个门:遗忘门、输入门和输出门,这些门共同控制信息的流动。
遗忘门决定哪些信息被丢弃,输入门控制新输入信息的传入,输出门控制从当前单元到下一个时间步的输出。 另外有一个单元状态(cell state)贯穿在网络中传递,能够在各个时间步之间保持和传递信息。

GRU


图52 gru
GRU简化了LSTM的结构,它只有两个门:更新门和重置门。
更新门用来控制前一时间步的单元状态传入当前状态的程度,像LSTM的遗忘门和输入门的结合体,重置门用来控制忘记前一时间步的信息的程度。GRU提供了与LSTM类似的性能,但参数更少,也更好理解。

self-attention

注意力机制则提供了一种更灵活的方式来处理序列数据。它允许模型在每一个时间步都查看整个输入序列,并根据当前的任务动态地选择关注哪些部分。这样模型可以更直接地捕捉不同词之间的关联关系,而不仅仅依赖于序列中词的顺序位置。
我们还是用当面付的描述-“扫码或出示付款码支付,资金马上到账”,用 bert-chinese-base 模型(bert 基于 transformer-encoder,自带自注意力机制,是双向注意力机制)可以看到“资”这个 token 对其他 token 的有不同的注意力权重,所有token 平分了权重 1。

图53 直观感受自注意力
通过这张图我们看到是第 1 个头的注意力,第1层的注意力。嗯, 自注意力是一个组件,可以并行很多个,由它和 FFN 残差链接等组成的 block 可以重复 N 层。比如 bert-base 具有 12 个头,12 层。下图是第1 层第 5 个头的注意力表达:

图54 直观感受自注意力 2
多个并行的Self-Attention称之为Multi-Head Attention MHA,每个 head 都独立地学习不同的注意力权重,类似于多个朋友给我们建议,他们的关注点都不同,最后融合一下他们的不同建议,可以同多个角度理解事情。  

Native Sparse Attention


我们用了Self-Attention之后会发现挺好,但是有一个比较明显的感觉,就是每个 token 要关注其他 token,比如 100 个 token,就会产生一个 100*100 的注意力矩阵,文本一长就会因为参数多影响训练和推理速度。
图 54 我们会发现,很多值都在 0 附近,有研究发现,真正需要注意的 token 只有 20% 左右,注意力是sparse的。问题就变成了怎么减少这个 token 数量了。比如参考一下LSTM 的逻辑呢,某个 token有选择的关注一下其他重要的 tokens。
deepseek 最新的论文《Native Sparse Attention: Hardware-Aligned and Natively Trainable Sparse Attention》提到了一种分层的稀疏策略,将注意力机制划分为三个分支:压缩(把 token 序列切成多个块,每块用 MLP 压缩成一个 token,粒度很粗,可以理解为做了分块总结)、选择(挑选最关注的 N 个 token)和滑动窗口(保留最末尾的几个 tokens,越近保留的信息量越大),依赖这个设计使得模型能够同时捕获全局上下文和局部细节,从而提高了模型处理长文本的能力。

发明 transformer

关于ransformer是变压器还是变形金刚我现在也没搞明白,曾看过一个人站变形金刚这个观点,认为encoder-decoder 是汽车人一边拆一边重新组装做变身?

自注意力来源于transformer,transformer 又从我们上边提到的 seq2seq 进化而来,两者同出自 Google。关于原理相信大家已经麻木了,所以不再贴模型及解释了。船涨老师的《人工智能 LLM 革命系列 1 前夜:一文读懂横扫自然语言处理的 Transformer 模型》写的非常透彻。
我们前文已经边学习边科研搞出来很多东西了,现在是时候用已有的知识想办法自己搭一个transformer 出来。
第一步先简单特征工程,one-hot,然后把特征数据输入到 self-attention中做表征学习。学完之后输出到到 MLP 中学习,MLP 的最后一层的节点个数是词表的大小,其表达未归一化的分数,这些分数被称为 logits(logit蒸馏就是让 student模型学到这个数据分布),最后借助 softmax判断每个词的概率,最后选择概率最高的那个 token。
   
图55 我们的发明 1
第一版设想,试了下效果不是特表好,还是之前我们发明 MLP 的思路,再增加一点参数,貌似现在只能把子注意+MLP左手右手重复 N 遍了, 最后再用一层神经元把多个注意力学到 embedding 压缩到词表大小。然后得到了下边的模型:

图 56 我们的发明 2
继续加参数,我们可以把中间层再作为一个整体左手右手重复 N 次。这也是 transformer 的高可扩展性优点了,可以扩展成大参数模型的基础。

图57 我们的发明最终版
这其实已经表达出了 tranformer-decoder 模块的核心了,我们可以自定义指定MHA 的层数集每个 MHA 中自注意力的头数。可以把神经网络设计的超级深了,但是深了会有两个问题,怎么加快训练让模型收敛,模型很容易梯度消失。所以会加两个组件在 MHA 中 Layer Normalization和残差连接,这两个兄弟算是深度学习模型中的基础组件了,就是我们 transformer 模型图中见到的 add&norm。另外输入MHA 之前,会加上位置编码(Positional Encoding),让模型真正了解我们输入的每个词的顺序。另外会对 MHA 做一点改造,确保模型在预测下一个词时,不能看到后续的词,防止未来信息泄露,实现单向注意力,称之为Masked Multi-Head Attention。
最后我们看一眼真正的 transformer 模型图中的 decoder 模块,对比下我们的发明和本尊。
 
图58 真实的 transformer  

魔改 transformer

另外需要注意的是 17 年这个模型就来了,截止到目前已经 8 年时间了,不同的人肯定会有不同的理解,世界在进步,模型在进步,不会存在一个绝对牛的东西存在,transformer 代表的大模型是不是 AGI 的唯一路径,以及大模型本身会不会抛弃 transformer 另寻出路,这个都是未知。
目前已知的 transformer魔改思路如下 下图左边Model-level 部分,不同的工作把每个组件换了个遍,比如我们之前提到sparse attention:

图59 来源于论文《A Survey of Transformers》


学成归来,重新做推荐

我们之前科研的模型上线后,会产生很多用户的行为数据,比如某个用户在周一看了篮球鞋,周二看了篮球,周三又看了篮球鞋,我们可以大致猜到他想换一身打篮球的装备了。这个行为序列的数据很有用,可以想办法用起来。
用户id
用户历史访问的商品列表
当前查看的内容
是否发生点击
1001
篮球鞋,篮球,篮球鞋
篮球

怎么给用户历史访问的商品列表做特征工程呢,比如用 muti-hot,显然体现不出来用户访问的时序,用户的兴趣是稍纵即逝的,所以还是想办法,专门对历史数据做一下兴趣提取。

时序模型

DIN

用户的兴趣建模,比如我们怎么知道用户当前喜欢什么,下一个喜欢的东西什么,捕捉用户的兴趣变化,其本质是用户此刻在注意什么,这个时候首先想到了万能的注意力机制。

图60 din
这就是大名鼎鼎的Deep Interest Network,简称DIN,出自阿里巴巴。模型的关键创新之处在于其对用户历史行为数据的处理。通过引入一种注意力机制(其和 transformer 同期,都发表在 17 年 6 月,所以 DIN注意力机制是另外一套方案),DIN能够动态地对于不同的候选商品赋予不同历史行为数据相应的权重,以便突出与当前预测最相关的用户兴趣点。
阿里深耕电商,在兴趣建模这个领域影响力十足,有用户行为序列肯定就会面临超长序列的问题,另外其注意力机制也在演进。
演进过程有引入 GRU 的DIEN, 将用户的行为分割成不同的“会话”(session),并引入自注意力机制的 DISN,应对一个人可能有多种兴趣的MIND,以及超长序列的的 SIM。

Interest Clock

来自2024年抖音的论文,Time Perception in Real-Time Streaming Recommendation System,提到基于自己场景设计的时钟兴趣推荐模型,抖音认为用户在一天 24h 的兴趣是不同的,所以用不同的时钟兴趣表达用户。
具体做法是通过构建小时级个性化偏好特征,利用正态分布对用户的时间感知偏好进行平滑处理(之所以平滑是希望音乐风格可以平滑切换),形成兴趣时钟嵌入。然后,这些嵌入被输入到 DCN v2 深度模型中进行预测。抖音将这个推荐算法应用在了自家的汽水音乐。

图61 兴趣时钟

拿来主义

我们知道  gru bert transformer 都是在语言领域(NLP)应用广泛的模型。如果我们把用户的行为数据理解成连在一起的tokens,行为变成了一段文本,文本又天然具有时序,所以可以稍加改造就可以用来做时序推荐了。这个思路下有一堆这种推荐模型,GRU4Rec,BERT4Rec,BST(transformer4rec)。

图62 BST
这些模型属于格局大开,世界万物皆可为我所用。


注入更多的知识

我们重新审视一下这些模型,从知识角度来看,模型的发展是为了加入更多的知识,模型因为学的多进而懂得多。我们已经通过注意力机制引入用户的行为数据,有没有可能引入用户的社会关系等知识图谱数据。
我们之前聊的图谱表征学习就可以用上了,我们可以将图谱中的用户、内容计算 graph embedding 之后作为特征输入到一个 MLP (或者更复杂选一个,dcn deepfm 都行)。

LSTTM

2022年腾讯依赖其丰富的用户图谱信息构造了两张图-长期行为图谱和短期的图谱,分别利用 GAT 捕捉兴趣计算 embedding,最后作为 deepfm 的特征进行学习,腾讯把该模型引用在了微信的内容推荐。

图63 LSTTM


停下来稍作总结

经过上边的学习,我们学了很多东西,可以简单总结成几点:
  • 深度学习的一个重要范式,可以总结为 embedding+MLP。前者可以通过模型学习到表征,针对不同的模态有不同的方法,比如图片-CNN,文字-word2vec、transformer,图谱-GNN ,减少了特征工程的工作。后者 MLP 代表了深度学习模型,我们可以根据自己的诉求去定制化它。
  • 有了这个范式我们就可以分别去做优化了。提升embedding的准确性,并开发一个强大的模型以进行有效学习。直观地说,就是要将更多的知识以最高质量的形式融入到一个更强大、参数更多的模型中,让其学到更多,做的更好。
  • 有了这个 embedding,其实可以单独用的,可以放在向量数据库中,就可以做向量相似度召回,比如有了句子的 embedding 就可以提供一个 RAG 方案了。
  • 我们的目标是让模型预测用户的点击概率。如果业务同时需要预测点击率和转化率,那么模型就需要具备多任务学习的能力,能够综合考虑这两个任务的loss。
  • 注意力机制出来后,所有的模型都在 transformer 化,后边我们会借着多模态,介绍 transformer 接管了 CV。
学完并且简单消化之后,可以开始做事了,我们还要给服务商推荐内容。


我们的实践

选模型

第一版本做了基础的特征工程后,先选了下模型,进行无数次了实验,发现 dcnv2 好点,所以我们选了这个模型。

图64 实验对比
通过我们之前的了解,有几个思路可以去优化。
1.建设领域知识图谱,将用户和 item 放在一张图中,让其在一个空间,通过 GNN 获取 graph embedding。
2.对于时序数据用 transformer 的 mha 来提取兴趣表达

知识图谱

对于什么是知识图谱,这里简单介绍下。一开始我以为是一个某种算法,其实知识图谱本质是上一个用了各种算法构建图谱的方式,本质上是工程+算法的聚合体。    

图65 知识图谱流程
如图不同的类型的数据会通过比如NLP 算法提取关键信息后存入到图数据库,依赖这个图数据库构造出领域图谱,并在图谱上做 GNN 等算法推理。
我们首先依赖蚂蚁的知识图谱平台-知蛛搭建了支付宝服务商领域的知识图谱。

时序特征

通过 transformer 的多头注意力 MHA学习了时序特征后添加一路时序特征输出给 dcn v2。我们发现加入后,模型的性能是提升明显的。 mcc 提升0.31,F1 提升 0.25,recall 提升 0.24。

更合适的 loss

在我们场景正负样本差异较大,正负比例 10:1。所以我们用了一个更适合的 loss--GHMC,减少训练偏差。    

更丰富的标签

此时大模型已经非常成熟了,所以我们借助大模型产生文本标签,具体思路是我们预置了 N 个标签,让大模型通过描述文本,选择合适的多个标签。然后标签特征工程后输入到模型。


美好假设

我们看一下作为打工人是怎么做事的。面对一个新的的任务,我们往往不需要从头开始学习,之前的很多经验都可以做到复用,继续偷懒到底。回到算法模型这里:
  • 每次有了需求都要重新训练一个模型,是不可以存在一个基础模型,它具有基本认知能力,等新任务来了,只需要稍加训练(迁移学习)就可以快速适应新任务,真正的做到重复使用。


  • 如果用全人类的知识+目前最强大 transformer 模型,是不是就可以可以训练出一个史无前例的模型。
后一个想法其实是大模型的思路,下一章我们会借着大模型的演进,将深度学习带入预训练模型+迁移学习的范式

53AI,企业落地大模型首选服务商

产品:场景落地咨询+大模型应用平台+行业解决方案

承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

添加专属顾问

回到顶部

加载中...

扫码咨询