360免費(fèi)做網(wǎng)站凡科建站怎么導(dǎo)出網(wǎng)頁
目錄
- 一、知識文檔的準(zhǔn)備
- 二、OCR轉(zhuǎn)換
- 三、分詞處理
- 四、創(chuàng)建向量數(shù)據(jù)庫
- 五、初始化語言聊天模型
- 1.prompt
- 2.檢索鏈
- 3.對話
- 完整代碼
知識文檔的準(zhǔn)備:首先需要準(zhǔn)備知識文檔,這些文檔可以是多種格式,如Word、TXT、PDF等。使用文檔加載器或多模態(tài)模型(如OCR技術(shù))將這些文檔轉(zhuǎn)換為可理解的純文本數(shù)據(jù)。對于長篇文檔,還需進(jìn)行文檔切片,以便更高效地處理和檢索信息。
嵌入模型:將文本轉(zhuǎn)換為向量形式,以便通過計(jì)算向量之間的差異來識別語義上相似的句子。常見的嵌入模型包括Word2Vec、BERT和GPT系列等。
向量數(shù)據(jù)庫:將嵌入后的向量數(shù)據(jù)存儲在向量數(shù)據(jù)庫中,以便進(jìn)行高效的相似性搜索。
查詢檢索:當(dāng)用戶提出查詢時(shí),系統(tǒng)會將查詢通過嵌入模型轉(zhuǎn)換為向量,然后在向量數(shù)據(jù)庫中進(jìn)行相似性搜索,找到與查詢最相關(guān)的文檔或信息。
生成回答:將檢索到的相關(guān)信息與用戶的查詢結(jié)合,生成最終的回答。生成模型會利用檢索到的信息作為上下文輸入,并結(jié)合大語言模型來生成文本內(nèi)容。
這里的嵌入模型用的是本地部署的ollama,也可以使用openai,但是連接不太穩(wěn)定,還有阿里云的通義千問。
一、知識文檔的準(zhǔn)備
知識庫中存放pdf等類型的文檔,準(zhǔn)備后面轉(zhuǎn)換為txt文本
二、OCR轉(zhuǎn)換
OCR轉(zhuǎn)換會將PDF、圖片這些信息提取得到TXT文本。數(shù)據(jù)質(zhì)量的好壞直接影響著后面模型對話效果。因此PDF解析選用的工具必須精確且合適。
在這個(gè)例子中,我是事先將PDF用MinerU解析成markdown形式了
三、分詞處理
文本分詞處理(Tokenization)是自然語言處理(NLP)中的一個(gè)重要步驟,其目的是將連續(xù)的文本字符串分割成有意義的單元,這些單元通常被稱為“詞”或“標(biāo)記”(tokens)。分詞處理是文本分析的基礎(chǔ),因?yàn)榇蠖鄶?shù)NLP任務(wù)都需要在詞級別上進(jìn)行操作,例如文本分類、情感分析、機(jī)器翻譯等。
中文分詞使用了jieba庫
jieba 是一個(gè)非常流行的 Python 中文分詞庫,主要用于將中文文本切分成單個(gè)詞語。它支持多種分詞模式,并提供了豐富的功能來滿足不同的自然語言處理需求。
主要功能和特點(diǎn):
分詞模式:
精確模式:將文本精確地切分成單個(gè)詞語,適合用于文本分析。
全模式:將文本中所有可能的詞語都掃描出來,速度非常快,但可能存在冗余數(shù)據(jù)。
搜索引擎模式:在精確模式的基礎(chǔ)上,對長詞再次進(jìn)行切分,提高召回率,適合用于搜索引擎分詞。
自定義詞典:用戶可以通過自定義詞典來增加新詞,以提高分詞的準(zhǔn)確性。
關(guān)鍵詞提取:jieba 提供了基于 TF-IDF 算法的關(guān)鍵詞提取功能,可以從文本中提取出最重要的詞。
詞性標(biāo)注:通過 jieba.posseg 模塊,可以在分詞的同時(shí)獲取詞性信息。
并行分詞:支持并行分詞,以提高分詞速度
。
四、創(chuàng)建向量數(shù)據(jù)庫
def create_vector_store(tokenized_texts: List[List[str]], embeddings_model: OllamaEmbeddings) -> FAISS:"""將分詞后的文本創(chuàng)建向量庫"""try:# 將分詞列表轉(zhuǎn)換回文本processed_texts = [' '.join(tokens) for tokens in tokenized_texts]# 批量處理優(yōu)化batch_size = 100 # 可以根據(jù)實(shí)際情況調(diào)整vectors = []# # 如果有 GPU# if FAISS.get_num_gpus():# res = FAISS.StandardGpuResources()# index = FAISS.index_cpu_to_gpu(res, 0, index)for i in tqdm(range(0, len(processed_texts), batch_size), desc="創(chuàng)建向量數(shù)據(jù)庫"):batch = processed_texts[i:i + batch_size]# 批量創(chuàng)建向量vector_store = FAISS.from_texts(texts=batch,embedding=embeddings_model,metadatas=[{"index": j} for j in range(i, i + len(batch))] # 添加元數(shù)據(jù)以追蹤文檔)vectors.append(vector_store)# 如果有多個(gè)批次,合并它們if len(vectors) > 1:final_vector_store = vectors[0]for v in vectors[1:]:final_vector_store.merge_from(v)else:final_vector_store = vectors[0]# 保存向量庫到本地final_vector_store.save_local("resume_vectors")return final_vector_storeexcept Exception as e:print(f"創(chuàng)建向量庫時(shí)發(fā)生錯(cuò)誤: {str(e)}")raise
五、初始化語言聊天模型
剛剛就是制作了向量數(shù)據(jù)庫,這是大模型的第一步,下面還需要有明確的提示詞prompt
1.prompt
2.檢索鏈
檢索鏈(Retrieval Chain)是一種在信息檢索和自然語言處理中使用的技術(shù)流程,主要用于從大規(guī)模數(shù)據(jù)集中高效地找到與用戶查詢最相關(guān)的信息片段或文檔
3.對話
使用一個(gè)while循環(huán)始終在對話中
完整代碼
import os
import jieba
import re
from typing import List
import pdf
# from langchain_community.embeddings import OpenAIEmbeddings
from langchain_community.embeddings import OllamaEmbeddings
from langchain_community.vectorstores import FAISS
from langchain_community.llms import Ollama
from tqdm import tqdm from loguru import logger
from magic_pdf.data.data_reader_writer import FileBasedDataWriter
from magic_pdf.pipe.UNIPipe import UNIPipeimport nltk
# 下載punkt
nltk.download('punkt')# 設(shè)置 OpenAI API 密鑰
# os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
# 創(chuàng)建 OpenAI API 密鑰
api_key = "sk-xxx"
os.environ["OPENAI_API_KEY"] = api_key# 定義簡歷文件夾路徑
resume_folder = "./data/demo"# 讀取 PDF 文件并提取文本
# def extract_text_from_pdfs(folder_path):
# texts = []
# for filename in os.listdir(folder_path):
# if filename.endswith(".pdf"):
# with open(os.path.join(folder_path, filename), "rb") as file:
# reader = PyPDF2.PdfReader(file)
# text = ""
# for page in reader.pages:
# text += page.extract_text()
# texts.append(text)
# return texts# 讀取markdown文件并提取文本
def extract_text_from_markdown(folder_path):texts = []for filename in os.listdir(folder_path):if filename.endswith(".md"):with open(os.path.join(folder_path, filename), "r", encoding="utf-8") as file:text = file.read()texts.append(text)return textsdef clean_text(text: str) -> str:"""清理文本,移除特殊字符和多余的空白"""# 替換多個(gè)空白字符為單個(gè)空格text = re.sub(r'\s+', ' ', text)# 移除特殊字符,保留中文、英文、數(shù)字和基本標(biāo)點(diǎn)text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9.,!?;:,。!?;:]', ' ', text)return text.strip()def tokenize_texts(texts: List[str]) -> List[List[str]]:"""對文本進(jìn)行分詞處理""Args:texts: 要處理的文本列表Returns:處理后的分詞列表"""tokenized_texts = []for text in texts:# 清理文本cleaned_text = clean_text(text)# 分別處理中文和英文words = []# 使用jieba進(jìn)行中文分詞segments = jieba.cut(cleaned_text)# 過濾空字符串和純空白字符words = [word for word in segments if word.strip()]# 移除停用詞(可選)# words = [word for word in words if word not in stopwords]tokenized_texts.append(words)return tokenized_textsdef create_vector_store(tokenized_texts: List[List[str]], embeddings_model: OllamaEmbeddings) -> FAISS:"""將分詞后的文本創(chuàng)建向量庫"""try:# 將分詞列表轉(zhuǎn)換回文本processed_texts = [' '.join(tokens) for tokens in tokenized_texts]# 批量處理優(yōu)化batch_size = 100 # 可以根據(jù)實(shí)際情況調(diào)整vectors = []# # 如果有 GPU# if FAISS.get_num_gpus():# res = FAISS.StandardGpuResources()# index = FAISS.index_cpu_to_gpu(res, 0, index)for i in tqdm(range(0, len(processed_texts), batch_size), desc="創(chuàng)建向量數(shù)據(jù)庫"):batch = processed_texts[i:i + batch_size]# 批量創(chuàng)建向量vector_store = FAISS.from_texts(texts=batch,embedding=embeddings_model,metadatas=[{"index": j} for j in range(i, i + len(batch))] # 添加元數(shù)據(jù)以追蹤文檔)vectors.append(vector_store)# 如果有多個(gè)批次,合并它們if len(vectors) > 1:final_vector_store = vectors[0]for v in vectors[1:]:final_vector_store.merge_from(v)else:final_vector_store = vectors[0]# 保存向量庫到本地final_vector_store.save_local("resume_vectors")return final_vector_storeexcept Exception as e:print(f"創(chuàng)建向量庫時(shí)發(fā)生錯(cuò)誤: {str(e)}")raise# 提取簡歷文本
resume_texts = extract_text_from_markdown(resume_folder)
# resume_texts = extract_text_from_pdfs(resume_folder)
print("簡歷文本提取完成")# 簡歷文本分詞
tokenized_texts = tokenize_texts(resume_texts)
print(f"簡歷文本分詞完成,共處理 {len(tokenized_texts)} 份文檔")# 可以打印一些統(tǒng)計(jì)信息(可選)
for i, tokens in enumerate(tokenized_texts):print(f"文檔 {i+1} 分詞數(shù)量: {len(tokens)}")# 創(chuàng)建 OpenAI 嵌入
embeddings = OllamaEmbeddings(model="nomic-embed-text:latest",base_url='xxx')
print("ollama 嵌入完成~")# 創(chuàng)建向量庫
vector_store = create_vector_store(tokenized_texts, embeddings)
print("向量庫創(chuàng)建完成")# Initialize OpenAI model
# from langchain_community.chat_models import ChatOpenAI
# llm = ChatOpenAI(model='gpt-4o', temperature=0.1, api_key=os.environ.get("OPENAI_API_KEY"))from langchain_community.llms import Ollama
llm = Ollama(model="llama3.3:70b", temperature=0.1, num_ctx=60000,base_url='xxx')# Update imports
from langchain.chains import RetrievalQA # Changed from create_retrieval_qa_chain
from langchain.prompts import PromptTemplate # Import the necessary classPROMPT_TEMPLATE = """
已知信息:{context}。
"你是一個(gè)核聚變、人工智能、科學(xué)計(jì)算領(lǐng)域的人才鑒別專家,你具備管理大量簡歷的能力;請注意我向你提供了很多簡歷PDF文件,但每個(gè)PDF文件對應(yīng)一個(gè)候選人,包括候選人的姓名、年齡、技能、經(jīng)歷、項(xiàng)目、成果等內(nèi)容,請仔細(xì)識別各個(gè)信息。 \
現(xiàn)在需要你幫我分析每個(gè)候選人的詳細(xì)信息,包括:年齡、教育程度、專業(yè)技能、職業(yè)履歷、項(xiàng)目背景、研究成果、獲得榮譽(yù)、發(fā)展?jié)摿?#xff0c;然后幫我完成以下兩個(gè)功能: \
1.當(dāng)我給出開展項(xiàng)目的描述信息時(shí),你能幫我準(zhǔn)確地按照適配項(xiàng)目的優(yōu)先級推薦相關(guān)候選人,每次允許按照優(yōu)先級順序推薦多個(gè)候選人,并且詳細(xì)給出推薦原因,使用markdown的表格形式給出,包括以下信息:姓名、年齡、專業(yè)技能、推薦原因; \
2.當(dāng)我需要分析一個(gè)候選人時(shí),請你能進(jìn)行客觀、準(zhǔn)確的評估,先描述其主要信息,然后按照:專業(yè)能力、科研成果、項(xiàng)目成績、發(fā)展?jié)摿?、綜合能力進(jìn)行評分,每項(xiàng)總分100,結(jié)果以markown形式給出。 \
請務(wù)必注意每個(gè)簡歷僅對應(yīng)一個(gè)候選人,切記不要混淆各個(gè)人的信息;1個(gè)候選人只需引用1次相關(guān)文檔即可,仔細(xì)識別每個(gè)文檔中候選人的姓名。"請回答以下問題:{question}
"""
# 創(chuàng)建提示模板
prompt_template = PromptTemplate(input_variables=["context", "question"],template=PROMPT_TEMPLATE
)# 創(chuàng)建檢索鏈
chain_type_kwargs = {"prompt": prompt_template,"document_variable_name": "context",
}# 創(chuàng)建檢索鏈
qa = RetrievalQA.from_chain_type(llm=llm,chain_type="stuff",retriever=vector_store.as_retriever(),chain_type_kwargs=chain_type_kwargs,
)def chat_loop():print("\n歡迎使用簡歷分析助手!")print("輸入 'quit' 或 'exit' 結(jié)束對話")while True:# Get user inputuser_input = input("\n請輸入您的問題: ").strip()# Check if user wants to exitif user_input.lower() in ['quit', 'exit']:print("感謝使用,再見!")breakif user_input:try:# Get the responseresult = qa.run(user_input)print("\n回答:")print(result)except Exception as e:print(f"發(fā)生錯(cuò)誤: {str(e)}")continueif __name__ == "__main__":chat_loop()