资讯详情

pytorch 从放弃到入门(二)

深入学习相关知识:

基本配置

导入包:

import os import numpy as np import torch import torch.nn as nn from torch.utils.data import Dataset, DataLoader import torch.optim as optimizer

很有可能使用涉及表格信息的读入pandas,对于不同的项目可能还需要导入一些更上层的包如cv2.如果涉及可视化,也会使用。matplotlib、seaborn等等。也常用于下游分析和指标计算sklearn。

可统一设置超参数,便于后续调试修改:

batch_size = 16 lr = 1e-4 max_epochs = 100

GPU有两种常见的设置方法:

# 方案一:使用os.environ,如果使用这种情况GPU不需要设置 os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'  # 方案二:使用device”,后续使用GPU的变量用.to(device)即可 device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")

数据读入:

简单描述流程图,首先是for循环使用DataLoader,进入DataLoader之后是否采用多进程进入DataLoaderlter,进入DataLoaderIter之后会使用sampler去获取Index,拿到索引后传输DatasetFetcher,在DatasetFetcher中会调用Dataset,Dataset根据给定的Index,在getitem从硬盘中读取实际内容Img和Label,读取了一个batch_size在数据之后,通过一个数据collate_fn整理数据,整理成batch_Data然后可以输入模型进行训练。

pytorch - 在数据读取机制中Dataloader与Dataset - 全栈程序员必看

class MyDataset(Dataset):     def __init__(self, data_dir, info_csv, image_list, transform=None):         """         Args:             data_dir: path to image directory.             info_csv: path to the csv file containing image indexes                 with corresponding labels.             image_list: path to the txt file contains image names to training/validation set             transform: optional transform to be applied on a sample.         """         label_info = pd.read_csv(info_csv)         image_file = open(image_list).readlines()         self.data_dir = data_dir         self.image_file = image_file         self.label_info = label_info         self.transform = transform      def __getitem__(self, index):         """         Args:             index: the index of item         Returns:             image and its labels         """         image_name = self.image_file[index].strip('\n') #按行读取图片,去掉每行换行符"\n"         raw_label = self.label_info.loc[self.label_info['Image_index'] == image_name]         #显示索引:.loc,第一个参数是 index切片,第二个为 columns列名          #https://blog.csdn.net/zz5957/article/details/118549238         label = raw_label.iloc[:,0]          #.iloc[:,0]隐藏索引:.iloc(integer_location),只能传入整数         image_name = os.path.join(self.data_dir, image_name)         #path.join拼接规则 https://www.jianshu.com/p/3090f7875f9b         image = Image.open(image_name).convert('RGB')         if self.transform is not None:          #https://blog.csdn.net/weixin_40123108/article/details/85099449             image = self.transform(image)         return image, label#返回的单个样本      def __len__(self):         return len(self.image_file)   #构建好Dataset之后,就可以用了DataLoader按批次读入数据,实现代码如下:  from torch.utils.data import DataLoader  train_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, num_workers=4, shuffle=True, drop_last=True) val_loader = torch.utils.data.DataLoader(val_data, batch_size=batch_size, num_workers=4, shuffle=False)
  • batch_size:样本按批读入,batch_size是每次读入的样本数

  • num_workers:读取数据有多少个过程?

  • shuffle:读入的数据是否被打乱?

  • drop_last:样本的最后一部分没有达到批次数,因此不再参加培训

PyTorch中的DataLoader可以使用读取next和iter来完成:

import matplotlib.pyplot as plt images, labels = next(iter(val_loader)) print(images.shape) plt.imshow(images[0].transpose(1,2,0)) plt.show()

next () 返回下一个迭代器项目。next() 生成迭代器的函数和函数 iter () 一起使用函数。 iter () 函数获取这些可迭代对象的迭代器。不断使用获得的迭代器 next () 通过函数获取条数据,调用可迭代对象 iter 方法. iterable – 可迭代对象,default – 可选设置在没有下一个元素的情况下返回默认值。如果不设置,则不触发下一个元素 StopIteration 异常。

模型构建:

基于 Module 类的模型来完成的,它让模型构造更加灵活。

Module 类是 nn 模块中提供的模型结构类是所有神经网络模块的基类,我们可以继承它来定义我们想要的模型。下面继承 Module 多层感知机的类结构。这里定义的 MLP 类重载了 Module 类的 init 函数和 forward 函数。它们用于创建模型参数和定义前向计算。前向计算也是正向传播。

import torch from torch import nn  class MLP(nn.Module):   # 声明了带有模型参数的层,声明了两个全连接层   def __init__(self, **kwargs):     # 调用MLP父类Block构造函数进行必要的初始化。这样,在构造实例中也可以指定其他函数     super(MLP, self).__init__(**kwargs)     self.hidden = nn.Linear(784, 256#设置全连接层     self.act = nn.ReLU()     self.output = nn.Linear(256,10)         # 定义模型的前向计算,即如何根据输入x计算返回所需的模型输出   def forward(self, x)
    o = self.act(self.hidden(x))
    return self.output(o) 


#实例化。我们可以实例化 MLP 类得到模型变量 net 。
#下面的代码初始化 net 并传入输入数据 X 做一次前向计算。
#其中, net(X) 会调用 MLP 继承自 Module 类的 call 函数,
#这个函数将调用 MLP 类定义的forward 函数来完成前向计算。
X = torch.rand(2,784)
net = MLP()
print(net)
net(X)  

以上的 MLP 类中无须定义反向传播函数。系统将通过自动求梯度而自动生成反向传播所需的 backward 函数,输出如下:  

#输出:
MLP(
  (hidden): Linear(in_features=784, out_features=256, bias=True)
# in_features:size of each input sample  
#out_features: size of each output sample  
#bias:if set to false,the layer will not learn an additive bias
#[in_features,out_features]
#从输入输出的张量的shape角度来理解,相当于一个输入为[batch_size, in_features]的张量变换成了[batch_size, out_features]的输出张量。
  (act): ReLU()
  (output): Linear(in_features=256, out_features=10, bias=True)
)
tensor([[ 0.0149, -0.2641, -0.0040,  0.0945, -0.1277, -0.0092,  0.0343,  0.0627,
         -0.1742,  0.1866],
        [ 0.0738, -0.1409,  0.0790,  0.0597, -0.1572,  0.0479, -0.0519,  0.0211,
         -0.1435,  0.1958]], grad_fn=<AddmmBackward>)

注意,这里并没有将 Module 类命名为 Layer (层)或者 Model (模型)之类的名字,这是因为该类是一个可供自由组建的部件。它的子类既可以是⼀个层(如PyTorch提供的 Linear 类),又可以是一个模型(如这里定义的 MLP 类),或者是模型的⼀个部分。

神经网络中常见层:

构造的 MyLayer 类通过继承 Module 类自定义了一个的层,并将层的计算定义在了 forward 函数里。这个层里不含模型参数。

import torch
from torch import nn

class MyLayer(nn.Module):
    def __init__(self, **kwargs):
        super(MyLayer, self).__init__(**kwargs)
    def forward(self, x):
        return x - x.mean()  

#测试,实例化该层,然后做前向计算
layer = MyLayer()
layer(torch.tensor([1, 2, 3, 4, 5], dtype=torch.float))

#结果:
tensor([-2., -1.,  0.,  1.,  2.])

自定义含模型参数的自定义层。其中的模型参数可以通过训练学出。

Parameter 类其实是 Tensor 的子类,如果一个 Tensor 是 Parameter ,那么它会自动被添加到模型的参数列表里。所以在自定义含模型参数的层时,我们应该将参数定义成 Parameter ,除了直接定义成 Parameter 类外,还可以使用 ParameterList 和 ParameterDict 分别定义参数的列表和字典。

class MyListDense(nn.Module): 
# nn.Module 就是一个pytorch类,能够保持对状态的跟踪。
#我们可以创建 nn.Module 子类,nn.Module有很多属性和方法(如.parameters()和.zero_grad()),继承后将使用这些属性和方法。
    def __init__(self):
        super(MyListDense, self).__init__()
        self.params = nn.ParameterList([nn.Parameter(torch.randn(4, 4)) for i in range(3)])
        self.params.append(nn.Parameter(torch.randn(4, 1)))

    def forward(self, x):
        for i in range(len(self.params)):
            x = torch.mm(x, self.params[i])
        return x
net = MyListDense()
print(net)

class MyDictDense(nn.Module):
    def __init__(self):
        super(MyDictDense, self).__init__()
        self.params = nn.ParameterDict({
                'linear1': nn.Parameter(torch.randn(4, 4)),
                'linear2': nn.Parameter(torch.randn(4, 1))
        })
        self.params.update({'linear3': nn.Parameter(torch.randn(4, 2))}) # 新增

    def forward(self, x, choice='linear1'):
        return torch.mm(x, self.params[choice])

net = MyDictDense()
print(net)

二维卷积层将输入和卷积核做互相关运算,并加上一个标量偏差来得到输出。卷积层的模型参数包括了卷积核和标量偏差。在训练模型的时候,通常我们先对卷积核随机初始化,然后不断迭代卷积核和偏差。

#卷积运算(二维互相关)
def corr2d(X, K): 
    h, w = K.shape
    X, K = X.float(), K.float()
    Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))
    for i in range(Y.shape[0]):
        for j in range(Y.shape[1]):
            Y[i, j] = (X[i: i + h, j: j + w] * K).sum()
    return Y
#Y[i, j] = (X[i: i + h, j: j + w] * K).sum() 
# 二维卷积层
class Conv2D(nn.Module):
    def __init__(self, kernel_size):
        super(Conv2D, self).__init__()
        self.weight = nn.Parameter(torch.randn(kernel_size))
        self.bias = nn.Parameter(torch.randn(1))

    def forward(self, x):
        return corr2d(x, self.weight) + self.bias #corr2d 上面的二维卷积运算
#padding
X = X.view((1, 1) + X.shape)#原shape前加两个维度
# 在⾼和宽两侧的填充数分别为2和1,将每次滑动的行数和列数称为步幅(stride)
conv2d = nn.Conv2d(..., padding=(2, 1), stride=(3, 4))
#pooling最大池化或平均池化
 if mode == 'max':
                Y[i, j] = X[i: i + p_h, j: j + p_w].max()
 elif mode == 'avg':
                Y[i, j] = X[i: i + p_h, j: j + p_w].mean()
#一个nn.Module包含各个层和一个forward(input)方法,该方法返回output
#instance:
#前馈神经网络 (feed-forward network)(LeNet)
#步骤:
#定义包含一些可学习参数(或者叫权重)的神经网络
#在输入数据集上迭代
#通过网络处理输入
#计算 loss (输出和正确答案的距离)
#将梯度反向传播给网络的参数
#更新网络的权重,一般使用一个简单的规则:weight = weight - learning_rate * gradient
import torch
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
#n包则依赖于autograd包来定义模型并对它们求导。
#一个nn.Module包含各个层和一个forward(input)方法,该方法返回output。
#backward函数会在使用autograd时自动定义,backward函数用来计算导数。
    def __init__(self):
        super(Net, self).__init__()#Net找其父类,将父类init东西给self
        # 输入图像channel:1;输出channel:6;5x5卷积核
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        # an affine operation: y = Wx + b
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        # 2x2 Max pooling
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        # 如果是方阵,则可以只使用一个数字进行定义
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.view(-1, self.num_flat_features(x))#把特征拍扁
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

    def num_flat_features(self, x):#计算特征数用于拍扁
        size = x.size()[1:]  # 除去批处理维度的其他所有维度
        num_features = 1
        for s in size:
            num_features *= s
        return num_features
net = Net()
print(net)#print the net's structure.

#返回可学习参数
print(net.parameters())
params = list(net.parameters())
#print(params)
print(len(params))
print(params[0].size())  # conv1的权重

#to use my model
#注意:这个网络 (LeNet)的期待输入是 32x32 的张量。
#如果使用 MNIST 数据集来训练这个网络,要把图片大小重新调整到 32x32。
input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
net.zero_grad()#梯度清零
out.backward(torch.randn(1, 10))#随机梯度的反向传播
#torch.nn supports mini-batches rather than only one sample.
#nn.Conv2d 接受一个4维的张量,即nSamples x nChannels x Height x Width。
#如果是一个单独的样本,只需要使用input.unsqueeze(0) 来添加一个“假的”批大小维度。

torch.nn只支持小批量处理 (mini-batches)。整个 torch.nn 包只支持小批量样本的输入,不支持单个样本的输入。比如,nn.Conv2d 接受一个4维的张量,即nSamples x nChannels x Height x Width 如果是一个单独的样本,只需要使用input.unsqueeze(0) 来添加一个“假的”批大小维度。

  • torch.Tensor - 一个多维数组,支持诸如backward()等的自动求导操作,同时也保存了张量的梯度。

  • nn.Module - 神经网络模块。是一种方便封装参数的方式,具有将参数移动到GPU、导出、加载等功能。

  • nn.Parameter - 张量的一种,当它作为一个属性分配给一个Module时,它会被自动注册为一个参数。

  • autograd.Function - 实现了自动求导前向和反向传播的定义,每个Tensor至少创建一个Function节点,该节点连接到创建Tensor的函数并对其历史进行编码。

class AlexNet(nn.Module):
    def __init__(self):
        super(AlexNet, self).__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(1, 96, 11, 4), # in_channels, out_channels, kernel_size, stride, padding
            nn.ReLU(),
            nn.MaxPool2d(3, 2), # kernel_size, stride
            # 减小卷积窗口,使用填充为2来使得输入与输出的高和宽一致,且增大输出通道数
            nn.Conv2d(96, 256, 5, 1, 2),
            nn.ReLU(),
            nn.MaxPool2d(3, 2),
            # 连续3个卷积层,且使用更小的卷积窗口。除了最后的卷积层外,进一步增大了输出通道数。
            # 前两个卷积层后不使用池化层来减小输入的高和宽
            nn.Conv2d(256, 384, 3, 1, 1),
            nn.ReLU(),
            nn.Conv2d(384, 384, 3, 1, 1),
            nn.ReLU(),
            nn.Conv2d(384, 256, 3, 1, 1),
            nn.ReLU(),
            nn.MaxPool2d(3, 2)
        )
         # 这里全连接层的输出个数比LeNet中的大数倍。使用丢弃层来缓解过拟合
        self.fc = nn.Sequential(
            nn.Linear(256*5*5, 4096),
            nn.ReLU(),
            nn.Dropout(0.5),
            nn.Linear(4096, 4096),
            nn.ReLU(),
            nn.Dropout(0.5),
            # 输出层。由于这里使用Fashion-MNIST,所以用类别数为10,而非论文中的1000
            nn.Linear(4096, 10),
        )

    def forward(self, img):
        feature = self.conv(img)
        output = self.fc(feature.view(img.shape[0], -1))
        return output

模型初始化:

#随机初始化:

#高斯分布
torch.nn.init.normal_(tensor: torch.Tensor, mean: float = 0.0, std: float = 1.0) → torch.Tensor
#均匀分布
torch.nn.init.uniform_(tensor: torch.Tensor, a: float = 0.0, b: float = 1.0) → torch.Tensor
#常数分布
torch.nn.init.constant_(tensor: torch.Tensor, val: float) → torch.Tensor
#全0分布
torch.nn.init.zeros_(tensor: torch.Tensor) → torch.Tensor
#全1分布
torch.nn.init.ones_(tensor: torch.Tensor) → torch.Tensor
#等等等等...

我们通常会根据实际模型来使用torch.nn.init进行初始化,通常使用isinstance来进行判断模块(回顾模型构建)属于什么类型。

import torch
import torch.nn as nn

conv = nn.Conv2d(1,3,3)
linear = nn.Linear(10,1)

isinstance(conv,nn.Conv2d)
isinstance(linear,nn.Conv2d)


#输出
True 
False

#对于不同的类型层,我们就可以设置不同的权值初始化的方法。

# 对conv进行kaiming初始化
torch.nn.init.kaiming_normal_(conv.weight.data)
conv.weight.data
# 对linear进行常数初始化
torch.nn.init.constant_(linear.weight.data,0.3)
linear.weight.data

初始化函数的封装

将各种初始化方法定义为一个initialize_weights()的函数并在模型初始后进行使用:

def initialize_weights(self):
	for m in self.modules():
		# 判断是否属于Conv2d
		if isinstance(m, nn.Conv2d):
			torch.nn.init.xavier_normal_(m.weight.data)
			# 判断是否有偏置
			if m.bias is not None:
				torch.nn.init.constant_(m.bias.data,0.3)
		elif isinstance(m, nn.Linear):
			torch.nn.init.normal_(m.weight.data, 0.1)
			if m.bias is not None:
				torch.nn.init.zeros_(m.bias.data)
		elif isinstance(m, nn.BatchNorm2d):
			m.weight.data.fill_(1) 		 
			m.bias.data.zeros_()	

这段代码流程是遍历当前模型的每一层,然后判断各层属于什么类型,然后根据不同类型层,设定不同的权值初始化方法。我们可以通过下面的例程进行一个简短的演示:

# 模型的定义
class MLP(nn.Module):
  # 声明带有模型参数的层,这里声明了两个全连接层
  def __init__(self, **kwargs):
    # 调用MLP父类Block的构造函数来进行必要的初始化。这样在构造实例时还可以指定其他函数
    super(MLP, self).__init__(**kwargs)
    self.hidden = nn.Conv2d(1,1,3)
    self.act = nn.ReLU()
    self.output = nn.Linear(10,1)
    
   # 定义模型的前向计算,即如何根据输入x计算返回所需要的模型输出
  def forward(self, x):
    o = self.act(self.hidden(x))
    return self.output(o)

mlp = MLP()
print(list(mlp.parameters()))
print("-------初始化-------")

initialize_weights(mlp)
print(list(mlp.parameters()))

------------------------------------------------------------------------------------

tensor([[[[ 0.2103, -0.1679,  0.1757],
          [-0.0647, -0.0136, -0.0410],
          [ 0.1371, -0.1738, -0.0850]]]], requires_grad=True), Parameter containing:
tensor([0.2507], requires_grad=True), Parameter containing:
tensor([[ 0.2790, -0.1247,  0.2762,  0.1149, -0.2121, -0.3022, -0.1859,  0.2983,
         -0.0757, -0.2868]], requires_grad=True), Parameter containing:
tensor([-0.0905], requires_grad=True)]
"-------初始化-------"
[Parameter containing:
 tensor([[[[-0.3196, -0.0204, -0.5784],
           [ 0.2660,  0.2242, -0.4198],
           [-0.0952,  0.6033, -0.8108]]]], requires_grad=True),
 Parameter containing:
 tensor([0.3000], requires_grad=True),
 Parameter containing:
 tensor([[ 0.7542,  0.5796,  2.2963, -0.1814, -0.9627,  1.9044,  0.4763,  1.2077,
           0.8583,  1.9494]], requires_grad=True),
 Parameter containing:
 tensor([0.], requires_grad=True)]

损失函数:

总结如下:

3.6 损失函数 — 深入浅出PyTorch

训练和评估:

#我们前面在DataLoader构建完成后介绍了如何从中读取数据,在训练过程中使用类似的操作即可,区别在于此时要用for循环读取DataLoader中的全部数据。
for data, label in train_loader:

#之后将数据放到GPU上用于后续计算,此处以.cuda()为例
data, label = data.cuda(), label.cuda()

#开始用当前批次数据做训练时,应当先将优化器的梯度置零:
optimizer.zero_grad()

#之后将data送入模型中训练:
output = model(data)

#根据预先定义的criterion计算损失函数:
loss = criterion(output, label)

#将loss反向传播回网络:
loss.backward()

#使用优化器更新模型参数:
optimizer.step()

#这样一个训练过程就完成了,后续还可以计算模型准确率等指标。

验证/测试的流程基本与训练过程一致,不同点在于:

  • 需要预先设置torch.no_grad,以及将model调至eval模式

  • 不需要将优化器的梯度置零

  • 不需要将loss反向回传到网络

  • 不需要更新optimizer

#一个完整的图像分类的训练过程如下所示:

def train(epoch):
    model.train()
    train_loss = 0
    for data, label in train_loader:
        data, label = data.cuda(), label.cuda()
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(label, output)
        loss.backward()
        optimizer.step()
        train_loss += loss.item()*data.size(0)
    train_loss = train_loss/len(train_loader.dataset)
		print('Epoch: {} \tTraining Loss: {:.6f}'.format(epoch, train_loss))

#一个完整图像分类的验证过程如下所示:

def val(epoch):       
    model.eval()
    val_loss = 0
    with torch.no_grad():
        for data, label in val_loader:
            data, label = data.cuda(), label.cuda()
            output = model(data)
            preds = torch.argmax(output, 1)
            loss = criterion(output, label)
            val_loss += loss.item()*data.size(0)
            running_accu += torch.sum(preds == label.data)
    val_loss = val_loss/len(val_loader.dataset)
    print('Epoch: {} \tTraining Loss: {:.6f}'.format(epoch, val_loss))

 Pytorch优化器

优化器是根据网络反向传播的梯度信息来更新网络的参数,以起到降低loss函数计算值,使得模型输出更加接近真实标签

  • defaults:存储的是优化器的超参数

  • state:参数的缓存

  • param_groups:管理的参数组,是一个list,其中每个元素是一个字典,顺序是params,lr,momentum,dampening,weight_decay,nesterov

  • zero_grad():清空所管理参数的梯度,PyTorch的特性是张量的梯度不自动清零,因此每次反向传播后都需要清空梯度。

  • step():执行一步梯度更新,参数更新

  • add_param_group():添加参数组

  • load_state_dict() :加载状态参数字典,可以用来进行模型的断点续训练,继续上次的参数进行训练

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

#实际操作

import os
import torch

# 设置权重,服从正态分布  --> 2 x 2
weight = torch.randn((2, 2), requires_grad=True)
# 设置梯度为全1矩阵  --> 2 x 2
weight.grad = torch.ones((2, 2))
# 输出现有的weight和data
print("The data of weight before step:\n{}".format(weight.data))
print("The grad of weight before step:\n{}".format(weight.grad))
# 实例化优化器
optimizer = torch.optim.SGD([weight], lr=0.1, momentum=0.9)
# 进行一步操作
optimizer.step()
# 查看进行一步后的值,梯度
print("The data of weight after step:\n{}".format(weight.data))
print("The grad of weight after step:\n{}".format(weight.grad))
# 权重清零
optimizer.zero_grad()
# 检验权重是否为0
print("The grad of weight after optimizer.zero_grad():\n{}".format(weight.grad))
# 输出参数
print("optimizer.params_group is \n{}".format(optimizer.param_groups))
# 查看参数位置,optimizer和weight的位置一样,我觉得这里可以参考Python是基于值管理
print("weight in optimizer:{}\nweight in weight:{}\n".format(id(optimizer.param_groups[0]['params'][0]), id(weight)))
# 添加参数:weight2
weight2 = torch.randn((3, 3), requires_grad=True)
optimizer.add_param_group({"params": weight2, 'lr': 0.0001, 'nesterov': True})
# 查看现有的参数
print("optimizer.param_groups is\n{}".format(optimizer.param_groups))
# 查看当前状态信息
opt_state_dict = optimizer.state_dict()
print("state_dict before step:\n", opt_state_dict)
# 进行5次step操作
for _ in range(50):
    optimizer.step()
# 输出现有状态信息
print("state_dict after step:\n", optimizer.state_dict())
# 保存参数信息
torch.save(optimizer.state_dict(),os.path.join(r"D:\pythonProject\Attention_Unet", "optimizer_state_dict.pkl"))
print("----------done-----------")
# 加载参数信息
state_dict = torch.load(r"D:\pythonProject\Attention_Unet\optimizer_state_dict.pkl") # 需要修改为你自己的路径
optimizer.load_state_dict(state_dict)
print("load state_dict successfully\n{}".format(state_dict))
# 输出最后属性信息
print("\n{}".format(optimizer.defaults))
print("\n{}".format(optimizer.state))
print("\n{}".format(optimizer.param_groups))


---------------------------------------输出结果------------------------------------------、
# 进行更新前的数据,梯度
The data of weight before step:
tensor([[-0.3077, -0.1808],
        [-0.7462, -1.5556]])
The grad of weight before step:
tensor([[1., 1.],
        [1., 1.]])
# 进行更新后的数据,梯度
The data of weight after step:
tensor([[-0.4077, -0.2808],
        [-0.8462, -1.6556]])
The grad of weight after step:
tensor([[1., 1.],
        [1., 1.]])
# 进行梯度清零的梯度
The grad of weight after optimizer.zero_grad():
tensor([[0., 0.],
        [0., 0.]])
# 输出信息
optimizer.params_group is 
[{'params': [tensor([[-0.4077, -0.2808],
        [-0.8462, -1.6556]], requires_grad=True)], 'lr': 0.1, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False}]

# 证明了优化器的和weight的储存是在一个地方,Python基于值管理
weight in optimizer:1841923407424
weight in weight:1841923407424
    
# 输出参数
optimizer.param_groups is
[{'params': [tensor([[-0.4077, -0.2808],
        [-0.8462, -1.6556]], requires_grad=True)], 'lr': 0.1, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False}, {'params': [tensor([[ 0.4539, -2.1901, -0.6662],
        [ 0.6630, -1.5178, -0.8708],
        [-2.0222,  1.4573,  0.8657]], requires_grad=True)], 'lr': 0.0001, 'nesterov': True, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0}]

# 进行更新前的参数查看,用state_dict
state_dict before step:
 {'state': {0: {'momentum_buffer': tensor([[1., 1.],
        [1., 1.]])}}, 'param_groups': [{'lr': 0.1, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False, 'params': [0]}, {'lr': 0.0001, 'nesterov': True, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'params': [1]}]}
# 进行更新后的参数查看,用state_dict
state_dict after step:
 {'state': {0: {'momentum_buffer': tensor([[0.0052, 0.0052],
        [0.0052, 0.0052]])}}, 'param_groups': [{'lr': 0.1, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False, 'params': [0]}, {'lr': 0.0001, 'nesterov': True, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'params': [1]}]}

# 存储信息完毕
----------done-----------
# 加载参数信息成功
load state_dict successfully
# 加载参数信息
{'state': {0: {'momentum_buffer': tensor([[0.0052, 0.0052],
        [0.0052, 0.0052]])}}, 'param_groups': [{'lr': 0.1, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False, 'params': [0]}, {'lr': 0.0001, 'nesterov': True, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'params': [1]}]}

# defaults的属性输出
{'lr': 0.1, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False}

# state属性输出
defaultdict(<class 'dict'>, {tensor([[-1.3031, -1.1761],
        [-1.7415, -2.5510]], requires_grad=True): {'momentum_buffer': tensor([[0.0052, 0.0052],
        [0.0052, 0.0052]])}})

# param_groups属性输出
[{'lr': 0.1, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False, 'params': [tensor([[-1.3031, -1.1761],
        [-1.7415, -2.5510]], requires_grad=True)]}, {'lr': 0.0001, 'nesterov': True, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'params': [tensor([[ 0.4539, -2.1901, -0.6662],
        [ 0.6630, -1.5178, -0.8708],
        [-2.0222,  1.4573,  0.8657]], requires_grad=True)]}]



#1.每个优化器都是一个类,我们一定要进行实例化才能使用,比如下方实现:
class Net(nn.Moddule):
    ···
net = Net()
optim = torch.optim.SGD(net.parameters(),lr=lr)
optim.step()


#2.optimizer在一个神经网络的epoch中需要实现下面两个步骤:
#梯度置零
#梯度更新
optimizer = torch.optim.SGD(net.parameters(), lr=1e-5)
for epoch in range(EPOCH):
	...
	optimizer.zero_grad()  #梯度置零
	loss = ...             #计算loss
	loss.backward()        #BP反向传播
	optimizer.step()       #梯度更新


#3.给网络不同的层赋予不同的优化器参数。
from torch import optim
from torchvision.models import resnet18

net = resnet18()

optimizer = optim.SGD([
    {'params':net.fc.parameters()},#fc的lr使用默认的1e-5
    {'params':net.layer4[0].conv1.parameters(),'lr':1e-2}],lr=1e-5)# 可以使用param_groups查看属性

标签: 1435连接器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台