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函数
对数几率回归与线性回归
机器学习模型训练步骤
数据、模型、损失函数、优化器
# -*- 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
-
安装anaconda,pycharm, CUDA+CuDNN(可选),虚拟环境,pytorch,并实现hello pytorch查看pytorch的版本
-
张量与矩阵、向量、标量的关系是怎么样的?
都属于不同阶数的张量,标量、向量、矩阵分别是0阶、1阶、2阶张量
-
Variable“赋予”张量什么功能?
由以下几个模块共同组成了自动求导功能
data:被包装的Tensor
requires_grad:是否需要求梯度,依赖于叶子节点的默认为True
grad:data的梯度
grad_fn:创建该节点使用的方法,函数
is_leaf:只是张量是否是叶子节点
叶子节点:用户创建的
-
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
- 实现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
-
调整线性回归模型停止条件以及y = 2*x + (5 + torch.randn(20, 1))中的斜率,训练一个线性回归模型;
梯度下降可视化代码,非常清晰
2. 计算图的两个主要概念是什么?
节点和边,节点是数据,边是运算
-
动态图与静态图的区别是什么?
动态图是边构建计算图边计算,静态图是先构建完计算图再计算
作业3
-
逻辑回归模型为什么可以进行二分类?
- 线性回归解决的是回归问题;逻辑回归解决的是二分类问题。
- 线性回归的目的是学习一个线性模型,使得预测值拟合真实值;逻辑回归就是用线性模型,使得预测的几率去拟合真实的几率。(概率是事情发生的次数/所有事情的次数,几率是事情发生的概率/事情不发生的概率。)
- 逻辑回归又称对数几率回归,Logit = log it ,it值得是几率,it = y/(1-y)
- 根据公式推导,对数几率回归最终化为sigmoid函数的形势,将x映射至(0,1),可用于分类。
参考1
参考2
-
采用代码实现逻辑回归模型的训练,并尝试调整数据生成中的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周围的一个几乎线性的区间内,避免梯度爆炸或者梯度消失,有助于反向传播。