InternLM 模型部署
準備環境
拷貝環境
/root/share/install_conda_env_internlm_base.sh InternLM
激活環境
conda activate InternLM
安裝依賴
# 升級pip
python -m pip install --upgrade pippip install modelscope==1.9.5
pip install transformers==4.35.2
pip install streamlit==1.24.0
pip install sentencepiece==0.1.99
pip install accelerate==0.24.1
模型下載
mkdir -p /root/data/model/Shanghai_AI_Laboratory
cp -r /root/share/temp/model_repos/internlm-chat-7b /root/data/model/Shanghai_AI_Laboratory/internlm-chat-7b
LangChain 相關環境配置
pip install langchain==0.0.292
pip install gradio==4.4.0
pip install chromadb==0.4.15
pip install sentence-transformers==2.2.2
pip install unstructured==0.10.30
pip install markdown==3.3.7
同時,我們需要使用到開源詞向量模型 Sentence Transformer:(我們也可以選用別的開源詞向量模型來進行 Embedding,目前選用這個模型是相對輕量、支持中文且效果較好的,同學們可以自由嘗試別的開源詞向量模型)
首先需要使用 huggingface 官方提供的 huggingface-cli 命令行工具。安裝依賴:
pip install -U huggingface_hub
然后在和 /root/data 目錄下新建python文件 download_hf.py,填入以下代碼:
- resume-download:斷點續下
- local-dir:本地存儲路徑。(linux環境下需要填寫絕對路徑)
import os# 下載模型
os.system('huggingface-cli download --resume-download sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 --local-dir /root/data/model/sentence-transformer')
如果下載速度慢可以使用鏡像下載將 download_hf.py 中的代碼修改為以下代碼:
import os# 設置環境變量
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'# 下載模型
os.system('huggingface-cli download --resume-download sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 --local-dir /root/data/model/sentence-transformer')
執行腳本
python download_hf.py
下載 NLTK 相關資源
下載
cd /root
git clone https://gitee.com/yzy0612/nltk_data.git --branch gh-pages
cd nltk_data
mv packages/* ./
cd tokenizers
unzip punkt.zip
cd ../taggers
unzip averaged_perceptron_tagger.zip
下載本項目代碼
cd /root/data
git clone https://github.com/InternLM/tutorial
知識庫搭建
數據收集
選擇mindspre docs代碼倉作為語料庫來源
地址:
https://gitee.com/mindspore/docs
# 進入到數據庫盤
cd /root/data
# clone 上述開源倉庫
git clone https://gitee.com/mindspore/docs.git
知識庫搭建的腳本
# 首先導入所需第三方庫
from langchain.document_loaders import UnstructuredFileLoader
from langchain.document_loaders import UnstructuredMarkdownLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
from tqdm import tqdm
import os# 獲取文件路徑函數
def get_files(dir_path):# args:dir_path,目標文件夾路徑file_list = []for filepath, dirnames, filenames in os.walk(dir_path):# os.walk 函數將遞歸遍歷指定文件夾for filename in filenames:# 通過后綴名判斷文件類型是否滿足要求if filename.endswith("_CN.md"):# 如果滿足要求,將其絕對路徑加入到結果列表file_list.append(os.path.join(filepath, filename))elif filename.endswith("_CN.txt"):file_list.append(os.path.join(filepath, filename))return file_list# 加載文件函數
def get_text(dir_path):# args:dir_path,目標文件夾路徑# 首先調用上文定義的函數得到目標文件路徑列表file_lst = get_files(dir_path)# docs 存放加載之后的純文本對象docs = []# 遍歷所有目標文件for one_file in tqdm(file_lst):file_type = one_file.split('.')[-1]if file_type == 'md':loader = UnstructuredMarkdownLoader(one_file)elif file_type == 'txt':loader = UnstructuredFileLoader(one_file)else:# 如果是不符合條件的文件,直接跳過continuedocs.extend(loader.load())return docs# 目標文件夾
tar_dir = ["/root/data/docs"
]# 加載目標文件
docs = []
for dir_path in tar_dir:docs.extend(get_text(dir_path))# 對文本進行分塊
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=150)
split_docs = text_splitter.split_documents(docs)# 加載開源詞向量模型
embeddings = HuggingFaceEmbeddings(model_name="/root/data/model/sentence-transformer")# 構建向量數據庫
# 定義持久化路徑
persist_directory = 'data_base/vector_db/chroma'
# 加載數據庫
vectordb = Chroma.from_documents(documents=split_docs,embedding=embeddings,persist_directory=persist_directory # 允許我們將persist_directory目錄保存到磁盤上
)
# 將加載的向量數據庫持久化到磁盤上
vectordb.persist()
可以在 /root/data 下新建一個 demo目錄,將該腳本和后續腳本均放在該目錄下運行。運行上述腳本,即可在本地構建已持久化的向量數據庫,后續直接導入該數據庫即可,無需重復構建。
InternLM 接入 LangChain
腳本
from langchain.llms.base import LLM
from typing import Any, List, Optional
from langchain.callbacks.manager import CallbackManagerForLLMRun
from transformers import AutoTokenizer, AutoModelForCausalLM
import torchclass InternLM_LLM(LLM):# 基于本地 InternLM 自定義 LLM 類tokenizer : AutoTokenizer = Nonemodel: AutoModelForCausalLM = Nonedef __init__(self, model_path :str):# model_path: InternLM 模型路徑# 從本地初始化模型super().__init__()print("正在從本地加載模型...")self.tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)self.model = AutoModelForCausalLM.from_pretrained(model_path, trust_remote_code=True).to(torch.bfloat16).cuda()self.model = self.model.eval()print("完成本地模型的加載")def _call(self, prompt : str, stop: Optional[List[str]] = None,run_manager: Optional[CallbackManagerForLLMRun] = None,**kwargs: Any):# 重寫調用函數system_prompt = """You are an AI assistant whose name is InternLM (書生·浦語).- InternLM (書生·浦語) is a conversational language model that is developed by Shanghai AI Laboratory (上海人工智能實驗室). It is designed to be helpful, honest, and harmless.- InternLM (書生·浦語) can understand and communicate fluently in the language chosen by the user such as English and 中文."""messages = [(system_prompt, '')]response, history = self.model.chat(self.tokenizer, prompt , history=messages)return response@propertydef _llm_type(self) -> str:return "InternLM"
將上述代碼封裝為 LLM.py,后續將直接從該文件中引入自定義的 LLM 類。
構建檢索問答鏈
整體腳本
from langchain.vectorstores import Chroma
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
import os# 定義 Embeddings
embeddings = HuggingFaceEmbeddings(model_name="/root/data/model/sentence-transformer")# 向量數據庫持久化路徑
persist_directory = 'data_base/vector_db/chroma'# 加載數據庫
vectordb = Chroma(persist_directory=persist_directory, embedding_function=embeddings
)from LLM import InternLM_LLM
llm = InternLM_LLM(model_path = "/root/data/model/Shanghai_AI_Laboratory/internlm-chat-7b")
llm.predict("你是誰")from langchain.prompts import PromptTemplate# 我們所構造的 Prompt 模板
template = """使用以下上下文來回答用戶的問題。如果你不知道答案,就說你不知道。總是使用中文回答。
問題: {question}
可參考的上下文:
···
{context}
···
如果給定的上下文無法讓你做出回答,請回答你不知道。
有用的回答:"""# 調用 LangChain 的方法來實例化一個 Template 對象,該對象包含了 context 和 question 兩個變量,在實際調用時,這兩個變量會被檢索到的文檔片段和用戶提問填充
QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],template=template)from langchain.chains import RetrievalQAqa_chain = RetrievalQA.from_chain_type(llm,retriever=vectordb.as_retriever(),return_source_documents=True,chain_type_kwargs={"prompt":QA_CHAIN_PROMPT})# 檢索問答鏈回答效果
question = "什么是MindSpore"
result = qa_chain({"query": question})
print("檢索問答鏈回答 question 的結果:")
print(result["result"])# 僅 LLM 回答效果
result_2 = llm(question)
print("大模型回答 question 的結果:")
print(result_2)
部署 Web Demo
from langchain.vectorstores import Chroma
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
import os
from LLM import InternLM_LLM
from langchain.prompts import PromptTemplate
from langchain.chains import RetrievalQAdef load_chain():# 加載問答鏈# 定義 Embeddingsembeddings = HuggingFaceEmbeddings(model_name="/root/data/model/sentence-transformer")# 向量數據庫持久化路徑persist_directory = 'data_base/vector_db/chroma'# 加載數據庫vectordb = Chroma(persist_directory=persist_directory, # 允許我們將persist_directory目錄保存到磁盤上embedding_function=embeddings)# 加載自定義 LLMllm = InternLM_LLM(model_path = "/root/data/model/Shanghai_AI_Laboratory/internlm-chat-7b")# 定義一個 Prompt Templatetemplate = """使用以下上下文來回答最后的問題。如果你不知道答案,就說你不知道,不要試圖編造答案。盡量使答案簡明扼要。總是在回答的最后說“謝謝你的提問!”。{context}問題: {question}有用的回答:"""QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],template=template)# 運行 chainqa_chain = RetrievalQA.from_chain_type(llm,retriever=vectordb.as_retriever(),return_source_documents=True,chain_type_kwargs={"prompt":QA_CHAIN_PROMPT})return qa_chainclass Model_center():"""存儲檢索問答鏈的對象 """def __init__(self):# 構造函數,加載檢索問答鏈self.chain = load_chain()def qa_chain_self_answer(self, question: str, chat_history: list = []):"""調用問答鏈進行回答"""if question == None or len(question) < 1:return "", chat_historytry:chat_history.append((question, self.chain({"query": question})["result"]))# 將問答結果直接附加到問答歷史中,Gradio 會將其展示出來return "", chat_historyexcept Exception as e:return e, chat_historyimport gradio as gr# 實例化核心功能對象
model_center = Model_center()
# 創建一個 Web 界面
block = gr.Blocks()
with block as demo:with gr.Row(equal_height=True): with gr.Column(scale=15):# 展示的頁面標題gr.Markdown("""<h1><center>InternLM</center></h1><center>書生浦語</center>""")with gr.Row():with gr.Column(scale=4):# 創建一個聊天機器人對象chatbot = gr.Chatbot(height=450, show_copy_button=True)# 創建一個文本框組件,用于輸入 prompt。msg = gr.Textbox(label="Prompt/問題")with gr.Row():# 創建提交按鈕。db_wo_his_btn = gr.Button("Chat")with gr.Row():# 創建一個清除按鈕,用于清除聊天機器人組件的內容。clear = gr.ClearButton(components=[chatbot], value="Clear console")# 設置按鈕的點擊事件。當點擊時,調用上面定義的 qa_chain_self_answer 函數,并傳入用戶的消息和聊天歷史記錄,然后更新文本框和聊天機器人組件。db_wo_his_btn.click(model_center.qa_chain_self_answer, inputs=[msg, chatbot], outputs=[msg, chatbot])gr.Markdown("""提醒:<br>1. 初始化數據庫時間可能較長,請耐心等待。2. 使用中如果出現異常,將會在文本輸入框進行展示,請不要驚慌。 <br>""")
gr.close_all()
# 直接啟動
demo.launch()
通過將上述代碼封裝為 run_gradio.py 腳本,直接通過 python 命令運行,即可在本地啟動知識庫助手的 Web Demo,默認會在 7860 端口運行,接下來將服務器端口映射到本地端口即可訪問: