前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >回顾:训练神经网络

回顾:训练神经网络

作者头像
小飞侠xp
发布2018-08-29 15:19:39
1.2K0
发布2018-08-29 15:19:39
举报
文章被收录于专栏:书山有路勤为径

通过PyTorch 进行深度学习

数学公式如下所示:

对于向量来说,为两个向量的点积/内积:

我们可以将这些单元神经元组合为层和堆栈,形成神经元网络。一个神经元层的输出变成另一层的输入。对于多个输入单元和输出单元,我们现在需要将权重表示为矩阵。

张量

实际上神经网络计算只是对张量进行一系列线性代数运算,矩阵是张量的一种形式。向量是一维张量,矩阵是二维张量,包含 3 个索引的数组是三维向量(例如 RGB 颜色图像)。神经网络的基本数据结构是张量,PyTorch(以及几乎所有其他深度学习框架)都是以张量为基础。

使用pytorch 构建神经网络
代码语言:javascript
复制
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
import numpy as np
import torch
import helper

一般而言,PyTorch 张量的行为和 Numpy 数组相似。它们的索引都以 0 开始,并且支持切片。

改变形状

改变张量的形状是一个很常见的运算。首先使用 .size()获取张量的大小和形状。然后,使用 .resize_()改变张量的形状。注意下划线,改变形状是原地运算。

在 Numpy 与 Torch 之间转换

在 Numpy 数组与 Torch 张量之间转换非常简单并且很实用。要通过 Numpy 数组创建张量,使用 torch.from_numpy()。要将张量转换为 Numpy 数组,使用 .numpy() 方法。

内存在 Numpy 数组与 Torch 张量之间共享,因此如果你原地更改一个对象的值,另一个对象的值也会更改。

通过Pytorch 构建神经网络

代码语言:javascript
复制
# Import things like usual

%matplotlib inline
%config InlineBackend.figure_format = 'retina'

import numpy as np
import torch

import helper

import matplotlib.pyplot as plt
from torchvision import datasets, transforms

需要获取数据集。这些数据位于 torchvision 软件包中。以下代码将下载 MNIST 数据集,然后为我们创建训练数据集和测试数据集

代码语言:javascript
复制
# Define a transform to normalize the data
transform = transforms.Compose([transforms.ToTensor(),
                              transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
                             ])
# Download and load the training data
trainset = datasets.MNIST('MNIST_data/', download=True, train=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)

# Download and load the test data
testset = datasets.MNIST('MNIST_data/', download=True, train=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=True)
代码语言:javascript
复制
dataiter = iter(trainloader)
images, labels = dataiter.next()

我们将训练数据加载到了 trainloader 中,并使用 iter(trainloader)使其变成迭代器。我们将用它循环访问数据集以进行训练,但是现在我只获取了第一批数据,以便查看数据。从下方可以看出,images 是一个大小为 (64, 1, 28, 28) 的张量。因此,每批有 64 个图像、1 个颜色通道,共有 28x28 个图像。

构建神经网络

要通过 PyTorch 构建神经网络,你需要使用 torch.nn 模块。网络本身是继承自 torch.nn.Module 的类。你需要单独定义每个运算,例如针对具有 784 个输入和 128 个单元的全连接层定义为 nn.Linear(784, 128)。 该类需要包含对网络实现前向传递的 forward 方法。在此方法中,你将对之前定义的每个运算传递输入张量 x。torch.nn 模块在 torch.nn.functional 中还具有一些对等的功能,例如 ReLU。此模块通常导入为 F。要对某个层(只是一个张量)使用 ReLU 激活函数,你需要使用 F.relu(x)。以下是一些常见的不同激活函数。

对于此网络,我将添加三个全连接层,然后添加一个预测类别的 softmax 输出。softmax 函数和 S 型函数相似,都会将输入调整到 0 到 1 之间,但是还会标准化这些输入,以便所有值的和为 1,就像正常的概率分布一样。

代码语言:javascript
复制
from torch import nn
from torch import optim
import torch.nn.functional as F
class Network(nn.Module):
    def __init__(self):
        super().__init__()
        # Defining the layers, 128, 64, 10 units each
        self.fc1 = nn.Linear(784, 128)
        self.fc2 = nn.Linear(128, 64)
        # Output layer, 10 units - one for each digit
        self.fc3 = nn.Linear(64, 10)
        
    def forward(self, x):
        ''' Forward pass through the network, returns the output logits '''
        
        x = self.fc1(x)
        x = F.relu(x)
        x = self.fc2(x)
        x = F.relu(x)
        x = self.fc3(x)
        x = F.softmax(x, dim=1)
        
        return x

model = Network()
model

权重等参数是系统自动初始化的,但是你也可以自定义如何初始化这些权重。权重和偏差是附加到你所定义的层的张量,你可以通过 net.fc1.weight 获取它们。

初始化权重和偏差
代码语言:javascript
复制
print(net.fc1.weight)
print(net.fc1.bias)

要自定义初始化过程,请原地修改这些张量。实际上存在 autograd 变量,因此我们需要通过 net.fc1.weight.data 获取真正的张量。获得张量后,可以用 0(针对偏差)或随机正常值填充这些张量。

代码语言:javascript
复制
# Set biases to all zeros
net.fc1.bias.data.fill_(0);
# sample from random normal with standard dev = 0.01
net.fc1.weight.data.normal_(std=0.01);
前向传递

我们已经创建好网络,看看传入图像后会发生什么。这一过程称之为前向传递。我们将图像数据转换为张量,然后传递给网络架构定义的运算。

代码语言:javascript
复制
# Grab some data 
dataiter = iter(trainloader)
images, labels = dataiter.next()
images.resize_(64, 1, 784)

# Need to wrap it in a Variable, will explain in next notebook
inputs = Variable(images) 

# Forward pass through the network
img_idx = 0
logits = net.forward(inputs[img_idx,:])

# Predict the class from the network output
ps = F.softmax(logits, dim=1)

img = images[img_idx]
helper.view_classify(img.resize_(1, 28, 28), ps)

从上图中可以看出,我们的网络基本上根本不知道这个数字是什么,因为我们还没训练它,所有权重都是随机的!接下来,我们将了解如何训练该网络,使其能学习如何正确地对这些数字进行分类。

PyTorch提供了一种方便的方法来构建这样的网络,其中张量通过操作顺序传递。使用它来构建等效网络nn.Sequential (documentation):

代码语言:javascript
复制
# Hyperparameters for our network
input_size = 784
hidden_sizes = [128, 64]
output_size = 10

# Build a feed-forward network
model = nn.Sequential(nn.Linear(input_size, hidden_sizes[0]),
                      nn.ReLU(),
                      nn.Linear(hidden_sizes[0], hidden_sizes[1]),
                      nn.ReLU(),
                      nn.Linear(hidden_sizes[1], output_size),
                      nn.Softmax(dim=1))
print(model)

# Forward pass through the network and display output
images, labels = next(iter(trainloader))
images.resize_(images.shape[0], 1, 784)
ps = model.forward(images[0,:])
helper.view_classify(images[0].view(1, 28, 28), ps)

还可以传入OrderedDict来命名各个图层和操作。 请注意,字典键必须是唯一的,因此每个操作必须具有不同的名称。

代码语言:javascript
复制
from collections import OrderedDict
model = nn.Sequential(OrderedDict([
                      ('fc1', nn.Linear(input_size, hidden_sizes[0])),
                      ('relu1', nn.ReLU()),
                      ('fc2', nn.Linear(hidden_sizes[0], hidden_sizes[1])),
                      ('relu2', nn.ReLU()),
                      ('output', nn.Linear(hidden_sizes[1], output_size)),
                      ('softmax', nn.Softmax(dim=1))]))
model

训练神经网络

一开始网络很朴素,不知道将输入映射到输出的函数。我们通过向网络展示实际数据样本训练网络,然后调整网络参数,使其逼近此函数。

要找到这些参数,我们需要了解网络预测真实输出的效果如何。为此,我们将计算损失函数(也称为成本),一种衡量预测错误的指标。例如,回归问题和二元分类问题经常使用均方损失

其中 n 是训练样本的数量,yi是真正的标签,y ̂ i 是预测标签 通过尽量减小相对于网络参数的这一损失,我们可以找到损失最低且网络能够以很高的准确率预测正确标签的配置。我们使用叫做梯度下降法的流程来寻找这一最低值。梯度是损失函数的斜率,指向变化最快的方向。要以最短的时间找到最低值,我们需要沿着梯度(向下)前进。可以将这一过程看做沿着最陡的路线下山。

反向传播

对于单层网络,梯度下降法实现起来很简单。但是,对于更深、层级更多的神经网络(例如我们构建的网络),梯度下降法实现起来更复杂。我们通过反向传播来实现,实际上是采用的微积分中的链式法则。最简单的理解方法是将两层网络转换为图形表示法。

在网络的前向传递过程中,我们的数据和运算从右到左。要通过梯度下降法训练权重,我们沿着网络反向传播成本梯度。从数学角度来讲,其实就是使用链式法则计算相对于权重的损失梯度。

我们使用此梯度和学习速率 α 更新权重。

对于训练步骤来说,首先我们需要定义损失函数。在 PyTorch 中,通常你会看到它写成了 criterion 形式。在此例中,我们使用 softmax 输出,因此我们希望使用 criterion = nn.CrossEntropyLoss() 作为损失函数。稍后在训练时,你需要使用 loss = criterion(output, targets) 计算实际损失。 我们还需要定义优化器,例如 SGD 或 Adam 等。我将使用 SGD,即 torch.optim.SGD,并传入网络参数和学习速率。

Autograd 自动计算梯度

Torch提供了一个自动编程模块,用于自动计算张量的梯度。 它通过跟踪在张量上执行的操作来实现此目的。 为了确保PyTorch跟踪张量上的运算并计算梯度,您需要在张量上设置requires_grad。 您可以使用requires_grad关键字在创建时执行此操作,也可以随时使用x.requires_grad_(True)执行此操作。 您可以使用torch.no_grad()内容关闭代码块的渐变: x = torch.zeros(1,requires_grad = True) with torch.no_grad(): ... y = x * 2 y.requires_grad false

此外,您可以使用torch.set_grad_enabled(True | False)完全打开或关闭梯度。 使用z.backward()相对于某个变量z计算梯度。 这会向后传递创建z的操作。

代码语言:javascript
复制
%matplotlib inline
%config InlineBackend.figure_format = 'retina'

from collections import OrderedDict

import numpy as np
import time

import torch
from torch import nn
from torch import optim
import torch.nn.functional as F

import helper
代码语言:javascript
复制
x = torch.randn(2,2, requires_grad=True)
print(x)
代码语言:javascript
复制
y = x**2
print(y)

下面我们可以看到创建y的操作,一个幂运算操作PowBackward0。

代码语言:javascript
复制
## grad_fn shows the function that generated this variable
print(y.grad_fn)

autgrad模块会跟踪这些操作,并知道如何计算每个操作的梯度。 通过这种方式,它能够针对任何一个张量计算一系列操作的梯度。 让我们将张量y减小到标量值,即平均值。

你可以检查X和Y的梯度,但是它们现在是空的

要计算梯度,您需要在Variable z上运行.backward方法。 这将计算z相对于x的梯度

这些梯度计算对神经网络特别有用。 对于训练,我们需要权重的梯度与成本。 使用PyTorch,我们通过网络向前运行数据来计算成本,然后向后计算与成本相关的梯度。 一旦我们得到了梯度,我们就可以做出梯度下降步骤。 未完待续。。。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018.08.29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 通过PyTorch 进行深度学习
    • 张量
      • 使用pytorch 构建神经网络
        • 改变形状
          • 在 Numpy 与 Torch 之间转换
          • 通过Pytorch 构建神经网络
            • 训练神经网络
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档