MxHanks' Blog

奔赴山海,保持热爱

0%

Pytorch 学习之路

Pytorch 学习之路

一、环境安装

Anaconda

Anaconda 已经在之前机器学习数学基础部分搭建完了,这里没有记录,自己去搜别的吧。

新建 conda 环境

1
2
conda create -n pytorch python=3.6
conda activate pytorch

Pytorch 安装

进入 Pytorch 官网往下拉,然后就能看到配置的界面。因为我目前的显卡是 AMD 的,所以用不了 CUDA ,就选择 Windows-CPU-Conda , 复制命令行运行 , 并进行测试。

1
2
3
conda install pytorch torchvision torchaudio cpuonly
python
import torch

如果导入周没有报错的话,那就安装成功了。

PyCharm

PyCharm 主要是配置环境要折腾一下,如图:
png

其实我还是更愿意使用 vscode + jupyter notebook 。

二、两大法宝函数

理解

dir() 相当于查找,看见,而 help() 类似于终端中常见的 help 帮助能告诉你函数的作用。

实战使用

1
2
3
4
import torch
dir(torch.cuda.is_available)
help(torch.cuda.is_available)

Help on function is_available in module torch.cuda:

is_available() -> bool
    Return a bool indicating if CUDA is currently available.

三、Pytorch 语法

Pytorch 加载数据初认识

数据 Dataset Dataloader
提供一种方式去获得数据以及 lable 为后面的网络提供不同的数据形式

图片(input)

Dataset 代码实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from torch.utils.data import Dataset
from PIL import Image
import os

class MyData(Dataset):

# 定义构造方法
def __init__(self, root_dir, lable_dir):
self.root_dir = root_dir
self.lable_dir = lable_dir
self.path = os.path.join(self.root_dir, self.lable_dir)
self.img_path = os.listdir(self.path)

# 按照索引获取值
def __getitem__(self, index):
img_name = self.img_path[index]
img_item_path = os.path.join(self.path, img_name)
img = Image.open(img_item_path)
lable = self.lable_dir
return img, lable

def __len__(self):
return len(self.img_path)

root_dir = "hymenoptera_data/train"
ants_lable_dir = "ants"
ants_dataset = MyData(root_dir, ants_lable_dir)
bees_lable_dir = "bees"
bees_dataset = MyData(root_dir, bees_lable_dir)
# print(ants_dataset.img_path)
# img, lable = ants_dataset[1]
# img.show()

train_dataset = ants_dataset + bees_dataset



图像变换与 Tensorboard 的使用

TensorBoard 是 Facebook AI Research 开发的一个可视化工具,常用于深度学习模型的训练过程中,以显示和监控训练过程中的各种数据,如损失、准确度、图像等。

TensorBoard

安装 TensorBoard

不要和我一样打成 borad , 英语基础不行啊…

1
2
conda install tensorboard
conda install torch_tb_profiler
使用 TensorBoard 绘制函数图像
1
2
3
4
5
6
7
from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter("logs")

# 绘制 y = x 的图像
for i in range(100):
writer.add_scalar("y=x", i, i)

此时发现文件夹根目录中出现了 log 文件夹。

我们可以通过命令行来处理产生的数据 (pytorch 环境终端):

1
tensorboard --logdir=logs --port={端口号}

其中 logs 为数据所在的文件夹, 输出如下

1
2
3
4
(pytorch)PS F:\Projects\Python\LearnPytorch> tensorboard --logdir=logs
TensorFlow installation not found - running with reduced feature set.
Serving TensorBoard on localhost; to expose to the network, use a proxy or pass --bind_all
TensorBoard 2.16.2 at http://localhost:6006/ (Press CTRL+C to quit)

我们可以通过打开网址 http://localhost:6006/ 来进入数据分析界面

如果使用的是 VSCode 的话,有插件可以直接在 VSCode 中进入该界面。我用的就是 VSCode 的官方插件。

1
2
for i in range(100):
writer.add_scalar("y=2x", 2 * i, i)

此时我们在页面中就能看到两个图标,标题分别为 y=xy=2x

我们把代码修改为:

1
2
for i in range(100):
writer.add_scalar("y=2x", 3 * i, i)

重新运行一遍代码,此时图像中却同时出现了粘合在一起的两个函数图像。

这是开发中容易遇到的问题,我们这时把 log 文件夹删除并且重新运行代码就能成功运行出只有 y=3x 的 y=2x 图像。

使用 TensorBoard 处理图像

我们通过阅读文档发现 tensorBoard 支持的图像数据通常以 NumPy 数组或 PyTorch、TensorFlow 等框架的张量形式存在, 因此使用可以使用 opencv 来读取图片,此处将 PIL 的图片类型转换为 numpy 类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import numpy as np

# 训练材料准备: 一张图片
test_idx = 0
test_img_path = ants_dataset.root_dir + ants_dataset.lable_dir + ants_dataset.img_path[test_idx]
test_img, test_img_lable = ants_dataset[test_idx]
test_img_arr = np.array(test_img)

'''
对于函数 writer.add_image():
Args:
tag (str): Data identifier
img_tensor (torch.Tensor, numpy.ndarray, or string/blobname): Image data
global_step (int): Global step value to record
walltime (float): Optional override default walltime (time.time())
seconds after epoch of event
dataformats (str): Image data format specification of the form
CHW, HWC, HW, WH, etc. 我们需要更换图片通道类型
'''

# 判断图片通道类型
print(test_img_arr.shape)
(512, 768, 3)

通过输出结果 (512, 768, 3), 对应上 (Height, Width, Channel) 得知为三通道类型

1
writer.add_image("test", test_img_arr, 1, dataformats="HWC")   

代码运行成功后,我们再次打开 tensorBoard 界面,发现多了 image 的选项卡,点开就能看到刚才加入的图片。

通常情况下,PIL 使用的颜色通道顺序是 RGB(红绿蓝),而 OpenCV 使用的是 BGR(蓝绿红)。

我们可以通过这个方法把所有的蜜蜂图片都加入到步骤之中:

在写代码时发现有些图像为灰度图像,我们需要把图像数据从 (H, W) 扩展为 (H, W, 1).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def standarize(img_pil):
img = np.array(img_pil)
if img.ndim == 2:
return img.reshape(img.shape + (1,))
else:
return img

"""
这一段代码是将所有图片全部写入 log 中, 但每次运行都要差不多三十秒,
为了节约时间我就改成小批量写入了。速度成功优化到 2s
for epoch in range(0, len(train_dataset)):
img, lable = train_dataset[epoch]
arr_img = standarize(img)
# print(arr_img.shape)
writer.add_image(lable, arr_img, epoch, dataformats="HWC")
"""

for epoch in range(0, 10):
img, lable = train_dataset[epoch]
arr_img = standarize(img)
# print(arr_img.shape)
writer.add_image(lable, arr_img, epoch, dataformats="HWC")
img, lable = train_dataset[epoch + 200]
arr_img = standarize(img)
# print(arr_img.shape)
writer.add_image(lable, arr_img, epoch, dataformats="HWC")

torchvision 中的 trainsforms

transforms 是 PyTorch 图像处理库 torchvision 中非常关键的一部分,用于对图像进行预处理、数据增强等操作。transforms 模块提供了多种图像变换的方法,可以将这些方法组合在一起形成变换流水线 (pipeline)

transforms 结构及用法

图片 -> transforms 工具 -> 想要的结果

Tensor 数据类型

Tensor(张量)是一种多维数组,可以包含各种数据类型,如整数(int)、浮点数(float)、双精度浮点数(double)、布尔值(bool)和字符串(string)等。Tensor数据类型是在深度学习中广泛使用的一种数据表示方式,它可以用来表示图像、文本、语音等各种类型的数据。

Tensor的维度(rank)可以大于2,因此它可以表示比矩阵更高维度的数据。例如,一个三维Tensor可以表示一个包含多个图像的数据集,其中每个图像都是一个二维矩阵。

在TensorFlow中,Tensor是一个核心概念,用于表示计算图中的变量和运算结果。TensorFlow提供了各种函数来创建和操作Tensor,如tf.constant()用于创建一个常量Tensor,tf.Variable()用于创建一个可训练的变量Tensor等。

总之,Tensor是一种多维数组,可以包含各种数据类型,是深度学习中广泛使用的一种数据表示方式。

常见的 transforms
题外: call() 方法
1
2
3
4
5
6
7
8
9
10
11
12
class Person:

def __call__(self, name):
print("__call__" + "Hello" + name)

def hello(self, name):
print("hello" + name)

person = Person()

person("zhangsan")
person.hello("zhangsan")
__call__Hellozhangsan
hellozhangsan
ToTensor 用于将 PIL 等对象转换为 tensor 张量
1
2
3
4
5
6
from torchvision import transforms

img, lable = train_dataset[10]
trans_totensor = transforms.ToTensor()
img_totensor = trans_totensor(img)
writer.add_image("ToTensor", img_totensor)
Normalize 用于进行数据的标准化,加快收敛。

transforms.Normalize 需要两个参数:mean 和 std,它们分别代表图像数据集的均值标准差。这些参数通常是在整个训练集上计算得出的。

1
2
3
trans_norm = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
img_norm = trans_norm(img_totensor)
writer.add_image("Normalize", img_norm)
Resize 用于统一图片的大小
1
2
3
4
5
6
print(img.size)
trans_resize = transforms.Resize((512, 512))
img_resize = trans_totensor(trans_resize(img))
print(img_resize.size)
writer.add_image("Resize", img_resize, 1)

(500, 375)
<built-in method size of Tensor object at 0x00000238F063B610>

如果 Resize 只给一个参数,那就会将图片等比缩放

1
2
3
trans_resize2 = transforms.Resize(512)
image_resize2 = trans_totensor(trans_resize2(img))
writer.add_image("Resize", image_resize2, 2)
RandomCrop 随机裁剪

随机裁剪指的是根据指定的大小在原图片中裁剪出想等大小的片段。

1
2
3
4
trans_random = transforms.RandomCrop(256)
for i in range(10):
img_crop = trans_totensor(trans_random(img))
writer.add_image("RandomCrop", img_crop, i)
Compose

transforms.Compose 是 PyTorch 中 torchvision.transforms 模块提供的一个类,用于将多个图像转换操作组合成一个序列。这个类允许用户定义一个转换操作的列表,然后将这个列表中的所有操作按照顺序应用到图像数据上。

在 PyTorch 中,图像预处理通常需要进行多个步骤,例如将图像转换为张量、归一化、随机裁剪等。使用 transforms.Compose 可以方便地将这些操作按照指定的顺序组合起来,形成一个完整的预处理流水线。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 定义一个转换操作的列表
transform_list = [
transforms.Resize((256, 256)), # 将图像缩放到 256x256
transforms.CenterCrop(224), # 从图像中心裁剪出 224x224 的区域
transforms.ToTensor(), # 将 PIL 图像或 numpy.ndarray 转换为 FloatTensor,并且缩放到 [0.0, 1.0]
transforms.Normalize(
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]
), # 归一化,这里的 mean 和 std 是在 ImageNet 数据集上预训练模型时使用的值
]

# 使用 transforms.Compose 将这些操作组合成一个流水线
transform = transforms.Compose(transform_list)

# 对图像应用这个流水线
transformed_image = transform(img)

writer.add_image("Compose", transformed_image)
总结

尝试去使用方法,如果不懂的话就看文档,关注方法需要的参数,使用两大法宝函数。

torchvision 中的数据集使用

Pytorch 官网中的 torchvision 资源. 在实际使用中,我们通过 import torchrvision 也可以直接使用数据集。

进入叫做 CIFAR 的数据集的官网 , 阅读介绍 :

CIFAR10 Dataset.

Parameters:

root (string) – Root directory of dataset where directory cifar-10-batches-py exists or will be saved to if download is set to True.

train (bool, optional) – If True, creates dataset from training set, otherwise creates from test set.

transform (callable, optional) – A function/transform that takes in an PIL image and returns a transformed version. E.g, transforms.RandomCrop

target_transform (callable, optional) – A function/transform that takes in the target and transforms it.

download (bool, optional) – If true, downloads the dataset from the internet and puts it in root directory. If dataset is already downloaded, it is not downloaded again.

1
2
3
4
5
6
7
from torchvision import datasets # 建议不要直接 import torch 导入时间会非常漫长

train_set = datasets.CIFAR10(root='./dataset', train=True, download=True)
test_set = datasets.CIFAR10(root='./dataset', train=False, download=True)

print(test_set[0])

Files already downloaded and verified
Files already downloaded and verified
(<PIL.Image.Image image mode=RGB size=32x32 at 0x238EC0C4560>, 3)

在这里学习一个学习方法,我们在 print(test_set[0]) 这条语句之前打一个断点进行调试,然后查看 test_set 中包含的所有数据。在调试的过程中我们能更好地理解库功能。通过调试我们知道了 test_set 含有 classes 等类别,data中存放着数据等信息。

1
2
3
4
5
6
7
print(test_set.classes)

img, target = test_set[0]
print(img)
print(target)
print(test_set.classes[target])
# img.show()
['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
<PIL.Image.Image image mode=RGB size=32x32 at 0x238F062B350>
3
cat

这个数据集包含十个类别,每个类别中含有 6000 张 32*32 的图像用于训练与测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
dataset_transformer = transforms.Compose([
transforms.ToTensor()
])

train_set = datasets.CIFAR10(root='./dataset', train=True, download=True, transform=dataset_transformer)
test_set = datasets.CIFAR10(root='./dataset', train=False, download=True, transform=dataset_transformer)

writer = SummaryWriter("p10")

for i in range(10):
img, target = test_set[i]
writer.add_image("test_set", img, target)

Files already downloaded and verified
Files already downloaded and verified

DataLoader 的使用

DataLoader (官方文档)是PyTorch中的一个重要组件,它主要用于加载数据并将其提供给模型进行训练。具体来说,DataLoader是一个可迭代的对象,它可以从数据集中按批次(batch)提取数据,并将这些数据传递给模型进行训练。

DataLoader的主要特点包括:

  1. 批处理:DataLoader可以按指定的批次大小(batch size)从数据集中提取数据,这样可以在一次迭代中处理多个样本,从而提高训练效率。
  2. 打乱数据:在每个训练周期(epoch)开始时,DataLoader可以自动将数据集中的样本顺序打乱,这有助于模型的泛化能力。
  3. 多线程读取:DataLoader支持多线程读取数据,这样可以充分利用计算资源,加速数据的加载速度。
  4. 数据增强:在提取数据时,DataLoader还可以对数据进行一些预处理操作,如裁剪、旋转、翻转等,以增加数据的多样性。
1
2
3
from torch.utils.data import DataLoader

test_loader = DataLoader(dataset=test_set, batch_size=4, shuffle=True, num_workers=0, drop_last=False)

使用 Dataloader(…, batch_size=4) 相当于从 dataset 中取出了 4 个实例

1
2
3
4
5
count = 0
for data in test_loader:
imgs, targets = data
writer.add_images("test_data", imgs, count)
count += 1

我们发现每个 imgs 中都包含了四张被打乱的图像数据。

drop_last 选项如果设置为 False , 则会在最后一组数据无法整除 batch_size 时用其他数据补满。

shuffle=True : 这意味着在每个训练周期(epoch)开始时,数据集中的样本顺序会被打乱。

神经网络

神经网络的基本骨架

神经网络-Pytorch官方文档 (NN - Nerual Network)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from torch import nn

class Hanks(nn.Module):
def __init__(self):
super().__init__()

def forward(self, input):
output = input + 1
return output

hanks = Hanks()
# 将 x 输入到神经网络中
x = torch.tensor(1.0)
output = hanks(x)
print(output)

tensor(2.)

卷积操作

Conv2d (conv 为 convolution 缩写)

TODO… 卷积操作这一步涉及到一些原理我还没有学习,所以暂时先记到这里,应该会有另一篇笔记来记神经网络的理论学习。

毕竟这个 Pytorch 只是个工具,只学工具不知理论是没用的。