创新实训2024.04.11日志:self-instruct生成指令

1. 参考文献

  • 代码:https://github.com/yizhongw/self-instruct
  • 论文:https://arxiv.org/abs/2212.10560

2. 前沿论文阅读

2.1. self-instruct技术的优势

作者在文章中提到:

The recent NLP literature has witnessed a tremendous amount of activity in building models that can follow natural language instructions.These developments are powered by two key components: large pretrained language models (LM) and human-written instruction data.However, collecting such instruction data is costly and often suffers limited diversity given that most human generations tend to be popular NLP tasks, falling short of covering a true variety of tasks and different ways to describe them. Continuing to improve the quality and coverage of instruction-tuned models necessitates the development of alternative approaches for supervising the instruction tuning process.


简单来说,传统的NLP技术需要涉及两大不可或缺的步骤:大型的预训练好的大语言模型以及人工搜集的语料(指令数据)。而搜集这些语料非常费工费时。因此需要一个替代的方式来生成语料并监督这个过程。

换句话说,我们不用自己搜集成千上万条语料了,而是由上百条初始的语料生成一个大规模的语料库并用于训练。

2.2. Working Pipeline

截图来自于论文。

简单来说,我们把一个初始的种子指令库输入到任务池中,随后每次从任务池中挑选一定批量的指令作为询问大模型的输入,并将其分为两类:

  1. 第一类是分类问题,采用Output-first模式。也就是先打上一个分类的标签(Class Label),然后根据这个标签去生成问题。
  2. 第二类是非分类问题,采用Input-first模式。也就是先生成问题,然后再生成回答。

不过我们的语料库中,没有分类问题,因此统一采用了Input-first模式。

最后计算生成的指令与已有指令的相似度,如果过高,则应该舍弃这条新的指令。

3. 基于ChatGLM-4的self-instruct

智谱AI提供了国产开源大模型ChatGLM系列,我们打算基于这个系列的LM,为后续的训练生成需要的指令。

3.1. 智谱AI开发者注册

智谱AI开发平台:智谱AI开发平台

用一个没注册过的手机号就能注册进去。现在他们家搞活动,新用户注册还送token(请求ChatGLM回答的必要资源),500w个,在财务管理->资源包管理可以查看:

还有个比较重要的,是调用API时需要的key。可以在查看APIkey中看到:

如果需要调用智谱的API,可以查看文档:智谱AI开放平台 (bigmodel.cn)

3.2. 初始化Seed Instructions

self-instruct技术需要初始化一些种子指令并加入Task pool,用来迭代生成新的问题,这个技术可以大幅减少人工搜集语料的成本,但不代表一点人工搜集的过程都没有。一开始的初始语料还是需要我们自己搜集的。

在做前期准备工作的时候,团队就已经搜集了一批有关易学的问答对,根据step1:Instruction Generation,我们把这批问答对的问题作为instruction输入LM。

可能会疑惑的是,一个完整的task,包括了instruction,input和output。为什么这里可以直接用input替代instruction呢?关于这一点,这项技术的提出者在文章中写到:

Note that the instruction and instance input does not have a strict boundary in many cases. For example, “write an essay about school safety” can be a valid instruction that we expect models to respond to directly, while it can also be formulated as “write an essay about the following topic” as the instruction, and “school safety” as an instance input.


需要注意的是,指令和实例的输入(也即问题)在定义上很多时候并没有严格意义的边界。例如,”写一篇有关校园安全的短文“可以是一个合法的、我们期望LM直接回答的问题,但当他变成”写一篇有关下述主题的短文“,就成了一个指令,随后”校园安全“可以作为一个实例的输入(也即问题)。

 在无法严格区分instruction和input时,我们为了数据格式的多样性,可以直接拿input代替instruction。

作为初始种子指令的instruction如下(其实就是我上一篇日志中,通过易学百科全书提取出来的QA对):

这里不会把所有内容放出来,因为这是团队成员共同努力的成果,我没有理由把它随意共享到网络上。如果你有正当的理由查看这些语料文档,请通过合法渠道联系我。

3.3. 迭代生成新的指令

(1)连接智谱AI服务器

首先我们要连接上智谱AI的服务器:

    client = ZhipuAI(api_key="xxx")  # 填写你自己的APIKey

这里为了保护我自己的隐私,这个key我用xxx替换掉了,如果你想通过我的代码复现这个过程,请自己注册账号并填写key。

(2)打开种子指令与迭代指令文件

然后我们打开存储了种子指令的excel表格,以及迭代生成的指令的txt文件(一开始我们可以把种子指令直接放到这个txt文件中作为迭代生成的一部分)

        # 打开 Excel 文件
        workbook = load_workbook(origin_QA)
        # 获取工作表
        sheet = workbook.active
        # 遍历行和列
        input_list = []
        output_list = []
        for row in sheet.iter_rows(min_row=2, max_row=97, values_only=True):
            input_list.append(list(row)[0])
            output_list.append(list(row)[1])
        print(input_list)
        print(output_list)

        with open("./self_instruct/target/generation_instruction.txt", 'r', encoding="UTF-8") as file2:
            generation_instructions_list = file2.readlines()

(3)判断是否结束迭代

随后看一下迭代生成的指令是否达到5000条(5000条是用来训练大模型的语料数量的下限),如果没达到5000条,我们就开启本轮迭代更新,否则就退出程序:

        if len(generation_instructions_list) >= 5000:
            break

(4)挑选本轮迭代的语料

之后,根据step1:Instruction Generation,从种子指令中挑选5条,迭代生成的指令中挑选5条(可能会重复,但不用担心,最后我们会去重的)。原文中是28开,这里试了几个参数后,决定是55开比较好:

        # 随机从种子指令和生成指令中取5条
        seed_instructions = random.sample(input_list, 5)
        generation_instructions = random.sample(generation_instructions_list, 5)

(5)请求大模型

拿到这部分语料后,我们就可以向大模型提问了,让他基于这些指令,自己再生成一批指令。这里就要写一个需求明确、逻辑清晰的prompt来提示大模型:

        # 建立模版
        prompt = "你是一名精通周易的专家,下面是一些与周易相关的问题:\n"
        for index, instruction in enumerate(seed_instructions):
            prompt += f"{str(index + 1)}、问题:{instruction.strip()}\n"
        for index, instruction in enumerate(generation_instructions):
            prompt += f"{str(index + 6)}、问题:{instruction.strip()}\n"
        prompt += "请参考上面问题的内容和形式,对上述问题进行补充和拓展,再提出10个关于周易的比较深入的问题"
        print(prompt)

因为我们的语料是10条,所以这里问他也是问了10条。

之后就可以调用api,向chatGLM4提出请求了:

        # 向GLM4提问并返回答案
        response = client.chat.completions.create(
            model="glm-4",  # 填写需要调用的模型名称
            messages=[
                {"role": "user",
                 "content": prompt}
            ],
        )

(6)正则表达式提取响应的instruction

得到响应后,从返回的message中提取出问题:

        # 提取问题
        questions = extract_questions(response.choices[0].message.content)
        for i, question in enumerate(questions, start=1):
            print(f"{question}")

这里chatGLM响应的问题的格式和我们提问的格式是一样的,都是一个索引+.+问题:+问题内容。所以就可以用正则表达式去匹配文本,提取问题内容。总结一下这个模式串的格式,应该为:

  • 以一串数字开始(可能后面有空白字符)。
  • 接着可能有一个中文顿号或英文句号(也可能没有),并可能后面有空白字符。
  • 然后是“问题”这个词,并可能后面有空白字符。
  • 接着可能有一个中文冒号或英文冒号(也可能没有),并可能后面有空白字符。
  • 最后是任意数量的任意字符,直到字符串的结束。

因此正则表达式串为:

^\d+\s*[、.]?\s*问题\s*[::]?\s*(.*)$

 逻辑为:

  1. ^\d+

    • ^:表示匹配字符串的开始。
    • \d+:匹配一个或多个数字(\d 是数字的简写,+ 表示一个或多个)。
  2. \s*

    • \s:匹配任何空白字符,包括空格、制表符、换页符等。
    • *:表示前面的字符(在这里是 \s)可以出现零次或多次。
  3. [、.]?

    • [、.]:匹配中文顿号(、)或英文句号(.)。
    • ?:表示前面的字符(在这里是 [、.])是可选的,即可以出现零次或一次。
  4. \s*:再次匹配零个或多个空白字符。

  5. 问题

    • 匹配字面字符串“问题”。
  6. \s*:再次匹配零个或多个空白字符。

  7. [::]?

    • [::]:匹配中文冒号(:)或英文冒号(:)。
    • ?:表示前面的字符(在这里是 [::])是可选的,即可以出现零次或一次。
  8. \s*:再次匹配零个或多个空白字符。

  9. (.*)$

    • .*:匹配任意数量的任意字符(. 表示任意字符,* 表示零个或多个)。
    • $:表示匹配字符串的结束。
    • 括号 () 用于捕获匹配的子字符串。
# 将模型的回答转换成问题的列表
def extract_questions(text):
    # 使用正则表达式匹配以数字、空格、英文句号和冒号开头的行
    pattern = r'^\d+\s*[、.]?\s*问题\s*[::]?\s*(.*)$'
    questions = []

    # 遍历文本的每一行
    for line in text.split('\n'):
        # 使用正则表达式进行匹配
        match = re.match(pattern, line)
        if match:
            question = match.group(1).strip()  # 提取匹配的问题,并去除两端的空格
            questions.append(question)

    return questions

(7)判断生成的指令是否与先前的指令重复

新生成的指令可能和早先已经生成的指令相似(甚至重复),我们要把这样的指令剔除掉。jieba分词器组件提供了计算语料相似程度的API:

# 计算bleu
def calculate_bleu(reference, hypothesis):
    reference_tokens = list(jieba.cut(reference))
    hypothesis_tokens = list(jieba.cut(hypothesis))
    smoothing_function = SmoothingFunction().method7
    score = sentence_bleu([reference_tokens], hypothesis_tokens, smoothing_function=smoothing_function)
    return score

对于短文本(比如这里的一个指令,采取BLEU分数来评估相似度比较合适;对于长文本,使用ROUGE_L分数比较合适)

  • jieba.cut 用于对中文文本进行分词。
  • SmoothingFunction().method7 提供了一种平滑方法,用于处理BLEU分数计算中可能出现的零分问题。
  • sentence_bleu 计算BLEU分数,它接受一个参考句子的分词列表(在这里是一个列表的列表,因为我们只有一个参考句子),一个假设句子的分词列表,以及一个平滑函数。

如果相似度在0.7以下,我们认为可以作为新的指令纳入Task Pool:

        # 指令存储到文件
        for question in questions:
            max_rouge = 0
            for generation_instruction in generation_instructions_list:
                bleu = calculate_bleu(question, generation_instruction)
                if bleu > max_rouge:
                    max_rouge = bleu
            if max_rouge < 0.7:
                print(f"录入:{question}")
                file.write(question + '\n')

3.4. 运行过程

4. 开源与复现

https://github.com/Liyanhao1209/ZhouYiLLM.git

python_scripts分支。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/537228.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python基础——python包【创建和导入,下载第三方包】

&#x1f4dd;前言&#xff1a; 在上一篇文章python基础——模块中&#xff0c;我们讲解了有关python模块的相关知识&#xff0c;这篇文章我们进一步讲解用于储存多个模块文件的python包&#xff1a; 1&#xff0c;什么是python包 2&#xff0c;如何创建和导入python包 3&#…

linux安装dubboAdmin

1.环境准备&#xff1a; jdk-8u391-linux-x64apache-maven-3.9.6apache-tomcat-8.5.100 2.安装注册中心zookeeper zookeeper的安装看我的另一篇文章&#xff0c;安装完成后保持启动状态 linux安装Zookeeper的详细步骤-CSDN博客 3.安装dubboadmin 源码下载地址&#xff1a;R…

Python学习从0开始——项目一day01爬虫(二)

Python学习从0开始——项目一day01爬虫&#xff08;二&#xff09; 一、解析response数据二、json转换三、文件保存四、存储json对象五、完整代码 上一篇 一、解析response数据 在已经知道我们获取图片的最终URL存在于请求响应response中&#xff0c;下一步的重点就放在解析re…

SQLite数据库文件格式(十五)

返回&#xff1a;SQLite—系列文章目录 上一篇:SQLite 4.9的虚拟表机制(十四) 下一篇&#xff1a;SQLite超详细的编译时选项&#xff08;十六&#xff09; ► 目录 本文档描述和定义磁盘上的数据库文件 自 SQLite 以来所有版本使用的格式 版本 3.0.0 &#xff08;2004-06-18…

Java数据结构二叉树

概念 一棵二叉树是结点的一个有限集合&#xff0c;该集合&#xff1a; 1. 或者为空 2. 或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。 从上图可以看出&#xff1a; 1. 二叉树不存在度大于2的结点 2. 二叉树的子树有左右之分&#xff0c;次序不能颠倒&#x…

设计模式代码实战-抽象工厂模式

1、问题描述 小明家新开了两个工厂用来生产家具&#xff0c;一个生产现代风格的沙发和椅子&#xff0c;一个生产古典风格的沙发和椅子&#xff0c;现在工厂收到了一笔订单&#xff0c;请你帮他设计一个系统&#xff0c;描述订单需要生产家具的信息。 输入试例&#xff1a; 3 …

2024-04-10 作业

作业要求&#xff1a; 1> 思维导图 2> 作业1&#xff1a; 作业2&#xff1a; 运行代码&#xff1a; main.cpp #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QDebug> #include <QTimerEvent> #include <QTime> #include &l…

YOLOv8草莓生长状态(灰叶病缺钙需要肥料)检测系统(python开发,带有训练模型,可以重新训练,并有Pyqt5界面可视化)

本次检测系统&#xff0c;不仅可以检测图片、视频或摄像头当中出现的草莓叶子是否有灰叶病&#xff0c;还可以检测出草莓叶是否缺钙、是否需要施肥等状态。基于最新的YOLO-v8训练的草莓生长状态检测模型和完整的python代码以及草莓的训练数据&#xff0c;下载后即可运行&#x…

Linux内核errno-base.h源码分析

上次写过一个博客&#xff0c;主要关于内核错误相关的源码分析&#xff08;链接&#xff09;&#xff0c;最近突然发现上次的分析不完善&#xff0c;因此本次完善相关分析。 Linux内核中经常见到一些返回值&#xff0c;如-12&#xff0c;比如下面是我遇到过的一个截图&#xff…

代理模式:控制对象访问的智能方式

在面向对象的软件开发中&#xff0c;代理模式是一种结构型设计模式&#xff0c;它为其他对象提供一个代理或占位符以控制对这个对象的访问。代理模式在实现权限控制、延迟初始化和远程对象访问等方面非常有用。本文将详细介绍代理模式的定义、实现、应用场景以及优缺点&#xf…

制作一个OpenHarmony视频播放器

简介 媒体子系统是 OpenHarmony 中重要的子系统&#xff0c;可以提供音视频播放能力。媒体子系统为开发者提供一套简单且易于理解的接口&#xff0c;使得开发者能够方便接入系统并使用系统的媒体资源。媒体子系统提供以下常用功能&#xff1a; 音视频播放&#xff08;AVPlaye…

TestNG执行测试用例的方法

TestNG是一个非常好用d自动化测试框架&#xff0c;对于经常使用selenium做web端UI测试的童鞋来说是个不错的工具。 具备基本常识的测试童鞋们&#xff0c;可能需要知道存在即合理&#xff0c;存在即有用的道理。任何一个工具&#xff0c;或者一件事的存在如果令人得不到益处&am…

前端大屏项目适配方法

要在F11全屏模式下查看 方法一&#xff0c;rem font-size 动态设置HTML根字体大小 和 body 字体大小&#xff08;lib_flexible.js&#xff09; 将设计稿的宽&#xff08;1920&#xff09;平均分成 24 等份&#xff0c; 每一份为 80px。HTML字体大小就设置为 80 px&#xff…

C/C++ 配置 jemalloc 的一些选项,处理一些疑似内存泄漏的问题。

在 jemalloc 之中有三种配置 jemalloc 选项的一些方式。 1、修改选项代码默认值&#xff08;重新编译&#xff09; 2、修改环境变量 MALLOC_CONF&#xff0c;并重启应用程序 注意&#xff1a; 仅支持 opt. 节配置选项 export MALLOC_CONF"retain:true,dirty_decay_ms:2…

什么是图神经网络?

什么是图神经网络&#xff1f; GNN 将深度学习的预测能力应用于丰富的数据结构&#xff0c;这些数据结构将对象及其关系描述为图中由线连接的点。 当两种技术融合时&#xff0c;它们可以创造出新奇而美妙的东西——比如手机和浏览器融合在一起打造智能手机。 如今&#xff0…

初学网络编程

网络编程是指编写能够在网络环境中运行&#xff0c;进行数据通信的程序的过程。它涵盖了从建立网络连接、发送和接收数据&#xff0c;到关闭连接等一系列操作。网络编程是开发网络应用程序的基础&#xff0c;它使得不同的计算机和设备能够通过网络进行数据交换和通信。 三个核…

《手把手教你》系列基础篇(八十三)-java+ selenium自动化测试-框架设计基础-TestNG测试报告-下篇(详解教程)

宏哥微信粉丝群&#xff1a;https://bbs.csdn.net/topics/618423372 有兴趣的可以扫码加入 1.简介 其实前边好像简单的提到过测试报告&#xff0c;宏哥觉得这部分比较重要&#xff0c;就着重讲解和介绍一下。报告是任何测试执行中最重要的部分&#xff0c;因为它可以帮助用户了…

Flask快速搭建文件上传服务与接口

说明&#xff1a;仅供学习使用&#xff0c;请勿用于非法用途&#xff0c;若有侵权&#xff0c;请联系博主删除 作者&#xff1a;zhu6201976 一、需求背景 前端通过浏览器&#xff0c;访问后端服务器地址&#xff0c;将目标文件进行上传。 访问地址&#xff1a;http://127.0.0…

Java 中文官方教程 2022 版(三)

原文&#xff1a;docs.oracle.com/javase/tutorial/reallybigindex.html 对象 原文&#xff1a;docs.oracle.com/javase/tutorial/java/javaOO/objects.html 一个典型的 Java 程序会创建许多对象&#xff0c;正如您所知&#xff0c;这些对象通过调用方法进行交互。通过这些对象…

前端开发攻略---简化响应式设计:利用 SCSS 优雅管理媒体查询

1、演示 2、未优化前的代码 .header {width: 100px;height: 100px;background-color: red; } media (min-width: 320px) and (max-width: 480px) {.header {width: 10px;} } media (min-width: 320px) and (max-width: 480px) {.header {height: 20px;} } media (min-width: 48…