ChatGPT|如何通过ChatGPT问一本书的问题?

在很多场景下需要私域数据,但是在使用ChatGPT对话回答是很泛或者没有相关答案,因此你就需要自己喂养数据,然后形成自己的私域数据数据集,以下就是用一本书作为例子,通过输入一本书问ChatGPT关于这本书其中的问题。其步骤如下:
(1)提取书中的内容;
(2)将书分为小块;
(3)建立语义索引;
(4)问书中的问题;

本文使用的是开源的书:pure-bash-bible中译版(https://github.com/dylanaraps/pure-bash-bible)

提取书中的内容

ChatGPT|如何通过ChatGPT问一本书的问题?

需要使用上一篇文章的LangChain库中和document_loaders模块。其中text_splitter库包含允许用户访问和操作来自不同来源的文本数据的函数和类。

a. UnstructuredPDFLoader,它用于从PDF文件加载和提取文本,PDF文件的路径指定为”xxxx.pdf“;
b. UnstructuredMarkdownLoader,它用于从Markdown文件加载和提取文本;
… 还有一些其他加载非结构化数据的库使用。

代码如下:

loader = UnstructuredMarkdownLoader("../docs/books/pure-bash-bible.md")
data = loader.load() # 加载文件数据
print(f'You have {len(data)} document(s) in your data')
print(f'There are {len(data[0].page_content)} characters in your document')

通过以上代码可以加载您需要训练的markdown文件,打印文本中大概多少字符。

将书分为小块

ChatGPT|如何通过ChatGPT问一本书的问题?


我们将把加载的文档分成更小的“页面”,每个页面有500个字符,目的是在我们向OpenAI提出问题时向它提供上下文信息。与其让 OpenAI 在我们每次提出问题时都阅读整本书,不如给它一小段相关信息来处理,这样效率更高、成本效益更高。

代码如下:

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500, chunk_overlap=200)
docs = text_splitter.split_documents(data)

print(f'Now you have {len(docs)} documents')

现在我们已经完成了文本的准备工作,可以开始下一步了。

建立语义索引

ChatGPT|如何通过ChatGPT问一本书的问题?

创建文档的嵌入,为语义搜索做好准备,将使用向量库,这样就可以将更多书籍添加到我们的语料库中,而不必每次都重新阅读原始文档,这里我们使用向量库FAISS。既然说到向量库,那么先展开说一说什么是向量库。
向量库:在现实生活中非结构化数据的体量变得越来越多,所谓结构化数据指的是像音频、视频、图片,包括用户信息、分子式、时序数据,地理位置数据等,这些数据相比传统的结构化数据更贴合人的需求,它的描述能力更强,同时处理难度也更高。根据IDC的调查显示,未来80%以上的数据可能都是非结构化数据。但目前非结构化数据并没有被很好地利用起来。传统上非结构化数据往往都是通过AI去处理,各种各样的非结构化数据通过深度学习模型转化成Vector Embedding,即一组高维的稠密的数据,这组数据通过他们最近邻的关系,就可以更好的实现推理模型,因此向量数据库构建索引并存储Vector Embedding,让你能够去做比较快的相似查询。
FAISS库:是由Facebook开发的适用于稠密向量匹配的开源库,支持多种向量检索方式,包括内积、欧氏距离等,同时支持精确检索与模糊搜索。FAISS的使用是围绕着index这一对象进行的,index中包含了被索引的数据库向量以及对应的索引值。在构建 index时,需预先提供数据库中每个向量的维度d,随后通过add()的方式将被检索向量存入index中,最终通过search()接口获取与检索向量最邻近topk的距离及索引。

以下代码就是将上面拆分好的docs,建立向量库:

embeddings = OpenAIEmbeddings(openai_api_key=OPENAI_API_KEY)
store = FAISS.from_documents(docs, embeddings)
faiss.write_index(store.index, "langchain-pure-base-bible.index")
store.index = None

with open("faiss-book.pkl""wb") as f:
    pickle.dump(store, f)

至此,我们已经将这本书分解成更小的文档,使用OpenAI嵌入将每个文档转换为一个向量,我们可以继续下一步,即构建实际的问答组件。

问书中的问题

ChatGPT|如何通过ChatGPT问一本书的问题?建立索引后,我们就可以查询这些文档以获取我们的答案。代码如下:

// 先加载前面训练好的语义索引
index = faiss.read_index(index_name)
with open(namespace, "rb") as f:
    docsearch = pickle.load(f)
docsearch.index = index

// 使用ChatOpenAI接口
llm = ChatOpenAI(
    temperature=0, openai_api_key=OPENAI_API_KEY, verbose=True)
chain = load_qa_chain(llm, chain_type="stuff")

// 调用OpenAI根据前面切割好的文档上下文回答问题
query_list = ["可以给一下shell的示例用法的代码么?""for循环该如何使用?""如何检查一个命令是否在用户的PATH中"]
for query in query_list:
    print("问题:", query)
    docs = docsearch.similarity_search(query, include_metadata=True)
    r = chain.run(input_documents=docs, question=query)
    print("结果:", r.encode('utf-8').decode('utf-8'))

ChatGPT回答如下: 

问题:本书中条件表达式有哪些? 

结果:本书中介绍了以下条件表达式:

  • if-then
  • if-then-else
  • if-elif-else
  • case-in-esac
  • 三元条件表达式

此外,还介绍了一些与条件表达式相关的内容,如布尔运算符、比较运算符、逻辑运算符等。



从上可以看出ChatGPT并不是直接用原文来回答,是的确将文档模型建立好,总结提炼出的回答。

到目前为止,如何问一本书的问题步骤介绍完成,我们也可以采取许多其他步骤来改进系统并使其更加有效。例如,我们可以通过在更大的问题和答案数据集上进行训练来提高系统的准确性,我们还可以在特定领域(例如,历史、科学等)上微调OpenAI模型,以提高其在与该领域相关的问题上的表现。(可以结合LangChain的介绍来做更多有趣的事情)

资料

(1)https://bennycheung.github.io/ask-a-book-questions-with-langchain-openai
(2)代码如下:

from langchain.chains.question_answering import load_qa_chain, load_summarize_chain
from langchain.chat_models import ChatOpenAI  # 使用高版本的langchain
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.document_loaders import UnstructuredMarkdownLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
import os
import faiss
import pickle

OPENAI_API_KEY = os.environ["API_SECRET"]

index_name = "langchain-pure-base-bible.index"
namespace = "faiss-book.pkl"

def isExistTrainFile():
    return os.path.exists(index_name)

def train():
    loader = UnstructuredMarkdownLoader("../docs/books/pure-base-bible.md")
    data = loader.load()
    print(f'You have {len(data)} document(s) in your data')
    print(f'There are {len(data[0].page_content)} characters in your document')

    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=1000, chunk_overlap=0)
    docs = text_splitter.split_documents(data)

    print(f'Now you have {len(docs)} documents')

    embeddings = OpenAIEmbeddings(openai_api_key=OPENAI_API_KEY)
    store = FAISS.from_documents(docs, embeddings)
    faiss.write_index(store.index, index_name)
    store.index = None

    with open(namespace, "wb") as f:
        pickle.dump(store, f)

def runPrompt():
    index = faiss.read_index(index_name)
    with open(namespace, "rb") as f:
        docsearch = pickle.load(f)
    docsearch.index = index

    llm = ChatOpenAI(
        temperature=0, openai_api_key=OPENAI_API_KEY, verbose=True)
    chain = load_qa_chain(llm, chain_type="stuff")

    query_list = ["可以给一下shell的示例用法的代码么?""for循环该如何使用?""如何检查一个命令是否在用户的PATH中"]
    for query in query_list:
        print("问题:", query)
        docs = docsearch.similarity_search(query, include_metadata=True)
        r = chain.run(input_documents=docs, question=query)
        print("结果:", r.encode('utf-8').decode('utf-8'))

if __name__ == "__main__":
    if not isExistTrainFile():
        train()
    runPrompt()

原文始发于微信公众号(周末程序猿):ChatGPT|如何通过ChatGPT问一本书的问题?

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/169451.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!