pytorch入门(四)搭建多层的神经网络


MNIST是一个非常有名的手写体数字识别数据集,在很多资料中,这个数据集都会被用作深度学习的入门样例,接下来将围绕 MNIST数据集 训练多层的神经网络模型。

读取数据

数据集描述

MNIST数据集由250 个不同人手写的数字构成,其训练数据集包含 60,000 个样本, 测试数据集包含 10,000 样本。在 MNIST 数据集中的每张图片由 28 x 28 个像素点构成, 每个像素点用一个灰度值表示。labels 包含了相应的目标变量, 也就是手写数字的类标签(整数 0-9)

读取数据

## 读取pytorch自带数据集Minist
import torch
import torchvision.datasets as dset

root = 'D:\\pytorch\\mnistData\\'
train_set = dset.MNIST('root', train=True, download=False) # train为true表示读取训练集,false表示读取测试集,
test_set = dset.MNIST('root', train=False, download=False) # download为True表示下载,为false表示从本地读取
## 描述数据集
print("train_data:", train_set.train_data.size())
print("train_labels:", train_set.train_labels.size())
print("test_data:", test_set.test_data.size())

样本数据量大小:
在这里插入图片描述
train_set是一个torchvision.datasets.mnist.MNIST类型, 其包含如下信息:
在这里插入图片描述
对train_set 取样发现train_set包含两类信息:原数据
一个是手写数字的像素信息,由于在读取数据时未transform参数,所以特征信息并未转化为tensor类型,而是图片类型;另一个信息则是label,类型为int。
接下来打印前20组数据图像:

for i in range(24):
    image,label = train_set[i]
    path = 'D:\\pytorch\\mnistData\\MNIST\\figure\\{}-{}.png'.format(i,label)
    image.save(path)

由于image.show()仅显示一个临时图片,故将图片保存后做一个展示。
在这里插入图片描述test 内的数据和训练集数据结构相同,不再赘述。

数据预处理

由于读取数据时未定义Transform参数,故读取数据并非是tensor,参考博客link的处理方法,先定义一个转换,而后在读取数据时将此转换赋值到参数transform中读取。

  • 定义数据转换方式
# transforms.ToTensor()将图片转换成PyTorch中处理的对象Tensor,并且进行标准化(数据在0~1之间)
# transforms.Normalize()做归一化。它进行了减均值,再除以标准差。两个参数分别是均值和标准差
# transforms.Compose()函数则是将各种预处理的操作组合到了一起
data_tf = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize([0.5], [0.5])])

其中,transforms.ToTensor函数将图像类型数据转化为Tensor类型,并且做了一个归一化处理,即将Tensor数值均除以255;而Normalize函数对tensor做标准化处理,即将色彩值映射到[-1,1]闭区间里面,假设原色彩值是0,那么映射后就是-1。因为mnist图片都是灰度图,所以只有一个色彩通道。正常的彩色图片是RGB三通道的,transforms.Normalize([r,g,b],[d,e,f])这样对应每个RGB的均值和方差。

利用dataloader读取train_set及test_set,该函数将读取的dataset根据batch size大小、是否shuffle等封装成一个Batch Size大小的Tensor,用于后面的训练。

from torch.utils.data import dataloader

train_dataset = dset.MNIST('root', train=True, transform=data_tf, download=False)
test_dataset = dset.MNIST('root', train=False, transform=data_tf) # 按data_tf的方式转化数据
train_loader = dataloader.DataLoader(dataset=train_set, batch_size=64, shuffle=True) # 数据加载器。组合了一个数据集和采样器,并提供关于数据的迭代器
test_loader = dataloader.DataLoader(dataset=test_set, batch_size=64)
# 将原数据集做一个shuffle并做批处理,每个mini-batch的数目为64个

dataloader将原数据集做一个shuffle并做批处理,每个mini-batch的数目为64个,共有938个mini-batch。

构建计算图

选择网络层

这里选择一个三层的全连接网络且每层为一个线性函数。

选择激活函数

激活函数是人工神经网络的一个极其重要的特征。它决定一个神经元是否应该被激活,激活代表神经元接收的信息与给定的信息有关。激活函数对输入信息进行非线性变换。 然后将变换后的输出信息作为输入信息传给下一层神经元。

  • 常见分类器的适用场景
    1、用于分类器时,Sigmoid函数及其组合通常效果更好。
    2、由于梯度消失问题,有时要避免使用sigmoid和tanh函数。
    3、ReLU函数是一个通用的激活函数,目前在大多数情况下使用。
    4、ReLU函数只能在隐藏层中使用。
    5、如果神经网络中出现死神经元,那么PReLU函数就是最好的选择。

Tip:可以从ReLU函数开始,如果ReLU函数没有提供最优结果,再尝试其他激活函数。

import torch
import torch.nn.functional as F
import torch.nn as nn

## 定义一个三层的全连接网络
class Model(torch.nn.Module):
    def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
        super(Model,self).__init__()
        
        self.l1 = torch.nn.Linear(in_dim, n_hidden_1) #Linear中的w,b一定要初始化,若自己不定义初始化,则用默认的初始化方式初始化
        self.l2 = torch.nn.Linear(n_hidden_1, n_hidden_2)
        self.l3 = torch.nn.Linear(n_hidden_2, out_dim)
        
    # 激活函数既可以使用nn,又可以调用nn.functional
    def forward(self,x):
        out = F.relu(self.l1(x)) # # 激活函数,直接调用torch.nn.functional中集成好的Relu
        out = self.l2(out)
        out = self.l3(out)
        return out
model = Model(28 * 28, 300, 100, 10)     

选择损失函数与优化器

常见损失函数

torch.nn内置很多损失函数,下面做一个简要介绍

损失函数 表达式 应用场景
nn.CrossEntropyLoss loss ( x , c l a s s ) = log ( exp ( x [ c l a s s ] ) j exp ( x [ j ] ) ) = x [ c l a s s ] + log ( j exp ( x [ j ] ) ) \text{loss}(x, class) = -\log\left(\frac{\exp(x[class])}{\sum_j \exp(x[j])}\right) = -x[class] + \log\left(\sum_j \exp(x[j])\right) 常用于图像分类神经网络模型
nn.BCELoss ( x , y ) = L = { l 1 , , l N } , l n = w n [ y n log x n + ( 1 y n ) log ( 1 x n ) ] \ell(x, y) = L = \{l_1,\dots,l_N\}^\top, \quad l_n = - w_n \left[ y_n \cdot \log x_n + (1 - y_n) \cdot \log (1 - x_n) \right]
nn.MSELoss ( x , y ) = L = { l 1 , , l N } , l n = ( x n y n ) 2 \ell(x, y) = L = \{l_1,\dots,l_N\}^\top, \quad l_n = \left( x_n - y_n \right)^2

涉及到图像处理,故此处选择CrossEntropy损失函数。

criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(),lr=0.02)# 用了relu,所以lr很小

训练模型

由于一个epoch有938个mini-batch,故这里先训练一次。

# 训练模型
epochs=1
cost=[]
rou =0
for epoch in range(epochs):
    for data in train_loader:
        img, label = data
        img = img.view(img.size(0), -1) # 改变数据的size,变成784*1
        img = torch.autograd.Variable(img)
        label = torch.autograd.Variable(label)
        out = model(img)
        loss = criterion(out, label)
        cost.append(loss.data.numpy())
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        rou+=1
        if rou%50 == 0:
            print('epoch: {}, loss: {:.4}'.format(rou, cost[-1]))
  • tip:在实际训练过程中,每次拟合model后训练模型的结果差别很大,这表明模型效果受参数的初始值的影响,所以参数初始化尤为重要。
  • 可以自定义模型参数初始化的方法,在创建model时apply这个方法即可

模型评估

# 模型评估
model.eval()
eval_loss = 0
eval_acc = 0
for data in test_loader: # 将模型应用在test数据集中
    img, label = data
    img = img.view(img.size(0), -1) # resize
    out = model(img)
    loss = criterion(out, label)
    eval_loss += loss.data.item()*label.size(0) 
    _, pred = torch.max(out, 1) # 取最高概率的标签为预测值
    num_correct = (pred == label).sum() # 计算预测正确的标签数
    eval_acc += num_correct.item() # 计算正确率
print('Test Loss: {:.6f}, Acc: {:.6f}'.format(eval_loss / (len(test_dataset)),eval_acc / (len(test_dataset))))

out:
在这里插入图片描述

参考资料

1)https://blog.csdn.net/Season_For_Lin/article/details/80979012
2)https://blog.csdn.net/out_of_memory_error/article/details/81414986
3)https://blog.csdn.net/jacke121/article/details/82812218
4)http://baijiahao.baidu.com/s?id=1582399059360085084&wfr=spider&for=pc

猜你喜欢

转载自blog.csdn.net/qq_39446239/article/details/89266447
今日推荐