广告

ChatGPT如何构建可以回答有关您网站的问题的 AI

ChatGPT如何构建可以回答有关您网站的问题的 AI

本教程介绍了一个简单的网站抓取示例(在本示例中为 OpenAI 网站),使用Embeddings API将抓取的页面转换为嵌入,然后创建一个基本的搜索功能,允许用户询问有关嵌入信息的问题. 这旨在成为使用自定义知识库的更复杂应用程序的起点。

入门

Python 和 GitHub 的一些基础知识对本教程很有帮助。在深入研究之前,请确保设置一个 OpenAI API 密钥并完成快速入门教程。这将为如何充分发挥 API 的潜力提供良好的直觉。

Python 与 OpenAI、Pandas、transformers、NumPy 和其他流行的程序包一起用作主要的编程语言。如果您在学习本教程时遇到任何问题,请在OpenAI 社区论坛上提问。

要从代码开始,请在 GitHub 上克隆本教程的完整代码。或者,跟随并将每个部分复制到 Jupyter 笔记本中并逐步运行代码,或者只是阅读。避免任何问题的一个好方法是设置一个新的虚拟环境并通过运行以下命令安装所需的包:

  • python -m venv env

  • source env/bin/activate

  • pip install -r requirements.txt
设置网络爬虫
本教程的主要重点是 OpenAI API,因此如果您愿意,可以跳过有关如何创建网络爬虫的上下文并直接下载源代码。否则,请展开下面的部分以完成抓取机制的实施。

了解如何构建网络爬虫
构建嵌入索引

欧易OKX(知名品牌)

全球三大交易所之一,注册并登录App即可领取高达60,000元的数字货币盲盒!

CSV 是存储嵌入的常用格式。您可以通过将原始文本文件(位于文本目录中)转换为 Pandas 数据帧来将此格式与 Python 结合使用。Pandas 是一个流行的开源库,可帮助您处理表格数据(存储在行和列中的数据)。
空白空行会使文本文件混乱并使它们更难处理。一个简单的函数可以删除这些行并整理文件。
  1. def remove_newlines(serie):
  2. serie = serie.str.replace('n', ' ')
  3. serie = serie.str.replace('n', ' ')
  4. serie = serie.str.replace('  ', ' ')
  5. serie = serie.str.replace('  ', ' ')
  6. return serie
将文本转换为 CSV 需要循环访问之前创建的文本目录中的文本文件。打开每个文件后,删除多余的间距并将修改后的文本附加到列表中。然后,将删除了新行的文本添加到空的 Pandas 数据框中,并将数据框写入 CSV 文件。

额外的间距和新行会使文本混乱并使嵌入过程复杂化。此处使用的代码有助于删除其中一些字符,但您可能会发现第 3 方库或其他方法有助于删除更多不必要的字符。
  1. import pandas as pd

  2. # Create a list to store the text files
  3. texts=[]

  4. # Get all the text files in the text directory
  5. for file in os.listdir("text/" + domain + "/"):

  6. # Open the file and read the text
  7. with open("text/" + domain + "/" + file, "r", encoding="UTF-8") as f:
  8. text = f.read()

  9. # Omit the first 11 lines and the last 4 lines, then replace -, _, and #update with spaces.
  10. texts.append((file[11:-4].replace('-',' ').replace('_', ' ').replace('#update',''), text))

  11. # Create a dataframe from the list of texts
  12. df = pd.DataFrame(texts, columns = ['fname', 'text'])

  13. # Set the text column to be the raw text with the newlines removed
  14. df['text'] = df.fname + ". " + remove_newlines(df.text)
  15. df.to_csv('processed/scraped.csv')
  16. df.head()
将原始文本保存到 CSV 文件后的下一步是标记化。此过程通过分解句子和单词将输入文本拆分为标记。通过查看文档中的 Tokenizer可以看到对此的可视化演示。

  • 一个有用的经验法则是,对于普通英文文本,一个标记通常对应于 ~4 个字符的文本。这相当于大约 ¾ 个单词(因此 100 个标记 ~= 75 个单词)。

API 对嵌入的输入令牌的最大数量有限制。要保持在限制以下,CSV 文件中的文本需要分成多行。将首先记录每一行的现有长度,以确定需要拆分哪些行。
  1. import tiktoken

  2. # Load the cl100k_base tokenizer which is designed to work with the ada-002 model
  3. tokenizer = tiktoken.get_encoding("cl100k_base")

  4. df = pd.read_csv('processed/scraped.csv', index_col=0)
  5. df.columns = ['title', 'text']

  6. # Tokenize the text and save the number of tokens to a new column
  7. df['n_tokens'] = df.text.apply(lambda x: len(tokenizer.encode(x)))

  8. # Visualize the distribution of the number of tokens per row using a histogram
  9. df.n_tokens.hist()

最新的嵌入模型可以处理多达 8191 个输入标记的输入,因此大多数行不需要任何分块,但对于每个被抓取的子页面来说可能并非如此,因此下一个代码块会将较长的行拆分为较小的块。
  1. max_tokens = 500

  2. # Function to split the text into chunks of a maximum number of tokens
  3. def split_into_many(text, max_tokens = max_tokens):

  4. # Split the text into sentences
  5. sentences = text.split('. ')

  6. # Get the number of tokens for each sentence
  7. n_tokens = [len(tokenizer.encode(" " + sentence)) for sentence in sentences]

  8. chunks = []
  9. tokens_so_far = 0
  10. chunk = []

  11. # Loop through the sentences and tokens joined together in a tuple
  12. for sentence, token in zip(sentences, n_tokens):

  13. # If the number of tokens so far plus the number of tokens in the current sentence is greater
  14. # than the max number of tokens, then add the chunk to the list of chunks and reset
  15. # the chunk and tokens so far
  16. if tokens_so_far + token > max_tokens:
  17. chunks.append(". ".join(chunk) + ".")
  18. chunk = []
  19. tokens_so_far = 0

  20. # If the number of tokens in the current sentence is greater than the max number of
  21. # tokens, go to the next sentence
  22. if token > max_tokens:
  23. continue

  24. # Otherwise, add the sentence to the chunk and add the number of tokens to the total
  25. chunk.append(sentence)
  26. tokens_so_far += token + 1

  27. return chunks


  28. shortened = []

  29. # Loop through the dataframe
  30. for row in df.iterrows():

  31. # If the text is None, go to the next row
  32. if row[1]['text'] is None:
  33. continue

  34. # If the number of tokens is greater than the max number of tokens, split the text into chunks
  35. if row[1]['n_tokens'] > max_tokens:
  36. shortened += split_into_many(row[1]['text'])

  37. # Otherwise, add the text to the list of shortened texts
  38. else:
  39. shortened.append( row[1]['text'] )
再次可视化更新后的直方图有助于确认行是否已成功拆分为缩短的部分。
  1. df = pd.DataFrame(shortened, columns = ['text'])
  2. df['n_tokens'] = df.text.apply(lambda x: len(tokenizer.encode(x)))
  3. df.n_tokens.hist()

内容现在被分解成更小的块,可以向 OpenAI API 发送一个简单的请求,指定使用新的 text-embedding-ada-002 模型来创建嵌入:

  1. import openai

  2. df['embeddings'] = df.text.apply(lambda x: openai.Embedding.create(input=x, engine='text-embedding-ada-002')['data'][0]['embedding'])

  3. df.to_csv('processed/embeddings.csv')
  4. df.head()
这大约需要 3-5 分钟,但之后您就可以使用嵌入了!

使用嵌入构建问答系统


嵌入已准备就绪,此过程的最后一步是创建一个简单的问答系统。这将接受用户的问题,创建它的嵌入,并将其与现有嵌入进行比较,以从抓取的网站中检索最相关的文本。然后,text-davinci-003 模型将根据检索到的文本生成听起来自然的答案。
将嵌入转换为 NumPy 数组是第一步,考虑到在 NumPy 数组上运行的许多可用函数,这将在如何使用它方面提供更大的灵活性。它还会将维度展平为一维,这是许多后续操作所需的格式。
  1. import numpy as np
  2. from openai.embeddings_utils import distances_from_embeddings

  3. df=pd.read_csv('processed/embeddings.csv', index_col=0)
  4. df['embeddings'] = df['embeddings'].apply(eval).apply(np.array)

  5. df.head()
现在数据已准备就绪,需要将问题转换为具有简单函数的嵌入。这很重要,因为嵌入搜索使用余弦距离比较数字向量(这是原始文本的转换)。这些向量可能相关,如果它们的余弦距离接近,则可能是问题的答案。OpenAI python 包有一个内置distances_from_embeddings函数,在这里很有用。
  1. def create_context(
  2. question, df, max_len=1800, size="ada"
  3. ):
  4. """
  5. Create a context for a question by finding the most similar context from the dataframe
  6. """

  7. # Get the embeddings for the question
  8. q_embeddings = openai.Embedding.create(input=question, engine='text-embedding-ada-002')['data'][0]['embedding']

  9. # Get the distances from the embeddings
  10. df['distances'] = distances_from_embeddings(q_embeddings, df['embeddings'].values, distance_metric='cosine')


  11. returns = []
  12. cur_len = 0

  13. # Sort by distance and add the text to the context until the context is too long
  14. for i, row in df.sort_values('distances', ascending=True).iterrows():

  15. # Add the length of the text to the current length
  16. cur_len += row['n_tokens'] + 4

  17. # If the context is too long, break
  18. if cur_len > max_len:
  19. break

  20. # Else add it to the text that is being returned
  21. returns.append(row["text"])

  22. # Return the context
  23. return "nn###nn".join(returns)
文本被分解成更小的标记集,因此按升序循环并继续添加文本是确保完整答案的关键步骤。如果返回的内容多于所需,也可以将 max_len 修改为更小的值。

上一步只检索了与问题语义相关的文本块,因此它们可能包含答案,但不能保证。通过返回前 5 个最有可能的结果,可以进一步增加找到答案的机会。

然后,回答提示将尝试从检索到的上下文中提取相关事实,以便形成连贯的答案。如果没有相关答案,提示将返回“我不知道”。

可以使用完成端点创建问题的真实答案text-davinci-003。
  1. def answer_question(
  2. df,
  3. model="text-davinci-003",
  4. question="Am I allowed to publish model outputs to Twitter, without a human review?",
  5. max_len=1800,
  6. size="ada",
  7. debug=False,
  8. max_tokens=150,
  9. stop_sequence=None
  10. ):
  11. """
  12. Answer a question based on the most similar context from the dataframe texts
  13. """
  14. context = create_context(
  15. question,
  16. df,
  17. max_len=max_len,
  18. size=size,
  19. )
  20. # If debug, print the raw model response
  21. if debug:
  22. print("Context:n" + context)
  23. print("nn")

  24. try:
  25. # Create a completions using the question and context
  26. response = openai.Completion.create(
  27. prompt=f"Answer the question based on the context below, and if the question can't be answered based on the context, say "I don't know"nnContext: {context}nn---nnQuestion: {question}nAnswer:",
  28. temperature=0,
  29. max_tokens=max_tokens,
  30. top_p=1,
  31. frequency_penalty=0,
  32. presence_penalty=0,
  33. stop=stop_sequence,
  34. model=model,
  35. )
  36. return response["choices"][0]["text"].strip()
  37. except Exception as e:
  38. print(e)
  39. return ""
完成了!一个工作的 Q/A 系统已经准备就绪,该系统具有从 OpenAI 网站嵌入的知识。可以进行一些快速测试以查看输出质量:

  1. answer_question(df, question="What day is it?", debug=False)

  2. answer_question(df, question="What is our newest embeddings model?")

  3. answer_question(df, question="What is ChatGPT?")
响应将类似于以下内容:
  1. "I don't know."

  2. 'The newest embeddings model is text-embedding-ada-002.'

  3. 'ChatGPT is a model trained to interact in a conversational way. It is able to answer followup questions, admit its mistakes, challenge incorrect premises, and reject inappropriate requests.'
如果系统无法回答预期的问题,则值得搜索原始文本文件以查看预期已知的信息是否最终被嵌入。最初完成的爬网过程被设置为跳过所提供的原始域之外的站点,因此如果有子域设置,它可能不知道这些信息。

目前,每次都传入数据框来回答问题。对于更多的生产工作流程,应该使用矢量数据库解决方案而不是将嵌入存储在 CSV 文件中,但当前的方法是原型制作的一个很好的选择。

微信图片_20230304233009

ARVO币(Arvo Finance)如何获得?
BIO币(BITONE)实时行情?
SXI币(SafeXI)是什么意思?
VDL币(Video Link)投资?

24小时热点

元宇宙被国家认可吗

答:不被国家认可。 万物之源——元宇宙,一直是深奥而神 ...

413682

itBit

拉人头的公司肯定是违法的

答:拉人头是指做员工招聘,企业通过给那些推荐优秀人才的人提供 ...

68508

Bitstamp交易所

不花钱挖矿传销骗局揭秘

传销骗局的挖矿“获利”模式被很多人看到,但大多数人都不了解这 ...

17015

红杉资本中国基金

玩比特币的都是什么人

比特币是一种去中心化、众包货币,近些年在社会中引起了强烈关注 ...

565194

火星财经
广告

热点专题

专门忽悠企业家的中国十大骗子大师

区块链网创立于2015年初,为国内第一批有资质的区块链媒体, ...

2749984

希壤

2023年打假总结:Pi Network项目的传销性质Pi币

曾经,号称“走路就能赚钱”拥有大量用户和广泛影响的国内APP ...

2424691

文昌链

国内460种传销币套路和骗子币名单大曝光

打着区块链旗号,以聚集性传销、网络传销为手段,以每枚3元的价 ...

2093130

Coin Metrics

原力元宇宙佛萨奇骗局

原力元宇宙佛萨奇骗局是一种不可思议的幻想计划,它由一群英勇可 ...

1856564

聚币网

诈骗披露:成功学周文强

又一个“成功学”大师周文强翻车 你想一夜暴富吗?你想年薪百 ...

1672583

Lazy Lions

中国十大骗局之pi network(π币、pi币、派币、兀币)

中国十大骗局之pi network(π币、pi币、派币、兀币 ...

1416851

Gemini 交易所

柴犬币SHIB来了

因其可爱形象和马斯克代言,近段时间最耀眼的加密货币非狗狗币莫 ...

1267517

Luart

链圈打诈:3000名解放军去缅甸真的假的?

3000名解放军去缅甸这个是假新闻,出兵这是个大问题,如果我 ...

1171859

a16z

非常硬核的LP流动性挖矿的核算

MDX不同挖矿方法的真实收益率 如何挖取高APY?2021年 ...

1156899

RMRK

什么是去中心化交易所(DEX)?

去中心化交易所是一个基于区块链的交易所,它不将用户资金和个人 ...

1144708

bitFlyer交易所