Optimization is the process of finding the set of parameters WW that minimize the loss function.
从上一个chapter,得到loss function如下:
换一种写法:
其中wj是类j的权重向量.可以发现,L其实是wj的一个线性函数的和.
假设现在每张图xi只有一个维度,给三个图,他们的ground truth分别是class1,2,3.
那么,图0,1,2(x0,x1,x2)的loss分别如下:
下图是以w0为例的loss的可视化.左边是L0,L1,L2,右边则是L.
其中横轴为w0.
可以发现,sum of loss是一个碗装的凸函数,我们可以优化该函数得到最优解.
上图中x是一维的,如果变成多维状态,sum of loss将不再是一个凸函数.
注意到loss function是不可微分的,由于max函数,导致函数中存在一定的不可导点.
就是随机选择weights,然后看哪个loss低.
Our strategy will be to start with random weights and iteratively refine them over time to get lower loss.
有一种方式是把这个过程看作是盲人登山,山上每一个点都是一个loss(山的海拔).
随机选择一个方向,随机走一个step_size,如果loss下降,则继续往下走.
Ps: np.random.randn(10, 3073) * 0.001
原型numpy.random.randn(d0, d1, ..., dn)
其中d0, d1, ..., dn : int, optional.The dimensions of the returned array, should be all positive.
不需要随机选择方向,而是可以用数学方式计算出最速下降的方向.
The gradient is just a vector of slopes (more commonly referred to as derivatives) for each dimension in the input space.
两种方式:缓慢,近似但非常简单的数值梯度;快速但是易错,需要微积分的解析梯度.
这部分代码使用迭代器:
def eval_numerical_gradient(f, x):
"""
a naive implementation of numerical gradient of f at x
- f should be a function that takes a single argument
- x is the point (numpy array) to evaluate the gradient at
"""
fx = f(x) # evaluate function value at original point
grad = np.zeros(x.shape)
h = 0.00001
# np.nditer原来是numpy array自带的迭代器
#flags=['multi_index']表示对a进行多重索引.
#op_flags=['readwrite']表示不仅可以对a进行read(读取),还可以write(写入).
即相当于在创建这个迭代器的时候,我们就规定好了有哪些权限。
it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
while not it.finished:
# evaluate function at x+h
#it.multi_index表示元素的索引
ix = it.multi_index
old_value = x[ix]
x[ix] = old_value + h # increment by h
fxh = f(x) # evalute f(x + h)
x[ix] = old_value # restore to previous value (very important!)
# compute the partial derivative
grad[ix] = (fxh - fx) / h # the slope
#表示进入下一次迭代
it.iternext() # step to next dimension
return grad
这里h虽然在数学意义上趋向于0,但是这里用一个极小值1e-5代替就可以了.
新的weight向着df的反方向走(df就是grad).
W_new = W - step_size * df
Step_size就是learning rate,取值过大可能progress很大但是会有risky.取值过小progress过小.
用这种方式,对于有10*3072个参数的网络而言,我们每走一步,要计算30721个loss,这对于现代神经网络所拥有的庞大参数量而言,非常worse.
analytic gradient是易错的,因此常常需要用的numerical gradient和它进行check.
SVM的loss function如下:
现在我们要求Loss对于w的微分,对于每一张图,有class行w,对于每一行w,如果是ground truth那一行,就是:
Ps:这里1指的是,括号里面的判别式取true则为1,否则取0.
如果不是,则是:
优化的核心:
# Vanilla Gradient Descent
while True:
weights_grad = evaluate_gradient(loss_fun, data, weights)
weights += - step_size * weights_grad # perform parameter update
由于计算所有的gradient会非常的wasteful,所以我们选择一个小batch做这一工作(也会更加容易收敛).
# Vanilla Minibatch Gradient Descent
while True:
data_batch = sample_training_data(data, 256) # sample 256 examples
weights_grad = evaluate_gradient(loss_fun, data_batch, weights)
weights += - step_size * weights_grad # perform parameter update
最极端的是batch_size=1的情况,被称为随机梯度下降(on-line gradient descent). batch_size的大小一般不用cross-validation来确定.
march along 行进
piecewise-linear 分段线性
bumpy 颠簸的
Interchangeably 互换的
To reiterate, 重申
Perturbations 扰动
steepest descend 最速下降
at least in the limit as the step size goes towards zero 至少在步长接近零的范围内
instantaneous rate 顺势速率
error-prone 易错的
Calculus 微积分
generic function 通用函数
headache-inducing 令人头疼的
overshoot 超调
Derive 得到
vanilla 香草
Stochastic 随机的