PyTorch Week 1

PyTorch Week 1

目录

本篇记录在深度之眼学习到的Pytorch基础操作

一、张量简介与创建

Tensor 与 Variable

0.4.0 Variable并入Tensor
data, requires_grad, grad, grad_fn, is_leaf(叶子节点)
dtype, shape, device

张量的创建

1、直接创建
(1)torch.tensor()

torch.tensor(data,
					dtype=None,
					device=None,
					requires_grad=Falsem,#是否需要梯度
					pin_memory=False#是否存于锁页内存,与转换效率有关,通常False)

将tensor存入GPU

torch.tensor(data,device='cuda')

(2)从numpy数组创建torch.from_numpy(ndarray)
注意: 变量t与arr指向同一个内存。

arr = np.array([[1,2,3],[4,5,6]])
t = torch.from_numpy(arr)

2、依据数值创建
全零张量:torch.zeros()、torch.zeros_like()

torch.zeros(*size
			out = None,#输出的张量名
			layout=torch.strided,#内存中的布局形式
			device=None,
			requires_grad=False)			
out_t = torch.tensor([1])
t = torch.zeros((3,3), out=out_t)#id(t)==id(out_t)

全“1”张量:

torch.ones()、torch.ones_like()

自定义全值张量:

torch.full(size, fill_vlaue)、forch.full_like()

等差数列张量:

torch.arange(start,end,step)#step:公差

线性均分张量:

torch.linespace(start,end,step)#step:长度

对数均分张量:

torch.logspace(start,end,step,base)#step:长度,base:底数

对角矩阵张量:

torch.eye(n, m)#n:行数, m:列数

2、依据概率分布创建张量
高斯分布

torch.normal(mean, std)
torch.normal(mean, std, size)
torch.randn(*size)

标准正态分布

torch.randn()
torch.randn_like()

均匀分布

torch.rand()
torch.rand_like()

均匀整数分布

torch.randint()
torch.randint_like()

乱序的随机排列

torch.randperm(n)#n:长度

伯努利分布

torch.bernoulli(input)#input为概率值

二、张量操作与线性回归

张量拼接与切分

1 拼接

torch.cat()#在现有维度拼接
torch.stack()#在新的维度拼接

2 chunk按dim平均切分

torch.chunk(input,
			chunks,#份数,不能整除的话最后一维小于
			 dims)#指定维度

3 split按维度切分

torch.split(tensor,
			split_size_or_sections,#int或者list,按整数均分或者按list元素切分
			dim)#

张量索引

1 在第dim个维度上按index索引

torch.index_select(input,
					dim,
					index)#必须是torch.long

2 按mask中的True进行索引,返回一维张量

t = torch.randint(0,9,size=(3,3))
mask = t.ge(5)
t_select = torch.masked_select(t,mask)#
t:
tensor([[4,5,0],
		[5,7,1],
		[2,5,8]])
mase:
tensor([[False, True, False],
		[True,  True, False],
		[False, True, True]])
t_select:
tensor([5, 5, 7, 5, 8])

张量变换

1 形状

torch.reshape()

2 维度交换

torch.transpose()#
torch.t()#二维

3 压缩维度

torch.squeeze(input,
			dim=None)#
torch.unsqueeze(input,
			dim=None)#

张量运算

torch.add()
torch.addcdiv()
torch.addcmul()

线性回归

torch.manual_seed(10)
lr = 0.1 # 学习率

# 创建训练数据
x = torch.rand(20,1)*10
y = 2*x +(5+torch.randn(20,1))

# 构建线性回归参数
w = torch.randn((1), requires_grad = True)
b = torch.zeros((1),requires_grad = True)

for iteration in range(1000):

    #forward
    wx = torch.mul(w,x)
    y_pred = torch.add(wx, b)

    #计算loss
    loss = (0.5*(y - y_pred)**2).mean()

    #反向传播
    loss.backward()

    #更新参数
    b.data.sub_(lr*b.grad)
    w.data.sub_(lr*w.grad)

    #绘图
    if iteration%20 == 0:
        plt.scatter(x.data.numpy(),y.data.numpy())
        plt.plot(x.data.numpy(),y_pred.data.numpy(), 'r-', lw=5)
        plt.text(2,20,'Loss=%.4f'%loss.data.numpy(), fontdict={'size':20,'color':'red'})
        plt.xlim(1.5,10)
        plt.ylim(8,28)
        plt.title("Iteration:{}\nw:{} b:{}".format(iteration, w.data.numpy(), b.data.numpy()))
        plt.pause(0.5)
        # plt.show()

        if loss.data.numpy()<1:
            break

三、计算图与动态图机制

计算图

结点(Node):数据,如向量,矩阵,张量等
边(Edge):运算,加减乘除卷积等
叶子节点:用户创建的节点
is_leaf:指示张量是否是叶子节点
grad_fn:记录创建该张量时所用的方法,叶子节点None

import torch

w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)

a = torch.add(w, x)
a.retain_grad()#保存非叶子节点的梯度
b = torch.add(w, 1)
y = torch.mul(a,b)

y.backward()
print(w.grad)

#叶子节点
print("is_leaf:\n", w.is_leaf, x.is_leaf, a.is_leaf, b.is_leaf, y.is_leaf)

#查看梯度
print("gradient:\n",w.grad, x.grad, a.grad, b.grad, y.grad)
tensor([5.])
is_leaf:
 True True False False False
gradient:
 tensor([5.]) tensor([2.]) tensor([2.]) None None #a.retain_grad()保存非叶子节点的梯度
grad_fn:
 None None <AddBackward0 object at 0x0000019AA3A837C0> <AddBackward0 object at 0x0000019AA3B38E80> <MulBackward0 object at 0x0000019AA3B384F0> #w,x是用户创建的叶子节点

动态图 VS 静态图

运算与搭建同时 VS 先搭建,后运算

四、autograd

1 torch.gutograd.backward 反向传播

torch.gutograd.backward(tensors,#用于求导的张量,y,loss
						grad_tensors = None,#多梯度权重
						retain_graph = None,#默认计算图求导后自动释放,保存计算图
						create_graph = False)#创建导数计算图,用于高阶求导
#========================retain_graph===========================#						
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
a = torch.add(w, x)
b = torch.add(w, 1)
y.backward(retain_graph=True)#保存计算图
y.backward()
#========================gradient===========================#
y0 = torch.mul(a,b)
y1 = torch.add(a,b)
loss = torch.cat([y0, y1], dim=0)#loss是向量
grad_tensors = torch.tensor([1., 1.])#设计loss中y0和y1的权重
loss.backward(gradient=grad_tensors)#

2 torch.autograd.grad 求取梯度

torch.autograd.grad(outputs,
					inputs,
					grad_outputs=None,
					retain_graph=None,
					create_graph=False)
#========================create_graph===========================#
x = torch.tensor([3.], requires_grad=True)
y = torch.pow(x,2)

grad_1 = torch.autograd.grad(y,x,create_graph=True)#grad_1是元组
grad_2 = torch.autograd.grad(grad_1[0],x)

3 autograd的几个特点

  • 梯度不会自动清零,
#========================梯度不会自动清零===========================#
flag = True
# flag = False
if flag:
    w = torch.tensor([1.], requires_grad=True)
    x = torch.tensor([2.], requires_grad=True)

    for i in range(4):
        a = torch.add(w, x)
        b = torch.add(w, 1)
        y = torch.mul(a,b)

        y.backward()
        print(w.grad)
        #w.grad.zero_() :'_'表示in-place操作,对节点进行in-place操作,重置节点的值
tensor([5.])
tensor([10.])
tensor([15.])
tensor([20.])
  • 依赖于叶子结点的节点,requires_grad默认为True
  • 叶子节点不可执行in-place
    前向传播记录了叶子节点的地址,以供反向传播通过地址调用值。对叶子节点进行in-place操作会改变地址储存的值,导致求导出错

五、逻辑回归

线性二分类模型
y = f(WX+b)
f为sigmoid函数,也称Logistic函数

对数几率回归与线性回归

PyTorch Week 1

机器学习模型训练步骤

数据、模型、损失函数、优化器

# -*- coding: utf-8 -*-
"""
# @file name  : Logsitic-Regression-norm.py
# @author     : TingsongYu https://github.com/TingsongYu
# @date       : 2019-09-08 10:08:00
# @brief      :
"""
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import numpy as np
torch.manual_seed(10)

lr = 0.01  # 学习率

# 生成虚拟数据
sample_nums = 100#样本数量
mean_value = 1.7#均值
bias = 5         #偏差
n_data = torch.ones(sample_nums, 2)#全“1”矩阵,100个样本,每个样本有2个特征
x0 = torch.normal(mean_value * n_data, 1) + bias      # 类别0 数据 shape=(100, 2)
y0 = torch.zeros(sample_nums)                         # 类别0 标签 shape=(100, 1)
x1 = torch.normal(-mean_value * n_data, 1) + bias     # 类别1 数据 shape=(100, 2)
y1 = torch.ones(sample_nums)                          # 类别1 标签 shape=(100, 1)
train_x = torch.cat((x0, x1), 0)
train_y = torch.cat((y0, y1), 0)
#y0是一组围绕点a(mean_value+bias,mean_vale+bias)的数据
#y1是一组围绕点b(-mean_value+bias,-mean_vale+bias)的数据
#改变mean_value会改变a,b两点间的距离,使得两数据簇分布的更近或更远,距离相近的数据簇会难以得到较好的分类,分布较远的数据簇会得到较好的分类
#改变bias只会改变a,b在空间中的位置,不会改变a,b的相对位置,不会影响分类效果


# 定义模型
class LR(nn.Module):
    def __init__(self):
        super(LR, self).__init__()
        self.features = nn.Linear(2, 1)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        x = self.features(x)
        x = self.sigmoid(x)
        return x


lr_net = LR()

# 定义损失函数与优化器
loss_fn = nn.BCELoss()
optimizer = torch.optim.SGD(lr_net.parameters(), lr=0.01, momentum=0.9)

for iteration in range(1000):

    # 前向传播
    y_pred = lr_net(train_x)

    # 计算 MSE loss
    loss = loss_fn(y_pred, train_y.unsqueeze(1))
    """
    如果不加unsqueeze(1),会产生如下报错 ,可思考是为什么,也可查看官方BCELoss文档,或者等到第四周的课再研究
    Using a target size (torch.Size([200])) that is different to the input size (torch.Size([200, 1])) is deprecated. 
    Please ensure they have the same size.
    """

    # 反向传播
    loss.backward()

    # 更新参数
    optimizer.step()

    # 清空梯度
    optimizer.zero_grad()

    # 绘图
    if iteration % 40 == 0:
        plt.clf()

        mask = y_pred.ge(0.5).float().squeeze()  # 以0.5为阈值进行分类
        correct = (mask == train_y).sum()  # 计算正确预测的样本个数
        acc = correct.item() / train_y.size(0)  # 计算精度

        plt.scatter(x0.data.numpy()[:, 0], x0.data.numpy()[:, 1], c='r', label='class 0')
        plt.scatter(x1.data.numpy()[:, 0], x1.data.numpy()[:, 1], c='b', label='class 1')

        w0, w1 = lr_net.features.weight[0]
        w0, w1 = float(w0.item()), float(w1.item())
        plot_b = float(lr_net.features.bias[0].item())
        plot_x = np.arange(-6, 6, 0.1)
        plot_y = (-w0 * plot_x - plot_b) / w1

        plt.xlim(-5, 10)
        plt.ylim(-7, 10)
        plt.plot(plot_x, plot_y)

        plt.text(-5, 5, 'Loss=%.4f' % loss.data.numpy(), fontdict={'size': 20, 'color': 'red'})
        plt.title("Iteration: {}\nw0:{:.2f} w1:{:.2f} b: {:.2f} accuracy:{:.2%}".format(iteration, w0, w1, plot_b, acc))
        plt.legend()

        plt.show()
        plt.pause(0.5)

        if acc > 0.99:
            break

作业1

  1. 安装anaconda,pycharm, CUDA+CuDNN(可选),虚拟环境,pytorch,并实现hello pytorch查看pytorch的版本
    
  2. 张量与矩阵、向量、标量的关系是怎么样的?
    

都属于不同阶数的张量,标量、向量、矩阵分别是0阶、1阶、2阶张量

  1. Variable“赋予”张量什么功能?
    

由以下几个模块共同组成了自动求导功能
data:被包装的Tensor
requires_grad:是否需要求梯度,依赖于叶子节点的默认为True
grad:data的梯度
grad_fn:创建该节点使用的方法,函数
is_leaf:只是张量是否是叶子节点
叶子节点:用户创建的

  1. List item采用torch.from_numpy创建张量,并打印查看ndarray和张量数据的地址;
    

from_numpy会使得创建的Tensor包括多个属性,其中data属性指向ndarray的地址,其他属性储存在Tensor的地址中。

arr = np.array([[1,2,3],[4,5,6]])
t = torch.from_numpy(arr)
print(arr)
print(t)
print("id(t):{} id(arr):{}".format(id(t), id(arr)))
print(id(t)==id(arr))

arr[0,0] = 4
print(arr)
print(t)
print("id(t):{} id(arr):{}".format(id(t), id(arr)))
print(id(t) == id(arr))
[[1 2 3]
 [4 5 6]]
tensor([[1, 2, 3],
        [4, 5, 6]], dtype=torch.int32)
id(t):2162806604160 id(arr):2162710427376
False
[[4 2 3]
 [4 5 6]]
tensor([[4, 2, 3],
        [4, 5, 6]], dtype=torch.int32)
id(t):2162806604160 id(arr):2162710427376
False
  1. 实现torch.normal()创建张量的四种模式。
#mean标量, std标量
mean = torch.tensor(1.)
std = torch.tensor(1)
t = torch.normal(mean,std,(4,))
print(t)
# mean标量, std张量
mean = torch.tensor(1.)
std = torch.tensor([1, 2, 3, 4])
t = torch.normal(mean, std)
print(t)
# mean张量, std标量
mean = torch.tensor([1.,2.,3.,4.])
std = torch.tensor(1.)
t = torch.normal(mean, std)
print(t)
# mean标量, std标量
mean = torch.tensor([1.,2.,3.,4.])
std = torch.tensor([1, 2, 3, 4])
t = torch.normal(mean, std)
print(t)
tensor([0.6691, 2.3742, 0.3446, 0.6620])
tensor([2.7141, 2.5079, 0.3724, 5.7675])
tensor([1.4993, 3.2231, 4.4613, 5.4309])
tensor([-0.9941,  1.0040,  6.4833,  5.8631])

作业2

  1.  调整线性回归模型停止条件以及y = 2*x + (5 + torch.randn(20, 1))中的斜率,训练一个线性回归模型;
    

梯度下降可视化代码,非常清晰
2. 计算图的两个主要概念是什么?
节点和边,节点是数据,边是运算

  1.  动态图与静态图的区别是什么?
    

动态图是边构建计算图边计算,静态图是先构建完计算图再计算

作业3

  •  逻辑回归模型为什么可以进行二分类?
    

PyTorch Week 1

  • 线性回归解决的是回归问题;逻辑回归解决的是二分类问题。
  • 线性回归的目的是学习一个线性模型,使得预测值拟合真实值;逻辑回归就是用线性模型,使得预测的几率去拟合真实的几率。(概率是事情发生的次数/所有事情的次数,几率是事情发生的概率/事情不发生的概率。)
  • 逻辑回归又称对数几率回归,Logit = log it ,it值得是几率,it = y/(1-y)
  • 根据公式推导,对数几率回归最终化为sigmoid函数的形势,将x映射至(0,1),可用于分类。
    参考1
    参考2
  1.  采用代码实现逻辑回归模型的训练,并尝试调整数据生成中的mean_value,将mean_value设置为更小的值,例如1,或者更大的值,例如5,会出现什么情况?再尝试仅调整bias,将bias调为更大或者负数,模型训练过程是怎么样的?
    
  • 当mean_value=1时,两团数据簇距离更近,相互渗透,逻辑回归的分类效果变差;当mean_value=5时,数据簇距离更远,界线分明,逻辑回归的分类效果变好
  • bias决定了两团数据簇在全体数据空间中的绝对位置,不改变其相对位置。在不限制迭代次数,并不改变学习率的情况下,bias的改变不影响最终分类效果,但分类速度取决于参数的初始化值与bias的距离,两者距离越近,分类越快,距离越远,分类越慢。在限制迭代次数,并不改变学习率的情况下,最终分类效果取决于参数的初始化值与bias的距离,两者距离越近,分类效果越好,距离越远,分类效果越差。 通过sigmoid函数曲线理解,当bias非常大时,wx+b很大,导致 f(wx+b) 落在一个导数几乎为0的区间内,更新速度非常慢。从这个角度去考虑BN的作用,标准化数据使输入落在0周围的一个几乎线性的区间内,避免梯度爆炸或者梯度消失,有助于反向传播。
    PyTorch Week 1
上一篇:The Sixth Week


下一篇:python – Google App Engine的灯具