[{"data":1,"prerenderedAt":374},["ShallowReactive",2],{"content-query-cpaMATH2Er":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"cover":11,"type":12,"category":13,"body":14,"_type":368,"_id":369,"_source":370,"_file":371,"_stem":372,"_extension":373},"/technology-blogs/zh/3699","zh",false,"","基于昇思MindSpore实现PWCNet光流估计","作者：Yeats_Liao    单位/来源：昇思MindSpore社区论坛","2025-04-14","https://obs-mindspore-file.obs.cn-north-4.myhuaweicloud.com/file/2025/04/18/5bb88fe9fd0848e7b775e52e4fd9e460.png","technology-blogs","开发者分享",{"type":15,"children":16,"toc":360},"root",[17,25,31,36,54,62,76,81,86,94,99,106,114,126,131,138,143,150,155,162,167,177,184,189,197,206,214,222,230,235,243,248,256,261,269,274,282,289,294,302,310,315,323,330,338,345,353],{"type":18,"tag":19,"props":20,"children":22},"element","h1",{"id":21},"基于昇思mindspore实现pwcnet光流估计",[23],{"type":24,"value":8},"text",{"type":18,"tag":26,"props":27,"children":28},"p",{},[29],{"type":24,"value":30},"作者：Yeats_Liao",{"type":18,"tag":26,"props":32,"children":33},{},[34],{"type":24,"value":35},"单位/来源：昇思MindSpore社区论坛",{"type":18,"tag":37,"props":38,"children":40},"h3",{"id":39},"_01-环境准备",[41,47,49],{"type":18,"tag":42,"props":43,"children":44},"strong",{},[45],{"type":24,"value":46},"# 01",{"type":24,"value":48}," ",{"type":18,"tag":42,"props":50,"children":51},{},[52],{"type":24,"value":53},"环境准备",{"type":18,"tag":26,"props":55,"children":56},{},[57],{"type":18,"tag":42,"props":58,"children":59},{},[60],{"type":24,"value":61},"1、进入ModelArts官网",{"type":18,"tag":26,"props":63,"children":64},{},[65,67],{"type":24,"value":66},"云平台帮助用户快速创建和部署模型，管理全周期AI工作流，选择下面的云平台以开始使用昇思MindSpore，可以在昇思教程中进入ModelArts官网（",{"type":18,"tag":68,"props":69,"children":73},"a",{"href":70,"rel":71},"https://console.huaweicloud.com/modelarts/?region=cn-north-4#/dashboard%EF%BC%89",[72],"nofollow",[74],{"type":24,"value":75},"https://console.huaweicloud.com/modelarts/?region=cn-north-4#/dashboard）",{"type":18,"tag":26,"props":77,"children":78},{},[79],{"type":24,"value":80},"创建notebook，点击【打开】启动，进入ModelArts调试环境页面。",{"type":18,"tag":26,"props":82,"children":83},{},[84],{"type":24,"value":85},"注意选择西南-贵阳一，mindspore_2.3.0。",{"type":18,"tag":26,"props":87,"children":88},{},[89],{"type":18,"tag":90,"props":91,"children":93},"img",{"alt":7,"src":92},"https://obs-mindspore-file.obs.cn-north-4.myhuaweicloud.com/file/2025/04/18/19d3a06d8bc84a5c991e50155cab62ea.png",[],{"type":18,"tag":26,"props":95,"children":96},{},[97],{"type":24,"value":98},"等待环境搭建完成。",{"type":18,"tag":26,"props":100,"children":101},{},[102],{"type":18,"tag":90,"props":103,"children":105},{"alt":7,"src":104},"https://obs-mindspore-file.obs.cn-north-4.myhuaweicloud.com/file/2025/04/18/44fb579978ab4f33a1c00b05839297ce.png",[],{"type":18,"tag":26,"props":107,"children":108},{},[109],{"type":18,"tag":42,"props":110,"children":111},{},[112],{"type":24,"value":113},"2、下载案例notebook文件",{"type":18,"tag":26,"props":115,"children":116},{},[117,119],{"type":24,"value":118},"基于MindSpore框架实现PWCNet光流估计：",{"type":18,"tag":68,"props":120,"children":123},{"href":121,"rel":122},"https://github.com/mindspore-courses/applications/blob/master/pwc%5C_net/pwc%5C_net.ipynb",[72],[124],{"type":24,"value":125},"https://github.com/mindspore-courses/applications/blob/master/pwc\\_net/pwc\\_net.ipynb",{"type":18,"tag":26,"props":127,"children":128},{},[129],{"type":24,"value":130},"选择ModelArts Upload Files上传.ipynb文件。",{"type":18,"tag":26,"props":132,"children":133},{},[134],{"type":18,"tag":90,"props":135,"children":137},{"alt":7,"src":136},"https://obs-mindspore-file.obs.cn-north-4.myhuaweicloud.com/file/2025/04/18/69f8c5a041ed44a5843ac75179b383c4.png",[],{"type":18,"tag":26,"props":139,"children":140},{},[141],{"type":24,"value":142},"进入昇思MindSpore官网，点击上方的安装获取安装命令",{"type":18,"tag":26,"props":144,"children":145},{},[146],{"type":18,"tag":90,"props":147,"children":149},{"alt":7,"src":148},"https://obs-mindspore-file.obs.cn-north-4.myhuaweicloud.com/file/2025/04/18/619fe6ca6ab047959ed1d354b75b7b92.png",[],{"type":18,"tag":26,"props":151,"children":152},{},[153],{"type":24,"value":154},"MindSpore版本升级，镜像自带的MindSpore版本为2.3，该活动要求在MindSpore2.4.0版本体验，所以需要进行MindSpore版本升级。",{"type":18,"tag":26,"props":156,"children":157},{},[158],{"type":18,"tag":90,"props":159,"children":161},{"alt":7,"src":160},"https://obs-mindspore-file.obs.cn-north-4.myhuaweicloud.com/file/2025/04/18/9b74a8eac1084ef6879261cb30cec698.png",[],{"type":18,"tag":26,"props":163,"children":164},{},[165],{"type":24,"value":166},"命令如下：",{"type":18,"tag":168,"props":169,"children":171},"pre",{"code":170},"export no_proxy='a.test.com,127.0.0.1,2.2.2.2'\npip install https://ms-release.obs.cn-north-4.myhuaweicloud.com/2.4.0/MindSpore/unified/aarch64/mindspore-2.4.0-cp39-cp39-linux_aarch64.whl --trusted-host ms-release.obs.cn-north-4.myhuaweicloud.com -i https://pypi.tuna.tsinghua.edu.cn/simple\n",[172],{"type":18,"tag":173,"props":174,"children":175},"code",{"__ignoreMap":7},[176],{"type":24,"value":170},{"type":18,"tag":26,"props":178,"children":179},{},[180],{"type":18,"tag":90,"props":181,"children":183},{"alt":7,"src":182},"https://obs-mindspore-file.obs.cn-north-4.myhuaweicloud.com/file/2025/04/18/ccfbb99cfabf4701bb472f89b2741d06.png",[],{"type":18,"tag":26,"props":185,"children":186},{},[187],{"type":24,"value":188},"回到Notebook中，在第一块代码前加命令：",{"type":18,"tag":168,"props":190,"children":192},{"code":191},"\npip install --upgrade pip\n\npip install mindvision\n\npip install download\n",[193],{"type":18,"tag":173,"props":194,"children":195},{"__ignoreMap":7},[196],{"type":24,"value":191},{"type":18,"tag":37,"props":198,"children":200},{"id":199},"_02",[201],{"type":18,"tag":42,"props":202,"children":203},{},[204],{"type":24,"value":205},"# 02",{"type":18,"tag":37,"props":207,"children":209},{"id":208},"案例实现",[210],{"type":18,"tag":42,"props":211,"children":212},{},[213],{"type":24,"value":208},{"type":18,"tag":168,"props":215,"children":217},{"code":216},"import os\nimport logging\nos.environ['CUDA_VISIBLE_DEVICES'] = '0'\n# logging.basicConfig(level=logging.ERROR)\nlogging.disable(logging.WARNING)\n",[218],{"type":18,"tag":173,"props":219,"children":220},{"__ignoreMap":7},[221],{"type":24,"value":216},{"type":18,"tag":168,"props":223,"children":225},{"code":224},"%matplotlib inline\nimport mindspore as m\n\nm.set_context(mode=m.GRAPH_MODE, device_target=\"GPU\") # 训练时使用静态图\n# m.set_context(mode=m.PYNATIVE_MODE, device_target=\"GPU\") # 设置为动态图方便debug\n",[226],{"type":18,"tag":173,"props":227,"children":228},{"__ignoreMap":7},[229],{"type":24,"value":224},{"type":18,"tag":26,"props":231,"children":232},{},[233],{"type":24,"value":234},"将MindSpore设置为图执行模式，并设置为使用GPU进行训练。",{"type":18,"tag":168,"props":236,"children":238},{"code":237},"\ntrain_data_path = r\"data/MPI-Sintel-complete/training\"\nval_data_path = r\"data/MPI-Sintel-complete/training\"\n\npretrained_path = r\"pretrained_model/pwcnet-mindspore.ckpt\"\n\nbatch_size = 4\nlr = 0.0001\nnum_parallel_workers = 4\nlr_milestones = '6,10,12,16'\nlr_gamma = 0.5\nmax_epoch = 20\nloss_scale = 1024\nwarmup_epochs = 1\n",[239],{"type":18,"tag":173,"props":240,"children":241},{"__ignoreMap":7},[242],{"type":24,"value":237},{"type":18,"tag":26,"props":244,"children":245},{},[246],{"type":24,"value":247},"设置数据集路径，设置训练参数，包括batch_size、epoch_size、learning_rate等。",{"type":18,"tag":168,"props":249,"children":251},{"code":250},"\nimport mindspore.dataset.vision as V\n\nfrom src.dataset_utils import RandomGamma\n\naugmentation_list = [\n    V.ToPIL(),\n    V.RandomColorAdjust(brightness=0.5, contrast=0.5, saturation=0.5, hue=0.5),\n    V.ToTensor(),\n    RandomGamma(min_gamma=0.7, max_gamma=1.5, clip_image=True),\n]\n",[252],{"type":18,"tag":173,"props":253,"children":254},{"__ignoreMap":7},[255],{"type":24,"value":250},{"type":18,"tag":26,"props":257,"children":258},{},[259],{"type":24,"value":260},"设置数据增强方法，包括使用随机颜色变换和随机Gamma变换。",{"type":18,"tag":168,"props":262,"children":264},{"code":263},"from black import out\nfrom src.dataset import getFlyingChairsTrainData, getSintelValData\n\ndl_train, len_dl_train, dataset = getSintelValData(\n    root=train_data_path,\n    split=\"train\",\n    augmentations=augmentation_list,\n    batch_size=batch_size,\n    num_parallel_workers=num_parallel_workers,\n)\ndl_val, len_dl_val, val_dataset = getSintelValData(\n    root=val_data_path,\n    split=\"train\",\n    augmentations=augmentation_list,\n    batch_size=batch_size,\n    num_parallel_workers=num_parallel_workers,\n)\ntrain_len = dl_train.get_dataset_size()\ndl_train = dl_train.repeat(max_epoch)\nprint(f\"The dataset size of dl_train: {dl_train.get_dataset_size()}\")\nprint(f\"The dataset size of dl_val: {dl_val.get_dataset_size()}\")\n\ndict_datasets = next(dl_train.create_dict_iterator())\nprint(dict_datasets.keys())\nprint(dict_datasets[\"im1\"].shape)\nprint(dict_datasets[\"im2\"].shape)\nprint(dict_datasets[\"flo\"].shape)\nprint(type(dict_datasets[\"flo\"]))\nprint(dict_datasets[\"flo\"].max(), dict_datasets[\"flo\"].min())\nprint(dict_datasets[\"flo\"].max() * 0.05, dict_datasets[\"flo\"].min() * 0.05)\ndl_train = dl_train.create_tuple_iterator(output_numpy=False, do_copy=False)\ndl_val = dl_val.create_tuple_iterator(output_numpy=False, do_copy=False)\n",[265],{"type":18,"tag":173,"props":266,"children":267},{"__ignoreMap":7},[268],{"type":24,"value":263},{"type":18,"tag":26,"props":270,"children":271},{},[272],{"type":24,"value":273},"查看数据集的训练集和测试集的数量。同时查看数据集中RGB图片和光流图片的分辨率大小。",{"type":18,"tag":168,"props":275,"children":277},{"code":276},"\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport flow_vis\n\nfig = matplotlib.pyplot.gcf()\nfig.set_size_inches(18.5, 10.5)\nax = plt.subplot(131)\nax.imshow(np.transpose(dict_datasets[\"im1\"][0].asnumpy(), (1, 2, 0)))\nax.set_title(\"Image 1\")\nax.set_axis_off()\nax = plt.subplot(132)\nax.imshow(np.transpose(dict_datasets[\"im2\"][0].asnumpy(), (1, 2, 0)))\nax.set_title(\"Image 2\")\nax.set_axis_off()\nax = plt.subplot(133)\nax.imshow(\n    flow_vis.flow_to_color(np.transpose(dict_datasets[\"flo\"][0].asnumpy(), (1, 2, 0)))\n)\nax.set_axis_off()\nax.set_title(\"Optical Flow\")\n",[278],{"type":18,"tag":173,"props":279,"children":280},{"__ignoreMap":7},[281],{"type":24,"value":276},{"type":18,"tag":26,"props":283,"children":284},{},[285],{"type":18,"tag":90,"props":286,"children":288},{"alt":7,"src":287},"https://obs-mindspore-file.obs.cn-north-4.myhuaweicloud.com/file/2025/04/18/5ee258922ac74e88ab11fe99fe2fcbdb.png",[],{"type":18,"tag":26,"props":290,"children":291},{},[292],{"type":24,"value":293},"使用flow_vis和matplotlib库分别将光流图片与RGB图片可视化。",{"type":18,"tag":168,"props":295,"children":297},{"code":296},"# from src.pwc_net import PWCNet\n# from src.loss import PyramidEPE, MultiStepLR\n\n# from mindspore.nn import Adam\n\n# network = PWCNet()\n# criterion = PyramidEPE()\n\n# optimizer = Adam(params=net.trainable_params(), learning_rate=lr, loss_scale=loss_scale)\n",[298],{"type":18,"tag":173,"props":299,"children":300},{"__ignoreMap":7},[301],{"type":24,"value":296},{"type":18,"tag":168,"props":303,"children":305},{"code":304},"from collections import Counter\nimport numpy as np\n\nclass _WarmUp():\n    \"\"\"\n    Basic class for warm up\n    \"\"\"\n    def __init__(self, warmup_init_lr):\n        self.warmup_init_lr = warmup_init_lr\n    \n    def get_lr(self):\n        # Get learning rate during warmup\n        raise NotImplementedError\n\nclass _LRScheduler():\n    \"\"\"\n    Basic class for learning rate scheduler\n    \"\"\"\n    def __init__(self, lr, max_epoch, steps_per_epoch):\n        self.base_lr = lr\n        self.steps_per_epoch = steps_per_epoch\n        self.total_steps = int(max_epoch * steps_per_epoch)\n   \n    def get_lr(self):\n        # Compute learning rate using chainable form of the scheduler\n        raise NotImplementedError\n\nclass _LinearWarmUp(_WarmUp):\n    \"\"\"\n    Class for linear warm up\n    \"\"\"\n    def __init__(self, lr, warmup_epochs, steps_per_epoch, warmup_init_lr=0):\n        self.base_lr = lr\n        self.warmup_init_lr = warmup_init_lr\n        self.warmup_steps = int(warmup_epochs * steps_per_epoch)\n        super(_LinearWarmUp, self).__init__(warmup_init_lr)\n    \n    def get_warmup_steps(self):\n        return self.warmup_steps\n    \n    def get_lr(self, current_step):\n        lr_inc = (float(self.base_lr) - float(self.warmup_init_lr)) / float(self.warmup_steps)\n        lr = float(self.warmup_init_lr) + lr_inc * current_step\n        return lr\n\nclass MultiStepLR(_LRScheduler):\n    \"\"\"\n    Multi-step learning rate scheduler\n    \n    Decays the learning rate by gamma once the number of epoch reaches one of the milestones.\n   \n    Args:\n        lr (float): Initial learning rate which is the lower boundary in the cycle.\n        milestones (list): List of epoch indices. Must be increasing.\n        gamma (float): Multiplicative factor of learning rate decay.\n        steps_per_epoch (int): The number of steps per epoch to train for.\n        max_epoch (int): The number of epochs to train for.\n        warmup_epochs (int, optional): The number of epochs to Warmup. Default: 0\n   \n    Outputs:\n        numpy.ndarray, shape=(1, steps_per_epoch*max_epoch)\n    \n    Example:\n        >>> # Assuming optimizer uses lr = 0.05 for all groups\n        >>> # lr = 0.05     if epoch \u003C 30\n        >>> # lr = 0.005    if 30 \u003C= epoch \u003C 80\n        >>> # lr = 0.0005   if epoch >= 80\n        >>> scheduler = MultiStepLR(lr=0.1, milestones=[30,80], gamma=0.1, steps_per_epoch=5000, max_epoch=90)\n        >>> lr = scheduler.get_lr()\n    \"\"\"\n   \n    def __init__(self, lr, milestones, gamma, steps_per_epoch, max_epoch, warmup_epochs=0):\n        self.milestones = Counter(milestones)\n        self.gamma = gamma\n        self.warmup = _LinearWarmUp(lr, warmup_epochs, steps_per_epoch)\n        super(MultiStepLR, self).__init__(lr, max_epoch, steps_per_epoch)\n   \n    def get_lr(self):\n        warmup_steps = self.warmup.get_warmup_steps()\n       \n        lr_each_step = []\n        current_lr = self.base_lr\n        for i in range(self.total_steps):\n            if i \u003C warmup_steps:\n                lr = self.warmup.get_lr(i+1)\n            else:\n                cur_ep = i // self.steps_per_epoch\n                if i % self.steps_per_epoch == 0 and cur_ep in self.milestones:\n                    current_lr = current_lr * self.gamma\n                lr = current_lr\n            \n            lr_each_step.append(lr)\n       \n        return np.array(lr_each_step).astype(np.float32)\n",[306],{"type":18,"tag":173,"props":307,"children":308},{"__ignoreMap":7},[309],{"type":24,"value":304},{"type":18,"tag":26,"props":311,"children":312},{},[313],{"type":24,"value":314},"初始化神经网络、损失函数、优化器、模型和回调函数。",{"type":18,"tag":168,"props":316,"children":318},{"code":317},"import mindspore.nn as nn\nimport mindspore.ops as ops\nfrom mindspore.train.callback import LossMonitor, TimeMonitor\nfrom mindspore import Model, load_checkpoint, load_param_into_net\n\nfrom src.pwc_net import BuildTrainNetwork, PWCNet\nfrom src.loss import PyramidEPE\n\nclass CustomWithLossCell(nn.Cell):\n    \n    def __init__(self, network, criterion):\n        super(CustomWithLossCell, self).__init__(auto_prefix=False)\n        self.network = network\n        self.criterion = criterion\n    \n    def construct(self, im1, im2, flow):\n        out = self.network(im1, im2)\n        loss = self.criterion(out, flow)\n        return loss\n\nnetwork = PWCNet()\ncriterion = PyramidEPE()\n\nparam_dict = load_checkpoint(pretrained_path)\nparam_dict_new = {}\nfor key, values in param_dict.items():\n    if key.startswith('moment1.' or 'moment2' or 'global_step' or 'beta1_power' or 'beta2_power' or\n                        'learning_rate'):\n        continue\n    elif key.startswith('network.'):\n        param_dict_new[key[8:]] = values\n    else:\n        param_dict_new[key] = values\nload_param_into_net(network, param_dict_new)\n\ntrain_net = BuildTrainNetwork(network, criterion)\n\n# model = Model(\n#     network=net_with_loss,\n#     # loss_fn=criterion,\n#     optimizer=optimizer,\n#     eval_network=net_with_loss,\n#     metrics={\"loss\"},\n#     amp_level=\"O0\",\n# )\n",[319],{"type":18,"tag":173,"props":320,"children":321},{"__ignoreMap":7},[322],{"type":24,"value":317},{"type":18,"tag":168,"props":324,"children":325},{"code":317},[326],{"type":18,"tag":173,"props":327,"children":328},{"__ignoreMap":7},[329],{"type":24,"value":317},{"type":18,"tag":168,"props":331,"children":333},{"code":332},"print('Start training...')\nfor i, data in enumerate(dl_train):\n    # clean grad + adjust lr + put data into device + forward + backward + optimizer, return loss\n    # print(data[0].shape, data[1].shape, data[2].shape)\n    # print(data[0].max(), data[0].min(), data[1].max(), data[1].min(), data[2].max(), data[2].min())\n    loss = train_net_step(data[0], data[1], data[2])\n    # print(loss)\n    loss_meter.update(loss.asnumpy())\n\n    if i == 0:\n        time_for_graph_compile = time.time() - create_network_start\n        print('graph compile time={:.2f}s'.format(time_for_graph_compile))\n\n    if i % 10 == 0 and i > 0:\n        t_now = time.time()\n        epoch = int(i / train_len)\n        print('epoch: [{}], iter: [{}], loss: [{:.4f}], time: [{:.2f}]s'.format(epoch, i, loss_meter.avg, t_now - t_end))\n        t_end = t_now\n        loss_meter.reset()\n\n    if i % train_len == 0  and i > 0:\n        epoch_time_used = time.time() - t_epoch\n        epoch = int(i / train_len)\n        fps = batch_size * train_len / epoch_time_used\n        print('=================================================')\n        print('epoch[{}], iter[{}], [{:.2f}] imgs/sec'.format(epoch, i, fps))\n        t_epoch = time.time()\n        validation_loss = 0\n        sum_num = 0\n        for _, val_data in enumerate(dl_val):\n            network.set_train(False)\n            val_output = network(val_data[0], val_data[1], training=False)\n            val_loss = criterion(val_output, val_data[2], training=False)\n            validation_loss += val_loss\n            sum_num += 1\n        if (validation_loss / sum_num) \u003C best_val_loss:\n            best_val_loss = validation_loss / sum_num\n        print('validation EPE: {}, best validation EPE: {}'.format(validation_loss / sum_num, best_val_loss))\n",[334],{"type":18,"tag":173,"props":335,"children":336},{"__ignoreMap":7},[337],{"type":24,"value":332},{"type":18,"tag":26,"props":339,"children":340},{},[341],{"type":18,"tag":90,"props":342,"children":344},{"alt":7,"src":343},"https://obs-mindspore-file.obs.cn-north-4.myhuaweicloud.com/file/2025/04/18/7dfd2e2616224ad5a3f510056e69fb13.png",[],{"type":18,"tag":168,"props":346,"children":348},{"code":347},"\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport flow_vis\n\nout_flow = network(dict_datasets['im1'][3][None, ...], dict_datasets['im2'][3][None, ...], training=False)\n\nfig = matplotlib.pyplot.gcf()\nfig.set_size_inches(18.5, 10.5)\nax = plt.subplot(141)\nax.imshow(np.transpose(dict_datasets[\"im1\"][3].asnumpy(), (1, 2, 0)))\nax.set_title(\"Image 1\")\nax.set_axis_off()\nax = plt.subplot(142)\nax.imshow(np.transpose(dict_datasets[\"im2\"][3].asnumpy(), (1, 2, 0)))\nax.set_title(\"Image 2\")\nax.set_axis_off()\nax = plt.subplot(143)\nax.imshow(\n    flow_vis.flow_to_color(np.transpose(dict_datasets[\"flo\"][3].asnumpy(), (1, 2, 0)))\n)\nax.set_axis_off()\nax.set_title(\"Optical Flow\")\nax = plt.subplot(144)\nax.imshow(\n    flow_vis.flow_to_color(np.transpose(out_flow[0].asnumpy(), (1, 2, 0)))\n)\nax.set_axis_off()\nax.set_title(\"Predicted Optical Flow\")\n# plt.show()\n",[349],{"type":18,"tag":173,"props":350,"children":351},{"__ignoreMap":7},[352],{"type":24,"value":347},{"type":18,"tag":26,"props":354,"children":355},{},[356],{"type":18,"tag":90,"props":357,"children":359},{"alt":7,"src":358},"https://obs-mindspore-file.obs.cn-north-4.myhuaweicloud.com/file/2025/04/18/065084be0fda4c1aa0a4e42150bcea4d.png",[],{"title":7,"searchDepth":361,"depth":361,"links":362},4,[363,366,367],{"id":39,"depth":364,"text":365},3,"# 01 环境准备",{"id":199,"depth":364,"text":205},{"id":208,"depth":364,"text":208},"markdown","content:technology-blogs:zh:3699.md","content","technology-blogs/zh/3699.md","technology-blogs/zh/3699","md",1776506133274]