谈论AI 时被经常提及的“嵌入(embedding)”和“向量(Vector)”到底是个啥?
随着 ChatGPT 等 AI 应用的持续火热,讨论到相关话题时,相信大家都经常会听到几个词「嵌入(embedding)、向量(Vector)」,这东西到底是个啥?从很多的交流中可以发现,其实大多数人对这个东西都还是似懂非懂的状态。
我们先来说,什么叫嵌入(embedding),通俗一点讲,就是把文本的意思嵌入向量里,也就是用向量来表示文本的含义。「当然向量里面能存的可不仅仅是文本,我前面只是以文本举例子,像音频、视频都是可以用向量表示的。」
那为啥要转向量呢?因为转向量后,这样我们就可以通过一些算法来计算文本的相似程度「比如余弦相似度(Cosine Similarity)、欧氏距离(Euclidean Distance)等等」
那又如何把文本转化成向量呢?转化的过程其实就是文本特征提取,要用专业名词就是词嵌入。这个过程需要特定的模型「比如词袋模型、TF-IDF等」来帮我们完成。
在上面的这个转化的过程当中,使用的技术方法不同,最终的效果也是不同的,比如为什么 ChatGPT 会这么智能,就是因为 GPT 模型使用的并不是传统的嵌入技术,而是一种叫做「上下文词向量表示法」的方法,这个方法的关键在于,每个单词的向量表示不仅取决于这个单词本身,还取决于这个单词在文本中的上下文。也就是说,同一个单词在不同的上下文中可能会有不同的含义,而这种含义的差异能够被GPT的向量表示捕捉到。举个例子,“苹果”这个词在讨论水果时和在讨论电子产品(如苹果电脑)时的含义就完全不同。
这种表示方法被称为 Transformer 的自注意力机制,可以很好地捕捉和理解复杂的语言模式和语义关系。据说 GPT 模型就是在大规模的文本数据学习中自己学会的这个方法,现在大家应该有点明白为什么 ChatGPT 的理解能力和逻辑能力很强了吧?
那说了这么多的“向量”,“向量”到底是个啥?
在数学中,向量是一种表示有大小和方向的量的工具。如果把向量想象成一支箭头,那箭头的长度代表了大小,箭头的指向就代表了方向。
但在机器学习中,向量就变得更抽象了,抽象在哪呢?它会被用来表示更复杂的信息「不像数学中只有大小和方向」,如词语的意思、句子的含义、甚至整篇文章的主题等等。
比如,我们可以把一个单词转化成一个向量,这个向量的元素可能表示这个单词的语法角色(比如它是名词、动词还是形容词)、语义特性(比如它是表示快乐的,还是表示悲伤的)、甚至是它和其他单词的关联程度等等。
我们可以简单把向量理解为一种数据的编码方式,每个维度都可以被认为是一个特性或属性,这些特性或属性共同描述了一个数据点(例如,一个词、一个句子或一个文档),要注意的是,这些信息并不是直接编码的,因此我们不能直接从一个词向量中读出这个词是一个名词还是动词,或者这个词表示的是快乐还是悲伤。相反,这些信息是通过词向量在高维空间中的位置和方向隐含的。如果觉得这里不好理解,我们尝试把向量想象成一种复杂的密码。这个密码是一串数字,但你不能直接看着这串数字就知道它代表的意思。相反,你需要一种特殊的工具或者方法才能解读这个密码。
最后我们通过一个实例来进行说明,我们使用 OpenAI 的 embedding-ada-002 模型「接口 https://api.openai.com/v1/embeddings」来创建三个向量,一个表示‘苹果’,一个表示‘梨子’,一个表示‘电脑’:
‘苹果’如下
[
0.011903401, -0.023080304, -0.0015027695, -0.0205655, 0.0051274044,
0.0037442625, -0.031882115, 0.0006243348, 0.009241901, -0.022675142,
0.0007037956, 0.021054491, 0.004872431, 0.011023221, -0.015689578,
0.012203781, 0.0112467585, -0.003702349, 0.016304307, -0.026642943,
-0.034089554, -0.004407891, 0.0075863227, -0.012175838, -0.009521324,
0.011190874, 0.016625643, -0.021403769, 0.010296722, 0.0012652603,
0.03674407, -0.0036534502, -0.016402105, -0.0072230734, -0.0049632434,
-0.018092612, -0.016485931, -0.015186616, 0.010422462, -0.013950172,
0.0046873135, 0.012413348, -0.014208637, 0.003814118, -0.016416077,
0.014865281, -0.027090019, 0.007830817, -0.025818646, 0.006203181,
-0.011344557, -0.010841596, -0.029982042, -0.0002543182, -0.0004846235,
-0.003670914, -0.010275765, 0.008124211, -0.028040055, 0.0004093104,
0.005979643, 0.023513408, -0.034955762, 0.033670418, -0.010939393,
-0.00084699964, -0.01784113, -0.022381747, 0.020509617, 0.028556986,
0.02414211, 0.028277565, 0.023261929, 0.012140911, 0.002916473,
0.00079024193, -0.02945114, -0.011309628, 0.009633093, -0.001005048,
0.028123882, -0.025469366, 0.005839932, -0.006524517, 0.015284414,
0.01244129, -0.036604356, 0.020872867, -0.011079105, -0.0115191955,
0.01497705, 0.025287742, 0.006845853, 0.02514803, -0.018092612,
0.034760166, 0.013461182, 0.033195402, 0.0012268397, -0.025525251,
... 1436 more items
]
‘梨子’如下
[
0.011790048, -0.017644417, -0.014175162, -0.02421703, 0.0018853913,
0.020137945, -0.0070401495, 0.005607049, -0.007636428, -0.04531715,
-0.004983667, 0.013877022, -0.02009729, 0.007372168, -0.02002953,
0.0035234627, 0.03434021, -0.009228762, 0.030491505, -0.024433859,
-0.003804662, -0.03130461, 0.0062439824, -0.023959545, -0.0015609956,
-0.009750505, 0.037944984, -0.02009729, 0.013809264, 0.017779935,
0.027699837, 0.009581109, -0.012759, -0.0033049402, -0.022658575,
-0.03030178, -0.0010307822, -0.004001163, 0.0017617313, 0.002893305,
0.0019904177, 0.0012281301, -0.0066742515, -0.0049023563, -0.023661407,
0.00872057, -0.03531594, -0.015096682, -0.016546723, 0.014283576,
-0.004688916, -0.008842536, -0.0069859424, 0.0011519012, -0.007053701,
0.009757281, 0.019202871, -0.014934061, -0.004472087, 0.0044551473,
0.019582322, 0.022441747, -0.03325607, 0.010035093, 0.0015889462,
0.016262135, -0.014825647, -0.0006335457, 0.011356391, 0.004970115,
0.013036812, 0.023187095, 0.02542314, 0.014879854, 0.006958839,
-0.0004156585, -0.03244296, -0.008083637, -0.014080299, -0.019921117,
0.018836973, -0.014283576, -0.012650587, -0.008137844, 0.015882686,
0.0011417374, -0.033337377, 0.003994387, -0.0034573977, -0.012901294,
0.025626415, 0.0056714197, 0.011762945, 0.025572209, -0.00921521,
0.011417374, 0.008280138, 0.013890574, -0.012305016, -0.024176374,
... 1436 more items
]
‘电脑’如下
[
-0.0089573935, -0.018338805, 0.004793398, -0.030820854, -0.0116141355,
0.015277921, -0.022181474, -0.017225757, 0.00021987685, -0.019915625,
0.02231398, 0.03206641, 0.0019511482, 0.010388456, -0.014747898,
0.018352056, 0.009282033, -0.010567339, 0.028117735, -0.015662188,
-0.024844842, 0.009686176, 0.018484563, -0.023599287, -0.024831591,
0.0011610823, 0.018511062, -0.017888285, -0.017490769, -0.005240605,
0.033788983, -0.022737999, -0.016284965, -0.025202608, -0.029177781,
-0.0026318969, -0.001831893, 0.008858014, 0.0029167845, -0.011753267,
0.014098619, 0.02210197, -0.0020869668, 0.028568255, -0.0064894725,
0.023281273, -0.02341378, -0.00956692, -0.015211668, 0.0054923664,
-0.0026087083, 0.013217456, -0.0027163692, -0.01955786, -0.008268363,
-0.019080838, -0.013157828, -0.0036207216, 0.0063768425, -0.014005865,
0.0037598526, 0.007983476, -0.033444468, 0.021439442, 0.0009358224,
0.018723072, 0.0048994026, 0.0013159484, 0.0065524126, -0.0053863614,
0.025229108, 0.04441595, 0.012309791, 0.011534631, 0.016443972,
0.0037532274, -0.0204854, 0.015370675, 0.0064563463, 0.019186843,
0.014217875, -0.022366982, 0.005823631, -0.0071089375, 0.0057606907,
-0.0025424554, -0.021929713, 0.0018799263, -0.00092754076, -0.014694896,
0.014774399, 0.028329745, 0.0029383167, 0.041050304, -0.0045813886,
0.015185167, 0.006446408, 0.024208814, -0.0094410395, -0.023864299,
... 1436 more items
]
使用 OpenAI 的 embedding-ada-002 模型创建的向量有 1536 个维度,我们日常讨论空间的时候,通常都说二维、三维,在这我们就可以简单做个类比,二维空间就好比是一个二维向量,三维空间就是一个三维向量,那么我们前面创建的向量是 1536 维的,也就是说它构成了一个 1536 维的空间,我们的向量就分布在这个空间里,如果两个词的向量表示在这个1536维的向量空间中很接近,那么我们可以认为这两个词在某种意义上是相似的。现在大家应该也能感受到什么是‘高维向量’了吧?
通过前面我们获取到的向量表示,看起来‘苹果’、‘梨子’和‘电脑’的向量,分别是一个长度为 1536 的数字列表「数组」,很显然我们是没有办法看向量就知道它代表的是‘苹果’还是‘梨子’,但是我们可以使用一个叫做‘余弦相似度’的算法来比较两个向量,发现它们两个的向量比较相似,并且它们两个都与水果类型的非常接近「在空间中的位置比较近」,所以 ChatGPT 就大概知道了它们两个应该是水果,应该都能吃。但是‘大象’的向量表示跟他们并不相似,所以 ChatGPT 也就知道了大象应该不是水果类的,跟它们有比较大的不同。
我们简单实现一个“余弦相似度”的算法,用来分别比较一下‘苹果’、‘梨子’和‘电脑’的值,我们就可以直观的看到向量之间的差异
‘苹果’和‘梨子’的余弦相似度:
0.8703165905925271
‘苹果’和‘电脑’的余弦相似度:
0.8479234895482726

“余弦相似度”主要度量的是两个向量间的角度,是一个 -1 到 1 之间的数字:
值越接近 1 表示越相似
值越接近 -1 表示越相反
值越接近 0 表示越不相关
"苹果"和"梨子"的余弦相似度为0.8703,这表示在多维向量空间中,这两个词的距离比较接近,这意味着模型认为"苹果"和"梨子"在语义上更为相似。这与我们在现实生活中的感觉是一致的,因为它们都是水果,有很多相似的地方。
"苹果"和"电脑"的余弦相似度为0.8479,它的值比"苹果"和"梨子"的要小,说明"苹果"和"梨子"的相似度比"苹果"和"电脑"的要高。这与我们在现实生活中的感受也是吻合的。
但要注意的是,"苹果"和"电脑"的余弦相似度为0.7973,这个值依然较高,从数值的角度来看表明它们也有一定的相似性,但是一个是电器一个是水果,这两个感觉一点都不相似啊?这恰恰就是我们前面说的,GPT 用的是「上下文词向量表示法」,因为我们都知道有一个东西叫「苹果电脑」,所以"苹果"和"电脑"也经常一起出现,因此它们也有一定的语义关联性。
最后再说一点,向量有个很神奇的地方,就是它是和语言无关的。什么意思呢?举个例子,我把一组英文数据集进行向量转化并存储在向量数据库,然后用户提问的时候,是可以使用中文进行匹配和返回的。这好像是间接性的实现了多语言,已经有不少的开源项目在利用这个特性提供自己的帮助手册,它们把自己的帮助手册向量化,然后可以让用户进行提问,你用什么语言提问,它最后就可以给你返回什么语言。比如我在文中使用 langchain 框架「https://js.langchain.com/docs/」、supabase docs「https://supabase.com/docs/guides/database/overview」,大家可以去体验一下。


注意事项:
有一点注意的是,使用不同的向量嵌入模型以及比对算法,最终的效果是差异很大的。以上的示例是使用的 OpenAI 的 embedding-ada-002 模型创建的向量,比对是使用的余弦相似度算法。你使用的方案不同,最终的效果可能会跟本文不同。
关于「嵌入(embedding)、向量」的相关概念就说到这了,希望本次分享能对大家的理解有所帮助。