基于大语言模型的商业答疑系统

项目名称:  基于大语言模型的商业答疑系统

项目背景

随着人工智能和数字化技术在经济领域的广泛应用,经济领域对于高质量的检索和问答需求日益增长,而大模型的能力在这方面具有巨大的潜力。但普通的大模型无法回答很多商业问题,因为大模型并没有相关数据收集, 已进行测试大部分商业问题无法回答

项目流程

  1. 收集股票相关数据(开源),收集股民问题(论坛爬取),并进行数据预处理
  2. 对于股民问题,将其中不含有公司名称的问题(即潜在的使用SQL查询进行回答 的问题)使用tokenizer统计词频,两两计算余弦相似度后进行谱聚类,构造RAG-ICL样本库,编写各类问题的prompt以及校对SQL语句和回答
  3. 将系统制作成网页并可以进行面向股民的商业答疑

工作流

问题输入后分类

对于SQL查询问题,新问题使用Qwen大模型的tokenizer统计词频,与RAG-ICL样本库中的问题比较,选取最相似的2-4个问题-SQL语句对加入prompt,利用Qwen大模型做“填空与替换”,生成高可解释性与可靠性的SQL查询。运行查询,利用Qwen大模型将查询结果和问题生成为答案。

对于文本理解问题,新问题使用Qwen大模型的tokenizer统计词频,与分段的Text+表格文件计算总词频加权的余弦相似度,选出与问题最相关的20个文本片段,利用他们生成答案。

项目特点

  1. 大模型基础:
    • 本项目基于 Qwen大模型,这是一个强大的大语言模型。
  1. 数据集:
    • 本项目使用包括10张基金表数据和80篇招股书文档,数据较为丰富,涵盖了基金的基本信息、股票持仓明细、债券持仓明细、可转债持仓明细、日行情等。
  1. 以检索增强上下文学习(RAG-ICL)为核心,使得我们可以在仅标注较少数据集 (不到200条),不精调模型的情况下快速得到一个效果较好的商业答疑系统。

同时本方案具有较高的可解释性,对于可见的新问题类型也无需调整模型,只需补充RAG-ICL样本即可,可拓展性强

项目意义

本项目可以探索大预言模型在科技中的应用潜力。将大模型和经济领域结合,将计算机和领域交叉,挖掘新时代大模型的潜力。

大模型项目部署:从魔搭到Auto算力云

在我们的模型部署过程中,我们遇到了一些挑战,特别是由于魔搭平台的限制,最终选择了Auto算力云来完成项目。以下是我们从选择平台到成功部署模型的完整经历,并介绍了模型的基本使用和操作,包括tokenizer的详细使用,希望能为其他开发者提供一些参考和帮助。

遇到的问题

首先,我们在魔搭平台上进行了初步尝试。虽然魔搭提供了免费算力,但并不支持SSH远程控制,这使得在该平台上完成我们的项目变得极为困难。我们发现,所需的显存高于24GB,而魔搭提供的免费算力显存不足以满足这一需求。因此,我们必须寻找其他算力资源来完成我们的项目。

当我们看到阿里云GPU服务器一个月8000元的花费时,非常沮丧。于是,我们在网上搜索并在一些论坛和B站视频的推荐下,发现了Auto算力云平台。这个平台的最高费用不过每小时六块钱左右,对于学生定档来说,非常友好。于是我们决定使用Auto算力云的算力资源。

适应新平台

选择好平台后,我们需要花一些时间来适应这个陌生的平台。以往我们对Jupyter Notebook的刻板印象是,它只是一个不用自己配置环境的网页笔记本编译器。但在使用过程中,我们发现它可以非常快速和简单地试验代码。

此外,Auto算力云平台上数据盘和系统盘的分区也很明显。经过向客服询问后,我们决定将大约30GB的模型放在数据盘,将代码相关内容放在系统盘。

模型下载和部署

接下来就是下载模型。由于系统盘的内存资源有限,我们决定将模型下载到数据盘。然而,第一次下载过程中,我们不小心把模型下载到了系统盘里。删除后发现内存依然被占用,这让我们十分困惑。最后,我们决定清空实例,重新部署,最终解决了这一问题,并成功将模型部署在数据盘内。

具体的下载代码如下:

from modelscope.hub.snapshot_download import snapshot_download

model_dir = snapshot_download('TongyiFinance/Tongyi-Finance-14B-Chat', cache_dir='/root/autodl-tmp')

模型的基本使用和操作

下载和部署完成后,接下来是如何使用模型。以下是基本的使用步骤和示例代码:

加载模型

首先,我们需要加载已经下载好的模型。可以使用transformers库来加载模型:

from transformers import AutoModelForCausalLM, AutoTokenizer

model_name_or_path = '/root/autodl-tmp/TongyiFinance/Tongyi-Finance-14B-Chat'
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path)
model = AutoModelForCausalLM.from_pretrained(model_name_or_path)
生成文本

加载模型后,我们可以使用模型来生成文本。以下是一个简单的文本生成示例:

input_text = "今天天气真好,我们一起去"
inputs = tokenizer(input_text, return_tensors='pt')
output = model.generate(inputs['input_ids'], max_length=50, num_return_sequences=1)

generated_text = tokenizer.decode(output[0], skip_special_tokens=True)
print(generated_text)

Tokenizer的详细使用

tokenizer是处理文本输入的关键工具。它将文本转换为模型可以理解的格式,并在生成后将其转换回人类可读的文本。以下是tokenizer的一些详细使用方法:

基本用法
编码文本

将文本转换为模型输入的张量:

inputs = tokenizer("你好,世界!", return_tensors="pt")
print(inputs)

输出将是一个包含输入ID的字典,适用于PyTorch模型。

解码文本

将模型输出的ID转换回文本:

output_ids = [101, 872, 3221, 100, 9480, 102]
decoded_text = tokenizer.decode(output_ids, skip_special_tokens=True)
print(decoded_text)
处理批量数据

tokenizer还可以处理批量数据,这对于处理大量文本数据非常有用:

batch_texts = ["你好,世界!", "模型部署很有趣。"]
batch_inputs = tokenizer(batch_texts, padding=True, truncation=True, return_tensors="pt")
print(batch_inputs)
特殊Token和配置

tokenizer配置了许多特殊token,如填充token、分隔token等,可以根据需要进行定制:

print(tokenizer.cls_token)
print(tokenizer.sep_token)
print(tokenizer.pad_token)

你也可以更改这些特殊token:

tokenizer.pad_token = "[PAD]"
print(tokenizer.pad_token)
高级功能
添加新词汇

在处理特定领域的文本时,可能需要添加新的词汇:

new_tokens = ["新词1", "新词2"]
tokenizer.add_tokens(new_tokens)
model.resize_token_embeddings(len(tokenizer))
保存和加载自定义tokenizer

自定义的tokenizer可以保存并在之后加载:

tokenizer.save_pretrained("./my_custom_tokenizer")
tokenizer = AutoTokenizer.from_pretrained("./my_custom_tokenizer")

总结

这是我们在大模型项目中遇到的部署问题及解决过程,以及详细的模型使用和操作方法,包括如何使用tokenizer。希望这些经验能对你有所帮助:

  1. 选择合适的算力资源:在选择算力资源时,考虑到显存需求和费用问题,选择最合适的平台。
  2. 适应新平台:在新平台上操作时,多向客服咨询,合理使用数据盘和系统盘。
  3. 正确下载和部署模型:注意下载路径,避免内存占用问题。
  4. 模型使用和微调:了解如何加载、生成文本、微调和评估模型。
  5. Tokenizer的使用:掌握文本编码、解码、处理批量数据和自定义配置等高级功能。

通过这次经历,我们学到了很多关于算力平台使用和模型部署的知识。如果你也在进行类似的项目,希望这些经验能对你有所帮助。

question数据处理模块

1. 简介

本报告旨在介绍数据处理模块的功能、实现细节以及使用方法。该模块用于从JSON文件中读取问题数据,对问题进行处理,并将处理后的数据写入CSV文件。

2. 功能

数据处理模块主要完成以下功能:

- 从JSON文件中读取问题数据。
- 对问题进行处理,去除空格等不必要的字符。
- 将处理后的问题数据写入CSV文件。

3. 实现细节

下面是数据处理模块的具体实现细节:

- 使用`jsonlines`库打开JSON文件,并逐行读取JSON对象。
- 定义`read_jsonl`函数,该函数接受文件路径作为参数,并返回包含所有JSON对象的列表。
- 使用`csv`库创建CSV文件,并定义CSV writer对象。
- 遍历前1000个问题,提取问题内容并去除空格,然后将问题ID和处理后的问题内容写入CSV文件。
- 关闭CSV文件流。

4. 使用方法

数据处理模块的使用方法如下:

- 调用`read_jsonl`函数并传入JSON文件路径,将返回包含问题数据的列表。
- 调用`write_jsonl`函数并传入CSV文件路径和处理后的问题数据,将数据写入CSV文件。

5. 示例

cont = read_jsonl('data/question.json')  # 读取问题数据
g = open('processed_data/question_csv.csv', 'w', newline='', encoding ='utf-8-sig')  # 创建CSV文件
csvwriter = csv.writer(g)
csvwriter.writerow(['问题id','问题'])
for cyc in range(1000):
    temp_question = cont[cyc]['question']
    temp_question = temp_question .replace(' ','')
    csvwriter.writerow([str(cont[cyc]['id']),cont[cyc]['question']])
g.close()

6. 结论

数据处理模块实现了从JSON文件中读取问题数据,对问题进行处理,并将处理后的数据写入CSV文件的功能。该模块可以为后续的数据分析和建模提供清洗后的数据源。

问题分类模块

1. 简介

该报告旨在介绍问题分类模块的功能、实现细节和使用方法。该模块用于根据问题的内容,将其分类为属于公司招股说明书还是基金股票数据库。

2. 功能

问题分类模块主要完成以下功能:

- 使用预训练的语言模型判断问题的答案可能在公司的招股说明书中还是在基金股票数据库中。
- 根据问题的内容和模型生成的响应,将问题分类为两种类型之一。

3. 实现细节

以下是问题分类模块的主要实现细节:

- 导入所需的库,包括csv、pandas、numpy、re以及来自modelscope库的AutoModelForCausalLM、AutoTokenizer和GenerationConfig。
- 定义预训练语言模型的路径和加载模型。
- 读取处理后的问题数据和公司名称数据。
- 定义问题分类的提示信息,包括示例问题和预期分类。
- 对每个问题进行循环处理:
    - 生成带有问题内容的提示信息。
    - 使用语言模型预测问题的分类。
    - 根据生成的响应确定问题的分类,如果在响应中提到了“招股说明书”而没有提到“股票数据库”,则分类为“Text”,反之则分类为“SQL”。
    - 将问题ID、问题内容、模型生成的响应和分类写入CSV文件。
- 关闭CSV文件流。

4. 使用方法

问题分类模块的使用方法如下:

- 确保已处理过的问题数据和公司名称数据已准备就绪。
- 将处理后的问题数据路径和公司名称数据路径传递给相应的变量。
- 运行代码以进行问题分类。
- 分类结果将写入CSV文件,可以在后续的分析中使用。

5. 示例

# 代码示例,处理问题并进行分类
cont = read_jsonl('processed_data/question_csv.csv')
g = open('/A01_question_classify.csv', 'w', newline='', encoding='utf-8-sig')
csvwriter = csv.writer(g)
csvwriter.writerow(['问题id', '问题', '答案', '分类'])
prompt = """
    你是一个问题分类器。对于每个提供给你的问题,你需要猜测答案是在该公司的招股说明书中还是在基金股票数据库里。以下是一些例子:
    ...
    """

for cyc in range(len(new_question_file)):
    temp_question = new_question_file[cyc:cyc+1]['问题'][cyc]
    prompt1 = prompt + temp_question + """?"""
    response_new, history_new = model.chat(tokenizer, prompt1, history=None)

    # 判断分类
    if '招股说明书' in response_new and '股票数据库' not in response_new:
        temp_class = 'Text'
    elif '招股说明书' not in response_new and '股票数据库' in response_new:
        temp_class = 'SQL'
        for company_name in company_list:
            if company_name in temp_question:
                temp_class = 'Text'
    else:
        temp_class = 'SQL'
        for company_name in company_list:
            if company_name in temp_question:
                temp_class = 'Text'

    csvwriter.writerow([str(new_question_file[cyc:(cyc+1)]['问题id'][cyc]),
                        str(new_question_file[cyc:(cyc+1)]['问题'][cyc]),
                        response_new, temp_class])
g.close()

6. 结论

问题分类模块实现了根据问题内容的分类功能,将问题分类为公司的招股说明书还是基金股票数据库。该模块为进一步的问题解答和数据分析提供了有效的分类工具。

7. 心得体会和感悟

- 数据预处理是关键:在处理问题数据之前,需要进行适当的数据预处理,包括去除空格、标点符号等无意义字符,以及对文本内容进行规范化处理,以确保数据的质量和一致性。

- 模型选择和调参至关重要:选择合适的语言模型对于问题分类的准确性至关重要。在本项目中,我选择了预训练的语言生成模型,并对其进行了调参以提高分类的准确性和稳定性。

- 实时监测和改进模型效果:在模型部署后,需要实时监测模型的效果,并根据实际情况进行调整和改进。通过持续的性能监控和反馈循环,可以不断优化模型的性能和准确性。

在这个部分中,prompt的设计是关键的,因为它直接影响了语言模型对问题的分类结果。下面详细介绍prompt的设计:

  1. 问题示例和分类提示

    在prompt中包含了一些典型的问题示例,以及对应的预期分类结果。这些示例问题覆盖了项目中可能遇到的各种情况,包括问题涉及的内容、需要回答的信息以及预期的分类结果。示例问题的设计应该尽可能地全面和具体,以确保语言模型能够准确理解问题的意图。

  2. 问题插值

    prompt中使用了变量temp_question来代表当前要分类的具体问题。这样做的目的是为了将每个具体的问题与示例问题结合起来,形成一个完整的问题描述。通过插值,语言模型可以更好地理解当前要分类的问题,从而做出更准确的分类预测。

  3. 问句结尾

    每个问题的末尾都包含了一个问号,这是为了确保prompt中的文本能够被正确识别为一个完整的问题。问号的存在可以帮助语言模型更好地理解文本的语义和意图,从而提高分类的准确性。

  4. 分类提示说明

    在prompt的最后,对每个示例问题都提供了一个预期的分类结果。这些分类结果说明了问题的答案可能在公司的招股说明书中还是基金股票数据库中。这样做的目的是为了提供给语言模型一个明确的分类标准,从而帮助它更好地理解问题的分类要求。

通过以上设计,prompt不仅为语言模型提供了问题分类的上下文信息,还为模型提供了明确的分类标准,从而帮助模型更准确地对问题进行分类。这种设计可以提高模型的分类准确性和稳定性,为项目的顺利实施提供了重要的支持。

​​​​​​​​​​​​​​​​​​​​​

问题转化部分

框架

设置模型目录和实例化一个自动tokenizer对象
读取两个CSV文件:A01_question_classify.csv 和 AF0_pdf_to_company.csv
循环遍历 AF0_pdf_to_company.csv 中的公司名称和文件名,并使用tokenizer处理它们,以便后续用于问题和公司名称之间的相似度比较。
打开一个新的CSV文件 A02_question_classify_entity.csv,准备写入分类后的问题、对应的实体和文件名。
遍历 A01_question_classify.csv 中的每个问题,分析分类,并确定对应的实体和文件名。
对于分类为 “Text” 的问题,通过计算问题与每个公司名称之间的相似度,找到最相似的公司,将其作为实体,并将其对应的文件名写入CSV文件。
对于分类为 “SQL” 的问题,不做处理,直接将其写入CSV文件。
对于其他分类的问题,尝试从问题中找到是否包含公司名称,如果找到则将其作为实体,并将对应的文件名写入CSV文件,否则将实体和文件名置为 “N_A”。
关闭CSV文件,输出完成信息,并退出程序。

实现的功能

功能是根据输入的问题,将其分类并确定其对应的实体和CSV文件名,并将结果写入一个CSV文件中。

生成SQL部分

框架

定义变量和列表

table_name_list = ['基金基本信息', ...]  # 表名列表
table_info_dict = {}  # 存储表名和对应的信息
n = 5  # 示例问题数量
deny_list = [...]  # 拒绝词列表

连接数据库

db2 = SQLDatabase.from_uri("sqlite:root/llm/data/dataset/股票数据.db", sample_rows_in_table_info=2)
dbd2 = db2.table_info

遍历表信息,提取目标表信息

for piece in list1:
    for word in table_name_list:
        if word in piece:
            table_info_dict[word] = piece

读取问题文件

question_csv_file_dir = "/root/llm/processed_data/A01_question_classify.csv"
question_csv_file = pd.read_csv(question_csv_file_dir,delimiter = ",",header = 0)

加载预训练的语言模型

   model_dir = '/root/auto_tmp/models/Tongyi-Finance-14B-Chat'
   tokenizer = AutoTokenizer.from_pretrained(model_dir, trust_remote_code=True)
   model = AutoModelForCausalLM.from_pretrained(model_dir, device_map="cuda:0", trust_remote_code=True, bf16=True).eval()    
   model.generation_config = GenerationConfig.from_pretrained(model_dir,trust_remote_code=True,temperature = 0.0001,top_p = 1,do_sample = False,seed = 1234)

构建拒绝词的token列表

   deny_token_list = list()
   for word in deny_list:
       temp_tokens = tokenizer(word)
       temp_tokens = temp_tokens['input_ids']
       deny_token_list = deny_token_list + temp_tokens

定义生成提示信息函数

   def get_prompt_v33(question, index_list):
       ...
       return impt2

读取SQL示例文件

SQL_examples_file_dir = "/root/llm/files/ICL_EXP.csv"
SQL_examples_file = pd.read_csv(SQL_examples_file_dir,delimiter = ",",header = 0)

准备写入问题文件的CSV文件

g = open('/root/llm/processed_data/question_SQL_V6.csv', 'w', newline='', encoding = 'utf-8-sig') 
csvwriter = csv.writer(g)
csvwriter.writerow(['问题id','问题','SQL语句','prompt'])

循环处理问题,并生成SQL代码

for cyc in range(len(question_csv_file)):
    ...
  • 对于每个问题,首先判断是否是SQL类型的问题。
  • 计算问题与示例问题的相似度。
  • 根据相似度找到示例问题作为提示信息。
  • 使用语言模型生成SQL代码,并写入CSV文件。

功能

这段代码的功能是根据给定的问题生成相应的SQL查询代码,并结合预训练的语言模型和示例问题进行辅助提示,以帮助用户编写正确的SQL代码。

SQL执行部分

框架

这段代码的功能是执行SQL查询并将执行结果写入CSV文件中。我们逐步解释每个部分的功能和实现细节:

启用faulthandler

   faulthandler.enable()

定义变量

   term_list_1 = ['基金股票持仓明细', '基金债券持仓明细', '基金可转债持仓明细']  # 用于检查SQL语句中是否包含特定表名的列表

连接数据库

   conn = sqlite3.connect('/root/llm/data/股票数据.db')
   cs = conn.cursor()

读取问题文件

   new_question_file_dir = '/root/llm/processed_data/question_SQL_V6.csv'
   new_question_file = pd.read_csv(new_question_file_dir, delimiter=",", header=0)

准备写入执行结果的CSV文件

   g = open('/root/llm/processed_data/question_SQL_V6_exed.csv', 'w', newline='', encoding='utf-8-sig')
   csvwriter = csv.writer(g)
   csvwriter.writerow(['问题id', '问题', 'SQL语句', '能否成功执行', '执行结果', 'List'])

循环执行SQL查询

   for cyc in range(1000):
    if cyc % 50 == 0:
        print(cyc)
    SQL_list = list()
    SQL_exe_flag = 0
    Use_similar_table_flag = 0
    SQL_exe_result = 'N_A'
    temp_sql = new_question_file[cyc:cyc+1]['SQL语句'][cyc]
    temp_sql = temp_sql.replace("B股票日行情表",'A股票日行情表')   
    temp_sql = temp_sql.replace("创业板日行情表",'A股票日行情表')   
    if " 股票日行情表" in temp_sql:
        temp_sql = temp_sql.replace(" 股票日行情表",' A股票日行情表')  
    if " 港股日行情表" in temp_sql:
        temp_sql = temp_sql.replace(" 港股日行情表",' 港股票日行情表')  
    temp_sql = temp_sql.replace("”",'').replace("“",'')                                    
    
    origin_success_flag = 0
    try:
        cs.execute(temp_sql)
        cols = cs.fetchall()
        SQL_exe_result = str(cols)
        origin_success_flag = 1
    except:
        for item in term_list_1:
            if item in temp_sql:
                Use_similar_table_flag = 1
                original_item = item
                break
        if Use_similar_table_flag == 1:
            for item in temp_sql:
                try:
                    cs.execute(temp_sql.replace(original_item,item))
                    cols = cs.fetchall()
                    SQL_exe_result = str(cols)
                    SQL_exe_flag = 2
                    break
                except:
                    pass

    csvwriter.writerow([str(new_question_file[cyc:(cyc+1)]['问题id'][cyc]),
                    str(new_question_file[cyc:(cyc+1)]['问题'][cyc]),
                    temp_sql,
                    origin_success_flag,
                    SQL_exe_result])
  • 对于每个问题,首先提取对应的SQL语句。
  • 对SQL语句进行一系列替换操作,将表名进行统一。
  • 尝试执行SQL语句,如果执行成功则记录执行结果,否则进行一些修正并重新尝试执行。
  • 将问题ID、问题、SQL语句、执行结果等信息写入CSV文件。

功能

在循环中,针对每个问题,首先替换了一些表名的别名,然后尝试执行SQL语句。如果执行成功,将结果写入CSV文件。如果执行失败,会尝试使用一组备选的表名进行执行,如果有任何一次执行成功,也会将结果写入CSV文件。如果所有尝试都失败,会将执行结果标记为失败。实际上,这段代码是一个简单的SQL执行器,用于执行给定的SQL语句,并将执行结果写入CSV文件中。

生成文本答案部分

框架

设置参数

   n = 4  # 控制示例问题数量
   deny_list = [ ... ]  # 拒绝词列表
   pattern1 = r'\d{8}'  # 正则表达式模式,用于匹配日期格式

读取数据文件

   data_file_dir = '/root/llm/processed_data/question_SQL_V6_exed.csv'
   data_file = pd.read_csv(data_file_dir, delimiter=",", header=0)

   data_file2_dir = 'root/llm/processed_data/A01_question_classify.csv'
   data_file2 = pd.read_csv(data_file2_dir, delimiter=",", header=0)

加载语言模型

   from modelscope import AutoModelForCausalLM, AutoTokenizer, snapshot_download
   from modelscope import GenerationConfig

   model_dir = '/root/auto_tmp/models/Tongyi-Finance-14B-Chat'
   tokenizer = AutoTokenizer.from_pretrained(model_dir, trust_remote_code=True)
   model = AutoModelForCausalLM.from_pretrained(model_dir, device_map="cuda:0", trust_remote_code=True, bf16=True).eval()

   model.generation_config = GenerationConfig.from_pretrained(model_dir,
                                                              trust_remote_code=True,
                                                              temperature=0.00001,
                                                              top_p=1,
                                                              do_sample=False,
                                                              seed=1234)

生成提示信息函数

   def get_prompt_v33(question, data, index_list):
       ...
       return impt2

读取示例数据文件

   SQL_examples_file_dir = "/root/llm/files/ICL_EXP.csv"
   SQL_examples_file = pd.read_csv(SQL_examples_file_dir, delimiter=",", header=0)

   example_question_list = []
   example_data_list = []
   example_FA_list = []
   example_token_list = []

写入结果的CSV文件准备

   g = open('/root/llm/processed_data/FA_V5_SQL.csv', 'w', newline='', encoding='utf-8-sig')
   csvwriter = csv.writer(g)
   csvwriter.writerow(['问题id', '问题', 'FA', 'SQL结果'])

循环处理问题

   for cyc in range(1000):
       ...
  • 对于每个问题,首先根据分类判断是否需要生成答案。
  • 如果SQL执行结果存在且成功,则尝试生成答案。否则,将SQL执行结果标记为未成功执行。
  • 使用模型生成答案,并将结果写入CSV文件中。

功能

这段代码利用预训练的语言模型,根据给定的问题和执行结果,生成相应的文本答案。通过分析问题的分类以及SQL执行的结果,决定是否需要生成答案。生成的答案会被写入到CSV文件中,以供后续使用。

为什么使用RAG

在给出的代码中,使用了RAG模型的主要目的是为了生成SQL查询的自然语言描述。让我们更详细地分析一下为什么选择使用RAG模型:

  1. 自然语言生成:代码的主要任务是执行SQL查询,并将结果以自然语言的形式描述出来。这种任务需要将结构化的数据库查询结果转换为易于理解的自然语言描述。RAG模型以其在自然语言生成任务上的优秀表现而闻名,因此选择使用它来生成描述性的文本是合理的选择。
  2. 语境理解:RAG模型能够理解问题的语境,并根据问题的内容生成相应的文本。在代码中,根据给定的问题和SQL查询,RAG模型可以考虑问题的语境以及查询结果的含义,生成与之相匹配的自然语言描述。
  3. 模型预训练:RAG模型基于大规模语料进行预训练,拥有丰富的语言知识和语境理解能力。这意味着模型可以更好地理解问题,并生成更加准确和连贯的描述性文本。
  4. 模型的端到端处理流程:RAG模型提供了端到端的处理流程,能够直接从问题和SQL查询开始,一直到生成自然语言描述,简化了整个处理流程,提高了效率和性能。
  5. 适应性:RAG模型具有很强的适应性,可以应对各种不同类型的问题和查询,并生成相应的描述性文本。这使得模型在处理不同领域和数据集时都能取得良好的效果。

使用RAG模型来生成SQL查询结果的自然语言描述是不错的选择。它能够理解问题的语境,生成连贯准确的文本,并且能够适应不同的查询和问题类型。

prompt

文本-> SQL: 通常LLMs对Text2SQL的表现不佳。从创建虚构的表和字段到用户查询中的拼写错误等原因都可能导致LLMs执行失败。鉴于这种数据库相当普遍,因此出现了很多帮助LLM准确创建SQL查询的方式,但都与特定的知识库相关。对于一个新的数据库,只能通过构建、迭代、修复来优化LLMs的表现。

Create Table + Select 3:在了解更多高级技术之前,这是获得模型在数据库上的表现基线的最直接的方法。在Prompt设计中,对于每张表,可以包括一个CREATE TABLE描述,并在一个SELECT语句中提供三个示例行。
Create Table + Select 3​编辑
使用Prompt的目的是为了指导模型生成特定类型的输出。
在这种情况下,我们想要评估模型在数据库操作方面的能力,特别是在执行CREATE TABLE和SELECT操作时。通过设计一个包含CREATE TABLE描述和SELECT语句的Prompt,我们可以引导模型生成与这些操作相关的SQL代码。
这样做有助于模型理解用户提出的任务,并在这个上下文中生成适当的响应。通过给出示例表格描述和SELECT语句,Prompt还可以帮助模型更好地理解数据表的结构和内容,从而生成更准确和有用的SQL代码。

RAG和微调的区别
​​​​​​​

研究显示,RAG 特别适合于融合新知识,而微调则能够通过优化模型内部知识、输出格式以及提升复杂指令的执行能力,来增强模型的性能和效率。

RAG在与其他模型优化方法相比​​​​​​​编辑

相对于 Prompt Engineering 和 Fine-tuning 方法,RAG 模型有一些独特的特性,比如:

  1. 全局信息融合
    • Prompt Engineering 通常依赖于手动设计的提示语来引导模型生成期望的输出。这种方法需要设计者具有领域知识和创造力,以确保提示语能够准确地引导模型进行任务。然而,Prompt 的设计可能会受到限制,难以捕捉到全局语境和信息。
    • Fine-tuning 则是在预训练模型的基础上,通过在特定任务数据上进行微调来调整模型参数。这种方法通常需要大量标记数据,并且容易出现过拟合的问题。
    • 相比之下,RAG 模型通过在三个步骤中综合利用检索、阅读和生成的信息,可以更全面地获取和理解输入文本的语义和语境,从而在生成任务中更准确地提供结果。
  2. 多模态信息利用
    • Prompt Engineering 和 Fine-tuning 主要依赖于单一模型的信息处理。Prompt Engineering 通过设计提示语来引导模型的生成,而 Fine-tuning 则是在一个模型上微调参数以适应特定任务。
    • RAG 模型利用了检索、阅读和生成三个步骤,从多个模态的信息中获取和理解输入文本,这使得模型在生成任务中能够更好地综合和利用不同类型的信息。
  3. 灵活性和可扩展性
    • Prompt Engineering 和 Fine-tuning 方法在设计和调整提示语或微调任务模型时可能会受到限制,需要大量的人工设计和标注数据。
    • RAG 模型具有更强的灵活性和可扩展性,可以与不同的检索模型、阅读理解模型和生成模型相结合,以适应不同的任务和数据。这种模块化的设计使得模型更易于调整和优化,同时也更易于应用于不同领域的应用场景。

ICL—EXP.csv(RAG-ICL的样本库)的来源

将1000个Questions中不含有公司名称的问题(即潜在的使用SQL查询进行回答 的问题),使用tokenizer统计词频,两两计算余弦相似度后进行谱聚类,类别个数75,可以看到每 类问题基本具有相同格式。从每类问题中随机选择2个,构成RAG-ICL的样本库,编写并校对 SQL语句和回答。

我们需要通过计算谱聚类来确定合适的类数量,使得样本库覆盖所有种类的问题。首先,我们可以使用一种方法来自动确定最佳聚类数。这里,我们可以使用“肘部法”或“轮廓系数”来找到最佳的聚类数。

以下是代码的更新版本,通过肘部法来自动确定最佳聚类数,并进行谱聚类:

import pandas as pd
import numpy as np
from modelscope import AutoTokenizer
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.cluster import SpectralClustering
from sklearn.metrics import silhouette_score
import random

读取CSV文件

csv_file = "questions.csv"
questions_df = pd.read_csv(csv_file)

过滤出分类结果为SQL的问题

sql_questions_df = questions_df[questions_df['分类'] == 'SQL']

初始化模型

model_dir = "path_to_your_model_directory"
tokenizer = AutoTokenizer.from_pretrained(model_dir, trust_remote_code=True)

统计词频

def tokenize_and_count(text):
    tokens = tokenizer.tokenize(text)
    return dict((token, tokens.count(token)) for token in set(tokens))

计算词频向量

def vectorize(token_counts, vocabulary):
    vector = [token_counts.get(token, 0) for token in vocabulary]
    return vector

获取所有问题的词频

token_counts_list = sql_questions_df['问题'].apply(tokenize_and_count).tolist()

获取所有唯一的词汇

all_tokens = set(token for token_counts in token_counts_list for token in token_counts)

构建词汇表

vocabulary = list(all_tokens)
```python

将词频转换为向量
```python
vectors = np.array([vectorize(token_counts, vocabulary) for token_counts in token_counts_list])

计算余弦相似度

similarity_matrix = cosine_similarity(vectors)

自动确定最佳聚类数

def find_best_cluster_number(similarity_matrix, max_clusters=10):
    silhouette_scores = []
    for n_clusters in range(2, max_clusters + 1):
        clustering = SpectralClustering(n_clusters=n_clusters, affinity='precomputed', assign_labels='kmeans')
        cluster_labels = clustering.fit_predict(similarity_matrix)
        silhouette_avg = silhouette_score(similarity_matrix, cluster_labels, metric="precomputed")
        silhouette_scores.append((n_clusters, silhouette_avg))
    
    best_n_clusters = max(silhouette_scores, key=lambda x: x[1])[0]
    return best_n_clusters

找到最佳聚类数

best_num_clusters = find_best_cluster_number(similarity_matrix, max_clusters=20)
print(f"最佳聚类数: {best_num_clusters}")

谱聚类

clustering = SpectralClustering(n_clusters=best_num_clusters, affinity='precomputed', assign_labels='kmeans')
labels = clustering.fit_predict(similarity_matrix)

将聚类结果加入数据框

sql_questions_df['Cluster'] = labels

从每类中随机选择2个问题,构建样本库

sampled_questions = []
for cluster in range(best_num_clusters):
    cluster_questions = sql_questions_df[sql_questions_df['Cluster'] == cluster]
    if len(cluster_questions) > 2:
        sampled_questions.extend(cluster_questions.sample(n=2).to_dict('records'))
    else:
        sampled_questions.extend(cluster_questions.to_dict('records'))

输出样本库

for sample in sampled_questions:
    print(f"问题id: {sample['问题id']}, 问题: {sample['问题']}")

将样本库保存为CSV文件

sampled_df = pd.DataFrame(sampled_questions)
sampled_df.to_csv("sampled_questions.csv", index=False)

在这段代码中,我们引入了 find_best_cluster_number 函数,它通过计算不同聚类数量的轮廓系数来确定最佳聚类数。然后,我们使用这个最佳聚类数进行谱聚类,并从每个类中随机选择两个问题,构建样本库。

TF-IDF余弦相似度理论

在处理文本时,由于众多无意义的词语(如“的”、“是”、“有”等词语)在文章中广泛出现,词频高但对于文章的重要性并不高,因此采用TF-IDF算法。词频(TF)的计算公式为:某个词出现次数/文章总词数,逆文本频率(IDF)的计算公式为:log(语料库中的文档总数/(包含该词汇的文档数+1)),最后将TF*IDF。这样计算的话,即便一个词的词频再高,当它出现在大量文档中时,其TF-IDF也不会很高,这样方便我们检索对于该文章相对更关键的词语。



​​​​​​​

计算文本间相似度采用的方法是计算余弦相似度。具体计算方法是将不同的关键词看作是不同的维度,将关键词对应的词频看作是该维度的值,只计算问题和文本中出现过的关键词,最后通过余弦相似度公式:两向量的点乘积除以两向量模的乘积。以此方法计算问题与所有文本的余弦相似度,其值越高说明问题与文本越相似。

词频字典的处理与余弦相似度的计算

为了计算余弦相似度,我们需要先得到所有目标文档的词频。因此,我们需要使用Tongyi-Finance-14B-Chat模型提供的分词器,并遍历所有txt文件。在处理每个txt文件时,我们需要统计所有关键词出现的次数,并使用分词器将关键词转化为token ID,用字典来保存所有token ID及其出现的次数。最后将txt文件名和token ID字典保存到csv文件中,方便后续使用。遍历完所有txt文件后,我们就得到了所有的文件所对应的词频字典,并保存到本地的csv文件中。

在计算问题文本与文档文本的余弦相似度时,我们采用加权的TF-IDF余弦相似度计算,具体公式如下。

在这里插入图片描述

在计算余弦相似度时,我们需要在计算词频时,将原文本文档的标准化字典作为分母。这样我们可以将出现数量多但对原文本处理没有重要意义的词的权重降低,而将对文章更为关键的词的权重提升,以此法得到的余弦相似度和文本相似度的关联更密切。

问题文本的处理

在处理问题文本时,我们首先获取问题及其对应实体,如果对应实体(问题中提及的公司名)为空,则不进行处理;否则就获取其对应的csv文件和txt文件。然后我们将问题文本中的空格和对应实体去掉,防止其影响文本相似度的计算,再将一些对文本相似度计算没有价值的停用词和标点符号换为空格。这步操作可以减少噪音,使分析聚焦于更有意义的文本上。这样我们按空格进行分割,就可以得到了一个包含文本词汇的list列表。

得到词汇列表后,我们遍历该列表,并使用Tongyi-Finance-14B-Chat的分词器,将分词后的结果转化为字典,并获取每个单词转化后的token ID序列。最后我们遍历该token ID序列,合并所有token到一个list列表中,并创建问题词频计数器来计算统计文本中token出现的次数。

执行完上述操作后,我们遍历该问题对应的csv文件,获取其纯文本,用空格代替纯文本中的标点符号,然后用分词器处理该纯文本,获取token ID序列,并储存在list列表中。然后再用词频计数器统计文档片段中的token出现次数。最后我们计算该文档片段与问题的文本余弦相似度,将其结果储存到list列表中。

遍历完所有csv文件后,我们记录前n个余弦相似度最大的值及其对应的索引值。然后我们再创建一个list列表,将前n个余弦相似度最大的值所对应的纯文本保存在该list列表中。

最后,我们在本地保存一个csv文件,保存其问题、问题id、对应实体、csv文件名,这几项与原来的文件一致,另外还需要保存前n个余弦相似度最大的索引值、前n个余弦相似度最大的余弦相似度值、前n个余弦相似度最大的csv文件的纯文本。

只有txt文本和加权的余弦相似度并不一定能够得到答案,因此我们还需要处理表格内容。

具体处理与前面的内容相似,但是在获取文档文本时,我们不仅需要获取纯文本,还需要加入表格内容一起处理。后续的减少噪音和文档分割与前面均相似。在求文本间的余弦相似度时,我们需要使用标准的TF-IDF来求解,与前文的方法不同,目的是为了获取不同的文档片段,当上面的文本无法经过处理获取答案后,我们可以再用该方法获得的文本进行处理。处理结束后,也将前n个余弦相似度最大的文本片段保存到本地的csv文件中。

这样,我们就得到了两个csv文件,在搜索答案时共同处理,以得到更加丰富的答案。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值