LLM - 使用 XTuner 指令微调 多模态大语言模型(InternVL2) 教程

news2024/12/23 18:17:05

欢迎关注我的CSDN:https://spike.blog.csdn.net/
本文地址:https://spike.blog.csdn.net/article/details/142528967

免责声明:本文来源于个人知识与公开资料,仅用于学术交流,欢迎讨论,不支持转载。


XTuner

XTuner 是高效、灵活且功能齐全的大语言模型和多模态模型微调工具,支持简单配置和轻量级运行,通过配置文件,封装大部分微调场景,降低微调的门槛,同时,支持多种预训练模型,如 InternVL 等,支持多种数据集格式,包括文本、图像或视频等。

相关GitHub:

  • InternVL:https://github.com/OpenGVLab/InternVL
  • XTuner:https://github.com/InternLM/xtuner

1. 准备模型

InternVL2-2B 为例,下载 HuggingFace 的 InternVL2-2B 模型:

export HF_ENDPOINT="https://hf-mirror.com"
pip install -U huggingface_hub hf-transfer
huggingface-cli download --token [your hf token] --resume-download  OpenGVLab/InternVL2-2B --local-dir InternVL2-2B

或 下载 ModelScope 的 InternVL2-2B 模型,推荐:

pip install modelscope
modelscope --token [your ms token] download  --model OpenGVLab/InternVL2-2B --local_dir InternVL2-2B

ModelScope 的 Token 是 SDK Token,在网站注册之后获取。

使用 Docker 构建环境:

docker run -it \
--privileged \
--network host \
--shm-size 32G \
--gpus all \
--ipc host \
--ulimit memlock=-1 \
--ulimit stack=67108864 \
--name xtuner \
-v [your disk]:[your disk] \
nvcr.io/nvidia/pytorch:23.03-py3 \
/bin/bash

注意:需要继续配置 pip 与 conda 环境

2. 配置 XTuner 环境

配置 XTuner 的 conda 环境,参考 GitHub - xtuner,即:

  • lmdeploy:用于部署 VL 大模型
  • streamlit:用于处理数据

即:

conda create --name xtuner-env python=3.10 -y
conda activate xtuner-env

pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install lmdeploy==0.5.3 -i https://pypi.tuna.tsinghua.edu.cn/simple

apt install libaio-dev
pip show transformers		# 4.39.3
pip show streamlit			# 1.36.0
pip show lmdeploy				# 0.5.3

测试 PyTorch 可用,即:

python

import torch
print(torch.__version__)  
print(torch.cuda.is_available())  
exit()

配置 XTuner 库,建议使用源码安装:

git clone https://github.com/InternLM/xtuner.git
git checkout v0.1.23
pip install -e '.[deepspeed]'

xtuner version
xtuner help

3. 准备 VL 数据集

测试使用的 HuggingFace 数据集: zhongshsh/CLoT-Oogiri-GO,冷笑话数据集

export HF_ENDPOINT="https://hf-mirror.com"
pip install -U huggingface_hub hf-transfer
huggingface-cli download --token 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 --repo-type dataset --resume-download  zhongshsh/CLoT-Oogiri-GO --local-dir CLoT-Oogiri-GO

使用 I2T (Image to Text) 的数据集进行训练,只选择图像到中文的数据。

核心数据内容 cn.jsonl 是中文注释,images 是图像内容:

├── [8.8M]  cn.jsonl
├── [3.5M]  en.jsonl
├── [1.5M]  images
├── [1.3G]  images.zip

数据样本如下:

{"text": "『住手!现在的你还不是那家伙的对手!先撤吧!!", "question": null, "star": null, "type": "I2T", "image": "007aPnLRgy1hb39z0im50j30ci0el0wm"}

在构建数据集时,如果 question 是空,替换默认提示文字:请你根据这张图片,讲一个脑洞大开的梗。

安装图像处理的 Python 包,即:

pip install datasets matplotlib Pillow timm -i https://pypi.tuna.tsinghua.edu.cn/simple

处理 CLoT-Oogiri-GO 数据集,转换成 XTuner 的格式,参考:

{
  "id": 0,
  "image": "images/00000000.jpg",
  "width": 897,
  "height": 1152,
  "conversations": [
    {
      "from": "human",
      "value": "<image>\nCan you extract any readable text from the image?"
    },
    {
      "from": "gpt",
      "value": "Dares Wins Vol. 5 Tommy's Heroes Vol. 6: For Tomorrow Vol. 7: Closing Time miniseries. Clark Kent is being interviewed about Superman's connection to notorious killer Tommy Monaghan. Taking the conversation..."
    }
  ]
}

参考 InternVL - Chat Data Format 的 Single-Image Data 格式。

处理之后的数据集样本数量是 39736,文件是 Json 格式,即ex_cn.json,格式如下:

[
# ...
    {
        "id": 2,
        "image": "images/007aPnLRgy1hb39z0im50j30ci0el0wm.jpg",
        "width": 450,
        "height": 404,
        "conversations": [
            {
                "from": "human",
                "value": "<image>\n请你根据这张图片,讲一个脑洞大开的梗。"
            },
            {
                "from": "gpt",
                "value": "『住手!现在的你还不是那家伙的对手!先撤吧!!"
            }
        ]
    },
#...
]

4. 指令微调(Finetune)

修改训练配置 xtuner/configs/internvl/v2/internvl_v2_internlm2_2b_qlora_finetune_my.py,即:

  • data_root :数据集 的位置
  • data_path :处理之后,符合 xtuner 格式的 json 数据集
  • image_folder :图像路径,注意,不包括 images,默认会查找 image_folder/images 文件夹
  • max_length :最大 Token 长度
  • prompt_template :提示词模版,参考xtuner/utils/templates.py
  • batch_size:batch size 影响显存占用
  • accumulative_counts:梯度累计,影响显存占用
  • dataloader_num_workers:数据加载,影响速度
  • max_epochs:运行的最大 epoch

即:

# Model
# path = 'OpenGVLab/InternVL2-2B'
path = "llm/InternVL2-2B"

# Data
# data_root = './data/llava_data/'
# data_path = data_root + 'LLaVA-Instruct-150K/llava_v1_5_mix665k.json'
# image_folder = data_root + 'llava_images'
data_root = 'llm/CLoT-Oogiri-GO/'
data_path = data_root + 'ex_cn.json'
image_folder = data_root
prompt_template = PROMPT_TEMPLATE.internlm2_chat
max_length = 8192

# Scheduler & Optimizer
batch_size = 8  # per_device
accumulative_counts = 2
dataloader_num_workers = 4
max_epochs = 1

其中 PROMPT_TEMPLATE.internlm2_chat 如下:

internlm2_chat=dict(
    SYSTEM='<|im_start|>system\n{system}<|im_end|>\n',
    INSTRUCTION=('<|im_start|>user\n{input}<|im_end|>\n'
                 '<|im_start|>assistant\n'),
    SUFFIX='<|im_end|>',
    SUFFIX_AS_EOS=True,
    SEP='\n',
    STOP_WORDS=['<|im_end|>']),

运行训练脚本:

CUDA_VISIBLE_DEVICES=2,3,4,5 NPROC_PER_NODE=4 xtuner train xtuner/configs/internvl/v2/internvl_v2_internlm2_2b_qlora_finetune_my.py --work-dir xtuner/outputs/internvl_v2_internlm2_2b_qlora_finetune_my --deepspeed deepspeed_zero1

CUDA_VISIBLE_DEVICES 的卡数,需要与 NPROC_PER_NODE 的数量一致。

运行日志,包括:

  • lr 学习率
  • eta 预估的训练时间
  • time 单步运行时间
  • data_time 数据处理时间
  • memory 现存占用
  • loss 损失函数

即:

dynamic ViT batch size: 56, images per sample: 7.0, dynamic token length: 3409
09/25 15:07:26 - mmengine - INFO - Iter(train) [  10/1242]  lr: 5.0002e-06  eta: 3:58:14  time: 11.6030  data_time: 0.0238  memory: 37911  loss: 5.6273
09/25 15:08:52 - mmengine - INFO - Iter(train) [  20/1242]  lr: 1.0556e-05  eta: 3:25:19  time: 8.5596  data_time: 0.0286  memory: 37906  loss: 5.7473
09/25 15:10:20 - mmengine - INFO - Iter(train) [  30/1242]  lr: 1.6111e-05  eta: 3:14:46  time: 8.7649  data_time: 0.0290  memory: 37850  loss: 5.1485
09/25 15:11:49 - mmengine - INFO - Iter(train) [  40/1242]  lr: 2.0000e-05  eta: 3:09:50  time: 8.9780  data_time: 0.0293  memory: 37850  loss: 5.0301
09/25 15:13:18 - mmengine - INFO - Iter(train) [  50/1242]  lr: 1.9995e-05  eta: 3:05:54  time: 8.8847  data_time: 0.0283  memory: 37803  loss: 5.0017
09/25 15:14:42 - mmengine - INFO - Iter(train) [  60/1242]  lr: 1.9984e-05  eta: 3:01:05  time: 8.3671  data_time: 0.0271  memory: 37691  loss: 4.7469
09/25 15:17:02 - mmengine - INFO - Iter(train) [  70/1242]  lr: 1.9965e-05  eta: 3:13:06  time: 14.0434  data_time: 0.0302  memory: 37892  loss: 4.9295
09/25 15:18:25 - mmengine - INFO - Iter(train) [  80/1242]  lr: 1.9940e-05  eta: 3:07:30  time: 8.2537  data_time: 0.0324  memory: 37757  loss: 4.8976
09/25 15:19:43 - mmengine - INFO - Iter(train) [  90/1242]  lr: 1.9908e-05  eta: 3:01:51  time: 7.7941  data_time: 0.0348  memory: 37891  loss: 4.7055
09/25 15:20:55 - mmengine - INFO - Iter(train) [ 100/1242]  lr: 1.9870e-05  eta: 2:56:03  time: 7.2490  data_time: 0.0288  memory: 37729  loss: 4.8404
dynamic ViT batch size: 40, images per sample: 5.0, dynamic token length: 3405
09/25 15:22:13 - mmengine - INFO - Iter(train) [ 110/1242]  lr: 1.9824e-05  eta: 2:51:54  time: 7.7299  data_time: 0.0280  memory: 37869  loss: 5.1263
09/25 15:23:29 - mmengine - INFO - Iter(train) [ 120/1242]  lr: 1.9772e-05  eta: 2:48:05  time: 7.6363  data_time: 0.0345  memory: 37937  loss: 4.8139
09/25 15:24:46 - mmengine - INFO - Iter(train) [ 130/1242]  lr: 1.9714e-05  eta: 2:44:44  time: 7.6952  data_time: 0.0355  memory: 37875  loss: 5.0916

输出目录 xtuner/outputs/internvl_v2_internlm2_2b_qlora_finetune_my/20240925_150518

  • 20240925_150518.log :日志缓存
  • scalars.json:运行 loss 相关的日志
  • config.py:配置缓存

即:

├── [ 58K]  20240925_150518.log
└── [4.0K]  vis_data
    ├── [ 15K]  20240925_150518.json
    ├── [4.6K]  config.py
    └── [ 15K]  scalars.json

5. LMDeploy 部署模型

LMDeploy 工具用于部署 VL 模型,注意,如果需要 ModelScope:

pip install modelscope
export LMDEPLOY_USE_MODELSCOPE=True

安装环境:

pip install lmdeploy==0.5.3 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install datasets matplotlib Pillow timm -i https://pypi.tuna.tsinghua.edu.cn/simple

模型评估代码:

from lmdeploy import pipeline
from lmdeploy.vl import load_image

import os
os.environ["CUDA_VISIBLE_DEVICES"] = "1"


pipe = pipeline('llm/InternVL2-2B/')

image = load_image('llm/CLoT-Oogiri-GO/007aPnLRgy1hb39z0im50j30ci0el0wm.jpg')
response = pipe(('请你根据这张图片,讲一个脑洞大开的梗。', image))
print(response.text)

注意:如果是 Jupyter 运行,注意清空输出,避免重复加载模型,报错。

测试 InternVL2 的图像效果:

Warning: Flash attention is not available, using eager attention instead.                                               
[WARNING] gemm_config.in is not found; using default GEMM algo

这张图片中的猫咪看起来非常可爱,甚至有点滑稽。让我们来脑洞大开一下,看看这个梗会如何发展:

**梗名:“猫咪的愤怒表情”**

**梗描述:**
1. **场景设定**:猫咪站在一个高处,看起来像是在观察周围的环境。
2. **猫咪的表情**:猫咪张大嘴巴,眼睛瞪得大大的,似乎在发出愤怒的吼声。
3. **背景细节**:背景中有一个楼梯,猫咪似乎在楼梯上,可能是在观察楼上的人或物。

**梗发展**:
- **猫咪的愤怒**:猫咪的愤怒表情非常夸张,仿佛它真的在生气,甚至可能在向人发出警告。
- **猫咪的愤怒原因**:猫咪的愤怒可能与它所处的环境有关,比如楼上的人或物让它感到不安,或者它觉得自己的领地受到了侵犯。
- **猫咪的愤怒反应**:猫咪可能会通过大声吼叫、挠痒痒、甚至跳起来来表达它的愤怒。

**梗应用**:
- **搞笑图片**:这张图片可以用来制作搞笑的社交媒体帖子,猫咪的愤怒表情非常生动,能够引起大家的共鸣和笑声。
- **猫咪行为研究**:通过观察猫咪的愤怒表情,研究者可以更好地了解猫咪的情感表达方式,从而更好地照顾和训练它们。

**总结**:
这张图片通过夸张的猫咪愤怒表情,引发了人们对猫咪行为的思考和讨论。它不仅展示了猫咪的可爱,还通过幽默的方式引发了更多关于猫咪行为和情感的有趣话题。

6. 其他

CLoT-Oogiri-GO 数据集转换成 XTuner 格式的脚本:

import json
import os

import PIL.Image as Image
from tqdm import tqdm

from root_dir import DATA_DIR


class OogirlGoProcessor(object):
    """
    将 oogirl_go 数据集转换为 xtuner 格式
    """
    def __init__(self):
        pass

    @staticmethod
    def process(json_path, output_path):
        print(f"[Info] json_path: {json_path}")
        print(f"[Info] output_path: {output_path}")
        with open(json_path, "r", encoding="utf-8") as f:
            lines = f.readlines()
        r_id = 0
        dataset_dir = os.path.dirname(json_path)
        sample_list = []
        for line in tqdm(lines):
            data = json.loads(line)
            # print(data)
            img_name = data["image"]
            r_image = f"images/{img_name}.jpg"
            img_path = os.path.join(dataset_dir, r_image)
            if not os.path.exists(img_path):
                continue
            img = Image.open(img_path)
            r_width, r_height = img.size
            # print(f"[Info] w: {r_width}, h: {r_height}")
            r_human = data["question"]
            if not r_human:
                r_human = "请你根据这张图片,讲一个脑洞大开的梗。"
            r_gpt = data["text"]
            if not r_gpt:
                continue
            sample_dict = {
                "id": r_id,
                "image": r_image,
                "width": r_width,
                "height": r_height,
                "conversations": [
                    {
                        "from": "human",
                        "value": f"<image>\n{r_human}"
                    },
                    {
                        "from": "gpt",
                        "value": r_gpt
                    }
                ]
            }
            sample_list.append(sample_dict)
            r_id += 1
        print(f"[Info] 全部样本数量: {r_id}")
        with open(output_path, "w", encoding="utf-8") as f:
            json.dump(sample_list, f, ensure_ascii=False, indent=4)


def main():
    json_path = os.path.join(DATA_DIR, "CLoT-Oogiri-GO", "cn.jsonl")
    output_path = os.path.join(DATA_DIR, "CLoT-Oogiri-GO", "ex_cn.json")
    ogp = OogirlGoProcessor()
    ogp.process(json_path, output_path)


if __name__ == '__main__':
    main()

BugFix1:ImportError: libGL.so.1: cannot open shared object file: No such file or directory

解决方案:

apt-get update && apt-get install ffmpeg libsm6 libxext6  -y

参考:

  • InternVL - 垂直领域场景微调实践
  • xTuner - Quickstart
  • StackOverflow - ImportError: libGL.so.1: cannot open shared object file: No such file or directory

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

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

相关文章

国庆节到了,扣子智能体coze画板功能实现贺卡编辑智能体自动添加logo和二维码,让海报品牌化

大家好,我是Shelly,一个专注于输出AI工具和科技前沿内容的AI应用教练,体验过300+款以上的AI应用工具。关注科技及大模型领域对社会的影响10年+。关注我一起驾驭AI工具,拥抱AI时代的到来。 自媒体时代,不管是一个人、一个团队还是一家公司,都是一个IP。那么添加品牌的标志…

JavaWeb校园二手交易平台

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 spring-mybatis.xml3.5 spring-mvc.xml3.5 login.jsp 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优…

AI大模型助力数据消费,构建数据飞轮科学、高效的体系

随着互联网的技术高速发展&#xff0c;越来越多的应用层出不穷&#xff0c;伴随着数据应用的需求变多&#xff0c;为快速响应业务需求&#xff0c;很多企业在初期没有很好的规划的情况下&#xff0c;存在不同程度的烟囱式的开发模式&#xff0c;这样会导致企业不同业务线的数据…

Java Map类

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;Java 目录 &#x1f449;&#x1f3fb;map1. 常见的实现2. 主要方法2.1. put(K key, V value)2.2. get(Object key)2.3. remove(Object key)2.4. containsKe…

西部移动硬盘怎么恢复数据?4种详细且实用的方法

面对西部移动硬盘数据丢失的问题&#xff0c;用户往往感到焦虑和无助。本文将为您提供一系列详细且实用的数据恢复方法&#xff0c;帮助您轻松应对数据丢失的挑战&#xff0c;重拾宝贵信息。 图片来源于网络&#xff0c;如有侵权请告知 一、西部移动硬盘数据丢失原因 西部移动…

生成式AI在电商场景的应用、前景与挑战,零基础入门到精通,收藏这一篇就够了

编者按 百舸争流的AI时代&#xff0c;“AI”行动在千行百业迅速开展。电商是一个重要场景&#xff0c;**据阿里调研&#xff0c;在电商平台&#xff0c;约30%受访商家已经使用生成式AI&#xff0c;成为生成式AI技术普惠的最佳试验场之一。**目前&#xff0c;已使用生成式AI的商…

828华为云征文|华为云Flexus云服务器X实例之openEuler系统下部署经典扫雷小游戏

828华为云征文&#xff5c;华为云Flexus云服务器X实例之openEuler系统下部署经典扫雷小游戏 前言一、Flexus云服务器X实例介绍1.1 Flexus云服务器X实例简介1.2 Flexus云服务器X实例特点1.3 Flexus云服务器X实例使用场景 二、本次实践介绍2.1 本次实践简介2.2 扫雷小游戏简介2.3…

KPaaS平台用户权限管理系统方案之表单设计统一单据制作与授权

不同的业务系统各自独立运行&#xff0c;需要分别进行授权操作&#xff0c;这不仅繁琐耗时&#xff0c;还容易出现错误和不一致的情况&#xff0c;导致企业在多系统用户权限角色管理中常常陷入困境&#xff0c;那么&#xff0c;有没有一种高效、便捷的解决方案呢&#xff1f; …

关于预处理详解,#define,宏的使用以及命名 函数与宏的区别详细对比

预定义符号 C语⾔设置了⼀些预定义符号&#xff0c;可以直接使⽤&#xff0c;预定义符号也是在预处理期间处理的 __FILE__ //进⾏编译的源⽂件 __LINE__ //⽂件当前的⾏号 __DATE__ //⽂件被编译的⽇期 __TIME__ //⽂件被编译的时间 __STDC__ //如果编译器遵循ANSI C&#xff…

汉诺塔的理解

数学思想——归纳推理&#xff08;不是反证法&#xff09; 为了方便&#xff0c;我把塔叫做牌&#xff0c;最左边的是从大到小&#xff08;底部开始&#xff09;放置的的牌堆。 数字的那一列是递归调用&#xff0c;右边长度不一的箭头是&#xff0c;数字阶段向下调用方法的情况…

稀土抗菌剂在涂料中应用的神奇表现

稀土抗菌剂的抗菌抑菌机理有四个层面:一是稀土化合物与细菌表面静电结合&#xff0c;造成直接的杀灭二是基于稀土的光催化半导体特性&#xff0c;通过光生氧自由基ROS机理杀灭细菌;三是稀土化合物破坏细胞膜通透性&#xff0c;造成破损导致细胞质流出杀灭细菌;四是稀土离子跨膜…

C标准库<string.h>-str、strn开头的函数

char *strcat(char *dest, const char *src) 函数功能 strcat 函数用于将一个字符串追加到另一个字符串的尾部。 参数解释 dest&#xff1a;指向目标字符串的指针&#xff0c;这个字符串的尾部将被追加 src 字符串的内容。src&#xff1a;指向源字符串的指针&#xff0c;其…

最精简的VScode Verilog RTL开发环境搭建教程

【2024-9月更新】最精简的VScode Verilog RTL开发环境搭建教程 文章目录 【2024-9月更新】最精简的VScode Verilog RTL开发环境搭建教程一、官网下载VScode二、登录账号同步三、安装配置拓展插件1.Verilog-HDL/systemVerilog拓展2.安装Universal Ctags● Windows系统安装univer…

(附源码) Springboot 飞速物流管理平台78584

摘要 受疫情的影响&#xff0c;很多城市处于静默的状态&#xff0c;导致店铺很多店铺都处于关闭的状态&#xff0c;给商家带来了极大的损失&#xff0c;很多商家为了减少损失都通过线上进行销售&#xff0c;比如直播、微商等&#xff1b;同时对于消费者来说&#xff0c;网上购买…

【Redis】分布式锁之 Redission

一、基于setnx实现的分布式锁问题 重入问题&#xff1a;获得锁的线程应能再次进入相同锁的代码块&#xff0c;可重入锁能防止死锁。例如在HashTable中&#xff0c;方法用synchronized修饰&#xff0c;若在一个方法内调用另一个方法&#xff0c;不可重入会导致死锁。而synchroni…

mysql练习题使用的表

dept(部门表):部门编号&#xff0c;部门名字&#xff0c;部门地点 salgrode工资等级表&#xff1a;等级&#xff0c;最高工资&#xff0c;最低工资 emp表&#xff1a;员工编号&#xff0c;员工名字&#xff0c;工作&#xff0c;领导编号MGR&#xff0c;入职时间&#xff0c;工…

Spring Boot 整合MyBatis-Plus 实现多层次树结构的异步加载功能

文章目录 1&#xff0c;前言2&#xff0c;什么是多层次树结构&#xff1f;3&#xff0c;异步加载的意义4&#xff0c;技术选型与实现思路5&#xff0c;具体案例5.1&#xff0c;项目结构5.2&#xff0c;项目配置&#xff08;pom.xml&#xff09;5.3&#xff0c;配置文件&#xf…

c++难点核心笔记(二)

系列文章目录 c难点&核心笔记(一) 继续接着上一章记录的重点内容包括函数&#xff0c;类和对象&#xff0c;指针和引用&#xff0c;C对象模型和this指针等内容&#xff0c;继续给大家分享&#xff01;&#xff01; 文章目录 系列文章目录友元全局函数做友元类做友元成员函…

国庆节怎么利用PHP发送文字短信

国庆节作为中国重要的法定节假日之一&#xff0c;不仅是全民欢庆的时刻&#xff0c;也是商家们进行促销活动的黄金时期。发送营销短信成为许多商家吸引顾客、提高销量的重要手段。 支持免费对接试用乐讯通PaaS平台 找好用的短信平台,选择乐讯通,短信群发|短信平台|群发短信软件…

分布式事务(1)

1.分布式事务 首先我们看看项目中的下单业务整体流程&#xff1a; 由于订单、购物车、商品分别在三个不同的微服务&#xff0c;而每个微服务都有自己独立的数据库&#xff0c;因此下单过程中就会跨多个数据库完成业务。而每个微服务都会执行自己的本地事务&#xff1a; 交易服…