AI之RAG技术与实战
2025-04-26
RAG 技术之向量化与相似度计算
一、课程回顾:为什么需要 RAG?
大模型的问题
幻觉问题:生成内容可能偏离事实(信息偏差)。
知识滞后:训练数据无法实时更新,缺乏最新信息。
不可追溯:回答无明确信息来源,内容缺乏依据。
专业领域受限:在医疗、法律等领域表现不足。
RAG 的作用
检索 - 生成架构:通过外部知识检索补充大模型,解决知识滞后和不可追溯问题。
流程核心:文档处理(向量化存储)→ 用户问题向量化→ 相似度检索→ 生成回答。
二、向量化处理技术
基础向量化方法
词袋模型(Bag of Words)
优点:简单直接,统计词频。
缺点:无语义信息,词汇表过长导致维度爆炸。
词嵌入模型(Word Embedding)
Word2Vec:通过上下文预测中心词(CBOW)或反之(Skip-Gram),捕捉语义关系。
预训练模型(如 BERT):基于大规模语料训练,支持中文,能处理上下文语义(如 “苹果” 在 “水果” 和 “手机” 中的不同含义)。
代码实操:使用 BERT 进行文本向量化
from transformers import BertTokenizer, BertModel import torch # 1. 加载分词器和模型(需提前下载预训练模型) model_name = "bert-base-chinese" # 或本地路径 tokenizer = BertTokenizer.from_pretrained(model_name) model = BertModel.from_pretrained(model_name) # 2. 输入文本 texts = [ "苹果和李子是常见的水果", "我喜欢吃芒果和百香果", "苹果今年推出了新的手机", "深度学习是机器学习的重要分支" ] # 3. 文本分词和向量化 inputs = tokenizer(texts, padding=True, truncation=True, return_tensors="pt") # 4. 生成向量(获取最后一层隐藏层输出) with torch.no_grad(): outputs = model(**inputs) embeddings = outputs.last_hidden_state.mean(dim=1) # 句子级向量(平均池化) # 5. 查看向量维度 print("向量维度:", embeddings.shape) # 输出:torch.Size([4, 768])
注意:
需安装依赖:
pip install transformers torch
模型下载较慢时,可指定本地路径(如
model_name="path/to/local/bert"
)。
三、相似度计算算法
常见算法对比
代码实操:余弦相似度计算
import numpy as np # 假定向量数据(从BERT输出转换为numpy数组) vec1 = embeddings[0].numpy() vec2 = embeddings[2].numpy() # 计算余弦相似度 def cosine_similarity(vec_a, vec_b): dot_product = np.dot(vec_a, vec_b) norm_a = np.linalg.norm(vec_a) norm_b = np.linalg.norm(vec_b) return dot_product / (norm_a * norm_b) similarity = cosine_similarity(vec1, vec2) print("余弦相似度:", similarity) # 输出:接近0的值(因“苹果”语义不同)
修复问题:
若报错 “模块未找到”,需安装
numpy
:pip install numpy
。向量需先转换为 numpy 数组(原代码中直接使用 PyTorch 张量可能导致计算错误)。
四、应用领域
信息检索:电商搜索、内容推荐(如抖音推荐系统)。
知识图谱:学科关联分析、医疗病例匹配。
对话系统:语音识别、智能客服的语义理解。
文本分类与聚类:文档相似度分组、重复内容检测。
五、问题总结与修复
代码运行报错 “模型未找到”
原因:未正确指定模型名称或路径。
修复:确保
model_name
为 Hugging Face 有效名称(如"bert-base-chinese"
)或本地模型路径。
向量化维度不一致
原因:不同预训练模型输出维度不同(如 BERT 为 768,RoBERTa 可能为 1024)。
修复:根据模型文档调整后续计算逻辑(如相似度算法兼容不同维度)。
相似度结果异常
原因:未对向量进行归一化或使用错误池化方法(如未对词向量求平均)。
修复:采用平均池化或 CLS token 池化生成句子级向量,确保输入向量为句子级表示。
六、课后任务
使用 AI 生成 JSON 格式的面试题数据(问题 + 答案),用于后续 RAG 流程测试。
尝试不同预训练模型(如 RoBERTa、ERNIE),对比向量化结果差异。
总结:向量化和相似度计算是 RAG 的核心环节,通过预训练模型可高效处理语义信息,结合余弦相似度等算法实现精准检索。实际应用中需注意模型加载、向量处理和算法选择的细节,确保流程稳定高效。
RAG 案例实战与向量数据库应用
一、RAG 案例实战:构建 AI 面试官系统
1. 系统设计与类定义
目标:实现文档加载、向量化、相似度检索的完整 RAG 流程。
类定义:创建RAG
类,封装核心功能。
from transformers import BertTokenizer, BertModel
import torch
import numpy as np
class RAG:
def __init__(self, model_name="bert-base-chinese"):
"""初始化模型和分词器"""
self.tokenizer = BertTokenizer.from_pretrained(model_name)
self.model = BertModel.from_pretrained(model_name)
self.knowledge_base = [] # 存储知识库内容(问题-答案对)
self.embeddings = [] # 存储问题向量化结果
def load_knowledge(self, file_path):
"""加载JSON格式的知识库"""
import json
with open(file_path, "r", encoding="utf-8") as f:
self.knowledge_base = json.load(f)
# 提取问题并向量化
self.embeddings = [self.text_to_embedding(q["question"]) for q in self.knowledge_base]
def text_to_embedding(self, text):
"""文本向量化方法"""
inputs = self.tokenizer(text, padding=True, truncation=True, return_tensors="pt")
with torch.no_grad():
outputs = self.model(**inputs)
# 平均池化获取句子级向量
embedding = outputs.last_hidden_state.mean(dim=1).numpy()[0]
return embedding
def search(self, query, top_k=1):
"""相似度检索"""
query_emb = self.text_to_embedding(query)
similarities = [self.cosine_similarity(query_emb, emb) for emb in self.embeddings]
# 按相似度降序排序,获取top_k索引
top_indices = np.argsort(similarities)[-top_k:][::-1]
results = []
for idx in top_indices:
results.append({
"question": self.knowledge_base[idx]["question"],
"answer": self.knowledge_base[idx]["answer"],
"similarity": similarities[idx]
})
return results
@staticmethod
def cosine_similarity(vec1, vec2):
"""余弦相似度计算"""
dot_product = np.dot(vec1, vec2)
norm1 = np.linalg.norm(vec1)
norm2 = np.linalg.norm(vec2)
return dot_product / (norm1 * norm2)
2. 知识库加载与向量化
数据格式:JSON 文件存储问题 - 答案对(例:
questions.json
):[ {"question": "什么是MySQL主键", "answer": "MySQL主键是唯一标识表中每行数据的字段..."}, {"question": "事务的特性有哪些", "answer": "事务具有ACID特性:原子性、一致性、隔离性、持久性..."}, ... ]
代码调用:
rag = RAG() rag.load_knowledge("questions.json")
3. 查询处理与结果返回
用户提问:
query = "MySQL中主键的作用是什么"
检索逻辑:
results = rag.search(query, top_k=2) for res in results: print(f"问题: {res['question']}\n答案: {res['answer']}\n相似度: {res['similarity']:.4f}\n")
输出示例:
问题: 什么是MySQL主键 答案: MySQL主键是唯一标识表中每行数据的字段... 相似度: 0.9234
二、向量数据库基础
1. 为什么使用向量数据库?
优势:
持久化存储:解决内存存储数据易丢失问题。
高效检索:支持大规模向量数据的快速相似度查询。
语义匹配:基于向量距离计算,实现语义级检索(区别于传统数据库的精确匹配)。
应用场景:
大模型知识外挂、推荐系统、图像 / 文本检索等。
2. 常用向量数据库
3. 工作流程
数据预处理:文档分块、向量化(使用预训练模型如 BERT)。
向量存储:将向量化结果存入数据库,建立索引。
查询检索:用户问题向量化后,在数据库中检索相似向量,返回相关文档。
三、向量数据库实战:内存模式(以 FAISS 为例)
1. 安装与初始化
pip install faiss-cpu # 或 faiss-gpu(需CUDA支持)
import faiss
import numpy as np
# 初始化FAISS客户端(内存模式)
index = faiss.IndexFlatL2(768) # 768为向量维度,需与向量化结果一致
2. 添加数据
数据结构:每个条目包含
向量
、文档内容
、元数据
。
# 假设已有向量化后的向量列表embeddings(形状为[N, 768])
# 模拟文档内容和元数据
documents = [
"Java开发入门指南",
"Python数据分析实战",
"MySQL数据库优化",
]
metadata = [{"source": "技术文档"}, {"source": "数据分析"}, {"source": "数据库"}]
# 添加到FAISS索引
index.add(np.array(embeddings, dtype=np.float32))
3. 查询示例
# 用户问题向量化
query_emb = np.array([...], dtype=np.float32).reshape(1, -1)
# 检索最相似的2个结果
k = 2
distances, indices = index.search(query_emb, k)
# 输出结果
for idx in indices[0]:
print(f"匹配文档: {documents[idx]}\n元数据: {metadata[idx]}\n")
四、常见问题与修复
1. 文件读取乱码问题
问题:加载 JSON / 文本文件时因编码错误导致读取失败。
修复:指定编码格式
encoding="utf-8"
(尤其处理中文文件):with open(file_path, "r", encoding="utf-8") as f: data = json.load(f)
2. 向量化维度不一致
问题:预训练模型输出维度与向量数据库索引维度不匹配(如 BERT 输出 768 维,索引定义为 384 维)。
修复:确保向量化模型与索引维度一致,或在存储前对向量进行维度转换。
3. 相似度计算错误
问题:未对向量化结果进行归一化,导致余弦相似度计算异常。
修复:在
cosine_similarity
函数中添加归一化检查,确保向量非零:if norm1 == 0 or norm2 == 0: return 0.0 # 避免除零错误
五、课后任务
扩展知识库:使用 AI 生成更多领域的问题 - 答案对(如 Python、算法),丰富
questions.json
。对比实验:尝试不同向量数据库(FAISS vs. Milvus),观察检索速度和准确率差异。
性能优化:对大规模文档进行分块处理(如按段落切割),提升向量化效率。
总结:RAG 案例实战通过封装向量化和检索逻辑,实现了基于外部知识库的智能问答,而向量数据库则解决了大规模向量数据的存储和高效检索问题。实际应用中需注意数据预处理、模型选型和异常处理,确保系统稳定高效。