Machine Learning & Recommendation & NLP & DL

Pytorch学习记录-使用Pytorch进行深度学习,使用案例

2019-03-26  本文已影响25人  我的昵称违规了
首页.jpg

注意确定已经安装了torch和torchvision
使用案例学习Pytorch
在完成60分钟入门之后,接下来有六节tutorials和五节关于文本处理的tutorials。争取一天一节。不过重点是关注神经网络构建和数据处理部分。
教程通过下面的几个例子介绍了Pytorch的基本概念。Pytorch的核心是以下两个特性:

接下来教程会使用全连接Relu网络作为运行案例,这个网络有一个隐藏层hy,使用梯度下降进行训练拟合随即数据。
下面是教程目录。
1.Tensors(张量)
1.1 Warm-up:numpy
1.2 PyTorch:Tensors
2.Autograd(自动梯度)
2.1 PyTorch:Variables and autograd (变量和自动梯度)
2.2 PyTorch : Defining new autograd functions(定义新的自动梯度函数)
2.3 TensorFlow: Static Graphs (静态图)
3.nn module
3.1 PyTorch: nn
3.2 PyTorch: optim
3.3 PyTorch: Custom nn Modules (定制nn模块)
3.4 PyTorch: Control Flow + Weight Sharing (控制流+权重分享)

1. Tensor

1.1 热身

介绍Pytorch之前使用numpy实现一个网络。numpy提供的是一个n维数组对象,以及操作这些数组的函数。但是numpy不是为计算图、深度学习、梯度计算而生,在这里生成一个两层的网络,然后使用随即数据拟合,前向、后向传播均使用numpy操作。

# -*- coding:utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt

N, D_in, H, D_out = 64, 1000, 100, 10
x = np.random.randn(N, D_in)
y = np.random.randn(N, D_out)

w1 = np.random.randn(D_in, H)
w2 = np.random.randn(H, D_out)

learning_rate = 1e-6
for t in range(500):
    h = x.dot(w1)
    h_relu = np.maximum(h, 0)
    y_pred = h_relu.dot(w2)

    loss = np.square(y_pred - y).sum()
    print(t, loss)

    grad_y_pred = 2.0 * (y_pred - y)
    grad_w2 = h_relu.T.dot(grad_y_pred)
    grad_h_relu = grad_y_pred.dot(w2.T)
    grad_h = grad_h_relu.copy()
    grad_h[h < 0] = 0
    grad_w1 = x.T.dot(grad_h)

    w1 -= learning_rate * grad_w1
    w2 -= learning_rate * grad_w2

我们输出一下损失函数


image.png

1.2 PyTorch:Tensors

Pytorch的基本概念Tensor,Tensor与numpy的array类似,是一个n维的array。
Pytorch提供多种进行Tensor运算,教程中还提到,Pytorch同样也不是专门进行深度学习、计算图、计算梯度的。但是优势在于能在GPU上运行。
在这里使用Pytorch Tensor来拟合一个2层的网络,需要手动执行前向和反向操作。

import torch
import matplotlib.pyplot as plt

dtype = torch.float
device = torch.device('cpu')
N, D_in, H, D_out = 64, 1000, 100, 10
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

w1 = torch.randn(D_in, H, device=device, dtype=dtype)
w2 = torch.randn(H, D_out, device=device, dtype=dtype)

learning_rate = 1e-6
loss_list = []
t_list = []

for t in range(500):
    h = x.mm(w1)
    h_relu = h.clamp(min=0)
    y_pred = h_relu.mm(w2)
    loss = (y_pred - y).pow(2).sum().item()
    print(t, loss)
    loss_list.append(loss)
    t_list.append(t)

    grad_y_pred = 2.0 * (y_pred - y)
    grad_w2 = h_relu.t().mm(grad_y_pred)
    grad_h_relu = grad_y_pred.mm(w2.t())
    grad_h = grad_h_relu.clone()
    grad_h[h < 0] = 0
    grad_w1=x.t().mm(grad_h)

    w1-=learning_rate*grad_w1
    w2-=learning_rate*grad_w2
image.png

2.Autograd(自动梯度)

2.1 PyTorch:Tensors and autograd

实际操作中肯定不能像上面那样手动操作,这样,就需要有一个自动求梯度,自动反向传递。
在之前60分钟已经做了介绍,那就是autograd包,在使用autograd时,前向通道定义一个计算图(computational graph),图中的节点是Tensor,边是依据输入Tensor产生的输出Tensro的函数,这样图的反向传播就可以计算梯度了。
操作历程如下

import torch

dtype = torch.float
device = torch.device('cpu')

N, D_in, H, D_out = 64, 1000, 100, 10

x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)
# 在这里设置requires_grad为True,就可以在接下来进行反向传播计算
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)

learning_rate = 1e-6

for t in range(500):
    y_pred = x.mm(w1).clamp(min=0).mm(w2)

    #计算损失,这时损失已经是一个Tensor,shape为(1,),loss.item()获取的就是损失的标量值
    loss=(y_pred-y).pow(2).sum()
    print(t,loss.item())

    loss.backward()
    with torch.no_grad():
        w1-=learning_rate*w1.grad
        w2-=learning_rate*w2.grad

        #在使用完之后将梯度归零
        w1.grad.zero_()
        w2.grad.zero_()

对比之前的操作,会发现Pytorch大大降低了手工操作的负担,只需要在设定的时候增加requires_grad=True,在最后对权重进行归零即可。

2.2 PyTorch : Defining new autograd functions(定义新的自动梯度函数)

在底层,每次原始的autograd操作都是对Tensor的两个方法的操作。

这个例子就是定制一个autograd函数来执行Relu非线性,进一步执行两层神经网络。

import torch

class MyRelu(torch.autograd.Function):
    @staticmethod
    def forward(ctx, input):
        '''
        ctx是一个上下文对象,可用于存储信息以进行反向计算。
        :param ctx: 
        :param input: 
        :return: 
        '''
        ctx.save_for_backward(input)
        return input.clamp(min=0)

    @staticmethod
    def backward(ctx, grad_output):
        '''
        在backwward中,获取一个含有损失的梯度的Tensor,并且计算相对于输入的损失的梯度。
        :param ctx: 
        :param grad_output: 
        :return: 
        '''
        input, = ctx.saved_tensors
        grad_input = grad_output.clone()
        grad_input[input < 0] = 0
        return grad_input


dtype = torch.float
device = torch.device("cpu")
N, D_in, H, D_out = 64, 1000, 100, 10

x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)

learning_rate = 1e-6
for t in range(500):
    relu = MyRelu.apply

    y_pred = relu(x.mm(w1)).mm(w2)
    loss = (y_pred - y).pow(2).sum()

    print(t, loss.item())
    loss.backward()

    with torch.no_grad():
        w1 -= learning_rate * w1.grad
        w2 -= learning_rate * w2.grad

        w1.grad.zero_()
        w2.grad.zero_()

2.3 TensorFlow: Static Graphs (静态图)

这一节空出来,主要内容是为了对比Tensorflow和Pytorch在动态图和静态图之间的区别,不用说,肯定Pytorch更好一些。

3.nn module(神经网络模块)

3.1 PyTorch: nn

计算图和autograd时很有用的范式,可以用来定义复杂运算符和自动求导。这时对于大型网络而言,原始的autograd就有些低级。
在构建神经网络时,会经常需要把计算安排在层(layers)中。某些层有可学习的参数,将会在学习中进行优化。
Pytorch提供的nn包就是类似Keras的高级抽象,其中定义了一系列Modules,相当于神经网络中的层,一个Module接收输入Variables,计算输出Variables,但是也可以保持一个内部状态,例如包含了可学习参数的Variables。nn 包还定义了一系列在训练神经网络时常用的损失函数。

接下来就是使用nn包来实现上面的两层神经网络

import torch

N, D_in, H, D_out = 64, 1000, 100, 10

x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

model = torch.nn.Sequential(
    torch.nn.Linear(D_in, H),
    torch.nn.ReLU(),
    torch.nn.Linear(H, D_out),
)
loss_fn = torch.nn.MSELoss(size_average=False)

learning_rate = 1e-6
for t in range(500):
    y_pred = model(x)
    loss = loss_fn(y_pred, y)
    print(t, loss.data[0])

    model.zero_grad()

    loss.backward()
    with torch.no_grad():
        for param in model.parameters():
            param -= learning_rate * param.grad

3.2 PyTorch: optim(优化)

在前面更新权重,使用的是手动改变学习参数,这对于使用简单的优化算法(SGD)是可以的,但是在使用更加复杂的优化算法(ADAGrad,RMSProp,Adam)训练神经网络。
Pytorch提供了optim包对优化算法进行抽象。
同样是刚才的那个2层神经网络,这里使用optim来进行优化。

import torch
N, D_in, H, D_out = 64, 1000, 100, 10

x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

model=torch.nn.Sequential(
    torch.nn.Linear(D_in,H),
    torch.nn.ReLU(),
    torch.nn.Linear(H,D_out),
)

loss_fn=torch.nn.MSELoss(size_average=False)
learning_rate=1e-4
optimizer=torch.optim.Adam(model.parameters(), lr=learning_rate)
for t in range(500):
    y_pred=model(x)
    loss=loss_fn(y_pred,y)
    print(t,loss.item())

    optimizer.zero_grad()

    loss.backward()
    optimizer.step()

对比前面的,你会发现越来越简化,就能生成一个神经网络。

3.3 PyTorch: Custom nn Modules (定制nn模块)

有时候,需要设定比现有模块序列更加复杂的模型。这时,你可以通过生成一个nn.Module的子类来定义一个forward。该forward可以使用其他的modules或者其他的自动梯度运算来接收输入Variables,产生输出Variables。
在这个例子中,我们实现两层神经网络作为一个定制的Module子类。

import torch


class TwoLayerNet(torch.nn.Module):
    def __init__(self, D_in, H, D_out):
        super(TwoLayerNet, self).__init__()
        self.linear1 = torch.nn.Linear(D_in, H)
        self.linear2 = torch.nn.Linear(H, D_out)

    def forward(self, x):
        h_relu = self.linear1(x).clamp(min=0)
        y_pred = self.linear2(h_relu)
        return y_pred


N, D_in, H, D_out = 64, 1000, 100, 10

x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

model = TwoLayerNet(D_in, H, D_out)
criterion=torch.nn.MSELoss(size_average=False)
optimizer=torch.optim.SGD(model.parameters(),lr=1e-4)
for t in range(500):
    y_pred=model(x)
    loss=criterion(y_pred,y)

    print(t,loss.item())
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

3.4 PyTorch: Control Flow + Weight Sharing (控制流+权重分享)

我们实现一个非常奇怪的模型来作为动态图和权重分享的例子。这个模型是一个全连接的ReLU网络。每一个前向通道选择一个1至4之间的随机数,在很多隐含层中使用。多次使用相同的权重来计算最内层的隐含层。
这个模型我们使用正常的Python流控制来实现循环。在定义前向通道时,通过多次重复使用相同的Module来实现权重分享。
我们实现这个模型作为一个Module的子类。

# -*- coding: utf-8 -*-
import random
import torch


class DynamicNet(torch.nn.Module):
    def __init__(self, D_in, H, D_out):
        
        super(DynamicNet, self).__init__()
        self.input_linear = torch.nn.Linear(D_in, H)
        self.middle_linear = torch.nn.Linear(H, H)
        self.output_linear = torch.nn.Linear(H, D_out)

    def forward(self, x):
        
        h_relu = self.input_linear(x).clamp(min=0)
        for _ in range(random.randint(0, 3)):
            h_relu = self.middle_linear(h_relu).clamp(min=0)
        y_pred = self.output_linear(h_relu)
        return y_pred


# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random Tensors to hold inputs and outputs
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

# Construct our model by instantiating the class defined above
model = DynamicNet(D_in, H, D_out)

# Construct our loss function and an Optimizer. Training this strange model with
# vanilla stochastic gradient descent is tough, so we use momentum
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4, momentum=0.9)
for t in range(500):
    # Forward pass: Compute predicted y by passing x to the model
    y_pred = model(x)

    # Compute and print loss
    loss = criterion(y_pred, y)
    print(t, loss.item())

    # Zero gradients, perform a backward pass, and update the weights.
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

最后这个例子我没实现,照抄的。
这算是基本上的模型构建,主要包括模型、损失函数计算、优化、最后的迭代。

上一篇 下一篇

猜你喜欢

热点阅读