学了大概一个月时间的pytorch和tensorflow,觉得pytorch写法比tf更简单,也更灵活。不需要通过session来run到结果,数据可以轻松地在cpu和gpu之间切换。
pytorch的一个最重要的机制就是autograd机制,通过这个机制你可以轻易的固定你的模型的部分参数,从而在训练中只是训练你希望训练的那部分计算图。
在pytorch中有两个类型的变量,一个是tensor,另外一个是variable。
tensor就相当于numpy中的ndarray,只是一个多维数组而已,有numpy矩阵的各种操作,也可以与numpy相互转换。
variable相当于一个功能加强版的tensor,其中不仅存放了变量本身的值,还存放了变量的其他属性,例如梯度(grad),是否需要求导(requires_grad)等。从variable所在的package也可以看出,它是在autogard中的类,所以variable是实现autograd的关键。
因此在创建variable时,你可以指定它的参数,requires_grad或者volatile。
requires_grad的特点是只要有一个输入变量是需要求导的,那么它们的输出也是requires_grad的。
例如这个官网的例子
>>> x = Variable(torch.randn(5, 5)) >>> y = Variable(torch.randn(5, 5)) >>> z = Variable(torch.randn(5, 5), requires_grad=True) >>> a = x + y >>> a.requires_grad False >>> b = a + z >>> b.requires_grad True因此你可以通过替换模型的最后一层在其他权重固定的情况下来重新训练
model = torchvision.models.resnet18(pretrained=True) for param in model.parameters(): param.requires_grad = False # Replace the last fully-connected layer # Parameters of newly constructed modules have requires_grad=True by default model.fc = nn.Linear(512, 100) # Optimize only the classifier optimizer = optim.SGD(model.fc.parameters(), lr=1e-2, momentum=0.9)volatile根据官网的描述,一般用于测试模型,即确定不需要进行进行反向传播的场合。在下面这个例子中,将输入置为volatile,可以得到volatile的输出。而且模型在运行过程中会占用最少的内存。
>>> regular_input = Variable(torch.randn(1, 3, 227, 227)) >>> volatile_input = Variable(torch.randn(1, 3, 227, 227), volatile=True) >>> model = torchvision.models.resnet18(pretrained=True) >>> model(regular_input).requires_grad True >>> model(volatile_input).requires_grad False >>> model(volatile_input).volatile True >>> model(volatile_input).grad_fn is None True
那么,pytorch所谓的自动求导机制究竟是如何实现的呢?
我们知道,不管是pytorch还是tf,其算法核心都是基于计算图的反向传播。即将输入数据作为叶子节点,操作作为边,输出作为根节点,构建一张计算图。这样就可以将输入数据通过前向传播计算出输出,再由输出反向传播,利用链式法则计算出各个叶子节点的梯度。
在pytorch中,variable会记录创建它的函数,这样就可以使它能够追踪计算过程,从而利用链式法则求导。如果一个variable是用户创建的,那么它的grad_fn被设为None,这样的变量被成为leaf。在网络进行forward传播的过程中,pytorch会同时计算一个梯度求解函数,放在变量的grad_fn中,在完成forward过程后,我们就可以使用backward()函数来调用grad_fn来进行求导了。
in-place操作
在pytorch中,tensor支持各种in-place操作,用来改变自身的值,并且占用更少的内存。但是variable却是由用户创建或者由其他variable生成。作者却极力阻止大家使用variable的in-place操作,因为pytorch中的aggressive buffer freeing and reuse 已经使非in-place的操作变得很高效了。使用variable的in-place操作有两个缺点:
1.会覆盖掉需要计算梯度的原始值,这也是variable不提供log()等函数的原因。
2.每次in-place操作实际上都会改变整个计算图的结构,例如某一个变量使用了in-place操作,那么它的记录的所有生成者的生成函数都要改变。
基于计算流图的计算框架,其要求是不能在计算图中引入闭环,使用in-place操作实际上引入了单个variable的闭环,这样无论是实现还是阅读都有很大麻烦。