pytorch学习笔记汇总

. tensor创建

1 . 张量的定义

张量在形式上就是多维数组,例如标量就是0维张量,向量就是一维张量,矩阵就是二维张量,而三维张量就可以想象RGB图片,每个channel是一个二维的矩阵,共有三个channel,还可以考虑更多。

在代码中创建张量Tensor数据类型时,除了封装张量本身的数据data外,还会附加张量的一些性质和操作,例如数据的梯度(grad),创建tensor的函数(grad_fun,是求导的关键),是否为叶子节点(is_leaf),是否需要梯度(require_grad)。

2 . 张量的创建

2 .1 tensor直接创建

torch.tensor([1])

arr = np.ones((3, 3))

t = torch.tensor(arr, device='cuda')

t = torch.tensor(arr)

值得注意的点:

(1)tensor()括号里的数据可以是list(以"()"表示),也可以是数组(以[]表示),也可以是numpy,即先用numpy创建一个numpy,然后直接导入(如下);

(2)注意数据类型,有时候需要在数字后面加"."表示float,因为求导时候需要float类型;

(3)可以添加device='cuda'获得加速。

2 .2 from _numpy 从numpy中创建

arr = np.array([[1, 2, 3], [4, 5, 6]])

t = torch.from_numpy(arr)

arr[0, 0] = 0

t[0, 0] = -1

值得注意的点:

(1)这个创建的tensor和原来的numpy共享内存,也即是说修改tensor就会修改原来的numpy。

2.3 从数字中创建

通过torch.zeros创建张量

out_t = torch.tensor([1])

#t=torch.zeros((3,3))

t = torch.zeros((3, 3), out=out_t)

值得注意的点:

(1)也可以先创建一个tensor,然后在zeros函数的out接收创建的zeros,二者的size可以不一样,创建完成后二者一致。

(2)还可以torch.ones

(3)全1张量还可以用full函数

t = torch.full((3, 3), 1)

注意试验一下是不是还可以创建全"2"张量?

(4)还可以利用torch.zeros_like(),torch.ones_like(),torch.full_like创建和input张量(类似于size,只不过是用一个真实的张量表示)一致的全0/1张量。

(5)torch.eye()创建单位对角矩阵

2 .4 等差均分创建

t = torch.arange(2, 10, 2)

(1)创建等差数列张量,后面为等差值,默认为1.

t = torch.linspace(2, 10, 5)

t = torch.linspace(2, 10, 6)

(1)在[start,end]中均分n等份,这时会出现小数。

(2)还可以等log创建,torch.logspace()

2 .5 依据概率创建

通过torch.normal创建正态分布张量

mean:张量 std: 张量

mean = torch.arange(1, 5, dtype=torch.float)

std = torch.arange(1, 5, dtype=torch.float)

t_normal = torch.normal(mean, std)

mean:标量 std: 标量

t_normal = torch.normal(0., 1., size=(4,))

mean:张量 std: 标量

mean = torch.arange(1, 5, dtype=torch.float)

std = 1

t_normal = torch.normal(mean, std)

(1)注意mean,std可以是标量和张量的组合,共四种模式。

(2)torch.randn(),torch.randn_like()创建标准正态分布张量;

(3)torch.rand(),torch.rand_like()创建[0,1]均匀分布。

(4)torch.randint(low,high),torch.randint_like(low,high)创建[low,high)均匀分布。

(5)torch.randperm(n),创建从0到n-1的随机排列张量

(6)torch.bernoulli(input),创建以input为概率值的伯努利分布张量。

3 . 张量的操作

3 .1 张量拼接

torch.cat(tensors,dim)

t = torch.ones((2, 3))

t_0 = torch.cat([t, t], dim=0)

t_1 = torch.cat([t, t, t], dim=1)

(1)是在原来的维度上进行拼接

torch.stack(tensor,dim)

t = torch.ones((2, 3))

t_stack = torch.stack([t, t, t], dim=0)

(1)是在新创建的维度上进行拼接,如果维度小于现存的维度,则创建该维度后,后面的递推。比如t现在维度是2*3,拼接后,则是3*2*3,其中后两维的2*3是原来的t。

3 .2 张量切分

torch.chunk(input,chunk,dim)

a = torch.ones((2, 7)) # 7

list_of_tensors = torch.chunk(a, dim=1, chunks=3) # 3

for idx, t in enumerate(list_of_tensors):

(1)在维度dim上进行chunk均分,如果不能整除,最后一份为余数。

torch.split(input,int/list,dim)

t = torch.ones((2, 5))

list_of_tensors = torch.split(t, [2, 1, 1], dim=1) # [2 , 1, 2]

for idx, t in enumerate(list_of_tensors):

list_of_tensors = torch.split(t, [2, 1, 2], dim=1)

for idx, t in enumerate(list_of_tensors):

(1)为int时,和chunk功能类似;

(2)为list时,可以按照设定值切分,但总和要与input维度上值一致。

3 .3 张量索引

torch.index_select(input,dim,select)

t = torch.randint(0, 9, size=(3, 3))

idx = torch.tensor([0, 2], dtype=torch.long) # float

t_select = torch.index_select(t, dim=0, index=idx)

(1)在dim维度上按照select索引数值。

torch.maksed_select(input,mask)

t = torch.randint(0, 9, size=(3, 3))

mask = t.le(5) # ge is mean greater than or equal/ gt: greater than le lt

t_select = torch.masked_select(t, mask)

  1. mask是和input同大小的布尔类型张量,按照TRUE返回一维张量。

3 .4 张量变形

torch.reshape(input,shape)

t = torch.randperm(8)

t_reshape = torch.reshape(t, (-1, 2, 2)) # -1

t[0] = 1024

3 .5 张量维度交换

torch.transpose(input,dim1,dim2)

torch.transpose

t = torch.rand((2, 3, 4))

t_transpose = torch.transpose(t, dim0=1, dim1=2) # c*h*w h*w*c

(1)维度变换之后,数据是如何变化的?

(2)torch.t()二维张量(矩阵)转置

3 .6 张量压缩

torch.sequeeze(input,dim)

t = torch.rand((1, 2, 3, 1))

t_sq = torch.squeeze(t)

t_0 = torch.squeeze(t, dim=0)

t_1 = torch.squeeze(t, dim=1)

(1)默认压缩所有为1的维度,也可以指定维度,若指定维度不为1,则不会压缩。

(2)torch.unsequeeze(),扩展维度的值。

4 . 张量的数学运算

t_0 = torch.randn((3, 3))

t_1 = torch.ones_like(t_0)

t_add = torch.add(t_0, 10, t_1)

  1. torch.add可同时执行乘法运算。

5 . 计算图

计算图是用来描述运算的有向无环图,包括结点(node)和边(edge)。结点表示数据,如向量,矩阵,张量等,边表示运算,如加减乘除卷积等。

import torch

w = torch.tensor([1.], requires_grad=True)

x = torch.tensor([2.], requires_grad=True)

a = torch.add(w, x) # retain_grad()

b = torch.add(w, 1)

y = torch.mul(a, b)

y.backward()

print(w.grad)

查看叶子结点

print("is_leaf:\n", w.is_leaf, x.is_leaf, a.is_leaf, b.is_leaf, y.is_leaf)

查看梯度

print("gradient:\n", w.grad, x.grad, a.grad, b.grad, y.grad)

查看 grad_fn

print("grad_fn:\n", w.grad_fn, x.grad_fn, a.grad_fn, b.grad_fn, y.grad_fn)

6. 自动求导

torch.autograd.backward(tensors,retain_graph)

w = torch.tensor([1.], requires_grad=True)

x = torch.tensor([2.], requires_grad=True)

a = torch.add(w, x)

b = torch.add(w, 1)

y = torch.mul(a, b)

y.backward(retain_graph=True)

print(w.grad)

y.backward()

w = torch.tensor([1.], requires_grad=True)

x = torch.tensor([2.], requires_grad=True)

a = torch.add(w, x) # retain_grad()

b = torch.add(w, 1)

y0 = torch.mul(a, b) # y0 = (x+w) * (w+1)

y1 = torch.add(a, b) # y1 = (x+w) + (w+1) dy1/dw = 2

loss = torch.cat([y0, y1], dim=0) # [y0, y1]

grad_tensors = torch.tensor([1., 2.])

loss.backward(gradient=grad_tensors) # gradient 传入 torch.autograd.backward()中的grad_tensors

torch.autograd.grad(outputs,inputs,retain_graph)

x = torch.tensor([3.], requires_grad=True)

y = torch.pow(x, 2) # y = x**2

grad_1 = torch.autograd.grad(y, x, create_graph=True) # grad_1 = dy/dx = 2x = 2 * 3 = 6

grad_2 = torch.autograd.grad(grad_1[0], x) # grad_2 = d(dy/dx)/dx = d(2x)/dx = 2

  1. autograd.grad()会返回梯度张量供保存。

二 数据处理

一般来说PyTorch中深度学习训练的流程是这样的:

  1. 创建Dateset

  2. Dataset传递给DataLoader

  3. DataLoader迭代产生训练数据提供给模型

对应的一般都会有这三部分代码

创建Dateset(可以自定义)

dataset = face_dataset # Dataset部分自定义过的face_dataset

Dataset传递给DataLoader

dataloader = torch.utils.data.DataLoader(dataset,batch_size=64,shuffle=False,num_workers=8)

DataLoader迭代产生训练数据提供给模型

for i in range(epoch):

for index,(img,label) in enumerate(dataloader):

pass

到这里应该就PyTorch的数据集和数据传递机制应该就比较清晰明了了。Dataset负责建立索引到样本的映射,DataLoader负责以特定的方式从数据集中迭代的产生一个个batch的样本集合。在enumerate过程中实际上是dataloader按照其参数sampler规定的策略调用了其dataset的getitem方法。其中,还会涉及数据的变化形式。

1 . 数据收集

找数据集,注意数据集格式.

Dataset是DataLoader实例化的一个参数。

CIFAR10是CV训练中经常使用到的一个数据集,在PyTorch中CIFAR10是一个写好的Dataset,我们使用时只需以下代码:

data = datasets.CIFAR10("./data/", transform=transform, train=True, download=True)

datasets.CIFAR10就是一个Datasets子类,data是这个类的一个实例。

用自己在一个文件夹中的数据作为数据集时可以使用ImageFolder这个方便的API。

FaceDataset = datasets.ImageFolder('./data', transform=img_transform)

如何自定义一个数据集

torch.utils.data.Dataset 是一个表示数据集的抽象类。任何自定义的数据集都需要继承这个类并覆写相关方法。

所谓数据集,其实就是一个负责处理索引(index)到样本(sample)映射的一个类(class)。

Pytorch提供两种数据集: Map式数据集Iterable式数据集

Map式数据集

一个Map式的数据集必须要重写getitem (self, index),len(self) 两个内建方法,用来表示从索引到样本的映射(Map).

这样一个数据集dataset,举个例子,当使用dataset[idx]命令时,可以在你的硬盘中读取你的数据集中第idx张图片以及其标签(如果有的话);len(dataset)则会返回这个数据集的容量。

自定义类大致是这样的:

class CustomDataset(data.Dataset):#需要继承data.Dataset

def init(self):

TODO

1. Initialize file path or list of file names.

pass

def getitem(self, index):

TODO

1. Read one data from file (e.g. using numpy.fromfile, PIL.Image.open).

2. Preprocess the data (e.g. torchvision.Transform).

3. Return a data pair (e.g. image and label).

#这里需要注意的是,第一步:read one data,是一个data

pass

def len(self):

You should change 0 to the total size of your dataset.

return 0

例子-1: 自己实验中写的一个例子:这里我们的图片文件储存在"./data/faces/"文件夹下,图片的名字并不是从1开始,而是从final_train_tag_dict.txt这个文件保存的字典中读取,label信息也是用这个文件中读取。大家可以照着上面的注释阅读这段代码。

from torch.utils import data

import numpy as np

from PIL import Image

class face_dataset(data.Dataset):

def init(self):

self.file_path = './data/faces/'

f=open("final_train_tag_dict.txt","r")

self.label_dict=eval(f.read()) # eval除了 计算 ,还 可以 将str转为dict

f.close()

def getitem(self,index):

label = list(self.label_dict.values())[index-1]

img_id = list(self.label_dict.keys())[index-1]

img_path = self.file_path+str(img_id)+".jpg"

img = np.array(Image.open(img_path))

return img,label

def len(self):

return len(self.label_dict)

Iterable式数据集

一个Iterable(迭代)式数据集是抽象类data.IterableDataset 的子类,并且覆写了iter方法成为一个迭代器。这种数据集主要用于数据大小未知,或者以流的形式的输入,本地文件不固定的情况,需要以迭代的方式来获取样本索引。

2 . 数据划分

数据划分主要是路径的处理。

def makedir(new_dir):

if not os.path.exists(new_dir):

os.makedirs(new_dir)

检测路径是否存在,若不存在,则创建此路径。

dataset_dir = os.path.join("..", "..", "data", "RMB_data")

设置路径,将它们组合在一起。相对于Python文件所在位置的相对路径。

for root, dirs, files in os.walk(dataset_dir):

for sub_dir in dirs:

imgs = os.listdir(os.path.join(root, sub_dir))

imgs = list(filter(lambda x: x.endswith('.jpg'), imgs))

random.shuffle(imgs)

img_count = len(imgs)

train_point = int(img_count * train_pct)

valid_point = int(img_count * (train_pct + valid_pct))

for i in range(img_count):

if i < train_point:

out_dir = os.path.join(train_dir, sub_dir)

elif i < valid_point:

out_dir = os.path.join(valid_dir, sub_dir)

else:

out_dir = os.path.join(test_dir, sub_dir)

makedir(out_dir)

target_path = os.path.join(out_dir, imgs[i])

src_path = os.path.join(dataset_dir, sub_dir, imgs[i])

shutil.copy(src_path, target_path)

os.walk

每一层遍历:

root保存的就是当前遍历的文件夹的绝对路径;

dirs保存当前文件夹下的所有子文件夹的名称(仅一层,孙子文件夹不包括)

files保存当前文件夹下的所有文件的名称

其次,发现它的遍历文件方式,在图的遍历方式中,那可不就是深度遍历嘛!!

  1. os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字的列表。

******shutil.copy()******Python中的方法用于将源文件的内容复制到目标文件或目录。它还会保留文件的权限模式,但不会保留文件的其他元数据(例如文件的创建和修改时间)。源必须代表文件,但目标可以是文件或目录。如果目标是目录,则文件将使用源中的基本文件名复制到目标中。另外,目的地必须是可写的。如果目标是文件并且已经存在,则将其替换为源文件,否则将创建一个新文件。

3 . 图像预处理-transforms

3.1 图像标准化

transforms.Normalize(mean,std,inplace)

逐通道的标准化,每个通道先求出平均值和标准差,然后标准化。Inplace表示是否原地操作。

3 .2 图像裁剪

train_transform = transforms.Compose([

transforms.Resize((32, 32)),

transforms.RandomCrop(32, padding=4),

transforms.ToTensor(),

transforms.Normalize(norm_mean, norm_std),

])

(1)transforms.CenterCrop(size)

从图片中心截取size大小的图片。

(2)transforms.RandomCrop(size,padding,padding_mode)

随机裁剪区域。

(3)transforms.RandomResizedCrop(size,scale,ratio)

随机大小,随机长宽比的裁剪。

3 .3 图像旋转

(1)transforms.RandomHorizationalFlip(p)

依据概率p水平翻转。

(2)transforms.RandomVerticalFlip(p)

依据概率p垂直翻转。

(3)transforms.RandomRotation(degrees,resample,expand)

transforms方法

Transforms Methods

一、裁剪

1. transforms.CenterCrop

2. transforms.RandomCrop

3. transforms.RandomResizedCrop

4. transforms.FiveCrop

5. transforms.TenCrop

二、翻转和旋转

1. transforms.RandomHorizontalFlip

2. transforms.RandomVerticalFlip

3. transforms.RandomRotation

三、图像变换

• 1. transforms.Pad

• 2. transforms.ColorJitter

• 3. transforms.Grayscale

• 4. transforms.RandomGrayscale

• 5. transforms.RandomAffine

• 6. transforms.LinearTransformation

• 7. transforms.RandomErasing

• 8. transforms.Lambda

• 9. transforms.Resize

• 10. transforms.Totensor

• 11. transforms.Normalize

四、transforms的操作

• 1. transforms.RandomChoice

• 2. transforms.RandomApply

• 3. transforms.RandomOrder

train_transform = transforms.Compose([

transforms.Resize((224, 224)),

1 CenterCrop

transforms.CenterCrop(512), # 512

2 RandomCrop

transforms.RandomCrop(224, padding=16),

transforms.RandomCrop(224, padding=(16, 64)),

transforms.RandomCrop(224, padding=16, fill=(255, 0, 0)),

transforms.RandomCrop(512, pad_if_needed=True), # pad_if_needed=True

transforms.RandomCrop(224, padding=64, padding_mode='edge'),

transforms.RandomCrop(224, padding=64, padding_mode='reflect'),

transforms.RandomCrop(1024, padding=1024, padding_mode='symmetric'),

3 RandomResizedCrop

transforms.RandomResizedCrop(size=224, scale=(0.5, 0.5)),

4 FiveCrop

transforms.FiveCrop(112),

transforms.Lambda(lambda crops: torch.stack([(transforms.ToTensor()(crop)) for crop in crops])),

5 TenCrop

transforms.TenCrop(112, vertical_flip=False),

transforms.Lambda(lambda crops: torch.stack([(transforms.ToTensor()(crop)) for crop in crops])),

1 Horizontal Flip

transforms.RandomHorizontalFlip(p=1),

2 Vertical Flip

transforms.RandomVerticalFlip(p=0.5),

3 RandomRotation

transforms.RandomRotation(90),

transforms.RandomRotation((90), expand=True),

transforms.RandomRotation(30, center=(0, 0)),

transforms.RandomRotation(30, center=(0, 0), expand=True), # expand only for center rotation

transforms.ToTensor(),

transforms.Normalize(norm_mean, norm_std),

])

4 . 数据加载- D ata Loader

三 模型创建与n n.Module

3 .1 n n.Module

模型构建两要素:

  1. 构建子模块------init()
  2. 拼接子模块------forward()

一个module可以有多个module;

一个module相当于一个运算,都必须实现forward函数;

每一个module有8个字典管理属性。

self._parameters = OrderedDict()

self._buffers = OrderedDict()

self._backward_hooks = OrderedDict()

self._forward_hooks = OrderedDict()

self._forward_pre_hooks = OrderedDict()

self._state_dict_hooks = OrderedDict()

self._load_state_dict_pre_hooks = OrderedDict()

self._modules = OrderedDict()

3 .2 网络容器

nn .Sequential()

是nn.Module()的一个容器,用于按照顺序包装一组网络层;

顺序性:网络层之间严格按照顺序构建;

自带forward():

各网络层之间严格按顺序执行,常用于block构建

class LeNetSequential(nn.Module):

def init(self, classes):

super(LeNetSequential, self).init()

self.features = nn.Sequential(

nn.Conv2d(3, 6, 5),

nn.ReLU(),

nn.MaxPool2d(kernel_size=2, stride=2),

nn.Conv2d(6, 16, 5),

nn.ReLU(),

nn.MaxPool2d(kernel_size=2, stride=2),)

self.classifier = nn.Sequential(

nn.Linear(16*5*5, 120),

nn.ReLU(),

nn.Linear(120, 84),

nn.ReLU(),

nn.Linear(84, classes),)

def forward(self, x):

x = self.features(x)

x = x.view(x.size()[0], -1)

x = self.classifier(x)

return x

nn .ModuleList()

是nn.Module的容器,用于包装网络层,以迭代方式调用网络层。

主要方法:

append():在ModuleList后面添加网络层;

extend():拼接两个ModuleList.

Insert():指定在ModuleList中插入网络层。

nn.ModuleList:迭代性,常用于大量重复网 构建,通过for循环实现重复构建

class ModuleList(nn.Module):

def init(self):

super(ModuleList, self).init()

self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(20)])

def forward(self, x):

for i, linear in enumerate(self.linears):

x = linear(x)

return x

n n.ModuleDict()

以索引方式调用网络层

主要方法:

• clear():清空ModuleDict

• items():返回可迭代的键值对(key-value pairs)

• keys():返回字典的键(key)

• values():返回字典的值(value)

• pop():返回一对键值,并从字典中删除

n.ModuleDict:索引性,常用于可选择的网络层

class ModuleDict(nn.Module):

def init(self):

super(ModuleDict, self).init()

self.choices = nn.ModuleDict({

'conv': nn.Conv2d(10, 10, 3),

'pool': nn.MaxPool2d(3)

})

self.activations = nn.ModuleDict({

'relu': nn.ReLU(),

'prelu': nn.PReLU()

})

def forward(self, x, choice, act):

x = self.choices[choice](x)

x = self.activations[act](x)

return x

3 .3 卷积层

nn .ConV2d()

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

in_channels:输入通道数,比如RGB图像是3,而后续的网络层的输入通道数为前一卷积层的输出通道数;

out_channels:输出通道数,等价于卷积核个数

kernel_size:卷积核尺寸

stride:步

padding:填充个数

dilation:空洞卷积大小

groups:分组卷积设置

bias:偏置

conv_layer = nn.Conv2d(3, 1, 3) # input:(i, o, size) weights:(o, i , h, w)

nn.init.xavier_normal_(conv_layer.weight.data)

calculation

img_conv = conv_layer(img_tensor)

这里使用 input*channel 为 3,output_channel 为 1 ,卷积核大小为 3×3 的卷积核nn.Conv2d(3, 1, 3),使用nn.init.xavier_normal*()方法初始化网络的权值。

我们通过`conv_layer.weight.shape`查看卷积核的 shape 是`(1, 3, 3, 3)`,对应是`(output_channel, input_channel, kernel_size, kernel_size)`。所以第一个维度对应的是卷积核的个数,每个卷积核都是`(3,3,3)`。虽然每个卷积核都是 3 维的,执行的却是 2 维卷积。

转置卷积 nn.ConvTranspose2d

转置卷积又称为反卷积(Deconvolution)和部分跨越卷积(Fractionally-stridedConvolution) ,用于对图像进行上采样(UpSample)

为什么称为转置卷积?

假设图像尺寸为4*4,卷积核为3*3,padding=0,stride=1

正常卷积:

转置卷积:

假设图像尺寸为2*2,卷积核为3*3,padding=0,stride=1

nn.ConvTranspose2d(in_channels, out_channels,

kernel_size,

stride=1,

padding=0,

output_padding=0,

groups=1,

bias=True,

dilation=1, padding_mode='zeros')

输出尺寸计算:

flag = 1

flag = 0

if flag:

conv_layer = nn.ConvTranspose2d(3, 1, 3, stride=2) # input:(i, o, size)

nn.init.xavier_normal_(conv_layer.weight.data)

calculation

img_conv = conv_layer(img_tensor)

print("卷积前尺寸:{}\n卷积后尺寸:{}".format(img_tensor.shape, img_conv.shape))

img_conv = transform_invert(img_conv[0, 0:1, ...], img_transform)

img_raw = transform_invert(img_tensor.squeeze(), img_transform)

plt.subplot(122).imshow(img_conv, cmap='gray')

plt.subplot(121).imshow(img_raw)

plt.show()

3 .4 池化层 nn.MaxPool2d && nn.AvgPool2d

池化运算:对信号进行 "收集"并 "总结",类似水池收集水资源,因而

得名池化层

"收集":多变少

"总结":最大值/平均值

nn.MaxPool2d

nn.MaxPool2d(kernel_size, stride=None,

padding=0, dilation=1,

return_indices=False,

ceil_mode=False)

主要参数:

• kernel_size:池化核尺寸

• stride:步长

• padding :填充个数

• dilation:池化核间隔大小

• ceil_mode:尺寸向上取整

• return_indices:记录池化像素索引

flag = 1

flag = 0

if flag:

maxpool_layer = nn.MaxPool2d((2, 2), stride=(2, 2)) # input:(i, o, size) weights:(o, i , h, w)

img_pool = maxpool_layer(img_tensor)

nn.AvgPool2d

nn.AvgPool2d(kernel_size,

stride=None,

padding=0,

ceil_mode=False,

count_include_pad=True,

divisor_override=None)

主要参数:

• kernel_size:池化核尺寸

• stride:步长

• padding :填充个数

• ceil_mode:尺寸向上取整

• count_include_pad:填充值用于计算

• divisor_override :除法因子

avgpoollayer = nn.AvgPool2d((2, 2), stride=(2, 2)) # input:(i, o, size) weights:(o, i , h, w)

img_pool = avgpoollayer(img_tensor)

img_tensor = torch.ones((1, 1, 4, 4))

avgpool_layer = nn.AvgPool2d((2, 2), stride=(2, 2), divisor_override=3)

img_pool = avgpool_layer(img_tensor)

print("raw_img:\n{}\npooling_img:\n{}".format(img_tensor, img_pool))

nn.MaxUnpool2d

功能:对二维信号(图像)进行最大值池化

上采样

主要参数:

• kernel_size:池化核尺寸

• stride:步长

• padding :填充个数

pooling

img_tensor = torch.randint(high=5, size=(1, 1, 4, 4), dtype=torch.float)

maxpool_layer = nn.MaxPool2d((2, 2), stride=(2, 2), return_indices=True)

img_pool, indices = maxpool_layer(img_tensor)

unpooling

img_reconstruct = torch.randn_like(img_pool, dtype=torch.float)

maxunpool_layer = nn.MaxUnpool2d((2, 2), stride=(2, 2))

img_unpool = maxunpool_layer(img_reconstruct, indices)

print("raw_img:\n{}\nimg_pool:\n{}".format(img_tensor, img_pool))

print("img_reconstruct:\n{}\nimg_unpool:\n{}".format(img_reconstruct, img_unpool))

3 .5线性层

nn.Linear(in_features, out_features, bias=True)

功能:对一维信号(向量)进行线性组合

主要参数:

• in_features:输入结点数

• out_features:输出结点数

• bias :是否需要偏置

计算公式:y = ������ + ������s

inputs = torch.tensor([[1., 2, 3]])

linear_layer = nn.Linear(3, 4)

linear_layer.weight.data = torch.tensor([[1., 1., 1.],

[2., 2., 2.],

[3., 3., 3.],

[4., 4., 4.]])

linear_layer.bias.data.fill_(0.5)

output = linear_layer(inputs)

print(inputs, inputs.shape)

print(linear_layer.weight.data, linear_layer.weight.data.shape)

print(output, output.shape)

3 .6 激活函数层

nn.Sigmoid

nn.tanh:

nn.ReLU

nn.LeakyReLU

negative_slope: 负半轴斜率

nn.PReLU

init: 可学习斜率

nn.RReLU

lower: 均匀分布下限

upper:均匀分布上限

四.损失函数和优化器

4 .1 均值初始化

为减轻梯度消失和梯度爆炸,选择合适的权重初值。

十种初始化方法

Initialization Methods

  1. Xavie r均匀分布

  2. Xavie r正态分布

  3. Kaiming正态分布

  4. 均匀分布

  5. 正态分布

  6. 常数分布

  7. 正交矩阵初始化

  8. 单位矩阵初始化

  9. 稀疏矩阵初始化

4 .2 损失函数

1、nn.CrossEntropyLoss

nn.CrossEntropyLoss(weight=None,

size_average=None,

ignore_index=-100,

reduce=None,

reduction='mean'')

功能: nn.LogSoftmax ()与nn.NLLLoss ()结合,进行

交叉熵计算

主要参数:

• w eigh t:各类别的loss设置权值

ignore _ind e x:忽略某个类别

redu c tion :计算模式,可为none/sum /m e an

none- 逐个元素计算

sum- 所有元素求和,返回标量

2、 nn.NLLLoss

功能:实现负对数似然函数中的负号功能

主要参数:

• weight:各类别的loss设置权值

• ignore_index:忽略某个类别

•reduction:计算模式,可为none/sum /m e an

none-逐个元素计算

nn.NLLLoss(weight=None,

size_average=None,

ignore_index=-100,

reduce=None,

reduction='mean')sum-所有元素求和,返回标量

m

e an-加权平均,返回标量

3、 nn.BCELoss

nn.BCELoss(weight=None,

size_average=None,

reduce=None,

reduction='mean')

功能:二分类交叉熵

注意事项:输入值取值在[0,1]

主要参数:

• weight:各类别的loss设置权值

• ignore_index:忽略某个类别

• reduction:计算模式,可为none/sum /m e an

none-逐个元素计算

4、 nn.BCEWithLogitsLoss

nn.BCEWithLogitsLoss(weight=None,

size_average=None,

reduce=None, reduction='mean',

pos_weight=None)

功能:结合Sigmoid与二分类交叉熵

注意事项:网络最后不加sigmoid函数

主要参数:

• pos _weight :正样本的权值

• weight:各类别的loss设置权值

•ignore_index:忽略某个类别

•reduction :计算模式,可为none/sum /mean

mean-加权平均,返回标量e aum

  1. nn.L1Loss

  2. nn.MSELoss

  3. nn.SmoothL1Loss

  4. nn.PoissonNLLLoss

  5. nn.KLDivLoss

  6. nn.MarginRankingLoss

  7. nn.MultiLabelMarginLoss

  8. nn.SoftMarginLoss

  9. nn.MultiLabelSoftMarginLoss

  10. nn.MultiMarginLoss

  11. nn.TripletMarginLoss

  12. nn.HingeEmbeddingLoss

  13. nn.CosineEmbeddingLoss

  14. nn.CTCLoss -所有元素求和,返回标量

4.3优化器 Optimizer

pytorch的优化器:管理并更新模型中可学习参数的值,使得模型输出更接近真实标签

导数:函数在指定坐标轴上的变化率

方向导数:指定方向上的变化率

梯度:一个向量,方向为方向导数取得最大值的方向

class Optimizer(object):

def init(self, params, defaults):

self.defaults = defaults

self.state = defaultdict(dict)

self.param_groups = []

param_groups = [{'params':

param_groups}]本属性

• defaults:优化器超参数

• state:参数的缓存,如mom en tum的缓存

• params_groups:管理的参数组

• _step_count:记录更新次数,学习率调整中使用

基本方法

• 1.zero_grad():清空所管理参数的梯度

pytorch特性:张量梯度不自动清零

class Optimizer(object):

def zero_grad(self):

for group in self.param_groups:

for p in group['params']:

if p.grad is not None:

p.grad.detach_()

p.grad.zero_()

  1. step():执行一步更新

  2. add_param_group():添加参数组

class Optimizer(object):

def add_param_group(self, param_group):

for group in self.param_groups:

param_set.update(set(group['params']))

self.param_groups.append(param_group)

4.state_dict():获取优化器当前状态信息字典

• 5.load_state_dict() :加载状态信息字典

class Optimizer(object):

def state_dict(self):

return {

'state': packed_state,

'param_groups': param_groups,

}

def load_state_dict(self, state_dict):

学习率

Learning Rate

梯度下降:

����+�� = ���� − ��(���� )

����+�� = ���� − LR * ��(����)

学习率(learning rate)控制更新的步伐

Momentum (动量,冲量):

结合当前梯度与上一次更新信息,用于当前更新

梯度下降:

����+�� = ���� − ���� ∗ ��(���� )

pytorch中更新公式:

���� = �� ∗ ����−�� + ��(���� )

����+�� = ���� − ���� ∗ ����

�������� = �� ∗ ������ + ��(��������)

= ��(��������) + �� ∗ (�� ∗ ������ + ��(������))

= ��(��������) + �� ∗ ��(������) + ���� ∗ ������

= ��(��������) + �� ∗ ��(������) + ���� ∗ ��(������) + ���� ∗ ������

1 .optim.SGD

主要参数:

• params:管理的参数组

• lr:初始学习率

• momentum:动量系数,贝塔

• weight_decay:L2正则化系数

• nesterov:是否采用NAG

optim.SGD(params, lr=<object object>,

momentum=0, dampening=0,

weight_decay=0, nesterov=False)

优化器

Optimizer

  1. optim.SGD:随机梯度下降法

  2. optim.Adagrad:自适应学习率梯度下降法

  3. optim.RMSprop: Adagrad的改进

  4. optim.Adadelta: Adagrad的改进

  5. optim.Adam:RMSprop结合Momentum

  6. optim.Adamax:Adam增加学习率上限

  7. optim.SparseAdam:稀疏版的Adam

  8. optim.ASGD:随机平均梯度下降

  9. optim.Rprop:弹性反向传播

  10. optim.LBFGS:BFGS的改进

小总结------完整的神经网络模型训练过程

1 . 导入常用的库和函数

import os

import random

import numpy as np

import torch

import torch.nn as nn

from torch.utils.data import DataLoader

import torchvision.transforms as transforms

import torch.optim as optim

from matplotlib import pyplot as plt

from model.lenet import LeNet

from tools.my_dataset import RMBDataset

2. 定义超参数(包括标签)

def set_seed(seed=1):

random.seed(seed)

np.random.seed(seed)

torch.manual_seed(seed)

torch.cuda.manual_seed(seed)

set_seed() # 设置随机种子

rmb_label = {"1": 0, "100": 1}

参数设置

MAX_EPOCH = 10

BATCH_SIZE = 16

LR = 0.01

log_interval = 10

val_interval = 1

3 . 数据处理

3 .1 数据切分-找到或创建数据的位置

split_dir = os.path.join("..", "..", "data", "rmb_split")

train_dir = os.path.join(split_dir, "train")

valid_dir = os.path.join(split_dir, "valid")

3.2 定义数据的变换形式------transforms

norm_mean = [0.485, 0.456, 0.406]

norm_std = [0.229, 0.224, 0.225]

train_transform = transforms.Compose([

transforms.Resize((32, 32)),

transforms.RandomCrop(32, padding=4),

transforms.ToTensor(),

transforms.Normalize(norm_mean, norm_std),

])

valid_transform = transforms.Compose([

transforms.Resize((32, 32)),

transforms.ToTensor(),

transforms.Normalize(norm_mean, norm_std),

])

3 .3 构建真实数据(利用X -D atase t 类)

将数据路径和transforms变换形式作为参数传到X-Dataset类里,得到数据实例

train_data = RMBDataset(data_dir=train_dir, transform=train_transform)

这里的RMBDataset是一个类,主要做两件事,一是根据数据路径便利获取数据信息,包括每张图片的路径和标签。

@staticmethod

def get_img_info(data_dir):

data_info = list()

for root, dirs, _ in os.walk(data_dir):

遍历类别

for sub_dir in dirs:

img_names = os.listdir(os.path.join(root, sub_dir))

img_names = list(filter(lambda x: x.endswith('.jpg'), img_names))

遍历图片

for i in range(len(img_names)):

img_name = img_names[i]

path_img = os.path.join(root, sub_dir, img_name)

label = rmb_label[sub_dir]

data_info.append((path_img, int(label)))

return data_info

二是对获取的图片调用函数transforms进行变换处理

def getitem(self, index):

path_img, label = self.data_info[index]

img = Image.open(path_img).convert('RGB') # 0~255

if self.transform is not None:

img = self.transform(img) # 在这里做transform,转为tensor等等

3 .4 构建Data Loader (后续训练遍历图片)

将变换得到的真实数据作为参数传到DataLoader函数里,并设置超参数batch_size等。

train_loader = DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)

4 . 构建模型

主要包括两步,一是实例化网络模型

net = LeNet(classes=2)

二是权重初始化

net.initialize_weights()

5 . 设置损失函数

criterion = nn.CrossEntropyLoss()

6 . 设置优化器

optimizer = optim.SGD(net.parameters(), lr=LR, momentum=0.9) # 选择优化器

scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1) # 设置学习率下降策略

7 . 训练

7 .1 定义训练曲线存储list

train_curve = list()

valid_curve = list()

7 .2 训练过程

1.大循环,在epoch里

for epoch in range(MAX_EPOCH):

#2.定义收集训练结果的参数

loss_mean = 0.

correct = 0.

total = 0.

#3.开始训练

net.train()

#4.遍历数据,其中参数就是DataLoader的实例化参数

for i, data in enumerate(train_loader):

#5.前向传播,从数据中选择输入和标签,将输入传到net里

forward

inputs, labels = data

outputs = net(inputs)

#5.后向传播,优化器梯度置0,计算损失函数(输出与标签作为参数),然后损失函数后向传播计算梯度

backward

optimizer.zero_grad()

loss = criterion(outputs, labels)

loss.backward()

#6.更新梯度

update weights

optimizer.step()

#7.统计输出结果的准确性

统计分类情况

_, predicted = torch.max(outputs.data, 1)

total += labels.size(0)

correct += (predicted == labels).squeeze().sum().numpy()

8.打印训练信息

loss_mean += loss.item()

train_curve.append(loss.item())

if (i+1) % log_interval == 0:

loss_mean = loss_mean / log_interval

print("Training:Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} Acc:{:.2%}".format(

epoch, MAX_EPOCH, i+1, len(train_loader), loss_mean, correct / total))

loss_mean = 0.

#9.更新学习率

scheduler.step() # 更新学习率

#10.验证模型,和获取训练结果一样的步骤,只是图片路径在valid里

validate the model

if (epoch+1) % val_interval == 0:

correct_val = 0.

total_val = 0.

loss_val = 0.

net.eval()

with torch.no_grad():

for j, data in enumerate(valid_loader):

inputs, labels = data

outputs = net(inputs)

loss = criterion(outputs, labels)

_, predicted = torch.max(outputs.data, 1)

total_val += labels.size(0)

correct_val += (predicted == labels).squeeze().sum().numpy()

loss_val += loss.item()

loss_val_epoch = loss_val / len(valid_loader)

valid_curve.append(loss_val_epoch)

valid_curve.append(loss.item()) # 20191022改,记录整个epoch样本的loss,注意要取平均

print("Valid:\t Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} Acc:{:.2%}".format(

epoch, MAX_EPOCH, j+1, len(valid_loader), loss_val_epoch, correct_val / total_val))

7 .3 训练过程可视化

train_x = range(len(train_curve))

train_y = train_curve

train_iters = len(train_loader)

valid_x = np.arange(1, len(valid_curve)+1) * train_iters*val_interval # 由于valid中记录的是epochloss,需要对记录点进行转换到iterations

valid_y = valid_curve

plt.plot(train_x, train_y, label='Train')

plt.plot(valid_x, valid_y, label='Valid')

plt.legend(loc='upper right')

plt.ylabel('loss value')

plt.xlabel('Iteration')

plt.show()

8 . 推理(简单版)

直接利用前面训练的网络模型,将test图片输入得到输出结果即可。

BASE_DIR = os.path.dirname(os.path.abspath(file))

test_dir = os.path.join(BASE_DIR, "test_data")

test_data = RMBDataset(data_dir=test_dir, transform=valid_transform)

valid_loader = DataLoader(dataset=test_data, batch_size=1)

for i, data in enumerate(valid_loader):

forward

inputs, labels = data

outputs = net(inputs)

_, predicted = torch.max(outputs.data, 1)

rmb = 1 if predicted.numpy()[0] == 0 else 100

print("模型获得{}元".format(rmb))

参考资料

深度之眼学习课程

相关推荐
虾球xz几秒前
游戏引擎学习第51天
学习·游戏引擎
iOS开发-上海6 分钟前
HarmonyOS学习 --- Mac电脑获取手机UDID
学习·harmony os
跃渊Yuey8 分钟前
【C++笔记】AVL树
c++·笔记
凌鲨11 分钟前
IOS学习路线图
学习·ios·cocoa
dubochao_xinxi15 分钟前
E: 仓库目录 /var/cache/apt/archives/partial 确实。 - Acquire (2: 没有那个文件或目录)
开发语言·数据库·python·qt
液态不合群17 分钟前
使用Python实现两组数据纵向排序
开发语言·python·算法
hu_wenjie22 分钟前
使用pyinstaller打包flask项目
后端·python·flask
虾球xz1 小时前
游戏引擎学习第52天
学习·游戏引擎
CM莫问1 小时前
python实战(十一)——情感分析
人工智能
chenchihwen1 小时前
TimesFM(Time Series Foundation Model)时间序列预测的数据研究(3)
人工智能·python