回顾:训练神经网络

通过PyTorch 进行深度学习

数学公式如下所示:

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

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

张量

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

使用pytorch 构建神经网络

%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 构建神经网络

# 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 数据集,然后为我们创建训练数据集和测试数据集

# 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)
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,就像正常的概率分布一样。

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 获取它们。

初始化权重和偏差
print(net.fc1.weight)
print(net.fc1.bias)

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

# 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);
前向传递

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

# 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):

# 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来命名各个图层和操作。 请注意,字典键必须是唯一的,因此每个操作必须具有不同的名称。

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的操作。

%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
x = torch.randn(2,2, requires_grad=True)
print(x)
y = x**2
print(y)

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

## grad_fn shows the function that generated this variable
print(y.grad_fn)

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

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

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

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏ATYUN订阅号

使用Python计算非参数的秩相关

当两个变量都有良好理解的高斯分布时,很容易计算和解释。而当我们不知道变量的分布时,我们必须使用非参数的秩相关(Rank Correlation,或称为等级相关)...

14830
来自专栏机器之心

基于注意力机制,机器之心带你理解与训练神经机器翻译系统

70680
来自专栏奇点大数据

扔掉代码表!用RNN“破解”摩斯电码

作者:Sandeep Bhupatiraju 剧透警告:摩斯电码并不需要破解。它很有用,因为消息可以使用这些代码以最少的设备发送,并且我说它不需要破解,因为代码...

45350
来自专栏机器之心

LSTM、GRU与神经图灵机:详解深度学习最热门的循环神经网络

选自MachineLearningMastery 作者:Jason Brownlee 机器之心编译 参与:熊猫 循环神经网络是当前深度学习热潮中最重要和最核心的...

42390
来自专栏人工智能头条

TensorFlow Wide And Deep 模型详解与应用

1.3K30
来自专栏SeanCheney的专栏

《Scikit-Learn与TensorFlow机器学习实用指南》 第14章 循环神经网络

击球手击出垒球,你会开始预测球的轨迹并立即开始奔跑。你追踪着它,不断调整你的移动步伐,最终在观众的掌声中抓到它。无论是在听完朋友的话语还是早餐时预测咖啡的味道,...

13720
来自专栏AI科技评论

开发 | 如何把时间序列问题转化为监督学习问题?通俗易懂的 Python 教程

AI科技评论按:本文作者 Jason Brownlee 为澳大利亚知名机器学习专家,对时间序列预测尤有心得。原文发布于其博客。 Jason Brownlee ...

35850
来自专栏AILearning

【Scikit-Learn 中文文档】聚类 - 无监督学习 - 用户指南 | ApacheCN

2.3. 聚类 未标记的数据的 Clustering(聚类) 可以使用模块 sklearn.cluster 来实现。 每个 clustering algo...

2.6K110
来自专栏SeanCheney的专栏

《Scikit-Learn与TensorFlow机器学习实用指南》 第15章 自编码器

自编码器是能够在无监督的情况下学习输入数据的有效表示(叫做编码)的人工神经网络(即,训练集是未标记)。这些编码通常具有比输入数据低得多的维度,使得自编码器对降维...

14730
来自专栏机器之心

教程 | 深度学习:自动编码器基础和类型

388160

扫码关注云+社区

领取腾讯云代金券