13.1.2 对CIFAR-10数据集使用图像增广来训练ResNet

news2025/2/25 11:21:34
%matplotlib inline
import torch
import torchvision
from torch import nn
from d2l import torch as d2l
import time
'''
    使用CIFAR-10数据集,而不是我们之前使用的Fashion-MNIST数据集。这是因为Fashion-MNIST数据集中对象的位置和大小已被规范化,而CIFAR-10数据集中对象的颜色和大小差异更明显。CIFAR-10数据集中的前32个训练图像如下所示。
'''
all_images = torchvision.datasets.CIFAR10(train=True, root="../data",download=True)
d2l.show_images([all_images[i][0] for i in range(32)], 4, 8, scale=0.8);

在这里插入图片描述

def load_cifar10(is_train, augs, batch_size):
    dataset = torchvision.datasets.CIFAR10(root="../data", train=is_train,transform=augs, download=True)
    # num_workers是设置多线程加载数据
    dataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size,
                                             shuffle=is_train, num_workers=d2l.get_dataloader_workers())
    return dataloader
def train_batch_ch13(net, X, y, loss, trainer, devices):
    """用多GPU进行小批量训练"""
    if isinstance(X, list):  # 判断X是不是一个tensor列表
        # 微调BERT中所需
        X = [x.to(devices[0]) for x in X] # 如果是的话,需要将输入数据中的每个tensor都放在第一个GPU设备(主设备)上进行处理
    else:
        X = X.to(devices[0])
    y = y.to(devices[0])
    net.train() # ?
    trainer.zero_grad() # ?
    pred = net(X)
    l = loss(pred, y)
    l.sum().backward()
    trainer.step()
    train_loss_sum = l.sum()
    train_acc_sum = d2l.accuracy(pred, y) # 总正确个数
    return train_loss_sum, train_acc_sum
# 上个函数是小批量数据进行多GPU训练,下面函数是对整个模型的训练操作
def train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs,devices=d2l.try_all_gpus()):
    # 定义一个时间用来计时,最后要算每秒处理多少图片,num_batches,是一个train_iter的图片数
    timer, num_batches = d2l.Timer(), len(train_iter)
    # 画图,设置x轴,y轴的范围
    animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0, 1],
    # 设置每条线的标签
    legend=['train loss', 'train acc', 'test acc'])
    # devices是多个GPU设备列表,devices[0]表示使用第一个GPU设备作为主设备,主设备用来存储模型参数和计算梯度。其他设备则用于并行计算。这样可以确保主设备具有最新的权重更新,并且在训练或推断过程中把数据传输到其他设备进行并行计算。
    net = nn.DataParallel(net, device_ids=devices).to(devices[0])
    best_test_acc = 0
    for epoch in range(num_epochs):
        # 4个维度:储存训练损失,训练准确度,样本数,元素总数
        metric = d2l.Accumulator(4)
        for i, (features, labels) in enumerate(train_iter):
            timer.start()
            # 调用训练小批量数据的函数
            l, acc = train_batch_ch13(net, features, labels, loss, trainer, devices)
            # labels.shape[0]是行数,即样本个数。 labels.numel()是元素的总数,如label.shape=(3,4),那么labels.numel()=3x4=12
            metric.add(l, acc, labels.shape[0], labels.numel()) # (loss,准确率,样本个数,元素总数)
            timer.stop()
            '''
                num_batches // 5 是总批次的1/5,(i + 1) % (num_batches // 5) == 0表示,每过1/5总批次,就做如下操作
                i == num_batches - 1 表示是否到最后一个批次
                train_l = metric[0](总损失) / metric[2](样本个数)
                train_acc = metric[1] (准确预测的个数) / metric[3] (标签总数)
            '''
            if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:
                avg_loss =  metric[0] / metric[2]
                avg_acc = metric[1] / metric[3]
                # test_acc先空着,因为要一个批次结束才开始测准确率
                animator.add(epoch + (i + 1) / num_batches,(avg_loss,avg_acc,None))
        test_acc = d2l.evaluate_accuracy_gpu(net, test_iter)
        if test_acc>best_test_acc:
            best_test_acc = test_acc
        animator.add(epoch + 1, (None, None, test_acc))
    print(f'loss {metric[0] / metric[2]:.3f}, train acc 'f'{metric[1] / metric[3]:.3f},'
          f' test acc {test_acc:.3f}, best test acc {best_test_acc:.3f}')
    print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec on 'f'{str(devices)}')
'''
    定义train_with_data_aug函数,使用图像增广来训练模型。该函数获取所有的GPU,并使用Adam作为训练的优化算法,将图像增广应用于训练集,最后调用刚刚定义的用于训练和评估模型的train_ch13函数。
'''
def train_with_data_aug(train_augs, test_augs, net,epoch=10, lr=0.001,batch_size=256):
    train_iter = load_cifar10(True, train_augs, batch_size)
    test_iter = load_cifar10(False, test_augs, batch_size)
    loss = nn.CrossEntropyLoss(reduction="none")
    trainer = torch.optim.Adam(net.parameters(), lr=lr)

    '''开始计时'''
    start_time = time.time()

    # 开始训练
    train_ch13(net, train_iter, test_iter, loss, trainer, epoch, devices)

    '''计时结束'''
    end_time = time.time()
    run_time = end_time - start_time
    # 将输出的秒数保留两位小数
    if int(run_time)<60:
        print(f'{round(run_time,2)}s')
    else:
        print(f'{round(run_time/60,2)}minutes')

模型架构

batch_size, devices, net,lr = 256, d2l.try_all_gpus(), d2l.resnet18(10, 3),0.001 # (类别数,输入通道数(3通道的图片,所以是3))
def init_weights(m):
    if type(m) in [nn.Linear, nn.Conv2d]:
        nn.init.xavier_uniform_(m.weight) # 按照正太分布随机生成不重复,均值为0,方差为1的数作为初始权重
net.apply(init_weights)
Sequential(
  (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (2): ReLU()
  (resnet_block1): Sequential(
    (0): Residual(
      (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    )
    (1): Residual(
      (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    )
  )
  (resnet_block2): Sequential(
    (0): Residual(
      (conv1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
      (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (conv3): Conv2d(64, 128, kernel_size=(1, 1), stride=(2, 2))
      (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    )
    (1): Residual(
      (conv1): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    )
  )
  (resnet_block3): Sequential(
    (0): Residual(
      (conv1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
      (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (conv3): Conv2d(128, 256, kernel_size=(1, 1), stride=(2, 2))
      (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    )
    (1): Residual(
      (conv1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    )
  )
  (resnet_block4): Sequential(
    (0): Residual(
      (conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
      (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (conv3): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2))
      (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    )
    (1): Residual(
      (conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    )
  )
  (global_avg_pool): AdaptiveAvgPool2d(output_size=(1, 1))
  (fc): Sequential(
    (0): Flatten(start_dim=1, end_dim=-1)
    (1): Linear(in_features=512, out_features=10, bias=True)
  )
)

无图片增广的训练代码和结果

batch_size, epoch,devices, net,lr = 256,20, d2l.try_all_gpus(), d2l.resnet18(10, 3),0.001 # (类别数,输入通道数(3通道的图片,所以是3))
def init_weights(m):
    if type(m) in [nn.Linear, nn.Conv2d]:
        nn.init.xavier_uniform_(m.weight) # 按照正太分布随机生成不重复,均值为0,方差为1的数作为初始权重
net.apply(init_weights)
#使用ToTensor实例将一批图像转换为深度学习框架所要求的格式,即形状为(批量大小,通道数,高度,宽度)的32位浮点数,取值范围为0〜1。
train_augs = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])
test_augs = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])
# 开始训练
train_with_data_aug(train_augs, test_augs, net=net,epoch=epoch,lr=lr,batch_size=batch_size)

在这里插入图片描述

加入图片增广的训练代码和结果

batch_size,epoch, devices, net,lr = 256,20, d2l.try_all_gpus(), d2l.resnet18(10, 3),0.001 # (类别数,输入通道数(3通道的图片,所以是3))
def init_weights(m):
    if type(m) in [nn.Linear, nn.Conv2d]:
        nn.init.xavier_uniform_(m.weight) # 按照正太分布随机生成不重复,均值为0,方差为1的数作为初始权重
net.apply(init_weights)
# 使用最简单的随机左右翻转,使用ToTensor实例将一批图像转换为深度学习框架所要求的格式,即形状为(批量大小,通道数,高度,宽度)的32位浮点数,取值范围为0〜1。
train_augs = torchvision.transforms.Compose([torchvision.transforms.RandomHorizontalFlip(),
                                             torchvision.transforms.ToTensor()])
test_augs = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])
# 开始训练
train_with_data_aug(train_augs, test_augs, net=net,epoch=epoch,lr=lr,batch_size=batch_size)

在这里插入图片描述
对比没有增广的,准确率提升了4点左右

图片增广+Normalize()归一化的训练代码和结果

batch_size,epoch, devices, net,lr = 256,20, d2l.try_all_gpus(), d2l.resnet18(10, 3),0.001 # ResNet(类别数,输入通道数(3通道的图片,所以是3))
def init_weights(m):
    if type(m) in [nn.Linear, nn.Conv2d]:
        nn.init.xavier_uniform_(m.weight) # 按照正太分布随机生成不重复,均值为0,方差为1的数作为初始权重
net.apply(init_weights)
'''
    [0.485, 0.456, 0.406]是均值(mean),[0.229, 0.224, 0.225]是标准差(standard deviation)。
    对于每个RGB通道,减去相应的均值,再除以相应的标准差,即可完成归一化处理。
    这些均值和标准差是基于ImageNet数据集计算得出的,并且在训练深度学习模型时被广泛使用
'''
normalize = torchvision.transforms.Normalize([0.485, 0.456, 0.406],
                                             [0.229, 0.224, 0.225])
# 使用最简单的随机左右翻转,使用ToTensor实例将一批图像转换为深度学习框架所要求的格式,即形状为(批量大小,通道数,高度,宽度)的32位浮点数,取值范围为0〜1。
train_augs = torchvision.transforms.Compose([torchvision.transforms.RandomHorizontalFlip(),
                                             torchvision.transforms.ToTensor(),
                                             normalize])
test_augs = torchvision.transforms.Compose([torchvision.transforms.ToTensor(),
                                            normalize])
# 开始训练
train_with_data_aug(train_augs, test_augs, net,lr=lr,batch_size=batch_size)

在这里插入图片描述

补充

transforms.ToTensor()把图像的取值范围压缩到了[0,1]与transforms.Normalize()归一化的区别

**ToTensor**的作用是将导入的图片转换为Tensor的格式,
1.导入的图片为PIL image 或者 numpy.nadrry格式的图片,其shape为(HxWxC)数值范围在[0,255],转换之后shape为(CxHxw)
2.数值范围在[0,1],方法是直接 每个元素/255

**Normalize()**作用是将图片在每个通道上做标准化处理,即将每个通道上的特征减去均值,再除以方差。

net = nn.DataParallel(net, device_ids=devices).to(devices[0])解读

这行代码的作用是将神经网络模型(net)包装成一个并行模型,以便在多个GPU设备上进行训练或推断。`nn.DataParallel`是PyTorch框架提供的一个工具类,用于并行地在多个GPU上运行模型。

在给定的代码中,`devices`是一个包含了多个GPU设备的列表,`devices[0]`表示使用第一个GPU设备作为主设备。`nn.DataParallel(net, device_ids=devices)`会将`net`模型复制到所有列出的GPU设备上,并自动进行数据的分割和并行计算。

最后,通过调用`.to(devices[0])`,确保模型和数据都在主设备上。主设备是用来存储模型参数和计算梯度的设备,其他设备则用于并行计算。这样可以确保主设备具有最新的权重更新,并且在训练或推断过程中把数据传输到其他设备进行并行计算。

normalize = torchvision.transforms.Normalize( [0.485, 0.456, 0.406], [0.229, 0.224, 0.225])

其中[0.485, 0.456, 0.406]是均值(mean),[0.229, 0.224, 0.225]是标准差(standard deviation)。对于每个RGB通道,减去相应的均值,再除以相应的标准差,即可完成归一化处理。

需要注意的是,这些均值和标准差是基于ImageNet数据集计算得出的,并且在训练深度学习模型时被广泛使用。但在某些特定的应用场景下,可能需要根据具体数据集的特点重新计算和调整这些值。

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

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

相关文章

探索未知,即刻搭建AI原生应用!WAVE SUMMIT Workshop等你来参加

你是否希望掌握大模型开发的秘诀&#xff1f;你是否渴望得到实践操作的机会&#xff1f;如果你的心中充满热情和期待&#xff0c;那么&#xff0c;WAVE SUMMIT 2023特别设置的Workshop将会是你的知识启航站&#xff01; 本次Workshop专注于AI开发与大模型应用&#xff0c;邀请一…

Python调用阿里云短信接口

注&#xff1a; 要使用Python调用阿里云的短信服务&#xff0c;你可以使用阿里云官方提供的SDK&#xff0c;即阿里云SDK for Python。首先&#xff0c;确保你已经在阿里云上创建了短信服务并获取了相应的Access Key ID和Access Key Secret。 1、准备工作 阿里云短信网址 1、…

使用C语言实现UDP消息接收

目录 简介:步骤:步骤 1: 创建套接字步骤 2: 接收消息步骤 3: 完成 函数及变量解释总结: 简介: 在网络通信中&#xff0c;UDP&#xff08;User Datagram Protocol&#xff09;是一种无连接协议&#xff0c;它提供了一种快速、高效的数据传输方法。本文将向您展示如何使用C语言编…

Effective Java笔记(33)优先考虑类型安全的异构容器

泛型最常用于集合&#xff0c;如 Set<E &#xff1e;和 Map<K ,V&#xff1e;&#xff0c;以及单个元素的容器 &#xff0c;如 ThreadLocal<T>和 AtomicReference<T&#xff1e; 。 在所有这些用法中&#xff0c;它都充当被参数化了的容器 。 这样就限制每个容器…

基于C函数封装的C++读写锁(附源码)

C++常用功能源码系列 文章目录 C++常用功能源码系列前言一、读写锁是什么二、读写锁封装源码三、读写锁注意点总结前言 本文是C/C++常用功能代码封装专栏的导航贴。部分来源于实战项目中的部分功能提炼,希望能够达到你在自己的项目中拿来就用的效果,这样更好的服务于工作实践…

Vue组件的嵌套关系;父组件传递子组件props;子组件传递给父组件$emit;自定义事件;案例

目录 1_Vue组件的嵌套关系1.1_认识组件的嵌套1.2_组件的拆分1.3_组件的通信 2_父组件传递子组件props2.1_父子组件之间通信的方式2.2_父组件传递给子组件2.3_Props的对象用法 3_子组件传递给父组件$emit4_自定义事件(了解)5_小案例6_补充 1_Vue组件的嵌套关系 1.1_认识组件的嵌…

java 11 新特效解读(2)

目录 全新的HTTP 客户端API 更简化的编译运行程序 废弃Nashorn引擎 ZGC 优势&#xff1a; ZGC的设计目标是&#xff1a; 在当前JDK中看不到什么&#xff1f; 一个标准化和轻量级的JSON API 新的货币API 展望 全新的HTTP 客户端API HTTP&#xff0c;用于传输网页的…

C++实现俄罗斯方块(源码+详解)

&#x1f442; Take me Hand Acoustic - Ccile Corbel - 单曲 - 网易云音乐 源码Debug工具 &#xff08;1&#xff09;cppreference.com &#xff08;主&#xff09; &#xff08;2&#xff09;必应 (bing.com) &#xff08;3&#xff09;GPT&#xff08;主&#xff09; &#…

Java多线程(3)---锁策略、CAS和JUC

目录 前言 一.锁策略 1.1乐观锁和悲观锁 ⭐ 两者的概念 ⭐实现方法 1.2读写锁 ⭐概念 ⭐实现方法 1.3重量级锁和轻量级锁 1.4自旋锁和挂起等待锁 ⭐概念 ⭐代码实现 1.5公平锁和非公平锁 1.6可重入锁和不可重入锁 二.CAS 2.1为什么需要CAS 2.2CAS是什么 ⭐CAS…

那些年的golang开发经验记录

goland 问题CreateProcess error216, 该版本的 %1 与你运行的 Windows 版本不兼容。请查看计算机的系统信息&#xff0c;然后联系软件发布者 Cannot run program "......" (in directory "D:\project\go\awesomeProject\src\test"): CreateProcess error2…

c语言内存管理

1、局部变量&静态局部变量 1、局部变量 a、作用域&#xff1a;在定义变量的{}之内有效 b、声明周期&#xff1a;程序运行至变量定义处开辟内存空间&#xff0c;所在函数运行结束之后释放空间 c、未初始化的变量值&#xff1a;随机 2、静态局部变量 a、作用域&#xff1…

AI人工智能简史

最近学习AI&#xff0c;顺便整理了一份AI人工智能简史&#xff0c;大家参考&#xff1a; 1951年 第一台神经网络机&#xff0c;称为SNARC&#xff1b;1956年 达特茅斯学院会议&#xff0c;正式确立了人工智能的研究领域&#xff1b;1966年 MIT发明ELIZA人机心理治疗对话程序&a…

Python测试框架pytest:常用参数、查找子集、参数化、跳过

Pytest是一个基于python的测试框架&#xff0c;用于编写和执行测试代码。pytest主要用于API测试&#xff0c;可以编写代码来测试API、数据库、UI等。 pytest是一个非常成熟的全功能的Python测试框架&#xff0c;主要有以下几个优点&#xff1a; 简单灵活&#xff0c;容易上手。…

检测ChatGPT生成内容的工具经常误报,怪不得OpenAI停用它

​近日&#xff0c;加利福尼亚大学戴维斯分校的学生被指控使用ChaGPT作弊。他的老师指控他在历史考试中使用了ChatGPT&#xff0c;这一指控得到了某生成式内容检测工具的支持。然而&#xff0c;该名学生坚决否认这一指控&#xff0c;他通过提供在线文档的编辑日志为自己洗清了嫌…

时序预测 | MATLAB实现EEMD-GRU、GRU集合经验模态分解结合门控循环单元时间序列预测对比

时序预测 | MATLAB实现EEMD-GRU、GRU集合经验模态分解结合门控循环单元时间序列预测对比 目录 时序预测 | MATLAB实现EEMD-GRU、GRU集合经验模态分解结合门控循环单元时间序列预测对比效果一览基本介绍模型搭建程序设计参考资料 效果一览 基本介绍 1.MATLAB实现EEMD-GRU、GRU时…

vue3多页面配置你一定会遇到的问题,踩坑指南

vue3实现多页面打包容易&#xff0c;关键是如何实现本地的开发和调试&#xff1f;我们接下来解决如下几个问题&#xff1a; 1 多页面项目的项目结构是怎样的&#xff1f; --public--src---App.vue---main.js---page1. ---App.vue---main.js----home.vue----list.vue---page2.…

Hadoop集群部署-完全分布式

文章目录 一、概述二、架构三、部署1. 基础环境配置2. 创建hadoop用户并且生成密钥3. 配置三台服务器免密登录4. Zookeeper安装5. JDK与Hadoop安装6. 配置环境变量7. 启动Zookeeper8. 配置HDFS9. 启动journalnode10. master节点格式化11. 配置YARN12. Hadoop开启Histotryserver…

无涯教程-Perl - lc函数

描述 此函数返回小写版本的EXPR,如果省略EXPR,则返回$_。 语法 以下是此函数的简单语法- lc EXPRlc返回值 此函数返回小写版本的EXPR,如果省略EXPR,则返回$_。 例 以下是显示其基本用法的示例代码- #!/usr/bin/perl$orig_string"This is Test and CAPITAL"; …

腾讯云服务器租用价格表_轻量_CVM_GPU(2023更新)

腾讯云服务器租用费用表新鲜出炉2023年更新&#xff0c;轻量应用服务器2核2G4M带宽112元一年&#xff0c;540元三年、2核4G5M带宽218元一年&#xff0c;2核4G5M带宽756元三年、云服务器CVM S5实例2核2G配置280.8元一年、GPU服务器GN10Xp实例145元7天&#xff0c;腾讯云服务器网…

黑马程序员Vue全套视频

Vue 2 创建vue实例 示例: <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><script src"https://cdn.jsdelivr.net/npm/vue2.…