在训练过程中遇到困难时(例如nan
、损失不收敛等),有时通过在'solver.prototxt'
文件中设置debug_info: true
来查看更详细的训练日志会很有帮助。
训练日志看起来会像这样:
I1109 ...] [Forward] Layer data, top blob data data: 0.343971 I1109 ...] [Forward] Layer conv1, top blob conv1 data: 0.0645037I1109 ...] [Forward] Layer conv1, param blob 0 data: 0.00899114I1109 ...] [Forward] Layer conv1, param blob 1 data: 0I1109 ...] [Forward] Layer relu1, top blob conv1 data: 0.0337982I1109 ...] [Forward] Layer conv2, top blob conv2 data: 0.0249297I1109 ...] [Forward] Layer conv2, param blob 0 data: 0.00875855I1109 ...] [Forward] Layer conv2, param blob 1 data: 0I1109 ...] [Forward] Layer relu2, top blob conv2 data: 0.0128249. ..I1109 ...] [Forward] Layer fc1, top blob fc1 data: 0.00728743I1109 ...] [Forward] Layer fc1, param blob 0 data: 0.00876866I1109 ...] [Forward] Layer fc1, param blob 1 data: 0I1109 ...] [Forward] Layer loss, top blob loss data: 2031.85I1109 ...] [Backward] Layer loss, bottom blob fc1 diff: 0.124506I1109 ...] [Backward] Layer fc1, bottom blob conv6 diff: 0.00107067I1109 ...] [Backward] Layer fc1, param blob 0 diff: 0.483772I1109 ...] [Backward] Layer fc1, param blob 1 diff: 4079.72...I1109 ...] [Backward] Layer conv2, bottom blob conv1 diff: 5.99449e-06I1109 ...] [Backward] Layer conv2, param blob 0 diff: 0.00661093I1109 ...] [Backward] Layer conv2, param blob 1 diff: 0.10995I1109 ...] [Backward] Layer relu1, bottom blob conv1 diff: 2.87345e-06I1109 ...] [Backward] Layer conv1, param blob 0 diff: 0.0220984I1109 ...] [Backward] Layer conv1, param blob 1 diff: 0.0429201E1109 ...] [Backward] All net params (data, diff): L1 norm = (2711.42, 7086.66); L2 norm = (6.11659, 4085.07)
这意味着什么呢?
回答:
乍一看,你可以看到这个日志部分分为两部分:[Forward]
和[Backward]
。回顾一下,神经网络的训练是通过前向-后向传播来完成的:
一个训练样本(批次)被输入到网络中,前向传递输出当前的预测。
基于这个预测计算损失。然后计算损失的导数,估算梯度并使用链式法则向后传播。
Caffe Blob
数据结构
简单回顾一下。Caffe使用Blob
数据结构来存储数据/权重/参数等。对于本讨论,重要的是要注意Blob
有两个“部分”:data
和diff
。Blob
的值存储在data
部分。diff
部分用于存储后向传播步骤的逐元素梯度。
前向传递
你会在这个日志部分看到从底到顶的所有层。对于每一层,你会看到:
I1109 ...] [Forward] Layer conv1, top blob conv1 data: 0.0645037
I1109 ...] [Forward] Layer conv1, param blob 0 data: 0.00899114I1109 ...] [Forward] Layer conv1, param blob 1 data: 0
层"conv1"
是一个卷积层,它有两个参数Blob:滤波器和偏置。因此,日志中有三行。滤波器Blob(param blob 0
)的data
I1109 ...] [Forward] Layer conv1, param blob 0 data: 0.00899114
即卷积滤波器权重的当前L2范数为0.00899。
当前偏置(param blob 1
):
I1109 ...] [Forward] Layer conv1, param blob 1 data: 0
意味着当前偏置设置为0。
最后但同样重要的是,"conv1"
层有一个输出,命名为"conv1"
(多么原始…)。输出的L2范数是
I1109 ...] [Forward] Layer conv1, top blob conv1 data: 0.0645037
请注意,[Forward]
传递的所有L2值都报告在相关Blob的data
部分上。
损失和梯度
在[Forward]
传递结束时,会出现损失层:
I1109 ...] [Forward] Layer loss, top blob loss data: 2031.85
I1109 ...] [Backward] Layer loss, bottom blob fc1 diff: 0.124506
在这个例子中,批次损失为2031.85,相对于fc1
的损失梯度被计算并传递到fc1
Blob的diff
部分。梯度的L2大小为0.1245。
后向传递
所有其余的层在这个部分按从顶到底的顺序列出。你可以看到现在报告的L2大小是Blob的diff
部分(参数和层的输入)。
最后
这次迭代的最后一行日志:
[Backward] All net params (data, diff): L1 norm = (2711.42, 7086.66); L2 norm = (6.11659, 4085.07)
报告了数据和梯度的总L1和L2大小。
我应该注意什么?
-
如果你的损失中有
nan
,看看你的数据或diff
在什么时候变成nan
:在哪个层?在哪个迭代? -
查看梯度的大小,它们应该在合理范围内。如果你开始看到
e+8
的值,你的数据/梯度开始变得很大。降低你的学习率! -
查看
diff
是否为零。零diff
意味着没有梯度=没有更新=没有学习。如果你从随机权重开始,考虑生成方差更高的随机权重。 -
查找激活值(而不是梯度)变为零的情况。如果你使用的是
"ReLU"
,这意味着你的输入/权重导致你进入ReLU门“未激活”的区域,导致“死亡神经元”。考虑将你的输入标准化为零均值,添加"BatchNorm"
层,设置ReLU中的negative_slope
。