Qwen3-Embedding-4B部署教程:离线环境全依赖打包,断网场景下仍可完整运行语义搜索
1. 项目简介
今天给大家介绍一个特别实用的项目——基于阿里通义千问Qwen3-Embedding-4B大模型构建的语义搜索演示服务。这个项目最大的特点就是能在完全离线的环境下运行,即使断网也能正常工作。
传统的搜索都是基于关键词匹配,你输入什么词就找包含这些词的内容。但这个项目不一样,它能理解文字的"意思"。比如你输入"我想吃点东西",它不仅能找到包含"吃"的内容,还能找到语义相近的"苹果是一种很好吃的水果",即使这两句话里没有一个字相同。
项目用Streamlit做了个很直观的界面,左边可以自己建知识库,右边进行搜索,结果用进度条和分数显示匹配程度,还能看到背后的向量数据,特别适合想了解大模型嵌入和向量搜索原理的朋友。
2. 环境准备与离线部署
2.1 系统要求
首先看看你的电脑需要满足什么条件:
- 操作系统:Linux Ubuntu 18.04+ 或 Windows 10/11(推荐Linux)
- GPU:NVIDIA显卡,显存至少8GB(因为模型有4B参数)
- Python版本:3.8-3.10
- CUDA:11.7或11.8(必须安装)
2.2 离线依赖打包
既然要离线运行,我们需要提前把所有依赖包都下载好。创建一个下载脚本download_dependencies.py:
import os import subprocess # 创建离线包目录 os.makedirs("offline_packages", exist_ok=True) # 需要下载的包列表 packages = [ "torch==2.0.1+cu117", "transformers==4.36.2", "streamlit==1.28.1", "sentencepiece==0.1.99", "accelerate==0.25.0", "tqdm==4.66.1", "numpy==1.24.3", "scipy==1.11.4", "pandas==2.0.3" ] print("开始下载离线依赖包...") for package in packages: print(f"正在下载: {package}") # 下载包及其所有依赖 subprocess.run([ "pip", "download", package, "-d", "offline_packages", "--prefer-binary", "--extra-index-url", "https://download.pytorch.org/whl/cu117" ]) print("所有依赖包已下载到 offline_packages 目录")运行这个脚本后,所有需要的包都会下载到offline_packages文件夹里。把这个文件夹拷贝到离线环境的机器上。
2.3 离线安装依赖
在离线机器上,进入项目目录,运行:
pip install --no-index --find-links=./offline_packages -r requirements.txt这样就能在没有网络的情况下安装所有依赖了。
3. 模型下载与配置
3.1 下载模型文件
我们需要提前下载Qwen3-Embedding-4B的模型文件。创建下载脚本:
from transformers import AutoModel, AutoTokenizer import os # 创建模型保存目录 model_path = "./qwen3-embedding-4b" os.makedirs(model_path, exist_ok=True) print("开始下载Qwen3-Embedding-4B模型...") # 下载tokenizer tokenizer = AutoTokenizer.from_pretrained( "Qwen/Qwen3-Embedding-4B", cache_dir=model_path, trust_remote_code=True ) # 下载模型 model = AutoModel.from_pretrained( "Qwen/Qwen3-Embedding-4B", cache_dir=model_path, trust_remote_code=True, device_map="auto" ) print("模型下载完成!保存在:", model_path)运行这个脚本后,模型文件会保存在qwen3-embedding-4b目录中。把这个目录完整拷贝到离线环境。
3.2 模型加载配置
创建模型加载配置文件model_config.py:
import torch from transformers import AutoModel, AutoTokenizer import os class EmbeddingModel: def __init__(self, model_path="./qwen3-embedding-4b"): self.model_path = model_path self.device = "cuda" if torch.cuda.is_available() else "cpu" self.model = None self.tokenizer = None def load_model(self): """加载模型和tokenizer""" print("正在加载模型...") # 加载tokenizer self.tokenizer = AutoTokenizer.from_pretrained( self.model_path, trust_remote_code=True, local_files_only=True # 关键:只使用本地文件 ) # 加载模型 self.model = AutoModel.from_pretrained( self.model_path, trust_remote_code=True, local_files_only=True, # 关键:只使用本地文件 device_map="auto", torch_dtype=torch.float16 ) self.model.eval() print("模型加载完成!") def get_embedding(self, text): """获取文本的向量表示""" if not self.model or not self.tokenizer: raise ValueError("请先加载模型") inputs = self.tokenizer( text, padding=True, truncation=True, return_tensors="pt", max_length=512 ).to(self.device) with torch.no_grad(): outputs = self.model(**inputs) embeddings = outputs.last_hidden_state.mean(dim=1) return embeddings.cpu().numpy()这个配置确保了模型完全从本地文件加载,不需要网络连接。
4. 核心功能实现
4.1 语义搜索核心代码
创建主要的搜索逻辑文件semantic_search.py:
import numpy as np from sklearn.metrics.pairwise import cosine_similarity import streamlit as st from model_config import EmbeddingModel class SemanticSearch: def __init__(self): self.model = EmbeddingModel() self.knowledge_base = [] self.embeddings = None def initialize(self): """初始化模型""" self.model.load_model() def build_knowledge_base(self, texts): """构建知识库并生成向量""" self.knowledge_base = [text for text in texts if text.strip()] if self.knowledge_base: # 批量生成向量 all_embeddings = [] for text in self.knowledge_base: embedding = self.model.get_embedding(text) all_embeddings.append(embedding) self.embeddings = np.vstack(all_embeddings) def search(self, query, top_k=5): """语义搜索""" if not self.knowledge_base: return [] # 生成查询向量 query_embedding = self.model.get_embedding(query) # 计算余弦相似度 similarities = cosine_similarity(query_embedding, self.embeddings)[0] # 获取最相似的结果 results = [] for i in np.argsort(similarities)[::-1][:top_k]: results.append({ "text": self.knowledge_base[i], "similarity": float(similarities[i]) }) return results4.2 Streamlit界面实现
创建主界面文件app.py:
import streamlit as st import numpy as np import plotly.express as px from semantic_search import SemanticSearch # 初始化 if "search_engine" not in st.session_state: st.session_state.search_engine = SemanticSearch() st.session_state.search_engine.initialize() st.title("🔍 Qwen3 语义雷达 - 智能语义搜索") st.markdown("基于Qwen3-Embedding-4B的离线语义搜索演示") # 侧边栏状态显示 st.sidebar.success("✅ 向量空间已展开") st.sidebar.info("模型加载完成,可以开始语义搜索") # 双栏布局 col1, col2 = st.columns(2) with col1: st.header("📚 知识库构建") knowledge_text = st.text_area( "输入知识库文本(每行一条):", height=200, value="苹果是一种很好吃的水果\n我喜欢在周末看电影\n编程需要逻辑思维\n健康饮食很重要\n机器学习是人工智能的核心\n运动对身体有益\nPython是流行的编程语言\n读书可以增长知识" ) if st.button("更新知识库"): texts = [line.strip() for line in knowledge_text.split('\n') if line.strip()] st.session_state.search_engine.build_knowledge_base(texts) st.success(f"知识库已更新,共{len(texts)}条文本") with col2: st.header("🔍 语义查询") query = st.text_input("输入查询内容:", "我想吃点东西") if st.button("开始搜索 🚀"): if not st.session_state.search_engine.knowledge_base: st.warning("请先构建知识库") else: with st.spinner("正在进行向量计算..."): results = st.session_state.search_engine.search(query) st.subheader("匹配结果") for i, result in enumerate(results): similarity = result["similarity"] color = "green" if similarity > 0.4 else "gray" st.markdown(f"**结果 {i+1}** (相似度: `{similarity:.4f}`)") st.progress(similarity) st.markdown(f"<span style='color:{color}'>{result['text']}</span>", unsafe_allow_html=True) st.divider() # 向量数据预览 with st.expander("查看幕后数据 (向量值)"): if st.button("显示我的查询词向量"): query_embedding = st.session_state.search_engine.model.get_embedding(query) vector = query_embedding[0] st.write(f"向量维度: {len(vector)}") st.write("前50维数值:") st.dataframe(vector[:50].reshape(1, -1)) # 可视化 fig = px.bar(x=range(50), y=vector[:50], labels={'x': '维度', 'y': '数值'}, title="查询向量前50维分布") st.plotly_chart(fig)5. 离线运行指南
5.1 启动离线服务
在离线环境中,确保所有依赖和模型文件都已就位,然后运行:
streamlit run app.py --server.port 8501 --server.address 0.0.0.0服务启动后,在浏览器访问http://localhost:8501就能看到界面了。
5.2 使用步骤
- 等待模型加载:看到侧边栏显示"✅ 向量空间已展开"表示准备好了
- 构建知识库:在左边文本框输入你的知识库内容,每行一条
- 点击更新知识库:让系统生成这些文本的向量
- 输入查询词:在右边输入你想搜索的内容
- 开始搜索:点击按钮看匹配结果
5.3 实际测试例子
你可以试试这些有趣的例子:
- 查询"我想吃点东西" → 会匹配到"苹果是一种很好吃的水果"
- 查询"如何保持健康" → 会匹配到"运动对身体有益"和"健康饮食很重要"
- 查询"电脑编程" → 会匹配到"编程需要逻辑思维"和"Python是流行的编程语言"
6. 常见问题解决
6.1 GPU内存不足
如果遇到GPU内存不足的问题,可以修改模型加载配置:
# 在model_config.py中修改 model = AutoModel.from_pretrained( model_path, trust_remote_code=True, local_files_only=True, device_map="auto", torch_dtype=torch.float16, # 使用半精度减少内存 low_cpu_mem_usage=True # 减少CPU内存使用 )6.2 运行速度优化
对于大量文本的处理,可以使用批量处理:
# 修改semantic_search.py中的build_knowledge_base方法 def build_knowledge_base(self, texts): self.knowledge_base = [text for text in texts if text.strip()] if self.knowledge_base: # 批量处理提高效率 embeddings = self.model.get_embedding(self.knowledge_base) self.embeddings = np.vstack(embeddings)6.3 离线环境验证
创建验证脚本check_offline.py:
import torch import transformers import streamlit import numpy as np print("=== 离线环境验证 ===") print("PyTorch版本:", torch.__version__) print("CUDA可用:", torch.cuda.is_available()) print("Transformers版本:", transformers.__version__) print("Streamlit版本:", streamlit.__version__) print("NumPy版本:", np.__version__) # 测试模型是否能离线加载 try: from transformers import AutoModel, AutoTokenizer tokenizer = AutoTokenizer.from_pretrained( "./qwen3-embedding-4b", local_files_only=True ) print("✅ 模型离线加载成功") except Exception as e: print("❌ 模型加载失败:", e)7. 总结
通过这个教程,你已经学会了如何在完全离线的环境中部署和运行Qwen3-Embedding-4B语义搜索服务。关键要点包括:
- 离线依赖打包:提前下载所有需要的Python包
- 模型本地化:下载模型文件到本地,确保断网可用
- GPU加速:利用CUDA大幅提升向量计算速度
- 直观界面:Streamlit双栏设计,操作简单明了
- 真正语义理解:基于余弦相似度的深度语义匹配
这个项目不仅是一个演示工具,更是学习大模型嵌入和向量搜索的绝佳实践。你可以在完全离线的环境中测试各种语义搜索场景,深入理解文本向量化的原理和应用。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。