Pytorch基础(数据处理工具箱 + 神经网络工具箱 + Tensor基础 + Numpy基础)

文章目录


前言

在这里插入图片描述

0.1、简介

PyTorch 是一个由 Facebook 团队于 2017 年发布的深度学习框架是 Torch 框架在 Python 上的衍生

PyTorch构建在Python科学计算库的基础之上,提供了丰富的工具和库

  • (1)提供了高级API:用于构建、训练和部署深度学习模型。如:优化器、损失函数、数据加载器等。
  • (2)提供了张量操作(tensor)、自动求导、动态计算图等功能。
    • 张量操作PyTorch的核心数据结构是张量(tensor),类似于NumPy的数组(ndarray)。提供了丰富的张量操作函数,支持多维数组操作、数学运算、线性代数运算等。
      • numpy - ndarray:只支持 CPU 计算;
      • torch - tensor:支持 CPU、GPU 和 TPU 张量运算;
    • 自动求导:通过自动微分(autograd)机制实现了自动求导功能。因此,在定义模型和计算损失函数后,可以直接调用.backward()方法来自动计算梯度,从而方便地进行梯度下降等优化算法的训练过程。
    • 动态计算图支持在运行时定义、修改和执行计算图。与静态计算图不同,动态计算图使得模型的构建更加灵活,可以根据需要动态地构建计算图,从而实现更加复杂的模型结构和训练过程。
  • (3)与Python库兼容。支持与Python库进行集成和交互(如NumPy、SciPy等),使得数据处理、可视化等任务更加便捷。

Pytorch的核心库

  • torch:用于张量操作,类似于 NumPy 的 ndarray。
  • torch.nn:用于构建神经网络。如:网络层、损失函数、优化器等。
  • torch.optim:用于构建优化器。如:SGD、Adam、RMSprop 等。
  • torch.autograd:用于计算张量的梯度(自动求导)。
  • torch.utils.data:数据集处理Dataset、数据装载DataLoader 等。
  • torchvision:数据集下载torchvision.datasets、预训练模型下载torchvision.models、文本处理torchtext、音频处理torchaudio、图像处理torchvision.transforms 等。

在机器学习和深度学习中,图像、音频、文本等输入数据最终都要转换为数组或矩阵。

0.2、安装

推荐离线安装:【深度学习环境配置】Anaconda +Pycharm + CUDA +cuDNN + Pytorch + Opencv(资源已上传)

(1)【CPU版本】Anaconda(Python) + Pycharm + Pytorch(CPU)
(2)【GPU版本】Anaconda(Python) + Pycharm + CUDA + cuDNN + Pytorch(GPU)

  • CUDA:是英伟达公司推出的通用计算架构,它能利用英伟达GPU的并行计算引擎,比CPU更高效的解决复杂计算任务。
  • cuDNN:是英伟达公司推出的用于深度神经网络的GPU加速库。

0.3、版本对应关系:Python、Pytorch、torchvision

pythonpytorchtorchvisioncuda
3.7-3.91.12.00.1210.2(不支持windows),11.3,11.6
>=3.61.11.00.12.011.3,10.2
>=3.61.10.0/10.11.0/210.2,11.3
>=3.61.9.00.10.010.2,11.3
>=3.61.8.00.9.010.2,11.1
>=3.61.7.10.8.29.2, 10.1,10.2,11.0
>=3.61.7.00.8.09.2, 10.1,10.2,11.0
>=3.61.6.00.7.09.2, 10.1,10.2
>=3.61.5.10.6.19.2, 10.1,10.2
>=3.61.5.00.6.09.2, 10.1,10.2

0.4、Pytorch官网

列有多个工具箱的详细介绍,以及对应的函数说明。
在这里插入图片描述


详看书籍:<Python深度学习基于Pytorch>


四、Pytorch数据处理工具箱

数据处理:决定了数据质量和模型性能,是深度学习中耗时又至关重要的任务。

常用工具箱如下:
(1)torch.utils.data:数据读取工具。
(2)torchvision:数据处理工具。—— 独立于Pytorch,需要另外安装。
(3)torch.tensorboardX:可视化工具。—— 先安装tensorflow(CPU或GPU),然后安装tensorboardX。

在这里插入图片描述

  • data.Dataset 只支持处理单个目录下的所有数据。:若数据在不同目录下,则很不方便(不同目录往往代表不同类别。如:1和0文件夹下分别存放了对应的数据集。)
  • torchvision 支持处理多个目录下的所有数据。:且可以自动获取标签,提供数据预处理、数据增强等转换函数。

4.1、torch.utils.data:数据读取工具

搭配调用:torch.utils.data.Dataset + torch.utils.data.DataLoader
详细过程:

  • (1)将输入数据转换为一个 torch.utils.data.Dataset 类;
  • (2)将 torch.utils.data.Dataset 类作为一个参数传递给 torch.utils.data.DataLoader 类,得到一个数据加载器。数据加载器实现每次返回一个 batch_size 数据供模型训练使用。
  • (3)在输出数据时,数据预处理或数据增强。

4.1.1、torch.utils.data.Dataset —— 读取数据(单样本)

torch.utils.data.Dataset:这是一个抽象类,用于表示数据集

若自定义Dataset,则需要继承torch.utils.data.Dataset,且需要实现下面三个方法。

  • __ init __ :用于初始化数据集
  • __ len __ :用于获取数据集的数量
  • __ getitem __ :用于根据索引获取数据集中的样本。

通过实现这些方法,你可以将自定义的数据集像 Python 的列表一样使用

import numpy as np
import torch
from torch.utils import data


class TestDataset(data.Dataset):  # 继承Dataset
    """若自定义Dataset,必须继承torch.utils.data.Dataset。"""
    def __init__(self):
        # 由二维向量表示的数据集
        self.Data = np.asarray([[1, 2], [3, 4], [1, 2], [3, 4], [4, 5], [1, 2], [3, 4], [1, 2], [7, 2], [7, 9]])
        # 数据集对应的标签
        self.Label = np.asarray([0, 1, 0, 1, 2, 0, 1, 0, 3, 5])

    def __getitem__(self, item):
        """通过给定索引获取数据和标签"""
        txt = torch.from_numpy(self.Data[item])  # 将numpy转换为Tensor
        label = torch.tensor(self.Label[item])  # 将numpy转换为Tensor
        return txt, label

    def __len__(self):
        """获取数据集的数量"""
        return len(self.Data)


test_dataset = TestDataset()  # 类的实例化
test_loader = data.DataLoader(test_dataset, batch_size=4, shuffle=False, drop_last=False)
for i, train_data in enumerate(test_loader):
    Data, Label = train_data
    print('迭代获取批数据:i=', i)
    print('Data={}, Label={}' .format(Data, Label))

4.1.2、torch.utils.data.DataLoader —— 数据加载器(加载batch_size数据)

torch.utils.data.DataLoader用于创建一个可迭代的数据加载器,实现批量加载数据,随机排列数据、多线程加载数据等等。

Dataset每次从数据源中,检索单个样本及其标签
Dataloader每次从 Dataset 中,并行加载一个batch_size的样本及其标签

DataLoader的耗时问题

  • 影响因素1:在每个epoch的N个batch_size训练中,前几个batch_size的耗时总是很高。
  • 原因分析:
    • (1)在每个epoch开始时,数据加载器会重新创建并启动。在重新启动期间,数据加载器需要执行一些初始化操作,例如重新加载数据和重新开始预取数据。这些操作可能会导致前几个批次的加载速度较慢。
    • (2)然而,一旦数据加载器完成了初始化并开始加载和预取数据,它通常会保持稳定的速度直到完成整个epoch。这是因为一旦开始加载数据,大部分工作都是由数据预取器线程执行的,它们独立于主训练循环线程。这意味着即使主循环线程稍微滞后一点,数据预取仍会按照预定的速度继续进行。
  • 解决方案:使用 MultiEpochsDataLoader 代替 DataLoader
  • 采用MultiEpochsDataLoader 和 CudaDataLoader (异步预先将数据从cpu加载到gpu中)进行加速
import torchvision.transforms
import torch

# (1)图像预处理
transform = torchvision.Compose([torchvision.ToTensor(), torchvision.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
# (2)下载数据集
train_set = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
test_set = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
# (3)数据加载器
train_loader = torch.utils.data.DataLoader(train_set, batch_size=16, shuffle=True, num_workers=2)
test_loader = torch.utils.data.DataLoader(test_set, batch_size=16, shuffle=False, num_workers=2)

"""##############################################################################
# 函数功能:创建一个可迭代的数据加载器
# 函数说明:data.DataLoader(dataset, batch_size, shuffle=False, sampler=None, batch_sampler=None,
#                   num_workers=0, collate_fn=None, pin_memory=False, drop_lost=False)
# 参数说明:
#       (1)dataset:     加载的数据集
#       (2)batch_size:  批大小(批大小越大,时耗越高)
#
#       (3)shuffle:     是否在每个epoch中将样本数据打乱,以减少模型过拟合。
#       (4)sampler:     样本抽样
#       (5)num_workers: 进程数(进程数越多,占用内存越大)。如:os.cpu_count
#				= 0		在主进程中加载数据,不使用进程	(适用于小数据)
#   			= 1		使用单进程加载数据
#       		> 1		使用多进程加载数据			    (适用于大数据)
# 				备注:创建与摧毁线程或进程,需要占用一定的时间开销。
#
#       (6)collate_fn:  如何将多个样本数据拼接成一个batch,一般取默认值。
#       (7)pin_memory:  是否将数据加载到固定的主机内存中,而不是临时内存,然后再从固定的主机内存中将数据直接传输到GPU内存中。
#               优点:加快数据从主机内存到GPU内存的传输速度。
#               缺点:(1)固定数据在主机内存中将增加内存占用量;(2)固定数据将导致内存碎片化
#               若pin_memory=True,此时,Dataset的数据类型必须为tensors、maps或包含tensors的可迭代对象,因为只有这些数据类型才能被有效地固定在主机内存中。
#                       
#       (8)drop_last:   是否丢弃最后一个不足batch_size的数据集。
##############################################################################"""

4.2、torchvision:视觉处理工具

4.2.1、torchvision.datasets —— 提供数据集下载(在线)

torchvision.datasets 数据集下载的 API 参数(详解)
所有数据集都是 torch.utils.data.Dataset 的子类,都具有 __ getitem() __ 和 __ len() __ 方法。因此,它们都可以传递给 torch.utils.data.DataLoader。

######################################################
# # torchvision.datasets.		  :提供数据集下载
# torchvision.datasets.ImageFolder:用于加载包含图像数据的文件夹,每个文件夹代表一个类别,文件夹中包含该类别的图像。
######################################################
以下是提供的部分数据集:

"""MNIST 手写数字数据集"""
from torchvision.datasets import MNIST
train_set = MNIST(root='./data', train=True, download=True)
test_set = MNIST(root='./data', train=False, download=True)

"""FashionMNIST 时尚服饰数据集"""
from torchvision.datasets import FashionMNIST
train_set = FashionMNIST(root='./data', train=True, download=True)
test_set = FashionMNIST(root='./data', train=False, download=True)

"""CIFAR-10 图像数据集"""
from torchvision.datasets import CIFAR10
train_set = CIFAR10(root='./data', train=True, download=True)
test_set = CIFAR10(root='./data', train=False, download=True)

"""CIFAR-100 图像数据集"""
from torchvision.datasets import CIFAR100
train_set = CIFAR100(root='./data', train=True, download=True)
test_set = CIFAR100(root='./data', train=False, download=True)

"""ImageNet 图像数据集"""
from torchvision.datasets import ImageNet
train_set = ImageNet(root='./data', split='train', download=True)
val_set = ImageNet(root='./data', split='val', download=True)

"""VOC 数据集(用于目标检测)"""
from torchvision.datasets import VOCDetection
train_set = VOCDetection(root='./data', year='2012', image_set='train', download=True)

"""COCO 数据集(用于目标检测和图像分割)"""
from torchvision.datasets import CocoDetection
train_set = CocoDetection(root='./data', annFile='annotations/instances_train2017.json', download=True)

4.2.2、torchvision.models —— 提供预训练模型下载

❤️ torchvision.models:提供了一些预训练的神经网络模型,包括经典的模型结构以及在 ImageNet 等数据集上预训练好的权重。

主要包括以下系列模型:

  • (1) AlexNet模型:alexnet
  • (2)VGG系列模型:vgg11、vgg13、vgg16、vgg19
  • (3)ResNet系列模型:resnet18、resnet34、resnet50、resnet101、resnet152
  • (4)DenseNet系列模型:densenet121、densenet169、densenet201
  • (5)SqueezeNet系列模型:squeezenet1_0、squeezenet1_1
  • (6)Inception模型:inception_v3
  • (7)MobileNetV2模型:mobilenet_v2
import torchvision.models as models

alexnet		 = models.alexnet(pretrained=True)
vgg16		 = models.vgg11(pretrained=True)
resnet18	 = models.resnet18(pretrained=True)
densenet121	 = models.densenet121(pretrained=True)
squeezenet	 = models.squeezenet1_0(pretrained=True)
inception	 = models.inception_v3(pretrained=True)
mobilenet_v2 = models.mobilenet_v2(pretrained=True)

"""########################################################################
# (1)若pretrained=False,则随机初始化模型的权重参数。
# (2)若pretrained= True,则加载预训练模型的权重参数,实现迁移学习到落地部署。
########################################################################"""

4.2.3、torchvision.utils —— 绘制网格图像

torchvision.utils提供了一些辅助函数,用于在 torchvision 中进行图像处理、数据转换等操作。

# make_grid:将多个张量数据拼接成一个网格形式的图像。
from torchvision.utils import make_grid
grid_image = make_grid(tensor_batch)

# save_image:保存图像到文件中。
from torchvision.utils import save_image
save_image(tensor_batch, 'output.png')

# draw_bounding_boxes:在图像上绘制边界框。
from torchvision.utils import draw_bounding_boxes
image_with_boxes = draw_bounding_boxes(image, boxes)

# draw_segmentation_masks:在图像上绘制分割掩码。
from torchvision.utils import draw_segmentation_masks
image_with_masks = draw_segmentation_masks(image, masks)

# draw_keypoints:在图像上绘制关键点。
from torchvision.utils import draw_keypoints
image_with_keypoints = draw_keypoints(image, keypoints)

在这里插入图片描述

import torch
import torchvision.transforms as transforms
from torchvision.utils import make_grid
import matplotlib.pyplot as plt

# (1)构造一个张量,假设有 16 张 3x64x64 的图像
images = torch.randn(16, 3, 64, 64)

# (2)使用 torchvision.transforms.ToPILImage() 将张量转换为 PIL 图像
to_pil = transforms.ToPILImage()
pil_images = [to_pil(img) for img in images]

# (3)将 PIL 图像转换为张量
to_tensor = transforms.ToTensor()
tensor_images = [to_tensor(img) for img in pil_images]

# (4)使用 torchvision.utils.make_grid() 将多张图像合并成一张网格图像
grid_image = make_grid(tensor_images, nrow=6)  # nrow 参数指定每行显示的图像数量

# (5)使用 matplotlib 显示合并后的网格图像
plt.imshow(grid_image.permute(1, 2, 0))  # 转换通道顺序
plt.axis('off')  # 关闭坐标轴
plt.show()

4.2.4、图像变换

torchvision.transforms提供了一系列用于图像预处理和数据增强的转换函数,这些函数可以应用于 PIL 图像或张量。这些转换包括调整大小、裁剪、旋转、翻转、归一化等操作,可用于准备训练数据、数据增强和测试数据预处理。

4.2.4.1、torchvision.transforms. —— 图像变换(单个操作)

玩转pytorch中的torchvision.transforms(图解)

Pytorch官网:torchvision

""" (1)Transforms on PIL Image(只适用于PIL图像,输入和输出都是PIL图像)"""
torchvision.transforms.Resize					# 图像缩放
torchvision.transforms.CenterCrop 				# 在图像中心裁剪出指定大小的区域
torchvision.transforms.ColorJitter  			# 颜色抖动(Jitter):对图像的亮度、对比度、饱和度和色相进行随机变换(0.2:表示在原图的基础上将对比度增加或减小20%)
torchvision.transforms.FiveCrop 				# 在四个角和中心位置各裁剪出一个指定大小的区域
torchvision.transforms.Grayscale 				# 将图像转换为灰度图像
torchvision.transforms.Pad 						# 图像填充
################################################
torchvision.transforms.RandomApply 				# 以一定的概率(可指定)随机应用一组图像变换中的部分变换。
torchvision.transforms.RandomChoice				# 从一组图像变换中,随机选择一种图像变换应用到图像上。
torchvision.transforms.RandomOrder				# 随机选择并以随机顺序应用所有图像变换到图像上。
################################################
torchvision.transforms.RandomCrop				# 随机区域裁剪。
torchvision.transforms.RandomResizedCrop 		# 随机区域裁剪为指定大小,并调整大小以匹配指定的输出大小。
torchvision.transforms.RandomGrayscale 			# 以一定的概率(默认为0.1)将图像转为灰度图像,否则保持不变。
torchvision.transforms.RandomHorizontalFlip 	# 以一定的概率(默认为0.5)随机水平翻转图像,否则保持不变。
torchvision.transforms.RandomVerticalFlip 		# 以一定的概率(默认为0.5)随机垂直翻转图像,否则保持不变。
torchvision.transforms.RandomRotation 			# 在指定的旋转角度范围内,随机选择一个角度,应用图像旋转。
torchvision.transforms.RandomPerspective 		# 变换参数随机生成的应用透视变换
torchvision.transforms.RandomAffine 			# 变换参数随机生成的应用仿射变换
################################################################################################
""" (2) Transforms on Torch Tensor(只适用于Tensor,输入和输出都是Tensor)"""
torchvision.transforms.GaussianBlur 			# 高斯模糊处理(输入参数:核大小 + 高斯核的标准差)
torchvision.transforms.LinearTransformation 	# 线性变换(需指定变换矩阵,包括平移、旋转、缩放等操作)
torchvision.transforms.Normalize 				# 归一化处理(输入参数:均值 + 标准差)
torchvision.transforms.RandomErasing 			# 随机擦除图像中的部分区域,模拟了图像可能遭受损坏或遮挡的情况,从而增强模型的鲁棒性和泛化能力。
################################################################################################
""" (3) Conversion Transforms(格式转换)"""
torchvision.transforms.ToPILImage 				# 将Tensor转换为PIL图像,而不支持numpy转换。
torchvision.transforms.ToTensor					# 将PIL图像或numpy数组转换为Tensor


"""##########################################################################################
# 函数功能:随机更改图像的亮度、对比度、饱和度和色调。
# 函数说明:torchvision.transforms.ColorJitter(brightness=0, contrast=0, saturation=0, hue=0)
# 参数说明:
#         brightness:     亮度(非负数:float或python元组)
#         contrast:       对比度(非负数:float或python元组)
#         saturation:     饱和度(非负数:float或python元组)
#         hue:            色调(非负数:float或python元组)

# brightness    若为float,取值范围[max(0, 1-亮度), 1+亮度]      若为元组(min, max),取值范围=[0, inf]       其中: [0, 1]表示降低亮度,[1, inf]表示增加亮度。
# contrast      若为float,取值范围[max(0, 1-对比度), 1+对比度]   若为元组(min, max),取值范围=[0, inf]       其中: 0表示灰色图像,1表示原始对比度。
# saturation    若为float,取值范围[max(0, 1-饱和度), 1+饱和度]   若为元组(min, max),取值范围=[0, inf]       其中: 0表示无饱和度,1表示原始饱和度。
# hue           若为float,取值范围[-色调, 色调]                 若为元组(min, max),取值范围=[-0.5, 0.5]    其中: 负值表示向绿色和蓝色偏移,正值表示向红色和黄色偏移。
##########################################################################################"""

"""##########################################################################################
# 函数功能:用于对图像进行高斯模糊的函数。
# 函数说明:torchvision.transforms.GaussianBlur(kernel_size=3, sigma=(0.1, 2.0))
# 参数说明:
#         kernel_size:  指定高斯核的大小。核大小必须为奇数。
#                 (1)整数: 表示方形核大小的。        如: kernel_size=3       表示 3x3 的核;
#                 (2)元组: 表示方形核高度和宽度的。   如: kernel_size=(3, 5)  表示高度为 3,宽度为 5 的核。
#         sigma:        高斯核的标准差。
#                 (1)单个值: 表示在指定的核大小下使用相同的标准差。
#                 (2)若为一个范围(min, max): 表示在此范围内随机选择标准差。
##########################################################################################"""

4.2.4.2、torchvision.transforms.Compose() —— 图像变换(组合操作)

torchvision.transforms.Compose()将多个图像变换操作组合在一起。系统自动遍历transforms列表中所有transform变换,然后按顺序依次作用在图像上。

应用了图像变换后,原始图像不会再出现在训练集中。图像变换会生成新的图像副本,并将其添加到数据集中。

from torchvision import transforms
from PIL import Image

# 定义图像变换
transform = transforms.Compose([
    transforms.RandomHorizontalFlip(p=1),  # 随机水平翻转(概率为1)
    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1),  # 随机颜色变换
    transforms.RandomRotation(degrees=30),  # 随机旋转
    transforms.RandomResizedCrop(size=224, scale=(0.8, 1.2)),  # 随机裁剪和缩放
])

image = Image.open('gray_cat.png')  # 加载图像
transformed_image = transform(image)  # 图像变换

print(f"输入图像类型:{type(image)}")
print(f"输出图像类型:{type(transformed_image)}")

"""
输入图像类型:<class 'PIL.Image.Image'>                  # 表示 PIL 库中通用的图像对象
输出图像类型:<class 'PIL.PngImagePlugin.PngImageFile'>  # 表示 PNG 格式的图像文件对象
"""

4.2.5、torchvision.datasets.ImageFolder —— 读取多个文件夹下的数据

torchvision.datasets.ImageFolder()用于加载图像数据的数据集类。

此时,图像数据集的组织结构需符合以下约定

  • (1)每个类别的图像都存储在单独的文件夹中;
  • (2)文件夹的名称即为类别的名称;
  • (3)图像文件的名称没有特定的要求,加载时会自动获取文件名作为标签。

torchvision.datasets.ImageFolder前的数据整理及使用方法

例如:假设你有一个图像数据集,包含两个类别(猫和狗),目录结构如下:

data/
    ├── cat/
    │   ├── cat1.jpg
    │   ├── cat2.jpg
    │   └── ...
    └── dog/
        ├── dog1.jpg
        ├── dog2.jpg
        └── ...
     
此时,cat文件夹下包含所有猫的图像、dog文件夹下包含所有狗的图像。
import torch
import torchvision
from torch.utils import data
import matplotlib.pyplot as plt

transform = torchvision.transforms.Compose([torchvision.transforms.RandomResizedCrop(224), torchvision.transforms.ToTensor()])
train_data = torchvision.datasets.ImageFolder(R'data/torchvision_data', transform=transform)
train_loader = torch.utils.data.DataLoader(train_data, batch_size=8, shuffle=True)

for index_batch, img in enumerate(train_loader):
    if index_batch == 0:
        fig = plt.figure()
        grid = torchvision.utils.make_grid(img[0])
        plt.imshow(grid.numpy().transpose((1, 2, 0)))
        plt.show()
        torchvision.utils.save_image(grid, 'test01.png')
    break
    
"""#####################################################################################
# 函数说明:dataset = ImageFolder(root='path/to/data', transform=None, target_transform=None, loader=None, 
#                               is_valid_file=None, extensions=None, transform_img_file=None)
# 参数说明:
#         - root(str)         :图像数据集的根目录路径。该目录下包含一个或多个类别文件夹。
#         - transform          :(可选)用于对图像进行转换的函数或变换操作。默认None,表示不进行任何转换。
#         - target_transform   :(可选)用于对目标进行转换的函数或变换操作。默认None,表示不进行任何转换。
#         - loader             :(可选)用于加载图像的函数。默认default_loader,使用PIL的 Image.open() 来加载图像。
#         - is_valid_file      :(可选)用于判断文件是否为有效文件的函数。默认None。
#         - extensions         :(可选)要加载的图像文件的扩展名列表。
#                                 默认IMG_EXTENSIONS,包含".jpg", ".jpeg", ".png", ".ppm", ".bmp", ".pgm", ".tif", ".tiff", ".webp"等格式。
#         - transform_img_file :(可选)用于自定义对图像文件进行转换。默认None。
#####################################################################################"""

4.3、tensorboardX:可视化工具

TensorboardX用于在TensorBoard中记录和可视化PyTorch模型的训练过程和结果。如:损失函数、准确率、学习率等,以及模型的结构和参数分布等信息。

tensorboardX 官网文档

  • tensorboard:是 TensorFlow 框架的可视化工具,用于可视化训练过程中的指标、模型结构、数据分布等信息。
  • tensorboardX:是 PyTorch 的一个扩展库,可以与 TensorBoard 兼容,用于在 PyTorch 中记录和可视化训练过程中的各种指标和结果。
    • (1)虽然 TensorBoardX 是为 PyTorch 提供的一个扩展库,但它实际上是对 TensorFlow 的 TensorBoard 的一个兼容接口。
    • (2)将 PyTorch 中的数据格式转换为 TensorFlow 的格式,并通过 TensorFlow 的 SummaryWriter 将数据写入到 TensorBoard 的日志文件中,从而实现在 PyTorch 中使用 TensorBoard 进行可视化的功能。

三、Pytorch神经网络工具箱

3.1、torch.autograd:自动求导机制

在神经网络中,Tensor可以保证完成前向传播,而训练时的反向传播与梯度更新,需要torch.autograd完成自动求导。通过requires_grad参数来创建支持自动求导机制的Tensor,其表示是否需要对该Tensor进行求导。

  • requires_grad=False表示不需要求导(默认值);
  • requires_grad=True表示需要求导,并且依赖于该Tensor之后的所有节点都需要求导。
import torch
a = torch.randn(2, 2, requires_grad=True)
b = torch.randn(2, 2)
print(a.requires_grad, b.requires_grad)
# True False
##################################################
# 通过内置函数requires_grad_()将不需要求导的Tensor变为需求求导。修改后requires_grad=True
b.requires_grad_()
print(b.requires_grad)
# True
##################################################
# 由于依赖的Tensor需要求导,故计算生成的Tensor也需要求导。
c = a + b
print(c.requires_grad)
# True

(1)自动求导要点

(1)创建叶子节点的Tensor。其参数requires_grad=True表示自动求导。缺省值为False。可利用 requires_grad_方法修改Tensor的 requires_grad 属性。
(3)对非叶子节点的Tensor计算,会自动记录在grad_fn属性中,用于反向传播的梯度计算,而叶子节点的grad_fn=None
(4)对根节点进行反向传播backward(gradient=None, retain_graph=None, create_graph=False)

  • 11、叶子节点的梯度:自动计算,结果保存在 .grad 属性中。
  • 22、非叶子节点的梯度:反向传播后自动清空
  • 33、梯度值累加:训练时需指定 retain_graph=True**

(5)取消梯度计算.detach() 或 with torch.no_grad()

备注1:梯度(gradient): 即某一函数在该点处的方向导数,其沿着该方向取得最大值。
备注2:缺省值:即默认值(计算机术语)

备注3:Pytorch不允许Tensor对Tensor求导,求导时都是标量对Tensor进行求导。

  • 如果根节点是向量,则应配以对应大小的权重并求和,进而得到标量后反向传播。
  • 如果根节点是标量,则该参数可以省略,默认为1。

(2)计算图

什么是计算图?
什么是叶子节点、非叶子节点、算子节点?

计算图:即神经网络的具体实现形式,包括每一个Tensor以及Tensor之间的计算函数。

  • 例如:z = w * x + b。该公式可以拆分为y = w * x; z = y + b
    • x、w、b为输入变量(即叶子节点);
    • y、z为计算得到的变量(即非叶子节点),z又叫做根节点。
    • mul()和add()操作符(即算子节点)。

在这里插入图片描述
Pytorch的计算图为动态的,且为有向无环图(DAG)。而其他深度学习框架,如:TensorFlow、Keras为静态图。

  • 动态图:在程序运行时,每次前向传播时重新构建计算图,这样不同的前向传播就有不同的计算图。且可以在过程中插入各种控制语句。

3.1.1、实战:计算图 + 节点属性

假设z=wx+b; 其中,x、w、b都是标量。对标量z调用backward(),无需传入参数。

# (1)定义叶子节点
import torch
x = torch.Tensor([2])
w = torch.randn(1, requires_grad=True)      # 初始化权重参数 w
b = torch.randn(1, requires_grad=True)      # 初始化权重参数 b
# (2)前向传播:定义算子节点
y = torch.mul(w, x)     # 乘法计算
z = torch.add(y, b)     # 加法计算
# (3)反向传播:实现目标函数的梯度自动计算
z.backward()

##############################################################################
# 查看各节点是否为叶子节点(.is_leaf)
print('x、w、b、y、z是否为叶子节点:{},{},{},{},{}' .format(x.is_leaf, w.is_leaf, b.is_leaf, y.is_leaf, z.is_leaf))

# 查看叶子节点的requires_grad属性(缺省值为False)
print('x、w、b的requires_grad属性分别为:{},{},{}' .format(x.requires_grad, w.requires_grad, b.requires_grad))
# 查看非叶子节点的requires_grad属性 ———— 因为y、z与w、b有依赖关系
print('y、z的requires_grad属性分别为:{},{}' .format(y.requires_grad, z.requires_grad))

# 查看叶子节点的grad_fn属性
print('x、w、b的grad_fn属性分别为:{},{},{}' .format(x.grad_fn, w.grad_fn, b.grad_fn))
# 查看非叶子节点的grad_fn属性
print('y、z的grad_fn属性分别为:{},{}' .format(y.grad_fn, z.grad_fn))

# 查看叶子节点的梯度 ———— x是叶子节点但无须求导,故其梯度为None
print('x、w、b的梯度分别为:{},{},{}' .format(x.grad, w.grad, b.grad))
# 查看非叶子节点的梯度(非叶子节点不支持调用梯度属性,系统警告提示)
print('y、z的梯度分别为:{},{}' .format(y.grad, z.grad))

3.1.2、实战:基于Numpy实现回归任务 —— 手动计算梯度

这里是引用

##########################################
# 题目:(1)随机给定向量 x。目标函数:y=3x^2+2+随即噪音。
#      (2)构建机器学习模型
#               11、通过给定数据,基于y=wx^2+b,【手动计算梯度】w、b;
#               22、采用梯度下降法学习参数,并多次迭代,得到最接近目标函数的w、b值(w=3、b=2)。
##########################################
import numpy as np
from matplotlib import pyplot as plt

# (1)生成输入向量(x)及目标数据(y)
np.random.seed(100)     # 如果设置相同的seed值,则每次运行程序都将生成相同的随机数
x = np.linspace(-1, 1, 100).reshape(100, 1)     # 生成线性等份向量,并改变形状
y = 3*np.power(x, 2) + 2 + 0.2*np.random.rand(x.size).reshape(100, 1)
# plt.scatter(x, y)
# plt.show()

# (2)随机初始化权重参数
w1 = np.random.rand(1, 1)
b1 = np.random.rand(1, 1)

#  (3)训练模型
lr = 0.001      # 学习率初始化
global y_predict
for ii in range(800+1):     # 右值取不到
    y_predict = np.power(x, 2)*w1 + b1      # 前向传播
    loss = 0.5*(y_predict - y) ** 2         # 定义损失函数
    loss = loss.sum()

    # 对损失函数(手动)求导,计算梯度
    grad_w = np.sum((y_predict - y) * np.power(x, 2))
    grad_b = np.sum((y_predict - y))
    # 使用梯度下降法学习参数,使loss最小
    w1 -= lr * grad_w
    b1 -= lr * grad_b

    if ii % 100 == 0:       # 每隔100次打印一次训练结果
        print('迭代次数{} ———— 损失值={:.4},权重={},偏置={}' .format(ii, loss, w1, b1))

# (4)可视化结果
plt.plot(x, y_predict, 'r-', label='predict')                   # 预测值
plt.scatter(x, y, color='blue', marker='o', label='true')       # 真实值
plt.xlim(-1, 1), plt.ylim(2, 6), plt.legend(loc=1)
plt.show()
###########################################
# 获取axes并作图:plt.plot()
# 图例位置调整:legend(loc=num) 其中,num=1~10, 不同数值对应不同位置。默认loc=1
# 设置x轴的数值显示范围:plt.xlim(x_min, x_max)
# 设置y轴的数值显示范围:plt.ylim(y_min, y_max)
###########################################

3.1.3、实战:基于Tensor及autograd回归任务 —— 自动计算梯度

这里是引用

##########################################
# 题目:(1)随机给定向量 x。目标函数:y=3x^2+2+随即噪音。
#      (2)构建机器学习模型
#               11、通过给定数据,基于y=wx^2+b,【自动计算梯度】w、b;
#               22、采用梯度下降法学习参数,并多次迭代,得到最接近目标函数的w、b值(w=3、b=2)。
##########################################
import torch
from matplotlib import pyplot as plt

import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'     # "OMP: Error #15: Initializing libiomp5md.dll"

# (1)生成输入向量(x)及目标数据(y)
torch.manual_seed(100)     # 如果设置相同的seed值,则每次运行程序都将生成相同的随机数
x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1)     # 添加一个维度(dim=0按行索引,dim=1按列索引)
y = 3*torch.pow(x, 2) + 2 + 0.2*torch.rand(x.size())
# plt.scatter(x.numpy(), y.numpy())
# plt.show()

# (2)随机初始化权重参数
w1 = torch.randn(1, 1, requires_grad=True)      # requires_grad=True表示参数w、b需要学习
b1 = torch.randn(1, 1, requires_grad=True)

#  (3)训练模型
lr = 0.001      # 学习率初始化
global y_predict
for ii in range(800+1):     # 右值取不到
    y_predict = torch.pow(x, 2).mm(w1) + b1     # 前向传播      # torch.mm(input1, input2)      # 对2D矩阵进行点积运算
    loss = 0.5*(y_predict - y) ** 2             # 定义损失函数
    loss = loss.sum()

    # 自动计算梯度,梯度存放在属性grad中
    loss.backward()         # 反向传播
    with torch.no_grad():
        w1 -= lr * w1.grad
        b1 -= lr * b1.grad
        w1.grad.zero_()     # 梯度清零
        b1.grad.zero_()

    if ii % 100 == 0:       # 每隔100次打印一次训练结果
        print('迭代次数{} ———— 损失值={:.4},权重={},偏置={}' .format(ii, loss, w1, b1))

# (4)可视化结果
plt.plot(x.numpy(), y_predict.detach().numpy(), 'r-', label='predict')          # 预测值
# 调用 .detach() 或 with torch.no_grad():     表示不再计算张量的梯度,不再跟踪张量的历史记录。

plt.scatter(x.numpy(), y.numpy(), color='blue', marker='o', label='true')       # 真实值
plt.xlim(-1, 1), plt.ylim(2, 6), plt.legend(loc=1)
plt.show()
###########################################
# 获取axes并作图:plt.plot()
# 图例位置调整:legend(loc=num) 其中,num=1~10, 不同数值对应不同位置。默认loc=1
# 设置x轴的数值显示范围:plt.xlim(x_min, x_max)
# 设置y轴的数值显示范围:plt.ylim(y_min, y_max)
###########################################

3.2、torch.optim:优化器

torch.optim继承于基类torch.optim.optimizer,且封装了常用优化算法。

3.2.1、优化器(常用优化算法)

优化器(optimizer)作用:通过优化策略(梯度下降)来更新可学习参数(权值W和偏置bias),使得损失函数Loss值逐步降低,输出的模型更接近真实标签。
常用优化器

  • 00、经典的梯度下降法。
  • 11、梯度下降优化算法:SGDSGDMNAG
       缺点:缓解了参数空间的方向问题,但需要新增参数,且对学习率的控制也不太理想。
  • 22、自适应优化算法:AdaGrad(累积梯度平方)、RMSProp(累积梯度平方的滑动平均)、Adam(带动量项的RMSProp)
    备注:自适应优化算法,学习率不再是一个固定不变值,它会根据实际情况自动调整以适应环境。

3.2.2、优化器(主要步骤)

1)优化器实例化:optimizer = torch.optim.SGD(model.parameters(), Learning_Rate=lr, momentum=momentum)2)前向传播:out = model(img)		# model是模型实例化对象。输入img,执行forward(),得到预测值。2)损失函数:loss = loss_func(out, label)3)梯度清零:optimizer.zero_grad()4)反向传播:loss.backward()5)参数更新:optimizer.step()

3.2.3、实战:多种优化器的收敛效果对比

在这里插入图片描述

import torch
import torch.utils.data as Data
import torch.nn.functional as F
import matplotlib.pyplot as plt

import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'     # "OMP: Error #15: Initializing libiomp5md.dll"
####################################
# (1)定义超参数
LR = 0.01
BATCH_SIZE = 32
EPOCH = 12
####################################
# (2)生成数据
# torch.manual_seed(100)        # 如果设置相同的seed值,则每次运行程序都将生成相同的随机数
x = torch.unsqueeze(torch.linspace(-1, 1, 1000), dim=1)     # torch只能处理二维数据
y = x.pow(2) + 0.1*torch.normal(torch.zeros(*x.size()))     # 目标函数+随即噪音

torch_dataset = Data.TensorDataset(x, y)
loader = Data.DataLoader(dataset=torch_dataset, batch_size=BATCH_SIZE, shuffle=True)
####################################


# (3)构建神经网络
class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.hidden = torch.nn.Linear(1, 20)        # 隐藏层
        self.predict = torch.nn.Linear(20, 1)       # 输出层

    # 前向传递
    def forward(self, x):
        x = F.relu(self.hidden(x))                  # 激活函数
        x = self.predict(x)
        return x


####################################
if __name__ == '__main__':
    # (4)定义多个神经网络
    net_SGD = Net()
    net_Momentum = Net()
    net_RMSprop = Net()
    net_Adam = Net()
    nets = [net_SGD, net_Momentum, net_RMSprop, net_Adam]

    # (5)调用多个优化器
    opt_SGD = torch.optim.SGD(net_SGD.parameters(), lr=LR)
    opt_Momentum = torch.optim.SGD(net_Momentum.parameters(), lr=LR, momentum=0.9)
    opt_RMSprop = torch.optim.RMSprop(net_RMSprop.parameters(), lr=LR, alpha=0.9)
    opt_Adam = torch.optim.Adam(net_Adam.parameters(), lr=LR, betas=(0.9, 0.99))
    optimizers = [opt_SGD, opt_Momentum, opt_RMSprop, opt_Adam]

    # (6)定义损失函数
    loss_func = torch.nn.MSELoss()
    losses = [[], [], [], []]                       # 记录损失值

    # (7)训练函数
    for epoch in range(EPOCH):
        for step, (batch_x, batch_y) in enumerate(loader):
            for net, opt, loes in zip(nets, optimizers, losses):
                output = net(batch_x)               # 获取每层网络的输出
                loss = loss_func(output, batch_y)   # 比较每层网络的损失
                opt.zero_grad()                     # 梯度清零
                loss.backward()                     # 自动计算梯度
                opt.step()                          # 梯度更新
                loes.append(loss.data.numpy())      # 保存每次训练的结果(损失值)。画图数据需numpy格式

    # (8)可视化结果
    labels = ['SGD', 'Momentun', 'RMSprop', 'Adam']
    colors = ['b', 'k', 'y', 'r']     # plot中画线的颜色通常是八种:r红、g绿、b蓝、c蓝绿、m紫红、y黄、k黑、w白
    for ii, loes in enumerate(losses):
        plt.plot(loes, label=labels[ii], color=colors[ii])      # 获取axes并作图:plt.plot()
    plt.legend(loc='best')      # 图例位置调整:legend(loc=num) 其中,num=1~10, 不同数值对应不同位置。默认loc=1
    plt.xlabel('Steps')         # 设置x轴标签
    plt.ylabel('Loss')          # 设置y轴标签
    plt.ylim((0, 0.2))          # 设置y轴的数值显示范围:plt.ylim(y_min, y_max)
    plt.show()

3.3、torch.nn + torch.nn.functional:构建神经网络

构建神经网络的两个主要工具:torch.nn、torch.nn.functional。 —— 两者功能相同,且性能没有很大区别。
【官方推荐】具有学习参数的网络层,使用torch.nn.Module。如:卷积层、全连接层、Dropout层
【官网推荐】没有学习参数的网络层,使用torch.nn.functional。如:激活函数、池化层

 
两者主要区别:
(1)nn.Module中的网络层将继承Module类,会自动提取可学习参数(表现形式:nn.Xxx)

  • 11、nn.Xxx能够与nn.Sequential结合使用。   —— 而nn.functional.xxx不能。
  • 22、nn.Xxx不需要自定义和管理weight、bias参数,可自动计算。 —— 而nn.functional.xxx需要自定义且需要手动传入。
  • 33、nn.Xxx定义Dropout,在调用module.eval()之后,可自动实现状态转换。 —— 而nn.functional.xxx不能。

(2)nn.functional更像是纯函数。(表现形式:nn.xxx)

3.3.1、构建网络模型(主要步骤)

(1)构建网络层:torch.nn.Sequential() 。 —— 将网络的层组合到一起。
(2)前向传播:forward()。 —— 将输入层、网络层、输出层连接起来
(3)后向传播:loss.backward()。 —— 选择损失函数和优化器

  • 方法一:基于torch.autograd。 Tensor需设置requires_grad=True,然后调用backward(),最后再从grad属性中提取梯度。
  • 方法二:基于torch.nn。 优化器实例化 -> 损失函数 -> optimizer.zero_grad() -> loss.backward() -> optimizer.step()

(4)训练与测试

  • 训练模式:调用model.train()将所有的module设置为训练模式。
  • 测试模式:调用model.eval()将所有的training属性设置为False
    备注1:如果希望用GPU训练,调用.to(device)。    —— 将模型、训练数据、测试数据发送到GPU上。
    备注2:如果希望用多GPU训练,可使模型或相关数据引用nn.DataParallel。

3.3.2、构建网络模型(方法)

PyTorch中的nn.Sequential、nn.ModuleList和nn.ModuleDict用法总结

3.3.2.1、方法一:nn.Sequential

nn.Sequential 是 PyTorch 中的一个模块容器,它可以将多个网络层按顺序组合起来,形成一个整体的神经网络模型。它的作用类似于将多个层堆叠在一起。

nn.Sequential 和 nn.Sequential(OrderedDict) 都可以用来构建神经网络模型

  • nn.Sequential:(简单的)是一个顺序排列的网络结构,按照添加层的顺序自动建立网络结构,层之间的连接是顺序的,不能插入和删除层。
  • nn.Sequential(OrderedDict):(更灵活)是一个有序字典,按照自定义顺序命名和组织网络层。支持任意顺序添加、删除和查看层,或自定义层名称,而不会丢失网络结构的信息。
import torch.nn as nn
from collections import OrderedDict

# (1)使用 nn.Sequential 定义一个神经网络模型
model = nn.Sequential(
    nn.Linear(784, 256),
    nn.ReLU(),
    nn.Linear(256, 10)
)
print(model)
###########################################
# (2)使用 OrderedDict 定义一个神经网络模型
model = nn.Sequential(OrderedDict([
    ('fc1', nn.Linear(784, 256)),
    ('relu1', nn.ReLU()),
    ('fc2', nn.Linear(256, 10))
]))
print(model)

"""
Sequential(
  (0): Linear(in_features=784, out_features=256, bias=True)
  (1): ReLU()
  (2): Linear(in_features=256, out_features=10, bias=True)
)

Sequential(
  (fc1): Linear(in_features=784, out_features=256, bias=True)
  (relu1): ReLU()
  (fc2): Linear(in_features=256, out_features=10, bias=True)
)
"""
3.3.2.2、方法二:nn.ModuleList
  • nn.ModuleList里面储存了不同 module,并自动将每个 moduleparameters 添加到网络之中的容器(注册),里面的module是按照List类型进行顺序存储的,但是在forward中调用的时候可以随意组合。
  • 可以任意将 nn.Module 的子类 (比如 nn.Conv2d, nn.Linear 之类的) 加到这个 List 里面,通过 extend,append,insert() 等操作。
  • append():在ModuleList后面添加网络层
  • extend():拼接两个ModuleList
  • insert():指定ModuleList中位置插入网络层
import torch.nn as nn
class MyNet(nn.Module):
    def __init__(self):
        super(MyNet, self).__init__()
        self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(10)])

    def forward(self, x):
        # ModuleList can act as an iterable, or be indexed using ints
        for i, l in enumerate(self.linears):
            x = self.linears[i // 2](x) + l(x)
        return x
myNet = MyNet()
print(myNet)

"""
MyModule(
  (linears): ModuleList(
    (0): Linear(in_features=10, out_features=10, bias=True)
    (1): Linear(in_features=10, out_features=10, bias=True)
    (2): Linear(in_features=10, out_features=10, bias=True)
    (3): Linear(in_features=10, out_features=10, bias=True)
    (4): Linear(in_features=10, out_features=10, bias=True)
    (5): Linear(in_features=10, out_features=10, bias=True)
    (6): Linear(in_features=10, out_features=10, bias=True)
    (7): Linear(in_features=10, out_features=10, bias=True)
    (8): Linear(in_features=10, out_features=10, bias=True)
    (9): Linear(in_features=10, out_features=10, bias=True)
  )
)
"""
3.3.2.3、方法三:nn.ModuleDict
  • nn.ModuleDict可以像常规Python字典一样索引,同样自动将每个 moduleparameters 添加到网络之中的容器(注册)。
  • 可以使用OrderedDict、dict或者ModuleDict进行update
  • clear(): 清空ModuleDict
  • items(): 返回可迭代的键值对(key-value pairs)
  • keys(): 返回字典的键(key)
  • values(): 返回字典的值(value)
  • pop(): 返回一对键值,并从字典中删除
  • update(): 添加dict、OrderedDict或者ModuleDict结构。
import torch.nn as nn
class MyNet(nn.Module):
    def __init__(self):
        super(MyNet, self).__init__()
        self.choices = nn.ModuleDict({
                'conv': nn.Conv2d(10, 10, 3),
                'pool': nn.MaxPool2d(3)
        })
        self.activations = nn.ModuleDict([
                ['lrelu', nn.LeakyReLU()],
                ['prelu', nn.PReLU()]
        ])

    def forward(self, x, choice, act):
        # x = self.choices[choice](x)
        # x = self.activations[act](x)
        return x
my_net = MyNet()
print(my_net)

"""
MyModule(
  (choices): ModuleDict(
    (conv): Conv2d(10, 10, kernel_size=(3, 3), stride=(1, 1))
    (pool): MaxPool2d(kernel_size=3, stride=3, padding=0, dilation=1, ceil_mode=False)
  )
  (activations): ModuleDict(
    (lrelu): LeakyReLU(negative_slope=0.01)
    (prelu): PReLU(num_parameters=1)
  )
)
"""
3.3.2.4、方法四:手动逐个添加网络层
import torch.nn as nn
class MyNet(nn.Module):
    def __init__(self):
        super(MyNet, self).__init__()
        self.conv2d_1 = nn.Conv2d(1, 20, 5)
        self.ReLU = nn.ReLU()
        self.conv2d_2 = nn.Conv2d(20, 64, 5)

    def forward(self, x):
        x = self.conv2d_1(x)
        x = self.ReLU(x)
        x = self.conv2d_2(x)
        x = self.ReLU(x)
        return x
my_net = MyNet()
print(my_net)

print('='*50)
for param in my_net.parameters():
    print(type(param.data), param.size())

"""
net_modlist(
  (conv2d_1): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
  (ReLU): ReLU()
  (conv2d_2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
)
==================================================
<class 'torch.Tensor'> torch.Size([20, 1, 5, 5])
<class 'torch.Tensor'> torch.Size([20])
<class 'torch.Tensor'> torch.Size([64, 20, 5, 5])
<class 'torch.Tensor'> torch.Size([64])
"""
3.3.2.5、方法五:add_module() 添加网络子模块

pytorch中使用add_module添加网络子模块

  • add_module(name, module):为modle添加一个子module。其中:name为子模块的名字,module为自定义的网络层(子module)。
  • add_module()可以快速地替换特定结构可以不用修改过多的代码。
from torch import nn
class Net_test(nn.Module):
    def __init__(self):
        super(Net_test, self).__init__()
        self.conv_1 = nn.Conv2d(3,6,3)
        self.add_module('conv_2', nn.Conv2d(6,12,3))		# 添加子模块
        self.conv_3 = nn.Conv2d(12,24,3)
        
    def forward(self,x):
        x = self.conv_1(x)
        x = self.conv_2(x)
        x = self.conv_3(x)
        return x
model = Net_test()									# 模型实例化
model.add_module('conv_1', nn.Conv2d(500,6,3))		# 替换原有网络结构
model.add_module('conv_4', nn.Conv2d(24,12,3))		# 添加子模块
print(model)

"""
Net_test(
  (conv_1): Conv2d(500, 6, kernel_size=(3, 3), stride=(1, 1))
  (conv_2): Conv2d(6, 12, kernel_size=(3, 3), stride=(1, 1))
  (conv_3): Conv2d(12, 24, kernel_size=(3, 3), stride=(1, 1))
  (conv_4): Conv2d(24, 12, kernel_size=(3, 3), stride=(1, 1))
)
"""

3.3.3、实战:手写数字识别(MNIST)

在这里插入图片描述
在这里插入图片描述

############################################
# 主要步骤:
#       (1)利用Pytorch内置函数mnist下载数据。
#       (2)利用torchvision对数据进行预处理,调用torch.utils建立一个数据迭代器。
#       (3)可视化源数据
#       (4)利用nn工具箱构建神经网络模型
#       (5)实例化模型,并定义损失函数及优化器。
#       (6)训练模型
#       (7)可视化结果
############################################
import numpy as np
import matplotlib.pyplot as plt

import torch
import torch.nn.functional as F
import torch.optim as optim
import torch.nn as nn

from torchvision.datasets import mnist          # 导入内置的mnist数据
import torchvision.transforms as transforms     # 导入图像预处理模块
from torch.utils.data import DataLoader

import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'     # "OMP: Error #15: Initializing libiomp5md.dll"
############################################
# (1)定义超参数
train_batch_size = 64
test_batch_size = 128
learning_rate = 0.01
num_epoches = 20
lr = 0.01
momentum = 0.5
############################################
# (2)下载数据,并进行数据预处理
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize([0.5], [0.5])])
#       11、transforms.Compose()方法是将多种变换组合在一起。Compose()会将transforms列表里面的transform操作进行遍历。
#       22、torchvision.transforms.Normalize(mean, std):用给定的均值和标准差分别对每个通道的数据进行正则化。
#           单通道=[0.5], [0.5]     ————     三通道=[m1,m2,m3], [n1,n2,n3]

train_dataset = mnist.MNIST('./pytorch_knowledge', train=True, transform=transform, download=True)
test_dataset = mnist.MNIST('./pytorch_knowledge', train=False, transform=transform)
# download参数控制是否需要下载。如果目录下已有MNIST,可选择False。

train_loader = DataLoader(train_dataset, batch_size=train_batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=test_batch_size, shuffle=False)
############################################
# (3)可视化源数据
examples = enumerate(test_loader)
batch_idx, (example_data, example_targets) = next(examples)

fig = plt.figure()
for i in range(6):
    plt.subplot(2, 3, i+1)
    plt.tight_layout()
    plt.imshow(example_data[i][0], cmap='gray', interpolation='none')
    plt.title('Ground Truth:{}' .format((example_targets[i])))
    plt.xticks(([]))
    plt.yticks(([]))
plt.show()
############################################


# (4)构建网络模型
class Net(nn.Module):
    # 使用Sequential构建网络,将网络的层组合到一起
    def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
        super(Net, self).__init__()
        self.layer1 = nn.Sequential(nn.Linear(in_dim, n_hidden_1), nn.BatchNorm1d(n_hidden_1))
        self.layer2 = nn.Sequential(nn.Linear(n_hidden_1, n_hidden_2), nn.BatchNorm1d(n_hidden_2))
        self.layer3 = nn.Sequential(nn.Linear(n_hidden_2, out_dim))

    def forward(self, x):
        x = F.relu(self.layer1(x))
        x = F.relu(self.layer2(x))
        x = self.layer3(x)
        return x


if __name__ == '__main__':
    ############################################
    # (5)检测是否有可用的GPU,有则使用,否则使用GPU
    device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
    # 实例化网络
    model = Net(28*28, 300, 100, 10)
    model.to(device)
    # 定义损失函数和优化器
    optimizer = optim.SGD(model.parameters(), lr=lr, momentum=momentum)
    criterion = nn.CrossEntropyLoss()
    ############################################
    # (6)训练模型
    losses = []
    acces = []
    eval_losses = []
    eval_acces = []
    for epoch in range(num_epoches):
        # 动态修改参数学习率
        if epoch % 5 == 0:
            optimizer.param_groups[0]['lr'] *= 0.1

        # 训练集 #######################################
        train_loss = 0
        train_acc = 0
        # 将模型切换为训练模式
        model.train()
        for img, label in train_loader:
            img = img.to(device)
            label = label.to(device)
            img = img.view(img.size(0), -1)

            out = model(img)                    # 前向传播
            loss = criterion(out, label)        # 损失函数
            optimizer.zero_grad()               # 梯度清零
            loss.backward()                     # 反向传播
            optimizer.step()                    # 参数更新

            # 记录误差
            train_loss += loss.item()
            # 记录分类的准确率
            _, pred = out.max(1)        # 提取分类精度最高的结果
            num_correct = (pred == label).sum().item()      # 汇总准确度
            acc = num_correct / img.shape[0]
            train_acc += acc
        train_loss_temp = train_loss / len(train_loader)        # 记录单次训练损失
        train_acc_temp = train_acc / len(train_loader)          # 记录单次训练准确度
        losses.append(train_loss / len(train_loader))
        acces.append(train_acc / len(train_loader))

        # 测试集 #######################################
        eval_loss = 0
        eval_acc = 0
        # 将模型切换为测试模式
        model.eval()
        for img, label in test_loader:
            img = img.to(device)
            label = label.to(device)
            img = img.view(img.size(0), -1)

            out = model(img)                    # 前向传播
            loss = criterion(out, label)        # 损失函数

            # 记录误差
            eval_loss += loss.item()
            # 记录分类的准确率
            _, pred = out.max(1)        # 提取分类精度最高的结果
            num_correct = (pred == label).sum().item()
            acc = num_correct / img.shape[0]
            eval_acc += acc
        eval_loss_temp = train_loss / len(train_loader)         # 记录单次测试损失
        eval_acc_temp = train_acc / len(train_loader)           # 记录单次测试准确度
        eval_losses.append(eval_loss / len(test_loader))
        eval_acces.append(eval_acc / len(test_loader))
        print('epoch:{}, Train_loss:{:.4f}, Train_Acc:{:.4f}, Test_loss:{:.4f}, Test_Acc:{:4f}'
              .format(epoch, train_loss_temp, train_acc_temp, eval_loss_temp, eval_acc_temp))

    # (7)可视化结果
    plt.title('Train Loss')
    plt.plot(np.arange(len(losses)), losses)
    plt.legend(['train loss'], loc='upper right')
    plt.xlabel('Steps')         # 设置x轴标签
    plt.ylabel('Loss')          # 设置y轴标签
    plt.ylim((0, 1.2))          # 设置y轴的数值显示范围:plt.ylim(y_min, y_max)
    plt.show()
    
# 备注1:model.eval()的作用是不启用 Batch Normalization 和 Dropout。
# 备注2:model.train()的作用是启用 Batch Normalization 和 Dropout。

model.eval()和model.train()


二、Tensor基础

Tensor 是 PyTorch 中的基本数据类型,类似于 NumPy 的 ndarray,但可以在 GPU 上运行加速计算

  • Tensor 可以用来表示任意维度的数组,并支持各种数学运算和操作。
  • 在深度学习中,神经网络的输入、输出、权重和梯度等数据通常都是以 Tensor 的形式表示和处理。

Tensor操作按接口分为两类:
(1)torch.function
(2)tensor.function
对于大部分Tensor都是等价的,依据个人爱好选择。

2.03、Tensor内存共享 —— 指多个 Tensor 共享同一块内存空间

在 PyTorch 中,Tensor 内存共享通常发生在以下几种情况下:

  • Tensor 初始化另一个 Tensor:通常发生在切片、索引、变形等操作中。
  • 原地操作符(即in-place操作)
    • 在函数后面加上一个后缀" _ "实现。如:copy_、clamp_
    • 执行操作后,将改变原tensor,而不是新建tensor。
  • Tensor 与 NumPy 的转换:此时,对其中一个数据的修改可能会影响另一个数据。

2.02、torch.device() —— 分配到指定设备上

import torch

cpu_device = torch.device('cpu')        # 创建一个表示 CPU 设备的对象
cuda_device = torch.device('cuda')      # 创建一个表示默认 CUDA 设备的对象
cuda0_device = torch.device('cuda:0')   # 创建一个表示指定索引的 CUDA 设备的对象
cuda_name_device = torch.device('cuda:GeForce GTX 1080 Ti')     # 创建一个表示指定名称的 CUDA 设备的对象
cuda_index_device = torch.device(1)     # 创建一个表示指定索引的 CUDA 设备的对象
default_device = torch.device(None)     # 创建一个表示默认设备的对象(当前默认设备是 CUDA 设备)

x = torch.tensor([1, 2, 3], device="cpu")     		# 创建一个 Tensor,并将其发送到指定设备
x = torch.tensor([1, 2, 3], device=cpu_device )     # 创建一个 Tensor,并将其发送到指定设备

#######################################################################
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

if torch.cuda.is_available():
    device = torch.device("cuda")
else:
    device = torch.device("cpu")
# data = data.to(device)      # 表示将张量分配到指定设备上
# model = Model.to(device)    # 表示将模型分配到指定设备上
"""###########################################################################
# 函数功能:用于表示设备的抽象类,它不仅仅支持张量,还可以用于指定模型、张量、张量列表等在哪个设备上执行。
# 函数说明:torch.device(设备类型)
# 参数说明:
#         (1)字符串参数:       指定设备。
#         常见的字符串参数有:
#             'cpu':          表示在 CPU 上执行操作。
#             'cuda':         表示在默认的 CUDA 设备上执行操作。
#             'cuda:0':       表示在指定索引的 CUDA 设备上执行操作。
#             'cuda:1':       表示在另一个指定索引的 CUDA 设备上执行操作。
#             'cuda:device_name':表示在指定名称的 CUDA 设备上执行操作,例如 'cuda:GeForce GTX 1080 Ti'。
#         (2)整数参数:        指定使用 CUDA 设备的索引。例如,torch.device(0) 表示使用索引为 0 的 CUDA 设备。
#         (3)None参数:       表示默认情况,即使用当前默认的设备。
###########################################################################"""

2.01、Tensor在CPU和GPU之间的转换

(1)CPU tensor转GPU tensor:cpu_tensor.cuda()
(2)GPU tensor 转CPU tensor:gpu_tensor.cpu()

2.0、Tensor与Numpy相互转换:torch.Tensor + torch.from_numpy + torch.numpy

import torch
import numpy as np

np_ndarray = np.random.random([4, 4])  # 生成 0~1 之间的随机数
################################################
# (1)数据类型转换:numpy to tensor
torch_tensor = torch.Tensor(np_ndarray)             # 将NumPy数组的数据进行拷贝,生成一个新的PyTorch张量。即使原始数据发生变化,生成的张量也不会受到影响。
torch_from_numpy = torch.from_numpy(np_ndarray)     # 创建一个与NumPy数组共享内存地址的张量。即NumPy数组变化,张量的数据随之变化,反之亦然。只支持numpy输入。
torch_as_tensor = torch.as_tensor(np_ndarray)       # 创建一个与Python对象共享内存地址的张量。支持各种Python对象作为输入(列表、数值、numpy数据)
################################################
# (2)数据类型转换:tensor to numpy
torch_numpy = torch_tensor.numpy()                  # 使用.numpy()方法:最简单和常用的方法,适用于所有类型的张量。
torch_detach_numpy = torch_tensor.detach().numpy()  # 使用.detach().numpy()方法:若需要避免梯度追踪,则可以先调用.detach()方法。
torch_cpu_numpy = torch_tensor.cpu().numpy()        # 使用.cpu().numpy()方法:若张量位于GPU上,需要先将其移动到CPU上,然后再转换为NumPy数组。


print(f"图像类型:{type(np_ndarray)}")           # 图像类型:<class 'numpy.ndarray'>

print(f"图像类型:{type(torch_tensor)}")         # 图像类型:<class 'torch.Tensor'>
print(f"图像类型:{type(torch_from_numpy)}")     # 图像类型:<class 'torch.Tensor'>
print(f"图像类型:{type(torch_as_tensor)}")      # 图像类型:<class 'torch.Tensor'>

print(f"图像类型:{type(torch_numpy)}")          # 图像类型:<class 'numpy.ndarray'>
print(f"图像类型:{type(torch_detach_numpy)}")   # 图像类型:<class 'numpy.ndarray'>
print(f"图像类型:{type(torch_cpu_numpy)}")      # 图像类型:<class 'numpy.ndarray'>

2.1、创建Tensor

AttributeError: 'Tensor' object has no attribute 'copy'

2.1.1、torch.eye + torch.rand + torch.randn + torch.randint

2.1.2、torch.ones + torch.zeros + torch.ones_like + torch.zeros_like

2.1.3、torch.arange + torch.linspace + torch.logspace

import torch

torch_Tensor = torch.Tensor([3, 4])		    # torch.Tensor(size)	        # 创建Tensor
torch_rand = torch.rand([3, 4])			    # torch.rand(size)		        # 生成[0, 1]均匀分布数据
torch_randn = torch.randn([3, 4])		    # torch.randn(size)		        # 生成[0, 1]标准正态分布数据 ———— 标准正态分布:服从均值为0,方差为1的分布。
torch_randint = torch.randint(10, [3, 4])   # torch.randint(high, size)		# 创建size大小数组,元素值在0~high之间随机生成(torch.int64)

torch_eye = torch.eye(5)				            # torch.eye(size)		        # 创建指定大小的单位矩阵
torch_ones = torch.ones([3, 4])			            # torch.ones(size)			    # 创建指定大小的数组,元素全为1。
torch_zeros = torch.zeros([3, 4])		            # torch.zeros(size)			    # 创建指定大小的数组,元素全为0。
torch_ones_like = torch.ones_like(torch_ones)	    # torch.ones_like(input)		# 创建与input相同维度,元素全为1的数组。
torch_zeros_like = torch.zeros_like(torch_zeros)    # torch.zeros_like(input)		# 创建与input相同维度,元素全为0的数组。

torch_arange = torch.arange(0, 10, 3)       # torch.arange(start, stop, step)		# 在区间[start, end]上以步长step生成一个序列张量。
torch_linspace = torch.linspace(0, 10, 3)   # torch.linspace(start, stop, num)		# 生成线性等份向量:从start到stop
torch_logspace = torch.logspace(0, 10, 3)   # torch.logspace(start, stop, num)		# 生成log等份向量:从10^start到10^stop

2.2、Tensor查看属性:torch.numel() + torch.dtype + torch.ndim + torch.shape + torch.size()

import torch
torch_rand = torch.rand([3, 4])		# torch.rand(size)	# 生成[0, 1]均匀分布数据

temp_numel = torch_rand.numel()		# Tensor.numel		# 计算Tensor元素个数
temp_dtype = torch_rand.dtype		# Tensor.dtype		# 查看Tensor数据类型
temp_ndim = torch_rand.ndim			# Tensor.ndim		# 查看数组维度数

temp_shape = torch_rand.shape		# Tensor.shape		# 查看Tensor形状
temp_size = torch_rand.size()		# Tensor.size()		# 查看Tensor形状

2.3、Tensor修改形状:torch.reshape + torch.view

import torch

x = torch.tensor([[1, 2, 3],
                  [4, 5, 6]])

reshaped_tensor = torch.reshape(x, (3, 2))  # Reshape to (3, 2)
print("Reshaped tensor:")
print(reshaped_tensor)

viewed_tensor = x.view(3, 2)  # View as (3, 2)
print("\nViewed tensor:")
print(viewed_tensor)

2.4、(在指定位置)插入或压缩一个维度:torch.squeeze() + torch.unsqueeze()

import torch
import numpy as np

np_ndarray = np.arange(6).reshape(2, 3)
torch_tensor = torch.Tensor(np_ndarray)

# (1)torch.unsqueeze(): 在指定位置插入一个新维度。
unsqueeze1 = torch_tensor.unsqueeze(0)
unsqueeze2 = unsqueeze1.unsqueeze(1)

# (2)torch.squeeze(): 压缩长度为 1 的维度。若没有维度为 1,调用不会出错,且不改变形状。
squeeze = unsqueeze1.squeeze()

print(unsqueeze1.shape, unsqueeze2.shape, squeeze.shape)
"""torch.Size([1, 2, 3]) torch.Size([1, 1, 2, 3]) torch.Size([2, 3])"""

2.4、区别:torch.Tensor + torch.tensor

import torch

print('torch.Tensor.type(): ', torch.Tensor(1).type())      # torch.Tensor.type():  torch.FloatTensor
print('torch.tensor.type(): ', torch.tensor(1).type())      # torch.tensor.type():  torch.LongTensor
print('torch.Tensor.dtype(): ', torch.Tensor(1).dtype)      # torch.Tensor.dtype():  torch.float32
print('torch.tensor.dtype(): ', torch.tensor(1).dtype)      # torch.tensor.dtype():  torch.int64

print('torch.Tensor(): ', torch.Tensor(1))      # torch.Tensor:  tensor([8.4489e-39])
print('torch.tensor(): ', torch.tensor(1))      # torch.tensor:  tensor(1)

"""#####################################################
# 定义不同:
#       torch.Tensor(1)返回一个随机初始化值的张量
#       torch.tensor(1)返回一个整数=1的张量。
#
# 数据类型不同:       
#       torch.Tensor使用全局默认dtype(FloatTensor)
#       torch.tensor从数据中推断数据类型
#####################################################"""

2.5、Tensor数据类型:torch.Tensor + torch.FloatTensor + torch.IntTensor + torch.LongTensor

import torch

print(torch.Tensor().dtype)			# torch.Tensor.dtype(): 		torch.float32
print(torch.FloatTensor().dtype)	# torch.FloatTensor.dtype():  	torch.float32
print(torch.IntTensor().dtype)		# torch.IntTensor.dtype():  	torch.int32
print(torch.LongTensor().dtype)		# torch.LongTensor.dtype():  	torch.int64

2.6、Tensor获取元素和索引

2.6.1、torch.nonzero + torch.masked_select + torch.index_select

2.6.2、torch.gather + torch.scatter

import torch

"""######################################################
# 函数功能:获取张量中非零元素的索引。
# 函数说明:indices = torch.nonzero(input, *, as_tuple=False)
# 参数说明:
#           input		输入张量
#           as_tuple    (可选)默认False。若为True,则返回的索引将作为元组的列表。
######################################################"""
input_tensor = torch.tensor([[1, 2, 3],
                             [4, 5, 6],
                             [7, 8, 9]])
output_indices = torch.nonzero(input_tensor)  # 获取非零元素的索引
output_indices_tuple = torch.nonzero(input_tensor, as_tuple=True)  # 作为元组返回索引
print(output_indices)
print(output_indices_tuple)

"""######################################################
# 函数功能:根据指定的掩码,从输入张量中选择元素。掩码张量中的非零元素表示需要选择的位置。
# 函数说明:output = torch.masked_select(input, mask)
# 参数说明:
#           input		输入张量
#           mask:      掩码张量。用于指定需要选择的位置。形状必须与输入张量相同。
######################################################"""
input_tensor = torch.tensor([[1, 2, 3],
                             [4, 5, 6],
                             [7, 8, 9]])
mask = torch.tensor([[0, 1, 0],
                     [1, 0, 1],
                     [0, 1, 0]], dtype=torch.bool)
output_masked_select = torch.masked_select(input_tensor, mask)
print(output_masked_select)

"""######################################################
# 函数功能:根据索引,从输入张量中选择子张量的函数。
# 函数说明:output = torch.index_select(input, dim, index)
# 参数说明:
#           input		输入张量
#			dim		    指定在哪个维度上进行索引选择。0表示行索引,1表示列索引
# 			index		一个张量,包含了要选择的索引。数据的索引号(必须是LongTensor)
######################################################"""
input_tensor = torch.tensor([[1, 2, 3],
                             [4, 5, 6],
                             [7, 8, 9]])
output0 = torch.index_select(input_tensor, 0, torch.tensor([0, 2]))  # 选择第0维度的第0和2个元素
output1 = torch.index_select(input_tensor, 1, torch.tensor([1, 2]))  # 选择第1维度的第1和2个元素
print(output0)
print(output1)

"""######################################################
# 函数功能:根据索引,从输入张量中收集元素。
# 函数说明:output = torch.gather(input, dim, index, out=None)
# 参数说明:
#           input:      输入张量。
#           dim:        指定收集元素的维度。
#           index:      索引张量,用于指定要收集的元素在指定维度上的位置。
#           out:        (可选)用于指定输出张量。
######################################################"""
input_tensor = torch.tensor([[1, 2],
                             [3, 4],
                             [5, 6]])
index = torch.tensor([[0, 1],
                      [1, 0],
                      [0, 1]])

output_gather = torch.gather(input_tensor, 1, index)
print(output_gather)

"""######################################################
# 函数功能:根据索引,将值散布到新张量中。
# 函数说明:output = tensor.scatter(dim, index, src)
# 参数说明:
#           dim:        指定收集元素的维度。
#           index:      索引张量,,用于指定散布位置的索引。
#           src:        源张量,包含了要散布的值。
######################################################"""
target = torch.zeros(4, 4, dtype=torch.int32)
index = torch.tensor([[0, 1], [2, 3]])
src = torch.tensor([[1, 2], [3, 4]], dtype=torch.int32)
output_scatter = target.scatter(0, index, src)
print(output_scatter)

2.7、广播机制

import numpy as np
import torch

# 创建两个NumPy数组
AA = np.arange(4).reshape(4, 1)
BB = np.arange(0, 3)

# 将NumPy数组转换为PyTorch张量
A1 = torch.from_numpy(AA)
B1 = torch.from_numpy(BB)

# 执行逐元素的张量加法
C1 = A1 + B1

print("张量 A1:")
print(A1)
print("\n张量 B1:")
print(B1)
print("\n结果张量 C1:")
print(C1)

2.8、逐元素操作

2.8.1、torch.add + torch.abs + torch.sqrt + torch.exp + torch.log + torch.pow

2.8.2、torch.sign + torch.ceil + torch.floor

2.8.3、torch.sigmoid + torch.tanh + torch.softmax

import torch

print(f"torch.add(input1, input2):加法计算 ———— {torch.add(torch.Tensor([3.1, -4.92]), torch.Tensor([3, 4]))}")
print(f"torch.abs(input):取绝对值 ———— {torch.abs(torch.Tensor([3.1, -4.92]))}")
print(f"torch.sqrt(input):开根号 ———— {torch.sqrt(torch.Tensor([3.1, -4.92]))}")
print(f"torch.exp(input):指数函数 ———— {torch.exp(torch.Tensor([3.1, -4.92]))}")
print(f"torch.log(input):以自然数e为底的对数函数 ———— {torch.log(torch.Tensor([3.1, -4.92]))}")
print(f"torch.pow(input, exponent):以exponent为底的幂函数 ———— {torch.pow(torch.Tensor([3.1, -4.92]), 2)}")
print(f"torch.sign(input):取符号(1表示正,-1表示负) ———— {torch.sign(torch.Tensor([3.1, -4.92]))}")
print(f"torch.ceil(input):向上取整 ———— {torch.ceil(torch.Tensor([3.1, -4.92]))}")
print(f"torch.floor(input):向下取整 ———— {torch.floor(torch.Tensor([3.1, -4.92]))}")
print(f"torch.sigmoid(input):激活函数 ———— {torch.sigmoid(torch.Tensor([3.1, -4.92]))}")
print(f"torch.tanh(input):激活函数 ———— {torch.tanh(torch.Tensor([3.1, -4.92]))}")
print(f"torch.softmax(input, dim, dtype):激活函数 ———— {torch.softmax(torch.Tensor([3.1, -4.92]), 0, torch.float32)}")

2.9、归并操作

2.9.1、torch.sum + torch.prod + torch.cumsum + torch.cumprod

2.9.2、torch.mean + torch.median + torch.std + torch.var

2.9.2、torch.norm + torch.dist

import torch

# 可以对整个Tensor,也可以沿着指定维度操作
torch_lin = torch.linspace(1, 10, 6).view(2, 3)	# 初始化线性等份向量Tensor并改变数组形状

print(torch.sum(torch_lin, 0))			        # torch.sum(input, dim)			# 求和
print(torch.prod(torch_lin, 0))			        # torch.prod(input, dim)		# 积运算
print(torch.cumsum(torch_lin, 0))		        # torch.cumsum(input, dim)		# 对指定维度数据进行【累加】
print(torch.cumprod(torch_lin, 0))		        # torch.cumprod(input, dim)		# 对指定维度数据进行【累积】
# 累加或累积的计算方式:dim=0表示第一列不变,后面的列依次在上一列基础上加/乘上自身的数。

print(torch.mean(torch_lin, 0))			        # torch.mean(input, dim)		# 计算均值
print(torch.median(torch_lin, 0))		        # torch.median(input, dim)		# 计算中位数
print(torch.std(torch_lin, 0))				    # torch.std(input, dim)			# 计算标准差
print(torch.var(torch_lin, 0))				    # torch.var(input, dim)			# 计算方差

print(torch.norm(torch_lin, 1))					# torch.norm(input, p)			# 返回 p 阶范数(默认值p=2)
print(torch.dist(torch_lin, torch_lin-1, 2))	# torch.dist(input1, input2, p)	# 返回a,b之间的 p 阶范数(默认值p=2)

"""##########################################################
# 范数是一个函数,表示方式为:||X||
# 范数定义了向量空间里的距离,它的出现使得向量之间的比较成为了可能。
# 简单理解:范数可以把一组实数列表,映射成一个实数。
#		L1范数:表示向量x中非零元素的绝对值之和。
#		L2范数:表示向量元素的平方和再开平方。
##########################################################"""

2.10、比较操作

2.10.1、torch.max + torch.min

2.10.2、torch.eq + torch.ge + torch.le + torch.gt + torch.lt

2.10.2、torch.topk + torch.equal

import torch

# 逐元素比较,也可以沿着指定维度操作
torch_lin1 = torch.linspace(1, 10, 6).view(2, 3)  # 初始化线性等份向量Tensor并改变数组形状

print(torch.max(torch_lin1, 0))  # torch.max(input, dim)	# 返回最大值
print(torch.min(torch_lin1, 0))  # torch.min(input, dim)	# 返回最小值

print(torch.eq(torch_lin1, 2.8))  # torch.eq(input, digit)	# 相等比较:比较Tensor中的每个元素是否与digit相等
print(torch.ge(torch_lin1, 2.8))  # torch.ge(input, digit)	# 大于比较:比较Tensor中的每个元素是否大于digit
print(torch.le(torch_lin1, 2.8))  # torch.le(input, digit)	# 小于比较:比较Tensor中的每个元素是否小于digit
print(torch.gt(torch_lin1, 2.8))  # torch.gt(input, digit)	# 大于等于比较:比较Tensor中的每个元素是否大于等于digit
print(torch.lt(torch_lin1, 2.8))  # torch.lt(input, digit)	# 小于等于比较:比较Tensor中的每个元素是否小于等于digit

print(torch.topk(torch_lin1, 1, 0))         # torch.topk(input, k, dim)		# 在指定维度上,分别取前K个最大值
print(torch.equal(torch_lin1, torch_lin1))  # torch.equal(input1, input2)	# 比较两个Tensor是否有相同的shape与值。

2.11、矩阵操作

2.11.1、torch.dot + torch.mm + torch.bmm

import torch

a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])
print(torch.dot(a, b))  # 输出 32

A = torch.tensor([[1, 2], [3, 4]])
B = torch.tensor([[5, 6], [7, 8]])
print(torch.mm(A, B))  # 输出 [[19, 22], [43, 50]]

batch_A = torch.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
batch_B = torch.tensor([[[9, 10], [11, 12]], [[13, 14], [15, 16]]])
print(torch.bmm(batch_A, batch_B))  # 输出 [[[31, 34], [71, 78]], [[129, 140], [179, 194]]]

"""
torch.dot(input1, input2):  用于计算两个张量之间的内积(点积),其中一个张量为一维张量(向量),另一个张量也必须是一维张量。两个向量必须具有相同的长度。
torch.mm(input1, input2):   用于执行两个矩阵的乘法,其中一个矩阵为左矩阵(m×n),另一个矩阵为右矩阵(n×p),返回的结果是一个新的矩阵(m×p)。注意,两个矩阵的维度必须满足乘法规则。
torch.bmm(input1, input2):  用于执行两个批量矩阵的乘法。与 torch.mm() 类似,但输入张量的维度为 (batch_size, m, n) 和 (batch_size, n, p),返回的结果是一个新的张量,每个批次的矩阵乘法结果都包含在其中。
"""

2.11.2、torch.mv + torch.t + torch.svd

import torch

mat = torch.tensor([[1, 2, 3], [4, 5, 6]])
vec = torch.tensor([7, 8, 9])
print(torch.mv(mat, vec))  # 输出 [50, 122]

mat = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(torch.t(mat))  # 输出 [[1, 4], [2, 5], [3, 6]]

input_matrix = torch.tensor([[1., 2.], [3., 4.], [5., 6.]])
print(torch.svd(input_matrix))
"""
torch.return_types.svd(
U=tensor([[-0.2298,  0.8835],
        [-0.5247,  0.2408],
        [-0.8196, -0.4019]]),
S=tensor([9.5255, 0.5143]),
V=tensor([[-0.6196, -0.7849],
        [-0.7849,  0.6196]]))
"""

"""
torch.mv():     用于执行矩阵和向量之间的乘法。给定一个矩阵 mat(大小为 m×n)和一个向量 vec(大小为 n),它返回一个新的向量,是矩阵 mat 和向量 vec 之间的乘积。
torch.t():      用于计算张量的转置。如果是二维张量,则返回转置后的矩阵;如果是高维张量,则可以指定沿着哪些维度进行转置。
torch.svd():    用于执行奇异值分解(Singular Value Decomposition, SVD)。给定一个矩阵 input,它返回三个张量 U, S, V,其中 U 和 V 是正交矩阵,S 是一个对角矩阵,对角线上的元素称为奇异值。
"""

2.12、复制操作

2.12.1、tensor.clone() + tensor.detach() + tensor.clone().detach()

import torch

"""#################################################################
# tensor.clone():返回一个和原张量同shape、dtype和device的张量。两者不共享内存。tensor仍保存在计算图中,即提供梯度计算。
# tensor.detach():返回一个和原张量同shape、dtype和device的张量。两者共享内存,即如果修改其中一个,另一个也会变。其requires_grad=False,即脱离计算图,不提供梯度计算
# 联合使用:tensor.clone().detach()
#		(1)clone提供非数据共享的梯度计算功能,而detach不提供梯度计算功能。
#		(2)联合使用意味着只简单地复制数据,即不数据共享,不梯度共享,两个张量毫无关联。
#################################################################"""

a_tensor = torch.tensor((1.0, 1.0), requires_grad=True, device='cpu', dtype=torch.float64)
print(a_tensor)
print(a_tensor.clone())
print(a_tensor.detach())
print(a_tensor.clone().detach())
print(f"若id相同,则为同一个对象:{id(a_tensor)}, {id(a_tensor.clone())}, {id(a_tensor.detach())}, {id(a_tensor.clone().detach())}")
print(f"不同方法对应的requires_grad:{a_tensor.requires_grad}, {a_tensor.clone().requires_grad}, {a_tensor.detach().requires_grad}, {a_tensor.clone().detach().requires_grad}")
print(f"若内存地址相同,则共享内存:{a_tensor.data_ptr()}, {a_tensor.clone().data_ptr()}, {a_tensor.detach().data_ptr()}, {a_tensor.clone().detach().data_ptr()}")

"""
tensor([1., 1.], dtype=torch.float64, requires_grad=True)
tensor([1., 1.], dtype=torch.float64, grad_fn=<CloneBackward0>)
tensor([1., 1.], dtype=torch.float64)
tensor([1., 1.], dtype=torch.float64)
若id相同,则为同一个对象:2367395354960, 2367396997136, 2367396997136, 2368234527136
不同方法对应的requires_grad:True, True, False, False
若内存地址相同,则共享内存:2368247772992, 2368247770880, 2368247772992, 2368247770816
"""

2.12.2、target_tensor.copy_(source_tensor)

import torch

"""#############################################################################################
# 函数功能:PyTorch 中张量的一个方法,将一个张量的数据复制到另一个张量中(但数据不共享),并保留目标张量的设定。
# 函数说明:target_tensor.copy_(source_tensor)
# 参数说明:
#         source_tensor: 源张量,即要复制数据的张量。
#         target_tensor: 目标张量,即要将数据复制到的张量。

# 调用 copy_() 方法后,target_tensor 的数据会被 source_tensor 的数据替换,但 target_tensor 的其他属性(如形状、数据类型、设备、梯度等)会保持不变。
#############################################################################################"""

b_tensor = torch.tensor([[1, 2, 3], [4, 5, 6]], device='cpu')
c_tensor = torch.tensor([7.0, 8.0, 9.0], requires_grad=True)
b_tensor.copy_(c_tensor)
print(b_tensor)
"""
tensor([[7, 8, 9],
        [7, 8, 9]])
"""

2.12.3、copy.copy() + copy.deepcopy()

import copy

"""###########################################################################################
# 浅拷贝copy.copy():     将原数据打上一个新标签(非独立)。     当其中一个标签被改变时,另一个标签也会随之改变。
# 深拷贝copy.deepcopy(): 完全复制对象(独立个体)。           改变原复制对象不会对已复制对象产生影响。
###########################################################################################"""

list_data = [1, 2, [3, 4]]
list_data_copy = copy.copy(list_data)
list_data_deepcopy = copy.deepcopy(list_data)
print(f"原对象:{list_data}")
print(f"两个对象的值是否相同:{list_data_copy == list_data_deepcopy}")
print(f"两个对象是否为同一个对象:{list_data_copy is list_data_deepcopy}")

list_data[2][0] = 'hey!'
print(f"原对象更新:{list_data}, 浅拷贝随之改变:{list_data_copy}, 深拷贝不改变:{list_data_deepcopy}")

list_data_copy[2][1] = 'hey!'
print(f"原对象更新:{list_data}, 浅拷贝随之改变:{list_data_copy}, 深拷贝不改变:{list_data_deepcopy}")

"""
原对象:[1, 2, [3, 4]]
两个对象的值是否相同:True
两个对象是否为同一个对象:False
原对象更新:[1, 2, ['hey!', 4]], 浅拷贝随之改变:[1, 2, ['hey!', 4]], 深拷贝不改变:[1, 2, [3, 4]]
原对象更新:[1, 2, ['hey!', 'hey!']], 浅拷贝随之改变:[1, 2, ['hey!', 'hey!']], 深拷贝不改变:[1, 2, [3, 4]]
"""

2.13、区间限制:torch.clamp()

import torch

a = torch.randn(4)
b = torch.clamp(a, min=-0.2, max=0.2)
print(a)  # tensor([-1.6694,0.1316,-0.6164,1.0030])
print(b)  # tensor([-0.2000,0.1316,-0.2000,0.2000])

"""#################################################################
# 函数功能:将输入张量的每个元素都收进到区间[min,max], 并返回结果到一个新张量。
# 函数说明:torch.clamp(input, min, max, out=None)
# 参数说明:
#         input (Tensor) – 输入张量
#         min (Number) – 限制范围下限
#         max (Number) – 限制范围上限
#         out (Tensor, optional) – 输出张量
#################################################################"""

一、Numpy基础

NumPy(Numerical Python):是一个用于科学计算的 Python 库。

  • 提供多维数组对象(如:ndarray)
  • 提供各种派生对象(如:masked arrays 和 matrices)
  • 提供数组操作函数(如:数学、逻辑、形状操作、排序、选择、I/O 、离散傅里叶变换、基本线性代数、基本统计运算、随机模拟等等)。

导入 Numpy 库:import numpy as np

Numpy API reference
使用建议:搜索想要了解的函数,会有非常详细的解释以及函数源码。

1.1、数组生成

1.1.1、生成数组:np.array + np.eye + np.diag

1.1.2、生成数组(指定纬度):np.zeros + np.ones + np.empty + np.full

1.1.3、生成数组(相同纬度):np.zeros_like + np.ones_like + np.empty_like + np.full_like

1.1.4、生成向量(线性等份):np.linspace

1.1.5、生成向量(指定范围):np.arange

import numpy as np

##########################################################################
# 列表转换为数组
# np_list1 = np.array([3.14, 1], dtype=float)		# 列表转换为ndarray(元组等同)
# np_list2 = np.array( [[3.14, 1], [1, 2]] )		# 嵌套列表转换为多维ndarray(元组等同)

# 提取数据(向量):
#       1、获取指定位置的数据:arr[3]
#       2、截取片段数据:arr[3, 6]
#       3、截取固定间隔数据:arr[1:6:2]
#       4、倒序取数:arr[::-2]
# 提取数据(数组):
#       5、截取多维数组的区域数据:arr[1:3, 1:2]
#       6、截取多维数组的条件数据:arr[(arr>3) & (arr<10)]
##########################################################################
# 创建数组
np_eye = np.eye(5)				# np.eye(5)				# 创建5*5的单位矩阵。对角线为1,其余为0。
np_diag = np.diag([1, 2, 3])    # np.diag([1,2,3])		# 创建三阶对角矩阵。对角线分别为1,2,3,其余为0。

# 创建指定纬度的数组
np_zeros = np.zeros((3, 4))		# np.zeros((3, 4))		# 创建3*4的元素全为0的数组。输入可为列表或元组。
np_ones = np.ones((3, 4))		# np.ones((3, 4))		# 创建3*4的元素全为1的数组。
np_empty = np.empty((3, 4))		# np.empty((3, 4))		# 创建3*4的空数组。空数组中的值不为0,而是未初始化的垃圾值。
np_full = np.full((3, 5), 666)  # np.full((3, 5), 666)	# 创建3*5的元素全为666的数组,666为指定值。

# 创建相同纬度的数组
np_zeros_like = np.zeros_like(np_zeros)		# np.zeros_like(arr)			# 以arr相同维度创建元素全为0的数组。
np_ones_like = np.ones_like(np_zeros)		# np.ones_like(arr)				# 以arr相同维度创建元素全为1的数组。
np_empty_like = np.empty_like(np_zeros)		# np.empty_like(arr)			# 以arr相同维度创建空数组(未初始化的垃圾值)。
np_full_like = np.full_like(np_full, 666)   # np.full_like(np_full , 666)	# 以arr相同维度创建全为666的数组,666为指定值。

"""##########################################################################
# 函数功能:生成线性等份向量
# 函数说明:numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0)
# 输入参数:
#         start:    数列的起始值。
#         stop:     数列的结束值,生成的数列包括该值。
#         num:      (可选)生成数列的个数。默认为 50。
#         endpoint: (可选)是否包含结束值。如果为 True,则生成的数列包含结束值;如果为 False,则不包含结束值。默认为 True。
#         retstep:  (可选)是否返回步长。如果为 True,则返回数列的步长;如果为 False,则不返回步长。默认为 False。
#         dtype:    (可选)数列的数据类型。如果未提供,则根据其他输入参数自动推断数据类型。
#         axis:     (可选)如果输入参数是数组,则指定数组的轴方向进行操作(可选)。默认为 0。
##########################################################################"""
np_linspace2 = np.linspace(1, 2, 5)
print(np_linspace2)

"""##########################################################################
# 函数功能:生成指定范围向量(输入参数可以为:一个参数,两个参数,三个参数)
# 函数说明:np.arange(start, stop, step, dtype)
# 输入参数:
#         start:    (可选)起始值。如果不提供起始值,则默认为0。
#         stop:     结束值,生成的数列不包括该值。
#         step:     (可选)步长。默认为1。
#         dtype:    (可选)数据类型。如果未提供,则根据其他输入参数自动推断数据类型。
##########################################################################"""
np_arange = np.arange(5)
np_arange1 = np.arange(5, 10)
np_arange2 = np.arange(5, 10, 2)
print(np_arange)
print(np_arange1)
print(np_arange2)

1.2、生成随机数:np.random.rand + np.random.randn + np.random.randint + np.random.uniform + np.random.sample + np.random.normal

import numpy as np

print(np.random.rand(3, 3))                   # 生成一个形状为 (3, 3) 的随机浮点数数组
print(np.random.randn(3, 3))                  # 生成一个形状为 (3, 3) 的随机浮点数数组
print(np.random.randint(1, 10))               # 生成 [1, 10) 范围内的随机整数
print(np.random.randint(1, 10, size=(3, 3)))  # 生成一个形状为 (3, 3) 的随机整数数组
print(np.random.uniform(1, 10))               # 生成 [1, 10) 范围内的随机浮点数
print(np.random.uniform(1, 10, size=(3, 3)))  # 生成一个形状为 (3, 3) 的随机浮点数数组
print(np.random.sample((3, 3)))               # 生成一个形状为 (3, 3) 的随机浮点数数组
print(np.random.normal(0, 1, size=(3, 3)))    # 生成一个形状为 (3, 3) 的服从均值为 0,标准差为 1 的随机浮点数数组

"""######################################################################
np.random.rand:         生成指定形状的服从 [0, 1) 均匀分布的随机浮点数数组。
np.random.randn:        生成指定形状的服从标准正态分布(均值为 0,标准差为 1)的随机浮点数数组。
np.random.randint:      生成指定范围内的随机整数或整数数组。
np.random.uniform:      生成指定范围内的随机浮点数或浮点数数组。
np.random.sample:       生成指定形状的随机浮点数数组,其值位于 [0, 1) 之间。
np.random.normal:       生成指定形状的服从指定均值和标准差的正态分布的随机浮点数数组。
np.random.shuffle:      随机打乱数组中的元素顺序。
######################################################################"""

1.2.2、随机数种子:np.random.seed + np.random.shuffle + np.random.choice

import numpy as np

np.random.seed(123)
print(np.random.random([4, 4]))
"""######################################################################
# 函数功能:设置随机数种子(若种子数相同,则每次生成的随机数相同;若不设置则不相同。)
# 函数说明:np.random.seed()
######################################################################"""

list_temp = [1, 2, 3, 4, 5]
np.random.shuffle(list_temp)	# 随机打乱 my_array 中的元素顺序
print(list_temp)            

print(np.random.choice([1, 2, 3, 4, 5]))                                # 从数组中随机选择一个元素
print(np.random.choice([1, 2, 3, 4, 5], size=3))                        # 从数组中随机选择 3 个元素
print(np.random.choice([1, 2, 3, 4, 5], size=3, replace=True))          # 从数组中随机选择 3 个元素,并允许重复选择
print(np.random.choice([1, 2, 3, 4, 5], size=3, replace=False))         # 从数组中随机选择 3 个元素,并不允许重复选择
print(np.random.choice([1, 2, 3, 4, 5], p=[0.1, 0.2, 0.3, 0.2, 0.2]))   # 从数组中按照指定概率随机选择一个元素
"""######################################################################
# 函数功能:用于从给定的一维数组中随机选择元素。(有放回或无放回抽样)
# 函数说明:numpy.random.choice(a, size=None, replace=True, p=None)
# 输入参数:
#        a:         一维数组
#        size:      表示抽样结果的大小。如果为整数,则表示抽取的元素个数;如果为元组,则表示抽样结果的形状。
#        replace:   表示是否允许重复抽样。如果为 True,则表示有放回抽样;如果为 False,则表示无放回抽样。
#        p:         表示每个元素被抽取的概率。如果为 None,则表示所有元素被抽取的概率相同。
######################################################################"""

1.3、查看数组参数:shape + size + ndim + dtype + itemsize

import numpy as np

arr = np.random.random([4, 4])		# np.random.random()		# 生成0~1之间的随机数
print(arr.shape)		# 查看数组形状
print(arr.size)		    # 查看数组元素个数
print(arr.ndim)		    # 查看数组维度数
print(arr.dtype)		# 查看数组数据类型
print(arr.itemsize)	    # 查看数组元素的字节大小

1.3.2、保存数据为txt文本:np.savetxt()

import numpy as np

np.savetxt(X=np.eye(5), fname='1.txt')

"""######################################
# 函数功能:保存数据到txt文本中
# 函数说明:np.savetxt(arr, file_path)
# 输入参数:
#           arr             数组
#           file_path       保存路径+文件名
# 
# 若指定路径下有相同文件,则覆盖原文本数据。
######################################"""

1.4、算术运算

1.4.1、np.dot() + np.multipy()

import numpy as np

A_arr = np.array([[2, 2, 2], [1, 3, 2], [3, 5, 1]])		# 嵌套列表转换为多维ndarray
B_arr = np.array([[1, 1, 1], [2, 2, 2], [1, 1, 1]])		# 嵌套列表转换为多维ndarray
# (1)逐元乘法(对应位置的元素相乘)
C1 = A_arr * B_arr 		                    # 数组(乘)数组		# 方法一:*
C2 = np.multiply(A_arr, B_arr)				                    # 方法二:np.multipy()
C3 = A_arr * 2			                    # 数组(乘或除)数值

# (2)点积或内积元素:np.dot(A, B)
np_dot = np.dot(A_arr, B_arr)
print(np_dot)
####################################################
# 计算如下:2 2 2     1 1 1 	2x1+2x2+2x1 = 8,  8,  8
#         1 3 2  x  2 2 2 = 1x1+3x2+2x1 = 9,  9,  9
#         3 5 1     1 1 1   3x1+5x2+1x1 =14, 14, 14
####################################################

1.5、改变数组形状:arr.reshape + arr.T + arr.ravel + arr.flatten

import numpy as np

# (1)新建数组
arr = np.arange(6).reshape(2, 3)

# (2)改变数组形状
arr_reshape = arr.reshape(3, -1)	 # arr.reshape()	# 改变数组维度。-1表示自动计算该位置对应的值
arr_T = arr.T						 # arr.T			# 数组转置
arr_reval = arr.ravel()			     # arr.ravel()		# 数组展平 ———— (默认)行展平:arr.ravel(); 列展平:arr.ravel('F')
arr_flatten = arr.flatten()			 # arr.flatten()	# 数组转换为向量(行展平) ———— 应用于卷积网络与全连接层之间

1.6、插入一个维度 + 维度变换:np.newaxis + np.transpose

import numpy as np

np_ndarray = np.arange(6).reshape(2, 3)

# (1)np.newaxis: 在指定位置插入一个长度为1的新维度。
arr_newaxis = np_ndarray[:, :, np.newaxis]

# (2)np.transpose(): 维度变换
arr_transpose1 = np_ndarray.transpose(1, 0)
arr_transpose2 = np.transpose(np_ndarray, (1, 0))

print(arr_newaxis.shape, arr_transpose1.shape, arr_transpose2.shape)
"""(2, 3, 1) (3, 2) (3, 2)"""

1.6、合并数组

1.6.1、np.append + np.concatenate

1.6.2、np.stack + np.vstack + np.hstack + np.dstack

import numpy as np

# np.append(A, B, axis=0)		# 将数组B合并到A(占用内存大)
# np.concatenate(A, B, axis=0)	# 沿指定轴连接数组或矩阵(没有内存问题)
#
# np.stack(A, B, axis=0)		# 沿指定轴合并数组或矩阵								# 堆叠:stack
# np.vstack(A, B, C)			# x轴合并(axis=0):多张图垂直拼接(上下)			# 垂直:vertical
# np.hstack(A, B, C)			# y轴合并(axis=1):多张图水平拼接(左右)			# 水平:horizontal
# np.dstack(A, B, C)			# z轴合并(axis=2):多张图叠在一起(举例:叠乌龟)		# 深度:deep
#
# 注意1:np.append、np.concatenate 以及 np.stack   ———— 都有axis参数,用于控制数组的合并方式。(默认)行:axis=0 ———— 列:axis=1
# 注意2:np.append 和 np.concatenate 			    ———— 要求待合并的数组必须具有相同的行数或列数(满足其一即可)
# 注意3:np.vstack、np.hstack 以及 np.dstack 	    ———— 要求待合并的数组必须具有相同的形状(shape)

1.7、数值计算

1.7.1、np.sqrt + np.log + np.exp + np.sin + np.abs + np.power

1.7.2、np.sum + np.mean + np.median + np.std + np.var + np.corrcoef

import numpy as np

arr_ufunc = 0.5 + np.random.random(10)		# 生成0~1之间的随机数

# 下述方法: 支持输入为数值或向量
np_sqrt = np.sqrt(arr_ufunc)						# np.sqrt()				        # 平方根
np_log = np.log(arr_ufunc)							# np.log/log10/log2()	        # 对数函数
np_exp = np.exp(arr_ufunc)							# np.exp(arr)			        # 指数函数
np_sin = np.sin(arr_ufunc)							# np.sin/cos(arr)		        # 三角函数
np_abs = np.abs(arr_ufunc)							# np.abs(arr)			        # 平方根
np_power = np.power(arr_ufunc, 2)					# np.power(arr, x)		        # arr的x次方

# 下述方法: 只支持输入向量
np_sum = np.sum(arr_ufunc)							# np.sum(arr)			        # 求和
np_mean = np.mean(arr_ufunc)						# np.mean(arr)			        # 均值
np_median = np.median(arr_ufunc)					# np.median(arr)		        # 中位数
np_std = np.std(arr_ufunc)							# np.std(arr)			        # 标准差
np_var = np.var(arr_ufunc)							# np.var(arr)			        # 方差
np_corr = np.corrcoef(arr_ufunc, arr_ufunc-0.2)		# np.corrcoef(arr1, arr2) 		# 相关系数

1.8、广播机制

import numpy as np

A_mac = np.arange(4).reshape(4, 1)
B_mac = np.arange(0, 3)
C_mac = A_mac + B_mac
print('A_shape:{}, B_shape:{}, C_shape:{}' .format(A_mac.shape, B_mac.shape, C_mac.shape))

"""#################################################################################
# 适用范围:当数组的shape不相等时
# 
# 需满足下述四个必要条件:
#		条件1:所有输入数组都向最大维度的数组看齐,维度不足的数组则在最前面加1进行补齐。
#		条件2:输出数组的shape:取输入数组各个维度上的最大值。
#		条件3:当输入数组的某个轴的长度为1时,沿着此轴运算时都用(或复制)此轴上的第一组值。
#		条件4:【注意】如果输入数组的某个轴和输出数组的对应轴的长度相同或某个轴的长度为1时,则该数组能被用于计算,否则出错。
#
# (例如)目的:C=A+B  其中A为(4, 1)二维矩阵, B为一维数值(3,)
# 		条件一:B向A看齐,把B变为(1, 3)
#		条件二:输出结果C=(4, 3)
#		条件三:将A的列数组复制两份拼成4*3;将B的行数据复制三份拼成4*3;然后A(4*3) + B(4*3)得到C(4*3)
#################################################################################"""

1.9、将数组转化为列表:numpy.adarray.tolist()

import numpy as np

arr = np.array([1, 2, 3])
print(arr)                  # 输出结果:{ndarray:(3,)}	[1,2,3]
print(arr.tolist())		    # 输出结果:{list:3}			[1,2,3]
print(arr.tolist()[0])		# 输出结果:{int:1}			1

1.10、截断函数:np.clip()

import numpy as np

"""####################################################################################
# 函数作用:将数组a中的所有数限定在[a_min, a_max]之间。
# 函数说明:np.clip(a, a_min, a_max, out=None)
# 参数说明:	
#         a:		输入矩阵;
#         a_min:	被限定的最小值,所有比a_min小的数都会强制变为a_min;
#         a_max:	被限定的最大值,所有比a_max大的数都会强制变为a_max;
#         out:	    指定输出矩阵的对象,shape与a相同
# 
# 备注1:a_min可以是数值,也可以是数组。
# 备注2:若a_min为数组,则数组长度必须和输入数组等长;两个数组元素根据对应的索引值进行比较
####################################################################################"""

if __name__ == '__main__':
    # (1)一维矩阵
    x = np.arange(10)  # 生成指定范围向量
    print(x)
    print(np.clip(x, 3, 8))  # 将 x 限制在[3, 8]之间(不改变原数组)
    print(np.clip(x, 3, 8, out=x))  # 将修剪后的数组保存到 x 中
    print(np.clip(x, [5,4,3,2,1,4,4,4,4,4], 8))  # 当a_min为数组时

    # (2)多维矩阵
    y = np.arange(10).reshape(2, 5)
    print(np.clip(y, 3, 8))

    """
    [0 1 2 3 4 5 6 7 8 9]
    [3 3 3 3 4 5 6 7 8 8]
    [3 3 3 3 4 5 6 7 8 8]
    [5 4 3 3 4 5 6 7 8 8]
    [[3 3 3 3 4]
     [5 6 7 8 8]]
    """

1.11、获取唯一值:np.unique()

import numpy as np

""""####################################################################################
# 函数功能:获取数组中的唯一灰度值以及它们的出现次数
# 函数说明:unique_values = np.unique(array, return_counts=False, return_index=False, return_inverse=False)
# 参数说明:
#         array:            要查找唯一值的输入数组。
#         return_counts:    若为True,返回每个唯一值在输入数组中出现的次数。
#         return_index:     若为True,返回每个唯一值在输入数组中第一次出现的索引。
#         return_inverse:   若为True,返回一个整数数组,该数组可以用于重新构建原始数组。
####################################################################################"""

if __name__ == '__main__':
    # 创建一个示例数组
    array = np.array([[1, 2, 3], [2, 3, 4], [5, 6, 7]])
    unique_values, counts = np.unique(array, return_counts=True)
    print(f"数组中的唯一灰度值:{unique_values}")
    print(f"每个唯一值的出现次数:{counts}")
    
    # 查找指定的灰度值
    target_value = 3
    coordinates = np.argwhere(array == target_value)
    for coordinate in coordinates:
        print(f"灰度值为 {target_value} 的坐标:{coordinate}")
    
    """
    数组中的唯一灰度值:     [1 2 3 4 5 6 7]
    每个唯一值的出现次数:   [1 2 2 1 1 1 1]
    灰度值为 3 的坐标:     [0 2]
    灰度值为 3 的坐标:     [1 1]
    """

1.12、归一化:np.percentile()

import numpy as np

"""####################################################################################
# 函数功能:用于计算数组中指定百分位数的值
# 函数说明:np.percentile(a, q, axis=None, out=None, overwrite_input=False, 
#                       method='linear', keepdims=False, *, interpolation=None)[source]
# 输入参数:
        (1)a                   输入数组(一维、二维、多维)
        (2)q                   百分位数。值必须介于[0, 100]
        (3)axis                (可选)指定轴方向(默认:沿着扁平数组)
        (4)out                 (可选)保存输出结果。必须与预期输出一致(数组形状、数组类型、数值类型)
        (5)overwrite_input     (可选)若为True,则允许通过中间计算修改输入数组a(如:排序后的数组赋值给原数组),以节省内存。
        (6)method              (可选)估计百分位数的方法
                ‘inverted_cdf’
                ‘averaged_inverted_cdf’
                ‘closest_observation’
                ‘interpolated_inverted_cdf’
                ‘hazen’
                ‘weibull’
                ‘linear’ (default)
                ‘median_unbiased’
                ‘normal_unbiased’
        (7)keepdims             (可选)若为True,则缩小的轴将作为大小为 1 的维度保留在结果中。使用此选项,结果将针对原始数组a正确广播。
####################################################################################"""
def percentile(a, pMin=98):
    flattened_a = a.flatten()  # 将二维数组扁平化为一维数组
    sorted_a = np.sort(flattened_a)  # 数组排序
    index = int(len(sorted_a) * pMin * 0.01)  # 计算百分位数对应的索引
    data_at_percentile = sorted_a[index]  # 索引百分位数对应的值
    print("百分位数对应的值:", data_at_percentile)
    ####################################################################################
    # 算法流程:
    #       (1)一维数组排序
    #       (2)计算指定百分位数对应的值。
    #               2.1、若数组长度为奇数,则取对应值
    #               2.2、若数组长度为偶数,则取两个数的平均值
    #
    #       例如:data数组长度为10000,则 2% 对应的位置为(排序后)10000 * 0.02 =  200,即data[200]对应的值。
    #       例如:data数组长度为10000,则98% 对应的位置为(排序后)10000 * 0.98 = 9800,即data[9800]对应的值。
    ####################################################################################


if __name__ == '__main__':
    a = np.array([[10, 7, 4, 2, 6], [3, 2, 1, 8, 5]])
    b = np.array(1.0)
    b1 = np.percentile(a, 50, out=b)  # 输出结果保存在b中
    b2 = np.percentile(a, 50, overwrite_input=True)  # 此时,数组a = 排序后的数组
    b3 = np.percentile(a, 50, axis=0)  # 沿着X轴计算
    b4 = np.percentile(a, 50, axis=1)  # 沿着Y轴计算
    b5 = np.percentile(a, 50, axis=1, keepdims=True)
    print("b1 =", b1); print("b2 =", b2); print("b3 =", b3); print("b4 =", b4); print("b5 =", b5)

1.13、图像类型的最大灰度值:np.iinfo()

import numpy as np


"""#############################################################################################
# 函数功能:用于获取整数类型的机器限制信息,包括数据类型的最小值、最大值和其他属性。
# 函数说明:info = np.iinfo(type)
# 参数说明:
#         type:整数数据类型,可以是 numpy 的整数类型(如 np.int8, np.int16, np.int32, np.int64)或 Python 的内建整数类型(如 int)。
# 返回值:
#         info:返回一个 numpy.iinfo 对象,包含指定整数数据类型的机器限制信息。
# 属性说明:
#         info.bits:表示整数数据类型的位数。
#         info.min:表示整数数据类型的最小值。
#         info.max:表示整数数据类型的最大值。
#         info.dtype:表示整数数据类型的 numpy dtype 对象。
#############################################################################################"""
if __name__ == '__main__':
    image_array = np.random.randint(0, 256, size=(100, 100), dtype=np.uint8)
    image_array = np.clip(image_array, 0, 244)
    
    max_gray_value = np.iinfo(image_array.dtype).max
    max_value = np.max(image_array)
    
    print("图像类型的最大灰度值:", max_gray_value)  # 图像类型的最大灰度值: 255
    print("图像数组的最大灰度值:", max_value)  # 图像数组的最大灰度值: 244

1.13、测试数组中是否至少有一个元素为 True:np.any()

import numpy as np

"""#############################################################################################
# 函数功能:测试数组中是否至少有一个元素为 True。
# 函数说明:result = np.any(a, axis=None, out=None, keepdims=False, where=True)
# 参数说明:
#         a:输入数组或待测试条件。
#         axis:可选参数,表示沿指定轴进行操作。默认为 None,即测试整个数组。
#         out:可选参数,返回结果的数组。如果提供,它必须具有与预期输出相同的形状和缓冲区。
#         keepdims:可选参数,布尔值。如果为 True,则在输出中保留原始数组的轴,结果数组将保留输入数组的维度。默认为 False。
#         where:可选参数,布尔数组,与输入数组 a 形状相同。如果为 True,则包括元素,否则不包括。默认为 True。
# 返回值:
#         result:布尔值或布尔数组。如果没有给定 axis 参数,返回一个布尔值。如果指定了 axis 参数,返回一个布尔数组。
#############################################################################################"""
if __name__ == '__main__':
    # 示例 1: 简单的一维数组
    array_1d = np.array([0, 0, 1, 0])
    print(f"np.any(array_1d): {np.any(array_1d)}")  # 输出 True,因为有一个元素为 1

    # 示例 2: 二维数组
    array_2d = np.array([[0, 0, 0], [0, 0, 0], [0, 0, 1]])
    print(f"np.any(array_2d): {np.any(array_2d)}")  # 输出 True,因为有一个元素为 1

    # 示例 3: 全 False 的数组
    array_false = np.array([0, 0, 0, 0])
    print(f"np.any(array_false): {np.any(array_false)}")  # 输出 False,因为没有任何元素为 1

    # 示例 4: 空数组
    array_empty = np.array([])
    print(f"np.any(array_empty): {np.any(array_empty)}")  # 输出 False,因为没有元素

    # 示例 5: 布尔数组
    array_bool = np.array([False, False, True, False])
    print(f"np.any(array_bool): {np.any(array_bool)}")  # 输出 True,因为有一个元素为 True

    # 示例 6: 指定轴进行检查
    array_2d = np.array([[0, 1, 0], [0, 0, 0], [0, 0, 0]])
    print(f"np.any(array_2d, axis=0): {np.any(array_2d, axis=0)}")  # 输出 [False  True False],因为在第一列没有1,在第二列有1,在第三列没有1
    print(f"np.any(array_2d, axis=1): {np.any(array_2d, axis=1)}")  # 输出 [ True False False],因为第一行有1,第二行没有1,第三行没有1

    # 示例 7: 三维数组
    array_3d = np.array([[[0, 0], [0, 0]], [[0, 1], [0, 0]]])
    print(f"np.any(array_3d): {np.any(array_3d)}")  # 输出 True,因为有一个元素为 1

  • 15
    点赞
  • 103
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

胖墩会武术

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

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

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

打赏作者

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

抵扣说明:

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

余额充值