手扒Github项目文档级知识图谱构建框架RAKG(保姆级)Day3

100 阅读12分钟

Embedding 文本向量化的过程解读

  1. 文本的向量化需要借助embedding模型,所以本次解读聚焦于项目中的llm_provider.py,并在最后写出单独测试代码以供读者理解整个embedding过程。 代码总览如下:
#llm_provider.py文件
from langchain_ollama import OllamaLLM, OllamaEmbeddings
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from src.config import (
    OLLAMA_BASE_URL, DEFAULT_MODEL, EMBEDDING_MODEL, SIMILARITY_MODEL,
    OPENAI_API_KEY, OPENAI_MODEL, OPENAI_EMBEDDING_MODEL, OPENAI_SIMILARITY_MODEL,
    USE_OPENAI, base_url
)

class LLMProvider:
    def __init__(self):
        if USE_OPENAI:
            # 使用 ChatOpenAI 并启用 JSON 模式
            self.llm = ChatOpenAI(
                model=OPENAI_MODEL,
                api_key=OPENAI_API_KEY,
                base_url=base_url,
                temperature=0,
            ).bind(response_format={"type": "json_object"})
            self.embedding_model = OpenAIEmbeddings(
                model=OPENAI_EMBEDDING_MODEL,
                api_key=OPENAI_API_KEY,
                base_url=base_url
            )
            self.similarity_model = ChatOpenAI(
                model=OPENAI_SIMILARITY_MODEL,
                api_key=OPENAI_API_KEY,
                base_url=base_url,
                temperature=0,
            ).bind(response_format={"type": "json_object"})
        else:
            # 使用 Ollama 模型
            self.llm = OllamaLLM(
                model=DEFAULT_MODEL,
                base_url=OLLAMA_BASE_URL,
                format='json',
                temperature=0
            )
            self.embedding_model = OllamaEmbeddings(
                model=EMBEDDING_MODEL,
                base_url=OLLAMA_BASE_URL
            )
            self.similarity_model = OllamaLLM(
                model=SIMILARITY_MODEL,
                base_url=OLLAMA_BASE_URL,
                format='json',
                temperature=0
            )

    def get_llm(self):
        return self.llm

    def get_embedding_model(self):
        return self.embedding_model

    def get_similarity_model(self):
        return self.similarity_model
  • 我们先聚焦于导入工具包环节:
# Ollama 相关:用于本地部署的开源模型
from langchain_ollama import OllamaLLM, OllamaEmbeddings
# OpenAI 相关:用于调用 OpenAI API(或兼容API)
from langchain_openai import ChatOpenAI, OpenAIEmbeddings

# 配置文件:所有的配置参数都集中管理
from src.config import (
    OLLAMA_BASE_URL,        # Ollama 服务地址,如 "http://localhost:11434"
    DEFAULT_MODEL,          # 默认 LLM 模型,如 "qwen2.5:14b"
    EMBEDDING_MODEL,        # 嵌入模型,如 "nomic-embed-text"
    SIMILARITY_MODEL,       # 相似度判断模型
    OPENAI_API_KEY,        # OpenAI 的 API 密钥
    OPENAI_MODEL,          # OpenAI 模型名,如 "gpt-4"
    OPENAI_EMBEDDING_MODEL, # OpenAI 嵌入模型,如 "text-embedding-3-small"
    OPENAI_SIMILARITY_MODEL,# OpenAI 相似度模型
    USE_OPENAI,            # 布尔值:True 使用 OpenAI,False 使用 Ollama
    base_url               # OpenAI API 的基础 URL(可能是第三方兼容服务)
)

此处牵涉了本项目中的另一个文件config.py,作者单独列出了配置文档以方便对模型配置参数直接操作,现在我们延伸至config.py进行查看和解读:

#config.py文件
# Global configuration variables
OLLAMA_BASE_URL = "http://localhost:11434"  # Change this to your Ollama server URL

# qwen2.5:72b太大跑不了,注意这里选择的模型要适配自己电脑的显存
DEFAULT_MODEL = "qwen2.5:7b"   
EMBEDDING_MODEL = "bge-m3:latest"   
SIMILARITY_MODEL = "qwen2:7b"



# OpenAI Configuration
#这里填写的是API网站的URL地址,大部分情况下,是以.cn/v1结尾的地址,这里使用的是硅谷流动第三方API

base_url="https://api.siliconflow.cn/v1" # https://api.siliconflow.cn/v1 for siliconflow
OPENAI_API_KEY = "这里填你的秘钥,此处是私人秘钥,需要隐藏"  # Set your OpenAI API key here
OPENAI_MODEL = "Qwen/Qwen2.5-72B-Instruct"  # Default model
OPENAI_EMBEDDING_MODEL = "BAAI/bge-m3"  # Default embedding model
OPENAI_SIMILARITY_MODEL = "Qwen/Qwen2.5-14B-Instruct"  # Model for similarity checks


# Model Provider Selection
# 调用OPEN AI或者本地Ollama模型的开关
USE_OPENAI = False  # Set to True to use OpenAI, False to use Ollama

所以此处llm_provider.py的from src.config import操作就是导入了config.py里预设好的一系列变量,其实config.py和llm_provider.py可以进行合并。

  • 好的,延伸阅读结束,继续回到llm_provider.py解读的主线,来看模型配置部分。

代码中定义类了LLM_Provider类,__init__分别初始化了llm模型,embedding模型,similarity模型

    def __init__(self):
        # 这是整个类的核心决策点:根据配置选择模型供应商
        if USE_OPENAI:
            # OpenAI
            
            # 1. 主力 LLM 模型配置
            self.llm = ChatOpenAI(
                model=OPENAI_MODEL,           # 指定模型,如 "gpt-4"
                api_key=OPENAI_API_KEY,       # API 密钥,用于身份验证
                base_url=base_url,            # API 地址,可能是官方或第三方
                temperature=0,                # 温度=0:确保输出的确定性和一致性
            ).bind(response_format={"type": "json_object"})  
            # ↑ 关键点:bind() 方法强制模型输出 JSON 格式
            # 这是 OpenAI 的新特性,确保输出可以被 json.loads() 解析
            
            # 2. 嵌入模型配置
            self.embedding_model = OpenAIEmbeddings(
                model=OPENAI_EMBEDDING_MODEL,  # 如 "text-embedding-3-small"
                api_key=OPENAI_API_KEY,
                base_url=base_url
            )
            # 嵌入模型不需要 temperature,因为它输出的是确定的向量
            
            # 3. 相似度判断专用模型
            self.similarity_model = ChatOpenAI(
                model=OPENAI_SIMILARITY_MODEL,
                api_key=OPENAI_API_KEY,
                base_url=base_url,
                temperature=0,  # 同样设为0,确保判断的稳定性
            ).bind(response_format={"type": "json_object"})
            
        else:
            # Ollama(本地模型)
            
            # 1. 主力 LLM 模型配置
            self.llm = OllamaLLM(
                model=DEFAULT_MODEL,      # 如 "qwen2.5:14b"
                base_url=OLLAMA_BASE_URL, # 通常是 "http://localhost:11434"
                format='json',            # 直接指定输出格式为 JSON
                temperature=0             # 保持输出稳定性
            )
            
            # 2. 嵌入模型配置
            self.embedding_model = OllamaEmbeddings(
                model=EMBEDDING_MODEL,    # 如 "nomic-embed-text"
                base_url=OLLAMA_BASE_URL
            )
            
            # 3. 相似度判断专用模型
            self.similarity_model = OllamaLLM(
                model=SIMILARITY_MODEL,   # 可能和主模型相同,也可能不同
                base_url=OLLAMA_BASE_URL,
                format='json',
                temperature=0
            )

Temperature 控制输出的随机性,0 = 完全确定性,相同输入总是得到相同输出,对于知识图谱构建,我们需要一致性。

.bind(response_format={"type": "json_object"}),强制 OpenAI 模型输出有效的 JSON,避免输出类似 "Here is the JSON: ..." 这样的前缀,确保输出可以直接被 json.loads() 解析

为什么要设置三个不同模型?1. 主模型(llm) :通用能力强,处理复杂的提取任务。2. 嵌入模型(embedding_model) :专门优化for向量化,速度快。3. 相似度模型(similarity_model) :可以是更小、更快的模型,专门做二分类。

这种设计模式可以支持多种 LLM 供应商,可以通过config.py中的 USE_OPENAI 标志动态选择策略,可以扩展添加新供应商(如 Anthropic),只需添加新分支即可,例如:

elif USE_CLAUDE:
    self.llm = Claude(
        model="claude-3-opus",
        api_key=CLAUDE_API_KEY
    )

修改config.py,添加USE_Anthropic标志,并在llm_provider.py里添加elif语句使用Claude模型,这样的类似操作都可以实现。读者可自行尝试。

  • 接下来是统一的接口,只要调用下面的三种方法,就可以获得模型的返回值
def get_llm(self):
    """获取主力 LLM 模型
    返回值:配置好的 LLM 实例,可能是 ChatOpenAI 或 OllamaLLM
    """
    return self.llm

def get_embedding_model(self):
    """获取嵌入模型
    返回值:配置好的嵌入模型实例,用于文本向量化
    """
    return self.embedding_model

def get_similarity_model(self):
    """获取相似度判断模型
    返回值:专门用于判断实体相似性的模型实例
    """
    return self.similarity_model

  1. 代码解读完成后,我们使用自己的测试代码,理解文本向量化的整个过程。这个过程为:模型配置+向量化,即获取工具+使用工具的过程。

测试代码如下:测试代码集成了config.py和llm_provider.py,可以让人更好理解整个获取工具和使用工具的过程。

# test_embedding.py

# 获取工具

# 导入我们将要用到的所有工具
import numpy as np  # 用于进行高效的数学计算,特别是向量运算
import os  # 用于和操作系统交互,比如读取环境变量

# 从不同的 LangChain 库中导入对应的 Embedding 类
from langchain_openai import OpenAIEmbeddings
from langchain_ollama import OllamaEmbeddings

# --- 1. 配置部分 (模拟项目中的 src/config.py) ---
# 在这里设置配置,就像在 config.py 文件中一样

# !! 关键开关 !!: 设置为 True 来测试 OpenAI, 设置为 False 来测试 Ollama
USE_OPENAI = False

# --- OpenAI 的配置 ---
# os.getenv 会尝试从你电脑的环境变量中读取'OPENAI_API_KEY'
# 如果找不到,它会使用 "your_api_key_here" 作为默认值。
# 强烈建议设置环境变量,而不是把密钥直接写在代码这里。
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "your_api_key_here")
# 这是 OpenAI 兼容 API 的地址,如果你用的是官方服务,通常是 "https://api.openai.com/v1"
# 如果你使用了第三方代理或者本地部署的模型,请修改这里
#base_url = "https://api.openai.com/v1"
base_url = "https://sg.uiuiapi.com/v1"
# 你想使用的 OpenAI embedding 模型的具体名称
OPENAI_EMBEDDING_MODEL = "text-embedding-3-small"  #这里是具体的模型名称,可以在api代理那里查看可使用的embedding模型

# --- Ollama 的配置 ---
# 你的本地 Ollama 服务的地址
OLLAMA_BASE_URL = "http://localhost:11434"
# 你在 Ollama 中下载并想要使用的 embedding 模型的名称
# 常见的有 "mxbai-embed-large", "nomic-embed-text" 等
#EMBEDDING_MODEL = "nomic-embed-text"
EMBEDDING_MODEL = "bge-m3:latest"

# --- 2. 根据配置初始化 Embedding 模型 ---
# 这段逻辑完全模仿了 llm_provider.py 文件

print("--- 正在初始化 Embedding 模型 ---")
if USE_OPENAI:
    # 如果开关设置为 True,我们就初始化 OpenAI 的 Embedding 模型
    print(f"模式: OpenAI, 模型: {OPENAI_EMBEDDING_MODEL}")
    if OPENAI_API_KEY == "your_api_key_here":
        print("警告: 你没有设置 OpenAI API Key。代码可能会失败。")

    print(f"--- [调试信息] 准备使用的 API Key 是: '{OPENAI_API_KEY}'")

    embeddings = OpenAIEmbeddings(
        model=OPENAI_EMBEDDING_MODEL,  # 使用上面定义的模型名称
        api_key=OPENAI_API_KEY,  # 传入你的 API Key
        base_url=base_url  # 传入 API 的地址
    )
else:
    # 如果开关设置为 False, 我们就初始化 Ollama 的 Embedding 模型
    print(f"模式: Ollama, 模型: {EMBEDDING_MODEL}")
    print(f"请确保你的 Ollama 服务正在 {OLLAMA_BASE_URL} 运行,并且已经下载了模型 '{EMBEDDING_MODEL}'")

    embeddings = OllamaEmbeddings(
        model=EMBEDDING_MODEL,  # 使用上面定义的 Ollama 模型名称
        base_url=OLLAMA_BASE_URL  # 传入 Ollama 服务的地址
    )

print("--- 模型加载完成 ---")



# 使用工具



# --- 3. 实验部分:向量化与相似度计算 ---
# 这部分和之前的脚本一样,但现在它能用不同的模型来执行了

# 定义两个语义相近的句子和两个语义不相关的句子
sentence1 = "今天的天气真不错"
sentence2 = "今天阳光明媚"
sentence3 = "我喜欢吃披萨"
sentence4 = "人工智能正在改变世界"

# 将句子转换为向量 (现在这个 embeddings 对象可能是 OpenAI 的也可能是 Ollama 的)
print("\n--- 正在将句子转换为向量 ---")
vec1 = embeddings.embed_query(sentence1)
vec2 = embeddings.embed_query(sentence2)
vec3 = embeddings.embed_query(sentence3)
vec4 = embeddings.embed_query(sentence4)

print(f"\n句子1的向量(前5个维度): {vec1[:5]}")
print(f"向量的维度是: {len(vec1)}")

# --- 理解向量的意义:计算余弦相似度 ---
# numpy array 转换,便于计算
vec1 = np.array(vec1)
vec2 = np.array(vec2)
vec3 = np.array(vec3)
vec4 = np.array(vec4)


# 余弦相似度计算函数
def cosine_similarity(v1, v2):
    # 这是计算两个向量夹角的余弦值的标准公式
    # dot product / (magnitude of v1 * magnitude of v2)
    return np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))


sim_1_2 = cosine_similarity(vec1, vec2)
sim_1_3 = cosine_similarity(vec1, vec3)
sim_1_4 = cosine_similarity(vec1, vec4)

print("\n--- 语义相似度对比 ---")
print(f"'{sentence1}' vs '{sentence2}' (语义相近): {sim_1_2:.4f}")
print(f"'{sentence1}' vs '{sentence3}' (语义无关): {sim_1_3:.4f}")
print(f"'{sentence1}' vs '{sentence4}' (语义无关): {sim_1_4:.4f}")

知识点1:你的API密钥可以在第三方网站获取后,导入到系统的环境变量-用户变量之中,os.getenv,os.getenv("OPENAI_API_KEY", "your_api_key_here")会自动读取到你的密钥,当然,你也可以把密钥直接输入在代码之中,但这样会存在密钥安全性问题。设置好环境变量后,简单调试,打印出获取的API秘钥,或者查看编辑配置栏,确保是你的API密钥能够使用的,也就是确保获取的工具是能用的,否则在后续使用工具时会报错。如下:

openai.AuthenticationError: Error code: 401 - {'error': {'message': '无效的令牌

此时确定你的API密钥是否配置到了IDE中,举一个形象的例子:你要派一个机器人去送快递。在它出发前,你把一张写着“地址A”的纸条放进了它的口袋。机器人出发了。 半路上,你改了主意,把家里桌上的纸条换成了“地址B”。 但是,已经出发的那个机器人会知道吗?  不会。因为它口袋里的,仍然是出发时你给它的那张“地址A”的纸条。它只认自己口袋里的东西。你的程序(Python 脚本)、你的 IDE(PyCharm/VS Code)、你的终端(Anaconda Prompt)都像这个机器人。当它们被启动时,会把当时系统里所有的环境变量复制一份,放进自己的“口袋”里。之后,无论你在系统里怎么修改环境变量,这些已经启动的程序都不会知道,它们只会用自己口袋里那份旧的、启动时的“快照”。 所以,你需要确保它们的更新。要么重启终端或IDE,要么直接编辑配置。

知识点2:可以调用API的第三方代理网站:sg.uiuiapi.com, 或者也可以用硅谷流动。 输入的Base_url一般都是以.com/v1结尾的地址。

知识点3:在配置ollama时,你需要记住一些常见的控制ollama操作的Bash命令,如下:

ollama list  # 查看已经下载过的模型
ollama pull bge-m3 # 下载某个模型
ollama run qwen2.5:7b  #运行某个模型
ollama run qwen2:7b
ollama rm 模型名   #移除某个模型

获取工具阶段总结:我们可以清晰的看到一种模式,即导入需要的库和包————获取工具信息————配置工具使用开关,一共三步的模式,这在以后的实际项目运用中,可以当做经验模式借鉴。

工具使用阶段:

知识点1:余弦相似度

在机器学习算法中,有各种方式衡量用户或者物品的距离或者相似度,如曼哈顿距离、欧几里得距离、Pearson相关系数、Jaccard系数等(可参考blog.csdn.net/lin00jian/a… 我们这里主要详细介绍一下余弦相似度。余弦相似度被广泛用于协同过滤算法中,尤其是Item-base的协同过滤。

余弦相似度衡量的是2个向量间的夹角大小,通过夹角的余弦值表示结果,因此2个向量的余弦相似度为:

image.png

分子为向量A与向量B的点乘,分母为二者各自的L2相乘,即将所有维度值的平方相加后开方。
余弦相似度的取值为[-1,1],值越大表示越相似。

image.png

image.png 原文可见jediael_lu. 另有一篇了解余弦相似度的应用可以读:相似度计算方法-余弦相似度 (Cosine Similarity)-CSDN博客

  • 在理解了所有的原理之后,就可以运行测试代码查看结果,理解文本向量化的操作细节。运行结果如下:
--- 正在初始化 Embedding 模型 ---
模式: Ollama, 模型: bge-m3:latest
请确保你的 Ollama 服务正在 http://localhost:11434 运行,并且已经下载了模型 'bge-m3:latest'
--- 模型加载完成 ---

--- 正在将句子转换为向量 ---

句子1的向量(前5个维度): [-0.019258307, 0.045720316, -0.04726419, -0.016728299, -0.028020825]
向量的维度是: 1024

--- 语义相似度对比 ---
'今天的天气真不错' vs '今天阳光明媚' (语义相近): 0.8349
'今天的天气真不错' vs '我喜欢吃披萨' (语义无关): 0.5437
'今天的天气真不错' vs '人工智能正在改变世界' (语义无关): 0.5448