最优化 Ooptimization

  • 损失函数可视化
  • 最优化

前面介绍了两部分:
1. 评分函数:将原始图像像素映射为分类评分值
2. 损失函数:根据评分函数和训练集图像数据实际分类的一致性,衡量某个具体参数集的质量好坏。
那么寻找到能使损失函数值最小化的参数的过程就是最优化 Optimization。

损失函数可视化

损失函数L可以看作是权重W的函数,在CIFAR-10中一个分类器的权重矩阵大小是[10,3073],即 L(W1,W2,....,W10) L ( W 1 , W 2 , . . . . , W 10 ) ,对其中某一个分类器 Wi W i 有3073个参数,想要得到 L L 关于 Wi W i 的可视化很难。
但是方法还是有的,随机生成一个权重矩阵W,并沿着此方向计算损失值, L(W+αW1) L ( W + α W 1 )

具体参考这里!
还有这样一篇paper!

总而言之,就是将高维空间压缩到二维, Wi[1,3073] W i [ 1 , 3073 ] 转换到 [1,1] [ 1 , 1 ] 然后在此基础上,画出loss关于它的值。
如果是压缩到三维,就是[1,3073]->[1,2],那么完整的loss就是这个形状的3073/2*10维的版本。

最优化Optimization

在数学上我们已经知道loss下降最快的方向就是梯度方向(gradient)。

有限差值法计算梯度

公式: df(x)dx=limh0f(x+h)f(x)h d f ( x ) d x = lim h → 0 f ( x + h ) − f ( x ) h
下面代码是一个输入为函数f和向量x,计算f的梯度的通用函数,它返回函数f在点x处的梯度:

def eval_numerical_gradient(f, x):
  """  
  一个f在x处的数值梯度法的简单实现
  - f是只有一个参数的函数
  - x是计算梯度的点
  """ 

  fx = f(x) # 在原点计算函数值
  grad = np.zeros(x.shape)   ##
  h = 0.00001

  # 对x中所有的索引进行迭代
  it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
  while not it.finished:

    # 计算x+h处的函数值
    ix = it.multi_index
    old_value = x[ix]
    x[ix] = old_value + h # 增加h
    fxh = f(x) # 计算f(x + h)
    x[ix] = old_value # 存到前一个值中 (非常重要)

    # 计算偏导数
    grad[ix] = (fxh - fx) / h # 坡度
    it.iternext() # 到下个维度

  return grad

可以使用上面这个公式来计算任意函数在任意点上的梯度。下面计算权重空间中的某些随机点上,CIFAR-10损失函数的梯度:

import random
import numpy as np
from cs231n.data_utils import load_CIFAR_batch
from cs231n.classifiers.softmax import softmax_loss_vectorized

##原始数据
cifar10_dir = 'cs231n/datasets/cifar-10-batches-py/data_batch_1' #只取一组数据
X_train, y_train = load_CIFAR_batch(cifar10_dir)  #(1000,32,32,3) (1000,1)
X_train = np.reshape(X_train, (X_train.shape[0], -1))   ##(1000,3072)
#归一化
mean_image = np.mean(X_train, axis = 0)
X_train -= mean_image
# add bias dimension and transform into columns
X_train = np.hstack([X_train, np.ones((X_train.shape[0], 1))])

##根据softmax损失函数据算loss,这个函数里面有用微分法计算梯度,但我们只取loss
def CIFAR_loss_fun(W):
    loss,dw = softmax_loss_vectorized(W, X_train, y_train, 0.000005)
    return  loss

W = np.random.randn(3073, 10) * 0.0001  ##随机权重向量
df = eval_numerical_gradient(CIFAR_loss_fun, W) ##计算权重空间下任意点关于loss的梯度

loss_original = CIFAR_loss_fun(W)  ##初始损失值
print("original loss: %f"%(loss_original,))

#查看不同步长的效果
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
    loss_new = CIFAR_loss_fun(W_new)
    print("for step size %f new loss: %f" % (step_size, loss_new))
original loss: 2.379955
for step size 0.000000 new loss: 2.379945
for step size 0.000000 new loss: 2.379855
for step size 0.000000 new loss: 2.378953
for step size 0.000000 new loss: 2.370042
for step size 0.000001 new loss: 2.291368
for step size 0.000010 new loss: 2.294673
for step size 0.000100 new loss: 12.200434


/home/panxie/文档/cs231n/assignment1/cs231n/classifiers/softmax.py:85: RuntimeWarning: divide by zero encountered in log
  loss = np.sum(-np.log(coef[range(num_train), y]))


for step size 0.001000 new loss: inf
for step size 0.010000 new loss: inf
for step size 0.100000 new loss: inf

微分分析计算梯度

有限差值法太太太太慢了,而且终究只是近似。第二个梯度计算方法是利用微分来分析,能得到计算梯度的公式(不是近似),用公式计算梯度速度很快,唯一不好的就是实现的时候容易出错。为了解决这个问题,在实际操作时常常将分析梯度法的结果和数值梯度法的结果作比较,以此来检查其实现的正确性,这个步骤叫做梯度检查。

以sofemax为例进行推导:

对单个样本其损失值:
Li=log(efyijefj) L i = − log ⁡ ( e f y i ∑ j e f j )

整个数据集的损失值:
L=1NiLi+λklW2k,l L = 1 N ∑ i L i + λ ∑ k ∑ l W k , l 2

L=1Nilog(efyijefj)+λklW2k,l L = 1 N ∑ i − log ⁡ ( e f y i ∑ j e f j ) + λ ∑ k ∑ l W k , l 2
这里 fyi f y i 是一个样本对应的真实标签的值,shape=(1,)

L=1Nilog(efyijefj)+λklW2k,l=1Ni(log(jefj)fyi)+λklW2k,l L = 1 N ∑ i − log ⁡ ( e f y i ∑ j e f j ) + λ ∑ k ∑ l W k , l 2 = 1 N ∑ i ( l o g ( ∑ j e f j ) − f y i ) + λ ∑ k ∑ l W k , l 2
这里 fyi f y i 是所有样本对应的真实标签的值,shape=(N,1) 代码中: score[range(N),y]

其中 fyi=Wyix f y i = W y i x , fi=Wjyix f i = W j ≠ y i x

对W求导, Wyi W y i Wjyi W j ≠ y i 的求导是不一样的

LWyi=1N(ieWyixjeWjxxxI(j==yi))+2λW ∂ L ∂ W y i = 1 N ( ∑ i e W y i x ∑ j e W j x x − x I ( j == y i ) ) + 2 λ W

注意这其中的求和,先以行为单位求和,即每个样本的loss,然后以列为单位求和,求出总的loss

代码实现:

score = X.dot(W)  ##(N,3073)*(3073,10) = (N,10)
score -= np.max(score, axis=1, keepdims=True)  # [N,10]
exp_score = np.exp(score)  # [N,10]
sum_score = np.sum(exp_score, axis=1, keepdims=True)   ##(N,1)以行为单位求和
coef = exp_score / sum_score #(N,10)
loss = np.sum(-np.log(coef[range(num_train), y])) ##取出coef中每个样本真实标签对应的那列,然后以行为单位求和,即总loss
loss /= num_train
loss += reg * np.sum(W * W)

coef_yi = np.zeros_like(coef)
coef_yi[range(num_train), y] = 1  ##W矩阵中{j==y_i}每一行真实标签对应的位置参数要-1
dW = X.T.dot(coef - coef_yi)
dW /= num_train
dW += reg * 2 * W

你可能感兴趣的:(cs231n)