pytorch 常见函数
概率分布
torch.randn_like() 函数:返回一个与 input 形状相同的服从 N(0,1) 的张量
GPU 相关
查看 torch 的版本
torch.__version__
查看 cuda 是否 available:
torch.cuda.is_available()
查看 GPU 数量
torch.cuda.device_count()
数据读入
从文件中读取数据是必要的操作.
从文件中读取
input = open("D:/hw/chapter1/w4/input.txt", "r")
遍历数据每一行
import numpy as np import torch import torch.nn.functional as F input = open("D:/hw/chapter1/w4/input.txt", "r") # 遍历 input 数据中的每一行 for line in input: # 将 line 中的每一行都以 float 的形式读入. item = [float(i) for i in line.split()] b = torch.tensor(item) print(b)
判断字符串是否能转换成是数字
def is_number(s): try: float(s) return True except ValueError: return False
构建矩阵
import torch # 构建全 0 的 (5, 3) 矩阵 x = torch.empty(5, 3) # 构建随机数填充的 (5, 3) 矩阵 x = torch.rand(5, 3) # 定义矩阵类型为 long x = torch.zeros(5, 3, dtype=torch.long) # 直接定义 torch 矩阵 x = torch.tensor([5.5, 3]) # 覆盖 x, 且全部元素为 0 x = x.new_zeros(5, 3, dtype=torch.double) # 覆盖 x, 且全部元素为 1 x = x.new_ones(5, 3, dtype = torch.long) # 用随机数覆盖 x, 矩阵大小不发生变化. x = torch.randn_like(x, dtype=torch.float) print(x)
基本操作
这里是一些常见的对于 $\mathrm{tensor}$ 的操作,如果手写的话速度会很慢而且不方便.
防止类型转换错误
torch.set_default_tensor_type(torch.DoubleTensor)
赋值
# 将 y 赋值为 x y.copy_(x)
输出第 i 列 / 行
# 输出 x 的第 i 列/行 x = torch.rand(5, 3) print(x[:, 2]) # 输出 x 的第 3 列. print(x[2, :]) # 输出 x 的第 3 行.
改变尺寸
# 改变尺寸. x = torch.randn(4, 4) y = x.view(16) # y 为 x 平展的结果. z = x.view(2, -1) # 将 x 展为 (2, k) z = x.view(-1, 8) # 将 x 展为 (k, 8)
拼接
x = torch.ones(2, 3) z = torch.zeros(2, 3) y = torch.cat([x, z], dim = 0) y = torch.cat([x, z], dim = 1) print(y)
矩阵乘法
# 矩阵乘法:y1, y2, y3 获取的都是 x 与 x.T 的矩阵乘法. x = torch.ones(2, 3) y1 = x @ x.T y2 = x.matmul(x.T) y3 = torch.empty(1, 1) torch.matmul(x, x.T, out=y3)
矩阵对应元素相乘
# 两个矩阵对应位置元素的乘积. z = torch.ones(2, 3) z1 = z * z z2 = z.mul(z) z3 = torch.rand_like(z) torch.mul(z, z, out=z3) y = torch.ones(2, 3) # 具有广播功能. y.mul_(torch.tensor([[2], [233]])) # 带有 _ 结尾的运算会直接改变矩阵.
将 tensor 变量转为数值变量
# item(): 将 torch 变量转换为普通数值变量 h = torch.tensor([[1, 2], [3, 4]]) agg = h.sum() agg_item = agg.item()
矩阵每个位置乘/加数字
# 给矩阵的每个位置加一个数 / 乘一个数 z = torch.ones(2, 3) z.mul_(233) z.add_(-233)
numpy 和 torch 共享地址
# numpy 和 torch 可以共享存储地址(即同时进行改变) t = torch.ones(2, 3) n = t.numpy() t.mul_(233) # 这里 t 和 n 会被同时乘以 233
numpy 转为 tensor
# numpy 转为 tensor n = np.ones((1, 5)) t = torch.from_numpy(n) # t 就变成一个 tensor 了.
普通矩阵转为 tensor
# 将普通数据(矩阵)转为 tensor data = [[1, 2],[3, 4]] x_data = torch.tensor(data) print(x_data)
压缩数组与解压数组
dim = 0: 变为 (1, n)
dim = 1: 变为 (n, 1)
# 将 (1, n) 或 (n, 1) 压缩成 (n) x = torch.ones(1, 3) x = torch.squeeze(x) print(x.shape) y = torch.ones(3, 1) y = torch.squeeze(y) print(y.shape) # dim = 0 -> (1, n) # dim = 1 -> (n, 1) z = torch.ones(1, 5) z = torch.squeeze(z) z = torch.unsqueeze(z, dim = 1) print(z.shape)
交换/重排矩阵的维度
# 可以将 3 * (2, 3) 的矩阵转为 (2, 3) * 3 的矩阵. x = torch.tensor( [[[2, 2, 2], [2, 2, 2]], [[1, 1, 1], [1, 1, 1]], [[3, 3, 3], [3, 3, 3]]]) x = x.permute(1, 2, 0) for i in range(2) : for j in range(3): print(x[i][j][0], x[i][j][1], x[i][j][2])
将 numpy 数据保存到文件中
np.savez('e.npz', ep = iep, train_loss = itrain_loss, test_acc = itest_acc) e = np.load('e.npz') h = np.load('h.npz') ep = e['ep'] train_loss = e['train_loss'] test_acc = e['test_acc']
保存网络框架
torch.save(net, './net') net = torch.load('./net')
梯度计算
手写反向传播过程是深度学习中最困难的过程之一,极易出错而且不好写。
pytorch 威力最大之处就是提供自动计算梯度的功能,可以极大方便反向传播的构建。
追踪梯度
# requires_grad = True: 要求追踪 w 的梯度 # s.backward(): 由最后的变量向后传播. x = torch.ones(5) # input tensor y = torch.zeros(3) # expected output w = torch.randn(5, 3, requires_grad = True) b = torch.randn(3, requires_grad = True) z = torch.matmul(x, w) + b p = z.sum() p.backward() # print(w.grad) # 得到 w 中每一个元素相对于 p 的梯度.
将变量从计算图中移除
# 将 z 从计算图中移除掉. # 法一 z = torch.matmul(x, w)+b print(z.requires_grad) with torch.no_grad(): z = torch.matmul(x, w)+b print(z.requires_grad) # 发现 z 并不在计算图中了. # 法二 z = torch.matmul(x, w)+b z_det = z.detach() print(z_det.requires_grad) # z 从计算图中脱落了.
深度学习框架
$\mathrm{Pytorch}$ 提供了可以直接调用的深度学习框架,速度和准确率都很高.
损失函数
CrossEntropyLoss
交叉熵函数,这里注意训练样本不能用 one - hot 表示,要直接存入结果
参数初始化
高斯初始化
这里 $\mathrm{mean}$ 是正态分布均值,$\mathrm{std}$ 是正态分布标准差
torch.nn.init.normal_(self.hidden.weight, mean = 0, std = 1)
均匀分布
$\mathrm{a,b}$ 分别为均匀分布的下界和上界
torch.nn.init.uniform_(x, a = -100, b = 100)
Xavier均匀分布初始化
torch.nn.init.xavier_normal_(tensor, gain=1.0)
Xavier正态分布初始化
torch.nn.init.xavier_normal_(tensor, gain=1.0)
kaiming正态分布初始化
原则上只能配合 $\mathrm{relu}$ 函数使用
torch.nn.init.kaiming_normal_(tensor, a=0, mode='fan_in', nonlinearity='leaky_relu')
正则化
为了防止过拟合,可以加入 $\mathrm{L2}$ 正则化:
optimizer = torch.optim.Adam([{"params":net.hidden.weight, 'weight_decay': wd}], lr = 0.01)
框架默认是不进行正则化的,这里要正则化的矩阵是 $\mathrm{hidden}$ 矩阵,$\mathrm{lambda=wd}$.
回归问题
基本框架:
$\mathrm{super(Net, self)}$ 意味着继承 $\mathrm{torch.nn.Module}$ 中的属性并在起之上进行修改.
class Net(torch.nn.Module): def __init__(self, n_feature, n_hidden, n_out): super(Net, self).__init__() self.hidden = torch.nn.Linear(n_feature, n_hidden) self.predict = torch.nn.Linear(n_hidden, n_out) def forward(self, X): X = F.relu(self.hidden(X)) X = self.predict(X) return X
调用函数:
step = 2002 net = Net(n_feature = 1, n_hidden = 10, n_out = 1) # 构建神经网络 optimizer = torch.optim.SGD(net.parameters(), lr = 0.2) # 选择梯度下降算法和学习率 loss_func = torch.nn.MSELoss() # 选择损失函数种类 for st in range(1000): out = net(x) loss = loss_func(out, y) optimizer.zero_grad() # 梯度清空 loss.backward() # 开始反向传播 optimizer.step() # 按照梯度更新参数
CNN
基础知识
设原图为 $(n,n)$, 过滤器为 $(f,f)$, 填充为 $p$, 步长为 $s$
则有新图 $o = \left \lfloor \frac{n+2p-f}{s} \right \rfloor + 1$, 且一般过滤器长度为奇数.
流程:卷积 -> 激活函数激活 -> 池化 -> 下一轮卷积...... -> 平展为向量并连向全连接神经网络
基本框架
class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() self.conv1 = nn.Sequential( # input shape (209, 3, 64, 64) nn.Conv2d( in_channels = 3, out_channels = 5, kernel_size = 4, # filter size stride = 2, # filter step padding = 1, # 填充大小. ), # output shape (209, 5, 32, 32) nn.ReLU(), # activation nn.MaxPool2d(kernel_size = 2), # 在 2x2 空间里向下采样, output shape (5, 16, 16) ) self.conv2 = nn.Sequential( # input shape (209, 5, 16, 16) nn.Conv2d(5, 32, 5, 1, 0), # out shape (209, 32, 12, 12) nn.ReLU(), nn.MaxPool2d(2), # output shape (209, 32, 6, 6) ) self.out = nn.Linear(32 * 6 * 6, 2) # fully connected layer, output 2 classes def forward(self, x): x = self.conv1(x) x = self.conv2(x) x = x.view(x.size(0), -1) # 展平多维的卷积图成 (209, 32 * 6 * 6) output = self.out(x) return output
例:吴恩达第 3 次课程作业.
训练数据正确率达到 $99$% 的时候测试准确率达到了 $84$%,对于二维图片的识别率非常好.
import torch import numpy as np import torch.nn.functional as F import torch.nn as nn import torchvision from lr_utils import load_dataset torch.set_default_tensor_type(torch.DoubleTensor) def debug(a, b): print("训练次数 " + str(a) + ": " + str(b)) def trans_torch(a, b, c, d): return torch.from_numpy(a), torch.from_numpy(b), torch.from_numpy(c), torch.from_numpy(d) class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() self.conv1 = nn.Sequential( # input shape (209, 3, 64, 64) nn.Conv2d( in_channels = 3, out_channels = 5, kernel_size = 4, # filter size stride = 2, # filter step padding = 1, # 填充大小. ), # output shape (209, 5, 32, 32) nn.ReLU(), # activation nn.MaxPool2d(kernel_size = 2), # 在 2x2 空间里向下采样, output shape (5, 16, 16) ) self.conv2 = nn.Sequential( # input shape (209, 5, 16, 16) nn.Conv2d(5, 32, 5, 1, 0), # out shape (209, 32, 12, 12) nn.ReLU(), nn.MaxPool2d(2), # output shape (209, 32, 6, 6) ) self.out = nn.Linear(32 * 6 * 6, 2) # fully connected layer, output 2 classes def forward(self, x): x = self.conv1(x) x = self.conv2(x) x = x.view(x.size(0), -1) # 展平多维的卷积图成 (209, 32 * 6 * 6) output = self.out(x) return output X, Y, tx, ty , classes = load_dataset() X, Y, tx, ty = trans_torch(X, Y, tx, ty) X , tx = X.permute(0, 3, 1, 2) / 255, tx.permute(0, 3, 1, 2) / 255 Y = torch.squeeze(Y) ty = torch.squeeze(ty) # CNN 卷积神经网络: 达到了非常高的检测效果. cnn = CNN() optimizer = torch.optim.SGD(cnn.parameters(), lr = 0.03) loss_func = nn.CrossEntropyLoss() step = 4000 for i in range(step): output = cnn(X) loss = loss_func(output, Y) optimizer.zero_grad() loss.backward() optimizer.step() if i % 100 == 0: cc = 0 for j in range(209): cur = 0 if output[j][1] > output[j][0]: cur = 1 if cur == Y[j]: cc += 1 debug(i + 1, cc / 209 * 100) cc = 0 outt = cnn(tx) for j in range(50): cur = 0 if outt[j][1] > outt[j][0]: cur = 1 if cur == ty[j]: cc += 1 print("测试数据 " + str(i + 1) + ": " + str(cc * 2))