资讯详情

资讯详情

ImageAI 自定义图像分类模型训练指南:从数据集准备到迁移学习的完整实战

人工智能计算机视觉深度学习【免费下载链接】ImageAIA python library built to empower developers to build applications and systems with self-contained Computer Vision capabilities项目地址https://gitcode.com/gh_mirrors/im/ImageAI点击查看免费下载本文以仓库 imageai_tf_deprecated/Classification/CUSTOMTRAINING.md 为主体内容结合当前仓库源码imageai/Classification/Custom/init.py与实际示例、测试用例系统讲解如何用 ImageAI 在任意图像数据集上训练自定义图像分类模型涵盖数据集组织、5 行代码启动训练、训练日志解读、IdenProf 实战、连续训练与迁移学习并在文末给出源码级佐证路径。ImageAI为自定义图像分类模型训练提供了极为简洁的封装只需组织好数据集、调用ClassificationModelTrainer即可在 ResNet50、InceptionV3、DenseNet121及 MobileNetV2等网络上训练自己的模型。训练过程会自动生成一份「类别映射 JSON 文件」并输出多个候选模型训练结束后挑选验证精度最高的模型即可配合 JSON 文件执行自定义图像预测。读完本文你将掌握完整的数据集组织规范、训练 API 的全部参数语义、控制台输出的解读方法以及连续训练、迁移学习两种进阶训练策略并能将训练产物直接接入预测流程。一、训练前置条件硬件建议模型训练是计算密集型任务。原文档明确建议使用配备NVIDIA GPU并安装 GPU 版深度学习框架的计算机进行训练在纯 CPU 环境下训练可能耗时数小时乃至数天而 GPU 环境可将时间压缩到数小时量级。原文档同时提到可借助 Google Colab其提供 K80 GPU开展此类实验。需要说明的是本文对应的旧版文档基于 TensorFlow/Keras 后端位于 imageai_tf_deprecated/Classification/Custom/init.py而当前仓库主版本 imageai/Classification/Custom/init.py 已迁移到 PyTorch 实现仓库根目录的 BACKEND_MIGRATION.md 记录了该迁移过程。因此下文在保留原文档 API 语义的同时会给出当前 PyTorch 版 API 的对照说明方便你在当前仓库上直接复现。当前 PyTorch 版会在训练启动时自动检测设备并打印提示源码 imageai/Classification/Custom/init.py 第 268-270 行GPU 可用时输出Training with GPU否则输出Training with CPU. This might cause slower train.。二、数据集准备8 步构建标准目录结构训练自定义分类模型的第一步是准备图像数据。请严格按下述 8 步组织数据集创建数据集根目录目录名即为你的数据集名称例如pets在数据集根目录下创建train子目录在数据集根目录下创建test子目录在train目录中为每个要预测的类别分别创建以类别名命名的子目录例如dog、cat、squirrel、snake在test目录中同样为每个类别创建同名子目录在train下每个类别目录中放入该类别图像训练用。为获得实用场景下的良好表现原文档建议每类至少约 500 张图每类 1000 张图效果更佳在test下每个类别目录中放入约100 至 200 张该类别图像训练过程中用于测试模型完成后数据集目录结构应如下所示pets//train//dog//dog-train-images pets//train//cat//cat-train-images pets//train//squirrel//squirrel-train-images pets//train//snake//snake-train-images pets//test//dog//dog-test-images pets//test//cat//cat-test-images pets//test//squirrel//squirrel-test-images pets//test//snake//snake-test-images这种「根目录 / train 或 test / 类别子目录 / 图像文件」的组织方式被两个后端的源码共同印证旧版 TF 后端setDataDirectory()的 docstringimageai_tf_deprecated/Classification/Custom/init.py 第 84-96 行给出了完全相同的train class1 class1_train_images结构说明当前 PyTorch 版 imageai/Classification/Custom/init.py 的setDataDirectory()第 136-160 行要求传入一个真实存在的目录并同样要求其中包含train与test子目录__load_data()第 114-134 行通过torchvision.datasets.ImageFolder递归读取这两个子目录类别数由 train 目录下的子目录数量自动推断self.__class_names image_dataset[train].classes这正是新 API 中不再需要显式传入num_objects的原因。提示旧版 TF 的setDataDirectory()还额外支持train_subdirectory、test_subdirectory、models_subdirectory、json_subdirectory四个可选参数默认分别为train、test、models、json可自定义各子目录名称当前 PyTorch 版固定使用train/test约定。三、5 行代码启动训练核心 API 与参数详解数据集就绪后训练代码极简。原文档给出的「5 行代码」如下from imageai.Classification.Custom import ClassificationModelTrainer model_trainer ClassificationModelTrainer() model_trainer.setModelTypeAsResNet50() model_trainer.setDataDirectory(pets) model_trainer.trainModel(num_objects4, num_experiments100, enhance_dataTrue, batch_size32, show_network_summaryTrue)拆解来看from imageai.Classification.Custom import ClassificationModelTrainer # 导入训练类 model_trainer ClassificationModelTrainer() # 创建训练器实例 model_trainer.setModelTypeAsResNet50() # 指定网络类型 model_trainer.setDataDirectory(pets) # 指定数据集目录前四行完成导入类、实例化、设置网络类型、指向数据集目录四件事。其中setModelTypeAsResNet50()属于四个网络类型设置方法之一旧版还含setModelTypeAsMobileNetV2()、setModelTypeAsDenseNet121()、setModelTypeAsInceptionV3()并提供了已弃用的setModelTypeAsResNet()/setModelTypeAsDenseNet()别名见 imageai_tf_deprecated/Classification/Custom/init.py 第 42-65 行。3.1 旧版trainModel()参数语义原文档核心原文档详细列出的trainModel()参数及其含义如下参数必填说明num_objects必填数据集中目标类别的数量即 train 下类别子目录个数num_experiments必填网络遍历全部训练图像的次数即epochs旧版默认 200enhance_data可选是否让网络生成训练图像的修改副本以提升效果默认Falsebatch_size可选网络每次处理的图像数量图像按批次处理直到该轮experiment耗尽全部数据旧版默认 32通常取 16/32/64/128 等 2 的幂show_network_summary可选是否在控制台打印训练网络结构默认False此外旧版trainModel()还支持以下可选项源码 docstringimageai_tf_deprecated/Classification/Custom/init.py 第 147-163 行initial_learning_rate初始学习率默认1e-3原文档建议在无深入理解时保持默认值training_image_size训练图像尺寸默认 224最小 100小于 100 会自动回退到 100continue_from_model接续训练详见第六节transfer_from_model/initial_num_objects/transfer_with_full_training迁移学习相关详见第七节save_full_model以完整模型形式保存加载时可免指定网络类型。enhance_dataTrue在旧版底层对应ImageDataGenerator的水平翻转与 10% 高度/宽度位移增强imageai_tf_deprecated/Classification/Custom/init.py 第 354-365 行训练与测试生成器均将像素缩放到1/255。3.2 当前 PyTorch 版 API 对照当前主版本imageai/Classification/Custom/init.py 第 208-215 行的trainModel()签名如下def trainModel(self, num_experiments100, batch_size8, model_directoryNone, transfer_from_modelNone, verboseTrue) - None:参数对照关系num_experiments语义不变epochs当前默认100batch_size语义不变当前默认8model_directory新增模型与 JSON 映射的保存目录默认落在数据集根目录/modelstransfer_from_model预训练模型路径对应旧版迁移学习能力详见第七节verbose是否打印训练日志不再需要num_objects类别数由ImageFolder自动推断旧版的enhance_data、show_network_summary、training_image_size等由当前版本内置的数据变换与固定 224 输入InceptionV3 为 299取代。当前版本内置的数据增强定义在 imageai/Classification/Custom/data_transformation.py训练阶段使用RandomResizedCrop(224) RandomHorizontalFlip ToTensor Normalize测试阶段使用Resize(256) CenterCrop(224)InceptionV3 使用 299 尺寸的对应变换data_transforms2。各网络的默认训练超参优化器 SGD、动量 0.9、学习率与权重衰减集中定义在 imageai/Classification/Custom/training_params.py例如 ResNet50 与 DenseNet121 初始学习率 0.1、weight_decay 1e-4InceptionV3 学习率 0.045 并配合StepLR(0.94, step2)衰减MobileNetV2 学习率 0.045 配合StepLR(0.98, step1)。训练启动时imageai/Classification/Custom/init.py 第 63-88 行会按模型类型替换最后一层分类头使输出维度与当前数据集类别数一致。仓库提供的官方示例 examples/custom_model_training.py 即新 API 的典型用法from imageai.Classification.Custom import ClassificationModelTrainer model_trainer ClassificationModelTrainer() model_trainer.setModelTypeAsResNet50() model_trainer.setDataDirectory(idenprof) model_trainer.trainModel(num_experiments200, batch_size32)四、训练过程与控制台输出解读4.1 旧版TF训练启动输出旧版启动训练后控制台会依次出现网络参数汇总、增强提示与数据统计Total params: 23,608,202 Trainable params: 23,555,082 Non-trainable params: 53,120 ____________________________________________________________________________________________________ Using Enhanced Data Generation Found 4000 images belonging to 4 classes. Found 800 images belonging to 4 classes. JSON Mapping for the model classes saved to C:\Users\User\PycharmProjects\ImageAITest\pets\json\model_class.json Number of experiments (Epochs) : 100解读要点Total params / Trainable params / Non-trainable params网络总参数量、可训练参数量、冻结不可训练参数量本例约 2360 万参数其中约 2355 万可训练说明预训练骨干未被冻结Found 4000 images belonging to 4 classes训练集共 4000 张图、4 个类别Found 800 images belonging to 4 classes测试集共 800 张图、4 个类别JSON Mapping ... model_class.json类别映射 JSON 已保存到数据集目录下的json/model_class.jsonNumber of experiments (Epochs) : 100本轮共执行 100 个 epoch。4.2 逐 Epoch 训练日志训练推进过程中控制台每轮epoch会输出批次进度、损失与精度Epoch 1/100 1/25 [.............................] - ETA: 52s - loss: 2.3026 - acc: 0.2500 2/25 [............................] - ETA: 41s - loss: 2.3027 - acc: 0.1250 ... 25/25 [] - 51s - loss: 2.3095 - acc: 0.0600 - val_loss: 2.3026 - val_acc: 0.1000 Epoch 00000: saving model to ...\pets\models\model_ex-000_acc-0.100000.h5逐行解读原文档要点Epoch 1/100当前是 100 个目标 epoch 中的第 1 个1/25 [.....] - ETA: 52s - loss: 2.3026 - acc: 0.25001/25表示本 epoch 已完成 1/25 个批次ETA为预计剩余时间loss/acc为累计平均损失与训练精度Epoch 00000: saving model to ...model_ex-000_acc-0.100000.h5本 epoch 结束后保存模型。命名中ex_000表示第 0 个 experimentepochacc_0.100000与val_acc: 0.1000表示该模型在测试集上的精度最大值为 1.0。该信息用于帮助识别表现最佳的模型供后续自定义图像预测使用。旧版模型保存由ModelCheckpoint回调驱动imageai_tf_deprecated/Classification/Custom/init.py 第 340-345 行文件命名模板为model_ex-{epoch:03d}_acc-{accuracy:03f}.h5并配合LearningRateScheduler按 epoch 分段衰减学习率第 119-142 行、第 393-395 行。4.3 当前 PyTorch 版训练日志当前版本每 epoch 输出形式如下示例来自仓库内文档 imageai/Classification/CUSTOMTRAINING.md Training with GPU Epoch 1/100 ---------- 100%|████████████████████████████████████████████████████████| 282/282 [02:1500:00, 2.08it/s] train Loss: 3.8062 Accuracy: 0.1178 100%|████████████████████████████████████████████████████████| 63/63 [00:2600:00, 2.36it/s] test Loss: 2.2829 Accuracy: 0.1215每轮依次执行train与test两个阶段训练阶段更新权重loss.backward()optimizer.step()测试阶段仅前向评估源码 imageai/Classification/Custom/init.py 第 277-325 行InceptionV3 的输出为InceptionOutputs时取output[0]参与损失计算。每个 epoch 结束后若测试精度优于历史最佳最佳模型会被自动保存文件命名为{model_type}-{dataset}-test_acc_{acc:.5f}_epoch-{epoch}.pt例如resnet50-idenprof-test_acc_0.78200_epoch-91.pt并且同一目录下只保留精度最高的那一份权重第 316-325 行。全部训练结束后会打印总耗时与最佳测试精度Best test accuracy: ...。五、训练产物类别映射 JSON 与最佳模型的选择训练过程会生成两类关键产物1. 类别映射 JSON 文件——旧版保存为数据集目录/json/model_class.json键为类别索引值为类别名见 imageai_tf_deprecated/Classification/Custom/init.py 第 377-386 行当前 PyTorch 版保存为模型目录/{dataset_name}_model_classes.json键同样为数字索引imageai/Classification/Custom/init.py 第 252-258 行。以仓库测试数据集 IdenProf 为例产物名为idenprof_model_classes.json。该文件是预测阶段将模型输出索引映射回类别名的依据。2. 各 epoch 的候选模型文件——如上文所述旧版每个 epoch 都可能落盘一个.h5文件当前 PyTorch 版则只保留测试精度最高的.pt权重。挑选模型时应选择测试精度最高的那一份acc/test_acc值最大最大值为 1.0。仓库测试用例 test/test_custom_classification_training.py 对上述产物契约进行了自动化验证四个测试test_resnet50_training、test_densenet121_training、test_inceptionv3_training、test_mobilenetv2_training分别以num_experiments1、batch_size2训练一个 epoch并断言models目录被创建、{dataset}_model_classes.json存在、目录中至少存在一个.pt模型文件每个测试还通过pytest.mark.parametrize同时覆盖「使用预训练模型迁移」与「不使用预训练模型」两种路径是理解训练产物格式与迁移学习用法的第一手参考。六、IdenProf 实战从零训练 10 类职业识别模型原文档提供了一套完整的 IdenProf 实战示例。IdenProf 是包含 10 类穿制服职业人员的图像数据集厨师、医生、工程师、农民、消防员、法官、机械师、飞行员、警察、服务员等。下图展示了该数据集中用于训练职业识别模型的样本图像下面的代码会自动下载 IdenProf 数据集的训练与测试压缩包、解压到本地随后启动 ResNet50 训练类别数num_objects10100 个 epoch。该代码完整保留了原文档的下载与训练流程可直接作为「自动准备数据集 启动训练」的模板from io import open import requests import shutil from zipfile import ZipFile import os from imageai.Classification.Custom import ClassificationModelTrainer execution_path os.getcwd() TRAIN_ZIP_ONE os.path.join(execution_path, idenprof-train1.zip) TRAIN_ZIP_TWO os.path.join(execution_path, idenprof-train2.zip) TEST_ZIP os.path.join(execution_path, idenprof-test.zip) DATASET_DIR os.path.join(execution_path, idenprof) DATASET_TRAIN_DIR os.path.join(DATASET_DIR, train) DATASET_TEST_DIR os.path.join(DATASET_DIR, test) if(os.path.exists(DATASET_DIR) False): os.mkdir(DATASET_DIR) if(os.path.exists(DATASET_TRAIN_DIR) False): os.mkdir(DATASET_TRAIN_DIR) if(os.path.exists(DATASET_TEST_DIR) False): os.mkdir(DATASET_TEST_DIR) if(len(os.listdir(DATASET_TRAIN_DIR)) 10): if(os.path.exists(TRAIN_ZIP_ONE) False): print(Downloading idenprof-train1.zip) data requests.get(https://github.com/OlafenwaMoses/IdenProf/releases/download/v1.0/idenprof-train1.zip, stream True) with open(TRAIN_ZIP_ONE, wb) as file: shutil.copyfileobj(data.raw, file) del data if (os.path.exists(TRAIN_ZIP_TWO) False): print(Downloading idenprof-train2.zip) data requests.get(https://github.com/OlafenwaMoses/IdenProf/releases/download/v1.0/idenprof-train2.zip, streamTrue) with open(TRAIN_ZIP_TWO, wb) as file: shutil.copyfileobj(data.raw, file) del data print(Extracting idenprof-train1.zip) extract1 ZipFile(TRAIN_ZIP_ONE) extract1.extractall(DATASET_TRAIN_DIR) extract1.close() print(Extracting idenprof-train2.zip) extract2 ZipFile(TRAIN_ZIP_TWO) extract2.extractall(DATASET_TRAIN_DIR) extract2.close() if(len(os.listdir(DATASET_TEST_DIR)) 10): if (os.path.exists(TEST_ZIP) False): print(Downloading idenprof-test.zip) data requests.get(https://github.com/OlafenwaMoses/IdenProf/releases/download/v1.0/idenprof-test.zip, streamTrue) with open(TEST_ZIP, wb) as file: shutil.copyfileobj(data.raw, file) del data print(Extracting idenprof-test.zip) extract ZipFile(TEST_ZIP) extract.extractall(DATASET_TEST_DIR) extract.close() model_trainer ClassificationModelTrainer() model_trainer.setModelTypeAsResNet50() model_trainer.setDataDirectory(DATASET_DIR) model_trainer.trainModel(num_objects10, num_experiments100, enhance_dataTrue, batch_size32, show_network_summaryTrue)脚本要点通过len(os.listdir(DATASET_TRAIN_DIR)) 10判断训练目录是否已有全部 10 个类别子目录避免重复下载requests流式下载 shutil.copyfileobj落盘再用ZipFile.extractall解压若改用当前 PyTorch 版 API只需将最后两行替换为model_trainer.setDataDirectory(DATASET_DIR)与model_trainer.trainModel(num_experiments100, batch_size32)类别数自动推断为 10。七、连续模型训练从已保存模型继续训练连续训练适用于两种典型场景一是训练因计算时长限制或数据集过大而中断需要接着未完成的训练继续二是希望对已有模型进一步训练。原文档特别强调连续训练针对的是用同一数据集上先前保存的模型继续训练。旧版用法只需在trainModel()中传入continue_from_model参数值为先前保存的模型文件路径from imageai.Classification.Custom import ClassificationModelTrainer import os trainer ClassificationModelTrainer() trainer.setModelTypeAsDenseNet121() trainer.setDataDirectory(idenprof) trainer.trainModel(num_objects10, num_experiments50, enhance_dataTrue, batch_size8, show_network_summaryTrue, continue_from_modelidenprof_densenet-0.763500.h5)底层实现上旧版在continue_from_model非空时直接以该权重文件作为weights加载并保留分类头继续训练imageai_tf_deprecated/Classification/Custom/init.py 第 189-192、218-221、247-250、278-281 行控制台会打印Training using weights from a previouly model。需要说明当前 PyTorch 版trainModel()签名中不再单设continue_from_model参数而是统一通过transfer_from_model加载已有权重imageai/Classification/Custom/init.py 第 236-242 行。从源码结构看若将先前在同一数据集上训练的.pt模型路径传入transfer_from_model加载状态字典后继续训练即可实现与旧版连续训练等效的能力配合fineTuneAllLayers()默认全层微调或freezeAllLayers()冻结除分类头外的所有层见第 194-206 行可以控制参与训练的层范围。八、迁移学习小数据集训练高精度模型ImageAI 团队从用户反馈中发现大多数自定义模型的训练数据集规模都远低于「每类 500 张」的最低建议因此难以达到可用精度。为此ImageAI 提供迁移学习能力直接取用在大规模数据集如 ImageNet上预训练好的 ResNet50、SqueezeNet、InceptionV3、DenseNet121 模型作为自定义训练的起点从而用少量图像也能训练出高精度模型。旧版用法只需在trainModel()中设置两个参数transfer_from_model预训练模型文件的路径initial_num_objects预训练模型原训练数据集的类别数例如 ImageNet 为 1000——该参数在设置transfer_from_model时为必填。from imageai.Classification.Custom import ClassificationModelTrainer import os trainer ClassificationModelTrainer() trainer.setModelTypeAsResNet50() trainer.setDataDirectory(idenprof) trainer.trainModel(num_objects10, num_experiments50, enhance_dataTrue, batch_size32, show_network_summaryTrue, transfer_from_modelresnet50_imagenet_tf.2.0.h5, initial_num_objects1000)上述代码演示了从 ImageNet 上训练的 ResNet50 模型出发进行迁移学习。旧版底层实现是以预训练权重构造去掉分类头include_topFalse、poolingavg的骨干网络在其后接一个输出维度为当前类别数的新Dense分类层imageai_tf_deprecated/Classification/Custom/init.py 第 195-216 行等控制台会打印Training using weights from a pre-trained ImageNet model。可选参数transfer_with_full_training决定是重新训练全部层还是仅训练顶层。当前 PyTorch 版同样支持迁移学习将预训练权重路径传入trainModel(transfer_from_model...)即可__set_transfer_learning_mode()imageai/Classification/Custom/init.py 第 90-112 行负责加载预训练状态字典对 DenseNet121 还会做旧版权重键名的正则兼容转换随后__set_training_param()将分类头替换为当前数据集类别数。仓库测试 test/test_custom_classification_training.py 中每个模型测试都通过transfer_learning参数化用例验证了「从预训练权重如resnet50-19c8e357.pth迁移训练」与「从头训练」两条路径均可正常产出模型。九、训练完成之后用自定义模型做图像预测训练完成后即可使用CustomImageClassification类加载「最佳模型 对应 JSON 映射」进行预测。原文档给出的预测链路如下完整用法详见仓库文档 imageai/Classification/CUSTOMCLASSIFICATION.mdfrom imageai.Classification.Custom import CustomImageClassification import os execution_path os.getcwd() prediction CustomImageClassification() prediction.setModelTypeAsResNet50() prediction.setModelPath(os.path.join(execution_path, resnet50-idenprof-test_acc_0.78200_epoch-91.pt)) prediction.setJsonPath(os.path.join(execution_path, idenprof_model_class.json)) prediction.loadModel(num_objects10) predictions, probabilities prediction.classifyImage(os.path.join(execution_path, 4.jpg), result_count5) for eachPrediction, eachProbability in zip(predictions, probabilities): print(eachPrediction : eachProbability)要点setModelTypeAsResNet50()必须与模型文件对应的网络类型一致setModelPath()指向训练产物中的最佳模型以test_acc最高者为准setJsonPath()指向训练生成的类别映射 JSONloadModel(num_objects10)加载权重并指定可预测的类别数classifyImage(path, result_count5)返回两个数组预测类别名列表与对应百分比概率二者按位置一一对应。预测时还可通过loadModel()的classification_speed参数可选normal、fast、faster、fastest在推理速度与精度间权衡fast将输入尺寸降为 160faster为 120fastest为 100旧版实现见 imageai_tf_deprecated/Classification/Custom/init.py 第 478-499 行。十、扩展阅读与仓库佐证训练 API 当前实现imageai/Classification/Custom/init.pyClassificationModelTrainer位于第 26 行起trainModel()位于第 208-330 行训练超参与数据增强imageai/Classification/Custom/training_params.py、imageai/Classification/Custom/data_transformation.py旧版TF 后端训练实现imageai_tf_deprecated/Classification/Custom/init.py训练产物契约的自动化测试test/test_custom_classification_training.py官方训练示例examples/custom_model_training.py自定义分类预测完整文档imageai/Classification/CUSTOMCLASSIFICATION.md总体而言ImageAI 将「数据组织 → 训练 → 产物选择 → 预测」的完整闭环压缩到了极少的代码量中数据集按train/test 类别子目录规范组织后训练、JSON 映射生成、最佳模型筛选与保存全部自动完成开发者只需根据控制台输出的test_acc挑选模型并接入CustomImageClassification即可连续训练与迁移学习则覆盖了「算力受限续训」与「小样本高精度」两个高频实战场景。赞分享人工智能计算机视觉深度学习【免费下载链接】ImageAIA python library built to empower developers to build applications and systems with self-contained Computer Vision capabilities项目地址https://gitcode.com/gh_mirrors/im/ImageAI点击查看免费下载相关推荐Video2X AI模型训练自定义数据集与迁移学习终极指南Video2X AI模型训练自定义数据集与迁移学习终极指南 想要打造专属于你的超分辨率AI模型吗Video2X作为一款强大的机器学习视频超分辨率和帧插值框架音视频视频处理图像处理深度学习如何用PyTorch-DeepLab-Xception实现自定义数据集训练从数据准备到模型部署的完整指南如何用PyTorch DeepLab Xception实现自定义数据集训练从数据准备到模型部署的完整指南 PyTorch DeepLab Xception是一深度学习计算机视觉WeNet训练自定义数据集从数据准备到模型微调的完整流程WeNet训练自定义数据集从数据准备到模型微调的完整流程 想要为你的语音识别项目训练一个专属的WeNet模型吗️ 这篇完整指南将带你从零开始掌握WeN人工智能语音深度学习预训练模型推理服务上一篇GoodbyeDPI如何免费绕过运营商查包下一篇SQLCipher日志系统解析从showwal.c到showjournal.c创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →