1.Pytorch已经为我们提供了丰富的函数以及神经网络模块了,但是有的时候我们需要自定神经网络模块。
2.自定神经网络模块时候我们分为有参数和无参数定义。
3.如果是自定义的层没有可以需要学习的参数,直接用Function类就行,比如ReLu。
4.如果有参数,就使用Module类来实现。
我们在学习反向传播时,经常会有这么一个例子。
一个向量[1,1],将他*4,然后求取这个向量的长度。
我们通常时将其中的分量1作为x1,另外一个作为x2,然后y当作长度,y是x1,x2的结果,然后我们利用反向传播这个方法去求取y关于x1,x2的偏微分,然后得到这个向量的长度在这两个方向上的分量就可以得到,这里就是长度。当我们想要求长度关于某一个分量(比如对于(x1))的导数时,我们实际上是想要知道长度在(x1)方向变化的速率,微分理解一下^^
好了,我们来实现一个无参数的函数来计算y,并且可以反向传播求导。
import torch
from torch.autograd import Function
import torch.nn as nn
import numpy as np
class CustomizeNone(Function):
# 自定义的类我们需要两个函数,前向传播,反向传播
# 前向传播我们需要用到两个参数,ctx,input,ctx用于储存反向传播需要用到的对象
# 这是Python中的装饰器语法,用于将下面的forward方法声明为一个静态方法(static method)。
@staticmethod
def forward(ctx, input):
ctx.save_for_backward(input)
# torch.detach()是PyTorch中的一个方法,用于创建一个新的张量,该张量与原始张量共享数据,但是与计算图分离。
# 在深度学习中,计算图用于跟踪数据流和梯度计算,
# detach()方法允许我们从计算图中分离张量,这意味着在将来的计算中不会再跟踪与原始张量相关的梯度信息。
numpy_input = input.detach().numpy()
result1 = numpy_input*4
# np.linalg.norm()函数用于计算向量或矩阵的范数
# 使用某层输出的范数作为后续计算的一部分。通过计算输出的范数,我们可以获得有关该层输出的一些信息,例如层中值的分布情况、大小等
result2 = np.linalg.norm(result1, keepdims=True)
# 创建一个新的与input具有相同数据类型的张量,并用result2的值初始化它
return input.new(result2)
@staticmethod
def backward(ctx, grad_output):
input, = ctx.saved_tensors
# 这里是乘4后求微分
# 即dy/dx1 = d(4(x1^2 + x2^2)^1/2)/dx1 = 4*1/2*(x1^2 + x2^2)^-1/2*2x1
# (x1 ^ 2 + x2 ^ 2) ^ 1 / 2范数
grad = 4*(1/2)*(1/input.norm().item())*(2*input)
return grad
customize = CustomizeNone.apply
input = torch.Tensor([1, 1])
input.requires_grad = True
print("Input:", input)
result = customize(input)
print("result:", result)
result.backward()
print("input grad:", input.grad)
接下来实现有参数的层,需要用到Module类,我们来定义一个线性层。
# 下面是有参数的线性层的定义
# 定义了一个自定义的自动求导函数LinearFunction,用于执行线性操作的前向和反向计算。
class LinearFunction(Function):
@staticmethod
def forward(ctx, input, weight, bias=None):
ctx.save_for_backward(input, weight, bias)
# 对输入张量input和转置权重张量weight.t()进行矩阵乘法运算,以计算输出张量。
output = input.mm(weight.t())
if bias is not None:
output += bias.unsqueeze(0).expand_as(output)
return output
@staticmethod
def backward(ctx, grad_output):
input, weight, bias = ctx.saved_tensors
grad_input = grad_weight = grad_bias = None
if ctx.needs_input_grad[0]:
grad_input = grad_output.mm(weight)
if ctx.needs_input_grad[1]:
grad_weight = torch.matmul(grad_output.t(), input)
if bias is not None and ctx.needs_input_grad[2]:
grad_bias = grad_output.sum(0).squeeze(0)
return grad_input, grad_weight, grad_bias
class Linear(nn.Module):
def __init__(self, input_features, output_features, bias=True):
super(Linear, self).__init__()
self.input_features = input_features
self.output_features = output_features
self.weight = nn.Parameter(torch.Tensor(output_features, input_features))
if bias:
self.bias = nn.Parameter(torch.Tensor(output_features))
else:
self.register_parameter('bias', None)
self.weight.data.uniform_(-0.1, 0.1)
if bias is not None:
self.bias.data.uniform_(-0.1, 0.1)
def forward(self, input):
return LinearFunction.apply(input, self.weight, self.bias)
def extra_repr(self):
return 'in_features={}, output_features={}, bias={}'.format(
self.input_features, self.output_features, self.bias is not None
)
linear = Linear(4,2)
input = torch.Tensor(3, 4)
input.requires_grad = True
#通过线性层对象linear进行前向传播操作,得到输出张量output。
output = linear(input)
output.backward(torch.ones(output.size()))
print(input.grad)