工作生活

【深度学习DL-PyTorch】四、深度学习工具PyTorch

2019-11-08  本文已影响0人  ChiangCMBA

神经网络又称为全连接或密集网络。

一、张量(Tensor)

张量(Tensor)是向量和矩阵的泛化形式。所以,张量可以是一维、二维、三维、...六维...等等。
向量是一位的张量;
矩阵是二维的张量;
张量是PyTorch和其他神经网络框架中的基本数据结构。

特征是指神经网络的输入特征/输入数据

torch.randn()创建由正态变量组成的张量,即来自正态分布的随机正态变量。
torch.randn_like()传入一个张量并查看该张量的形状,然后创建形状相同的另一个张量。
torch.sum()
可以使用矩阵乘法进行相同的运算,大多数情况下,都建议使用矩阵乘法。因为矩阵乘法更高效,这些线性代数运算已经使用了现代库加快了速度。推荐使用矩阵乘法,因为在 GPU 上使用现代库和高效计算资源使矩阵乘法更高效。
torch.mm()矩阵乘法更简单,并且对传入的张量要求更严格。必须符合矩阵乘法的条件:第一个矩阵的列数,必须等于第二个矩阵的行数。能够按照预期的方式进行运算。
torch.matmul() 支持广播,如果传入大小/形状很奇怪的张量,那么可能获得意料之外的输出结果。

tensor.shape() 在构建神经网络时,最常见的错误是形状错误,所以在设计神经网络架构时很重要的步骤就是,使张量的形状保持匹配
tensor.reshape()创建一个张量,形状是要求的形状,但是内存中的实际数据没有改变。有时候,它会返回克隆版本,也就是说,它把数据复制到内存中的另一个部分,然后返回该内存部分存储的张量。也就是说,复制数据比直接更改张量形状(不克隆数据)效率要低。
tensor.resize_() 下划线表示resize_这个方法是原地操作(in-place operation),原地操作是指根本不改变数据,只是改变位于该内存地址中的数据对应的张量。resize_方法的问题在于如果要求的形状比原始张量的元素多或少时,可能会丢失数据或者使用未初始化的内存创建虚假的数据。
tensor.view()会返回一个新张量包含的数据和旧张量在内存中的一样。不论什么时候,它都只是返回一个新的张量,不会更改内存中的任何数据。如果想获得新的大小使张量具有新的形状和不同数量的元素,就会报错。使用view方法可以确保在更改张量形状时始终获得相同数量的元素。

torch.from_numpy()
tensor.numpy()
需要注意的是:内存会在Numpy数组和torch张量之间共享。也就是说,如果对Numpy数组或张量执行任何原地操作就会改变对方的值。

二、在PyTorch中构建神经网络

反向传播_链式法则.png

如果希望神经网络能够学习非线性关系和规律,希望输出是非线性的,那么就需要在隐藏层里使用非线性激活函数。
ReLU是线性修正单元的简称,它是最简单的非线性函数,与S型函数和双曲正切函数相比,使用ReLU时网络的训练速度快多了。

2.1构建神经网络

PyTorch 提供了nn模块,大大地简化了网络构建过程。以下演示如何构建上述同一个网络,即包含 784 个输入、256 个隐藏单元、10 个输出单元和一个 softmax 输出。

from torch import nn
class Network(nn.Module):
    def __init__(self):
        super().__init__()
        
        # Inputs to hidden layer linear transformation
        self.hidden = nn.Linear(784, 256)
        # Output layer, 10 units - one for each digit
        self.output = nn.Linear(256, 10)
        
        # Define sigmoid activation and softmax output 
        self.sigmoid = nn.Sigmoid()
        self.softmax = nn.Softmax(dim=1)
        
    def forward(self, x):
        # Pass the input tensor through each of our operations
        x = self.hidden(x)
        x = self.sigmoid(x)
        x = self.output(x)
        x = self.softmax(x)
        
        return x

class Network(nn.Module):

先继承 nn.Module。与 super().__init__() 相结合,创建一个跟踪架构的类,并提供大量有用的方法和属性。注意,在为网络创建类时,必须继承 nn.Module。类可以随意命名。

self.hidden = nn.Linear(784, 256)

这行创建一个线性转换模块 x\mathbf{W} + b,其中有 784 个输入和 256 个输出,并赋值给 self.hidden。该模块会自动创建权重和偏差张量,供我们在 forward 方法中使用。创建网络 (net) 后,你可以使用 net.hidden.weightnet.hidden.bias 访问权重和偏差张量。

self.output = nn.Linear(256, 10)

同样,这里会创建另一个有 256 个输入和 10 个输出的线性转换。

self.sigmoid = nn.Sigmoid()
self.softmax = nn.Softmax(dim=1)

然后,定义了 S 型激活函数和 softmax 输出的运算。在 nn.Softmax(dim=1) 中设置 dim=1 会计算各个列的 softmax 值。

def forward(self, x):

nn.Module 创建的 PyTorch 网络必须定义 forward 方法。它会接受一个张量 x 并将其传入你在 __init__ 方法中定义的运算。

x = self.hidden(x)
x = self.sigmoid(x)
x = self.output(x)
x = self.softmax(x)

将输入张量 x 传入重新赋值给 x 的每个运算。可以看出输入张量经过隐藏层,然后经过 S 型函数、输出层,最终经过 softmax 函数。.变量可以命名为任何名称,只要运算的输入和输出与你要构建的网络架构匹配即可。在 __init__ 方法中的定义顺序不重要,但是需要在 forward 方法中正确地设定运算顺序。

现在我们可以创建一个 Network 对象。

# Create the network and look at it's text representation
model = Network()
model

可以使用 torch.nn.functional 模块来更简练清晰地定义网络。这是最常见的网络定义方式,因为很多运算是简单的元素级函数。我们通常将此模块导入为 F,即 import torch.nn.functional as F

import torch.nn.functional as F

class Network(nn.Module):
    def __init__(self):
        super().__init__()
        # Inputs to hidden layer linear transformation
        self.hidden = nn.Linear(784, 256)
        # Output layer, 10 units - one for each digit
        self.output = nn.Linear(256, 10)
        
    def forward(self, x):
        # Hidden layer with sigmoid activation
        x = F.sigmoid(self.hidden(x))
        # Output layer with softmax activation
        x = F.softmax(self.output(x), dim=1)
        
        return x
2.2 激活函数

到目前为止,我们只学习了 softmax 激活函数,但是通常任何函数都可以用作激活函数。但是,要使网络能逼近非线性函数,激活函数必须是非线性函数。下面是一些常见的激活函数示例:Tanh(双曲正切)和 ReLU(修正线性单元)。

activation.png

在实践中,ReLU 几乎一直用作隐藏层激活函数。

2.3 使用 nn.Sequential

PyTorch 提供了一种方便的方法来构建这类网络(其中张量按顺序执行各种运算):nn.Sequential (文档)。使用它来构建等效网络:

# 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

三、训练神经网络

3.1Autograd 自动计算梯度

我们已经知道如何计算损失,那么如何使用损失进行反向传播呢?Torch 提供了模块 autograd,用于自动计算张量的梯度。我们可以使用它计算所有参数相对于损失的梯度。Autograd 的计算方式是跟踪对张量执行的运算,然后反向经过这些运算并一路计算梯度。为了确保 PyTorch 能跟踪对张量执行的运算并计算梯度,你需要在张量上设置 requires_grad = True。你可以在创建时使用 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
3.2损失和 Autograd

使用 PyTorch 创建网络时,所有参数都通过 requires_grad = True 初始化。这意味着,当我们计算损失和调用 loss.backward() 时,会计算参数的梯度。这些梯度用于在梯度下降步骤中更新权重。下面是使用反向传播计算梯度的示例。

# Build a feed-forward network
model = nn.Sequential(nn.Linear(784, 128),
                      nn.ReLU(),
                      nn.Linear(128, 64),
                      nn.ReLU(),
                      nn.Linear(64, 10),
                      nn.LogSoftmax(dim=1))

criterion = nn.NLLLoss()
images, labels = next(iter(trainloader))
images = images.view(images.shape[0], -1)

logps = model(images)
loss = criterion(logps, labels)
3.3Autograd 自动计算梯度

PyTorch训练网络 的一般流程是:

model = nn.Sequential(nn.Linear(784, 128),
                      nn.ReLU(),
                      nn.Linear(128, 64),
                      nn.ReLU(),
                      nn.Linear(64, 10),
                      nn.LogSoftmax(dim=1))

criterion = nn.NLLLoss()
optimizer = optim.SGD(model.parameters(), lr=0.003)

epochs = 5
for e in range(epochs):
    running_loss = 0
    for images, labels in trainloader:
        # Flatten MNIST images into a 784 long vector
        images = images.view(images.shape[0], -1)
    
        # TODO: Training pass
        optimizer.zero_grad()
        
        output = model.forward(images)
        loss = criterion(output, labels)
        loss.backward()
        
        optimizer.step()
        
        running_loss += loss.item()
    else:
        print(f"Training loss: {running_loss/len(trainloader)}")

四、训练神经网络的基本步骤

The steps for training/learning from a batch of data are described in the comments below:

# number of epochs to train the model
n_epochs = 30  # suggest training between 20-50 epochs

model.train() # prep model for training

for epoch in range(n_epochs):
    # monitor training loss
    train_loss = 0.0
    
    ###################
    # train the model #
    ###################
    for data, target in train_loader:
        # clear the gradients of all optimized variables
        optimizer.zero_grad()
        # forward pass: compute predicted outputs by passing inputs to the model
        output = model(data)
        # calculate the loss
        loss = criterion(output, target)
        # backward pass: compute gradient of the loss with respect to model parameters
        loss.backward()
        # perform a single optimization step (parameter update)
        optimizer.step()
        # update running training loss
        train_loss += loss.item()*data.size(0)
        
    # print training statistics 
    # calculate average loss over an epoch
    train_loss = train_loss/len(train_loader.dataset)

    print('Epoch: {} \tTraining Loss: {:.6f}'.format(
        epoch+1, 
        train_loss
        ))

上一篇下一篇

猜你喜欢

热点阅读