来源:http://cs231n.github.io/optimization-1/
对于一个分类问题,可以概括为以下三部分: score function + lossfunction + optimization. 以图像分类为例,我们选择线性分类器,那么scorefunction可以写成如下形式:
同时Multiclass SupportVector Machine loss可以写成如下形式:
Loss function是W的函数,因此问题转化为最小化loss function的optimization问题。式中N是训练集中所有的样本数。也就是说,在计算loss function时候把所有的训练样本都用了个遍,后面可以看到,计算梯度的时候也用到了所有样本。
Strategy #1: A first very bad idea solution: Randomsearch
假设不知道W有什么规律,也不知道该怎么找,最笨的办法就是每次都随机一组数,如果loss function比之前小了就更新下weightvector W,代码如下:
# assume X_train is the data where each column is an example (e.g. 3073 x 50,000)
# assume Y_train are the labels (e.g. 1D array of 50,000)
# assume the function L evaluates the loss function
bestloss = float("inf") # Python assigns the highest possible float value
for num in xrange(1000):
W = np.random.randn(10, 3073) * 0.0001 # generate random parameters
loss = L(X_train, Y_train, W) # get the loss over the entire training set
if loss < bestloss: # keep track of the best solution
bestloss = loss
bestW = W
print 'in attempt %d the loss was %f, best %f' % (num, loss, bestloss)
# prints:
# in attempt 0 the loss was 9.401632, best 9.401632
# in attempt 1 the loss was 8.959668, best 8.959668
# in attempt 2 the loss was 9.044034, best 8.959668
# in attempt 3 the loss was 9.278948, best 8.959668
# in attempt 4 the loss was 8.857370, best 8.857370
# in attempt 5 the loss was 8.943151, best 8.857370
# in attempt 6 the loss was 8.605604, best 8.605604
# ... (trunctated: continues for 1000 lines)
显然这种办法是傻办法,而且很难找到最优解,是不可行的。
Strategy #2: Random Local Search
第二种策略是首先初始化个W, 然后让他一步一步往前走,定义步伐为 , 那么下一步他的位置就是 ,而且只有在loss function变小的时候才往下走,否则不走:
W = np.random.randn(10, 3073) * 0.001 # generate random starting W
bestloss = float("inf")
for i in xrange(1000):
step_size = 0.0001
Wtry = W + np.random.randn(10, 3073) * step_size
loss = L(Xtr_cols, Ytr, Wtry)
if loss < bestloss:
W = Wtry
bestloss = loss
print 'iter %d loss is %f' % (i, bestloss)
这种方法虽然比上一种好一点,但是计算成本太大,没有一个明确的方向,所以也不好。
Strategy #3: Following the Gradient
终于引出了梯度下降法,梯度的定义如下:
如果是一维情况就是求个微分,如果是高维情况就是求个偏微分。这里说的梯度就是高维向量中每一维的一个偏微分。
接下来介绍两种求梯度的方法。
numerical gradient:比较慢,是近似值,并且比较容易
analytic gradient:比较快,是精确值,但是容易犯错,需要微积分的知识。
Case1 Computing the gradient numerically with finitedifferences
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
# iterate over all indexes in x
it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
while not it.finished:
# evaluate function at x+h
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
# to use the generic code above we want a function that takes a single argument
# (the weights in our case) so we close over X_train and Y_train
def CIFAR10_loss_fun(W):
return L(X_train, Y_train, W)
W = np.random.rand(10, 3073) * 0.001 # random weight vector
df = eval_numerical_gradient(CIFAR10_loss_fun, W) # get the gradient
loss_original = CIFAR10_loss_fun(W) # the original loss
print 'original loss: %f' % (loss_original, )
# lets see the effect of multiple step sizes
for step_size_log in [-10, -9, -8, -7, -6, -5,-4,-3,-2,-1]:
step_size = 10 ** step_size_log
W_new = W - step_size * df # new position in the weight space
loss_new = CIFAR10_loss_fun(W_new)
print 'for step size %f new loss: %f' % (step_size, loss_new)
# prints:
# original loss: 2.200718
# for step size 1.000000e-10 new loss: 2.200652
# for step size 1.000000e-09 new loss: 2.200057
# for step size 1.000000e-08 new loss: 2.194116
# for step size 1.000000e-07 new loss: 2.135493
# for step size 1.000000e-06 new loss: 1.647802
# for step size 1.000000e-05 new loss: 2.844355
# for step size 1.000000e-04 new loss: 25.558142
上述代码使用 公式进行计算,实际情况使用centered difference formula会更好 ,原因[wiki ]
上述算法虽然可行但是效率太低,当特征维数太高时不能使用。
Case2 Computing the gradient analytically withCalculus
第一中方法虽然简单但是不是精确值,是近似值,实际可以通过直接推导的方式计算,但是也更容易出错,因此经常使用第二中方法计算然后再去用第一种方法验证下对不对,这种方法叫:gradient check。
———————————————————————————————————————
上述分析就是梯度下降法的思考过程,梯度下降法是目前最常用且成熟的方法,在优化神经网络loss function问题上。
但是当数据量较大时不适合用这种方法,因为将全部训练样本都用上只为了训练一个参数是非常耗时的,比如有1.2million个数据,使用上述方法是非常耗时的。
Mini-batch gradientdescent(MGD or BGD)是比较常用的方法,即只使用一部分训练样本做训练。假设一个极端的例子,如果1.2million样本中只有1000个样本是不一样的,剩下的全是这1000个样本的简单复制,那么即使使用全部样本训练最终也会和1000个样本的效果一样,因此下面的方法将会更快。当然实际情况可能不是完全复制,结果也只是真是结果的一个近似值,但是在牺牲一点精度的情况下可以大大提高效率。
MGD更极端一点的例子是只有一个图像,剩下的全是复制品,那么每次训练只用一个样本计算lossfunction,这种情况叫Stochastic Gradient Descent (SGD,有时候也叫on-line gradient descent,比较形象)。SGD这种情况实际使用并不多见,因为一次计算100个样本比计算1个样本100次要更有效率。有时人们说的SGD也是指的MGD。
MGD实际操作过程中一般选择样本个数时都选2的整次幂,因为在向量化的计算过程中速度可以更快。
注:上述文章从写完的word文档中直接copy,公式编辑器写的东西复制不过来,文章格式也有很多错乱,为了不浪费第二遍时间都不做整理。