pytorch深度学习和入门实战(三)transforms数据增强和ImageFolder数据集构造详解

torch.utils.data相关部分已经在上一个博客内容讲解过,如何使用Dataset和DataLoader,请参见pytorch深度学习和入门实战(二)Dataset和DataLoader使用详解

1.数据处理工具箱概述

数据下载和预处理是机器学习、深度学习实际项目中耗时又重要的任务,
尤其是数据预处理,关系到数据质量和模型性能,往往要占据项目的大部分时间。

PyTorch涉及数据处理(数据装载、数据预处理、数据增强等)主要工具包及相互关系如图:
在这里插入图片描述
主要包括两大部分:
(1)torch.utils.data相关部分
torch.utils.data工具包,它包括以下4个类函数。
1)Dataset:是一个抽象类,其他数据集需要继承这个类,并且覆写其
中的两个方法( getitem_()、len ())。
2)DataLoader:定义一个新的迭代器,实现批量(batch)读取,打乱
数据(shuffle)并提供并行加速等功能。
3)random_split:把数据集随机拆分为给定长度的非重叠的新数据集。
4)*sampler:多种采样函数

(2)torchvision工具包相关部分
它包括4个类,各类的主要功能如下。
1)datasets:提供常用的数据集加载,设计上都是继承自
torch.utils.data.Dataset,主要包括MMIST、CIFAR10/100、ImageNet和COCO
等。
2)models:提供深度学习中各种经典的网络结构以及训练好的模型
(如果选择pretrained=True),包括AlexNet、VGG系列、ResNet系列、
Inception系列等。
3)transforms:常用的数据预处理操作,主要包括对Tensor及PIL Image
对象的操作。
4)utils:含两个函数,一个是make_grid,它能将多张图片拼接在一个
网格中;另一个是save_img,它能将Tensor保存成图片。

本节主要介绍如何使用datasets的ImageFolder处理自定义数据集,以及如何使用transforms对源数据进行预处理、增强等。下面将重点介绍transforms及ImageFolder

2. transforms的所有数据增强的api

Transforms是指常见的图像变换功能。它们可以用Compose组合在一起。

# transforms (list of Transform objects) – list of transforms to compose.
# for example
transforms.Compose([
	#将给定的 PIL.Image 进行中心切割,得到给定的 size,
	#size 可以是 tuple,(target_height, target_width)。
	#size 也可以是一个 Integer,在这种情况下,切出来的图片形状是正方形。
	transforms.CenterCrop(10),
	#切割中心点的位置随机选取
	transforms.RandomCrop(20, padding=0),
	#把一个取值范围是 [0, 255] 的 PIL.Image 或者 shape 为 (H, W, C) 的 numpy.ndarray,
	#转换为形状为 (C, H, W),取值范围是 [0, 1] 的 torch.FloatTensor
	transforms.ToTensor(),
	#规范化到[-1,1]
	transforms.Normalize(mean = (0.5, 0.5, 0.5), std = (0.5, 0.5, 0.5))
])

此外,还有torchvision.transforms.functional模块。函数转换(Functional transforms)提供对转换的细粒度控制。在构建一些比较复杂的转换流程(例如,在分割任务的情况下),这可能很有用。

# for example
import torchvision.transforms.functional as TF
import random

def my_segmentation_transforms(image, segmentation):
    if random.random() > 0.5:
        angle = random.randint(-30, 30)
        image = TF.rotate(image, angle)
        segmentation = TF.rotate(segmentation, angle)
    # more transforms ...
    return image, segmentation

ref to official api : https://pytorch.org/docs/stable/
主要有一下几类:
1)针对PIL image的处理-----> 主要包括一些图像的裁剪、翻转、亮度变化等增强
2)针对 torch.*Tensor的处理-----> tensor数据标准化和线性化等
3)转化处理(Conversion Transforms)-----> 主要是图像类型的变换,imgs->tensor, tensor->imgs
4)自定义转化处理(Generic Transforms)-----> 自定义转换函数
5)转化函数 -----> 更加细粒度的图像处理函数

2.1 针对PIL image的处理

2.1.1裁剪

中心裁剪:transforms.CenterCrop

class torchvision.transforms.CenterCrop(size)

功能:
	依据给定的 size 从中心裁剪
参数:
	size- (sequence or int),若为 sequence,则为(h,w),若为 int,则(size,size)

随机裁剪:transforms.RandomCrop
class
torchvision.transforms.RandomCrop(size, padding=None, pad_if_needed=False, fill=0, padding_mode='constant')

功能:
	依据给定的 size 随机裁剪
参数:
	size- (sequence or int),若为 sequence,则为(h,w),若为 int,则(size,size)
	padding-(sequence or int, optional),此参数是设置填充多少个 pixel。当为 int 时,图像上下左右均填充 int 个,例如 padding=4,则上下左右均填充 4 个 pixel,若为 32*32,则会变成 40*40。当为 sequence 时,若有 2 个数,则第一个数表示左右扩充多少,第二个数表示上下的。当有 4 个数时,则为左,上,右,下。
	fill- (int or tuple) 填充的值是什么(仅当填充模式为 constant 时有用)。int 时,各通道均填充该值,当长度为 3 的 tuple 时,表示 RGB 通道需要填充的值。
	padding_mode- 填充模式,这里提供了 4 种填充模式:
	1.constant,常量。2.edge 按照图片边缘的像素值来填充。3.reflect,像镜像一样对称填充(不重复边缘上的最后一个值)。 4. symmetric,像镜像一样对称填充(重复边缘上的最后一个值)

随机长宽比裁剪:transforms.RandomResizedCrop
class torchvision.transforms.RandomResizedCrop(size, scale=(0.08, 1.0), ratio=(0.75, 1.33333 33333333333), interpolation=2)

功能:
	随机大小,随机长宽比裁剪原始图片,最后将图片 resize 到设定好的 size
参数:
	size- 输出的分辨率
	scale- 随机 crop 的大小区间,如 scale=(0.08, 1.0),表示随机 crop 出来的图片会在的 0.08倍至 1 倍之间。
	ratio- 随机长宽比设置
	interpolation- 插值的方法,默认为双线性插值(PIL.Image.BILINEAR)

上下左右中心裁剪:transforms.FiveCrop
class torchvision.transforms.FiveCrop(size)

功能:对图片进行上下左右以及中心裁剪,获得 5 张图片,返回一个 4D-tensor
参数:
	size- (sequence or int),若为 sequence,则为(h,w),若为 int,则(size,size)

上下左右中心裁剪后翻转,transforms.TenCrop
class torchvision.transforms.TenCrop(size, vertical_flip=False)

功能:
	对图片进行上下左右以及中心裁剪,然后全部翻转(水平或者垂直),获得 10 张图片,返回一个 4D-tensor。
参数:
	size- (sequence or int),若为 sequence,则为(h,w),若为 int,则(size,size)
	vertical_flip (bool) - 是否垂直翻转,默认为 flase,即默认为水平翻转

2.1.2 翻转和旋转

依概率 p 水平翻转:transforms.RandomHorizontalFlip(p=0.5)
class torchvision.transforms.RandomHorizontalFlip(p=0.5)

功能:依据概率 p 对 PIL 图片进行水平翻转
参数:
	p- 概率,默认值为 0.5

依概率 p 垂直翻转:transforms.RandomVerticalFlip(p=0.5)
class torchvision.transforms.RandomVerticalFlip(p=0.5)

功能:依据概率 p 对 PIL 图片进行垂直翻转
参数:
	p- 概率,默认值为 0.5

随机旋转:transforms.RandomRotation
class torchvision.transforms.RandomRotation(degrees, resample=False, expand=False, center=None, fill=None)

功能:依 degrees 随机旋转一定角度
参数:
	degress- (sequence or float or int) ,若为单个数,如 30,则表示在(-30,+30)之间随机旋转若为 sequence,如(30,60),则表示在 30-60 度之间随机旋转
	resample- 重采样方法选择,可选PIL.Image.NEAREST, PIL.Image.BILINEAR, PIL.Image.BICUBIC,默认为最近邻
	expand- –可选扩展标志。如果为true,则展开输出以使其足够大以容纳整个旋转图像。如果为false或省略,则使输出图像与输入图像的大小相同。请注意,expand标志假定围绕中心旋转而不平移。
	center- 可选为中心旋转还是左上角旋转
	fill(n元组或int或float)–旋转图像外部区域的像素填充值。如果是int或float,则该值分别用于所有带区。所有标注栏的默认值为0。此选项仅适用于pillow>=5.2.0。

2.1.3 图像变换

填充:transforms.Pad
class torchvision.transforms.Pad(padding, fill=0, padding_mode='constant')

功能:对图像进行填充
参数:
	padding-(sequence or int, optional),此参数是设置填充多少个 pixel。当为 int 时,图像上下左右均填充 int 个,例如 padding=4,则上下左右均填充 4 个 pixel,若为 32*32,则会变成 40*40。当为 sequence 时,若有 2 个数,则第一个数表示左右扩充多少,第二个数表示上下的。当有 4 个数时,则为左,上,右,下。
fill- (int or tuple) 填充的值是什么(仅当填充模式为 constant 时有用)。int 时,各通道均填充该值,当长度为 3 的 tuple 时,表示 RGB 通道需要填充的值。
padding_mode- 填充模式,这里提供了 4 种填充模式:
1.constant,常量。2.edge 按照图片边缘的像素值来填充。3.reflect,像镜像一样对称填充(不重复边缘上的最后一个值)。 4. symmetric,像镜像一样对称填充(重复边缘上的最后一个值)

修改亮度、对比度和饱和度:transforms.ColorJitter
class torchvision.transforms.ColorJitter(brightness=0, contrast=0, saturation=0, hue=0)

功能:修改修改亮度、对比度和饱和度

转灰度图:transforms.Grayscale
class torchvision.transforms.Grayscale(num_output_channels=1)

功能:将图片转换为灰度图
参数:
	num_output_channels- (int) ,当为 1 时,正常的灰度图,当为 3 时, 3 channel with r == g == b

仿射变换:transforms.RandomAffine
class torchvision.transforms.RandomAffine(degrees, translate=None, scale=None, shear=None, resample=False, fillcolor=0)

功能:仿射变换

依概率 p 转为灰度图:transforms.RandomGrayscale
class torchvision.transforms.RandomGrayscale(p=0.1)

功能:依概率 p 将图片转换为灰度图,若通道数为 3,则 3 channel with r == g == b

resize:transforms.Resize
class torchvision.transforms.Resize(size, interpolation=2)

功能:重置图像分辨率
参数:
	size- If size is an int, if height > width, then image will be rescaled to (size * height / width, size),所以建议 size 设定为 h*w
	interpolation- 插值方法选择,默认为 PIL.Image.BILINEAR

2.1.4 对 transforms 操作,使数据增强更灵活

transforms.RandomChoice(transforms), 从给定的一系列 transforms 中选一个进行操作

transforms.RandomApply(transforms, p=0.5),给一个 transform 加上概率,依概率进行操作

	功能:给一个 transform 加上概率,以一定的概率执行该操作

transforms.RandomOrder,将 transforms 中的操作随机打乱

功能:将 transforms 中的操作顺序随机打乱

2.2 针对 torch.*Tensor的处理

2.2.1 标准化:transforms.Normalize

class torchvision.transforms.Normalize(mean, std)

功能:对数据按通道进行标准化,即先减均值,再除以标准差,注意是 h*w*c

2.2.2 线性变换:transforms.LinearTransformation()

class torchvision.transforms.LinearTransformation(transformation_matrix)

功能:对矩阵做线性变化,可用于白化处理!
	 whitening: zero-center the data, compute the data covariance matrix
参数:
	transformation_matrix (Tensor) – tensor [D x D], D = C x H x W

2.2.3随机选择图像中的矩形区域并删除其像素:transforms.RandomErasing

class torchvision.transforms.RandomErasing(p=0.5, scale=(0.02, 0.33), ratio=(0.3, 3.3), value=0, inplace=False)

功能:随机选择图像中的矩形区域并删除其像素。参见 https://arxiv.org/pdf/1708.04896.pdf
参数
	p–执行随机擦除操作的概率。
	scale–擦除区域相对于输入图像的比例范围。
	ratio–擦除区域的纵横比范围。值–删除值。默认值为0。如果是一个整型,它将被用来清除所有像素。如果图像的channel为3,则分别用于擦除R、G、B通道。如果str为“random”,则用随机值擦除每个像素。
	inplace–布尔值以使此变换就位。默认设置为False。

2.3 数据形式转化处理

2.3.1 转为 tensor,并归一化至[0-1]:transforms.ToTensor

class torchvision.transforms.ToTensor

功能:将 PIL Image 或者 ndarray 转换为 tensor,并且归一化至[0-1]
注意事项:归一化至[0-1]是直接除以 255,若自己的 ndarray 数据尺度有变化,则需要自行修改

2.3.2 将数据转换为 PILImage:transforms.ToPILImage

class torchvision.transforms.ToPILImage(mode=None)

功能:将 tensor 或者 ndarray 的数据转换为 PIL Image 类型数据
参数:
	mode- 为 None 时,为 1 通道, mode=3 通道默认转换为 RGB,4 通道默认转换为 RGBA

2,4 自定义函数转化处理

用户自定义转换函数lambd:torchvision.transforms.Lambda(lambd)

class torchvision.transforms.Lambda(lambd)

功能: 使用用户自定义的lambd函数作为转换函数
参数:
    lambd (function) – Lambda/function to be used for transform.

2.5 更加细粒度的转化函数

---------------------------------------修改色彩和饱和度等------------------------------------------------------
transforms.functional.adjust_brightness(img: torch.Tensor, brightness_factor: float) → torch.Tensor
transforms.functional.adjust_contrast(img: torch.Tensor, contrast_factor: float) → torch.Tensor
transforms.functional.adjust_gamma(img, gamma, gain=1)
transforms.functional.adjust_hue(img: torch.Tensor, hue_factor: float) → torch.Tensor
transforms.functional.adjust_saturation(img: torch.Tensor, saturation_factor: float) → torch.Tensor

---------------------------------------仿射和变换等------------------------------------------------------
transforms.functional.affine(img, angle, translate, scale, shear, resample=0, fillcolor=None)
transforms.functional.perspective(img, startpoints, endpoints, interpolation=3, fill=None)
transforms.functional.erase(img, i, j, h, w, v, inplace=False)
transforms.functional.resize(img, size, interpolation=2)
transforms.functional.pad(img, padding, fill=0, padding_mode=‘constant’)

---------------------------------------裁剪等------------------------------------------------------
transforms.functional.center_crop(img, output_size)
transforms.functional.convert_image_dtype(image: torch.Tensor, dtype: torch.dtype = torch.float32) → torch.Tensor
transforms.functional.crop(img, top, left, height, width)
transforms.functional.five_crop(img, size)
transforms.functional.ten_crop(img, size, vertical_flip=False)
transforms.functional.resized_crop(img, top, left, height, width, size, interpolation=2)

---------------------------------------翻转和旋转------------------------------------------------------
transforms.functional.hflip(img: torch.Tensor) → torch.Tensor
transforms.functional.vflip(img: torch.Tensor) → torch.Tensor
transforms.functional.rotate(img, angle, resample=False, expand=False, center=None, fill=None)

---------------------------------------图像数据类型变化等------------------------------------------------------
transforms.functional.to_grayscale(img, num_output_channels=1)
transforms.functional.to_pil_image(pic, mode=None)
transforms.functional.to_tensor(pic)
transforms.functional.pil_to_tensor(pic)
transforms.functional.normalize(tensor, mean, std, inplace=False)

2.6 举例说明

# 数据预处理设置
normMean = [0.4948052, 0.48568845, 0.44682974]
normStd = [0.24580306, 0.24236229, 0.2603115]
normTransform = transforms.Normalize(normMean, normStd)
trainTransform = transforms.Compose([
    transforms.Resize(32),
    transforms.RandomCrop(32, padding=4),
    transforms.ToTensor(),
    normTransform
])

validTransform = transforms.Compose([
    transforms.ToTensor(),
    normTransform
])

前三行设置均值,标准差,以及数据标准化:transforms.Normalize()函数,这里是以通
道为单位进行计算均值,标准差。然后用 transforms.Compose 将所需要进行的处理给 compose 起来,并且按照顺序执行!

在训练时,依次对图片进行以下操作:
1. 随机裁剪
2. Totensor
3. 数据标准化(减均值,除以标准差)

1. 随机裁剪

第一个处理是随机裁剪,在裁剪之前先对图片的上下左右均填充上 4 个 pixel,值为0,即变成一个 4040 的数据,然后再随机进行 3232 的裁剪。例如下图,是经过 transforms.RandomCrop(32, padding=4),之后的图片,其中红色框是原始图片数据,31 列是填充的 0,28-31 行也是填充的 0
在这里插入图片描述

2. Totensor

第二个处理是 transforms.ToTensor()在这里会对数据进行 transpose,原来是 hwc,会经过 img = img.transpose(0, 1).transpose(0, 2).contiguous(),变成 chw 再除以 255,使得像素值归一化至[0-1]之间,来看看 Red 通道.
来看看 27 行,30 列 原来是 8 的, 经过 ToTensor 之后变成:8/255= 0.03137255
在这里插入图片描述

3. 数据标准化(减均值,除以标准差)

第三个处理是对图像进行标准化,通过标准化之后,至此,数据预处理完毕,最后转换成 Variable 类型,就是输入网络模型的数据了。 再来看看 Red 通道的数据:
在这里插入图片描述

3. ImageFolder

当文件依据标签处于不同文件下时,如:
─── data
├── zhangliu
│ ├── 001.jpg
│ └── 002.jpg
├── wuhua
│ ├── 001.jpg
│ └── 002.jpg

我们可以利用torchvision.datasets.ImageFolder来直接构造出dataset,代码如下:

data = datasets.ImageFolder(path)
loader = data.DataLoader(data)

3.1 函数参数

class torchvision.datasets.ImageFolder(root, transform=None, target_transform=None, loader=<function default_loader>, is_valid_file=None)

功能:一种通用数据加载器,其中图像以这种方式排列:
	root/dog/xxx.png
	root/dog/xxy.png
	root/dog/xxz.png

	root/cat/123.png
	root/cat/nsdf3.png
	root/cat/asd932_.png

参数:
    root (string) – 文件根目录
    transform (callable, optional) –一种接受PIL图像并返回转换后的版本的函数/转换。. E.g, transforms.RandomCrop
    target_transform (callable, optional) – 作用于目标对象的function/transform 
    loader (callable, optional) – 加载已知路径图片的函数
    is_valid_file – 检查文件路径是否争取
重要特性:
    classes (list): 分类名称
    class_to_idx (dict): 分类名称(文件夹名称)映射到数字(自动编码的类别序号,输出的label就是这个)上的对应dict, 形式为 (class_name, class_index).
    imgs (list):  (image path, class_index) tuples显示,即显示(文件位置,类别序号)

3.2 构造dataset迭代器

ImageFolder会将目录中的文件夹名自动转化成序列,当DataLoader载入时,标签自动就是整数序列了。
下面我们利用ImageFolder读取不同目录下的图片数据,然后使用transforms进行图像预处理,预处理有多个,我们用compose把这些操作拼接在一起。然后使用DataLoader加载。

注:
1. 本次使用的数据是cifa-10 保存成png图片的合集,共有10个文件夹,分别标注为0-10,用以存放不同类别的照片。
2. .totensor 过程 图片数据会有axis变化, 格式由 hwc ----> chw, 后续如果通过plt显示的时候,.transpose((1, 2, 0))进行转置
3. 构造的dataloader的图片img 数据格式 {batch, channel, height , weight}, 和make_grid对输入数据的要求一样。
4. 可以通过ImageFolder.classes()/.class_to_idx()/.imgs(), 显示相关分类名称, 分类序列号和分类名称dict, 图片路径和分类序号dict

在这里插入图片描述

对处理后的数据用torchvision.utils中的save_image保存为一个png格式文件,然后用Image.open打开该png文件,详细代码如下:

from torchvision import transforms, utils
from torchvision.datasets import ImageFolder
import matplotlib.pyplot as plt
from torch.utils.data import DataLoader

"""
	数据预处理
"""
my_trans = transforms.Compose([
	transforms.RandomResizedCrop(224),
	transforms.RandomHorizontalFlip(),
	transforms.ToTensor()
])

"""
	生成batch_size迭代器,用于加载数据
"""
train_data = ImageFolder(r'D:\Study\pytorch\PyTorch_Tutorial-master\Data\test', transform=my_trans)
train_loader = DataLoader(train_data, batch_size=8, shuffle=True, )

"""
	读取第一个batch的数据,并且通过plt显示和save_image保存
"""
for i_batch, img in enumerate(train_loader):
	if i_batch == 0:
		#  img[0] ->图像, img[1] -> label
		print(img[1])
		fig = plt.figure()
		#  img 格式 {batch,  channel,  height ,  weight}, 和make_grid要求一样;
		# make_grid 批量显示多幅画面   ->  输出 {channel,  height ,  weight}
		grid = utils.make_grid(img[0], padding=0)
		#  tensor -> numpy; chw -> hwc,  plt才能显示
		plt.imshow(grid.numpy().transpose((1, 2, 0)))
		plt.show()
		utils.save_image(grid, 'test01.png')
	break

输出:

	tensor([8, 3, 1, 9, 8, 6, 3, 4])

显示:
在这里插入图片描述

References

https://pytorch.org/docs/stable/
https://github.com/TingsongYu/PyTorch_Tutorial

  • 8
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: 《PyTorch深度学习入门实战》是一本以PyTorch为工具,介绍深度学习基础知识和实践的书籍。在本书中,作者通过简洁易懂的语言,结合实际案例,帮助读者理解深度学习的基本概念和原理,并教授如何使用PyTorch库来实现各种深度学习模型。 本书内容主要分为两部分,第一部分是深度学习的基础知识,包括神经网络、卷积神经网络、循环神经网络等;第二部分是深度学习的实践技巧,包括数据预处理、模型训练、模型优化等。通过这两部分的学习,读者可以逐步掌握深度学习的基本原理和实践技巧。 此外,本书还特别注重实战,为读者提供了大量的代码实例和实战案例。这些实例和案例不仅包括经典的深度学习任务,如图像分类、目标检测和语言生成等,还包括一些实际应用,如机器翻译、垃圾邮件识别等。通过这些实例和案例的学习,读者可以更好地理解深度学习的实际应用和解决实际问题的能力。 总的来说,本书以PyTorch为工具,以深度学习基础知识和实际案例为主线,通过深入浅出的讲解,帮助读者入门深度学习,并掌握使用PyTorch来实现深度学习模型的能力。无论是对于初学者还是有一定经验的开发者来说,《PyTorch深度学习入门实战》都是一本很好的学习资料。 ### 回答2: 《PyTorch深度学习入门实战》是一本介绍PyTorch深度学习库的教程图书。PyTorch是一种在机器学习领域广泛使用的开源深度学习库,它以动态图和静态图的混合方式,提供了灵活、高效的工具来构建和训练神经网络模型。 该书的目的是帮助读者快速入门PyTorch,并通过实战案例实践所学的知识。书中提供了丰富的示例代码和实验,从基础概念开始,逐步引导读者理解和掌握PyTorch的核心功能和应用。 在第一部分中,书籍详细介绍了PyTorch的安装和配置,包括如何创建和管理虚拟环境,以及如何使用pip安装PyTorch及其相关库。 第二部分主要介绍了PyTorch张量(Tensor)的基本操作和运算。这是深度学习中的基础,掌握好张量的操作对于后续的模型构建和训练非常重要。本书中通过大量的示例代码和实验,让读者逐步熟悉张量的创建、索引、切片以及各种元素级别的运算操作。 第部分是关于PyTorch神经网络模块(nn)的详细介绍,包括如何使用nn.Module构建网络模型,以及如何使用nn.functional模块定义前向传播方法。同时也介绍了常用的损失函数和优化器,在训练模型时如何选择和使用它们。 第四部分是实战篇,书中通过几个典型的深度学习任务案例,如图像分类、目标检测和自然语言处理等,示范了如何使用PyTorch构建和训练神经网络模型。每个案例都包括了详细的代码解析和实验结果展示,读者可以通过这些案例学习和掌握如何在实际项目中应用PyTorch进行深度学习。 总之,《PyTorch深度学习入门实战》是一本非常实用的教程图书,适合对PyTorch感兴趣的初学者和有一定基础的开发者阅读。通过阅读本书,读者可以系统性地学习和掌握PyTorch深度学习框架,为深度学习实战应用提供基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI扩展坞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值