2017年1月,FAIR (FacebookAI Research) 发布PyTorch,PyTorch是在Torch基础上用python语言重新打造的一款深度学习框架,Torch 是采用Lua语言为接口的机器学习框架,但因Lua语言较为小众导致Torch知名度不高。
张量是一个多维数组,它是标量、向量、矩阵的高维拓展
在深度学习中,张量(tensor)是一个广泛使用的数学和计算工具,它是多维数组的泛化。以下是对深度学习中张量的一些解释:
1.数据结构: 张量是一个多维数组,可以是一个标量(0维张量,即一个数)、向量(1维张量,例如一行或一列数字)、矩阵(2维张量,例如一个表格或图像)、或者更高维度的数组。
2.Rank(秩): 张量的秩表示张量的维度数量。例如,标量的秩是0,向量的秩是1,矩阵的秩是2。通常,深度学习中的张量秩是可以很大的,因为神经网络中的数据通常是高维的。
3.形状: 张量的形状描述了它每个维度上的大小。例如,一个形状为 (3, 4) 的张量表示一个 3 行 4 列的矩阵。
4.类型: 张量可以包含不同类型的数据,例如整数、浮点数等。在深度学习中,通常使用浮点数张量。
5.操作: 张量上可以进行各种数学运算,如加法、减法、乘法等。这些操作是深度学习中神经网络的基础。
6.自动微分: 在深度学习中,张量通常与自动微分结合使用。自动微分是通过计算图和链式法则来计算梯度,用于训练神经网络。
7.存储和计算优化: 张量在内存中的存储方式通常是连续的,这有助于在硬件上进行高效的计算。深度学习框架使用张量来表示神经网络的参数和输入输出。
8.GPU 加速: 张量的并行性和规则结构使得深度学习中的许多计算可以受益于 GPU 的并行计算能力。因此,深度学习框架通常支持在 GPU 上进行张量操作。
在常见的深度学习框架(如 TensorFlow、PyTorch等)中,张量是核心数据结构,它们提供了丰富的操作和函数来处理张量,支持自动微分、梯度下降等算法,使得深度学习模型的实现更加方便和高效。
Variable是torch.autograd中的数据类型主要用于封装Tensor,进行自动求导
data: 被包装的Tensor
grad: data的梯度
grad fn: 创建Tensor的Function,是自动求导的关键
requires_grad: 指示是否需要梯度
is leaf: 指示是否是叶子结点 (张量)
torch.tensor()
功能:从data创建tensor
? data: 数据, 可以是list, numpy
? dtype : 数据类型,默认与data的一致
? device : 所在设备, cuda/cpu
? requires_grad:是否需要梯度
? pin_memory:是否存于锁页内存
torch.from_numpy(ndarray)
注意事项: 从torch.from_numpy创建的tensor于原ndarray共享内存,当修改其中一个的数据,另外一个也将会被改动
功能:依size创建全0张量
? size: 张量的形状, 如(3, 3)、(3, 224,224)
? out : 输出的张量
? layout : 内存中布局形式, 有strided,sparse_coo等
? device : 所在设备, gpu/cpu
? requires_grad:是否需要梯度
功能:依input形状创建全0张量
? intput: 创建与input同形状的全0张量
? dtype : 数据类型
? layout : 内存中布局形式
功能:依input形状创建全1张量
? size: 张量的形状, 如(3, 3)、(3, 224,224)
? dtype : 数据类型
? layout : 内存中布局形式
? device : 所在设备, gpu/cpu
? requires_grad:是否需要梯度
功能:依input形状创建指定数据的张量
? size: 张量的形状, 如(3, 3)
? fill_value : 张量的值
功能:创建等差的1维张量
注意事项:数值区间为[start, end)
? start : 数列起始值
? end : 数列“结束值”
? step: 数列公差,默认为1
功能:创建均分的1维张量
注意事项:数值区间为[start, end]
? start : 数列起始值
? end : 数列结束值
? steps: 数列长度
功能:创建对数均分的1维张量
注意事项:长度为steps, 底为base
? start : 数列起始值
? end : 数列结束值
? steps: 数列长度
? base : 对数函数的底,默认为10
功能:创建单位对角矩阵( 2维张量)
注意事项:默认为方阵
? n: 矩阵行数
? m : 矩阵列数
功能:生成正态分布(高斯分布)
? mean : 均值
? std : 标准差
功能:生成标准正态分布
功能:在区间[0, 1)上,生成均匀分布
? size : 张量的形状
功能:区间[low, high)生成整数均匀分布
? size : 张量的形状
功能:生成生成从0到n-1的随机排列
? n : 张量的长度
功能:以input为概率,生成伯努力分布(0-1分布,两点分布)
? input : 概率值
功能:将张量按维度dim进行拼接
? tensors: 张量序列
? dim : 要拼接的维度
功能:在新创建的维度dim上进行拼接
? tensors:张量序列
? dim :要拼接的维度
功能:将张量按维度dim进行平均切分
返回值:张量列表
注意事项:若不能整除,最后一份张量小于其他张量
? input: 要切分的张量
? chunks : 要切分的份数
? dim : 要切分的维度
功能:将张量按维度dim进行切分
返回值:张量列表
? tensor: 要切分的张量
? split_size_or_sections : 为int时,表示每一份的长度;为list时,按list元素切分
? dim : 要切分的维度
功能:在维度dim上,按index索引数据
返回值:依index索引数据拼接的张量
? input: 要索引的张量
? dim: 要索引的维度
? index : 要索引数据的序号
功能:按mask中的True进行索引
返回值:一维张量
? input: 要索引的张量
? mask: 与input同形状的布尔类型张量
功能:变换张量形状
注意事项:当张量在内存中是连续时,新张量与input共享数据内存
? input: 要变换的张量
? shape: 新张量的形状
功能:交换张量的两个维度
? input: 要变换的张量
? dim0: 要交换的维度
? dim1: 要交换的维度
功能:2维张量转置,对矩阵而言,等价于torch.transpose(input, 0, 1)
功能:压缩长度为1的维度(轴)
? dim: 若为None,移除所有长度为1的轴;若指定维度,当且仅当该轴长度为1时,可以被移除;
功能:依据dim扩展维度
? dim: 扩展的维度
功能:逐元素计算 input+alpha×other
? input: 第一个张量
? alpha: 乘项因子
? other: 第二个张量
# -*- coding:utf-8 -*-
import torch
import matplotlib.pyplot as plt
torch.manual_seed(10)
lr = 0.05 # 学习率
# 创建训练数据
x = torch.rand(20, 1) * 10 # x data (tensor), shape=(20, 1)
y = 2 * x + (5 + torch.randn(20, 1)) # y data (tensor), shape=(20, 1)
# 构建线性回归参数
w = torch.randn((1), requires_grad=True)
b = torch.zeros((1), requires_grad=True)
for iteration in range(1000):
# 前向传播 y=wx+b
wx = torch.mul(w, x)
y_pred = torch.add(wx, b)
# 计算 MSE loss
loss = (0.5 * (y - y_pred) ** 2).mean()
# 损失反向传播来得到梯度grad
loss.backward()
# 更新参数
b.data.sub_(lr * b.grad) # sub_:原地减法操作
w.data.sub_(lr * w.grad)
# 清零张量的梯度
w.grad.zero_()
b.grad.zero_()
# 绘图
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)
# 设置一个终止条件,当loss小于1的时候停止更新
if loss.data.numpy() < 1:
break
结果展示:
计算图是用来描述运算的有向无环图计算图有两个主要元素:结点(Node)和边(Edge)结点表示数据,如向量,矩阵,张量边表示运算,如加减乘除卷积等用计算图表示:y = (x+ w) * (w+1);a = x + w ;b = w + 1 ;y = a * b
求导流程如下:
叶子结点:用户创建的结点称为叶子结点,如X 与 W;在torch中有如下图属性,is_leaf: 指示张量是否为叶子结点。
grad_fn: 记录创建该张量时所用的方法(函数)
当x,w使用torch方法创建之后,该属性会保留grad属性,a、b、y都是通过x,w计算得到的,在反向传播之后就会释放梯度,减少内存开销。
import torch
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
a = torch.add(w, x) # 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)
输出结果:
tensor([5.])
is_leaf:
True True False False False
# -*- coding:utf-8 -*-
import torch
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
a = torch.add(w, x) # retain_grad()
a.retain_grad()
b = torch.add(w, 1)
b.retain_grad()
y = torch.mul(a, b)
y.backward()
print(w.grad)
print("gradient:\n", w.grad, x.grad, a.grad, b.grad, y.grad)
输出结果:
tensor([5.])
gradient:
tensor([5.]) tensor([2.]) tensor([2.]) tensor([3.]) None
注意:a,b,y非叶子结点,所以反向传播之后会清除梯度,所以使用a.grad方法结果是none,需要在中间使用a.retain_grad()方法将a的梯度保留下来。
import torch
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
a = torch.add(w, x) # retain_grad()
# a.retain_grad()
b = torch.add(w, 1)
# b.retain_grad()
y = torch.mul(a, b)
y.backward()
print(w.grad)
# 查看 grad_fn
print("grad_fn:\n", w.grad_fn, x.grad_fn, a.grad_fn, b.grad_fn, y.grad_fn)
输出结果:
tensor([5.])
grad_fn:
None None <AddBackward0 object at 0x00000217F70B1330> <AddBackward0 object at 0x00000217F70B1300> <MulBackward0 object at 0x00000217F70B13F0>
从上面结果可以看出,a和b的梯度计算使用的是加法,y梯度使用的是乘法。
为什么近几年TensorFlow逐渐被淘汰了,因为TensorFlow使用的任然是静态图,先搭建网络后进行运算,这样会导致效率低下;
功能:自动求取梯度
? tensors: 用于求导的张量,如 loss
? retain_graph : 保存计算图
? create_graph : 创建导数计算图,用于高阶
求导
? grad_tensors:多梯度权重
功能:求取梯度
? outputs: 用于求导的张量,如 loss
? inputs : 需要梯度的张量
? create_graph : 创建导数计算图,用于高阶
求导
? retain_graph : 保存计算图
? grad_outputs:多梯度权重
逻辑回归是线性的二分类模型,模型表达式和函数图像如下:
线性回归是分析自变量x与因变量y(标量)之间关系的方法
逻辑回归是分析自变量x与因变量y(概率)之间关系的方法
# -*- coding: utf-8 -*-
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import numpy as np
torch.manual_seed(10)
# ============================ step 1/5 生成数据 ============================
sample_nums = 100
mean_value = 1.7
bias = 1
n_data = torch.ones(sample_nums, 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) # 在0维进行拼接
train_y = torch.cat((y0, y1), 0)
# ============================ step 2/5 选择模型 ============================
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() # 实例化逻辑回归模型
# ============================ step 3/5 选择损失函数 ============================
loss_fn = nn.BCELoss() # 二分类交叉熵损失函数
# ============================ step 4/5 选择优化器 ============================
lr = 0.01 # 学习率
optimizer = torch.optim.SGD(lr_net.parameters(), lr=lr, momentum=0.9)
# ============================ step 5/5 模型训练 ============================
for iteration in range(1000):
# 前向传播
y_pred = lr_net(train_x)
# 计算 loss
loss = loss_fn(y_pred.squeeze(), train_y)
# 反向传播
loss.backward()
# 更新参数
optimizer.step()
# 清空梯度
optimizer.zero_grad()
# 绘图
if iteration % 20 == 0:
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, 7)
plt.ylim(-7, 7)
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
运行结果:380次迭代之后准确率达到99.5%