百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

超全总结!玩转Pytorch张量(Tensor)!

toyiye 2024-06-21 12:40 18 浏览 0 评论

什么是张量(Tensor)?

在深度学习领域,PyTorch是一个广泛应用的开源库,Tensor之于PyTorch就好比是array之于Numpy或者DataFrame之于Pandas,都是构建了整个框架中最为底层的核心数据结构。Pytorch中的所有操作都是在张量的基础上进行的。

PyTorch官网对其的定义如下:

也就是说,一个Tensor是一个包含单一数据类型的多维矩阵。通常,其多维特性用三维及以上的矩阵来描述,例如下图所示:单个元素为标量(scalar),一个序列为向量(vector),多个序列组成的平面为矩阵(matrix),多个平面组成的立方体为张量(tensor)。

当然,张量也无需严格限制在三维及以上才叫张量,就像矩阵也有一维、二维矩阵乃至多维矩阵之分一样。

「在深度学习的范畴内,标量、向量和矩阵也可分为称为零维张量、一维张量、二维张量。」

为什么深度学习要搞出Tensor?

熟悉机器学习的小伙伴们应该都知道,有监督机器学习模型的输入X通常是多个特征列组成的二维矩阵,输出y是单个特征列组成的标签向量或多个特征列组成的二维矩阵。那么深度学习中,为何要定义多维矩阵Tensor呢?

深度学习当前最成熟的两大应用方向莫过于CV和NLP,其中CV面向图像和视频,NLP面向语音和文本,二者分别以卷积神经网络和循环神经网络作为核心基础模块,且标准输入数据集都是至少三维以上。其中,

  • 图像数据集:至少包含三个维度(样本数Nx图像高度Hx图像宽度W);如果是彩色图像,则还需增加一个通道C,包含四个维度(NxHxWxC);如果是视频帧,可能还需要增加一个维度T,表示将视频划分为T个等时长的片段。
  • 文本数据集:包含三个维度(样本数N×序列长度L×特征数H)。

因此,输入学习模型的输入数据结构通常都要三维以上,这也就促使了Tensor的诞生。

Tensor创建

Pytorch可基于给定数据手动创建Tensor,并提供了多种方式:

使用torch.tensor()函数直接创建

在PyTorch中,torch.tensor()函数用于直接从Python的数据结构(如列表、元组或NumPy数组)中创建一个新的张量。

"""
data:数据,可以是list,numpy
dtype:数据类型,默认与data对应
device:张量所在的设备(cuda或cpu)
requires_grad:是否需要梯度
pin_memory:是否存于锁存内存
"""
torch.tensor(data,dtype=None,device=None,requires_grad=False,pin_memory=False)

pin_memor用于实现锁页内存,创建DataLoader时,设置pin_memory=True,则意味着生成的Tensor数据最开始是属于内存中的锁页内存,这样将内存的Tensor转义到GPU的显存就会更快一些。

以下是使用torch.tensor()创建张量的基本示例:

import numpy as np
import torch
arr = np.ones((3, 3))
'''
[[1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]]
'''
print(arr)
# ndarray的数据类型:float64
print("ndarray的数据类型:", arr.dtype)
t= torch.tensor(arr)
'''
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]], dtype=torch.float64)
'''
print(t)

如需创建一个放在GPU的数据,则可做如下修改,运行结果同上。

import numpy as np
import torch
device= torch.device("cuda" if torch.cuda.is_available() else 'cpu')
arr = np.ones((3, 3))
print("ndarray的数据类型:", arr.dtype)
t = torch.tensor(arr, device=device)
print(t)

从numpy创建Tensor

torch.from_numpy(ndarray)

利用该方法创建的tensor与原ndarray共享内存,当修改其中一个数据,另外一个也会被更新。

import numpy as np
import torch
# 创建一个numpy数组
numpy_array = np.array([[1, 2, 3], [4, 5, 6]])

# 从numpy数组创建一个Tensor,并保持数据共享(更改Tensor内容会同时改变numpy数组)
tensor_from_numpy = torch.from_numpy(numpy_array)
print(tensor_from_numpy)

# 输出:
# tensor([[1, 2, 3],
#         [4, 5, 6]], dtype=torch.int32)

# 修改tensor,array也会被修改
print("# -----------修改tensor--------------*")
t[0, 0] = -1
print("numpy array: ", arr)
# 输出:
# numpy array:  [[-1  2  3]
#               [ 4  5  6]]
print("tensor : ", t)
# 输出:
# tensor([[-1, 2, 3],
#         [4, 5, 6]], dtype=torch.int32)

根据数值创建张量

  1. torch.zeros():根据size创建全0张量
'''
size:张量的形状
out:输出的张量,如果指定了out,torch.zeros()返回的张量则会和out共享同一个内存地址
layout:内存中的布局方式,有strided,sparse_coo等。如果是稀疏矩阵,则可以设置为sparse_coo以减少内存占用
device:张量所在的设备(cuda或cpu)
requires_grad:是否需要梯度
'''
torch.zeros(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
import torch
out_t = torch.tensor([1])
print(f"out_t初始值:{out_t}")
#指定out
t = torch.zeros((3, 3), out=out_t)
print(f"t:\n{t}")
print(f"out_t更新值:\n{out_t}")
# id是取内存地址,t和out_t是同一个内存地址
print(id(t), id(out_t), id(t) == id(out_t))

运行结果如下,由此可见,和out_t最终共享同一个内存地址。

out_t初始值:tensor([1])
t:
tensor([[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]])
out_t更新值:
tensor([[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]])
2083081770704 2083081770704 True
  1. torch.zeros_like:根据input形状创建全0张量
torch.zeros_like(input, dtype=None, layout=None, device=None, requires_grad=False, memory_format=torch.preserve_format)

同理还有全1张量的创建:torch.ones(),torch.ones_like()

  1. torch.full() & torch.full_like():创建自定义某一数值的张量。
'''
size:张量的形状,例如(3,3)
fill_value:张量中每一个元素的值。
'''

torch.full(size, fill_value, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

例如:

import torch
t = torch.full((4, 4), 10)
print(t)
'''
tensor([[10, 10, 10, 10],
        [10, 10, 10, 10],
        [10, 10, 10, 10],
        [10, 10, 10, 10]])
'''
  1. 创建等差的一维张量。

?注意区间为:[start,end)。

'''
start:数列起始值,默认为0
end:数列结束值,开区间,取不到结束值
step:数列公差,默认为1
'''
torch.arange(start=0, end, step=1, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

例如:

import torch
t = torch.arange(2, 10, 2)
print(t) # tensor([2, 4, 6, 8])
  1. torch.linspace():创建均分的一维张量

?注意区间为:[start,end]。

'''
step:数列长度(元素个数)
'''
torch.linspace(start, end, steps=100, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
import torch
t = torch.linspace(2, 10, 3)
print(t) # tensor([ 2.,  6., 10.])
  1. torch.logspace():创建对数均分的一维张量

?注意区间为:[start,end]。

'''
step:数列长度(元素个数)
base:对数函数的底,默认为 10
'''
torch.logspace(start, end, steps, base=10, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
import torch
t = torch.logspace(2, 4, 3)
print(t) # tensor([100.,  1000., 10000.])
  1. torch.eye():创建单位对角矩阵(2维张量)

?默认输出方阵。

'''
n: 矩阵行数。因为是方阵,通常只设置n
m: 矩阵列数
'''
torch.eye(n, m=None, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
import torch
t = torch.eye(3)
h = torch.eye(3,4)
print(f"t:{t}")
print(f"h:{h}")

运行结果如下:

t:tensor([[1., 0., 0.],
        [0., 1., 0.],
        [0., 0., 1.]])
h:tensor([[1., 0., 0., 0.],
        [0., 1., 0., 0.],
        [0., 0., 1., 0.]])

根据概率创建张量

  1. torch.normal():生成正态分布(高斯分布)

?返回一个张量,包含从给定参数means、std的离散正态分布中抽取的随机数。

'''
mean:均值
std:标准差
'''
torch.normal(mean, std, *, generator=None, out=None)

包含4种模式:

  • mean为标量,std为标量,此时需要设置size。
import torch
t_normal = torch.normal(0., 1., size=(4,))
# t_normal:tensor([ 0.7098, 1.5432, -0.1568, -0.6350])
print(f"t_normal:{t_normal}")
  • mean为张量,std为标量。
import torch
mean = torch.arange(1, 5, dtype=torch.float)
std = 1
t_normal = torch.normal(mean, std)
'''
mean:tensor([1., 2., 3., 4.])
std:1
'''
print("mean:{}\nstd:{}".format(mean, std))
#tensor([2.2450, 1.0230, 2.0299, 4.5855])
print(t_normal)

这4个数采样分布的均值不同,但是方差都是 1。

  • mean为标量,std为张量。
import torch
std = torch.arange(1, 5, dtype=torch.float)
mean = 2
t_normal = torch.normal(mean, std)
'''
mean:2
std:tensor([1., 2., 3., 4.])
'''
print("mean:{}\nstd:{}".format(mean, std))
# tensor([ 1.8482, 4.8143, -3.5074, 4.2010])
print(t_normal)
  • mean为张量,std为张量。
import torch
mean = torch.arange(1, 5, dtype=torch.float)
std = torch.arange(1, 5, dtype=torch.float)
t_normal = torch.normal(mean, std)
'''
mean:tensor([1., 2., 3., 4.])
std:tensor([1., 2., 3., 4.])
'''
print("mean:{}\nstd:{}".format(mean, std))
# tensor([ 0.8195, -3.9112, 4.8498, 2.3934])
print(t_normal)

其中0.8195是从正态分布N(1,1)中采样得到的,-3.9112是从正态分布N(2,2)中采样得到的,其他数字以此类推。

Tensor属性

Tensor形状

张量具有如下形状属性:

  • Tensor.ndim:张量的维度,例如向量的维度为1,矩阵的维度为2。
  • Tensor.shape:张量每个维度上元素的数量。
  • Tensor.shape[n]:张量第n维的大小。第n维也称为轴(axis)。
  • Tensor.numel:张量中全部元素的个数。

如下是创建一个四维Tensor,并通过图形直观表达以上几个概念的关系。

import torch
Tensor=torch.ones([2,3,4,5])
print("Number of dimensions:", Tensor.ndim)
print("Shape of Tensor:", Tensor.shape)
print("Elements number along axis 0 of Tensor:", Tensor.shape[0])
print("Elements number along the last axis of Tensor:", Tensor.shape[-1])
print('Number of elements in Tensor: ', Tensor.numel())  #用.numel表示元素个数

Tensor的axis、shape、dimension、ndim之间的关系如下图所示。

Tensor数据类型

torch.dtype属性标识了torch.Tensor的数据类型。PyTorch有八种不同的数据类型:

例如:

import torch
Tensor=torch.ones([2,3,4,5])
# Data Type of every element: torch.float32
print("Data Type of every element:", Tensor.dtype)

Tensor所在设备

如图所示,我们可以看到每种类型的数据都有一个CPU和一个GPU版本,因此我们对张量进行处理的时候需要指定一个设备,它要么是CPU要么是GPU,这是数据被分配的位置,这决定了给定张量的张量计算位置。

Pytorch支持多种设备的使用,我们可以用torch.device来创建一个设备,并指定索引,例如:

device=torch.device('cuda:0')

输出结果为:device(type='cuda',index=0),可看到类型为'cuda',即GPU,索引0表示为第一个GPU。

Tensor操作

形状重置

Tensor的shape可通过torch.reshape接口来改变。例如:

import torch
Tensor =torch.tensor([[[1, 2, 3, 4, 5],
                        [6, 7, 8, 9, 10]],
                      [[11, 12, 13, 14, 15],
                        [16, 17, 18, 19, 20]],
                      [[21, 22, 23, 24, 25],
                        [26, 27, 28, 29, 30]]])
print("the shape of Tensor:", Tensor.shape)
#利用reshape改变形状
reshape_Tensor = torch.reshape(Tensor, [2, 5, 3])
print("After reshape:\n", reshape_Tensor)

从输出结果看,将张量从[3, 2, 5]的形状reshape为[2, 5, 3]的形状时,张量内的数据不会发生改变,元素顺序也没有发生改变,只有数据形状发生了改变。

在指定新的shape时存在一些技巧:

  • -1 表示这个维度的值是从Tensor的元素总数和剩余维度自动推断出来的。因此,有且只有一个维度可以被设置为-1。
  • 0 表示该维度的元素数量与原值相同,因此shape中0的索引值必须小于Tensor的维度(索引值从 0 开始计,如第 1 维的索引值是 0,第二维的索引值是 1)。

例如:

# 直接指定目标 shape
origin:[3, 2, 5] reshape:[3, 10] actual: [3, 10]
# 转换为 1 维,维度根据元素总数推断出来是 3*2*5=30
origin:[3, 2, 5] reshape:[-1] actual: [30]
# 转换为 2 维,固定一个维度 5,另一个维度根据元素总数推断出来是 30÷5=6
origin:[3, 2, 5] reshape:[-1, 5]      actual: [6, 5]
# reshape:[0, -1]中 0 的索引值为 0,按照规则
# 转换后第 0 维的元素数量与原始 Tensor 第 0 维的元素数量相同,为3
# 第 1 维的元素数量根据元素总值计算得出为 30÷3=10。
origin:[3, 2, 5] reshape:[0, -1]      actual: [3, 10]
# reshape:[3, 1, 0]中 0 的索引值为 2
# 但原 Tensor 只有 2 维,无法找到与第 3 维对应的元素数量,因此出错。
origin:[3, 2] reshape:[3, 1, 0]       error:

另外还可以通过如下方式改变shape。

  • torch.squeeze:可实现Tensor的降维操作,即把Tensor中尺寸为1的维度删除。
  • torch.unsqueeze:可实现Tensor的升维操作,即向Tensor中某个位置插入尺寸为1的维度。
  • torch.flatten,将Tensor的数据在指定的连续维度上展平。
  • torch.transpose,对Tensor的数据进行重排。

索引和切片

通过索引或切片方式可访问或修改Tensor。

「访问Tensor」

import torch
ndim_2_Tensor = torch.tensor([[0, 1, 2, 3],
                               [4, 5, 6, 7],
                               [8, 9, 10, 11]])
print("Origin Tensor:\n", ndim_2_Tensor.numpy())
#索引或切片的第一个值对应第 0 维,第二个值对应第 1 维,
#依次类推,如果某个维度上未指定索引,则默认为 :
#所以下面两种操作结果一样
print("First row:", ndim_2_Tensor[0].numpy())
print("First row:", ndim_2_Tensor[0, :].numpy())
print("First column:", ndim_2_Tensor[:, 0].numpy())
print("Last column:", ndim_2_Tensor[:, -1].numpy())
print("All element:\n", ndim_2_Tensor[:].numpy())
print("First row and second column:", ndim_2_Tensor[0, 1].numpy())

「修改Tensor」

与访问张量类似,可以在单个或多个轴上通过索引或切片操作来修改张量。

import torch
ndim_2_Tensor = torch.ones([2, 3])
ndim_2_Tensor = ndim_2_Tensor.to(torch.float32)
print('Origin Tensor:\n ', ndim_2_Tensor)
# 修改第1维为0
ndim_2_Tensor[0] = 0
print('change Tensor:\n ', ndim_2_Tensor)
# 修改第1维为2.1
ndim_2_Tensor[0:1] = 2.1
print('change Tensor:\n ', ndim_2_Tensor)
# 修改全部Tensor
ndim_2_Tensor[...] = 3
print('change Tensor:\n ', ndim_2_Tensor)

Tensor运算

张量支持包括基础数学运算、逻辑运算、矩阵运算等100余种运算操作。

「数学运算」

x.abs()                       # 逐元素取绝对值
x.ceil()                      # 逐元素向上取整
x.floor()                     # 逐元素向下取整
x.round()                     # 逐元素四舍五入
x.exp()                       # 逐元素计算自然常数为底的指数
x.log()                       # 逐元素计算x的自然对数
x.reciprocal()                # 逐元素求倒数
x.square()                    # 逐元素计算平方
x.sqrt()                      # 逐元素计算平方根
x.sin()                       # 逐元素计算正弦
x.cos()                       # 逐元素计算余弦
x.add(y)                      # 逐元素加
x.subtract(y)                 # 逐元素减
x.multiply(y)                 # 逐元素乘(积)
x.divide(y)                   # 逐元素除
x.mod(y)                      # 逐元素除并取余
x.pow(y)                      # 逐元素幂
x.max()                       # 指定维度上元素最大值,默认为全部维度
x.min()                       # 指定维度上元素最小值,默认为全部维度
x.prod()                      # 指定维度上元素累乘,默认为全部维度
x.sum()                       # 指定维度上元素的和,默认为全部维度

「逻辑运算」

x.isfinite()                  # 判断Tensor中元素是否是有限的数字,即不包括inf与nan
x.equal_all(y)                # 判断两个Tensor的全部元素是否相等,并返回形状为[1]的布尔类Tensor
x.equal(y)                    # 判断两个Tensor的每个元素是否相等,并返回形状相同的布尔类Tensor
x.not_equal(y)                # 判断两个Tensor的每个元素是否不相等
x.less_than(y)                # 判断Tensor x的元素是否小于Tensor y的对应元素
x.less_equal(y)               # 判断Tensor x的元素是否小于或等于Tensor y的对应元素
x.greater_than(y)             # 判断Tensor x的元素是否大于Tensor y的对应元素
x.greater_equal(y)            # 判断Tensor x的元素是否大于或等于Tensor y的对应元素
x.allclose(y)                 # 判断两个Tensor的全部元素是否接近

「矩阵运算」

x.t()                         # 矩阵转置
x.transpose([1, 0])           # 交换第 0 维与第 1 维的顺序
x.norm('fro')                 # 矩阵的弗罗贝尼乌斯范数
x.dist(y, p=2)                # 矩阵(x-y)的2范数
x.matmul(y)                   # 矩阵乘法

Tensor广播机制

深度学习任务中,通常不可避免会遇到需要使用较小形状的Tensor与较大形状的Tensor执行计算的情况。此时,则需要将较小形状的Tensor扩展到与较大形状的Tensor一样的形状,以便于匹配计算,但是又「不会」对较小形状Tensor进行「数据拷贝」操作,从而「提升算法实现的运算效率」。这即是广播机制。

Tensor广播机制通常遵循如下规则:

  • 每个 Tensor 至少为一维 Tensor。
  • 从最后一个维度向前开始比较两个Tensor的形状,需要满足如下条件才能进行广播:
    • 两个Tensor的维度大小相等;或者其中一个Tensor的维度为1;或者其中一个Tensor的维度不存在。

例如:

  1. 两个Tensor的形状一致,可以广播。
import torch

x = torch.ones((2, 3, 4))
y = torch.ones((2, 3, 4))
z = x + y
print(z)
# tensor([[[2., 2., 2., 2.],
[2., 2., 2., 2.],
[2., 2., 2., 2.]],

[[2., 2., 2., 2.],
[2., 2., 2., 2.],
[2., 2., 2., 2.]]])
print(z.shape)
# torch.Size([2, 3, 4])
  1. 从最后一个维度向前依次比较:第一次y的维度大小为1,第二次x的维度大小为1,第三次x和y的维度大小相等,第四次y的维度不存在,所以x和y可以广播。
import torch

x = torch.ones((2, 3, 1, 5))
y = torch.ones((3, 4, 1))

z = x + y
print(z.shape)
# torch.Size([2, 3, 4, 5])
  1. 从最后一个维度向前依次比较:第一次比较:4不等于6,不可广播。
x = torch.ones((2, 3, 4))
y = torch.ones((2, 3, 6))
# z = x + y
# ValueError: (InvalidArgument) Broadcast dimension mismatch.

两个Tensor进行广播后的结果Tensor的形状计算规则如下:

  • 如果两个 Tensor 的形状的长度不一致,会在较小长度的形状矩阵前部添加 1,直到两个 Tensor 的形状长度相等。
  • 保证两个 Tensor 形状相等之后,每个维度上的结果维度就是当前维度上的较大值。

例如,y的形状长度为2,小于x的形状长度3,因此会在 y 的形状前部添加 1,结果就是 y 的形状变为[1, 3, 1]。广播之后z的形状为[2,3,4],且z的每一维度上的尺寸,将取x和y对应维度上尺寸的较大值,如第0维x的尺寸为2,y的尺寸为1,则z的第0维尺寸为2。

import torch

x = torch.ones((2, 1, 4))
y = torch.ones((3, 1))
z = x + y
print(z.shape)
# torch.Size([2, 3, 4])

广播机制运行过程如下图。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码