Qwen2大模型微调入门实战-命名实体识别(NER)任务(完整代码)

news2024/10/23 2:42:37

Qwen2是通义千问团队最近开源的大语言模型,由阿里云通义实验室研发。

以Qwen2作为基座大模型,通过指令微调的方式做高精度的命名实体识别(NER),是学习入门LLM微调、建立大模型认知的非常好的任务。

在这里插入图片描述

使用LoRA方法训练,1.5B模型对显存要求不高,10GB左右就可以跑。

在本文中,我们会使用 Qwen2-1.5b-Instruct 模型在 中文NER 数据集上做指令微调训练,同时使用SwanLab监控训练过程、评估模型效果。

  • 代码:完整代码直接看本文第5节 或 Github
  • 实验日志过程:Qwen2-1.5B-NER-Fintune - SwanLab
  • 模型:Modelscope
  • 数据集:chinese_ner_sft
  • SwanLab:https://swanlab.cn

知识点1:什么是指令微调?

大模型指令微调(Instruction Tuning)是一种针对大型预训练语言模型的微调技术,其核心目的是增强模型理解和执行特定指令的能力,使模型能够根据用户提供的自然语言指令准确、恰当地生成相应的输出或执行相关任务。

指令微调特别关注于提升模型在遵循指令方面的一致性和准确性,从而拓宽模型在各种应用场景中的泛化能力和实用性。

在实际应用中,我的理解是,指令微调更多把LLM看作一个更智能、更强大的传统NLP模型(比如Bert),来实现更高精度的NLP任务。所以这类任务的应用场景覆盖了以往NLP模型的场景,甚至很多团队拿它来标注互联网数据

知识点2:什么是命名实体识别?

命名实体识别 (NER) 是一种NLP技术,主要用于识别和分类文本中提到的重要信息(关键词)。这些实体可以是人名、地名、机构名、日期、时间、货币值等等。 NER 的目标是将文本中的非结构化信息转换为结构化信息,以便计算机能够更容易地理解和处理。

在这里插入图片描述

NER 也是一项非常实用的技术,包括在互联网数据标注、搜索引擎、推荐系统、知识图谱、医疗保健等诸多领域有广泛应用。

1.环境安装

本案例基于Python>=3.8,请在您的计算机上安装好Python,并且有一张英伟达显卡(显存要求并不高,大概10GB左右就可以跑)。

我们需要安装以下这几个Python库,在这之前,请确保你的环境内已安装好了pytorch以及CUDA

swanlab
modelscope
transformers
datasets
peft
accelerate
pandas

一键安装命令:

pip install swanlab modelscope transformers datasets peft pandas accelerate

本案例测试于modelscope1.14.0、transformers4.41.2、datasets2.18.0、peft0.11.1、accelerate0.30.1、swanlab0.3.11

2.准备数据集

本案例使用的是HuggingFace上的chinese_ner_sft数据集,该数据集主要被用于训练命名实体识别模型。

在这里插入图片描述

chinese_ner_sft由不同来源、不同类型的几十万条数据组成,应该是我见过收录最齐全的中文NER数据集。

这次训练我们不需要用到它的全部数据,只取其中的CCFBDCI数据集(中文命名实体识别算法鲁棒性评测数据集)进行训练,该数据集包含LOC(地点)、GPE(地理)、ORG(组织)和PER(人名)四种实体类型标注,每条数据的例子如下:

{
  "text": "今天亚太经合组织第十二届部长级会议在这里开幕,中国外交部部长唐家璇、外经贸部部长石广生出席了会议。",
  "entities": [
    {
        "start_idx": 23,
        "end_idx": 25,
        "entity_text": "中国",
        "entity_label": "GPE",
        "entity_names": ["地缘政治实体", "政治实体", "地理实体", "社会实体"]},
        {
            "start_idx": 25,
            "end_idx": 28,
            "entity_text": "外交部",
            "entity_label": "ORG",
            "entity_names": ["组织", "团体", "机构"]
        },
        {
            "start_idx": 30,
            "end_idx": 33,
            "entity_text": "唐家璇",
            "entity_label": "PER",
            "entity_names": ["人名", "姓名"]
        }, 
        ...
    ],
"data_source": "CCFBDCI"
}

其中text是输入的文本,entities是文本抽取出的实体。我们的目标是希望微调后的大模型能够根据由text组成的提示词,预测出一个json格式的实体信息:

输入:今天亚太经合组织第十二届部长级会议在这里开幕,中国外交部部长唐家璇、外经贸部部长石广生出席了会议。

大模型输出:{"entity_text":"中国", "entity_label":"组织"}{"entity_text":"唐家璇", "entity_label":"人名"}...

现在我们将数据集下载到本地目录。下载方式是前往chinese_ner_sft - huggingface下载ccfbdci.jsonl到项目根目录下即可:

在这里插入图片描述

3. 加载模型

这里我们使用modelscope下载Qwen2-1.5B-Instruct模型(modelscope在国内,所以直接用下面的代码自动下载即可,不用担心速度和稳定性问题),然后把它加载到Transformers中进行训练:

from modelscope import snapshot_download, AutoTokenizer
from transformers import AutoModelForCausalLM, TrainingArguments, Trainer, DataCollatorForSeq2Seq

model_id = "qwen/Qwen2-1.5B-Instruct"    
model_dir = "./qwen/Qwen2-1___5B-Instruct"

# 在modelscope上下载Qwen模型到本地目录下
model_dir = snapshot_download(model_id, cache_dir="./", revision="master")

# Transformers加载模型权重
tokenizer = AutoTokenizer.from_pretrained(model_dir, use_fast=False, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_dir, device_map="auto", torch_dtype=torch.bfloat16)
model.enable_input_require_grads()  # 开启梯度检查点时,要执行该方法

4. 配置训练可视化工具

我们使用SwanLab来监控整个训练过程,并评估最终的模型效果。

这里直接使用SwanLab和Transformers的集成来实现:

from swanlab.integration.huggingface import SwanLabCallback

swanlab_callback = SwanLabCallback(...)

trainer = Trainer(
    ...
    callbacks=[swanlab_callback],
)

如果你是第一次使用SwanLab,那么还需要去https://swanlab.cn上注册一个账号,在用户设置页面复制你的API Key,然后在训练开始时粘贴进去即可:

在这里插入图片描述

5. 完整代码

开始训练时的目录结构:

|--- train.py
|--- train.jsonl
|--- test.jsonl

train.py:

import json
import pandas as pd
import torch
from datasets import Dataset
from modelscope import snapshot_download, AutoTokenizer
from swanlab.integration.huggingface import SwanLabCallback
from peft import LoraConfig, TaskType, get_peft_model
from transformers import AutoModelForCausalLM, TrainingArguments, Trainer, DataCollatorForSeq2Seq
import os
import swanlab


def dataset_jsonl_transfer(origin_path, new_path):
    """
    将原始数据集转换为大模型微调所需数据格式的新数据集
    """
    messages = []

    # 读取旧的JSONL文件
    with open(origin_path, "r") as file:
        for line in file:
            # 解析每一行的json数据
            data = json.loads(line)
            input_text = data["text"]
            entities = data["entities"]
            match_names = ["地点", "人名", "地理实体", "组织"]
            
            entity_sentence = ""
            for entity in entities:
                entity_json = dict(entity)
                entity_text = entity_json["entity_text"]
                entity_names = entity_json["entity_names"]
                for name in entity_names:
                    if name in match_names:
                        entity_label = name
                        break
                
                entity_sentence += f"""{{"entity_text": "{entity_text}", "entity_label": "{entity_label}"}}"""
            
            if entity_sentence == "":
                entity_sentence = "没有找到任何实体"
            
            message = {
                "instruction": """你是一个文本实体识别领域的专家,你需要从给定的句子中提取 地点; 人名; 地理实体; 组织 实体. 以 json 格式输出, 如 {"entity_text": "南京", "entity_label": "地理实体"} 注意: 1. 输出的每一行都必须是正确的 json 字符串. 2. 找不到任何实体时, 输出"没有找到任何实体". """,
                "input": f"文本:{input_text}",
                "output": entity_sentence,
            }
            
            messages.append(message)

    # 保存重构后的JSONL文件
    with open(new_path, "w", encoding="utf-8") as file:
        for message in messages:
            file.write(json.dumps(message, ensure_ascii=False) + "\n")
            
            
def process_func(example):
    """
    将数据集进行预处理
    """

    MAX_LENGTH = 384 
    input_ids, attention_mask, labels = [], [], []
    system_prompt = """你是一个文本实体识别领域的专家,你需要从给定的句子中提取 地点; 人名; 地理实体; 组织 实体. 以 json 格式输出, 如 {"entity_text": "南京", "entity_label": "地理实体"} 注意: 1. 输出的每一行都必须是正确的 json 字符串. 2. 找不到任何实体时, 输出"没有找到任何实体"."""
    
    instruction = tokenizer(
        f"<|im_start|>system\n{system_prompt}<|im_end|>\n<|im_start|>user\n{example['input']}<|im_end|>\n<|im_start|>assistant\n",
        add_special_tokens=False,
    )
    response = tokenizer(f"{example['output']}", add_special_tokens=False)
    input_ids = instruction["input_ids"] + response["input_ids"] + [tokenizer.pad_token_id]
    attention_mask = (
        instruction["attention_mask"] + response["attention_mask"] + [1]
    )
    labels = [-100] * len(instruction["input_ids"]) + response["input_ids"] + [tokenizer.pad_token_id]
    if len(input_ids) > MAX_LENGTH:  # 做一个截断
        input_ids = input_ids[:MAX_LENGTH]
        attention_mask = attention_mask[:MAX_LENGTH]
        labels = labels[:MAX_LENGTH]
    return {"input_ids": input_ids, "attention_mask": attention_mask, "labels": labels}   


def predict(messages, model, tokenizer):
    device = "cuda"
    text = tokenizer.apply_chat_template(
        messages,
        tokenize=False,
        add_generation_prompt=True
    )
    model_inputs = tokenizer([text], return_tensors="pt").to(device)

    generated_ids = model.generate(
        model_inputs.input_ids,
        max_new_tokens=512
    )
    generated_ids = [
        output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
    ]
    
    response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
    
    print(response)
     
    return response


model_id = "qwen/Qwen2-1.5B-Instruct"    
model_dir = "./qwen/Qwen2-1___5B-Instruct"

# 在modelscope上下载Qwen模型到本地目录下
model_dir = snapshot_download(model_id, cache_dir="./", revision="master")

# Transformers加载模型权重
tokenizer = AutoTokenizer.from_pretrained(model_dir, use_fast=False, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_dir, device_map="auto", torch_dtype=torch.bfloat16)
model.enable_input_require_grads()  # 开启梯度检查点时,要执行该方法

# 加载、处理数据集和测试集
train_dataset_path = "ccfbdci.jsonl"
train_jsonl_new_path = "ccf_train.jsonl"

if not os.path.exists(train_jsonl_new_path):
    dataset_jsonl_transfer(train_dataset_path, train_jsonl_new_path)

# 得到训练集
total_df = pd.read_json(train_jsonl_new_path, lines=True)
train_df = total_df[int(len(total_df) * 0.1):]
train_ds = Dataset.from_pandas(train_df)
train_dataset = train_ds.map(process_func, remove_columns=train_ds.column_names)


config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
    inference_mode=False,  # 训练模式
    r=8,  # Lora 秩
    lora_alpha=32,  # Lora alaph,具体作用参见 Lora 原理
    lora_dropout=0.1,  # Dropout 比例
)

model = get_peft_model(model, config)

args = TrainingArguments(
    output_dir="./output/Qwen2-NER",
    per_device_train_batch_size=4,
    per_device_eval_batch_size=4,
    gradient_accumulation_steps=4,
    logging_steps=10,
    num_train_epochs=2,
    save_steps=100,
    learning_rate=1e-4,
    save_on_each_node=True,
    gradient_checkpointing=True,
    report_to="none",
)

swanlab_callback = SwanLabCallback(
    project="Qwen2-NER-fintune",
    experiment_name="Qwen2-1.5B-Instruct",
    description="使用通义千问Qwen2-1.5B-Instruct模型在NER数据集上微调,实现关键实体识别任务。",
    config={
        "model": model_id,
        "model_dir": model_dir,
        "dataset": "qgyd2021/chinese_ner_sft",
    },
)

trainer = Trainer(
    model=model,
    args=args,
    train_dataset=train_dataset,
    data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True),
    callbacks=[swanlab_callback],
)

trainer.train()

# 用测试集的随机20条,测试模型
# 得到测试集
test_df = total_df[:int(len(total_df) * 0.1)].sample(n=20)

test_text_list = []
for index, row in test_df.iterrows():
    instruction = row['instruction']
    input_value = row['input']
    
    messages = [
        {"role": "system", "content": f"{instruction}"},
        {"role": "user", "content": f"{input_value}"}
    ]

    response = predict(messages, model, tokenizer)
    messages.append({"role": "assistant", "content": f"{response}"})
    result_text = f"{messages[0]}\n\n{messages[1]}\n\n{messages[2]}"
    test_text_list.append(swanlab.Text(result_text, caption=response))
    
swanlab.log({"Prediction": test_text_list})
swanlab.finish()

看到下面的进度条即代表训练开始:

在这里插入图片描述

6.训练结果演示

在SwanLab上查看最终的训练结果:

可以看到在2个epoch之后,微调后的qwen2的loss降低到了不错的水平——当然对于大模型来说,真正的效果评估还得看主观效果。

在这里插入图片描述

可以看到在一些测试样例上,微调后的qwen2能够给出准确的实体抽取结果:

在这里插入图片描述

在这里插入图片描述

至此,你已经完成了qwen2在NER任务上的指令微调训练!

7. 推理训练好的模型

训好的模型默认被保存在./output/Qwen2-NER文件夹下。

推理模型的代码如下:

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel

def predict(messages, model, tokenizer):
    device = "cuda"

    text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
    model_inputs = tokenizer([text], return_tensors="pt").to(device)

    generated_ids = model.generate(model_inputs.input_ids, max_new_tokens=512)
    generated_ids = [output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)]
    response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]

    return response


# 加载原下载路径的tokenizer和model
tokenizer = AutoTokenizer.from_pretrained("./qwen/Qwen2-1___5B-Instruct/", use_fast=False, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained("./qwen/Qwen2-1___5B-Instruct/", device_map="auto", torch_dtype=torch.bfloat16)

# 加载训练好的Lora模型,将下面的[checkpoint-XXX]替换为实际的checkpoint文件名名称
model = PeftModel.from_pretrained(model, model_id="./output/Qwen2-NER/checkpoint-1700")

input_text = "西安电子科技大学的陈志明爱上了隔壁西北工业大学苏春红,他们约定好毕业后去中国的苏州定居。"
test_texts = {
    "instruction": """你是一个文本实体识别领域的专家,你需要从给定的句子中提取 地点; 人名; 地理实体; 组织 实体. 以 json 格式输出, 如; {"entity_text": "南京", "entity_label": "地理实体"} 注意: 1. 输出的每一行都必须是正确的 json 字符串. 2. 找不到任何实体时, 输出"没有找到任何实体". """,
    "input": f"文本:{input_text}"
}

instruction = test_texts['instruction']
input_value = test_texts['input']

messages = [
    {"role": "system", "content": f"{instruction}"},
    {"role": "user", "content": f"{input_value}"}
]

response = predict(messages, model, tokenizer)
print(response)

输出结果为:

{"entity_text": "西安电子科技大学", "entity_label": "组织"}
{"entity_text": "陈志明", "entity_label": "人名"}
{"entity_text": "西北工业大学", "entity_label": "组织"}
{"entity_text": "苏春红", "entity_label": "人名"}
{"entity_text": "中国", "entity_label": "地理实体"}
{"entity_text": "苏州", "entity_label": "地理实体"}

相关链接

  • 代码:完整代码直接看本文第5节 或 Github
  • 实验日志过程:Qwen2-1.5B-NER-Fintune - SwanLab
  • 模型:Modelscope
  • 数据集:chinese_ner_sft
  • SwanLab:https://swanlab.cn

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

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

相关文章

MySQL快速安装(mysql8.0.30区别之前yum安装)

目录 一.初始化环境并解压 二.创建程序用户管理 三.修改mysql目录和配置文件的权限 四.修改配置文件 五.设置环境变量&#xff0c;申明/宣告mysql命令便于系统识别 六.初始化数据库 七.设置系统识别&#xff0c;进行操作 八.初始化数据库密码 九.用户并设置密码 十.赋…

机器学习模型评估之校准曲线

模型校准曲线&#xff08;Calibration Curve&#xff09;&#xff0c;也称为可靠性曲线&#xff08;Reliability Curve&#xff09;或概率校准曲线&#xff08;Probability Calibration Curve&#xff09;&#xff0c;是一种评估分类模型输出概率准确性的图形工具。它可以帮助我…

STM32 串口通讯

使用STM32的串口通讯&#xff0c;接收串口助手的数据&#xff0c;并且将接收到的数据返回串口&#xff0c;重定义printf功能。 配置引脚信息 由于每次新建工程都需要配置信息&#xff0c;比较麻烦&#xff0c;好在STM32CubeIDE提供了导入.ioc文件的功能&#xff0c;可以帮我们…

达梦8 兼容MySQL语法支持非分组项作为查询列

MySQL 数据库迁移到达梦后&#xff0c;部分GROUP BY语句执行失败&#xff0c;报错如下&#xff1a; 问题原因&#xff1a; 对于Oracle数据库&#xff0c;使用GROUP BY时&#xff0c;SELECT中的非聚合列必须出现在GROUP BY后面&#xff0c;否则就会报上面的错误&#xff0c;达梦…

基于python+tkinter(Gui)的学生信息管理系统

博主介绍&#xff1a; 大家好&#xff0c;本人精通Java、Python、C#、C、C编程语言&#xff0c;同时也熟练掌握微信小程序、Php和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我有丰富的成品Java、Python、C#毕设项目经验&#xff0c;能够为学生提供各类…

[创业之路-119] :制造业企业的必备管理神器-ERP-主要功能模块说明与系统架构

目录 一、ERP功能的标准化 二、常见的ERP标准化功能 2.1 基础档案 2.2 供应链 2.3 人力资源管理 2.4 资产管理 2.5 生产制造 2.6 财务会计 2.7 管理会计 2.8 CRM客户管理管理 2.9 商业智能分析 三、常见的ERP软件供应商 国内ERP软件供应商 国外ERP软件供应商 四…

2024考古之还在用原始JDBC开发 手搓 案例 实现一个模块的增删改

JDBC案例 将来如果完成的话 就代表对JDBC里面的知识点全部融会贯通了 其实就是对数据的增删改查 我们入门做不出来前端的内容 很正常 准备环境 建表 use mybatis;create table tbl_brand (id int primary key auto_increment,brand_name varchar(20),company_name varcha…

在hue中使用ooize调度ssh任务无法执行成功,无法查看错误

ssh执行失败&#xff0c;但是hue没有给出明确的错误原因&#xff1a; 经过经验分析&#xff0c;原来是服务器上的sh文件用的是doc/window格式&#xff0c;需要使用notepad将格式改为unix之后就可以正常执行。 特此记录&#xff0c;避免遗忘知识点

图标设计新手手册:应用图标尺寸比例全解析

通常我们在App Store中寻找新的应用程序时&#xff0c;首先会快速扫描搜索栏中的一些关键词&#xff0c;然后选择感兴趣的应用程序&#xff0c;在选定的应用页面中查看具体信息&#xff0c;最后决定是否下载。在这一系列操作中&#xff0c;APP图标的大小比例是影响用户体验的关…

大腾智能,基于云原生的国产工业协同平台

大腾智能是一家基于云原生的国产工业软件与数字化协同平台&#xff0c;专注于推动企业数字化转型与升级&#xff0c;为企业提供一系列专业、高效的云原生数字化软件及方案&#xff0c;推动产品设计、生产及营销展示的革新&#xff0c;实现可持续发展。 大腾智能旗下产品 3D模型…

前端构建工具用得好,构建速度提升 10 倍

今天来盘点一下前端构建工具。 Turbopack Turbopack&#xff0c;由Vercel开源&#xff0c;是下一代高性能的JavaScript应用构建工具&#xff0c;目前用于 Next.js 中。Turbopack旨在通过革新JavaScript应用的打包流程来显著提升应用性能&#xff0c;它专注于缩短加载时间&…

小阿轩yx-Tomcat 部署及优化

小阿轩yx-Tomcat 部署及优化 Tomcat 概述 免费的、开放源代码的Web应用服务器Apache软件基金会(Apache Software Foundation)Jakarta项目中的一个核心项目由Apache、Sun和一些公司及个人共同开发而成深受Java爱好者的喜爱,并得到部分软件开发商的认可目前比较流行的Web应用服…

强化学习——基本概念

何为强化学习 机器学习的一大分支 强化学习&#xff08;Reinforcement Learning&#xff09;是机器学习的一种&#xff0c;它通过与环境不断地交互&#xff0c;借助环境的反馈来调整自己的行为&#xff0c;使得累计回报最大。强化学习要解决的是决策问题——求取当前状态下最…

sql资料库

1、distinct(关键词distinct用于返回唯一不同的值)&#xff1a;查询结果中去除重复行的关键字 select distinct(university) from user_profile select distinct university from user_profile distinct是紧跟在select后面的&#xff0c;不能在其他位置&#xff0c;不然就…

充电学习— 9、Typec Pd

GND&#xff1a;线缆接地 TX RX&#xff1a;数据流data传输&#xff0c;支持2.0 3.0 speed兼容 VBUS&#xff1a;线缆cable电源&#xff0c;bus power CC&#xff1a;电缆cable的连接、方向、角色检测和当前模式的配置通道&#xff1b; 有emark时&#xff0c; 一个成为VCONN&am…

Flutter【组件】按钮

简介 flutter 按钮组件。提供一种封装按钮组件的思路&#xff0c;并不支持过多的自定义属性。根据使用场景及设计规范进行封装&#xff0c;使用起来比较方便。 github地址&#xff1a;https://github.com/ThinkerJack/jac_uikit pub地址&#xff1a;https://pub.dev/package…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 密码解密(100分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

【多模态论文】CLIP(Contrastive Language-Image Pre-training)

论文&#xff1a;Learning Transferable Visual Models From Natural Language Supervision 链接&#xff1a;https://arxiv.org/abs/2103.00020 摘要 问题&#xff1a; 对预定的类别进行预测&#xff0c;这种有监督的训练形式受限于额外标记数据 。如何利用图像的原始文本来获…

MEME使用-motif分析(生物信息学工具-24)

01 背景 Motif分析是一种在生物信息学和计算生物学中广泛应用的技术&#xff0c;用于识别DNA、RNA或蛋白质序列中具有生物学功能的短保守序列模式&#xff08;motif&#xff09;。这些motif通常与特定的生物学功能相关&#xff0c;如DNA中的转录因子结合位点、RNA中的剪接位点…

VSCode调试python没有反应

原因&#xff1a;python的版本太低了&#xff0c;我的项目的python是3.5的&#xff0c;VSCode python debugger不支持低版本的python。 解决方法&#xff1a; debugging - debug python versions < 3.5 with vscode new debugger - Stack Overflow 安装支持低版本的Debug…