bluejad / deeplearning

deep learing note
1 stars 0 forks source link

CS231n: Convolutional Neural Networks for Visual Recognition #2

Open bluejad opened 7 years ago

bluejad commented 7 years ago

斯坦福CS231n:面向视觉识别的卷积神经网络

CS231n课程翻译系列

课程教师Andrej Karpathy

bluejad commented 7 years ago

利用有限差值计算梯度

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
# 要使用上面的代码我们需要一个只有一个参数的函数
# (在这里参数就是权重)所以也包含了X_train和Y_train
def CIFAR10_loss_fun(W):
  return L(X_train, Y_train, W)

W = np.random.rand(10, 3073) * 0.001 # 随机权重向量
df = eval_numerical_gradient(CIFAR10_loss_fun, W) # 得到梯度
loss_original = CIFAR10_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 = CIFAR10_loss_fun(W_new)
  print 'for step size %f new loss: %f' % (step_size, loss_new)

# 输出:
# 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
# for step size 1.000000e-03 new loss: 254.086573
# for step size 1.000000e-02 new loss: 2539.370888
# for step size 1.000000e-01 new loss: 25392.214036
bluejad commented 7 years ago

微分分析计算梯度

default

其中是一个示性函数,如果括号中的条件为真,那么函数值为1,如果为假,则函数值为0。虽然上述公式看起来复杂,但在代码实现的时候比较简单:只需要计算没有满足边界值的分类的数量(因此对损失函数产生了贡献),然后乘以就是梯度了。注意,这个梯度只是对应正确分类的W的行向量的梯度,那些行的梯度是:

j y_i

bluejad commented 7 years ago

普通的梯度下降

while True:
    weights_grad = evaluate_weights(loss_fun, data, weights)
    weights += - step_size * weighrts_grad
bluejad commented 7 years ago

小批量数据梯度下降(Mini-batch gradient descent)

while Ttue:
    bata_batch = sample_training_data(data, 256)
    weights_grad = evaluate_gradient(loss_fun, batch_data, weights)
    weights += step_size * weights_grad
bluejad commented 7 years ago

小批量数据策略有个极端情况,那就是每个批量中只有1个数据样本,这种策略被称为随机梯度下降(Stochastic Gradient Descent 简称SGD)

bluejad commented 7 years ago

在实际中使用分析梯度法,然后使用梯度检查来检查其实现正确与否,其本质就是将分析梯度法的结果与数值梯度法的计算结果对比

bluejad commented 7 years ago

default

bluejad commented 7 years ago

反向传播笔记

bluejad commented 7 years ago

反向传播是利用链式法则递归计算表达式的梯度的方法

bluejad commented 7 years ago

二元函数偏导数

default

导数的意义:函数变量在某个点周围的极小区域内变化,而导数就是变量变化导致的函数在该方向上的变化率

x

x

bluejad commented 7 years ago

函数关于每个变量的导数指明了整个表达式对于该变量的敏感程度

bluejad commented 7 years ago

梯度Δf是偏导数的向量,所以有

f

bluejad commented 7 years ago

加法操作求导

f x y x y

bluejad commented 7 years ago

最大值操作求导数

f x y max x y

bluejad commented 7 years ago

反向传播

f(x, y, z)= (x + y)z求导数

利用反向传播的方法,将公式分成两部分

q = x+y,f = qz

f qz

q x y

链式法则指出将这些梯度表达式链接起来的正确方式是相乘

default

bluejad commented 7 years ago

链式法则python代码实现

设置输入值

x = -2, y = 5, z = -4

先进行前向传播

q = x + y
f = q * z

先进行反向传播 先回传到 f = q * z

dfdz = q
dfdq = z

现在回传到 q = x + y dfdx = dfdq dqdx = -4 1.0 = -4.0, dfdq = q = -4,dqdx = 1.0 dfdy = dfdq dqdy = -4 1.0 = -4.0, dfdq = q = -4,dqdy = 1.0

dfdx = 1.0 * dfdq
dfdy = 1.0 * dfdq

输出fddx = -4.0,dfdy = -4.0 print 'dfdx = %f, dfdy = %f' %(dfdx, dfdy)

bluejad commented 7 years ago

default

bluejad commented 7 years ago

一个含输入x和权重w的2维的神经元,该神经元使用了sigmoid激活函数

x w 2 sigmoid

bluejad commented 7 years ago

一个含输入x和权重w的2维的神经元,该神经元使用了sigmoid激活函数

输入是[x0, x1],可学习的权重是[w0, w1, w2]

x w 2 sigmoid

bluejad commented 7 years ago

函数f_c使用对输入值进行了常量c的平移,f_a将输入值扩大了常量a倍

f_c c f_a a

bluejad commented 7 years ago
default
bluejad commented 7 years ago

sigmoid

bluejad commented 7 years ago

dq来代替dfdq

bluejad commented 7 years ago

神经元反向传播的python代码实现

随机变量

w = [2, -3, -3]
x = [-1, -2]

前向传播

dot = w[0] * x[0] + w[1] * x[1] + w[2]
f = 1.0/(1 + math.exp(-dot)

对神经元的反向传播 点积变量的梯度,用sigmoid函数求导

ddot = (1 - f) * f

回传到 x

dx = [w[0] * ddot, w[1] * ddot]

回传到 w

dw = [x[0] * ddot, x[1] * ddot, 1.0 * ddot] 得到输入的梯度

bluejad commented 7 years ago

begin

反向传播实践:分段计算

bluejad commented 7 years ago

default

bluejad commented 7 years ago

构建前向传播的代码模式:

x = 3
y = -4

前向传播

sigy = 1.0/(1 + math.exp(-y))
num = x + sigy
sigx = 1.0/(1 + math.exp(-x))
xpy = x + y
xpyspr = xpy ** 2
den = sigx + xpyspr
invden = 1.0/den
f = num * invden
bluejad commented 7 years ago

这样计算反向传播就简单了:我们对前向传播时产生每个变量(sigy, num, sigx, xpy, xpysqr, den, invden)进行回传。我们会有同样数量的变量,但是都以d开头,用来存储对应变量的梯度

bluejad commented 7 years ago

在反向传播的每一小块中都将包含了表达式的局部梯度,然后根据使用链式法则乘以上游梯度

bluejad commented 7 years ago

反向传播

回传 f = num * invden dnum = invden dinvden = num

回传 invden = 1.0/den dden = (-1.0/den ** 2) * invden

回传 den = sigx + xpyspr dsigx = (1) * dden dxpyspr = (1) * dden

回传 xpyspr = xpy * 2 `dxpy = (2xpy) dxpyspr`

回传 xpy = x + y dx = (1) * dxpy dy = (1) * dxpy

回传 sigx = 1.0/(1 + math.exp(-x)) dx += (1 - sigx) * sigx * dsigx

回传 num = x + sigy dx += (1) * dnum dsigy = (1) * dhum

回传 sigy = 1.0/(1 + math.exp(-y)) dy += (1 - sigy) * sigy * dsigy 完成

bluejad commented 7 years ago

矩阵相乘的梯度

前向传播

W = np.random.randn(5, 10)
X = np.random.randn(10, 3)
D = W.dot(X)

假设我们得到了D的梯度

dD = np.random.randn(*D.shape) # 和D一样的尺寸
dW = dD.dot(X.T) #.T就是对矩阵进行转置
dX = W.T.dot(dD)
bluejad commented 7 years ago

1

2

3

bluejad commented 7 years ago

反向传播实践:分段计算

end

bluejad commented 7 years ago

神经网络笔记

bluejad commented 7 years ago

大脑的基本计算单位是神经元(neuron)

bluejad commented 7 years ago

人类的神经系统中大约有860亿个神经元,它们被大约10^14-10^15个突触(synapses)连接起来

bluejad commented 7 years ago

每个神经元都从它的树突获得输入信号,然后沿着它唯一的轴突(axon)产生输出信号

bluejad commented 7 years ago

轴突在末端会逐渐分枝,通过突触和其他神经元的树突相连

bluejad commented 7 years ago

在神经元的计算模型中,沿着轴突传播的信号(比如x0)将基于突触的突触强度(比如w0),与其他神经元的树突进行乘法交互(比如w0x0)

bluejad commented 7 years ago

突触的强度也就是权重w

bluejad commented 7 years ago

突触的强度(也就是权重w),是可学习的且可以控制一个神经元对于另一个神经元的影响强度(还可以控制影响方向:使其兴奋(正权重)或使其抑制(负权重))

bluejad commented 7 years ago

在基本模型中,树突将信号传递到细胞体,信号在细胞体中相加。如果最终之和高于某个阈值,那么神经元将会激活,向其轴突输出一个峰值信号

bluejad commented 7 years ago
default
bluejad commented 7 years ago

一个神经元前向传播python实现

class Neuron(object):

    def forward(inputs):
        cell_body_sum = np.sum(inputs * self.weights) + self.bias
        firing_rate = 1.0/(1.0 + math.exp(-cell_body_sum))      sigmoid激活函数
    return firing_rate
bluejad commented 7 years ago

正则化损失从生物学角度可以看做逐渐遗忘,因为它的效果是让所有突触权重w在参数更新过程中逐渐向着0变化

bluejad commented 7 years ago

一个单独的神经元可以用来实现一个二分类分类器,比如二分类的Softmax或者SVM分类器

bluejad commented 7 years ago
sigmoid tanh

左边是Sigmoid非线性函数,将实数压缩到[0,1]之间。右边是tanh函数,将实数压缩到[-1,1]

bluejad commented 7 years ago

Sigmoid非线性函数

sigmoid 1

bluejad commented 7 years ago

现在sigmoid函数已经不太受欢迎,实际很少使用了,这是因为它有两个主要缺点:

bluejad commented 7 years ago

常用激活函数