Pytorch代码模板(可套用)

代码来源:https://github.com/LianHaiMiao/pytorch-lesson-zh,在学习这份代码时添加了一点思考,并修改了一些写法,添加了一些必要的注释。没有抄袭的想法, 只是觉得作者的代码很好,学习过程中记录一下,并自己理解总结一下,持续更新…

一、构建一个线性回归器


# -- coding: utf-8 --
import torch
from torch.autograd import Variable   
"""如果用numpy或者Tensor来实现神经网络,需要手动写出前向过程和反向过程。对于简单的网络,反向过程中的导数容易求得,但是随着网络深度以及网络复杂度的增加,求出梯度的解析表达式是非常困难的。PyTorch的包autograd提供了自动求导的功能,当使用autograd时,定义前向网络会生成 一个计算图,每个节点是一个Tensor,边表示由输入Tensor到输出Tensor的函数。沿着计算图的反向传播可以很容易地计算出各个变量的梯度。在实现的时候,用到了Variable对象。Variable对Tensor对象进行封装,只需要Variable::data即可取出Tensor,并且Variable还封装了该Tensor的梯度Variable::grad(是个Variable对象)。现在用Variable作为计算图的节点,则通过反向传播自动求得的导数就保存在Variable对象中了。"""
import torch.nn as nn
import matplotlib.pyplot as plt

# 生成数据
x = torch.unsqueeze(torch.linspace(-1,1,200), dim=1)
y = 5 * x + 0.8 * torch.rand(x.size())
"""torch.squeeze()主要对数据的维度进行压缩,去掉维度为1的维度,默认去掉所有维度为1的维度,也可以通过dim指定位置。torch.unsqueeze()主要是对数据维度进行扩充。需要通过dim指定维度,给指定位置加上维度为1的维度。torch.linspace(start, end, steps=100, out=None) → Tensor返回一个1维张量,包含在区间start和end上均匀间隔的step个点。size()函数返回张量的各个维度的尺度"""

# 先看一下散点图的样子,所以绘制模拟数据的图像
plt.scatter(x.numpy(),y.numpy())
plt.show()

# 为了能够自动求导,需要将x,y变成Variable对象
X = Variable(x)  """Pytorch中的Variable默认是允许自动求导的,所以requires_grad=True可以不加"""
Y = Variable(y)
_____________________________________________________________________________________________________
上面是对原始数据点进行处理,下面是定义一些模型和参数值。

# 定义初始化参数
def init_parameters():
    W = Variable(torch.randn(1,1), requires_grad=True) # 随机初始化  
    b = Variable(torch.zeros(1,1), requires_grad=True) # 初始化偏差
    parameters = {"W":W, "b":b}
    return parameters 
"""torch.randn()返回一个张量,包含了从标准正态分布(均值为0,方差为1,即高斯白噪声)中抽取的一组随机数。张量的形状由参数sizes定义。torch.rand()返回一个张量,包含了从区间[0, 1)的均匀分布中抽取的一组随机数。张量的形状由参数sizes定义。"""

# 定义模型
def model(X, parameters):
    return X * parameters["W"] + parameters["b"]

# 定义损失函数,这里损失函数用均方差,需要别的可以自己定义
def square_loss(y_hat, Y):
    loss = (y_hat - Y).pow(2).sum()
    return loss

# 定义梯度更新参数
def update_parameters(parameters, lr):
    parameters["W"].data -= lr * parameters["W"].grad.data
    parameters["b"].data -= lr * parameters["b"].grad.data
    return
—————————————————————————————————————————————————————————————————————————————————————————————————————
上面定义了一些模型和参数的细节,下面开始训练及其他

# 定义超参数
Epoch = 100  
learning_rate = 0.001

# 参数初始化
parameters = init_parameters()

# 开始训练
for t in range(Epoch):
    y_hat = model(X, parameters) """对x进行预测"""
    loss = square_loss(y_hat, Y)"""计算损失"""
    loss.backward()"""反向求导"""
    update_parameters(parameters, learning_rate)"""通过梯度,更新参数"""
    if t % 20 == 0:
        print(loss)
    parameters["W"].grad.data.zero_()
    parameters["b"].grad.data.zero_() """因为自动求导会对梯度积累,所以每次更新完参数之后都要清除梯度值"""
    
# 画图
plt.scatter(X.data.numpy(), Y.data.numpy())  """这时的X,Y已经是Variable对象了,所以要.data"""
plt.plot(X.data.numpy(), y_hat.data.numpy(), 'r-', lw = 4)
plt.show()

print("预测的参数w是:",parameters["W"])
print("预测的常数项b是:",parameters["b"])

"""
用torch.nn来构建模型
在PyTorch中 nn 包定义了一系列基本组件,这些组件(Modules)涵盖了大部分构建神经网络会使用的各种层。
一个组件(Modules)的输入是Variable,经过组件之后,输出又是另一个 Variable。

当然nn包中还包含了大部分我们平时使用的损失函数。
最后torch.optim中还含有很多我们平时使用的优化算法,用来更新梯度。(我们上面使用的就是梯度下降法,只不过上面是我们自己手写,现在可以直接调用了)
"""
# 刚才,我们使用的是Tensor和autograd来构建线性回归的模型,现在,我们来使用torch.nn来快速构建一个线性回归模型
# -- coding: utf-8 --
import torch
from torch.autograd import Variable   
"""如果用numpy或者Tensor来实现神经网络,需要手动写出前向过程和反向过程。对于简单的网络,反向过程中的导数容易求得,但是随着网络深度以及网络复杂度的增加,求出梯度的解析表达式是非常困难的。PyTorch的包autograd提供了自动求导的功能,当使用autograd时,定义前向网络会生成 一个计算图,每个节点是一个Tensor,边表示由输入Tensor到输出Tensor的函数。沿着计算图的反向传播可以很容易地计算出各个变量的梯度。在实现的时候,用到了Variable对象。Variable对Tensor对象进行封装,只需要Variable::data即可取出Tensor,并且Variable还封装了该Tensor的梯度Variable::grad(是个Variable对象)。现在用Variable作为计算图的节点,则通过反向传播自动求得的导数就保存在Variable对象中了。"""
import torch.nn as nn
import matplotlib.pyplot as plt

# 生成数据
x = torch.unsqueeze(torch.linspace(-1,1,200), dim=1)
y = 5 * x + 0.8 * torch.rand(x.size())
"""torch.squeeze()主要对数据的维度进行压缩,去掉维度为1的维度,默认去掉所有维度为1的维度,也可以通过dim指定位置。torch.unsqueeze()主要是对数据维度进行扩充。需要通过dim指定维度,给指定位置加上维度为1的维度。torch.linspace(start, end, steps=100, out=None) → Tensor返回一个1维张量,包含在区间start和end上均匀间隔的step个点。size()函数返回张量的各个维度的尺度"""

# 先看一下散点图的样子,所以绘制模拟数据的图像
plt.scatter(x.numpy(),y.numpy())
plt.show()

# 为了能够自动求导,需要将x,y变成Variable对象
X = Variable(x)  
"""Pytorch中的Variable默认是允许自动求导的,所以requires_grad=True可以不加"""
Y = Variable(y)
_____________________________________________________________________________________________________
上面是对原始数据点进行处理,下面是定义一些模型和参数值。

# 定义初始化参数
def init_parameters():
    W = Variable(torch.randn(1,1), requires_grad=True) # 随机初始化  
    b = Variable(torch.zeros(1,1), requires_grad=True) # 初始化偏差
    parameters = {"W":W, "b":b}
    return parameters 
"""torch.randn()返回一个张量,包含了从标准正态分布(均值为0,方差为1,即高斯白噪声)中抽取的一组随机数。张量的形状由参数sizes定义。torch.rand()返回一个张量,包含了从区间[0, 1)的均匀分布中抽取的一组随机数。张量的形状由参数sizes定义。"""

# 定义模型
"""使用torch.nn来定义我们的模型,这里的Linear表示的是线性模型,在初始化这个模型的时候,需要传入的参数是:in_feature, out_feature即输入特征和输出特征,默认初始化权重"""
model = nn.Linear(1,1)

# 定义损失函数,这里损失函数用均方差,需要别的可以自己定义
square_loss = nn.MSELoss(size_average=False)
"""size_average=False表示我们需要的是总的误差,不需要去平均误差"""

# 定义超参数
Epoch = 100  
learning_rate = 0.001

# 定义梯度更新参数
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
"""上面的代码中的梯度下降是手写的,现在直接使用torch.nn为用户封装好的。使用optim包来定义优化算法,可以自动帮我们对模型的参数进行梯度跟新。model.parameters()会自动地把模型中的参数提取出来,然后告诉优化器,它们是我们要更新的参数。"""
—————————————————————————————————————————————————————————————————————————————————————————————————————
上面定义了一些模型和参数的细节,下面开始训练及其他


# 参数初始化
parameters = init_parameters()

# 开始训练
for t in range(Epoch):
    y_hat = model(X)
    """对x进行预测,这里规定要把parameters参数删掉"""
    loss = square_loss(y_hat, Y)
    """计算损失"""
    if t % 20 == 0:
        print(loss)
    optimizer.zero_grad()  
	"""在我们反向求导之前,我们需要清空积累的梯度,由于我们使用的是 torch.optim包中的对象,我们可以直接调用该对象的方法,来自动的清空积累的梯度"""
    loss.backward()
    optimizer.step() 
"""反向求导结束,我们开始更新梯度,以前更新梯度需要手动输入w1.grad.data,现在只需要一行代码就可以搞定了!"""
"""这里可能会发现有一点差别,即上一份代码中,是:loss反向传播——更新参数——梯度清零。而这份代码中,是梯度清零——loss反向传播——更新参数。实际上这两种思路都是可以的,前者是在本次epoch结尾就把本次的梯度清零,这样下一个epoch就不用清零了。而后者选择在本次epoch开始时将上一次epoch的梯度清零。前者是今天的事情今天做,后者是昨天的事情今天做"""
# 画图
plt.scatter(X.data.numpy(), Y.data.numpy())  
"""这时的X,Y已经是Variable对象了,所以要.data"""
plt.plot(X.data.numpy(), y_hat.data.numpy(), 'r-', lw = 4)
plt.show()

print("预测的参数w是:",parameters["W"])
print("预测的常数项b是:",parameters["b"])

"""
用torch.nn来构建模型
在PyTorch中 nn 包定义了一系列基本组件,这些组件(Modules)涵盖了大部分构建神经网络会使用的各种层。
一个组件(Modules)的输入是Variable,经过组件之后,输出又是另一个 Variable。

当然nn包中还包含了大部分我们平时使用的损失函数。
最后torch.optim中还含有很多我们平时使用的优化算法,用来更新梯度。(我们上面使用的就是梯度下降法,只不过上面是我们自己手写,现在可以直接调用了)
刚才,我们使用的是Tensor和autograd来构建线性回归的模型,现在,我们来使用torch.nn来快速构建一个线性回归模型
"""
注意:
1. 在新版pytorch中,tensor和Variable已经合并,所以上面的代码 17-20 行可以删掉
2. 在使用了nn和optim包之后,有一些步骤可以简化:
   (1) 可以不再手动初始化参数,这两个包有默认的初始化参数方法。所以上面代码中的  24-30 行可以删掉,删掉之后对应着要把 37 行的代码square_loss = nn.MSELoss(size_average=False),改为square_loss = nn.MSELoss(reduction='sum'),以及要把 51-52 行、 56 行中的parameters、 74-75 行删掉。下面是最终版的:
# -- coding: utf-8 --
import torch
from torch.autograd import Variable   
"""如果用numpy或者Tensor来实现神经网络,需要手动写出前向过程和反向过程。对于简单的网络,反向过程中的导数容易求得,但是随着网络深度以及网络复杂度的增加,求出梯度的解析表达式是非常困难的。PyTorch的包autograd提供了自动求导的功能,当使用autograd时,定义前向网络会生成 一个计算图,每个节点是一个Tensor,边表示由输入Tensor到输出Tensor的函数。沿着计算图的反向传播可以很容易地计算出各个变量的梯度。在实现的时候,用到了Variable对象。Variable对Tensor对象进行封装,只需要Variable::data即可取出Tensor,并且Variable还封装了该Tensor的梯度Variable::grad(是个Variable对象)。现在用Variable作为计算图的节点,则通过反向传播自动求得的导数就保存在Variable对象中了。"""
import torch.nn as nn
import matplotlib.pyplot as plt

# 生成数据
x = torch.unsqueeze(torch.linspace(-1,1,200), dim=1)
y = 5 * x + 0.8 * torch.rand(x.size())
"""torch.squeeze()主要对数据的维度进行压缩,去掉维度为1的维度,默认去掉所有维度为1的维度,也可以通过dim指定位置。torch.unsqueeze()主要是对数据维度进行扩充。需要通过dim指定维度,给指定位置加上维度为1的维度。torch.linspace(start, end, steps=100, out=None) → Tensor返回一个1维张量,包含在区间start和end上均匀间隔的step个点。size()函数返回张量的各个维度的尺度"""

# 先看一下散点图的样子,所以绘制模拟数据的图像
plt.scatter(x.numpy(),y.numpy())
plt.show()
_____________________________________________________________________________________________________
上面是对原始数据点进行处理,下面是定义一些模型和参数值。

# 定义模型
"""使用torch.nn来定义我们的模型,这里的Linear表示的是线性模型,在初始化这个模型的时候,需要传入的参数是:in_feature, out_feature即输入特征和输出特征,默认初始化权重"""
model = nn.Linear(1,1)

# 定义损失函数,这里损失函数用均方差,需要别的可以自己定义
square_loss = nn.MSELoss(reduction='sum')
"""size_average=False表示我们需要的是总的误差,不需要去平均误差"""

# 定义超参数
Epoch = 100  
learning_rate = 0.001

# 定义梯度更新参数
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
"""上面的代码中的梯度下降是手写的,现在直接使用torch.nn为用户封装好的。使用optim包来定义优化算法,可以自动帮我们对模型的参数进行梯度跟新。model.parameters()会自动地把模型中的参数提取出来,然后告诉优化器,它们是我们要更新的参数。"""
—————————————————————————————————————————————————————————————————————————————————————————————————————
上面定义了一些模型和参数的细节,下面开始训练及其他

# 开始训练
for t in range(Epoch):
    y_hat = model(x)
    """对x进行预测,这里规定要把parameters参数删掉"""
    loss = square_loss(y_hat, y)
    """计算损失"""
    if t % 20 == 0:
        print(loss)
    optimizer.zero_grad()  
	"""在我们反向求导之前,我们需要清空积累的梯度,由于我们使用的是 torch.optim包中的对象,我们可以直接调用该对象的方法,来自动的清空积累的梯度"""
    loss.backward()
    optimizer.step() 
"""反向求导结束,我们开始更新梯度,以前更新梯度需要手动输入w1.grad.data,现在只需要一行代码就可以搞定了!"""
"""这里可能会发现有一点差别,即上一份代码中,是:loss反向传播——更新参数——梯度清零。而这份代码中,是梯度清零——loss反向传播——更新参数。实际上这两种思路都是可以的,前者是在本次epoch结尾就把本次的梯度清零,这样下一个epoch就不用清零了。而后者选择在本次epoch开始时将上一次epoch的梯度清零。前者是今天的事情今天做,后者是昨天的事情今天做"""
# 画图
plt.scatter(x.data.numpy(), y.data.numpy())  
"""这时的X,Y已经是Variable对象了,所以要.data"""
plt.plot(x.data.numpy(), y_hat.data.numpy(), 'r-', lw = 4)
plt.show()

"""
用torch.nn来构建模型
在PyTorch中 nn 包定义了一系列基本组件,这些组件(Modules)涵盖了大部分构建神经网络会使用的各种层。
一个组件(Modules)的输入是Variable,经过组件之后,输出又是另一个 Variable。

当然nn包中还包含了大部分我们平时使用的损失函数。
最后torch.optim中还含有很多我们平时使用的优化算法,用来更新梯度。(我们上面使用的就是梯度下降法,只不过上面是我们自己手写,现在可以直接调用了)
刚才,我们使用的是Tensor和autograd来构建线性回归的模型,现在,我们来使用torch.nn来快速构建一个线性回归模型
"""

二、构建一个多层感知机(使用nn和optim)


import torch
from torch.autograd import Variable
import numpy as np
import torch.nn as nn
import torch.nn.functional as F

# 生成数据
M, input_size, hidden_size, output_size = 64,1000, 100, 10
"""M是样本数量,input_size是输入层大小,hidden_size是隐含层大小,output_size是输出层大小"""

x = torch.randn(M, input_size)
y = torch.randn(M, output_size)
_____________________________________________________________________________________________________
上面是对原始数据点进行处理,下面是定义一些模型和参数值。

# 定义类,关于为什么要定义类,网上有很多的解释,简而言之就是类可以把具有强关系的函数聚到一起,或者属性之间有强关系的时候,使用类是比使用函数更高效的。
class TwoLayerNet(nn.Module): 
"""一定要继承nn.Module,因为pytorch里面一切自定义操作基本上都是继承nn.Module类来实现的"""
	def __init__(self, input_size, hidden_size, output_size):
        """
        	在构建模型时,能够使用nn.Sequential的地方,尽量多使用,因为这样可以让结构更加清晰
        """
        super(TwoLayerNet, self).__init__()  
        """调用父类的构造函数"""
        self.twolayernet = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, output_size)
        )
	def forward(self, x):
        y_pred = self.twolayernet(x)
        return y_pred
        


# 定义模型
"""使用nn包的Sequential来快速构建模型,Sequential可以 看成一个组件的容器,它涵盖神经网络中的很多层,并将这些层组合在一起构成一个模型,之后,我们输入的数据会按照这个Sequential的流程进行数据的传输,最后一层就是输出层。默认会帮我们进行参数初始化"""
model = TwoLayerNet(input_size, hidden_size, output_size)

# 定义损失函数,这里损失函数用均方差,需要别的可以自己定义
loss_fn = nn.MSELoss(reduction='sum')

# 定义超参数
learning_rate = 1e-4
Epoch = 300

# 定义梯度更新参数
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
—————————————————————————————————————————————————————————————————————————————————————————————————————
上面定义了一些模型和参数的细节,下面开始训练及其他

# 开始训练
for t in range(Epoch):
    y_pred = model(x)
    loss = loss_fn(y_pred, y)
    if t % 50 == 0:
        print(loss)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
# 画图

三、加载大型数据集的方法


"""深度学习里有很多情况下,需要加载非常大的数据集,尤其是图像处理领域,动辄几十G的数据集,一般用本机电脑加载的时候会显示内存不够,而Pytorch给我们提供了可以把数据集切成mini-batch的包,我们可以使用Pytorch的API快速地完成这些操作"""
import torch
from torch.utils.data import Dataset, DataLoader, TensorDataset
import numpy as np

data = np.loadtxt('dataset.csv', delimiter=',', dtype=np.float32)
"""delimiter-加载文件分隔符"""
x_data = torch.from_numpy(data[:, 0:-1])
y_data = torch.from_numpy(data[:, -1])

print(x_data.shape, y_data.shape)
"""torch.utils.data.Dataset负责表示数据集,而torch.utils.data.Dataloader提供了对batch的处理,如shuffle等。Dataset其后就需要封装在Dataloader中。而torchvision.datasets包中提前处理好了很多图片数据集,可以直接使用。torch.utils.data.TensorDataset继承自Dataset,TensorDataset经常和Dataloader联用。需要注意的是,Dataset类只相当于一个打包工具,包含了数据的地址。真正把数据读入内存的过程是由Dataloader进行批迭代输入的时候进行的。"""
# 首先,看一下直接使用TensorDataset来将数据包装成Dataset类的写法
deal_dataset = TensorDataset(x_data, y_data)
train_loader = DataLoader(dataset=deal_dataset,
                          batch_size=32,
                          shuffle=True,
                          num_workers=0)
"""num_workers:使用多进程加载的进程数,0代表不使用多进程"""
for epoch in range(2):
    for i, data in enumerate(train_loader):
        """enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。"""
        inputs, labels = data
        print(epoch, i, "inputs", inputs, "labels", labels)
# 然后我们再试一种方法:继承Dataset类,写一个将数据处理成DataLoader的类。
# 当继承了一个Dataset类之后,需要重写__len__方法,该方法提供了dataset的大小;__getitem方法,该方法支持从0到len(self)的索引。一般都要重写这两个方法。
import torch
from torch.utils.data import Dataset, DataLoader, TensorDataset
import numpy as np
class DealDataset(Dataset):
    """下载数据、初始化数据,都可以在这里完成"""
    def __init__(self):
        data = np.loadtxt('dataset.csv', delimiter=',', dtype=np.float32)
        self.x_data = torch.from_numpy(data[:, 0:-1])
        self.y_data = torch.from_numpy(data[:, -1]) 
        self.len = data.shape[0]
    def __getitem__(self, index):
        return self.x_data[index], self.y_data[index]
    def __len__(self):
        return self.len
"""实例化这个类,然后就得到了Dataset类型的数据,接下来就将这个类传给DataLoader"""  
dealDataset = DealDataset()
train_loader = DataLoader(dataset=dealDataset,
                         batch_size=32,
                         shuffle=True)
for epoch in range(2):
    for i, data in enumerate(train_loader):
        inputs, labels = data
        print(epoch, i, "inputs", inputs, "labels", labels)
        
"""这样就把加载数据集并初始化模块化了"""
# 加载图像数据集同样很重要,Pytorch提供了一个torchvision库,专门用来处理图像,这个包中有四个大类:torchvision.datasets torchvision.models torchvision.transforms torchvision.utils
1. torchvision.datasets是用来进行数据加载的,PyTorch团队在这个包中帮我们提前处理好了很多很多图片数据集,包括MNIST COCO Captions Detection LSUN ImageFolder Imagenet-12 CIFAR STL10 SVHN PhotoTour
import torchvision
from torch.utils.data import DataLoader
trainset = torchvision.datasets.MNIST(root='./data',
                                      train=True,
                                      download=True,
                                      transform=None)
"""root表示MNIST 数据的加载的相对目录;train表示是否加载数据库的训练集,false的时候加载测试集;download表示是否自动下载 MNIST 数据集;transform表示是否需要对数据进行预处理,none为不进行预处理"""
train_loader = DataLoader(dataset=trainset,
                         batch_size=32,
                         shuffle=True)
print("训练集总长度:", len(trainset))
print("每个mini-batch的size为32,一共有:", len(train_loader), "个")

2. torchvision.models中为我们提供了已经训练好的模型,让我们可以加载之后,直接使用。模型包括AlexNet VGG ResNet SqueezeNet DenseNet.
"""
# 我们可以直接使用如下代码来快速创建一个权重随机初始化的模型
# import torchvision.models as models
# resnet18 = models.resnet18()
# alexnet = models.alexnet()
# squeezenet = models.squeezenet1_0()
# densenet = models.densenet_161()
# 也可以通过使用 pretrained=True 来加载一个别人预训练好的模型
# import torchvision.models as models
# resnet18 = models.resnet18(pretrained=True)
# alexnet = models.alexnet(pretrained=True)
"""
import torchvision.models as models
resnet18 = models.resnet18()

import torchvision.models as models
resnet18 = models.resnet18(pretrained=True)
"""加载一个已经预训练好的模型,需要下载一段时间"""

3. torchvision.transforms 提供了一般的图像转换操作类
"""这里还是对MNIST进行处理,初始的MNIST是 28 * 28,我们把它处理成 96 * 96 的torch.Tensor的格式"""
from torchvision import transforms as transforms
import torchvision
from torch.utils.data import DataLoader

transform = transforms.Compose([
    transforms.Resize(96), 
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])
trainset = torchvision.datasets.MNIST(root='./data',
                                      train=True,
                                      download=True,
                                      transform=transform)
train_loader = DataLoader(dataset=trainset,
                         batch_size=32,
                         shuffle=True)
print("训练集总长度:", len(trainset))
print("每个mini-batch的size为32,一共有:", len(train_loader), "个")

四、实现一个稍微大一点的网络流程(下载数据+处理数据+网络设计+训练+处理分类任务)


# 构建模型LeNet-5,并用这个模型来处理MNIST数据集
import torch
import torch.nn as nn
import torch.nn.functional as F
# 生成数据
"""数据由于来自Pytorch库自带的数据集"""
import torchvision
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision import datasets

batch_size = 128
train_dataset = datasets.MNIST(root='./data', 
                               train=True, 
                               transform=transforms.ToTensor(), 
                               download=False)
test_dataset = datasets.MNIST(root='./data', 
                               train=False, 
                               transform=transforms.ToTensor(), 
                               )
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
_____________________________________________________________________________________________________
上面是对原始数据点进行处理,下面是定义一些模型和参数值。
# 定义类,关于为什么要定义类,网上有很多的解释,简而言之就是类可以把具有强关系的函数聚到一起,或者属性之间有强关系的时候,使用类是比使用函数更高效的。
class LeNet5(nn.Module):
    def __init__(self, in_dim, n_class):
        super(LeNet5, self).__init__()
        self.conv1 = nn.Conv2d(in_dim, 6, 5, padding=2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16*5*5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, n_class)
        
	def forward(self, x):
        x = F.max_pool2d(F.relu(self.conv1(x)), 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

# 定义模型    
leNet = LeNet5(1, 10)
print(leNet)
        
# 定义损失函数,这里损失函数用交叉熵,需要别的可以自己定义
loss_fn = nn.CrossEntropyLoss()
# 定义超参数
learning_rate = 0.0001
Epochs = 2
use_gpu = torch.cuda.is_available()
# 定义梯度更新参数
import torch.optim as optim
optimizer = optim.Adam(leNet.parameters(), lr = learning_rate)
—————————————————————————————————————————————————————————————————————————————————————————————————————
上面定义了一些模型和参数的细节,下面开始训练及其他
# 开始训练
for epoch in range(Epochs):
    print('Epoch {}'.format(epoch+1))
    print('*' * 10)
    running_loss = 0.0
    running_acc = 0.0
    for i, data in enumerate(train_loader, 1):
        """enumerate(train_loader, 1)这里的1的意思是'start=1',即原来从0开始,现在从1开始"""
        
        img, label = data
        out = leNet(img)
        loss = loss_fn(out, label)
        running_loss += loss * label.size(0)
        _, pred = torch.max(out, 1)
        """torch.max(input, dim),输出input中最大的数,即为预测的类别,这个函数返回值有两个,第一个tensor是每行的最大值(概率最大值),第二个tensor是每行最大值的索引(对应的类别索引号)"""
        num_correct = (pred == label).sum()
        accuracy = (pred == label).float().mean()
        """这里可以求mean的原因是(pred == label)实际上是一个128维的矩阵"""
        running_acc += num_correct
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        if i % 300 == 0:
            print('[{}/{}] Loss: {:.6f}, Acc:{:.6f}'.format(epoch+1, Epochs, running_loss/(batch_size * i), running_acc / (batch_size * i)))
            print('*' * 10)
print("Done!")        
"""需要强调的一点是,Pytorch中的nn.CrossEntropyLoss()跟平常的交叉熵还是有一点点不一样的。
因为nn.CrossEntropyLoss()的两个参数:out, label的维度是不一样的,out维度是模型的输出维度,即每一张图片属于10个类别各自的概率。而label就是一个标量,为了处理这种情况,Pytorch将label标量变成onehot形式的编码,也成为了10个类别各自的概率,只不过只有一个为1,其他全部为0而已,这样就可以做交叉熵损失了。但是在这里,loss得到的是一个张量,而实际上送进来的是128张图片,计算过程应该是把128张图片的损失平均了,所以为了得到running_loss,应该乘以128,得到这个batch的loss。"""
# 利用nn.Module自定义类的4种方式
1. 这种方式将所有的层都放在了构造函数__init__里,但是只定义了一系列的层,各个层之间到底是什么连接关系并没有说明,而是在forward里面实现了所有层的连接关系。
import torch
class MyNet(torch.nn.Module):
    def __init__(self):
        super(MyNet, self).__init__()  # 第一句话,调用父类的构造函数
        self.conv1 = torch.nn.Conv2d(3, 32, 3, 1, 1)
        self.relu1=torch.nn.ReLU()
        self.max_pooling1=torch.nn.MaxPool2d(2,1)
 
        self.conv2 = torch.nn.Conv2d(3, 32, 3, 1, 1)
        self.relu2=torch.nn.ReLU()
        self.max_pooling2=torch.nn.MaxPool2d(2,1)
 
        self.dense1 = torch.nn.Linear(32 * 3 * 3, 128)
        self.dense2 = torch.nn.Linear(128, 10)
 
    def forward(self, x):
        x = self.conv1(x)
        x = self.relu1(x)
        x = self.max_pooling1(x)
        x = self.conv2(x)
        x = self.relu2(x)
        x = self.max_pooling2(x)
        x = self.dense1(x)
        x = self.dense2(x)
        return x
model = MyNet()
2. 这种方式将没有训练参数的层没有放在构造函数里,所以这些层就不会出现在model里面,但是连接关系是在forward里面的functional的方法实现的。
import torch
import torch.nn.functional as F
class MyNet(torch.nn.Module):
    def __init__(self):
        super(MyNet, self).__init__()  # 第一句话,调用父类的构造函数
        self.conv1 = torch.nn.Conv2d(3, 32, 3, 1, 1)
        self.conv2 = torch.nn.Conv2d(3, 32, 3, 1, 1)
 
        self.dense1 = torch.nn.Linear(32 * 3 * 3, 128)
        self.dense2 = torch.nn.Linear(128, 10)
 
    def forward(self, x):
        x = self.conv1(x)
        x = F.relu(x)
        x = F.max_pool2d(x)
        x = self.conv2(x)
        x = F.relu(x)
        x = F.max_pool2d(x)
        x = self.dense1(x)
        x = self.dense2(x)
        return x
model = MyNet()

3. 这种方式将相同结构的层凝聚成了一个Sequential块。
import torch.nn as nn
class MyNet(nn.Module):
    def __init__(self):
        super(MyNet, self).__init__()
        self.conv_block = nn.Sequential(
            nn.Conv2d(3, 32, 3, 1, 1),
            nn.ReLU(),
            nn.MaxPool2d(2)
        )
        self.dense_block = nn.Sequential(
            nn.Linear(32 * 3 * 3, 128),
            nn.ReLU(),
            nn.Linear(128, 10)
        )
    def forward(self,x):
        conv_out1 = self.conv_block(x)
        conv_out2 = self.conv_block(conv_out1)
        out1 = self.dense_block(conv_out2)
        out2 = self.dense_block(out1)
        return out2
model = MyNet()
print(model)
上述三份代码来自:https://blog.csdn.net/qq_27825451/article/details/90550890?utm_medium=distribute.pc_relevant.none-task-blog-title-1&spm=1001.2101.3001.4242
    
4. 这种方式把参数放在了类外面,不过看来不适合定义复杂结构的网络结构,比如有卷积、有全连接、有池化的网络结构
import torch
import torch.nn as nn
M, input_size, hidden_size, output_size = 64, 1000, 100, 10
class TwoLayerNet(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        """
            我们在构建模型的时候,能够使用nn.Sequential的地方,尽量使用它,因为这样可以让结构更加清晰
        """
        super(TwoLayerNet, self).__init__()
        # self.input_size = input_size
        # self.hidden_size = hidden_size
        # self.output_size = output_size
        self.twolayernet = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, output_size),
        )

    def forward(self, x):
        """
        在forward函数中,我们会接受一个Variable,然后我们也会返回一个Varible
        """
        y_pred = self.twolayernet(x)
        return y_pred

model = TwoLayerNet(input_size, hidden_size, output_size)
print(model)


  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值