PyTorch 与神经网络学习

PyTorch 的基础操作

1 张量

  • 张量如同数组和矩阵一样,即一种特殊的数据结构。
  • 多作为 pytorch 中,神经网络的输入、输出以及网格的参数等数据,都用张量来描述
  • 张量的使用 和 numpy 的 ndarrays 类似,区别在于张量可以在GPU或者其他专用硬件上运行,以达到更快的效果
1.1 张量初始化与创建
# 初始化张量
'''
张量与numpy的数组最大的区别在于张量可以在gpu上运行
'''
data = [[1,2],[3,4]]
x_data= torch.tensor(data)

# numpy 初始化张量
from turtle import shape
import numpy as np
np_array = np.array(data)
x_np = torch.from_numpy(np_array)

# 使用张量去创建张量
# 新的张量将继承已有张量的数据属性(结构、类型),也可重新定义新的数据类型
x_ones = torch.ones_like(x_data) # 创建一个与x_data框架一样的全1张量保留 x_data的属性
print(f"Ones Tensor:\n {x_ones} \n")

x_rand = torch.rand_like(x_data, dtype=torch.float) # 创建一个与x_data框架一样的随机张量,重写 x_data 的数据类型 int——> float
print(f"Random Tensor: \n {x_rand} \n")

print("-----------------")
# 指定数据维度来生成张量
# shape 是元组类型,用来描述张量的维数,以下通过shape 来指定生成张量的维数
shape = (2,3,) # 元组必须写最后一个逗号
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)
print(f"rand_tensor:\n {rand_tensor} \n")
print(f"ones_tensor:\n {ones_tensor} \n")
print(f"zeros_tensor:\n {zeros_tensor} \n")

# 张量的属性
# 从属性上我们可以得到张量的维数、数据类型以及它们所存储的设备(CPU、GPU)
tensor = torch.rand(3,4)
print(f"Shape : {tensor.shape}") # 维数
print(f"Datetype :{tensor.dtype}") # 数据类型
print(f"Device tensor is stored on : {tensor.device}") # 存储设备

print("-------------")

# view 操作可改变张量的维度
x = torch.randn(4,4)
y = x.view(16) # 以16为维度赋值
z = x.view(-1,8) # -1 表示自动计算,4*4 / 8 = 2
print(x.size(), y.size(), z.size()) # 输出其维度

输出结果:

Ones Tensor:
 tensor([[1, 1],
        [1, 1]]) 

Random Tensor: 
 tensor([[0.8275, 0.6488],
        [0.4253, 0.0760]]) 

-----------------
rand_tensor:
 tensor([[0.6493, 0.7338, 0.3523],
        [0.6045, 0.2229, 0.1748]]) 

ones_tensor:
 tensor([[1., 1., 1.],
        [1., 1., 1.]]) 

zeros_tensor:
 tensor([[0., 0., 0.],
        [0., 0., 0.]]) 

Shape : torch.Size([3, 4])
Datetype :torch.float32
Device tensor is stored on : cpu
-------------
torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
1.2 张量运算操作
#索引与切片
from re import T

tensor = torch.ones(4,4)
tensor[:,1] = 0
print(tensor)

# 张量的拼接
t1 = torch.cat([tensor, tensor, tensor], dim=1) # 表示限定维数为1维,tensor 会按顺序依次排列成一维
print(t1)
print("-----------")
# 张量的乘积与矩阵的乘积

# 逐个元素相乘
print(f"tensor.mul(tensor):\n {tensor.mul(tensor)}\n")
# 等价写法
print(f"tensor * tensor : \n {tensor * tensor}")

#矩阵乘积
print(f"tensor.matmul(tensor.T): \n{tensor.matmul(tensor.T)} \n")
#等价写法 
print(f"tensor @ tensor.T :\n {tensor @ tensor.T} \n" )

print("-----------")
# 自动赋值运算
'''
    自动赋值运算通常在方法后有 _ 作为后缀,如 x.copy_(y), x.t_() 操作会改变x的取值
'''
print(tensor, "\n")
tensor.add_(5) # 每个元素加5 ,并改变tensor 的值
print(tensor)
tensor.add(1)
print(tensor)

PyTorch 的GPU环境启动

import torch

tensor = torch.ones(4,4)
tensor[:,1] = 0
print(tensor)

# 调用 GPU环境
# 判断当前环境GPU是否可用,然后将tensor导入GPU内运行
if torch.cuda.is_available():
    tensor = tensor.to('cuda')

torch.cuda.is_available()

# dir(工具区间) # 打开查看()
# help(道具) # 说明书
import torch
dir(torch.cuda)
help(torch.cuda.is_available)

输出结果:

tensor([[1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.]])
Help on function is_available in module torch.cuda:

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

# dir(工具区间) # 打开查看()
# help(道具) # 说明书
import torch
dir(torch.cuda)
help(torch.cuda.is_available)
Help on function is_available in module torch.cuda:

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

数据操作

PyTorch 数据存储与加载

* Dataset (数据获取)

  • Dataset 工具:用于将我们准备的数据集进行打包,方便我们提取数据以及对应的label
    它主要能为我们做的就是

(1)如何获取每一个数据及其label ,(2)告诉我们总共有多少的数据

  • Dataloader 工具: 为后面网络提供不同的加载数据的形式

  • 数据存放规范或者组织形式:

(1)train文件夹(含ants文件夹和bees文件夹等,对应文件夹中存储对应的图像数据集),测试文件另外创建vel

(2)train_images文件夹(保存如100.jpg,101.jpg。。。) 和 train_labels文件夹(保存如gt_100.txt,gt_101.txt。。。。),

opencv 基础补充
# opencv的补充(加载图片)
import cv2

gray_img = cv2.imread('datas\\hymenoptera_data\\train\\ants\\0013035.jpg', 0)  #加载灰度图像
rgb_img = cv2.imread('datas\\hymenoptera_data\\train\\ants\\0013035.jpg', 1)   #加载RGB彩色图像

cv2.imshow("test", rgb_img)
# 必须写这个两句,就是等待有按键按下,再销毁窗口,不然会窗口卡死
cv2.waitKey()
cv2.destroyAllWindows()
实战操作——数据获取与加载
# 实战加载数据(以方式1)
from torch.utils.data import Dataset # utils 是一个常用工具箱
import cv2
import os   # 用于获取地址
class MyData(Dataset): #继承于Dateset
    def __init__(self, root_dir, label_dir):
        self.root_dir = root_dir            # 文件夹地址
        self.label_dir = label_dir          # labol名(比如ants,bees)
        self.path = os.path.join(self.root_dir, self.label_dir) # 将两者拼接起来(window下对应\\)
        self.img_path = os.listdir(self.path) # 把对应文件的所有地址保存为列表
         

    def __getitem__(self, index):
        img_name = self.img_path[index] # index访问对应的地址
        img_item_path = os.path.join(self.root_dir, self.label_dir, img_name) # 形成整个地址
        img = cv2.imread(img_item_path, 1)
        label = self.label_dir
        return img, label
    
    def __len__(self):
        return len(self.img_path)

#获取数据集

root_dir = "datas\\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)        

trains_dataset = ants_dataset + bees_dataset

len(trains_dataset)

245

* DataLoader (数据)

  • 从 Dateset 中取数据,即所谓的数据加载
# DataLoader 测试
import torchvision

# 准备测试数据集
from torch.utils.data import DataLoader  # 数据加载
from torch.utils.tensorboard import SummaryWriter # 数据显示

test_data = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor()) # 将数据存储在Dataset中

# 参数1:Dataset数据集, 参数2(batch_size)表示多少个打包一次, shuffle表示是否打乱顺序,num_workers未知一般为0, 
# drop_last是为了当剩余数据不足以凑够batch_size大小时,true则舍去,false 则继续保留 
test_loader = DataLoader(dataset=test_data, batch_size=64, shuffle=True, num_workers=0, drop_last=True) 

# 测试数据集中第一张图片及 target
img, target = test_data[0] # 可以查看dataset的返回结构
print(img.shape)
print(target)

writer = SummaryWriter("logs") # 显示图片工具(下面讲)

for epoch in range(2):
    step = 0
    for data in test_loader:
        imgs, targets = data
        writer.add_images("test_dataloader: {}".format(epoch), imgs, step) # 这里是add_images 多张照片被被打包
        step = step + 1

writer.close()

Tensorboad 使用(数据显示)

  • 用于展示图像 add_image 或 add_scalor

  • 执行完以下代码后,在(pytorch)的anconda虚拟环境下,在 logs 同名下,

执行:tensorboard --logdir=logs 或者 tensorboard --logdir=logs --port=6007 由此指定端口号

然后将生成的 网络地址 放到浏览器打开,即可看到图像(数据图像与画面图像操作都一样)

from torch.utils.tensorboard import SummaryWriter 
import cv2
import numpy as np

writer = SummaryWriter("logs") # 图像显示口定义(当前目录下生成一个logs文件夹)

# add_image 的使用(获取图像数据)
images_path = "datas\\hymenoptera_data\\train\\ants_image\\0013035.jpg"

img = cv2.imread(images_path,1) #以rgb方式读取

img_array = np.array(img)

writer.add_image("test", img_array, 1, dataformats='HWC') # 参数1:名称, 参数2:(张量tensor 或 numpy的ndarray 或 字符串),参数3为 步骤数


# add_scalar 的使用(“名”,x轴, y轴)
# 绘制 y=x 的图像 
for i in range(100):
    writer.add_scalar("y=x", 2*i,i)
 
writer.close()

* transform (数据转换)

  • 对图像进行变换(图像转换成张量) transforms.ToTensor()

  • 归一化 处理的公式 其实就是正态分布进行处理成标准标准正态,意义为数值距离均值有几个标准差,当E(Z)=0,SD(Z)=1,即均值为0,标准差为1,则表示经过处理后的数据符合标准正态分布(transforms.Normalize(tensor)
    X N o r m a l i z e = x − μ σ {X_{Normalize}} = \frac{x-{\mu}}{\sigma} XNormalize=σxμ

  • 对图像进行等比缩放,transform.Resize((长,宽))

  • transform.compose([…]) 组合变换一起作业

# transform 结构及其作用,其实它就是个.py文件
# 包含各种对图像的转换,可将图像转换为多种数据类型

# transform.totensor (就是将 图像数据转换为tensor数据类型)
# 1 transforms 该如何使用;
from email.policy import default
import torch
import cv2
from torchvision import transforms
from torch.utils.tensorboard import SummaryWriter
from PIL import Image

img_path = "datas/hymenoptera_data/train/ants_image/0013035.jpg"
img = cv2.imread(img_path, 1)

# 转换成 ToTensor (张量处理)
tensor_trans = transforms.ToTensor() # 实例化一个对象(这个对象可以将图像转换成张量格式)
tensor_img = tensor_trans(img)    # 通过该对象进行转换并输出成张量
#  2 为什么我们需要Tensor数据类型
#  结合Tensorboard 使用, 显示图片

writer = SummaryWriter("logs")
writer.add_image("test",tensor_img) # 如果采用张量的话,不需要限定默认参数 dataformats='HWC'
#writer.close()


# 转换成 Normalize (归一化处理)(相当于变标准正态)
print(tensor_img[0][0][0])
trans_norm = transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5]) # 参数1 为均值(mean),参数2 为标准差(std),图片有三个信道,所以三个数据为一组
img_norm = trans_norm(tensor_img)
print(img_norm[0][0][0])

writer.add_image("norm",img_norm)

# 对图像进行等比缩放 Resize
print(img.shape)
trans_resize = transforms.Resize((512, 512)) #进行放缩实例化
#help(trans_resize)
#img_resize = trans_resize(tensor_img) # 会根据形参输入自动匹配对应的成员函数
img_resize = trans_resize.forward(tensor_img)
print(img_resize.shape)

# 对多种转换进行和并,再一并对图像进行转换 Compose
img_PIL = Image.open(img_path)
# PIL -> PIL -> Tensor
trans_compose = transforms.Compose([transforms.Resize((512,512)), transforms.ToTensor()])
img_compose = trans_compose(img_PIL) # 输入必须符合组合转换中每一个转换输入的要求
print(img_compose.shape)

writer.close()

输出结果:

tensor(0.9137)
tensor(0.8275)
(512, 768, 3)
torch.Size([3, 512, 512])
torch.Size([3, 512, 512])

官方数据集如何下载使用

  • 结合 Dataset 同样 Transform 一同使用
# 如何下载数据集(在pytorch官网的torchvision下 可以查看对应公开数据集的操作方法)
import torchvision

# 以下是下载pytorch 官网的数据集方法,三个参数分别是 root(数据集存放位置), train(是否为训练数据true为训练,false为测试,download是否下载)
train_set = torchvision.datasets.CIFAR10(root="./dataset", train=True, download=True) # 下载训练数据集
test_set = torchvision.datasets.CIFAR10(root="./dataset", train=False, download=True) # 下载测试数据集
# Dateset 和 Trnasform 联合使用 对数据集进行批量处理
import torchvision
from torch.utils.tensorboard import SummaryWriter

dataset_transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor()]) # 对数据集图像进行转换

# 以下是下载pytorch 官网的数据集方法,三个参数分别是 root(数据集存放位置), train(是否为训练数据true为训练,false为测试,download是否下载)
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("logs")
for i in range(10):
    img, target = test_set[i] # 访问对应元素每一个元素由元组组成(img,target)
    writer.add_image("test_set", img, i)

writer.close()

神经网络

卷积层 (Convolution layer)

  • nn.Conv1d , 其中 1d 代表 1维

  • nn.Conv2d 代表2维

  • nn.Conv3d 代表3维

  • 用的最多的是 Conv2d ,比如图像

卷积方法1:

torch.nn.functional.Conv2d(input, weight, bias, stride, padding, dilation, group)

input : 输入,其结构必须为(minibatch, in_channels, iH, iW),
其中minibtch代表打包包含多少个图像,in_channels代表多少个通道, iH表示高,iW 表示宽

weiget :权重,也叫卷积核,其结构也必须为( out_channels, i n c h a n n e l s g r o u p {\frac{in_channels}{group}} groupinchannels, kH, kW)

stride : 即表示 每一次当卷积核走多少步,可是单数,也可是元组(sH,sW),默认为1,横走1步,走完横,回到左边纵走1步,再继续从左往右走

padding : 为对输入进行列和行的填充(扩展),可以是单数,也可以是元组

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]])

input = torch.reshape(input, (1, 1, 5, 5)) # 使用重塑,为了符合 (minibatch, in_channels, iH, iW)
kernel = torch.reshape(kernel, (1, 1, 3, 3)) # 为了符合 ( out_channels, ${\frac{in_channels}{group}}$, kH, kW)

print(input.shape)
print(kernel.shape)

output = F.conv2d(input, kernel, stride=1, padding=1) # 卷积核横向步长和纵向步长都为1, 横向(左右都长1)和纵向(上下都长1)向外扩展1
print(output)

输出:

torch.Size([1, 1, 5, 5])
torch.Size([1, 1, 3, 3])
tensor([[[[ 1,  3,  4, 10,  8],
          [ 5, 10, 12, 12,  6],
          [ 7, 18, 16, 16,  8],
          [11, 13,  9,  3,  4],
          [14, 13,  9,  7,  4]]]])

卷积方法2*

torch.nn.Conv2d(in_channels, out_channels, kernel_size, strside=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')

dilation :表示卷积核的对应位的距离
groups : 分组卷积(几乎用不到)
bias : 偏置(是否对卷积后的结果是否加减一个常数),一般默认设置 True
padding_mode : 对被卷积对象进行填充(选择填充模式,默认为0填充(zeros)),可选 ‘reflect’,‘replicate’,‘circular’

in_channels : 输入通道数
out_channels : 输出通道数 ,1个通道数表示采用1个卷积核,2个通道表示采用2个卷积核得到2个卷积输出(这两卷积核内容不一样相同),依次类推
kernel_size : 卷积核的大小(可以是单数(如3 即表示3X3),也可以是元组),内容不需要设置,内容是系统在一些特殊分布中采样得到的

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

# 获取数据
test_dataset = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(), download=False)
# 加载数据
test_dataloader = DataLoader(dataset=test_dataset, batch_size=64)  # 打包个数64个,其他参数默认

# 定义神经网络
class My_nn(nn.Module):
    def __init__(self) -> None:
        super(My_nn, self).__init__()
        self.Conv = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3, stride=1, padding=0)
    
    def forward(self, x):
        x = self.Conv(x)
        return x

nn_test = My_nn() # 实例化
writer = SummaryWriter("logs")

step=0
for data in test_dataloader:
    imgs, targets = data # 一个data 表示一个打包好的组合
    outputs = nn_test(imgs)
    # torch.Size([64, 3, 32, 32])
    print(imgs.shape)
   # torch.Size([64, 6, 30, 30])
    print(outputs.shape)
    writer.add_images("imgs", imgs, step)
    writer.add_images("outputs", outputs, step) 
    step = step +1

池化层 (Pooling lays)

  • 也叫下采样 (**
 nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=false, ceil_mode=False)

(二维下采样) (最重要)
参数:

1) kernel_size :生成窗口(池化核)的尺寸(类似于前面的卷积核),可以是单数,可以是元组

  1. stride : 与卷积层类似,移动的步长(默认值是kernel_size, 这与前面卷积层不一样)

  2. padding :下外扩展

4) dilation : 表示加入空格

  1. return_indices (直接默认,基本不用)

  2. ceil_mode : True 为ceil模式(向上取整), False 为floor模式(向下取整)

调用方法与卷积方法2类似,需要创建完整神经网络框架

输入要求
input :(N,C, H i n {H_{in}} Hin, W i n {W_{in}} Win)
output : (N, C, H o u t {H_{out}} Hout, W o u t {W_{out}} Wout)

其中池化操作:
H o u t = [ H i n + 2 ∗ p a d d i n g [ 0 ] − d i l a t i o n [ 0 ] × ( k e r n e l s i z e [ 0 ] − 1 − 1 s t r i d e [ 0 ] + 1 ] H_{out} = [\frac{H_{in}+2*padding[0]-{dilation[0]}\times{(kernel_size[0]-1}-1}{stride[0]}+1] Hout=[stride[0]Hin+2padding[0]dilation[0]×(kernelsize[0]11+1]

W o u t = [ W i n + 2 ∗ p a d d i n g [ 1 ] − d i l a t i o n [ 1 ] × ( k e r n e l s i z e [ 1 ] − 1 − 1 s t r i d e [ 1 ] + 1 ] W_{out} = [\frac{W_{in}+2*padding[1]-{dilation[1]}\times{(kernel_size[1]-1}-1}{stride[1]}+1] Wout=[stride[1]Win+2padding[1]dilation[1]×(kernelsize[1]11+1]

  • nn.MaxUnpool2d (二维上采样)

MaxPool 即最大池化,对池化核对应的输入的几个数中取最大,用途:
比如降低分辨率,1080X1920,-> 720p,其实就是缩小文件尺寸,降低神经网络的数据量(必用)

import torchvision # 专门用于视觉操作的
from torch.utils.data import DataLoader # 数据加载
from torch import nn
from torch.utils.tensorboard import SummaryWriter

img_dataset = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(),download=False) # 获取

img_loader = DataLoader(dataset=img_dataset, batch_size=24) # 加载

writer = SummaryWriter("logs")

class MyMaxPool(nn.Module):
    def __init__(self) -> None:
        super(MyMaxPool, self).__init__()
        self.MaxPooltest= nn.MaxPool2d(kernel_size=3, ceil_mode=False) 
    
    def forward(self, input):
        output = self.MaxPooltest(input)
        return output

test_pool = MyMaxPool() # 池化操作

step = 0

for data in img_loader:
    img, target = data
    output = test_pool(img)
    writer.add_images("img", img, step)
    writer.add_images("output", output, step)
    step = step + 1

writer.close()

非线性激活 (None-linear Activations)

  • 为了给神经网络引入一些非线性特质
  • 比如:

1) torch.nn.ReLU(inplace=False) :

作用是 当 input > 0 时 取 output = input , 当input <= 0时,output = 0; 其中input:(N,* ),output:(N,* ),N表示batch_size

2)torch.nn.Sigmoid
表示:
S i g m o i d ( x ) = σ ( x ) = 1 1 + exp ⁡ ( − x ) Sigmoid(x) = \sigma(x) = \frac{1}{1+\exp(-x)} Sigmoid(x)=σ(x)=1+exp(x)1
其中input:(N,* ),output:(N,* ),N表示batch_size

基本操作与前面一致

线性化处理

  • torch.nn.Linear(in_features, out_features, bias = True)
    相当于在求: y = x A T + b {y = xA^{T}+b} y=xAT+b

bias 相当于 要不要 b

  • torch.flatten(x) ,将 张量 x 展开成 1 个序列

序列网络模型

  • torch.nn.Sequential(args)

作用是将多个操作按序列形式逐个操作

以下以一个 CIFAR10 为这个神经网络模型为例进行搭建

在这里插入图片描述

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

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

cifar_dataloader = DataLoader(cifar_dataset,batch_size=64)

class cifar_module(nn.Module):
    def __init__(self) -> None:
        super(cifar_module, self).__init__()
        self.sq_module = Sequential(
            Conv2d(3, 32, 5, padding=2), # 这个padding 需要使用卷积公式计算一下
            MaxPool2d(2),
            Conv2d(32, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, padding=2),
            MaxPool2d(2),

            Flatten(), # 64*4*4 变成一行
            Linear(1024, 64), # 线性化处理 进去64*4*4,出去时变64
            Linear(64, 10) # 进去 64 出去 10 (本身里面只有10种可分辨类型)
        )

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

# 实例化一个神经网络框架
Cifar = cifar_module()
print(Cifar)

# 使用一个全1张量测试

input = torch.ones((64, 3, 32, 32)) # 对应 N,C, W, H(输入通道数,输出通道数,长,宽)
output = Cifar(input)
print(output.shape)

输出:

cifar_module(
  (sq_module): Sequential(
    (0): Conv2d(3, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (2): Conv2d(32, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (4): Conv2d(32, 64, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (5): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (6): Flatten(start_dim=1, end_dim=-1)
    (7): Linear(in_features=1024, out_features=64, bias=True)
    (8): Linear(in_features=64, out_features=10, bias=True)
  )
)
torch.Size([64, 10])

损失函数 反向传播 优化器

  • nn.L1Loss
  • nn.MSELoss
  • nn.CrossEntropyLoss (分类常用,交叉熵) 常用
    l o s s ( x , c l a s s ) = − x [ c l a s s ] + l o g ( ∑ j exp ⁡ ( x [ j ] ) ) loss(x, class) = -x[class]+log(\sum_{j} \exp(x[j])) loss(x,class)=x[class]+log(jexp(x[j]))

比如一个分类问题,分类目标有 person, dog, cat

  • Target 就是对应上述分类目标的位置,从0开始计, person为0, dog为1, cat为2
  • output 是一个神经网络模型的输出,一般表现为对应分类目标的概率,如[0.1,0.2,0.3] 即有0.1的概率为person, 0.2的概率为dog, 0.3的概率为cat
  • 公式中的 x {x} x 代表的就是output x [ j ] {x[j]} x[j]就是output的概率元组
  • 公式中的 c l a s s {class} class 代表的是Target 值,
  • 这里的 l o g {log} log 一般是 l n {ln} ln
优化器
  • torch.optim
  • 官方的优化器使用方法示例:参数更新步骤
for input, target in dataset: 
    optimizer.zero_grad()       # 将上一次操作求出的梯度进行清零,以免影响后面的结果
    output = model(input)       # 经过一个神经网络模型 得到输出
    loss = loss_fn(output, target) # 求出输出与目标误差,即损失函数
    loss.backward()             # 使用损失进行反向传播,以得到每个要更新(神经元)的梯度
    optimizer.step()            # 优化器,根据每一步的梯度进行卷积核等参数的优化,以减少loss

torch.optim.xxx(params,…) # 优先器使用, xxx表示优化算,params 是我们需要提供的

例如:optimizer = torch.optim.SGD(模型对象.parameters(), lr=0.01) # lr学习率
#-*-coding:GBK -*-
from pickletools import optimize
import torchvision
import torch
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader

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

cifar_dataloader = DataLoader(cifar_dataset,batch_size=64)

class cifar_module(nn.Module):
    def __init__(self) -> None:
        super(cifar_module, self).__init__()
        self.sq_module = Sequential(
            Conv2d(3, 32, 5, padding=2), # 这个padding 需要使用卷积公式计算一下
            MaxPool2d(2),
            Conv2d(32, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, padding=2),
            MaxPool2d(2),

            Flatten(), # 64*4*4 变成一行
            Linear(1024, 64), # 线性化处理 进去64*4*4,出去时变64
            Linear(64, 10) # 进去 64 出去 10 (本身里面只有10种可分辨类型)
        )

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

# 实例化一个神经网络框架
Cifar = cifar_module()
print(Cifar)

# 使用一个全1张量测试
'''
input = torch.ones((64, 3, 32, 32)) # 对应 N,C, W, H(输入通道数,输出通道数,长,宽)
output = Cifar(input)
print(output.shape)
'''
# 损失函数-交叉熵
loss = nn.CrossEntropyLoss()

# 定义优化器
optimizer = torch.optim.SGD(Cifar.parameters(), lr=0.01) # SGD随机参数优化, Cifar.parameter()获取参数,lr为学习率, 学习率小点训练稍微稳定些

for data in cifar_dataloader:
    imgs, targets = data
    outputs = Cifar(imgs)

    result_loss = loss(outputs, targets) # 计算实际输出与目标之间的差距,作用2 是为我们更新输出提供一定的依据(反向传播),grad(梯度)

    optimizer.zero_grad() # 先对模型的grad进行清零
    result_loss.backward() # 反向传播,这样才会对每一层神经网络生成一个梯度grad(梯度用于确定最终的分类结果)
    # 有了梯度后,选择优化器进行调节,从而降低整体的误差
    optimizer.step() # 优化参数
    
print("ok")

网络模型保存与加载

## 修改已有模型的参数
#  以官方的 vgg16 网络架构为例
vgg16_true = torchvision.models.vgg16(pretrained=True) # 加载已经下载好的Vgg16网络,pretrained 表示已经训练好的,下面的 fasle 表示没有训练好的
vgg16_false = torchvision.models.vgg16(pretrained=False)
# 修改模型参数
vgg16_true.add_module("name", nn.Linear(1000, 10)) # 对现有模型进行修改, 加入一层名为“name” 的神经元,该神经元的作用是以输入为1000的数据线性化成输出为10的数据 

## 保存模型
## 。。。。(前面的神经网络框架)
# 实例化一个神经网络框架
Cifar = cifar_module()
## 保存 与加载 模型
# 方式1 
torch.save(Cifar, "./Cifar.pth") # 路径最好以.pth为后缀
torch.load("./Cifar.pth")

## 方式2保存 (官方推荐,空间占用较少)
torch.save(Cifar.state_dict(), "./Cifar.pth")
# 方式2的加载
Cifar = cifar_module()
Cifar.load_state_dict(torch.load("./Cifar.pth"))

神经网络整体的基本搭建方法

  • 1:获取数据:datasets
  • 2:加载数据:dataloader
  • 3:构建神经网络模型
  • 4:定义损失函数
  • 5:定义优化器
  • 6:for循环处理dataloader数据
  • 处理步骤:
    (1)经过神经网络模型对象
    (2)损失函数计算损失
    -优化器清零梯度
    (3)损失进行反向传播
    (4)优化器优化
  • 测试数据测试,监视每一轮训练的损失,损失或者正确率合格则停止训练

示例

GPU 训练方法1

# -*- coding:GBK -*-

# 如何调用gpu训练:#
# 针对 网络模型、数据(输入、标注)、损失函数变量, 调用对应的.cuda()即可
# #

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

import time # 用于计时

#---------  下载训练数据与测试数据集 到 Dataset
cifar_train_data = torchvision.datasets.CIFAR10("./datas", train=True, transform=torchvision.transforms.ToTensor(),download=True)
cifar_test_data = torchvision.datasets.CIFAR10("./datas", train=False, transform=torchvision.transforms.ToTensor(),download=True)

train_len = len(cifar_train_data)
test_len = len(cifar_test_data)
print("训练数据集大小:{}".format(train_len))
print("测试数据集大小:{}".format(test_len))

#---------  加载数据集到 Dataloader
cifar_train_dataloaders = DataLoader(cifar_train_data, batch_size=64)
cifar_test_dataloaders = DataLoader(cifar_test_data, batch_size=64)

# 加入 tensorboad的显示
writer = SummaryWriter("logs")

#---------  设计神经网络模型
class cifar_module(nn.Module):
    def __init__(self) -> None:
        super(cifar_module, self).__init__()
        self.netwoke = nn.Sequential(
            nn.Conv2d(3,32,5,stride=1, padding=2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 32, 5, stride=1, padding=2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 64, 5, stride=1, padding=2),
            nn.MaxPool2d(2),

            nn.Flatten(), # 排成序列
            nn.Linear(64*4*4, 64),
            nn.Linear(64, 10)
        )

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

Cifar_one = cifar_module() # 实例化一个神经网络模型 
# gpu 训练 —— 网络模型 
if torch.cuda.is_available():
   Cifar_one = Cifar_one.cuda()


#---------  定义损失函数
loss = nn.CrossEntropyLoss()
# gpu 训练 —— 损失函数#
if torch.cuda.is_available():
    loss = loss.cuda()

#---------  定义优化器
Leaning_rate = 1e-2 # 学习率
optimizer = torch.optim.SGD(Cifar_one.parameters(), lr=Leaning_rate)

# 记录训练次数
train_steps = 0 
# 记录测试次数
test_steps = 0
# 设定训练轮数
episode = 10 

# 训练轮数:定义(for)
    # DataLoader 打包处理(for)
        # 神经网络处理
        # 损失计算
        # 反向传播
        # 优化器优化
# start_time = time.time()

for i in range(episode): # 训练轮次
    print("-----第 {} 轮训练-----".format(i+1))

    # --------- 单轮训练步骤
    Cifar_one.train() # 在存在特定神经网络层(pytorch官方查看)时需要(这里可有可不),让网络进入训练状态

    for data in cifar_train_dataloaders:
        imgs, targets = data

        # gpu 训练 —— 输入数据#
        if torch.cuda.is_available():
            imgs = imgs.cuda()
            targets = targets.cuda()        

        output = Cifar_one(imgs)
        result_loss = loss(output, targets) # target 就是标签

        optimizer.zero_grad() # 清除上层的梯度
        result_loss.backward() # 反向传播 产生梯度
        optimizer.step() # 优化器优化模型参数

       # end_time = time.time()
       # print(end_time-start_time)

        train_steps +=1 # 记录训练次数
        if train_steps%50 == 0 : # 每50次打印一次
            print("训练次数:{}, Loss:{}".format(train_steps, result_loss.item())) # 加个item是为让其以数字形式显示,而不是以张量显示
            writer.add_scalar("train_step", result_loss.item(), train_steps)

    # --------- 单轮测试步骤
    Cifar_one.eval() # 在存在特定神经网络层(pytorch官方查看)时需要(这里可有可不),让网络进入验证状态

    total_right_num = 0 # 记录预测成功的个数

    total_test_loss = 0 # 记录测试的总损失
    # 测试每一轮的训练结果是否被训练好(),以测试集上的损失或者正确率来评估
    with torch.no_grad(): # 在测试时,需要使梯度没有
        for data in cifar_test_dataloaders:
            imgs, targets = data

             # gpu 训练 ——输入数据#
            if torch.cuda.is_available():
                imgs = imgs.cuda()
                targets = targets.cuda()    

            output = Cifar_one(imgs)
            result_loss = loss(output, targets)
            total_test_loss += result_loss.item()

            right_num = (output.argmax(1) == targets).sum() # 张量或者列表采用argmax(1)内行向最大的,argmax(0)列内最大的
            total_right_num += right_num

    print("经过该轮训练,整体测试数据集上的loss为:{}".format(total_test_loss))

    print("第{}轮的正确率为:{}".format(i+1, (total_right_num/test_len)))

    writer.add_scalar("test_step", total_test_loss, test_steps) # 测试的是每轮测试的总误差
    test_steps += 1

#--------- 保存模型参数
    torch.save(Cifar_one.state_dict(), "Cifar_{}.pth".format(i+1)) # 保存每轮的模型数据参数
    print("第{}轮训练的模型参数已保存".format(i))

writer.close()

GPU 训练方法2

# -*- coding:GBK -*-

# 如何调用gpu训练:#
# 针对 网络模型、数据(输入、标注)、损失函数变量, 调用对应的.cuda()即可
# #

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

import time # 用于计时

# device = torch.device("cpu")
# device = torch.device("cuda") # 使用gpu
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # python的三目表达式

#---------  下载训练数据与测试数据集 到 Dataset
cifar_train_data = torchvision.datasets.CIFAR10("./datas", train=True, transform=torchvision.transforms.ToTensor(),download=True)
cifar_test_data = torchvision.datasets.CIFAR10("./datas", train=False, transform=torchvision.transforms.ToTensor(),download=True)

train_len = len(cifar_train_data)
test_len = len(cifar_test_data)
print("训练数据集大小:{}".format(train_len))
print("测试数据集大小:{}".format(test_len))

#---------  加载数据集到 Dataloader
cifar_train_dataloaders = DataLoader(cifar_train_data, batch_size=64)
cifar_test_dataloaders = DataLoader(cifar_test_data, batch_size=64)

# 加入 tensorboad的显示
writer = SummaryWriter("logs")

#---------  设计神经网络模型
class cifar_module(nn.Module):
    def __init__(self) -> None:
        super(cifar_module, self).__init__()
        self.netwoke = nn.Sequential(
            nn.Conv2d(3,32,5,stride=1, padding=2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 32, 5, stride=1, padding=2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 64, 5, stride=1, padding=2),
            nn.MaxPool2d(2),

            nn.Flatten(), # 排成序列
            nn.Linear(64*4*4, 64),
            nn.Linear(64, 10)
        )

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

Cifar_one = cifar_module() # 实例化一个神经网络模型 
# gpu 训练 —— 网络模型 
#if torch.cuda.is_available():
#   Cifar_one = Cifar_one.cuda()
Cifar_one = Cifar_one.to(device)

#---------  定义损失函数
loss = nn.CrossEntropyLoss()
# gpu 训练 —— 损失函数#
#if torch.cuda.is_available():
#    loss = loss.cuda()
loss = loss.to(device)

#---------  定义优化器
Leaning_rate = 1e-2 # 学习率
optimizer = torch.optim.SGD(Cifar_one.parameters(), lr=Leaning_rate)

# 记录训练次数
train_steps = 0 
# 记录测试次数
test_steps = 0
# 设定训练轮数
episode = 10 

# 训练轮数:定义(for)
    # DataLoader 打包处理(for)
        # 神经网络处理
        # 损失计算
        # 反向传播
        # 优化器优化
# start_time = time.time()

for i in range(episode): # 训练轮次
    print("-----第 {} 轮训练-----".format(i+1))

    # --------- 单轮训练步骤
    Cifar_one.train() # 在存在特定神经网络层(pytorch官方查看)时需要(这里可有可不),让网络进入训练状态

    for data in cifar_train_dataloaders:
        imgs, targets = data

        # gpu 训练 —— 输入数据#
        #if torch.cuda.is_available():
        #    imgs = imgs.cuda()
        #    targets = targets.cuda()    
        imgs = imgs.to(device)
        targets = targets.to(device)    

        output = Cifar_one(imgs)
        result_loss = loss(output, targets) # target 就是标签

        optimizer.zero_grad() # 清除上层的梯度
        result_loss.backward() # 反向传播 产生梯度
        optimizer.step() # 优化器优化模型参数

       # end_time = time.time()
       # print(end_time-start_time)

        train_steps +=1 # 记录训练次数
        if train_steps%50 == 0 : # 每50次打印一次
            print("训练次数:{}, Loss:{}".format(train_steps, result_loss.item())) # 加个item是为让其以数字形式显示,而不是以张量显示
            writer.add_scalar("train_step", result_loss.item(), train_steps)

    # --------- 单轮测试步骤
    Cifar_one.eval() # 在存在特定神经网络层(pytorch官方查看)时需要(这里可有可不),让网络进入验证状态

    total_right_num = 0 # 记录预测成功的个数

    total_test_loss = 0 # 记录测试的总损失
    # 测试每一轮的训练结果是否被训练好(),以测试集上的损失或者正确率来评估
    with torch.no_grad(): # 在测试时,需要使梯度没有
        for data in cifar_test_dataloaders:
            imgs, targets = data

             # gpu 训练 ——输入数据#
            #if torch.cuda.is_available():
            #    imgs = imgs.cuda()
            #    targets = targets.cuda()  
            imgs = imgs.to(device)
            targets = targets.to(device)  

            output = Cifar_one(imgs)
            result_loss = loss(output, targets) # 输出与目标的损失
            total_test_loss += result_loss.item() 

            right_num = (output.argmax(1) == targets).sum() # 张量或者列表采用argmax(1)内行向最大的,argmax(0)列内最大的
            total_right_num += right_num

    print("经过该轮训练,整体测试数据集上的loss为:{}".format(total_test_loss))

    print("第{}轮的正确率为:{}".format(i+1, (total_right_num/test_len)))

    writer.add_scalar("test_step", total_test_loss, test_steps) # 测试的是每轮测试的总误差
    test_steps += 1

#--------- 保存模型参数
    torch.save(Cifar_one.state_dict(), "Cifar_{}.pth".format(i+1)) # 保存每轮的模型数据参数
    print("第{}轮训练的模型参数已保存".format(i))

writer.close()

如何使用已经训练好的模型进行预测

  • 预测对应的标签与target 可以通过对 dataset 的变量打断点的方式的在debug模式下查找到,比如 Cifar10 的
    在 class_to_idex 下面:
    在这里插入图片描述
# -*- coding:GBK -*-
from statistics import mode
import torch
import torchvision
from torch import nn
import cv2
from PIL import Image

#import train

image_path = "./imgs/deer.png" # 用于预测的图片路径
image = cv2.imread(image_path, 1) # 1 表示以rgb图片加载

print(image.shape) # numpy的数据格式【H*W*C】
#cv2.imshow("test", image)

#cv2.waitKey()
#cv2.destroyAllWindows()

transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)),
                                            torchvision.transforms.ToTensor()])
# opencv -> PIL
img_pil = Image.fromarray(cv2.cvtColor(image,cv2.COLOR_BGR2RGB)) 
# 已经转换成能够识别的格式了
image = transform(img_pil) # 因为transform只能定对 PIL 进行读入的数据操作,所以要对cv进行转换

print(image.shape) # tensor的数据格式【C*H*W】 通道数*高*宽

#---------  设计神经网络模型b (如果是自己设计的模型, 如果不使用import的话,就需要把模型再写一遍)
class cifar_module(nn.Module):
    def __init__(self) -> None:
        super(cifar_module, self).__init__()
        self.netwoke = nn.Sequential(
            nn.Conv2d(3,32,5,stride=1, padding=2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 32, 5, stride=1, padding=2),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 64, 5, stride=1, padding=2),
            nn.MaxPool2d(2),

            nn.Flatten(), # 排成序列
            nn.Linear(64*4*4, 64),
            nn.Linear(64, 10)
        )

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

Cifar = cifar_module() # 实例化模型对象
Cifar.load_state_dict(torch.load("./nn_test/module_data/Cifar_30.pth"))
# 如果是gpu训练的,但是又是cpu测试,则需要使用map_loaction参数进行映射
# Cifar.load_state_dict(torch.load("./nn_test/module_data/Cifar_30.pth",map_location="cpu"))

# 将image 设定为 N*C*H*W 格式
image = torch.reshape(image, (1, 3, 32, 32)) # batch_size 

Cifar.eval() # 对模型进行测试预备

with torch.no_grad():# 保证没有梯度
    output = Cifar(image) # 将测试图片输入模型进行测试

print(output)
print(output.argmax(1)) # 求行最大 即为对应预测结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

LionelMartin

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

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

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

打赏作者

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

抵扣说明:

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

余额充值