当前位置:首页> AI教程> ChatGLM-6B大模型在昇腾910加速卡上模型训练详解

ChatGLM-6B大模型在昇腾910加速卡上模型训练详解

释放双眼,带上耳机,听听看~!
本文详细介绍了在昇腾910加速卡上针对ChatGLM-6B大模型进行模型训练的过程,包括环境搭建、数据准备及格式转换等,对于对AI算力和模型训练感兴趣的读者具有很高的参考价值。

本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!

随着 ChatGPT 的现象级走红,引领了AI大模型时代的变革,从而导致 AI 算力日益紧缺。与此同时,中美贸易战以及美国对华进行AI芯片相关的制裁导致 AI 算力的国产化适配势在必行。之前讲述了基于昇腾910使用ChatGLM-6B进行模型推理,本文将讲述针对ChatGLM-6B大模型在昇腾910加速卡上面进行模型训练,为了文章具有更好的阅读体验,具体代码放置在GitHub:llm-action

环境搭建

  • 操作系统版本/架构:EulerOS release 2.0 (SP8)/aarch64
  • NPU:8卡 910 ProB 32G
  • Python:3.7
  • NPU 驱动:23.0.rc1,下载
  • NPU 固件:6.3.0.1.241,下载
  • CANN 工具包:6.3.RC1.alpha003,下载
  • MindSpore:2.0.0-rc1,下载
  • MindFormers:dev ( commit id: 6f2b0b8 )

运行环境与基于昇腾910使用ChatGLM-6B进行模型推理一致,这里就不再赘述了。

数据准备及数据格式转换

下面以 ADGEN (广告生成) 数据集为例来介绍微调的具体使用。

ADGEN 数据集为根据输入(content)生成一段广告词(summary),具体格式如下所示:

{
    "content": "类型#上衣*版型#宽松*版型#显瘦*图案#线条*衣样式#衬衫*衣袖型#泡泡袖*衣款式#抽绳",
    "summary": "这件衬衫的款式非常的宽松,利落的线条可以很好的隐藏身材上的小缺点,穿在身上有着很好的显瘦效果。领口装饰了一个可爱的抽绳,漂亮的绳结展现出了十足的个性,配合时尚的泡泡袖型,尽显女性甜美可爱的气息。"
}

请从官网下载 ADGEN 数据集或通过此链接下载,并将其解压到 AdvertiseGen 目录。

tar -zxvf AdvertiseGen.tar.gz

查看数据集大小:

> wc -l AdvertiseGen/*
> 1070 AdvertiseGen/dev.json
> 114599 AdvertiseGen/train.json
> 115669 total

使用 mindformers/tools/dataset_preprocess/glm/adgen_dataset.py 脚本将数据集处理成mindrecord格式。

执行命令生成训练数据集:

cd mindformers/tools/dataset_preprocess/glm
python adgen_dataset.py 
    --input_file /root/workspace/data/AdvertiseGen/train.json 
    --vocab_file /root/workspace/model/checkpoint_download/glm/ice_text.model
    --output_file /root/workspace/data/AdvertiseGen-ms/train_0604_128.mindrecord 
    --max_source_length 64 
    --max_target_length 64 
    --mode train

运行过程:

100%|##########################################################################| 114599/114599 [11:25<00:00, 167.17it/s]
2023-06-20 14:26:59,086 - mindformers - INFO - Wrote 114599 total instances

执行命令生成评估数据集:

cd mindformers/tools/dataset_preprocess/glm
python adgen_dataset.py 
    --input_file /root/workspace/data/AdvertiseGen/dev.json 
    --vocab_file /root/workspace/model/checkpoint_download/glm/ice_text.model 
    --output_file /root/workspace/data/AdvertiseGen-ms/eval_0604_256.mindrecord 
    --max_source_length 256 
    --max_target_length 256 
    --mode eval

运行过程:

100%|##############################################################################| 1070/1070 [00:01<00:00, 772.11it/s]
2023-06-20 14:17:20,492 - mindformers - INFO - Wrote 1070 total instances    

转换后的文件如下所示:

> tree AdvertiseGen-ms/
AdvertiseGen-ms/
├── eval_0711_256.mindrecord
├── eval_0711_256.mindrecord.db
├── train_0604_128.mindrecord
└── train_0604_128.mindrecord.db

其中,*.mindrecord为数据文件,*.mindrecord.db为索引文件。

补充说明

一个MindRecord文件由数据文件和索引文件组成:

  • 数据文件:包含文件头、标量数据页、块数据页,用于存储用户归一化后的训练数据,且单个MindRecord文件建议小于20G,用户可将大数据集进行分片存储为多个MindRecord文件。
  • 索引文件:包含基于标量数据(如:图像Label、图像文件名等)生成的索引信息,用于方便的检索、统计数据集信息。

ChatGLM-6B大模型在昇腾910加速卡上模型训练详解

数据文件主要由以下几个关键部分组成:

  • 文件头:文件头主要用来存储文件头大小、标量数据页大小、块数据页大小、Schema信息、索引字段、统计信息、文件分区信息、标量数据与块数据对应关系等,是MindRecord文件的元信息。
  • 标量数据页:标量数据页主要用来存储整型、字符串、浮点型数据,如:图像的Label、图像的文件名、图像的长宽等信息,即适合用标量来存储的信息会保存在这里。
  • 块数据页:块数据页主要用来存储二进制串、Numpy数组等数据,如:二进制图像文件本身、文本转换成的字典等。

模型准备及模型格式转换

模型准备及模型格式转换请参考基于昇腾910使用ChatGLM-6B进行模型推理,这里也不再赘述。

代码准备

模型训练的代码直接使用安装mindformers的代码即可,由于版本迭代很快,为了避免代码不兼容的情况,建议保持同样的commit id。

git clone https://gitee.com/mindspore/mindformers.git
cd mindformers/
git checkout 6f2b0b8

网络配置

当进行分布式训练时,需要通过昇腾软件中的HCCN Tool工具配置device的网卡IP,用于多个device间通信以实现网络模型参数的同步更新。

对于配置的要求如下:

  • AI Server中的第0/4,1/5,2/6,3/7号网卡需处于同一网段,第0/1/2/3号网卡在不同网段,第4/5/6/7号网卡在不同网段。
  • 对于集群场景,各AI Server对应的位置的device需处于同一网段,例如:AI Server1和AI Server2的0号网卡需处于同一网段,AI Server1 和 AI Server2 的1号网卡需处于同一网段。IP地址需要根据实际情况修改。

先检查/etc/下是否有hccn.conf配置文件,若不存在,需先执行如下命令进行设置:

hccn_tool -i 0 -ip -s address 192.168.100.101 netmask 255.255.255.0
hccn_tool -i 1 -ip -s address 192.168.101.101 netmask 255.255.255.0
hccn_tool -i 2 -ip -s address 192.168.102.101 netmask 255.255.255.0
hccn_tool -i 3 -ip -s address 192.168.103.101 netmask 255.255.255.0
hccn_tool -i 4 -ip -s address 192.168.100.100 netmask 255.255.255.0
hccn_tool -i 5 -ip -s address 192.168.101.100 netmask 255.255.255.0
hccn_tool -i 6 -ip -s address 192.168.102.100 netmask 255.255.255.0
hccn_tool -i 7 -ip -s address 192.168.103.100 netmask 255.255.255.0

参数说明:

  • -i 0:指定设备ID。
  • -netdetect:指定网络检测对象IP属性。
  • -s address:表示设置属性为IP地址。192.168.100.101表示服务器的设备0的ip地址。

查看/etc/hccn.conf文件:

> cat /etc/hccn.conf
address_0=192.168.100.101
netmask_0=255.255.255.0
...
address_7=192.168.103.100
netmask_7=255.255.255.0

软件、模型、数据、代码以及网络环境准备好之后,接下来进行模型训练。

模型训练

下面分别对模型全量微调以及LoRA微调进行讲解。

全量微调

首先,生成用于Ascend芯片分布式通信的芯片资源信息配置文件(RANK_TABLE_FILE)。Ascend HCCL 的 RANK_TABLE_FILE 文件提供Ascend分布式训练作业的集群信息。下面生成8卡的rank_table_file文件。

> python ./mindformers/tools/hccl_tools.py --device_num "[0,8)"

start ./mindformers/tools/hccl_tools.py
visible_devices:['0', '1', '2', '3', '4', '5', '6', '7']
server_id:192.168.1.196
device_num_list: [0, 1, 2, 3, 4, 5, 6, 7]
rank_id:0, device_id:0, device_ip:192.168.100.101
...
rank_id:7, device_id:7, device_ip:192.168.103.100
Completed: hccl file was save in : /root/workspace/code/mindformers/hccl_8p_01234567_192.168.1.196.json

修改配置文件(configs/glm/run_glm_6b_finetune.yaml),设置模型权重路径、训练和评估数据集路径等,目前使用张量并行为8进行训练。

load_checkpoint: 'https://b2.7b2.com/root/workspace/model/chatglm-convert/ms_glm_6b.ckpt'
train_dataset: &train_dataset
  data_loader:
    type: MindDataset
    dataset_dir: "https://b2.7b2.com/root/workspace/data/AdvertiseGen-ms/train_0604_128.mindrecord"
    shuffle: True
eval_dataset: &eval_dataset
  data_loader:
    type: MindDataset
    dataset_dir: "https://b2.7b2.com/root/workspace/data/AdvertiseGen-ms/eval_0604_256.mindrecord"
    shuffle: True
parallel_config:
  data_parallel: 1
  model_parallel: 8
  pipeline_stage: 1
  expert_parallel: 1

启动模型训练前,可以通过设置多卡间等待时间(HCCL_CONNECT_TIMEOUT)来避免长时间没收到其他卡的消息而失败的问题,默认为300(秒)。

export HCCL_CONNECT_TIMEOUT=1200

scripts目录下启动全量微调训练任务:

# cd /root/workspace/code/mindformers/scripts
# Usage Help: bash run_distribute.sh [RANK_TABLE_FILE] [CONFIG_PATH] [DEVICE_RANGE] [RUN_STATUS]
> bash run_distribute.sh /root/workspace/code/mindformers/hccl_8p_01234567_192.168.1.196.json ../configs/glm/run_glm_6b_finetune.yaml '[0,8]' finetune

start training for rank 0, device 0
...
start training for rank 7, device 7

参数说明

  • RANK_TABLE_FILE: hccl_tools.py生成的分布式json文件。
  • CONFIG_PATH: 配置文件。
  • DEVICE_RANGE: 单机分布式卡的范围,如:[0,8]为8卡分布式,不包含8本身。
  • RUN_STATUS: 任务运行状态,支持关键:train、eval、finetune、predict。

模型训练启动成功,输出目录的结构如下所示。

output/
├── checkpoint
├── log
└── strategy

其中,checkpoint文件夹放置权重文件,log文件夹方式日志文件,strategy文件夹放置模型切分策略文件。

部分训练日志如下所示:

...
[INFO] 2023-07-11 10:38:43,568 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/trainer/base_trainer.py:550] training_process: .........Build Running Wrapper From Config For Train..........
[INFO] 2023-07-11 10:38:43,568 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/trainer/base_trainer.py:391] create_model_wrapper: .........Build Model Wrapper for Train From Config..........
[INFO] 2023-07-11 10:38:43,582 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/trainer/base_trainer.py:562] training_process: .........Starting Init Train Model..........
[INFO] 2023-07-11 10:38:43,583 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/trainer/base_trainer.py:581] training_process: .........Build Callbacks For Train..........
[INFO] 2023-07-11 10:38:43,583 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/trainer/base_trainer.py:400] create_callbacks: .........Build Callbacks for Train From Config..........
[INFO] 2023-07-11 10:38:43,584 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:340] __init__: Integrated_save is changed to False when using auto_parallel.
[INFO] 2023-07-11 10:38:43,585 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/trainer/base_trainer.py:609] training_process: .........Starting Training Model..........
[INFO] 2023-07-11 10:38:43,585 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/trainer/base_trainer.py:610] training_process: .........Model Compiling, Please Wait a Moment...........
[INFO] 2023-07-11 10:47:36,427 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:269] print_output_info: Epoch:[  1/  1], step:[    4/  125], loss:[2.244/2.244], time:507844.205 ms, lr:[0.], overflow cond: True, loss_scale: 268435460.0
[INFO] 2023-07-11 10:47:37,342 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:146] epoch_end: Per sink_size step time: 533756.177 ms, per step time: 133439.044 ms, avg loss: 2.244
[INFO] 2023-07-11 10:47:44,861 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:269] print_output_info: Epoch:[  1/  1], step:[    8/  125], loss:[2.499/2.499], time:7480.938 ms, lr:[0.], overflow cond: True, loss_scale: 16777216.0
[INFO] 2023-07-11 10:47:44,874 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:146] epoch_end: Per sink_size step time: 7518.224 ms, per step time: 1879.556 ms, avg loss: 2.499
...
[INFO] 2023-07-11 10:48:35,199 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:146] epoch_end: Per sink_size step time: 1958.791 ms, per step time: 489.698 ms, avg loss: 2.091
[INFO] 2023-07-11 10:48:37,162 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:269] print_output_info: Epoch:[  1/  1], step:[  116/  125], loss:[2.220/2.220], time:1951.612 ms, lr:[2.4499998e-06], overflow cond: False, loss_scale: 16384.0
[INFO] 2023-07-11 10:48:37,163 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:146] epoch_end: Per sink_size step time: 1963.915 ms, per step time: 490.979 ms, avg loss: 2.220
[INFO] 2023-07-11 10:48:39,125 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:269] print_output_info: Epoch:[  1/  1], step:[  120/  125], loss:[2.092/2.092], time:1953.753 ms, lr:[2.5499999e-06], overflow cond: False, loss_scale: 16384.0
[INFO] 2023-07-11 10:48:39,126 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:146] epoch_end: Per sink_size step time: 1962.049 ms, per step time: 490.512 ms, avg loss: 2.092
[INFO] 2023-07-11 10:48:41,083 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:269] print_output_info: Epoch:[  1/  1], step:[  124/  125], loss:[2.346/2.346], time:1949.995 ms, lr:[2.65e-06], overflow cond: False, loss_scale: 16384.0
[INFO] 2023-07-11 10:48:41,084 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/core/callback/callback.py:146] epoch_end: Per sink_size step time: 1958.268 ms, per step time: 489.567 ms, avg loss: 2.346
[INFO] 2023-07-11 10:49:26,307 [/root/workspace/code/mindformers/scripts/mf_parallel1/mindformers/trainer/base_trainer.py:616] training_process: .........Training Over!.............

LoRA 简述

针对大模型来说,模型全量微调需要耗费很多的内存;除此之外,模型全量微调还会损失多样性,存在灾难性遗忘的问题,因此,催生了各种高效微调技术。而LoRA,无疑是如今最流行的高效微调方法之一。

LoRA(论文:LoRA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS),该方法的核心思想就是通过低秩分解来模拟参数的改变量,从而以极小的参数量来实现大模型的间接训练。

在涉及到矩阵相乘的模块,在原始的PLM旁边增加一个新的通路,通过前后两个矩阵A,B相乘,第一个矩阵A负责降维,第二个矩阵B负责升维,中间层维度为r,从而来模拟所谓的本征秩(intrinsic rank)。

ChatGLM-6B大模型在昇腾910加速卡上模型训练详解

更加详细的介绍可参考之前的文章:大模型参数高效微调技术原理综述(五)-LoRA、AdaLoRA、QLoRA

LoRA 微调

首先,生成用于Ascend芯片分布式通信的芯片资源信息配置文件,由于仅使用LoRA进行参数高效微调,所需内存减少,此处用4卡张量并行即可训练,因此,这里仅生成使用4卡的rank_table_file文件。

> python ./mindformers/tools/hccl_tools.py --device_num "[0,4)"
start ./mindformers/tools/hccl_tools.py
visible_devices:['0', '1', '2', '3', '4', '5', '6', '7']
server_id:192.168.1.196
device_num_list: [0, 1, 2, 3]
rank_id:0, device_id:0, device_ip:192.168.100.101
rank_id:1, device_id:1, device_ip:192.168.101.101
rank_id:2, device_id:2, device_ip:192.168.102.101
rank_id:3, device_id:3, device_ip:192.168.103.101
Completed: hccl file was save in : /root/workspace/code/mindformers/hccl_4p_0123_192.168.1.196.json

修改配置文件(configs/glm/run_glm_6b_lora.yaml),设置模型权重路径、训练和评估数据集路径等,具体配置同全量微调类似,目前使用张量并行为4进行训练。

load_checkpoint: "https://b2.7b2.com/root/workspace/model/chatglm-convert/ms_glm_6b.ckpt"
train_dataset: &train_dataset
  data_loader:
    type: MindDataset
    dataset_dir: "https://b2.7b2.com/root/workspace/data/AdvertiseGen-ms/train_0604_128.mindrecord"
    shuffle: True
eval_dataset: &eval_dataset
  data_loader:
    type: MindDataset
    dataset_dir: "https://b2.7b2.com/root/workspace/data/AdvertiseGen-ms/eval_0604_256.mindrecord"
    shuffle: True
parallel_config:
  data_parallel: 1
  model_parallel: 4
  pipeline_stage: 1
  expert_parallel: 1

scripts目录下启动LoRA微调训练任务:

# cd /root/workspace/code/mindformers/scripts
# Usage Help: bash run_distribute.sh [RANK_TABLE_FILE] [CONFIG_PATH] [DEVICE_RANGE] [RUN_STATUS]
> bash run_distribute.sh /root/workspace/code/mindformers/hccl_4p_0123_192.168.1.196.json ../configs/glm/run_glm_6b_lora.yaml '[0,4]' finetune

具体的参数同全量微调一致。

不管是全量微调,还是LoRA微调,如果使用多卡模型并行进行训练,模型训练完成之后,需要先合并模型权重文件。

模型权重合并

微调所得到的模型权重文件为根据模型切分策略切分后的权重,我们需要手动将切分后的权重合并为一个文件,以用于评估和推理。

全量微调权重合并

首先,获取模型切分策略文件,在执行全量参数微调时,模型完成编译后,将会在模型输出权重路径下strategy子目录生成类似名为ckpt_strategy*.ckpt的切分策略文件,找到对应的文件路径。

接下来,执行以下脚本(merge_ckpt.py),将8份模型文件合成一份。MindSpore提供了根据切分策略转换模型权重切分的接口(mindspore.transform_checkpoint_by_rank)。

python3 merge_ckpt.py --src_postfix=31_4 
--src_checkpoints_dir=/root/workspace/output/fullft_output 
--src_strategy_file=/root/workspace/output/fullft_output/strategy/ckpt_strategy_rank_0.ckpt 
--dst_checkpoints_dir=/root/workspace/output/fullft_merge_checkpoint/

参数说明:

  • src_postfix: 训练最新生成模型文件中数字,用于合并文件时索引。
  • src_checkpoints_dir: 训练生成的模型文件路径。
  • src_strategy_file: 模型切分策略文件。
  • dst_checkpoints_dir: 模型文件合并后文件夹路径。

合并之后的权重文件如下所示:

> tree -h /root/workspace/output/fullft_merge_checkpoint
/root/workspace/output/fullft_merge_checkpoint
├── [  13G]  filtered_transformed.ckpt
└── [  63G]  transformed.ckpt

LoRA微调权重合并

需要注意的是,在使用LoRA进行参数微调时,生成的切分策略文件将不包含被冻结的权重,导致权重文件合并失败;此时,需将 mindformers/models/glm/glm.py 文件中有关LoRA冻结权重的代码注释后,重新运行微调脚本,获取到正确的切分策略文件后停止训练进程;相关代码如下

@MindFormerRegister.register(MindFormerModuleType.MODELS)
class GLMForPreTrainingWithLora(GLMForPreTraining):


  def __init__(self, config: GLMConfig = None, pet=None, **kwargs):
    _ = kwargs
    super().__init__(config)
    # get Pet tuning model.
    self.pet = pet
    self.pet.pet_config.reg_rules = r'.*query_key_value*'
    self.transformer = LoraAdapter.get_pet_model(self.transformer, self.pet.pet_config)
    # freeze pretrained model
    PetAdapter.freeze_pretrained_model(self, self.pet.pet_type)     # 注释此行以生成新的策略文件

LoRA微调的权重合并与全量微调权重合并类似,具体命令如下:

python3 merge_ckpt_lora.py --src_postfix=31_4 
--src_checkpoints_dir=/root/workspace/output/lora_output 
--src_strategy_file=/root/workspace/code/mindformers/output/strategy/ckpt_strategy_rank_0.ckpt 
--dst_checkpoints_dir=/root/workspace/output/lora_merge_checkpoint_v2/

合并之后的模型权重文件如下所示:

> tree -h /root/workspace/output/lora_merge_checkpoint_v2/
/root/workspace/output/lora_merge_checkpoint_v2/
├── [  14G]  filtered_transformed.ckpt
└── [  14G]  transformed.ckpt

模型权重合并之后,接下来进行模型评估。

模型评估

全量微调模型评估

执行以下脚本(run_mindformer.py)进行模型评估:

python run_mindformer.py 
--config ./configs/glm/run_glm_6b_infer.yaml 
--run_mode eval 
--load_checkpoint /root/workspace/output/fullft_merge_checkpoint/filtered_transformed.ckpt 
--eval_dataset_dir /root/workspace/data/AdvertiseGen-ms/eval_0604_256.mindrecord 
--device_id 7

参数说明:

  • config: 指定用于评估的配置文件名称,此处为configs/glm/run_glm_6b_infer.yaml
  • run_mode: 指定执行模式,此为eval,表示为评估模式
  • load_checkpoint: 指定要加载的checkpoint路径,替换为权重合并之后的真实路径。
  • eval_dataset_dir: 评估数据集的路径。
  • device_id: 指定要使用的设备编号。

评估完成后会打印评估指标 bleu-4rouge-1rouge-2rouge-l。具体如下所示:

rouge-1: 25.986109999999993 
rouge-2: 4.22121 
rouge-l: 21.202881999999995 
bleu-4:  4.540767999999999 

注意:

由于默认评估指标为生成完整文本后与预期文本做比较,评估速度将受限于模型大小与文本生成速度,评估流程可能较为缓慢。

LoRA微调模型评估

LoRA微调模型评估同全量微调模型评估方法一致,具体命令如下。

python run_mindformer.py 
--config ./configs/glm/run_glm_6b_lora_infer.yaml 
--run_mode eval 
--load_checkpoint /root/workspace/output/lora_merge_checkpoint_v2/filtered_transformed.ckpt 
--eval_dataset_dir /root/workspace/data/AdvertiseGen-ms/eval_0711_256.mindrecord 
--device_id 0

结语

本文主要讲述了针对华为昇腾910加速卡基于ChatGLM-6B进行模型训练(全量微调以及LoRA微调)、权重合并以及模型评估。

如果觉得我的文章能够能够给您带来帮助,期待您的点赞收藏加关注~~

本网站的内容主要来自互联网上的各种资源,仅供参考和信息分享之用,不代表本网站拥有相关版权或知识产权。如您认为内容侵犯您的权益,请联系我们,我们将尽快采取行动,包括删除或更正。
AI教程

人脸识别门禁系统硬件调试问题解决方案

2023-11-22 20:10:14

AI教程

大模型与大数据之挑战与解决——湖仓一体框架的应用

2023-11-22 20:23:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索