permute函数_pytorch的基本使用, 函数实现SDG法训练网络, 梯度下降法(DG)案例

0f7bbb369db3784592fc3d2a216ac491.png

1 pytorch的概念

1.1 tensor张量

1.1.1 PyTorch的tensor与NumPy array相互转换

PyTorch的很多操作和numpy都是类似的,但是因为其能够在 GPU 上运行,所以比 NumPy 快很多。

import torch
import numpy as np
# 创建一个 numpy ndarray
numpy_tensor = np.random.randn(10, 20)
x = torch.randn(10, 20)

1.1.1.1 ndarray==>tensor

  • torch.Tensor 强制类型转换
  • torch.from_numpy创建函数
pytorch_tensor1 = torch.Tensor(numpy_tensor)
pytorch_tensor2 = torch.from_numpy(numpy_tensor)
print(type(pytorch_tensor2))
print(type(pytorch_tensor1))
<class 'torch.Tensor'>
<class 'torch.Tensor'>

1.1.1.2 tensor==>ndarray

# 如果 pytorch tensor 在 cpu 上
numpy_array = pytorch_tensor1.numpy()

# 如果 pytorch tensor 在 gpu 上
numpy_array = pytorch_tensor1.cpu().numpy()

1.1.2 PyTorch Tensor 使用 GPU 加速

# 第一种方式是定义 cuda 数据类型
dtype = torch.cuda.FloatTensor # 定义默认 GPU 的 数据类型
gpu_tensor = torch.randn(10, 20).type(dtype)

# 第二种方式更简单,推荐使用
gpu_tensor = torch.randn(10, 20).cuda() # 将 tensor 放在GPU 上

1.1.3 tensor的属性

# 可以通过下面两种方式得到 tensor 的大小
print(pytorch_tensor1.shape)
print(pytorch_tensor1.size())
# 得到 tensor 的数据类型
print(pytorch_tensor1.type())
## 维度
print(pytorch_tensor1.dim())
# 得到 tensor 的所有元素个数
print(pytorch_tensor1.numel())

1.1.4 tensor的数据类型变换

x = torch.randn(3, 2)
print(x)
x = x.type(torch.DoubleTensor)
print(x)
x_array = x.numpy()
print(x_array.dtype)

1.2 tensor的操作

1.2.1 squeeze和unsqueeze操作: 降维升维

print(torch.ones(2, 2))
#torch.Size([2, 2])
print(torch.ones(2, 2).size())
x = torch.ones(2, 2).unsqueeze(0)
torch.Size([1, 2, 2])
print(x) 
print(x.size())

# 将 tensor 中所有的一维全部都去掉
x = x.squeeze() 
print(x)
print(x.shape)

1.2.2 数值类型转换

# 将其转化为整形
x = x.long()
# x = x.type(torch.LongTensor)
print(x.type())

1.2.3 使用permute和transpose进行维度交换

x = torch.randn(3, 4, 5)
print(x.shape)

# 使用permute和transpose进行维度交换
x = x.permute(1, 0, 2) # permute 可以重新排列 tensor 的维度
print(x.shape)

x = x.transpose(0, 2)  # transpose 交换 tensor 中的两个维度
print(x.shape)

1.2.4 使用 view 对 tensor 进行 reshape

x = torch.randn(3, 4, 5)
print(x.shape)
## 拉伸
x = x.view(-1, 5) # -1 表示任意的大小,5 表示第二维变成 5
print(x.shape)
x = x.view(3, 20) # 重新 reshape 成 (3, 20) 的大小
print(x.shape)

1.2.5 tensor的运算:相加

x = torch.zeros(3, 4)
y = torch.ones(3, 4)
# 两个 tensor 求和
z = x + y
print(z)
z = torch.add(x, y)
print(z)

1.2.6 tensor的inplace操作

pytorch中大多数的操作都支持 inplace 操作,也就是可以直接对 tensor 进行操作而不需要另外开辟内存空间。方式非常简单,一般都是在操作的符号后面加_

1.3 变量

  • from torch.autograd import Variable
  • Variable 是对 tensor 的封装,操作和 tensor 是一样的,
  • 但是每个 Variabel都有三个属性,Variable 中的.data,梯度.grad以及这个 Variable 是通过什么方式得到的.grad_fn

1.3.1 变量的梯度

# 通过下面这种方式导入 Variable
from torch.autograd import Variable
x_tensor = torch.randn(10, 5)
y_tensor = torch.randn(10, 5)

# 将 tensor 变成 Variable
# 默认 Variable 是不需要求梯度的,所以我们用这个方式申明需要对其进行求梯度
x = Variable(x_tensor, requires_grad=True)

2 梯度下降法Gradient Descent (GD)介绍

2.1 梯度下降法简介

2.1.1 梯度

比如一个一个函数

,那么 f 的梯度就是

可以称为

或者
。具体某一点
的梯度就是

2.1.2 学习率

  • 来沿着梯度的反方向,我们能够更快地找到函数的最小值点
  • 我们需要每次往下走的那一步的长度,这个长度称为学习率,用 表示
  • 学习率太小会导致下降非常缓慢
  • 学习率太大又会导致跳动非常明显

2.1.3 更新梯度公式

- 1

是第t+1步的位置,

- 2
是微分,

- 3
是步长

2.2 梯队下降的缺点

  • 鞍点
    • 一个不是局部最小值的驻点(一阶导数为0的点)称为鞍点。
    • 数学含义是: 目标函数在此点上的梯度(一阶导数)值为 0, 但从改点出发的一个方向是函数的极大值点,而在另一个方向是函数的极小值点

鞍点的例子

, 函数图形为

5c36a9ace316d912f0000910256344a2.png

的(0,0)也是鞍点

2.3 Stochastic Gradient Descent (SGD) 算法

2.3.1 SGD的理解

  • 用一张图来表示,其实SGD就像是喝醉了酒的GD,它依稀认得路,最后也能自己走回家,但是走得歪歪扭扭
  • 虽然包含一定的随机性,但是从期望上来看,它是等于正确的导数的.

2.3.2 SGD的公式

  • 是第t+1步的位置,J 是随机微分,
    是步长 -
  • $J$的期望满足梯队函数

3 pytorch函数实现梯度下降法: 线性回归

3.1 线性回归的参数的误差更新公式

3.1.1 线性回归的公式:

是我们预测的结果,希望通过
来拟合目标
,通俗来讲就是找到这个函数拟合
使得误差最小,即最小化损失函数定义为

3.1.2 梯队下降

J对w,b求偏导, 微分得到

的关系
的关系如下

通过不断地迭代更新,最终我们能够找到一组最优的 w 和 b,这就是梯度下降法的原理。

3.1.3 线性回归的梯度下降

w 和 b 的梯度分别是

3.2 代码实现

import torch
import numpy as np
from torch.autograd import Variable
import matplotlib.pyplot as plt
%matplotlib inline
# 定义随机因子
torch.manual_seed(2019)
<torch._C.Generator at 0x2333a2f2c10>

3.2.1 创建张量tensor(测试样本数据)

tensor的使用接口和 numpy 非常相似

x_train = np.array([[3.3], [4.4], [5.5], [6.71], [6.93], [4.168],
                    [9.779], [6.182], [7.59], [2.167], [7.042],
                    [10.791], [5.313], [7.997], [3.1]], dtype=np.float32)

y_train = np.array([[1.7], [2.76], [2.09], [3.19], [1.694], [1.573],
                    [3.366], [2.596], [2.53], [1.221], [2.827],
                    [3.465], [1.65], [2.904], [1.3]], dtype=np.float32)
# 转换成 Tensor
x_train = torch.from_numpy(x_train)
y_train = torch.from_numpy(y_train)

3.2.2 创建变量Variable

  • from torch.autograd import Variable
  • Variable 是对 tensor 的封装,操作和 tensor 是一样的,
  • 但是每个 Variabel都有三个属性,Variable 中的.data,梯度.grad以及这个 Variable 是通过什么方式得到的.grad_fn
# 定义参数 w 和 b
w = Variable(torch.randn(1), requires_grad=True) # 随机初始化
b = Variable(torch.zeros(1), requires_grad=True) # 使用 0 进行初始化
x_train = Variable(x_train)
y_train = Variable(y_train)
print(w)
print(b)
tensor([-0.1187], requires_grad=True)
tensor([0.], requires_grad=True)

3.2.3 构建计算图: 拟合模型,损失函数

def linear_model(x):
    return x * w + b
# 计算误差
def get_loss(y_, y):
    return torch.mean((y_ - y_train) ** 2)

3.2.4 训练模型

3.2.4.1 训练模型-查看初始化参数的模型

  • 目的:
    • 根据初始超参数, 计算第一次估计的y值
    • 根据第一次估算的值, 计算误差
  • 画图
    • tensor的data属性
    • plt的输入为numpy类型
y_ = linear_model(x_train)
loss = get_loss(y_, y_train)
# 打印一下看看 loss 的大小
print(loss)
tensor(10.2335, grad_fn=<MeanBackward1>)
plt.plot(x_train.data.numpy(), y_train.data.numpy(), 'bo', label='real')
plt.plot(x_train.data.numpy(), y_.data.numpy(), 'ro', label='estimated')
plt.legend()
<matplotlib.legend.Legend at 0x2333d3d9358>

8eb900589f58cf2d571e18361edf0b5d.png

3.2.4.2 训练模型-第一次通过损失函数的梯队来优化参数,优化模型

注: 第一次不需要梯度归零 grad.zero_() 否则None,'NoneType' object has no attribute 'zero_'

## 计算w,b梯队微分
# 将 tensor 变成 Variable 
# 默认 Variable 是不需要求梯度的,所以我们用这个方式申明需要对其进行求梯度
# 根据tensor的gent_loss损失函数计算图中, x,y已知的损失值tensor对象Loss
## 自动对tensor对象求求参数的偏导函数,并且计算梯队微分
#x = Variable(x_tensor, requires_grad=True) 
#y = Variable(y_tensor, requires_grad=True)
print(loss)
loss.backward()
print(w.grad)
print(b.grad)
tensor(10.2335, grad_fn=<MeanBackward1>)
None
tensor([-41.1289])
tensor([-6.0890])

3.2.4.3 根据学习率和计算的w,b的梯队来更新w,b的参数

# 梯队下降,减去学习率*梯队微分, 更新一次参数
w.data = w.data - 1e-2 * w.grad.data
b.data = b.data - 1e-2 * b.grad.data
y_ = linear_model(x_train)
plt.plot(x_train.data.numpy(), y_train.data.numpy(), 'bo', label='real')
plt.plot(x_train.data.numpy(), y_.data.numpy(), 'ro', label='estimated')
plt.legend()
<matplotlib.legend.Legend at 0x2333d45ef28>

140ab25398c225fede9c9b28a6273922.png

3.2.4.4 迭代30次,训练模型的参数

for e in range(31): # 进行 30 次更新
    y_ = linear_model(x_train) ## 计算拟合的Y值: tensor
    loss = get_loss(y_, y_train) ## 计算损失值: tensor
    w.grad.zero_() # 记得归零梯度
    b.grad.zero_() # 记得归零梯度
    # 自动求导,计算梯队w.grad.data,b.grad.data
    loss.backward()
    # 使用梯队更新参数
    w.data = w.data - 1e-2 * w.grad.data # 更新 w
    b.data = b.data - 1e-2 * b.grad.data # 更新 b 
    if e%10==0:
        print('epoch: {}, loss: {}'.format(e, loss.item()))
epoch: 0, loss: 0.4142104387283325
epoch: 10, loss: 0.2260516732931137
epoch: 20, loss: 0.2231873869895935
epoch: 30, loss: 0.2204667031764984
y_ = linear_model(x_train)
plt.plot(x_train.data.numpy(), y_train.data.numpy(), 'bo', label='real')
plt.plot(x_train.data.numpy(), y_.data.numpy(), 'ro', label='estimated')
plt.legend()
<matplotlib.legend.Legend at 0x2333d4ef550>

aaede8c05171aebc55d9f4b79636a48e.png

4 pytorch函数实现实现SGD: mnist手写体识别

4.1 加载数据

import numpy as np
import torch
from torchvision.datasets import MNIST # 导入 pytorch 内置的 mnist 数据
from torch.utils.data import DataLoader
from torch import nn
from torch.autograd import Variable
import time
import matplotlib.pyplot as plt
%matplotlib inline

4.1.1 数据转换规范

def transform_mnist(x):
    x = np.array(x, dtype='float32') / 255 # 将数据变到 0 ~ 1 之间
    x = (x - 0.5) / 0.5 # 标准化,这个技巧之后会讲到
    x = x.reshape((-1,)) # 拉平
    x = torch.from_numpy(x)
    return x

4.1.2 从下载的文件中加载数据

def download_mnist_data(batch_size):
    # transform 函数式编程, 对数据的变换 train指定测试训练
    # 载入数据集,申明定义的数据变换
    # 定义train=True or false
    train_set = MNIST(r'E:aiai_labai_caseai_datapytorchMNISTdata', train=True, transform=transform_mnist, download=False) 
    test_set = MNIST(r'E:aiai_labai_caseai_datapytorchMNISTdata', train=False, transform=transform_mnist, download=False)
    # 加载数据,打乱数据
    ## DataLoader: 批量取多少图片
    train_data = DataLoader(train_set, batch_size=batch_size, shuffle=True)
    return train_data

4.2 定义网络结构,定义损失函数,定义SDG函数

4.2.1 使用 Sequential 定义 3 层神经网络

# 最后一层10个输出
net = nn.Sequential(
        nn.Linear(784, 200),
        nn.ReLU(),
        nn.Linear(200, 10),
)

4.2.2 定义损失函数

# 交叉熵函数
criterion = nn.CrossEntropyLoss()

4.2.3 定义SDG函数

def sgd_update(parameters, lr):
    for param in parameters:
        param.data = param.data - lr * param.grad.data

4.3 训练网络

4.3.1 定义网络训练的函数过程

注1: 梯队归零的方法 - 直接把模型的参数梯度设成0: * model.zero_grad() * optimizer.zero_grad() * 当optimizer=optim.Optimizer(model.parameters())时,两者等效 - 如果想要把某一Variable的梯度置为0 * Variable.grad.data.zero_()

def train_net(net,train_data,lr):
    train_losses=[]
    train_loss = 0
    losses_idx = 0
    ### batch_size影响im,label的大小
    ### batch_size影响迭代次数.
    for im, label in train_data:
        # 定义变量:
        im = Variable(im)
        label = Variable(label)
        # 前向传播,传入输入tensor,获得输出tensor
        out = net(im)
        # 计算损失tensor: 根据前向传播的预测值和真是标签
        loss = criterion(out, label)
        # 将网络的所有参数的梯度属性置为零
        net.zero_grad()
        # 默认 Variable 是不需要求梯度的, requires_grad=True 这个方式申明需要对其进行求梯度
        # 损失值tensor的对网络参数的偏导
        loss.backward()
        ## 对模型参数更新,
        ## 属性 net.parameters得到所有参数
        sgd_update(net.parameters(),lr) # 使用lr的学习率
        # 记录误差
        train_loss += loss.item()
        if losses_idx % 10 == 0:
            train_losses.append(loss.item())
        losses_idx += 1
    return (train_losses,train_loss,losses_idx)

4.3.2 定义主函数

def train(net,train_data,lr,batch_size):
    start = time.time() # 记时开始
    # 训练网络,记录损失值
    (train_losses,train_loss,losses_idx)=train_net(net,train_data,lr)
    end = time.time() # 计时结束
    print('epoch: 1, Train Loss: {:.6f}'.format(train_loss / len(train_data)))
    print('使用时间: {:.5f} s'.format(end - start))
    x_axis = np.linspace(0, 5, len(train_losses), endpoint=True)
    plt.semilogy(x_axis, train_losses, label='batch_size=%d'%batch_size)
    plt.legend(loc='best')

4.3.3 开始训练网络

定义学习率和批量规模

batch_size=100
lr=1e-2

下载数据, 使用torch.utils.data.DataLoader方法批量加载数据

train_data=download_mnist_data(batch_size=batch_size)

训练数据, 根据批量规模, 训练所有的数据

train(net,train_data,lr=1e-2,batch_size=100)
epoch: 1, Train Loss: 0.905261
使用时间: 7.80006 s

268393fe24f6e6788b28694b3b4193cb.png
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值