学习损失函数(Softmax或SVM)我们知道可以通过其计算出的损失值来评估一个权重矩阵对数据集的拟合能力。
损失函数的公式为
对于一个图像xi,如果我们的预测与正是情况所符合,那么损失值就会很低甚至为0,但是初始权重所得到的损失值都很高,如何降低损失只则成为了最重要的部分。
蒙眼徒步者的比喻:一个有助与我们理解的比喻就是把我们想象成一个蒙眼的徒步者,我们的目标就是走到山地,在cifar-10中的数据是3072维的,我们在这3072维上的每一个点
都对应一个损失值,这个损失值可以看成是山的高度
方法1:随机搜索
这个方法可以看作在山上多次随机选择出发点。我们可以多次随机初始化权重值,然后进行损失值计算找到最好的权重W进行测试,我所得到的最好结果为18%左右,可以看出这种方法并不管用
方法2:随机本地搜索
相较于方法1,这个方法可谓是聪明了不少,可以看成是我们在山上随机向周围迈步,如果是向下的就移动,我们随机生成一个权重W,然后再去生成一个随机扰动hW,只有当W+hW的损失值变低时才进行更新,但是这种方法过于浪费计算机资源
并且所得到的21.4%的正确率也不尽人意。
方法3:跟随梯度
这个方法可以看作我们可以感受到脚下的倾斜程度,然后朝着陡峭的地方下山。这个倾斜程度即为斜率也就是我们所常见的导数
梯度计算
梯度计算可以分为数值梯度法和分析梯度法,数值梯度法更新较慢,资源占用多,分析梯度法更新较快,但是容易出错(神经网络的反向传播中)
数值梯度法
我们可以对权重的每一个值加一个很小的数h(一般取0.00001),然后通过计算更新前后的损失值除h得到梯度值
def
optimization(
self
,W):
w=W
h =
0.00001
a
=
0
fx,label=
self
.calculate_score(w)
gardient=np.zeros(w.shape)
it = np.nditer(w,
flags
=[
'multi_index'
],
op_flags
=[
'readwrite'
])
while not
it.finished:
ix=it.multi_index
old_value=w[ix]
w[ix]=old_value+h
fxh,label=
self
.calculate_score(w)
gardient[ix]=(fxh-fx)/h
w[ix]=old_value
it.iternext()
return
gardient
上面的代码即为对权重W的每个参数进行迭代,计算出偏导数然后储存在gardient中,据说使用[f(x+h)-f(x-h)]/2h取得的效果更好
梯度更新
在梯度负方向更新:因为我们的目的是要减小损失值,所以我们需要向梯度dW的负方向进行更新
步长的影响:梯度已经指明了函数在哪个方向的变化率是最大的,但是没有指明应该走多远。如果步长过大就会造成梯度更新振幅较大,在损失值骤降之后有急剧升高,无法取得较好的效果
如果步长较小,则会造成损失值更新较慢,浪费计算资源
分析梯度法(微分计算)
使用数值梯度法所得到的梯度值不仅不准确而且非常浪费计算资源,在神经网络上千万的参数中显得力不从心,利用微分计算梯度则成为了最有用的方法,但是微分计算容易出错,所以可以采用数值梯度法得到
的梯度进行比较验证,这个方法叫做
梯度检查,这是权重更新中至关重要的一步。
对于SVM函数的梯度更新
在SVM函数中我们采用错误分类的评分减去正确分类的评分来获取损失值,当这个值为0时,则代表这些参数对损失值没有贡献,不进行更新,当大于0时,则这些参数产生了贡献,其导数为
对应数据集的值,并且通过对应行减去参数,正确分类行加上梯度来减小损失值
def
derivative(
self
,w,train_data):
num_train=train_data[
0
]
dW=np.zeros(w.shape)
margin
=
0
loss=
0
num_class=w.shape[
0
]
for
i
in
range
(num_train):
scores=np.dot(w,train_data[i])
correct_score=scores[
self
.y_train[i]]
for
j
in
range
(num_class):
if
j==
self
.y_train[i]:
continue
margin=scores[j]-correct_score+
1
if
margin>
0
:
loss+=margin
dW[
self
.y_train[i],:]+=(-train_data[i])
dW[j,:]+=train_data[i]
return
loss,dW