保姆级教程:Embedding本质是什么?

和大部分人一样,我对自然语言处理和语言模型的了解从 ChatGPT 开始。也和大部分人一样,第一次接触就被 ChatGPT 的能力所震惊,硅基智能确实做到了理解人类的语言。

我也产生了几乎人人都会有的疑问:怎么做到的?硅基智能潜力是否会远胜于碳基智能?

在这篇文章中,我并不试图去解释 ChatGPT 的一切,而是将从原理出发,思考计算机理解语言的关键要素,这些思考落到了一个具体的切入点:embedding,一个第一眼难以理解但极为关键的东西。

01

编码:文字的数字化

Embedding 这个词直译为中文是:嵌入,这是让人头秃的两个字:啥是嵌入?嵌入了啥?跟自然语言又有啥关系?

嵌入的体现形式是一组具有固定长度的数组,或者叫做向量,但它究竟是什么?为什么需要它?它在计算机理解自然语言的过程中扮演的是怎样的角色呢?

要回答这些问题,不妨先思考:让计算机理解自然语言,我们需要做什么?

计算的基础是数,而自然语言是文字,因此很容易想到要做的第一步是让文字数字化,为行文方便,我们将这个过程叫做编码。

要设计编码的方法,自然需要思考的问题是:哪些性质是编码规则必须要满足的?

有一条是显然可以给出的:

性质一:每一个词具有唯一量化值,不同词需要具有不同的量化值

背后的逻辑不言自明:一词多数,或是多词一数,都会增加计算机理解语言的难度,这种难度就如同多音字或是多义词给人类造成的困难,尽管人类的智慧让我们可以克服这些障碍,但对于仍然处于培育智能阶段的计算机,为它降低一些难度显然是必要的。

满足性质一的方法非常容易设计,例如:首先穷举出人类所有的文字或词组,这个集合必定是有限集。

例如汉字有 10 万个,辞海收录的词大概 60 万个,字母有 26 个,英语单词数小于 100 万个,由于是有限集,我们可以给每一个词分配一个固定的数字。

例如打开一个词典,将遇到的单词依次赋予一个不同的数值:

A --> 1
Abandon --> 2
Abnormal --> 3
...

这便完成了符合性质一的编码。例如 “Hello World” 这句话就可以作为 ”3942 98783“ 这样的数字序列输入,从而可以被计算机处理。

但这一方法存在的问题是显然的:数的值与词的义是割裂的

这种割裂会产生什么问题?可以通过一个简单的例子来思考:在英语中,a 和 an 是完全同质的词,而 a 和 abnormal 则是差异极大的词。

如果按照上述编码方式, a 可能会被赋予数值 1,abnormal 会被赋予数值 2,an 会被赋值赋予数值 123 ,这个时候我们可能会发现 a 和 abnormal 似乎在数值上更加靠近,而 a 和 an 这两个同质的词却隔得非常远。

这时容易想到要添加一条性质,来确保数字化后的数值与词义之间的关联:

性质二:词义相近词需要有"相近"的量化值;词义不相近的词量化值需要尽量“远离”。

02

基于词义的编码

上面的例子中虽然提到了字典编码法会割裂数值和词义,却未能解释为什么数值和词义应该关联,基于直觉的思考会认为这一点是显然的,但模糊的显然容易掩埋值得被清晰梳理的逻辑。

我能够想到的原因有两个:

  • 可以帮助更加高效理解语义;
  • 允许计算模型的设计有更大的自由度。

第 1 条怎么理解?如果说词的数值分布与词义无关,这会使得文本的序列变得过于随机,例如:

句子一:张三在讲话。
句子二:李四在发言。

这两句话有着非常强的同质性,但如果对于字/词的编码不符合性质二,这就会使得以上两句话的序列特征会有非常大的差异。

以下的例子或许足够直观:如果近义词具有相近的量化值,词和值之间的关系或许会是这样,看起来就是相似的形状。

张 --> 105, 李 --> 99
三 --> 3, 四 --> 4
在 --> 200,
讲话 --> 300, 发言 --> 295

在这里插入图片描述

而如果近义词具有不相近的量化值,词和值之间的关系或许会是这样,一眼看上去似乎没什么关系:

张 --> 33, 李 --> 1
三 --> 5, 四 --> 200
在 --> 45,
讲话 --> 2, 发言 --> 42

在这里插入图片描述

换言之,当性质二得到满足时,同义的句子在序列特征上会更加接近,这将有利于计算机而言更高效地理解共性、区分特性;反之则会给计算机制造非常多的困难。

难以捕捉同质内容之间的共性,就意味着模型需要更多的参数才能描述同等的信息量,学习的过程显然困难也会更大。

OpenAI 的 Jack Rae 在 Standford 的分享中提到了一个很深刻的理解语言模型的视角:语言模型就是一个压缩器。这个观点已有不少文章都做了阐释。

所有的压缩,大抵都能被概括在以下框架内:提取共性,保留个性,过滤噪声。带着这个视角去看,就更加容易认识到性质二的必要性。

不同词所编码的数值,是否基于词义本身的相似性形成高区分度的聚类,会直接影响到语言模型对于输入数据的压缩效率。

在这里插入图片描述

编码值未基于词义形成聚类

在这里插入图片描述

编码值基于词义形成聚类

第 2 条怎么理解?

因为词是离散分布的,而计算模型的输出,除非只使用非常简单的运算并且约束参数的权重,很难恰好落在定义好的量化值中。

对于神经网络模型,每一个节点、每一层都必须是连续的,否则便无法计算梯度从而无法应用反向传播算法。

这两个事实放在一起可能会出现的情况是: 词的量化值可以全部是整数,但是语言模型的输出不一定。例如当模型输出 1.5,词表只定义了 1 和 2,这时该如何处理呢?

我们会希望 1 和 2 都可以,甚至 3 可能也不会太离谱,因此 1 和 2 所代表的词在词义上最好有某种共性,而不是像 “a” 和 “abandon” 一样,几乎找不到词义上的关联。当相近的词聚集到一起,推断出有效输出的概率就会更高。

在这里插入图片描述

理解了这一点,GPT 模型的最后一层就非常容易理解了。在最后一层之前,推理的对象是以向量形式表征的语义,输出的是代表语义的一个“模糊”的向量。此处“模糊”指的是,这一向量或许并不对应任何一个已知的词。

因此,整个模型最后需要再做一个推测,基于这个“模糊”的向量所包含的语义信息,在词表中寻找最符合这些特征的词,来作为真正的输出。

在 transformer 中,最后的输出是一个概率分布,表示每一个词匹配这一“模糊”向量的概率。

现在我们知道了性质二是必要的,在考虑这一点的基础上是否有可能再抢救一下字典编码法?比如… 找一本近义词字典,针对相近的词赋予相近的数?

问题很快也就出现了:A 和 B 词义相似,B 和 C 词义相似,似乎并不意味着 A 和 C 词义也相近。

例如:

A = ”Love“,B = ”Passion“,C = "Rage"
A = ”Comedy“,B = ”Play“,C = "Game"

在这两个案例中,A 和 B 都是接近的,B 和 C 也是接近,但 A 和 C 却不是。问题在哪呢?

**词义的多维性:**当用一个标量来表示一个词时,词和词之间的关系只能基于两个标量间的差值得到,从而只有“远”和“近”两种状态;但实际情况可能是:两个词只在某些维度上接近。

“Love” 和 “Passion” 接近的地方是:情感浓度,都表示存在强烈的情感,但是在情感色彩方面,也就是消极还是积极,passion 具有更加中性的色彩,于是同样具有浓烈情感的 “Rage” 也与 “Passion” 相近,但是 “Rage” 的情感色彩却是消极的。

于是我们需要一个多维的数字形态,很自然会想到使用向量,对于每一个词,我们可以表达为一组数,而非一个数;这样一来,就可以在不同的维度上定义远近,词与词之间复杂的关系便能在这一高维的空间中得到表达。

这就是 embedding,它的意义也就不言自明了。“嵌入”这个名字太糟糕了,不如叫它“词义向量” 吧;而词义向量所处的空间,可以称为“词义空间”。

03

如何设计编码器

目前为止,我们已经找到了可以用于表达词义的数字化形式:向量,也知道了一个好的编码方式应当满足的性质。如何设计一套方法,来完成我们所期望的编码,就成了最后的问题。

一个比较容易想到的方法是,令词义的不同维度和向量不同维度进行关联。

例如,对词义的维度进行全面的拆分:名词性、动词性、形容词性、数量特征、人物、主动、被动、情感色彩、情感强度、空间上下、空间前后、空间内外、颜色特征…

只要维度的数量足够多,一定是可以把词义所包含的信息全都囊括在内;一旦我们给出每一个维度的定义,就可以给出每个词在相应维度上的数值,从而完成词的向量化,并且完美地符合以上给出的两点性质。但这个看似可行的设计,并不具备可实现性。

首先是要能够囊括所有词义的不同维度,需要维度数量必然是极高的,而要对词义进行这么精细的切分,就非常困难,其次即使切分出来了,要将每个词不同维度的意义赋予有效的数值,哪怕是资深的语言学家恐怕也会难以感到棘手。

今天大家所熟知的语言模型中,并没有一个是用这一方式对词进行向量化的。但是这个思想方案却是有意义的,词义向量的不同维度之于计算机,就如同上面我们列举的维度:词性、数量、时间、空间等等,之于人类。

纯构建的方式不可行,今天我们也已经知道了一套有效的解决办法:神经网络加大数据暴力出奇迹。这套范式的起源于是:Word2Vec

今天语言模型,无一不是基于词义向量,而词义向量真正开始有效,正是从 Word2Vec 开始。

图片

Word2Vec 的关键是一个重要的洞察、一个极具启发性的角度:一个词的意义,可以被它所出现的上下文定义

这句话换一种说法又可以表述为:上下文相似的词在词义上也一定存在相似性。想一想是不是很有道理?

这个观点是语言学家 Zellig Harris 在1954 提出的“Distribution Hypothesis”,随后被广泛接受。

Word2Vec 的两类做法分别是:

  • 中心词 --> 神经网络 --> 上下文
  • 上下文 --> 神经网络 --> 中心词

今天回头看,这个工作从一开始就注定了成功:原理上,是基于广泛接受的“Distribution Hypothesis”;方法上,使用了拟合能力强大的神经网络模型;最重要的,数据要多少有多少。

这个方法当然不是终点,它的局限性是明显的,但开创性已经足够了,只是利用和挖掘了“Distribution Hypothesis”的浅层结构。怎么理解这句话呢?

本质上是因为 Word2Vec 并没有尝试去理解句子内的语义。因此对于完全相同的上下文,不同的中心词的词义相似性是容易捕捉的;当词义向量的聚类逐渐形成,由近义词构成的上下文,也一定程度上能够标记词义相近的中心词。

但人类的语言结构非常复杂,当相同语义通过不同句式、语态、修辞进行表达时,某些近义词对的关系就会可能被深埋。

看看 ChatGPT 举的这个例子:

句子 1: Driven by an insatiable thirst for knowledge, she stayed late every night, her eyes dancing across the pages of books as if they were starry skies.*

句子 2: Isn’t it unusual, that she, prompted by an unquenchable intellectual curiosity, burns the midnight oil, pouring over pages as though navigating constellations?*

两个句子都在描述一个女性深夜仍在阅读,驱使她的是对知识的无尽渴望,两句话也存在非常多意义相近的词对,在不理解语义的情况下,这些词对之间的相似性是难以被辨识的。

接下来我们可以讨论 GPT 了。

它是一个有能力理解句子的模型。如果说此前讨论的 Word2Vec 这类构建词义向量的模型是教计算机“认字”的过程,那么 GPT 模型的训练,则是一个“认字”+“背书”的过程。老师最后只考书背的好不好,但为了把书背好,GPT 也被动地强化了其认字能力。

推理的核心是 transformer,transformer 的核心是 attention 机制,attention 机制是什么?

一言以蔽之:计算词义向量之间的“距离”后 ,对距离近的词投向更多注意力,而收到高注意力的词义则获得更高的激活值,当预测完成后,通过反向传播算法:当特定的激活帮助了最终的预测,对应词之间关联将被强化,反之则被弱化,模型便是通过这一方式学到了词之间的关系。

而在“Distribution Hypothesis”这一视角下,“认字”的实质就是认识一个词和其它词之间的关系。于是就形成了认字为了背书,背书帮助认字的结构。

这里提炼一个我个人的观点: attention 机制之所以重要和好用,原因之一是可以有效帮助词义向量(embedding)聚类。

GPT 的例子想想其实很有趣,一般的工程思维是将大的问题拆成多个小的问题然后一个一个解决,正如文中开始说的那句:

让计算机理解自然语言,我们需要做什么?

计算的基础是数,而自然语言是文字,因此很容易想到要做的第一步是让文字数字化…

这个表述隐含了一个解决问题的路径:先将文字数字化后,考虑理解句子的问题。

有趣的地方是:对词进行向量化编码的最好方法,是直接训练一个理解句子的语言模型;这就像为了让婴儿学会走路,我们直接从跑步开始训练。

人类会摔跤会受伤,但机器不会,至少在 embodied 之前不会,因此人类为了降低代价所建立的步骤化学习过程或许并不适合人工智能,也不难发现,深度学习中,许多好的解决方案往往都是一步到位的。

04

总结

这篇文章把我关于语言模型中 embedding 的理解都介绍完了。但 embedding 还不止这些。

图像可以有 embedding,句子和段落也可以有 embedding,本质都是通过一组数来表达意义。

段落的 embedding 可以作为基于语义搜索的高效索引,AI 绘画技术的背后,有着这两种 embedding 的互动,未来如果有一个大一统的多模态模型,embedding 必然是其中的基石和桥梁 。

图片

由 AI 掀起的时代浪潮毫无疑问地要来了,今天是一个还难以看清未来的节点。当下能做的为数不多的事情之一还是保持学习。希望这篇文章可以帮到正在学习的你。

如何学习AI大模型?

作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

这份完整版的大模型 AI 学习资料已经上传优快云,朋友们如果需要可以微信扫描下方优快云官方认证二维码免费领取【保证100%免费

一、全套AGI大模型学习路线

AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

img

二、640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

img

三、AI大模型经典PDF籍

随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。

img

四、AI大模型商业化落地方案

img

作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量。

<think>我们正在处理一个SQL语法错误问题。用户提供的SQL语句是: INSERT INTO public.vector_store (id, content, metadata, embedding) VALUES (?, ?, ?::jsonb, ?) ON CONFLICT (id) DO UPDATE SET content = ?, metadata = ?::jsonb, embedding = ? 这个语句在PostgreSQL中用于插入数据,如果发生主键冲突(即id已存在)则更新其他字段。 但是,用户报告了“bad SQL grammar”错误。我们需要检查这个语句的语法是否正确。 在PostgreSQL中,ON CONFLICT子句的语法是: ON CONFLICT (conflict_target) [WHERE condition] DO UPDATE SET { column_name = { expression | DEFAULT } } [, ...] [WHERE condition] 注意:在UPDATE SET部分,我们需要为每个要更新的列指定新值。这些新值可以来自EXCLUDED虚拟表,该表包含被拒绝的行(即由于冲突而未能插入的行)。 在用户提供的语句中,UPDATE SET部分直接使用了参数占位符(?),但没有指定这些值应该来自哪里。实际上,在更新时,我们通常使用EXCLUDED来引用原本要插入的值。 正确的做法应该是: ON CONFLICT (id) DO UPDATE SET content = EXCLUDED.content, metadata = EXCLUDED.metadata, embedding = EXCLUDED.embedding 或者,如果我们使用参数,那么参数应该与插入部分一一对应,然后在更新部分使用相同的参数。但是,在标准的JDBC中,我们无法在UPDATE SET部分直接引用插入的参数。因此,通常的做法是在更新部分使用EXCLUDED。 然而,在JDBC中,我们使用预编译语句,参数占位符(?)是按位置索引的。在原始语句中,插入部分有4个参数(id, content, metadata, embedding),更新部分又有3个参数(content, metadata, embedding),总共7个参数。但是,在更新部分,我们实际上应该使用插入时提供的值,所以不需要额外的参数。因此,我们可以使用EXCLUDED来引用这些值。 修改后的语句应该是: INSERT INTO public.vector_store (id, content, metadata, embedding) VALUES (?, ?, ?::jsonb, ?) ON CONFLICT (id) DO UPDATE SET content = EXCLUDED.content, metadata = EXCLUDED.metadata, embedding = EXCLUDED.embedding 这样,我们只需要4个参数(对应插入的4个值),更新部分使用EXCLUDED虚拟表来获取这些值。 因此,错误原因可能是:原语句中更新部分使用了额外的参数占位符,而实际上这些值应该来自EXCLUDED,不需要额外参数。另外,在更新部分,我们不需要再次指定类型转换(如?::jsonb),因为EXCLUDED.metadata已经是jsonb类型(因为插入部分已经转换了)。 所以,正确的SQL语句应该是: ```sql INSERT INTO public.vector_store (id, content, metadata, embedding) VALUES (?, ?, ?::jsonb, ?) ON CONFLICT (id) DO UPDATE SET content = EXCLUDED.content, metadata = EXCLUDED.metadata, embedding = EXCLUDED.embedding ``` 这样,我们只需要提供4个参数,而不是7个。 另外,注意在插入部分,我们将第三个参数转换为jsonb(?::jsonb),这是正确的,因为metadata字段是jsonb类型。在更新部分,EXCLUDED.metadata已经是插入时转换后的类型,所以直接使用即可。 如果用户使用的是JDBC,那么预编译语句的参数设置应该如下: ```java PreparedStatement pstmt = connection.prepareStatement(sql); pstmt.setString(1, id); // 或者setObject,根据id的类型 pstmt.setString(2, content); pstmt.setString(3, metadataJson); // 注意:这里传入的是JSON字符串,因为我们会将其转换为jsonb pstmt.setObject(4, embedding); // 假设embedding是一个向量,可能是float数组或PGvector类型 ``` 这样,总共只有4个参数。 总结:原SQL语句错误在于更新部分使用了额外的参数占位符,而应该使用EXCLUDED虚拟表来引用插入值。修改后,参数数量减少到4个,且语法正确。 如果用户之前使用的是7个参数,那么现在只需要4个,需要调整代码中设置参数的部分。</think>### SQL语法错误分析与修正 您遇到的SQL语法错误是由于`ON CONFLICT`子句的使用方式不正确导致的。在PostgreSQL中,当使用`ON CONFLICT DO UPDATE`时,更新部分应直接引用`EXCLUDED`虚拟表来获取插入值,而不是重复使用参数占位符。 #### 错误原因分析: 1. **冗余参数**:原始语句在`UPDATE SET`部分重复使用了参数占位符`?`,但实际只需引用`EXCLUDED`虚拟表 2. **类型转换重复**:`::jsonb`类型转换在`UPDATE SET`部分重复声明,而`EXCLUDED`已继承插入时的类型 3. **参数数量不匹配**:语句声明了7个参数(4个插入+3个更新),但实际只需要4个参数 #### 修正后的SQL语句: ```sql INSERT INTO public.vector_store (id, content, metadata, embedding) VALUES (?, ?, ?::jsonb, ?) ON CONFLICT (id) DO UPDATE SET content = EXCLUDED.content, metadata = EXCLUDED.metadata, embedding = EXCLUDED.embedding ``` #### 关键修改说明: 1. **使用`EXCLUDED`虚拟表**: 通过`EXCLUDED.column`直接引用冲突发生时的**待插入值**,无需额外参数[^1] 2. **简化类型声明**: 移除`UPDATE SET`中的`::jsonb`转换,因`EXCLUDED.metadata`已继承插入时的JSONB类型 3. **参数数量调整**: 只需4个参数(对应4个VALUES),而非原来的7个参数 #### JDBC参数设置示例(Java): ```java PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, "id_value"); // ID stmt.setString(2, "content_value"); // 内容 stmt.setString(3, "{\"key\":\"value\"}"); // 元数据(JSON字符串) stmt.setObject(4, embeddingArray); // 向量数组 stmt.executeUpdate(); ``` #### 注意事项: 1. **主键要求**: `ON CONFLICT (id)`要求`id`字段必须有唯一约束或主键索引[^1] 2. **JSONB处理**: 元数据参数应传入**有效的JSON字符串**,PostgreSQL会自动转换 3. **向量类型**: 确保`embedding`参数类型与数据库中的向量类型(如`vector`或`float[]`)匹配 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值