pytorch基础

基于教程:https://www.bilibili.com/video/BV1hE411t7RN

Dataset类

from torch.utils.data import Dataset
from PIL import Image
import os

#在这里面用文件jia名作为label
class Mydata(Dataset):

    def __init__(self, root_dir, label_dir):
        self.root_dir = root_dir
        self.label_dir = label_dir
        self.path = os.path.join(root_dir, label_dir) #图片在的文件夹的路径地址
        self.img_path = os.listdir(self.path) #获取所有图片的地址存在list里面

    def __getitem__(self, idx):
        img_name= self.img_path[idx] #单个图片名称
        img_item_path = os.path.join(self.root_dir, self.label_dir, img_name)
        img = Image.open(img_item_path)
        label = self.label_dir
        return img, label

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

root_dir = 'dataset/hymenoptera_data/train'
ants_label_dir = 'ants'
bees_label_dir = 'bees'
ants_dataset = Mydata(root_dir, ants_label_dir)
bees_dataset = Mydata(root_dir, bees_label_dir)

train_dataset = ants_dataset + bees_dataset #把两个数据集加起来

Tensorboard使用

1. add_scalar

from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter("logs")

#wirter.add_scalar("标题", y轴, x轴)
#y = 2x
for i in range(100):
    writer.add_scalar("y=2x", 2*i, i)
writer.close
  • 打开:tensorboard --logdir=事件文件所在的文件夹名 [--port=端口号]

e.g. tensorboard --logdir=D:\code\python\pytorch\test39xlEnv\logs --port=6007

2. add_image

from torch.utils.tensorboard import SummaryWriter
import numpy as np
from PIL import Image

writer = SummaryWriter("logs")
image_path = "练手数据集/train/ants_image/5650366_e22b7e1065.jpg"
img_PIL = Image.open(image_path)
img_array = np.array(img_PIL) #将PIL的图片转换为满足要求的numpy型
print(img_array.shape) #(512, 768, 3)表示HWC格式,3通道在最后面
# writer.add_image("标题", tensor型或numpy型的img, 步骤数, dataformats=图像格式)
writer.add_image("test", img_array, 2, dataformats='HWC')

#wirter.add_scalar("标题", y轴, x轴)
#y = 2x
for i in range(100):
    writer.add_scalar("y=2x", 2*i, i)
writer.close

torchvision中的transforms

from PIL import Image
from torchvision import transforms

img_path = '练手数据集/train/ants_image/0013035.jpg'
img = Image.open(img_path) #PIL类型的图片

tensor_trans = transforms.ToTensor() #transforms中的ToTensor类的对象
tensor_img = tensor_trans(img) #将图片转换为tensor类型

print(tensor_img)

常见的transforms

  • 关注点:输入, 输出,图片的打开方式(作用)
  • PIL——Image.open(), tensor——ToTensor(), narrays——cv.imread()
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms

writer = SummaryWriter("logs")
#PIL图片
img = Image.open("练手数据集/train/ants_image/0013035.jpg")


# ToTensor
trans_totensor = transforms.ToTensor()
img_tensor = trans_totensor(img)
writer.add_image("ToTensor", img_tensor)


# Normalize 归一化
print(img_tensor[0][0][0]) #输出tensor(0.3137)
#transforms.Normalize(均值, 标准差) 下面有3个信道所以是3个数
#input[channel] = (imput[channel] - mean[channel]) / std[channel]
#inout在0~1, result在-1~1
trans_norm = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
img_norm = trans_norm(img_tensor)
print(img_norm[0][0][0]) #输出tensor(-0.3725)
writer.add_image("Normalize", img_norm) #能够看到颜色变了


# Resize 相当于拉伸/压缩图片大小
#PIL图片输入,输出仍为PIL图片
print(img.size) #输出原尺寸(768, 512)
trans_resize = transforms.Resize((512, 512))
img_resize = trans_resize(img)
print(img_resize) #尺寸改变<PIL.Image.Image image mode=RGB size=512x512 at 0x231280DD760>
#给resize返回的图片转换为tensor类型
img_resize = trans_totensor(img_resize)
writer.add_image("Resize", img_resize, 0) #能看到图片被拉伸了


# Compose - resize - 2
trans_resize_2 = transforms.Resize(512) #PIL
print(trans_resize_2)
#Compose()的参数为列表 [arr1, arr2, ...] 表示结合多个操作
#在Compose()中前面的输入为后面的输出, 注意格式是否匹配
# PIL -> PIL -> tensor
trans_compose = transforms.Compose([trans_resize_2, trans_totensor])
img_resize_2 = trans_compose(img) #PIL -> tensor
writer.add_image("Resize", img_resize_2, 1) #能看到大小变了但长宽比例没变


# RandomCrop 随机裁剪
#参数可以是一个数字, 也可以是一个[H,W]的序列
trans_random = transforms.RandomCrop(512) #随机截取一个512的部分图片
#PIL  ->[RandomCrop]-> PIL ->[ToTensor]-> Tensor
trans_compose_2 = transforms.Compose([trans_random, trans_totensor])
for i in range(10):
    img_crop = trans_compose_2(img)
    writer.add_image("RandomCrop", img_crop, i)


writer.close()

Transform使用总结

  • 关注输入和输出的类型
  • 多看官方文档
  • 关注方法需要什么参数即可

不知道返回值时用print(), print(type()), debug

torchvision中的数据集使用

一些操作

import torchvision
from torch.utils.tensorboard import SummaryWriter

#torchvision.datasets.CIFAR10(数据集存放地址, 是否为训练集, 是否下载)
#要根据数据集的不同到官网上查看具体的数据集使用的参数
train_set = torchvision.datasets.CIFAR10(root="./dataset", train=True, download=True)
test_set = torchvision.datasets.CIFAR10(root="./dataset", train=False, download=True)

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

img, target = test_set[0]
print(img) #<PIL.Image.Image image mode=RGB size=32x32 at 0x1BD7B0341F0>
print(target) #3--说明这张图对应的是test_set.classes中的cat
print(test_set.classes[target]) #cat
img.show() #查看图片

writer.close()

结合transforms

import torchvision
from torch.utils.tensorboard import SummaryWriter

#对数据集中的每张图片的操作
dataset_transform = torchvision.transforms.Compose([
    torchvision.transforms.ToTensor()
    #后面也可以更具需求加上其他的操作
])

#torchvision.datasets.CIFAR10(数据集存放地址, 是否为训练集, 对数据集图片的操作, 是否下载)
train_set = torchvision.datasets.CIFAR10(root="./dataset", train=True, transform=dataset_transform, download=True)
test_set = torchvision.datasets.CIFAR10(root="./dataset", train=False, transform=dataset_transform, download=True)

writer = SummaryWriter("p10")
for i in range(10):
    img, target = test_set[i] #这里的图片经过ToTensor后就是SummaryWriter可用的tensor
    writer.add_image("test_set", img, i)

writer.close()

DataLoader的使用

  • dataset: 告诉程序数据集在哪里
  • dataloader: 加载器, 把数据加载到神经网络当中

dataloader参数

dataset: 从哪里加载数据集
batch_size: 每次处理的图像的数量[电脑性能不足时要调低]
shuffle: 是否打乱图像顺序
num_workers:多进程加载
drop_last:除不尽的剩余图像是否取出来使用

import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

test_data = torchvision.datasets.CIFAR10(root="./dataset", train=False, transform=torchvision.transforms.ToTensor())

test_loader = DataLoader(dataset=test_data, batch_size=64, shuffle=False, num_workers=0, drop_last=False)

#测试数据集的第一张样本及target
img, target = test_data[0]#定义的getitem()返回ing, target
print(img.shape)#torch.Size([3, 32, 32])表示3通道
print(target)#对应的target为第3个

writer = SummaryWriter("dataloader")
#取出test_loader的所有数据[取两次]
for epoch in range(2):
    step = 0 #当次次抓取时的step
    for data in test_loader:
        imgs, targets = data
        '''
        当 batch_size=4时
        print(imgs.shape) #第一次取: torch.Size([4, 3, 32, 32])四张图片,三通道,32*32的图片
        print(targets) #第一次取出来的图像对应的target tensor([6, 5, 6, 2])
        '''
        #因为shuffle=False所以不会打断顺序, 两次抓取的图片顺序一样, 改为True则会不一样 [一般都是True]
        writer.add_images('Epoch: {}'.format(epoch), imgs, step)
        step = step + 1

writer.close()

神经网络的基本骨架-nn.Module的使用

  • 写神经网络就相当于重写forward方法
import torch
from torch import nn

class TestNN(nn.Module):
    def __init__(self) -> None:
        super().__init__()

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

testnn = TestNN()
x = torch.tensor(1.0)
output = testnn(x)
print(output)

卷积操作 神经网络中一些基本结构的使用

  • 卷积核: 相当于权重weight
  • bias偏置
  • stride步径: 卷积核在计算中每次移动的距离
  • padding填充: 在输入图像的左右两边进行填充
  • 卷积后的输出: 就是把卷积核放进输入图像中按照重叠部分相乘再相加,最后得到的新矩阵就是卷积后的输出
  • strid步径为2时表示上下左右都是移动2步, 为元组[1, 2]时表示左右1步,上下2步
  • padding填充的输入为一个数或一个元组[sH, sW], 默认0, 默认填充值为0

代码例子

import torch
import torch.nn.functional as F

input = torch.tensor([[1, 2, 0, 3, 1],
                      [0, 1, 2, 3, 1],
                      [1, 2, 1, 0, 0],
                      [5, 2, 3, 1, 1],
                      [2, 1, 0, 1, 1]])

kernel = torch.tensor([[1, 2, 1],
                       [0, 1, 0],
                       [2, 1, 0]])

print(input.shape) #torch.Size([5, 5])
print(kernel.shape) #torch.Size([3, 3])

#被定义的input和kernel只有[H, W]不满足conv2d中input格式
#input的tendor的shape: (minibatch, in_channels, iH, iW)
#使用torch.reshape进行格式变换
input = torch.reshape(input, (1, 1, 5, 5))
kernel = torch.reshape(kernel, (1, 1, 3, 3))

print(input.shape) #torch.Size([1, 1, 5, 5])
print(kernel.shape) #torch.Size([1, 1, 3, 3])

output1 = F.conv2d(input, kernel, stride=1)
print(output1)

output2 = F.conv2d(input, kernel, stride=2)
print(output2)

output3 = F.conv2d(input, kernel, stride=1, padding=1)
print(output3)

输出:

神经网络-卷积层

import torch
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

#dataset = torchvision.datasets.CIFAR10("data路径", 要测试数据集, 转换数据类型,download=True表示下载)
dataset = torchvision.datasets.CIFAR10("../data", train=False, transform=torchvision.transforms.ToTensor(),
                                       download=True)
dataloader = DataLoader(dataset, batch_size=64)

#搭建的简单神经网络
class testConv(nn.Module):
    def __init__(self):
        super(testConv, self).__init__()
        #定义一个卷积层
        # in_channels输入图像的通道数 (彩色图像一般是3个)
        # out_channels输出通道数 (通过卷积后产生的结果输出的通道数)
        # kernel_size卷积核大小 (一个数或者一个元组, 3表示3X3的卷积, 元组定义不规则的卷积核例如1X2的)
        # stride卷积的时 候步径的大小 (横向纵向的步经)
        # padding选择是否卷积时的填充
        self.conv1 = Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1, padding=0)

    def forward(self, x):
        #对x进行一个卷积
        x = self.conv1(x)
        return x

test_conv = testConv()


writer = SummaryWriter("logs")
step = 0
#看dataloader中的每一个数据
for data in dataloader:
    imgs, targets = data
    output = test_conv(imgs)
    print(imgs.shape)#torch.Size([64, 3, 32, 32])
    print(output.shape)#torch.Size([64, 6, 30, 30])
    writer.add_images('input', imgs, step)

    #[64, 6, 30, 30]-->[xxx, 3, 30, 30]将通道数变成3
    output = torch.reshape(output, (-1, 3, 30, 30))#不确定的用-1
    writer.add_images('output', output, step)

    step = step + 1

writer.close()
  • shape的计算公式:

神经网络-最大池化的使用 pooling layers

  • 最大池化操作

以图片为例

import torch
from torch import nn
from torch.nn import MaxPool2d

input = torch.tensor([[1, 2, 0, 3, 1],
                     [0, 1, 2, 3, 1],
                     [1, 2, 1, 0, 0],
                     [5, 2, 3, 1, 1],
                     [2, 1, 0, 1, 1]], dtype=torch.float32)#max_pool2d无法处理Long
input = torch.reshape(input, (-1, 1, 5, 5))
print(input.shape)#torch.Size([1, 1, 5, 5])

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        # kernel_size池化核(选取最大值的窗口int或者元组)
        # stride步经[默认值为池化核大小, 与在卷积层不同, 卷积层默认值为1]
        # dilation空洞卷积
        # ceil_mode:默认为false, Ture时用ceil模式(池化核移动后有无覆盖部分保留), false时用floor模式(不保留)
        self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=True)

    def forward(self, input):
        output = self.maxpool1(input)
        return output

tudui = Tudui()
output = tudui(input)
print(output)

-最大池化的作用: 保留输入数据的特征, 并且把数据量减小 (训练更快), 类似于将1080p的视频传唤为480p会播放更快

import torch
import torchvision
from torch import nn
from torch.nn import MaxPool2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("../data", train=False, download=True,
                                       transform=torchvision.transforms.ToTensor())

dataloader = DataLoader(dataset, batch_size=64)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        # kernel_size池化核(选取最大值的窗口int或者元组)
        # stride步经[默认值为池化核大小, 与在卷积层不同, 卷积层默认值为1]
        # dilation空洞卷积
        # ceil_mode:默认为false, Ture时用ceil模式(池化核移动后有无覆盖部分保留), false时用floor模式(不保留)
        self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=False)

    def forward(self, input):
        output = self.maxpool1(input)
        return output

tudui = Tudui()

writer = SummaryWriter("logs_maxpool")
step = 0

for data in dataloader:
    imgs, targets = data
    writer.add_images("input", imgs, step)
    output = tudui(imgs)
    writer.add_images("output", output, step)
    step = step + 1

writer.close()

神经网络-非线性激活

[给神经网络引入一些非线性特质]

  • ReLU()
import torch
from torch import nn
from torch.nn import ReLU

input = torch.tensor([[1, -0.5],
                      [-1, 3]])
input = torch.reshape(input, (-1, 1, 2, 2))


class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.relu1 = ReLU()

    def forward(self, input):
        output = self.relu1(input)
        return output

tudui = Tudui()
output = tudui(input)
print(output)
#输出:[被截断了]
#tensor([[[[1., 0.],
#          [0., 3.]]]])

ReLU()中的inplace决定是否替换, 为True替换, 为False不替换(默认false)

  • sigmoid
import torch
import torchvision
from torch import nn
from torch.nn import ReLU, Sigmoid
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

input = torch.tensor([[1, -0.5],
                      [-1, 3]])

input = torch.reshape(input, (-1, 1, 2, 2))
print(input.shape)

dataset = torchvision.datasets.CIFAR10("../data", train=False, download=True,
                                       transform=torchvision.transforms.ToTensor())

dataloader = DataLoader(dataset, batch_size=64)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.relu1 = ReLU()
        self.sigmoid1 = Sigmoid()

    def forward(self, input):
        output = self.sigmoid1(input)
        return output

tudui = Tudui()

writer = SummaryWriter("logs_sigmoid")
step = 0
for data in dataloader:
    imgs, targets = data
    writer.add_images("input", imgs, global_step=step)
    output = tudui(imgs) #相当于tudui.forward(input)
    writer.add_images("output", output, step)
    step += 1

writer.close()

神经网络-线性层以及其他层简述

归一化层 Normalization Layers [防止过拟合]
Recurrent Layers [主要手写识别]
Transformer Layer
Linear layer
Dropout Layer [以概率p将input中的一些元素变为0, 防止过拟合]
Sparse Layer[用于自然语言]

-线性层

import torch
import torchvision
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("../data", train=False, transform=torchvision.transforms.ToTensor(),
                                       download=True)

dataloader = DataLoader(dataset, batch_size=64, drop_last=True)


class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        # infeatures=196608, outfeatures=10
        self.linear1 = Linear(196608, 10)

    def forward(self, input):
        output = self.linear1(input)
        return output


tudui = Tudui()

for data in dataloader:
    imgs, targets = data
    print(imgs.shape)# torch.Size([64, 3, 32, 32])
    output = torch.flatten(imgs)  # flatten类似于reshape, 只是把数据摊平
    print(output.shape)
    output = tudui(output)
    print(output.shape)

神经网络-搭建小实战和sequential的使用

  • CIFAR10 model 结构
    1. input:3通道, 32X32
    1. 经过一次5X5的卷积(变成32通道, 尺寸32X32)
    1. 经过一次2X2的最大池化(尺寸减半变成16X16)
    1. 经过一次5X5的卷积(还是32通道, 尺寸16X16)
    1. 经过一次2X2的最大池化(尺寸减半变成8X8)
    1. 经过一次5X5的卷积(变成64通道, 尺寸8X8)
    1. 经过一次2X2的最大池化(尺寸减半变成4X4)
    1. Flatten展平成一行
    1. 通过一次线性层, 并且线性outputs设置为10
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.tensorboard import SummaryWriter


class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.model1 = Sequential(
            Conv2d(3, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    def forward(self, x):
        x = self.model1(x)
        return x

tudui = Tudui()
print(tudui)
input = torch.ones((64, 3, 32, 32))
output = tudui(input)
print(output.shape)

writer = SummaryWriter("logs_seq")
writer.add_graph(tudui, input)
writer.close()

损失函数与反向传播

  • loss function
  1. 计算实际输出output与想要的目标target之间的差距
  2. 为我们更新输出提供一定的依据(反向传播)

    e.g.
  • L1Loss和MSELoss:
  • CrossEntropyLoss

    [以e为底计算指数]

-几种loss function的代码例子

#L1Loss()需要浮点数
inputs = torch.tensor([1, 2, 3], dtype = torch.float32)
targets = torch.tensor([1, 2, 5], dtype = torch.float32)

#1torch size?, 1channel, 1行, 3列
inputs = torch.reshape(inputs, (1, 1, 1, 3))
targets = torch.reshape(targets, (1, 1, 1, 3))

loss = L1Loss(reduction='sum')
L1result = loss(inputs, targets)
#L1Loss默认时结果为tensor(0.6667)【(0+0+2)/3=0.6667】
#L1Loss中reduction='sum'时结果为tensor(2.)【0+0+0=2】
print(L1result)

loss_mse = nn.MSELoss()
result_mse = loss_mse(inputs, targets)
print(result_mse)#tensor(1.3333)

x = torch.tensor([0.1, 0.2, 0.3])
y = torch.tensor([1])
# 1torch size?, 有3类(比如猫狗人这三类)
x = torch.reshape(x, (1, 3))
loss_cross = nn.CrossEntropyLoss()
result_cross = loss_cross(x, y)
print(result_cross)#tensor(1.1019)
  • 在神经网络中使用loss的例子
import torchvision
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("../data", train=False, transform=torchvision.transforms.ToTensor(),
                                       download=True)

dataloader = DataLoader(dataset, batch_size=1)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.model1 = Sequential(
            Conv2d(3, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    def forward(self, x):
        x = self.model1(x)
        return x


loss = nn.CrossEntropyLoss()
tudui = Tudui()
for data in dataloader:
    #target就是实际的输出
    imgs, targets = data
    #output就是通过神经网络得到的输出
    outputs = tudui(imgs)
    result_loss = loss(outputs, targets)
    print(result_loss)#tensor(2.2044, grad_fn=<NllLossBackward0>)

优化器

import torch
import torchvision
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("../data", train=False, transform=torchvision.transforms.ToTensor(),
                                       download=True)

dataloader = DataLoader(dataset, batch_size=1)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()
        self.model1 = Sequential(
            Conv2d(3, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    def forward(self, x):
        x = self.model1(x)
        return x


loss = nn.CrossEntropyLoss()
tudui = Tudui()
#1. 定义一个优化器
#随机梯度下降优化器
#lr是学习速率[一般一开始比较大,后面比较小]
optim = torch.optim.SGD(tudui.parameters(), lr=0.01)

#一共进行20轮学习
for epoch in range(20):#一般都是上千上万次
    running_loss=0.0
    #这个循环只对数据进行了一轮的学习
    for data in dataloader:
        imgs, targets = data
        outputs = tudui(imgs)
        result_loss = loss(outputs, targets)
        #2. 优化器中网络当中的每一个参数清零
        optim.zero_grad()
        #3. 调用损失函数的反向传播求出每一个节点的梯度
        result_loss.backward()
        #4. 对模型的(权重的)每个参数调优[使loss变小]
        optim.step()
        #running_loss相当于每一轮学习中整体的误差的总和
        running_loss = running_loss + result_loss
    print(running_loss)#从tensor(18655.5391, grad_fn=<AddBackward0>)开始每一次都会减少
posted @ 2023-02-10 16:43  学习笔记草稿存放账号  阅读(96)  评论(0)    收藏  举报