OpenAI的embedding模型的使用

       首先第一篇文章中探讨和使用了ChatGPT4的API-Key实现基础的多轮对话和流式输出,完成了对GPT-API的一个初探索,那第二步打算使用OpenAI的embedding模型来构建一个知识向量库,其实知识向量库本质上就是一个包含着一组向量的数组,然后通过查询输入文本生成的向量和数据库文本中的向量的余弦相似度来进行相似度判断,在使用的过程中还是非常舒服的。

前置文章:ChatGPT4 API-Key初探-本地调用API进行多轮对话方和流式输出

1.什么是Embedding


       Embedding在AI领域被翻译成词嵌入,但是光看这三个字其实不好理解,如果用一句话解释的话:embedding就是将文字转换成一个向量


使用 OpenAI 的 text-embedding 构建知识向量库并进行相似搜索-LMLPHP

图: Embedding的工作过程

  • 那为什么要转换成向量?
    因为要获得两段文字在空间中的关系进行运算,例如后续需要做的计算两个词的相似度,就需要首先经过embedding模型将两端文字转换成向量,然后算两个向量的余弦相似度。

       如果你不是研究自然语言处理的研究生或者算法工程师,那你只需要了解这一点就可以了,至于怎么转换的这一点可以不用深究,就像你可能不了解计算机组成原理但是你依然可以熟练的使用计算机。

2.使用OpenAI的embedding模型生成一个词向量


       OpenAI为我们提供了了一个方便的API接口来将输入文本直接转换为词向量,在官方文档的代码中其实并没有新手向的代码的表达都会集成一些功能一起发布,但是为了学习和理解要把其中最简单和核心的部分抽出来。

       下面的代码使用的是OpenAI的text-embedding-3-small模型将文本"父亲"转换为词向量。经过API之后得到了一个长度为1536的向量。

from openai import OpenAI
import numpy as np

# client = OpenAI(api_key="your-api-key-here") # 如果想在代码中设置Api-key而不是全局变量就用这个代码
client = OpenAI()

model = "text-embedding-3-small"


def get_embedding(text, model=model):
    return client.embeddings.create(input=text, model=model).data[0].embedding


text = '父亲'
vector = np.array(get_embedding(text))
print(vector.shape)
# (1536,)

3.使用OpenAI的embedding模型获得一个知识向量库


       OK那既然一条文本能转换成向量,那多条文本那就必然能转换成多个词向量,且词向量的长度是一致的,然后将这些词向量转换成数组然后保存这样就得到了一个知识向量库,本质上就是保存一个数组,返璞归真。

       首先我们得有一段文本,我让ChatGPT给我生成了50个医疗词汇,每个词汇一行,文本内容我放在文章最后。接下来给出代码。

       下列代码将本文中的医疗词汇逐行读取然后转换成词向量,保存到一个numpy数组当中,然后再将Numpy数组保存到本地,这样就得到了一个知识向量库。

from openai import OpenAI
import pandas as pd
import numpy as np
import time

# client = OpenAI(api_key="your-api-key-here") # 如果想在代码中设置Api-key而不是全局变量就用这个代码
client = OpenAI()

model = "text-embedding-3-small"

with open('test.txt', 'r', encoding='utf-8') as file:
    lines = file.readlines()

embedding = [client.embeddings.create(input=i.strip(), model=model).data[0].embedding for i in lines]

t = time.time()
np.save('embedding.npy', np.array(embedding))
print(time.time() - t)
# 0.002991914749145508 (运行时间/s)

测试用的文本内容

糖尿病
高血压
抑郁症
阿尔茨海默症
慢性阻塞性肺疾病(COPD)
骨折
哮喘
乳腺癌
心肌梗塞
脑卒中
化疗
放射疗法
心脏搭桥手术
经皮冠状动脉介入治疗(PCI)
腹腔镜手术
物理疗法
认知行为疗法
血液透析
绝育手术
骨髓移植
MRI扫描仪
CT扫描仪
心电图机
超声波设备
血糖仪
血压计
吸氧机
呼吸机
脉搏血氧仪
自动体外除颤器(AED)
青霉素
阿司匹林
他汀类药物
阿片类镇痛药
抗生素
抗抑郁药
胰岛素
利尿剂
抗凝血药
抗病毒药
免疫疗法
基因编辑
微创手术
患者健康记录(PHR)
电子医疗记录(EMR)
临床试验
医疗保健大数据
精准医疗
遥感监测
医疗伦理

3.从知识向量库中进行相似文本的查询


       首先在介绍查询方法之需要说一下计算相似度的余弦相似度,当两个向量夹角越小的时候两个向量的相似度越高,其计算公式如下:


cosine similarity ( A , B ) = A ⋅ B ∥ A ∥ ∥ B ∥ = ∑ i = 1 n A i B i ∑ i = 1 n A i 2 ∑ i = 1 n B i 2 \text{cosine similarity}(\mathbf{A}, \mathbf{B}) = \frac{\mathbf{A} \cdot \mathbf{B}}{\|\mathbf{A}\| \|\mathbf{B}\|} = \frac{\sum_{i=1}^{n}A_iB_i}{\sqrt{\sum_{i=1}^{n}A_i^2} \sqrt{\sum_{i=1}^{n}B_i^2}} cosine similarity(A,B)=A∥∥BAB=i=1nAi2 i=1nBi2 i=1nAiBi


       在查询任务中我们需要计算输入文本的向量和知识向量库中所有文本向量的余弦相似度,然后进行排序,当然作为一个算法工程师我们当然不能直接用for循环进行计算,本文基于numpy的广播机制,自己实现了一个高效的计算一个向量和一群向量的余弦相似度的代码。同时由于算法本身使用的是numpy,基于numpy底层为C语言实现,因此本程序的效率较高,作为新手的话只需要知道输入输出是什么就行了。代码如下。

import numpy as np

def cos_similarity(target, embedding):
    numerator = np.sum(target * embedding, axis=1)
    denominator = np.sqrt(np.sum(np.square(target)) * np.sum(np.square(embedding),axis=1))
    return numerator / denominator


if __name__ == '__main__':

    x = np.array([1, 2])
    y = np.array([[1, 2], [1, 1]])

    print(cos_similarity(x, y))
	# [1.        0.9486833]

将知识向量库中的文本按照相似度从大到小输出,代码如下:

  • 生成知识向量库和查询使用的模型得是一个模型别忘了,不然会报维度错误。
import numpy as np
from openai import OpenAI

model="text-embedding-3-small"
# client = OpenAI(api_key="your-api-key-here") # 如果想在代码中设置Api-key而不是全局变量就用这个代码
client = OpenAI()

def get_embedding(text, model=model):
   return client.embeddings.create(input=text, model=model).data[0].embedding

def cos_similarity(target, embedding):
    numerator = np.sum(target * embedding, axis=1)
    denominator = np.sqrt(np.sum(np.square(target)) * np.sum(np.square(embedding),axis=1))
    return numerator / denominator

with open('test.txt', 'r', encoding='utf-8') as file:
    lines = file.readlines()

# 获得文本数据
name = np.array([i.strip() for i in lines])
# 获得向量库
embedding = np.load("embedding.npy",allow_pickle=True)


# 获取用户的输入文本
search_text = input("User:")
# 获取用户输入文本使用embedding模型转换得到的词向量
search_embedding = get_embedding(search_text)
# 计算用户输入文本
embedding_similarity = cos_similarity(search_embedding,embedding)


# 由上到下输入相似度
for i in np.argsort(embedding_similarity)[::-1]:
    print(name[i],embedding_similarity[i])

接下来让GPT解释一下这段代码助于理解,我看了一下基本没有问题。

  1. 导入所需的库:使用了numpy库来处理数学运算和数组操作,以及OpenAI的API客户端来获取文本的嵌入向量。

  2. 设置模型和客户端:选择了一个特定的模型text-embedding-3-small来从OpenAI获取文本嵌入。这里有一段被注释掉的代码,用于手动设置API密钥,但在这个例子中,默认使用了全局变量设置的API密钥。

  3. 定义函数获取嵌入向量get_embedding函数通过OpenAI的API将文本转换成嵌入向量。这个向量是文本的数学表示,用于计算相似度。

  4. 定义余弦相似度函数cos_similarity函数计算两组向量之间的余弦相似度,这是衡量向量相似度的一种方法。余弦相似度越接近1,表示两个向量越相似。

  5. 读取文本数据:从test.txt文件中读取每行文本,每行代表一个可查询的项,并将其存储在名为name的数组中。

  6. 加载嵌入向量库:从embedding.npy文件加载预先计算好的嵌入向量,这些向量对应于test.txt文件中的文本项。

  7. 获取用户输入:通过input函数获取用户的查询文本。

  8. 获取查询文本的嵌入向量:使用get_embedding函数将用户的查询文本转换为嵌入向量。

  9. 计算相似度:使用cos_similarity函数计算用户查询的嵌入向量与嵌入向量库中所有向量之间的余弦相似度。

输出结果如下,这里就不全部粘贴了,把前面的几个粘贴上。

User:糖尿病
糖尿病 1.0
血糖仪 0.6027716430115105
高血压 0.4807989892102901
乳腺癌 0.46976679922966263
利尿剂 0.44671493260605705
抑郁症 0.3989793244972647
胰岛素 0.3678633339750386
心肌梗塞 0.3630205294730911
阿尔茨海默症 0.3554829250733137
血压计 0.3527248065537073
抗病毒药 0.3223748925983246

使用 OpenAI 的 text-embedding 构建知识向量库并进行相似搜索-LMLPHP

结束


官方文档:https://platform.openai.com/docs/guides/embeddings/embedding-models
目前支持的Embedding模型如下。
使用 OpenAI 的 text-embedding 构建知识向量库并进行相似搜索-LMLPHP
       在官方文档中还详细讲了很多的使用方式,例如如何进行可视化,如何进行机器学习技术等,但是文章中的内容是我抽出主要内容然后加上自己的理解实现的。如果有什么不对或者更好的方式非常欢迎交流。

03-16 01:54