PyTorch, Numpy常用函数、功能小结(含hook笔记)

PyTorch常用函数、功能小结

张量

创建

torch.from_numpy(np_array) # 将numpy array转换为张量
torch.zeros_like(input_tensor) # 创建一个除了data不同其他基本一致的零张量 
alpha_tensor = torch.tensor(self.alpha, device=self.device)

默认张量require_grad=False

维度

Understanding dimensions in PyTorch - https://towardsdatascience.com/understanding-dimensions-in-pytorch-6edf9972d3be

变形

a.t() // 矩阵转置
torch.transpose(mat, dim1, dim2) # 张量转置
torch.concat((a, b), 1) # 在列上拼接a, b张量
torch.squeeze(x, dim) # 增加维度
torch.unsqueeze(x, dim) # 此处dim 和x.dim()对应, with a dimension of size one inserted at the specified position
numpy.vstack([A, newrow])
pad_sequence(tensor_list)  

运算

torch.matmul(a, b) // 张量乘法
torch.mm(a,b) # 矩阵乘法(无broadcasting)
torch.dot(a, b) # 张量点乘
torch.div(a, b) # 张量element-wise 除法
torch.mean(sentence_embeddings, 0) 
a*b // 点乘

选择

a[rows]   # 选择行
  • 如果发现奇怪的选择值,需要检查index selector的数据类型。比如CUDA Tensor需要转换为int。

排序

max_values, argsorts = torch.sort(pred1, dim=1, descending=True) // 类似numpy.argsort
x.argsort()  # torch.sort的第二个返回值

其他常用函数

F.normalize(input, p, dim)

CUDA计算相关

self.projector.to(torch.device("cuda:" + str(device))) # 
t.detach().cpu().numpy() //将CUDA张量转换为numpy


CUDA-Out-of memory
  • 最直接的方法是减少batch_size
  • 查看GPU使用情况:$ nvidia-smi
  • 删除张量并释放显存
    for x in batch_dict: // batch_dict contains CUDA tensors 
        del x
    torch.cuda.empty_cache()
相关资料

https://zhuanlan.zhihu.com/p/85838274

模型

梯度传播

  • tensor.detach()返回一个新的tensor,从当前计算图中分离下来的,但是仍指向原变量的存放位置,不同之处只是requires_grad为false,得到的这个tensor永远不需要计算其梯度,不具有grad。
  • 当优化器包含模型内所有参数时, model.zero_grad() 等价于optim.zero_grad()

模型的基本组成

  • submodule
    Module可以包含其他多个module对象,submodule可以通过一般的属性赋值创建:
class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.conv1 = nn.Conv2d(1, 20, 5)
        self.conv2 = nn.Conv2d(20, 20, 5)
        
# 第二种方式:self.add_module()
class Net(nn.Module):
    def __init__(self):
    super(Net, self).__init__()
    self.add_module("conv1", Conv2d(3, 16, 5, padding=2))
  • 查看模型的参数
for name, param in model.named_parameters():
    if param.requires_grad:
        print name, param.data

训练的基本组成

损失函数可以自定义:

def my_loss(output, target):
    loss = torch.mean((output - target)**2)
    return loss

model = nn.Linear(2, 2)
x = torch.randn(1, 2)
target = torch.randn(1, 2)
output = model(x)
loss = my_loss(output, target)
loss.backward()
print(model.weight.grad)

定义模型的优化器:

optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

模型的状态

模型的某些层(Dropout, BatchNorm等)在train和eval下有不同的表现
model.train()
model.eval(): 常常和torch.no_grad() 一起使用(如下)

# evaluate model:
model.eval()

with torch.no_grad():
    ...
    out_data = model(data)
    ...

模型的训练

  • 基本操作:
predictions = model(inputs)               # Forward pass
loss = loss_function(predictions, labels) # Compute loss function
loss.backward()                           # Backward pass
optimizer.step()                          # Optimizer step
predictions = model(inputs)               # Forward pass with new parameters
训练大型数据的技巧
梯度累加

受限于GPU显存的大小,大一些的数据集无法被一次性放入模型中训练。

model.zero_grad()  # 清除累积梯度                                   # Reset gradients tensors
for i, (inputs, labels) in enumerate(training_set):
    predictions = model(inputs)                     # Forward pass
    loss = loss_function(predictions, labels)       # Compute loss function
    loss = loss / accumulation_steps                # Normalize our loss (if averaged)
    loss.backward()                                 # Backward pass
    if (i+1) % accumulation_steps == 0:             # Wait for several backward steps
        optimizer.step()                            # Now we can do an optimizer step
        model.zero_grad()                           # Reset gradients tensors
        if (i+1) % evaluation_steps == 0:           # Evaluate the model when we...
            evaluate_model()                        # ...have no gradients accumulated

还值得注意的是,为了经过 iter_size 次迭代后的累积梯度跟使用一个大的batch_size*iter_size 一样,需要将每次的 loss 除以 iter_size

模型的并行计算: DataParallel

 if torch.cuda.device_count() > 1:
     log.info("The model is running in parallel on {} GPUs.".format(torch.cuda.device_count()))
     model = DataParallelWrapper(model)

注意此时原model里的参数方法无法被直接调用, 解决方法:

class MyDataParallel(nn.DataParallel):
    def __getattr__(self, name):
        return getattr(self.module, name)

模型的储存

torch.save(model.state_dict(), PATH)

CUDA 计算

  • 将模型放入GPU:
device = torch.device("cuda:0")
model.to(device)

  • 改变CUDA的默认GPU编号:
CUDA_VISIBLE_DEVICES=1 python3 LAN_test.py

PyTorch机制

动态图

loss.backward()

功能 计算tensor对于图中叶结点的梯度。
根据pytorch中backward()函数的计算,当进行求导时,梯度是累积计算(累加)而不是被替换,但在处理每一个batch时并不需要与其他batch的梯度混合起来累积计算,因此需要对每个batch调用一遍zero_grad()将当前可变参数的梯度置0。

  • retain_graph: 每次 backward() 时,默认会把整个计算图free掉。一般情况下是每次迭代,若在当前backward()后,不执行forward() 而可以执行另一个backward(),需要在当前backward()时,指定保留计算图,即backward(retain_graph)

PyTorch Hook 笔记

概念

挂钩式编程(hook programming)
Hook的常见作用

PyTorch中的两种Hook:

  1. 作用在张量上的hook
  2. 作用在module上的hook

例子:简单的计算图

我们先来创建一个非常简单的计算图:

a = torch.Tensor(2.0, requires_grad=True)
b = torch.Tensor(3.0, requires_grad=True)

c = a * b 

a 和 b是叶节点,PyTorch默认只有叶结点会储存梯度。如果我们想储存c的梯度,需要对c使用c.retain_grad()
这个前向计算图会对应一个反向传播的图,如下图中的右边部分:
在这里插入图片描述
反向传播图不像用户自己定义的计算图(即前向传播图)那样,它难以直接获取数据。

作用在张量上的hook

重点:

  1. 张量上的hook函数是以OrderedDict形式储存起来的,因此顺序很重要——hook函数会按顺序被调用
  2. 如果hook函数返回值,那么这个返回值会修改该张量内的grad;如果不返回值,那么该张量内的grad不变
  3. 对于叶结点a, 如果调用了a.retain_grad(), return_grad_hook 会被register这个节点张量中。至于是储存hook改变之前还是之后的张量,取决于a.retain_grad()调用的位置。
  4. register_hook会返回这个hook的handler, 用来移除这个hook。
  5. 如果在hook函数里直接改变作为参数的grad的值,也会影响其他节点。因此不可以对grad做in-place操作,比如grad+=100这种。

例子

# define the computational graph 
a = torch.Tensor(2.0, requires_grad=True)
b = torch.Tensor(3.0, requires_grad=True)
c = a * b 
d = torch.Tensor(4.0, requires_grad=True)
e = c * d 

# define the hook function 
def c_hook(grad):
	print(grad)
	return grad + 2  

# register hooks 
h = c.register_hook(c_hook)
c.register_hook(lambda grad: print(grad))
c.retain_grad()
d.register_hook(lambda grad: grad + 100)

e.retain_grad() # 
e.register_hook(lambda grad: grad*2)

在这里插入图片描述
当我们想要移除某个hook时,

# remove hooks 
h.remove()

作用在Module上的hook

这种hook分为:

  1. register_forward_hook
  2. register_forward_pre_hook
  3. register_backward_hook (not fixed), 推荐直接用tensor based hook

hook函数的定义

def module_forward_hook(module, positional_arguments):
	...
def module_forward_pre_hook(module, positional_arguments, output):
	...
def module_forward_hook(module, grad_input, grad_output):
	...

如果forward(1,2, c=3),那么hook函数只会接收1,3


参考资料:
【PyTorch】PyTorch中的梯度累加
PyTorch 梯度累积小技巧
Pytorch的backward()相关理解
PyTorch Hooks Explained - In-depth Tutorial

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值