【self-instruct方式生成语料代码实战】

news2024/11/13 10:21:26

self-instruct方式生成语料代码实战

    • self-instruct 介绍
    • self-instruct 框架
    • 生成语料代码实现过程
      • Step1 通过模型生成新的指令
      • Step2 对模型生成的指令进行判断
      • Step3:根据Step2的判断结果,给出不同的输出
      • Step4:过滤及后处理

本文对 self-instruct 生成语料的流程进行了分析,并尝试使用该代码生成了一定数量的语料。

self-instruct 介绍

2023年3月14日,斯坦福发布了Stanford Alpaca,该模型是对Meta的LLaMA &B进行了微调,且只花费了不到600美元。
其中,微调过程:在8个80GB A100上训练了3个小时,不到100美元;而微调所用数据是使用OpenAI的API,通过self-instruct方式生成的52K指令数据,花费了500美元。

self-instruct是一种将预训练语言模型与指令对齐的方法。可以通过模型自己来生成数据,而不需要大量的人工标注。

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

self-instruct 框架

self-instruct 框架如下图所示:
在这里插入图片描述
从上图可以看出,整个流程共包含了 4 个步骤。而生成后的数据形式如下:

  • instruction: str,描述了模型应该执行的任务,也就是指令描述。
  • input: str,任务的可选上下文或输入。
  • output: str,由GPT3.5对应的API即 text-davinci-003生成的指令的答案。

Step1:通过模型生成新的指令;
根据人工设计的175个任务,每个任务都有对应的(指令,输入,输出)或(指令,输出);使用模型生成新的指令;
Step2:对模型生成的指令进行判断(指令是否是一个分类任务);
Step3:根据Step2的判断结果,给出不同的输出
如果是分类任务,就通过模型输出 Class_label 和 Input(Output-first);
如果不是分类任务,就通过模型输出 Input 和 Output(Input-first)。
Step4:过滤及后处理
对上述模型生成的数据进行过滤和后处理,将经过过滤和后处理的数据添加到种子池中。

对于以上4个步骤进行不断循环,直到种子池有足够多的数据(通常会设定一个具体的参数,比如:52000),生成过程停止。而对于每一步还需要展开描述下相关细节。

关于 Step1

生成指令时,先从种子池中随机抽取6个人工编写的指令,再随机抽取2个之前步骤中模型生成的指令,总共8个指令。按照指定模版格式组织之后,输入给模型,让模型输出一个新的指令。
需要注意的是,最开始的时候,是没有模型生成的指令,因此是会直接从种子池中随机抽取8条人工编写的指令。

关于 Step2
判断指令是否属于分类任务的操作如下:在种子池中随机挑选12条分类指令和19条非分类指令,然后加上新生成的指令。

关于Step4:过滤及后处理
为了数据的多样性,新生成的指令只有与种子池中的指令的 ROUGE-L 小于0.7时才会添加进入种子池;
排除一些无法被语言模型处理的指令,比如涉及图像、图片、图形的指令;
在给指令生成实例时,会过滤掉输入相同但是输出不同的实例。

生成语料代码实现过程

下面我们从代码部分来看详细的步骤。整个过程是要依次运行 4 个代码文件,对应前文中描述的 4 个步骤。

# 1. Generate instructions from the seed tasks
./scripts/generate_instructions.sh

# 2. Identify whether the instruction represents a classification task or not
./scripts/is_clf_or_not.sh

# 3. Generate instances for each instruction
./scripts/generate_instances.sh

# 4. Filtering, processing, and reformatting
./scripts/prepare_for_finetuning.sh

本次实验在本地的pytorch环境下进行。
1、首先将代码下载到本地,下面两种方式均可。

  • 使用 Download 下载zip文件
  • git clone https://github.com/yizhongw/self-instruct.git

我这里是在我的windows上操作的,所以无法执行bash命令,我这里直接用python命令运行。

2、进入conda环境(我这里用的pytorch这个环境) ,安装相关的包

cd self-instruct-main
pip install -r requirements.txt 

Step1 通过模型生成新的指令

先看下原始人工标注的175种子数据的样式,共包含4个部分,id,name,instruction,is_classification。

{
	"id": "seed_task_0", 
	"name": "breakfast_suggestion", 
	"instruction": "Is there anything I can eat for a breakfast that doesn't include eggs, yet includes protein, and has roughly 700-1000 calories?", "instances": [{"input": "", "output": "Yes, you can have 1 oatmeal banana protein shake and 4 strips of bacon. The oatmeal banana protein shake may contain 1/2 cup oatmeal, 60 grams whey protein powder, 1/2 medium banana, 1tbsp flaxseed oil and 1/2 cup watter, totalling about 550 calories. The 4 strips of bacon contains about 200 calories."}], 
	"is_classification": false
	}

本次只是实验,故将scripts/generate_instructions.sh中的5000改为100(这样产生的费用也较少)
运行命令如下:

python self_instruct/bootstrap_instructions.py --batch_dir data/ceshi --num_instructions_to_generate 100 --seed_tasks_path data/seed_tasks.jsonl --engine "davinci" --api_key "自己的openai API"

大概需要4分半的时间,生成100条数据。会写入data/ceishi/machine_generated_instructions.jsonl中,最终生成了122条。这些数据是通过LLM生成了与种子任务关联度比较弱的一些任务描述(一些相似度高的就删除了)。

从下面的代码中可以看出,最后写入文件时,一共包含了以下4个部分:instruction,most_similar,avg_similarity_score,metadata,request_idx。

fout.write(json.dumps({
    "instruction": inst,
    "most_similar": most_similar_instructions,
    "avg_similarity_score": float(np.mean(rouge_scores)),
    "metadata": metadata,
    "request_idx": request_idx
}) + "\n")

生成数据的核心代码如下:

# load the LM-generated instructions,使用生成模型得到新的100条 instruction 提示
    machine_instructions = []
# 开始生成 100 条 instruction 提示数据
    with open(os.path.join(args.batch_dir, "machine_generated_instructions.jsonl"), "a") as fout:
        while len(machine_instructions) < args.num_instructions_to_generate:
            batch_inputs = []
            # args.request_batch_size为5
            for _ in range(args.request_batch_size):
                # sample machine instructions from the pool(从生成模型中选,n表示最少的条数。这里为2)
                prompt_instructions = sample_machine_instructions(
                    machine_instructions, 
                    similarities=None,
                    n=2)
                # sample human instructions from the pool
                # 从默认的175条中选再选几条,相当于一共选了8条,其中从175条中选6条,使用LLM生成2条(最开始的时候,machine_instructions为空,因此会直接从175条中直接选8条)
                prompt_instructions += random.sample(seed_instructions, args.num_prompt_instructions - len(prompt_instructions))
                random.shuffle(prompt_instructions)
                prompt = encode_prompt(prompt_instructions, classification=args.use_clf_seed_tasks_only)
                batch_inputs.append(prompt)
            results = make_gpt3_requests(
                engine=args.engine,
                prompts=batch_inputs,
                max_tokens=1024,
                temperature=0.7,
                top_p=0.5,
                frequency_penalty=0,
                presence_penalty=2,
                stop_sequences=["\n\n", "\n16", "16.", "16 ."],
                logprobs=1,
                n=1,
                best_of=1,
                api_key=args.api_key,
                organization=args.organization,
            )

其中,对不同类型的数据需要构建不同的 prompt 数据(如:是分类数据,不是分类数据),构建方式在函数 encode_prompt中

# 构建prompt数据,针对是否分类分别构建不同的prompt数据,
# 是否是分类任务, 是=>输出优先,否=>输入优先,对应的 prompt_instructions/prompt_instances 不一样
def encode_prompt(prompt_instructions, classification=False):
    """Encode multiple prompt instructions into a single string."""
    if classification:
        # 源码中prompt
        # prompt = "Come up with a series of classification tasks. Try to specify the possible output labels when possible.\n"
        prompt = "Referring to a series of classification tasks, generate 8 more new tasks. Try to specify the possible output labels when possible.\n"
    else:
        # 源码中prompt
        # prompt = "Come up with a series of tasks:\n"
        prompt = "Referring to these eight tasks, generate 8 more new tasks:\n"
    for idx, instruction in enumerate(prompt_instructions):
        instruction = re.sub(r"\s+", " ", instruction).strip().rstrip(":")
        prompt += f"{idx+1}. {instruction}\n"
        prompt += f"{len(prompt_instructions) + 1}."
    return prompt

Step2 对模型生成的指令进行判断

3、判断是否是分类任务。

python self_instruct/identify_clf_or_not.py --batch_dir data/ceshi --engine "davinci" --request_batch_size 5 --api_key "自己的openai API"

会写入data/ceishi/is_clf_or_not_davinci_template_1.jsonl中,最终生成了122条。
内容包括:

{"instruction": "Find the largest number in this list.", "is_classification": " Yes"}
{"instruction": "What is the first name of your favorite actor?", "is_classification": " No"}
{"instruction": "Give me the number of distinct elements in this set.", "is_classification": " Yes"}
{"instruction": "Give me the top 5 countries that are exporting tea.", "is_classification": " Yes"}

核心代码如下:

# 执行输出过程
    with open(output_path, "w") as fout:
        for batch_idx in range(0, len(lines), args.request_batch_size):
            batch = [json.loads(line) for line in lines[batch_idx: batch_idx + args.request_batch_size]]
            if all(d["instruction"] in existing_requests for d in batch):
                for d in batch:
                    data = existing_requests[d["instruction"]]
                    data = OrderedDict(
                        (k, data[k]) for k in \
                            ["instruction", "is_classification"]
                        )
                    fout.write(json.dumps(data, ensure_ascii=False) + "\n")
            else:
                # prefix = compose_prompt_prefix(human_written_tasks, batch[0]["instruction"], 8, 2)
                prefix = templates[args.template]
                prompts = [prefix + " " + d["instruction"].strip() + "\n" + "Is it classification?" for d in batch]
                results = make_gpt3_requests(
                    engine=args.engine,
                    prompts=prompts,
                    max_tokens=3,
                    temperature=0,
                    top_p=0,
                    frequency_penalty=0,
                    presence_penalty=0,
                    stop_sequences=["\n", "Task"],
                    logprobs=1,
                    n=1,
                    best_of=1,
                    api_key=args.api_key,
                    organization=args.organization)
                for i in range(len(batch)):
                    data = batch[i]
                    if results[i]["response"] is not None:
                        data["is_classification"] = results[i]["response"]["choices"][0]["text"]
                    else:
                        data["is_classification"] = ""
                    data = {
                        "instruction": data["instruction"],
                        "is_classification": data["is_classification"]
                    }
                    data = OrderedDict(
                        (k, data[k]) for k in \
                            ["instruction", "is_classification"]
                        )
                    fout.write(json.dumps(data, ensure_ascii=False) + "\n")

Step3:根据Step2的判断结果,给出不同的输出

python self_instruct/generate_instances.py --batch_dir data/ceshi --input_file machine_generated_instructions.jsonl --output_file machine_generated_instances.jsonl --max_instances_to_gen 5 --engine "davinci" --request_batch_size 5 --api_key "自己的openai API"

如果遇到以下报错:
UnicodeDecodeError: ‘gbk’ codec can’t decode byte 0x9d in position 6169: illegal multibyte sequence
解决方法:
在open函数中添加encoding='utf-8’即可。

运行后会将结果写入 data/ceishi/machine_generated_instances.jsonl中。每条数据包含5部分:“instruction”, “raw_instances”, “instance_metadata”, “instruction_metadata”, “most_similar”, “avg_similarity_score”。
在这里插入图片描述
核心代码如下:

 with open(output_path, "w", encoding='utf-8') as fout:
        for batch_idx in range(0, len(tasks), args.request_batch_size):
            batch = tasks[batch_idx: batch_idx + args.request_batch_size]
            if all(d["instruction"] in existing_requests for d in batch):
                for d in batch:
                    data = existing_requests[d["instruction"]]
                    data = OrderedDict(
                        (k, data[k]) for k in \
                            ["instruction", "raw_instances", "instance_metadata", "instruction_metadata", 
                            "most_similar", "avg_similarity_score"]
                        )
                    fout.write(json.dumps(data, ensure_ascii=False) + "\n")
            else:
                prompts = []
                for task in batch:
                    if task_clf_types[task["instruction"]]:
                        prompt = output_first_template_for_clf + " " + task["instruction"].strip() + "\n"
                        prompts.append(prompt)
                    else:
                        prompt = input_first_template_for_gen + " " + task["instruction"].strip() + "\n"
                        prompts.append(prompt)
                results = make_gpt3_requests(
                    engine=args.engine,
                    prompts=prompts,
                    # because the clf template is longer, we need to decrease the max_tokens
                    max_tokens=300 if any(task_clf_types[task["instruction"]] for task in batch) else 350,
                    temperature=0,
                    top_p=0,
                    frequency_penalty=0,
                    presence_penalty=1.5,
                    stop_sequences=[f"Example {args.max_instances_to_generate + 1}", "Task:"],
                    logprobs=1,
                    n=1,
                    best_of=1,
                    api_key=args.api_key,
                    organization=args.organization)
                for i in range(len(batch)):
                    data = batch[i]
                    data["instance_metadata"] = results[i]
                    if results[i]["response"] is not None:
                        data["raw_instances"] = results[i]["response"]["choices"][0]["text"]
                    else:
                        data["raw_instances"] = ""
                    data = OrderedDict(
                        (k, data[k]) for k in \
                            ["instruction", "raw_instances", "instance_metadata", "instruction_metadata", 
                            "most_similar", "avg_similarity_score"]
                        )
                    fout.write(json.dumps(data, ensure_ascii=False) + "\n")
            progress_bar.update(len(batch))

Step4:过滤及后处理

python self_instruct/prepare_for_finetuning.py --instance_files data/ceshi/machine_generated_instances.jsonl --classification_type_files data/ceshi/is_clf_or_not_davinci_template_1.jsonl --output_dir data/ceshi/finetuning_data --include_seed_tasks --seed_tasks_path data/seed_tasks.jsonl

运行后会生成两个数据文件,均在data/ceshi/finetuning_data目录下:
all_generated_instances.jsonl 和 gpt3_finetuning_data_336.jsonl
其中,all_generated_instances.jsonl中包含的是 instruction,input,output
gpt3_finetuning_data_336.jsonl中包含的是prompt,completion。

核心代码如下:

for task in tqdm.tqdm(generated_tasks):
        # get instruction
        instruction = task["instruction"]
        task["is_classification"] = task_clf_types[instruction]

        # get the instances
        if task["is_classification"]:
            task_instances = parse_instances_for_classification_task(task["raw_instances"], instruction, task["instance_metadata"])
        else:
            task_instances = parse_instances_for_generation_task(task["raw_instances"], instruction, task["instance_metadata"])

        # we only allow max 5 instances per task
        task_instances = random.sample(task_instances, min(len(task_instances), 5))
        
        if not task_instances:
            continue

        training_instances += task_instances
# get the prompt and completion for training gpt3
    gpt3_instances = []
    for instance in training_instances:
        # get input and do preprocessing
        inst_input = instance[1]
        # for some tasks, we check whether the input contains colon, and if so, we remove the part before the colon
        if random.random() < 0.5:
            colon_words = re.findall(r"(\w+):", inst_input)
            # if only one colon is found, we assume the instance only have one input and we remove the field name before the colon
            if len(set(colon_words)) == 1:
                inst_input = inst_input.split(":", 1)[1].strip()
            else:
                inst_input = inst_input.strip()
            # we also replace two consecutive new lines with one new line half of the time
            inst_input = inst_input.replace("\n\n", "\n")
        
        gpt3_instances.append(encode_instance(instance[0], inst_input, instance[2]))

    # remove duplicates
    filtered_instances = []
    prompt_completion_set = set()
    for instance in gpt3_instances:
        instance_pair = (instance["prompt"], instance["completion"])
        if instance_pair not in prompt_completion_set:
            prompt_completion_set.add((instance["prompt"], instance["completion"]))
            filtered_instances.append(instance)
    gpt3_instances = filtered_instances

    # shuffle
    random.shuffle(gpt3_instances)
    with open(os.path.join(args.output_dir, f"gpt3_finetuning_data_{len(gpt3_instances)}.jsonl"), "w") as fout:
        for instance in gpt3_instances:
            fout.write(json.dumps({
                "prompt": instance["prompt"],
                "completion": instance["completion"],
            }) + "\n")

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(二)Eureka 高可用

1、Eureka 高可用介绍 EurekaServer可以是一个集群&#xff0c;形成高可用的Eureka注册中心 多个Eureka Server之间也会互相注册为服务&#xff0c;当服务提供者注册到Eureka Server集群中的某个节点时&#xff0c;该节点会把服务的信息同步给集群中的每个节点&#xff0c;从…

你还不知道iOS备忘录的10个使用技巧吗?那就OUT了!

案例&#xff1a;苹果备忘录的使用技巧 【友友们&#xff0c;苹果备忘录有哪些好用的使用技巧&#xff1f;可以分享一下吗&#xff1f;】 作为一款简单而实用的应用程序&#xff0c;ios备忘录可以帮助您记录生活中的各种事件、任务、待办事项等信息。除此之外&#xff0c;它还…

[架构之路-176]-《软考-系统分析师》-1-嵌入式系统分析与设计 - 实时性(任务切换时间、中断延迟时间、中断响应时间)、可靠性、功耗、体积、成本

目录 前言&#xff1a; 1 7 . 1 嵌 入 式 系 统 概 述 1 . 嵌入式系统的特点 (1) 系统专用性强。 (2) 系统实时性强。 (3) 软硬件依赖性强 (4) 处理器专用。 ( 5 ) 多种技术紧密结合。 (6) 系统透明性。 (7) 系统资源受限。 2 . 嵌入式系统的组成 1 7 . 3 嵌入式实…

第七章 3D地形搭建(下)

上一章节我们介绍了地形编辑&#xff0c;接下来介绍Paint Trees&#xff08;绘制树木&#xff09;。 由于树木属于3D模型&#xff0c;初学者不可能通过3ds max或者maya软件进行制作&#xff0c;因此我们还是建议大家从官方资源商店上面去下载免费的。这里我们依然使用上个章节…

骨传导耳机效果怎么样,骨传导耳机的具体好处有哪些

随着人们生活水平的提高&#xff0c;科技的发展&#xff0c;耳机已经成为了人们必不可少的工具&#xff0c;其中骨传导耳机成为了许多人的新宠。骨传导耳机与传统耳机相比&#xff0c;不入耳、不伤耳、无需塞入耳朵、能够在户外运动时享受音乐&#xff0c;使用场景非常丰富。接…

找网站绝对路径

目录 Linux系统 目标出网。且命令有回显 目标出网&#xff0c;命令无回显 目标不出网&#xff0c;命令无回显 Windows系统 目标出网&#xff0c;命令有回显 目标出网&#xff0c;命令无回显 目标不出网&#xff0c;命令无回显 Linux系统 目标出网。且命令有回显 find …

【MATLAB图像处理实用案例详解(12)】——利用BP神经网络实现图像压缩

目录 一、图像压缩二、BP神经网络实现图像压缩原理三、算法步骤3.1 图像块划分3.2 归一化3.3 建立BP神经网络3.4 保存结果 四、效果演示 一、图像压缩 常见的文件压缩软件如WinZip、WinRAR等采用的是无损压缩&#xff0c;能够完全恢复原文件内容。多媒体信息具有信息量大、冗余…

java基础项目:图书管理系统(详解)

java基础学习后适合写的测试语法和代码熟练度的小项目&#xff08;学校学java&#xff0c;总得练一练的&#xff09; 文章目录 架构 / 流程架构流程具体内容 代码实现booksBook&#xff08;图书类&#xff09;BookList&#xff08;图书列表类&#xff09; userUser&#xff08;…

U盘数据恢复怎么做?分享4个恢复方法!

案例&#xff1a;u盘数据恢复 【我之前丢失的u盘前两天突然找到了&#xff0c;但是我将它插入电脑后很多数据都读不出来了&#xff0c;遇到u盘无法读取的情况怎么办呀&#xff1f;怎么才能恢复u盘数据呢&#xff1f;】 u盘是一种便携式存储设备&#xff0c;广泛用于数据传输和…

使用Inno Setup将QT开发的软件制作成安装包(仅Windwos平台)

使用Inno Setup将QT开发的软件制作成安装包&#xff08;仅Windwos平台&#xff09; 前言 在使用QT完成软件开发后要把软件给别人使用&#xff0c;我之前是打包发布后直接弄成压缩包发给别人&#xff0c;但是接收的人就要通过解压软件解压才能使用软件&#xff0c;如果没有解压…

WizardKM:Empowering Large Language Models to Follow Complex Instructions

WizardKM:Empowering Large Language Models to Follow Complex Instructions Introduction参考 Introduction 作者表明当前nlp社区的指令数据比较单一&#xff0c;大部分都是总结、翻译的任务&#xff0c;但是在真实场景中&#xff0c;人们有各式各样的需求&#xff0c;这限制…

MyBats

一、MyBatis简介 1. MyBatis历史 MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下&#xff0c; iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到Github。 iBatis一词来…

【Python | 基础语法篇】02、标识符、运算符、字符串扩展及数据输入

目录 一、标识符 1.1 什么是标识符 1.2 标识符命名规则 1.2.1 标识符命名规则 - 内容限定 1.2.2 标识符命名规则 - 大小写敏感 1.2.3 标识符命名规则 - 不可使用关键字 1.3 案例演示 1.4 变量命名规范 1.4.1 变量命名规范 - 见名知意 ​1.4.2 变量命名规范 - 下划线…

Spring aop如何寻找advisor

1.bean的生命周期第一步回去解析所有的advisor 2.第四个是我们之前开启的注解EnableAspectJAutoProxy 3.org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation 4.org.springframework.aop.framework.autoproxy.AbstractAutoP…

ChatGPT的提示的一些高级知识

作为一个大型语言模型(LLM)接口&#xff0c;ChatGPT有令人印象深刻的潜力&#xff0c;但是真正能否用好取决与我们的提示&#xff08;Prompt &#xff09;&#xff0c;一个好的提示可以让ChatGPT晋升到一个更好的层次。 在这篇文章中&#xff0c;我们将介绍关于提示的一些高级…

可视化 | Flask+Pyecharts可视化模板二

文章目录 &#x1f3f3;️‍&#x1f308; 1. 系统说明界面&#x1f3f3;️‍&#x1f308; 2. 柱状图示例界面&#x1f3f3;️‍&#x1f308; 3. 散点图示例界面&#x1f3f3;️‍&#x1f308; 4. 折线图示例界面&#x1f3f3;️‍&#x1f308; 5. 饼图示例界面&#x1f3f…

单链表——你需要掌握的那些内容

如有错误&#xff0c;感谢不吝赐教、交流 文章目录 前言本文涉及题目&#xff1a;设计链表有无头结点的区别头指针无头结点有头结点为什么需要头结点呢&#xff1f;注意&#xff1a; 单链表&#xff0c;本文使用Java实现定义链表节点定义一个链表类并初始化get(int index)addA…

HAproxy与web集群

文章目录 一、HAproxy1.HAProxy是什么2.HAProxy的核心能力和关键特性3.LVS、Nginx、HAproxy的区别&#xff1a; 二、实验步骤1.Haproxy搭建 Web 群集 总结 一、HAproxy 1.HAProxy是什么 HAProxy是一个免费的负载均衡软件&#xff0c;可以运行于大部分主流的Linux操作系统上。…

儿童生长发育迟缓的鉴别和干预

&#xff08;英国&#xff09;国家临床医学研究所&#xff08;NICE&#xff09;2017年发布关于婴儿/儿童生长发育迟缓的鉴别、评估和监测的指南&#xff0c;该指南确定了生长发育的界值&#xff0c;指出了诱因及危险因素&#xff0c;并提出了干预的方案。 ▼Part1&#xff1a;…

“无人值守”时代已来,千巡翼X4给出怎样的答案?

随着技术和产品的逐渐成熟&#xff0c;无人机在各行业的应用越来越普遍&#xff0c;但如何进一步解放人力&#xff0c;提高运营效率&#xff0c;还有很大的探索空间。针对作业频率高、重复性高、周期性强、作业环境艰险危险等痛点&#xff0c;用户迫切需要更高效、更智能的全自…