Pytorch基础
Pytorch官网在线文档
Torch 类似于意义上的TensorFlow中的Tensor,可以看做是能在GPU计算中的矩阵;
熟悉numpy也可以理解为ndarray的GPU版;
在使用深度学习框架后,我们需要做的是设计任务流程和网络框架;
安装Pytorch
CPU版本:
pip install torch1.3.0 cpu torchvision0.4.1 cpu -f https://download.pytorch.org/whl/torch_stable.html
GPU版本:
pip install torch1.3.0 torchvision0.4.1 -f https://download.pytorch.org/whl/torch_stable.html (默认是CUDA10版本)
具体安装可查看官网,视频版本为1.3.实际上,我的机器安装了1.7.0版本(应安装)GPU版本没有专门安装CPU版本命令),主要是为了学习pytorch的使用;
安装相应的版本CUDA
CUDA:Nvidia显卡的GPU加速库,下载链接
基础操作
import torch torch.__version__ torch.version.cuda torch.cuda.is_available()
'1.7.0'
!pip show torch
Name: torch Version: 1.7.0 Summary: Tensors and Dynamic neural networks in Python with label GPU acceleration Home-page: https://pytorch.org/ Author: PyTorch Team Author-email: packages@pytorch.org License: BSD-3 Location: /Users/huaqiang/anaconda3/lib/python3.7/site-packages Requires: future, numpy, typing-extensions, dataclasses Required-by: torchvision, torchaudio, pytorch-pretrained-bert
我们注意到当地依赖torch的库:torchvision, torchaudio, pytorch-pretrained-bert
# 创建矩阵 x = torch.empty(5, 3) x # 类型为tensor,即张量 # 转换数据类型 x.to(torch.float)
tensor([[1.1210e-44, 0.0000e 00, 0.0000e 00], [0.0000e 00, 0.0000e 00, 0.0000e 00], [0.0000e 00, 0.0000e 00, 0.0000e 00], [0.0000e 00, 0.0000e 00, 0.0000e 00], [0.0000e 00, 0.0000e 00, 0.0000e 00]])
# 随机生成矩阵 torch.rand(5,3)
tensor([[0.4939, 0.6952, 0.3724], [0.3051, 0.1697, 0.6733], [0.2311, 0.2673, 0.2252], [0.0205, 0.5017, 0.8799], [0.6741, 0.4258, 0.1572]])
# 全0矩阵 torch.zeros(5,3,dtype=torch.long)
tensor([[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]])
# 使用多种方法和方法numpy很类似 torch.randn_like(x, dtype=torch.float)
tensor([[-0.7219, -1.0058, 0.1401], [-0.1806, -0.3656, 0.8092], [ 0.8398, 0.2060, 0.9734], [-0.1092, 0.4415, -0.0103], [-0.6904, -1.5415, 0.1186]])
torch.randn_like?
# 检查矩阵大小 x.size()
torch.Size([5, 3])
# 基本运算
x + x
torch.add(x,x)
tensor([[2.2421e-44, 0.0000e+00, 0.0000e+00],
[0.0000e+00, 0.0000e+00, 0.0000e+00],
[0.0000e+00, 0.0000e+00, 0.0000e+00],
[0.0000e+00, 0.0000e+00, 0.0000e+00],
[0.0000e+00, 0.0000e+00, 0.0000e+00]])
# 索引和切片
x[:, 1]
tensor([0., 0., 0., 0., 0.])
# 常用方法
# view:改变矩阵维度
x = torch.randn(4,4)
y = x.view(16)
z = x.view(-1, 8)
z
tensor([[ 0.2263, -0.2230, -0.1979, 1.1429, -0.6950, 0.2761, -0.1115, -0.1601],
[ 0.5172, 0.3535, 1.6254, 0.2054, 0.5812, 0.3431, 0.1358, -1.4275]])
# 与numpy的协同操作
a = torch.ones(5)
a
tensor([1., 1., 1., 1., 1.])
b = a.numpy()
b
array([1., 1., 1., 1., 1.], dtype=float32)
c = torch.from_numpy(b)
c
tensor([1., 1., 1., 1., 1.])
自动求导机制
反向传播算法
import torch
# 指定求导张量:方法1
x = torch.randn(3,4,requires_grad=True)
# 指定求导张量:方法2
y = torch.randn(3,4)
y.requires_grad = True
# 定义算式过程
t = x + y
t = t.sum()
# 调用反向传播
t.backward()
# 查看 梯度(自动计算求导)
# t.retain_grad
t.grad
<bound method Tensor.retain_grad of tensor(-3.9155, grad_fn=<SumBackward0>)>
计算流程举例
y = w * x
z = y + b
# 用z对x求偏导 = 用z对y求偏导 * 用y对x求偏导
x = torch.rand(1)
x
tensor([0.6581])
b = torch.rand(1, requires_grad=True)
w = torch.rand(1, requires_grad=True)
y = w * x
z = y + b
z
tensor([1.3465], grad_fn=<AddBackward0>)
# 查看是否需要计算梯度
x.requires_grad,b.requires_grad,w.requires_grad, y.requires_grad
(False, True, True, True)
# 查看是否是叶节点(不重要)
x.is_leaf, b.is_leaf, w.is_leaf, y.is_leaf, z.is_leaf
(True, True, True, False, False)
# 反向传播:梯度清0 如果不清空 梯度会累加
z.backward(retain_graph=True)
# 计算梯度
w.grad
tensor([0.6581])
b.grad # 反向传播中 如果梯度不清零 多次计算梯度的结果会累加
tensor([1.])
# 清零
# 计算
# 更新
线性回归Demo
import torch
import torch.nn as nn
import numpy as np
x_values = [i for i in range(11)]
x_train = np.array(x_values, dtype=np.float32)
x_train = x_train.reshape(-1,1)
x_train.shape
(11, 1)
y_values = [2*i + 1 for i in x_values]
y_train = np.array(y_values, dtype=np.float32)
y_train = y_train.reshape(-1,1)
y_train.shape
(11, 1)
- 线性回归模型:就是一个不加激活函数的全连接层
class LinearRegressionModel(nn.Module):
""" 线性回归模型 """
def __init__(self, input_dim, output_dim):
super(LinearRegressionModel, self).__init__()
# 全连接层
self.linear = nn.Linear(input_dim, output_dim)
def forward(self, x):
""" 前向传播过程 """
out = self.linear(x)
return out
input_dim = 1
output_dim = 1
# 一元一次方程 输入输出参数维度都是1
model = LinearRegressionModel(input_dim, output_dim)
- 指定参数 优化器 和损失函数
epochs = 1000
learning_rate = 0.01
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate) # 指定要优化的模型参数和学习率
criterion = nn.MSELoss() # 回归的损失函数 使用均方差
- 训练模型
for epoch in range(epochs):
# 转张量
inputs = torch.from_numpy(x_train)
labels = torch.from_numpy(y_train)
# 梯度清零(每一次迭代都要做)
optimizer.zero_grad()
# 前向传播
outputs = model(inputs)
# 计算损失
loss = criterion(outputs, labels)
# 反向传播
loss.backward()
# 更新权重参数
optimizer.step()
if (epoch % 50) == 0:
print("epoch {}, loss {}".format(epoch, loss.item()))
epoch 0, loss 1.5593505653388462e-11
epoch 50, loss 1.5593505653388462e-11
epoch 100, loss 1.5593505653388462e-11
epoch 150, loss 1.5593505653388462e-11
epoch 200, loss 1.5593505653388462e-11
epoch 250, loss 1.5593505653388462e-11
epoch 300, loss 1.5593505653388462e-11
epoch 350, loss 1.5593505653388462e-11
epoch 400, loss 1.5593505653388462e-11
epoch 450, loss 1.5593505653388462e-11
epoch 500, loss 1.5593505653388462e-11
epoch 550, loss 1.5593505653388462e-11
epoch 600, loss 1.5593505653388462e-11
epoch 650, loss 1.5593505653388462e-11
epoch 700, loss 1.5593505653388462e-11
epoch 750, loss 1.5593505653388462e-11
epoch 800, loss 1.5593505653388462e-11
epoch 850, loss 1.5593505653388462e-11
epoch 900, loss 1.5593505653388462e-11
epoch 950, loss 1.5593505653388462e-11
- 模型测试 进行预测
# 测试数据 转张量 取消梯度
# 支持数据集的批量预测
predicted = model(torch.from_numpy(x_train).requires_grad_(False))
# 结果转为numpy
predicted = predicted.data.numpy()
# 打印
predicted
array([[ 0.99999267],
[ 2.9999938 ],
[ 4.999995 ],
[ 6.9999967 ],
[ 8.999997 ],
[10.999998 ],
[13. ],
[15.000001 ],
[17.000002 ],
[19.000004 ],
[21.000004 ]], dtype=float32)
- 模型的保存和读取
torch.save(model.state_dict(), './model.pkl') # 使用 .pkl .pth 后缀均可
model.load_state_dict(torch.load('./model.pkl'))
<All keys matched successfully>
使用GPU进行训练
只需要把数据
和模型
传入到cuda
里面就可以了
注意:只要想用GPU做训练,model和输入张量 就需要 .to(device)
下,用cpu基本可以忽略!
train_on_gpu = torch.cuda.is_available()
if not train_on_gpu:
print('CUDA is not available, use CPU')
else:
print('CUDA is available, use GPU')
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 模型
model.to(device)
LinearRegressionModel(
(linear): Linear(in_features=1, out_features=1, bias=True)
)
# 数据
# ...
for epoch in range(epochs):
# 转张量
inputs = torch.from_numpy(x_train).to(device)
labels = torch.from_numpy(y_train).to(device)
# ...
这是一个最简单的结果,后续的会进行延伸
常见tensor形式
- scalar 数值
- vector 向量
- matrix 矩阵
- n-dimensional tensor 高维
from torch import tensor
x = tensor(100) # scalar
x
tensor(100)
x = tensor([1,2,3,4,5,6]) # vector
x.dim(), x.size()
(1, torch.Size([6]))
x = tensor([[1,2],[3,4]]) # matrix
x.dim(), x.size()
(2, torch.Size([2, 2]))
# 矩阵运算
x.matmul(x) # 矩阵相乘:行列 * 列行 = 行行
x*x # 矩阵内积:元素相乘(逐个对应)
tensor([[ 1, 4],
[ 9, 16]])
x = tensor([[[1,2],[3,4]],[[1,2],[3,4]]]) # 3-dimensional tensor
x.dim(), x.size()
(3, torch.Size([2, 2, 2]))
强大的hub模块
调用集成可用的网络模型架构及参数 直接使用;
比如 基于ResNet进行了图形分割 获取到的一个 同簇下的模型,就可以使用hub模块进行加载,然后直接使用;
import torch
# 实例1
model = torch.hub.load("pytorch/vision:v:0.4.2", "deeplabv3_resnet101", pretrained=True)
使用Pytorch训练模型,一定要注意一个细节:有时候明明训练很好,测试时候出问题? 这时候我们要找一下Model里是否有BN或者 Dropout层,如果存在了,那就要小心了!!! 测试之前加入下面这句话!!!! 注意为了排除BN和Dropout对测试影响
model = model.eval()
- 一般在训练模型时 加上 model.train(),这样可以正常使用 Batch Normalization 和 Dropout
- 测试的时候一般使用 model.eval(),这样就不会使用 Batch Normalization 和 Dropout
# 实例2
# 具体怎么用 请参考官网 这里只是一个使用示例
# 要注意 resnet默认的卷积核是3*3 也就是说 对应的是图片的3通道 如果要修改卷积核大小 可以下载源码然后自定义
model = torch.hub.load('pytroch/vision:v0.4.2', 'resnet18', pretrained = True)
# 查看指定版本所支持的模型
torch.hub.list("pytorch/vision:v1.7.0")
预处理过程组合举例
from torchvision import transforms
preprocess = transforms.Compose([
transforms.Normalize(mean=[0.485,0.456,0.406], std=[0.229,0.224,0.225]),
transforms.ToTensor(),
lambda x: torch.tensor(x), # or
])
Pytorch神经网 进行气温预测
# 读取数据
features = pd.read_csv('temps.csv') # 数据来源未知 仅仅是个示例
features.head()
# 数据可视化 import matplotlib.pyplot as plt plt.style.use('fivethirtyeight') # 设置布局 fig, ((ax1,ax2),(ax3,ax4)) = plt.subplots(nrows=2, ncols=2, figsize=(10,10)) fig.autofmt_xdate(rotation = 45) #
标签值 ax1.plot(dates, features['temp_1']) # 这里 dates 是一个 datetime.datetime类型的 Series ax1.set_xlabel('') ax1.set_ylabel('Temp') ax1.set_title('temp_1') ... plt.tight_layout(pad=2)
# 实质性数据 量化处理:one-hot编码
features = pd.get_dummies(features) # 会自动将 实质性数据(即分类数据)进行one-hot编码 有几个类就会加几列;
# 标签
labels = np.array(features['actual'])
# 特征
features = features.drop('actual', axis=1)
features = np.array(features)
数据特征工程
这是一个专门的领域,这里仅做简单的标准化处理
关于归一化:
- 归一化后加快了梯度下降求最优解的速度
- 归一化有可能提高精度
标准差标准化(standardScale)使得经过处理的数据符合标准正态分布,即均值为0,标准差为1(减均值除方差)
注意:会对每个特征都进行处理
from sklearn import preprocessing
# 预处理模块的标准化处理
scaler = preprocessing.StandardScaler()
input_features = scaler.fit_transform(features)
相对复杂的一种构建方式
# 尝试构建网络模型
# 特征和标签 转张量
x = torch.tensor(input_features, dtype=torch.float)
y = torch.tensor(labels, dtype=torch.float)
# 权重参数初始化
w1 = torch.randn((14,128), dtype=float, requires_grad=True) # 输入输出节点数 即对应权重参数数
b1 = torch.randn(128, dtype=float, requires_grad=True) # 偏置参数个数 与 输出节点数同
w2 = torch.randn((128,1), dtype=float, requires_grad=True)
b2 = torch.randn(1, dtype=float, requires_grad=True)
learning_rate = 0.01
losses = []
for i in range(1000):
# 前向传播
# 计算隐藏层
hidden1 = x.mm(w1) + b1
# 激活函数
hidden1 = torch.relu(hidden1)
# 计算输出层(输出层 一般不加激活函数)
predictions = hidden.mm(w2) + b2
# 计算损失
loss = torch.mean((predictions - y) ** 2)
losses.append(loss.data.numpy())
if i%100 == 0:
print('loss ', loss)
# 反向传播:计算更新后w和b的梯度值
loss.backward()
# 更新参数:使用计算后的梯度值进行更新
w1.data.add_(- learning_rate * w1.grad.data) # 沿着梯度反方向 更新 即 梯度下降!
b1.data.add_(- learning_rate * b1.grad.data)
w2.data.add_(- learning_rate * w2.grad.data)
b1.data.add_(- learning_rate * b2.grad.data)
# 每次迭代将梯度清空
w1.grad.data.zero_()
b1.grad.data.zero_()
w2.grad.data.zero_()
b2.grad.data.zero_()
更简单的构建方式
input_size = input_features.shape[1] # 14
hidden_size = 128
output_size = 1
batch_size = 16
my_nn = torch.nn.Sequential(
torch.nn.Linear(input_size, hidden_size), # 14 128
torch.nn.Sigmoid(),
torch.nn.Linear(hidden_size, output_size) # 128 1
)
cost = torch.nn.MSELoss(reduction='mean')
# Adam 支持动态衰减 学习率
optimizer = torch.optim.Adam(my_nn.parameters(), lr = 0.001)
losses = []
for i in range(1000):
batch_loss = []
for start in range(0, len(input_features), batch_size):
end = start + batch_size if start + batch_size < len(input_features) else len(input_features)
xx = torch.tensor(input_features[start:end], dtype=torch.float, requires_grad=True)
yy = torch.tensor(labels[start:end], dtype=torch.float, requires_grad=True)
prediction = my_nn(xx)
# 计算损失
loss = cost(prediction, yy)
# 梯度清零
optimizer.zero_grad()
# 反向传播
loss.backward(retain_graph=True)
# 更新权重
optimizer.step()
batch_loss.append(loss.data.numpy())
losses.append(np.mean(batch_loss))
对retain_graph参数的理解
# 假如你有两个Loss,先执行第一个的backward,再执行第二个backward
loss1.backward(retain_graph=True)
loss2.backward() # 执行完这个后,所有中间变量都会被释放,以便下一次的循环
optimizer.step() # 更新参数
预测结果的可视化举例
predictions_data = pd.DataFrame(data={
'date': test_dates, 'predictions':predict.reshape(-1)})
# 真实值
plt.plot(true_data['date'], true_data['actual'], 'b-', label='actual')
# 预测值
plt.plot(predictions_data['date'], predictions_data[<