激活函数的选择:
参考资料:https://zhuanlan.zhihu.com/p/172254089
sigmoid的梯度范围是(0,0.25),假设一个网络有多层,每层的激活函数是sigmoid,那么反传多层后梯度会变的非常小。也就是sigmoid会导致梯度消失。如果权重非常大,使得输出达到了饱和区域,那么梯度会接近于0。所以sigmoid容易发生梯度消失。
relu的公式是max(0,x),梯度取值为0和1两个值,多层relu之后梯度还是0或者1,1的话梯度继续传播,0的话从该位置开始停止传播。既然sigmoid和relu一样都会出现梯度很小的情况,那么为什么通常选择relu作为激活函数呢?因为sigmoid是双侧饱和(正负两个方向都会发生饱和),relu只是单侧饱和(只在正方向有可能发生饱和),1.负向饱和具有一定的意义。举例来说,神经元通常是作为特征提取的,假如某个神经元负责检测边缘,则正值越大代表边缘区分越明显(sharp)。那么负值越小代表什么意思呢?直觉上来说,用负值代表检测特征的缺失是合理的,但用负值的大小代表缺失的程度就不太合理,难道缺失也有程度吗?对于缺失的,直接用常量0表示就好。直接设为0的话;2.负值所受到的噪声的影响不再存在,也不会再传递给下一层;3.负值直接设为0不会出现神经元之间相互依赖。
总结来说1.sigmoid梯度小,容易发生梯度消失;2.单侧激活更加鲁棒;再补充一点3.relu的计算效率高。所以relu的效果优于sigmoid。
relu的问题:
1.死亡神经元(dying relu problem)如果输入到激活函数的值总是负的,那么反向传播过程中该位置的梯度恒为0,对应的权重无法得到更新,神经元无法学习,也就是神经元“死亡”。
2.梯度更新方向的锯齿路径。
疑问为什么lstm相较于rnn更不容易梯度消失
backward()计算梯度
step()用计算出的梯度更新参数
zero_grad()将参数梯度置0.一般是每个batch调用一次.如果不是处理每个batch清除一次梯度,而是两次或多次再清除一次,相当于提高了batch_size.
pytorch中backward里的retrain_graph
pytorch里面如果有个tensor(例如loss)调用了backward,那么这个tensor相关的tensor会被计算梯度。计算完了,为了省空间会将其涉及的tensor组成的graph给释放掉。假如有两个loss(多任务),每个loss单独回传梯度,那么在loss1.backward()后,调用loss2.backward()会导致错误。这是因为loss1.backward()后graph已经释放掉了,如果不想释放,可以设置retrain_graph为True。这样会恢复被释放掉的graph
补充:
如果多个loss相加得到最终的final_loss,只对final_loss做backward,那么会导致loss1的参数会受loss2的影响。