2.1 向量嵌入技术 — RAG系统的基础引擎 本节导读:掌握向量嵌入的核心原理、主流模型选择和优化策略,为RAG系统的检索质量奠定坚实基础。 学习目标 理解向量嵌入的基本原理和数学基础 掌握主流嵌入模型的特点和适用场景 学习嵌入质量评估和优化方法 实现高性能的向量嵌入系统 处理特殊场景下的嵌入挑战 核心概念 向量嵌入的本质 向量嵌入是将文本、图像等非结构化数据转换为高维数值向量的技术。
本节导读:掌握向量嵌入的核心原理、主流模型选择和优化策略,为RAG系统的检索质量奠定坚实基础。
向量嵌入是将文本、图像等非结构化数据转换为高维数值向量的技术。这些向量能够:
基于不同的架构和技术,嵌入模型可以分为几类:
# 核心依赖 pip install transformers torch pip install sentence-transformers pip install numpy scikit-learn # 可选依赖 pip install faiss-cpu pip install matplotlib seaborn
import torch from transformers import AutoTokenizer, AutoModel from sentence_transformers import SentenceTransformer import numpy as np from typing import List, Dict, Any, Optional import time import logging # 设置日志 logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # 检查CUDA支持 device = "cuda" if torch.cuda.is_available() else "cpu" print(f"使用设备: {device}")
import numpy as np from sentence_transformers import SentenceTransformer from typing import List, Dict, Any, Optional import time from transformers import AutoTokenizer, AutoModel import torch import faiss class BasicEmbeddingService: """基础嵌入服务实现""" def __init__(self, model_name: str = "paraphrase-multilingual-MiniLM-L12-v2"): self.model_name = model_name self.model = SentenceTransformer(model_name) self.device = "cuda" if torch.cuda.is_available() else "cpu" self.model.to(self.device) # 统计信息 self.stats = { 'total_requests': 0, 'cache_hits': 0, 'avg_response_time': 0.0 } # 简单缓存 self.text_cache = {} def encode(self, texts: List[str], batch_size: int = 32, show_progress_bar: bool = False) -> np.ndarray: """编码文本为向量""" start_time = time.time() # 检查缓存 cache_key = hash(tuple(texts)) if cache_key in self.text_cache: self.stats['cache_hits'] += 1 return self.text_cache[cache_key] # 批量编码 embeddings = self.model.encode( texts, batch_size=batch_size, show_progress_bar=show_progress_bar, convert_to_numpy=True, normalize_embeddings=True # 归一化嵌入 ) # 缓存结果 self.text_cache[cache_key] = embeddings # 更新统计 response_time = time.time() - start_time self.stats['total_requests'] += 1 self.stats['avg_response_time'] = ( (self.stats['avg_response_time'] * (self.stats['total_requests'] - 1) + response_time) / self.stats['total_requests'] ) logger.info(f"编码完成,共 {len(texts)} 个文本,耗时 {response_time:.2f}秒") return embeddings def encode_single(self, text: str) -> np.ndarray: """编码单个文本""" return self.encode([text])[0] def similarity(self, text1: str, text2: str) -> float: """计算两个文本的相似度""" embedding1 = self.encode_single(text1) embedding2 = self.encode_single(text2) # 使用余弦相似度 similarity = np.dot(embedding1, embedding2) return float(similarity) def get_stats(self) -> Dict[str, Any]: """获取服务统计信息""" total_requests = self.stats['total_requests'] cache_hit_rate = self.stats['cache_hits'] / total_requests if total_requests > 0 else 0 return { 'model_name': self.model_name, 'total_requests': total_requests, 'cache_hits': self.stats['cache_hits'], 'cache_hit_rate': cache_hit_rate, 'avg_response_time': self.stats['avg_response_time'], 'cache_size': len(self.text_cache) } # 使用示例 embedding_service = BasicEmbeddingService() # 测试文本 test_texts = [ "人工智能是计算机科学的一个重要分支", "机器学习是人工智能的核心技术", "深度学习基于神经网络模型", "天气很好,适合出去散步", "今天是晴天" ] # 编码测试 embeddings = embedding_service.encode(test_texts) print(f"嵌入形状: {embeddings.shape}") # 相似度测试 similarity = embedding_service.similarity(test_texts[0], test_texts[1]) print(f"文本相似度: {similarity:.4f}") # 获取统计信息 stats = embedding_service.get_stats() print("服务统计:", stats)
import pickle import os from typing import Dict, Any, Optional import hashlib import time from threading import Lock class CachedEmbeddingService(BasicEmbeddingService): """带缓存的嵌入服务""" def __init__(self, model_name: str = "paraphrase-multilingual-MiniLM-L12-v2", cache_dir: str = "./embedding_cache", max_cache_size: int = 10000): super().__init__(model_name) self.cache_dir = cache_dir self.max_cache_size = max_cache_size self.disk_cache = {} self.cache_lock = Lock() # 创建缓存目录 os.makedirs(cache_dir, exist_ok=True) # 加载磁盘缓存 self._load_disk_cache() def _get_cache_key(self, texts: List[str]) -> str: """生成缓存键""" text_hash = hashlib.md5(''.join(texts).encode()).hexdigest() return f"{self.model_name}_{text_hash}" def _load_disk_cache(self): """加载磁盘缓存""" cache_file = os.path.join(self.cache_dir, f"{self.model_name}_cache.pkl") if os.path.exists(cache_file): try: with open(cache_file, 'rb') as f: self.disk_cache = pickle.load(f) logger.info(f"加载了 {len(self.disk_cache)} 个缓存项") except Exception as e: logger.warning(f"加载缓存失败: {e}") self.disk_cache = {} def encode(self, texts: List[str], batch_size: int = 32, show_progress_bar: bool = False) -> np.ndarray: """增强的编码方法,包含多级缓存""" start_time = time.time() cache_key = self._get_cache_key(texts) # 检查内存缓存 if cache_key in self.text_cache: self.stats['cache_hits'] += 1 return self.text_cache[cache_key] # 检查磁盘缓存 if cache_key in self.disk_cache: self.stats['cache_hits'] += 1 # 加载到内存缓存 self.text_cache[cache_key] = self.disk_cache[cache_key] return self.disk_cache[cache_key] # 调用父类方法进行编码 embeddings = super().encode(texts, batch_size, show_progress_bar) # 保存到缓存 with self.cache_lock: self.text_cache[cache_key] = embeddings self.disk_cache[cache_key] = embeddings # 清理缓存 if len(self.text_cache) > self.max_cache_size: oldest_key = min(self.text_cache.keys(), key=lambda k: self.stats.get(k, 0)) del self.text_cache[oldest_key] return embeddings # 使用示例 cached_service = CachedEmbeddingService() # 编码测试(第一次会计算,第二次会使用缓存) print("第一次编码(会计算):") embeddings1 = cached_service.encode(test_texts[:2]) print("第二次编码(使用缓存):") embeddings2 = cached_service.encode(test_texts[:2])
import unicodedata from typing import List, Dict, Any, Optional import re class MultilingualEmbeddingService(CachedEmbeddingService): """多语言嵌入服务""" def __init__(self, model_name: str = "paraphrase-multilingual-MiniLM-L12-v2", cache_dir: str = "./multilingual_cache"): super().__init__(model_name, cache_dir) def detect_language(self, text: str) -> str: """简单的语言检测""" # 简单的语言检测逻辑 chinese_chars = len(re.findall(r'[\u4e00-\u9fff]', text)) english_chars = len(re.findall(r'[a-zA-Z]', text)) if chinese_chars > english_chars: return "zh" elif english_chars > 0: return "en" else: return "unknown" def normalize_text(self, text: str) -> str: """文本标准化""" # 基本清理 text = text.strip() text = re.sub(r'\s+', ' ', text) # 合并空格 text = unicodedata.normalize('NFKC', text) return text def encode_multilingual(self, texts: List[str], normalize: bool = True) -> np.ndarray: """多语言编码""" if normalize: texts = [self.normalize_text(text) for text in texts] return super().encode(texts) # 使用示例 multilingual_service = MultilingualEmbeddingService() # 多语言测试文本 multilingual_texts = [ "人工智能是计算机科学的重要分支", "Machine learning is a core technology of AI", "深度学习基于神经网络模型", "Deep learning uses neural networks" ] # 多语言编码 multilingual_embeddings = multilingual_service.encode_multilingual(multilingual_texts) print(f"多语言嵌入形状: {multilingual_embeddings.shape}") # 语言检测测试 for text in multilingual_texts: detected_lang = multilingual_service.detect_language(text) print(f"文本: {text[:20]}... -> 检测语言: {detected_lang}")
A:选择嵌入模型时需要考虑以下因素:
A:提高嵌入性能的方法包括:
A:长文本嵌入的处理策略:
A:保证嵌入质量一致性的方法:
通过本节的学习,我们掌握了向量嵌入技术的核心知识:
向量嵌入是RAG系统的核心技术,高质量的嵌入能够显著提升检索效果。
关键词:RAG高级优化, 向量嵌入, 嵌入模型, 质量评估, 多语言处理, 教程, 实战, 最佳实践
难度:进阶
预计阅读:20 分钟