模型开发需要对数据的预处理、数据增强、模型定义、权值初始化、模型 Finetune、学习率调整策略、损失函数选取、优化器选取、可视化等等。
Variable和Tensor
Pytorch Variable API has been deprecated
自动求导
[optimizer.step()可以重复执行多次吗?](https://www.baidu.com/s?wd=optimizer.step()%E5%8F%AF%E4%BB%A5%E9%87%8D%E5%A4%8D%E6%89%A7%E8%A1%8C%E5%A4%9A%E6%AC%A1%E5%90%97&rsv_spt=1&rsv_iqid=0x8212103e000034c6&issp=1&f=8&rsv_bp=0&rsv_idx=2&ie=utf-8&tn=baiduhome_pg&rsv_enter=1&rsv_sug3=31&rsv_t=7019Hp%2Fmx%2F7DdhZ7JCS90pYhCo4xV5mpq5eM0yy4VPvCrCI3wOPwZ3VlBrd5bylWzhDl&rsv_sug1=4&rsv_sug7=100&rsv_sug2=0&inputT=7066&rsv_sug4=7066)
mxnet若要对一个变量x求导(x = nd.arange(4).reshape((4, 1))),首先x.attach_grad()申请存储梯度所需要的内存。为了减少计算和内存开销,默认条件下MXNet不会记录用于求梯度的计算,调用autograd.record()来要求MXNet记录与求梯度有关的计算。接下来我们可以通过调用backward()函数自动求梯度。
pytorch:
optimizer.zero_grad() # 清空上一步的残余更新参数值
loss.backward() # 误差反向传播, 计算参数更新值
optimizer.step() # 将参数更新值施加到 net 的 parameters 上
mxnet每次计算完会清空上一次的梯度更新值,pytorch不会清空,所以需要执行zaro grad操作。
pytorch如何截断梯度传播?
1.使用detach和detach_函数
如果我们有两个网络A、B,两个关系是这样的y=A(x)、z=B(y)现在我们想用z.backward()来为B网络的参数来求梯度,但是又不想求A网络参数的梯度。我们可以这样:
# y=A(x), z=B(y) 求B中参数的梯度,不求A中参数的梯度
# 第一种方法
y = A(x)
z = B(y.detach())
z.backward()
# 第二种方法
y = A(x)
y.detach_()
z = B(y)
z.backward()
在这种情况下,detach和detach_都可以用。但是如果你也想用y来对A进行BP呢?那就只能用第一种方法了。因为第二种方法已经将A模型的输出给detach(分离)了。
2.例如a是一个tensor,a是A网络输出,现在a又是B网络的输入,B网络输出为b;即a=A(i)、b=B(a);如果我们对b.backward(),那么A网络也更新了,我们只想更新B网络,然后随后更新A网络。可以这样操作
a=A(i)
a1=Variable(a)
b=B(a)
b.backward()
这样只会更新B网络,因为B网络输入是用A网络输出a初始化的变量a1,a1是叶子结点。而且重要的是变量a1也保存了B网络反向的梯度,我们可以用变量a1保存的梯度来更新A网络。
这种情况适用于A、B网络更新策略不同。
3.利用model.parameters(),修改requires_grad 属性。
keras是高层api,反向传播不需要backward和step。
tensorflow:可以通过stop gradient阻止部分该部分的反向传播;可以通过apply gradient来阻止部分变量更新,compute gradient计算出来梯度之后,只对需要更新的变量执行apply gradient。
- opt.compute_gradients()
- opt.apply_gradients()
- tf.gradients():计算梯度的函数。
- tf.stop_gradient():阻挡节点BP的梯度。
(注意可以用tf gradients求导的一定是浮点数,整数不可以)(重要)tensorflow学习笔记(三十):tf.gradients 与 tf.stop_gradient() 与 高阶导数
opt.compute_gradients() 与 tf.gradients 与 tf.stop_gradient()
a = tf.multiply(w1, 3.0)
a_stoped = tf.stop_gradient(a)
下一步用a_stoped参与计算,a_stoped之前的Variable梯度计算都受影响,如果Variable梯度只能来自于a,那么梯度计算结果为None。
tensorflow中有一个计算梯度的函数tf.gradients(ys, xs),要注意的是,xs中的x必须要与ys相关,不相关的话,会报错。tf.gradients()中的要求grad_ys与ys维度一致的要求。
在使用tf.gradients()时,我有一个疑惑,返回值sum(dy/dx)到底是对多个输出变量进行求和,还是对batch进行求和,还是两者都需要求和呢?
如果,仅仅输入一个sample(也就是说不考虑batch),那么我们对于参数的更新,应该是要对多个输出变量对于该参数的导数进行求和的。那么该函数是否会对batch进行求均值处理?答案是会对batch进行求和,但不是求均值。我们可以看出,最终的返回值将会对各个batch以及各个输出变量关于参数的导数进行求和。
模型参数初始化
mxnet定义了模型net = nn.Sequential(),然后调用net.initialize()来初始化模型参数。
pytorch模型定义时就顺便初始化了,不需要专门的初始化步骤。
https://github.com/pytorch/pytorch/blob/master/torch/nn/modules/linear.py
定义Linear层参数初始化过程。
tensorflow需要在session中执行参数初始化的操作。
keras高阶api。
模型的训练和测试
pytorch在模型训练时会在前面加上model.train(),在测试模型时在前面使用model.eval()。当然如果你不写这两句程序也可以运行,这是因为这两个方法针对在网络训练和测试时采用不同方式的情况,比如Batch Normalization和Dropout。
mxnet的mod.forward()里面有一个参数is_train,训练的时候设成True,测试的时候设成False就成了。
tensorflow中的dropout和batch norm类似的层都有一个参数叫做is_training。所以设置一个is_training的placeholder,然后训练和测试阶段分别传入不同的值。如下
is_training = tf.placeholder(tf.bool, name='is_training')
summary_str, _, loss_, acc_ = sess.run([merged_summary_op, train_step, loss, accuracy], feed_dict={inputs: images_, labels: truths_, is_training: True})
valid_str, vloss, vacc = sess.run([merged_summary_op, loss, accuracy], feed_dict={inputs: valid_imgs_, labels: valid_trus_, is_training: False})
keras的fit函数可以指定x、y和validation_data,感觉内部其实都已经设好了。keras的api太高阶了。
数据读取
PyTorch中数据读取的一个重要接口是torch.utils.data.DataLoader,该接口定义在dataloader.py脚本中,只要是用PyTorch来训练模型基本都会用到该接口,该接口主要用来将自定义的数据读取接口的输出或者PyTorch已有的数据读取接口的输入按照batch size封装成Tensor,后续只需要再包装成Variable即可作为模型的输入,因此该接口有点承上启下的作用,比较重要。
PyTorch源码解读之torch.utils.data.DataLoader
pytorch实现自由的数据读取-torch.utils.data的学习
维度增加和维度交换以及维度扩展
pytorch中的view和permute函数。
view之前最好用contiguous函数把tensor移到连续的地址上。
expand:维度扩展,扩展某个size为1的维度,例如(2,2,1)扩展为(2,2,3)。expand没有分配内存,所以改原来维度为1位置的数据,整行数据全改。
expand_as:等于expand(tensor.size())。
repeat:沿着指定的维度重复张量。不同于expand()方法,本函数复制的是张量中的数据。
PyTorch 常用方法总结4:张量维度操作(拼接、维度扩展、压缩、转置、重复……)
模型定义
模型定义很多时候需要调用已有的接口,例如tensorflow的tf.nn.conv2d和pytorch的torch.nn.Conv2d接口,这两个接口第一个返回tensor,第二个返回Conv2d类型。因为tensorflow的conv2d接口定义的是计算过程;而pytorch的conv2d接口定义的是参数,计算过程在forward函数中定义。当然这也是静态图计算和动态图计算的最大区别了,静态图直接建立图把计算过程都定义好了。
对与tensorflow来说,它本身各种运算都在一个默认计算图中,一般tennsorflow也只定义一个默认图。
层继承tf.keras.layers.Layer和网络继承tf.keras.Model)。
pytorch:
定义model:继承自torch.nn.Module;__init__函数先初始化父类super().__init__(),然后定义各个层(其实就是定义参数);forward函数定义了这个model的层之间的运算关系,这个函数的输入是self和x,x是这个子模块的输入。
定义网络层:继承自Function;forward()是网络层的计算操作,能够根据你的需要设置参数;backward()是梯度计算操作。
Pytorch入门学习(八)—–自定义层的实现(甚至不可导operation的backward写法)(非常全)
对于前馈传播网络,如果每次都写复杂的forward函数会有些麻烦,有两种简化方式:ModuleList、Sequential。
Keras:
Sequential模型、函数式API(Model)。
Sequential模型:
多个Sequential模型可经由一个Merge层合并到一个输出。Merge层的输出是一个可以被添加到新Sequential的层对。下面这个例子将两个Sequential合并到一起:
https://github.com/skckompella/memn2n-keras/blob/master/src/single_layer.py 这里也使用了Merge合并多个Sequential模型。
from keras.layers import Merge
left_branch = Sequential()
left_branch.add(Dense(32, input_dim=784))
right_branch = Sequential()
right_branch.add(Dense(32, input_dim=784))
merged = Merge([left_branch, right_branch], mode='concat')
final_model = Sequential()
final_model.add(merged)
final_model.add(Dense(10, activation='softmax'))
这个两个分支的模型可以通过下面的代码训练:
final_model.compile(optimizer='rmsprop', loss='categorical_crossentropy')
final_model.fit([input_data_1, input_data_2], targets)
Model模型:
所有的模型都是可调用的,就像层一样。利用泛型模型的接口,我们可以很容易的重用已经训练好的模型:你可以把模型当作一个层一样,通过提供一个tensor来调用它。注意当你调用一个模型时,你不仅仅重用了它的结构,也重用了它的权重。
如下图,model和model1的的模型参数是一样的。
https://github.com/alecGraves/cyclegan_keras/blob/master/test/mnist_test.py 这里也是把Model当作层来调用的例子,然后调用之后定义一个新的模型,新的模型参数也包括被调用Model的参数。
from keras.layers import Input, Dense
from keras.models import Model
inputs = Input(shape=(784,))
x = Dense(64, activation='relu')(inputs)
x = Dense(64, activation='relu')(x)
predictions = Dense(10, activation='softmax')(x)
model = Model(input=inputs, output=predictions)
x = Input(shape=(784,))
y = model(x)
model1 = Model(input=x, output=y)
model1.summary()
自定义网络层:
1.keras.core.lambda()。 2.Layer继承类。build(input_shape):这是定义权重的方法,这个方法必须设置self.built = True,可通过调用super(layer,self).build()实现。call(self, x, mask=None)是定义层功能的方法,除非你希望你写的层支持masking,否则你只需要关心call的第一个参数。
获取模型中的变量,输出某一个tensor的值
tensorflow:
获取模型中的变量:
tf.trainable_variables和tf.all_variables的对比
1.tf.trainable_variables()生成一个list,里面是所有可训练的变量;tf.all_variables()生成一个list,里面是所有变量。其中类型是Variable,通过.name可以得到变量的名字。 2.tf.train.NewCheckpointReader()
输出某一个tensor的值:
1.如果是tensor在图中有定义,直接用sess.run输出。 2.如果是导入的模型,通过tensor的名字输出。
tf.get_default_graph().get_tensor_by_name('MNIST/f2_layer/add:0')
3.tf.train.NewCheckpointReader()类。
reader = tf.train.NewCheckpointReader('./model.ckpt')
w = reader.get_tensor("Variable_2")
print(type(w))
print(w.shape)
print(w[0])
pytorch:
PyTorch 第四弹_通过LeNet初识pytorch神经网络_上
【pytorch】Module.parameters()函数实现与网络参数管理
How to print model’s parameters with its name and `requires_grad value`?
1.module.parameters(),返回参数值。返回全部的参数值,迭代器。 2.module.named_parameters(),返回(参数名称,参数值)。返回参数名称和值,迭代器。
keras:
有一篇自己写的Keras文档中有相关内容。
模型中参数定义
pytorch:
torch.nn.Parameter来定义Model的参数。
模型持久化和加载
tensorflow:
持久化:
saver = tf.train.Saver()
saver.save(sess, '')
读取:
saver = tf.train.import_meta_graph()
with tf.Session() as sess:
saver.restore(sess, ckpt.model_checkpoint_path)
x = tf.get_default_graph().get_tensor_by_name('Placeholder:0')
y_ = tf.get_default_graph().get_tensor_by_name('Placeholder_1:0')
keep_prob = tf.get_default_graph().get_tensor_by_name('Placeholder_2:0')
result = sess.run(tensor_temp, feed_dict = {x: batch[0], y_: batch[1], keep_pr
ob: 0.5})
Keras:
Pytorch:
torch.save(net1, 'net.pkl') # 保存整个网络
torch.save(net1.state_dict(), 'net_params.pkl') # 只保存网络中的参数 (速度快, 占内存少)
提取整个神经网络:
net2 = torch.load('net.pkl')
提取参数:
net3.load_state_dict(torch.load('net_params.pkl'))
只训练部分参数、加载部分参数
tensorflow:
为self.optim.minimize函数传入var_list参数。
选择在加载网络之后重新初始化剩下的层。
saver = tf.train.Saver(variables_to_restore)传入需要恢复的参数列表var_list。保存的时候也可以设置这个变量指定要保存的参数列表。
pytorch:
局部微调:有时候我们加载了训练模型后,只想调节最后的几层,其他层不训练。其实不训练也就意味着不进行梯度计算,PyTorch中提供的requires_grad使得对训练的控制变得非常简单。
全局微调:有时候我们需要对全局都进行finetune,只不过我们希望改换过的层和其他层的学习速率不一样,这时候我们可以把其他层和新层在optimizer中单独赋予不同的学习速率。
修改模型内部网络层:如果需要对网络的内部结构进行改动,则需要采用参数覆盖的方法。即,先定义类似网络结构,再提取预训练模型的权重参数,覆盖到自定义网络结构中。
model.state_dict():是一个简单的python的字典对象,将每一层与它的对应参数建立映射关系。
model.parameters()输出网络所有的Parameter(有值和require_grad属性)。
输出中间层结果
pytorch:
Extracting and using features from a pretrained model
How to extract features of an image from a trained model
reshape
keras: layers.Reshape
tensorflow: tf.reshape
tf.reshape和keras.layers.Reshape不一样,tf针对所有的维度进行reshape,keras只针对0维以外的进行reshape。
keras.layers.Reshape和tf.reshape比较
输入
keras: keras.layers.Input 例如keras.layers.Input(shape=(None, None, None, 36))表示五个维度
tensorflow: tf.placeholder 例如tf.placeholder(tf.int32, [None, 36])表示两个纬度
矩阵乘法
keras: tf.keras.backend.batch_dot: 批量化的点积。K.dot
tensorflow: tf.matmul、tf.batch_matmul、tf.multiply
batch_dot和matmul差不多。K.dot会沿着两个矩阵最后两个维度进行乘法,不是element-wise矩阵乘法。
理解keras中的batch_dot,dot方法和TensorFlow的matmul
新版的tensorflow已经移除batch_matmul,使用时换为matmul就可以了。
重点:感觉batch_dot只能计算只能涉及最后两个维度。
Understanding batch_dot() in Keras with Tensorflow backend
tf.multiply: 矩阵x和矩阵y对应位置的元素相乘。
矩阵操作
pytorch: torch.cat、torch.stack、torch.transpose、permute、squeeze、unsqueeze
pytorch中的cat、stack、tranpose、permute、unsqeeze
tensorflow: concat、stack、unstack