笔记:CS231n+assignment2(作业二)(二)

一、参数更新策略

       1.SGD

  也就是随机梯度下降,最简单的更新形式是沿着负梯度方向改变参数(因为梯度指向的是上升方向,但是我们通常希望最小化损失函数)。假设有一个参数向量x及其梯度dx,那么最简单的更新的形式是:

x += - learning_rate * dx

其中learning_rate是一个超参数,表示的是更新的幅度。这是一个重要的参数,lr过大可能会出现loss异常的情况,过小会使训练时间过长,后面也会介绍lr参数更新的一些trick。

2. Momentum

    又被成为动量法,我觉得很多地方对这个方法并没有说清楚,我个人简单的理解是,在更新梯度的时候,我们要注意保留之前的梯度的信息,可以相信,一个梯度一直朝下减小的函数,一般不会遇到突然向左减小,所以加上动量以后,就可以处理这种情况,这个如果知道共轭点的话(不确定是不是这么叫的,也就是一个梯度下降的时候,向下一直减小,但是左右一直变化幅度很大的情况),就知道momentum的强大了。


def sgd_momentum(w, dw, config=None):
    """
    Performs stochastic gradient descent with momentum.
    config format:
    - learning_rate: Scalar learning rate.
    - momentum: Scalar between 0 and 1 giving the momentum value.
    Setting momentum = 0 reduces to sgd.
    - velocity: A numpy array of the same shape as w and dw used to store a moving
    average of the gradients.
    """
    if config is None: config = {}
    config.setdefault(‘learning_rate‘, 1e-2)
    config.setdefault(‘momentum‘, 0.9)
    v = config.get(‘velocity‘, np.zeros_like(w))
    next_w = None
    v = config[‘momentum‘] * v - config[‘learning_rate‘] * dw
    next_w = w + v
    config[‘velocity‘] = v    

    return next_w, config

 

 

 3.Nestero

这个是动量法的进阶版,既然我们要用之前梯度的信息,那么为什么不在更新当前梯度的时候,直接取最后的方向呢。

x_ahead = x + mu * v
# 计算dx_ahead(在x_ahead处的梯度,而不是在x处的梯度)
v = mu * v - learning_rate * dx_ahead
x += v

     在实际编程的时候,我们采用的是下面的方法,因为这样就可以和之前的代码统一起来了 ,因为一般来说我们只有dx:

v_prev = v # 存储备份
v = mu * v - learning_rate * dx # 速度更新保持不变
x += -mu * v_prev + (1 + mu) * v # 位置更新变了形式

    为什么可以这样做?据说只要根据X_ahead=x+mu*v应该是可以换算的,但是我试了一下...没推出来.我怀疑只是实际使用的时候效果相似,不一定推出来,因为你不可能去计算dx_ahead的..

4.RMSProp and Adam

这两个是学习速率的更新策略

def rmsprop(x, dx, config=None):
    """
    Uses the RMSProp update rule, which uses a moving average of squared gradient
    values to set adaptive per-parameter learning rates.
    config format:
    - learning_rate: Scalar learning rate.
    - decay_rate: Scalar between 0 and 1 giving the decay rate for the squared
    gradient cache.
    - epsilon: Small scalar used for smoothing to avoid dividing by zero.
    - cache: Moving average of second moments of gradients.
    """
    if config is None: config = {}
    config.setdefault(‘learning_rate‘, 1e-2)
    config.setdefault(‘decay_rate‘, 0.99)
    config.setdefault(‘epsilon‘, 1e-8)
    config.setdefault(‘cache‘, np.zeros_like(x))
    next_x = None
    cache = config[‘cache‘]
    decay_rate = config[‘decay_rate‘]
    learning_rate = config[‘learning_rate‘]
    epsilon = config[‘epsilon‘]
    cache = decay_rate * cache + (1 - decay_rate) * (dx**2)    #用一种更为平滑的方式更新lr,注意到这里是累加的,因为越往后惩罚项越大,lr越小
    x += - learning_rate * dx / (np.sqrt(cache) + epsilon)
    config[‘cache‘] = cache
    next_x = x    

    return next_x, config

def adam(x, dx, config=None):
    """
    Uses the Adam update rule, which incorporates moving averages of both the
    gradient and its square and a bias correction term.
    config format:
    - learning_rate: Scalar learning rate.
    - beta1: Decay rate for moving average of first moment of gradient.
    - beta2: Decay rate for moving average of second moment of gradient.
    - epsilon: Small scalar used for smoothing to avoid dividing by zero.
    - m: Moving average of gradient.
    - v: Moving average of squared gradient.
    - t: Iteration number.
    """
    if config is None: config = {}
    config.setdefault(‘learning_rate‘, 1e-3)
    config.setdefault(‘beta1‘, 0.9)
    config.setdefault(‘beta2‘, 0.999)
    config.setdefault(‘epsilon‘, 1e-8)
    config.setdefault(‘m‘, np.zeros_like(x))
    config.setdefault(‘v‘, np.zeros_like(x))
    config.setdefault(‘t‘, 0)
    next_x = None
    m = config[‘m‘]
    v = config[‘v‘]
    beta1 = config[‘beta1‘]
    beta2 = config[‘beta2‘]
    learning_rate = config[‘learning_rate‘]
    epsilon = config[‘epsilon‘]
    t = config[‘t‘]
    t += 1
    m = beta1 * m + (1 - beta1) * dx
    v = beta2 * v + (1 - beta2) * (dx**2)
    m_bias = m / (1 - beta1**t)
    v_bias = v / (1 - beta2**t)
    x += - learning_rate * m_bias / (np.sqrt(v_bias) + epsilon)
    next_x = x
    config[‘m‘] = m
    config[‘v‘] = v
    config[‘t‘] = t    

    return next_x, config

二、Batch Normalization

  接下来要介绍两个在训练神经网络的时候,非常有用技巧,首先是batch normalization,简单的解释:在每次NN的输入的时候,我们都知道要进行数据预处理,一般是让数据是zero means和单位方差的,这样对于训练是有好处的,但当数据走过几层以后,就基本不可能还保持这个特性了,BN做的事情就是在每一层的开始,加上这个操作,但是有的数据可能会因此丢失了一些信息,所以再加上beta和gama来恢复原始数据,这里beta和gama是可学习的。

#BN参数的初始化
if self.use_batchnorm and i < len(hidden_dims):
                self.params[‘gamma‘ + str(i+1)] = np.ones((1, layers_dims[i+1]))
                self.params[‘beta‘ + str(i+1)] = np.zeros((1, layers_dims[i+1]))

BN的的关键是前向传播,后向传播以及实际使用的。

按照上面推导出来的公式,即可进行BN层的构建:

def batchnorm_forward(x, gamma, beta, bn_param):
    mode = bn_param[‘mode‘]  #因为train和test是两种不同的方法
    eps = bn_param.get(‘eps‘, 1e-5)
    momentum = bn_param.get(‘momentum‘, 0.9)
    N, D = x.shape
    running_mean = bn_param.get(‘running_mean‘, np.zeros(D, dtype=x.dtype))
    running_var = bn_param.get(‘running_var‘, np.zeros(D, dtype=x.dtype))

    out, cache = None, None
    if mode == ‘train‘:
        sample_mean = np.mean(x, axis=0, keepdims=True)       # [1,D]
        sample_var = np.var(x, axis=0, keepdims=True)         # [1,D]
        x_normalized = (x - sample_mean) / np.sqrt(sample_var + eps)    # [N,D]
        out = gamma * x_normalized + beta
        cache = (x_normalized, gamma, beta, sample_mean, sample_var, x, eps)
        running_mean = momentum * running_mean + (1 - momentum) * sample_mean    #通过moument得到最终的running_mean和running_var
        running_var = momentum * running_var + (1 - momentum) * sample_var
    elif mode == ‘test‘:
        x_normalized = (x - running_mean) / np.sqrt(running_var + eps)    #test的时候如何通过BN层
        out = gamma * x_normalized + beta
    else:
        raise ValueError(‘Invalid forward batchnorm mode "%s"‘ % mode)

    # Store the updated running means back into bn_param
    bn_param[‘running_mean‘] = running_mean
    bn_param[‘running_var‘] = running_var

    return out, cache

def batchnorm_backward(dout, cache):
    dx, dgamma, dbeta = None, None, None
    x_normalized, gamma, beta, sample_mean, sample_var, x, eps = cache
    N, D = x.shape
    dx_normalized = dout * gamma       # [N,D]
    x_mu = x - sample_mean             # [N,D]
    sample_std_inv = 1.0 / np.sqrt(sample_var + eps)    # [1,D]
    dsample_var = -0.5 * np.sum(dx_normalized * x_mu, axis=0, keepdims=True) * sample_std_inv**3
    dsample_mean = -1.0 * np.sum(dx_normalized * sample_std_inv, axis=0, keepdims=True) - \
                                   2.0 * dsample_var * np.mean(x_mu, axis=0, keepdims=True)
    dx1 = dx_normalized * sample_std_inv
    dx2 = 2.0/N * dsample_var * x_mu
    dx = dx1 + dx2 + 1.0/N * dsample_mean
    dgamma = np.sum(dout * x_normalized, axis=0, keepdims=True)
    dbeta = np.sum(dout, axis=0, keepdims=True)

    return dx, dgamma, dbeta

Batch Normalization解决的一个重要问题就是梯度饱和,配合Relu可以说基本解决了梯度饱和的问题。

三、Dropout

  dropout是非常好理解的,就是在训练的时候以一定的概率来去每层的神经元,如下图所示:

  

  个人理解:每次训练进行deoptout的操作,可以防止过拟合,为什么呢,因为每次训练的模型都长的不一样,但是他们的参数实际上是共享的,可以简单的理解为是个bagging的操作,众所周知..bagging在machine learning中是防止过拟合的神器,每次限制神经元的数量也防止过大的神经网络对数据集的过拟合。

  还可以理解为dropout是一个正则化的操作,他在每次训练的时候,强行让一些feature为0,这样提高了网络的稀疏表达能力

  

def dropout_forward(x, dropout_param):
    p, mode = dropout_param[‘p‘], dropout_param[‘mode‘]
    if ‘seed‘ in dropout_param:
        np.random.seed(dropout_param[‘seed‘])

    mask = None
    out = None
    if mode == ‘train‘:
        mask = (np.random.rand(*x.shape) < p) / p    #注意这里除以了一个P,这样在test的输出的时候,维持原样即可
        out = x * mask
    elif mode == ‘test‘:
        out = x

    cache = (dropout_param, mask)
    out = out.astype(x.dtype, copy=False)

    return out, cache

def dropout_backward(dout, cache):
    dropout_param, mask = cache
    mode = dropout_param[‘mode‘]
    dx = None

    if mode == ‘train‘:
        dx = dout * mask
    elif mode == ‘test‘:
        dx = dout

    return dx

四、总结

  总的来说,很多trick都是在train model的时候很有用,不过向BN已经逐渐要被替代和取消了,所以说明实时的follow最新的会议是多么的重要......然后,一些推导之后还是要在自己做一做,预备在后面复习cs231n的讲义的时候做一遍推导。

时间: 2024-11-08 06:44:45

笔记:CS231n+assignment2(作业二)(二)的相关文章

Sharepoint2013搜索学习笔记之创建搜索服务(二)

第一步,进入管理中心,点击管理服务器上的服务 第二步,在服务器上选择需要承载搜索服务的服务器,并启动服务列表上的sharepoint server search 第三步,从管理中心进入管理服务应用程序 第四步,新建search service application 第五步,在弹出的新建窗口分别填好相应信息点击确定,主要注意的是 应用程序池可以选择已经有的,也可以自己填一个新的名称,选择填写新的之后,程序会在稍后新建一个应用程序池,一般推荐新建应用程序池. 默认情况,爬网组件会用配置好的搜索服务

(赵小明RHCE笔记)linux基础之二 vim的使用

一.introducing vim 1.vim是vi的新版本,是unix的标准文本编辑器  默认情况下执行vi运行的是vim 2.优点  速度:  简便化:  高可用性3.缺点  比一般的编辑器稍难二.VIM的使用1.VIM有多种模式2.三种主要模式  a.命令模式(默认):移动光标.剪切/粘帖文本,更改模式  b.插入编辑模式:修改文本内容  c.扩展模式:保存.退出等等3.按多次Esc会返回至命令模式4.进入文件 vim /tmp/passwd  q!:不保存退出  e!:重新读取该文件,之

java学习笔记 第二篇 核心技术(二)

第十四章 集合类 集合类用来存放对象的引用.继承关系如下图: 14.1 Collection 接口 是层次结构中的根接口,构成Collection的单位称为元素.Collection接口不能直接使用,但该接口提供了添加元素.删除元素.管理数据的方法. Collection接口常用方法: 14.2 List 集合 包括List接口以及List集合的所有实现类.List集合中的元素允许重复,各元素循序就是对象插入的顺序 1.List接口,两个重要方法: get(int index): 获取指定索引位

jqGrid 学习笔记整理——进阶篇(二)

jqGrid 学习笔记整理--进阶篇(二 ) 本篇开始正式与后台(java语言)进行数据交互,使用的平台为 JDK:java 1.8.0_71 myEclisp 2015 Stable 2.0 Apache Tomcat-8.0.30 Mysql 5.7 Navicat for mysql 11.2.5(mysql数据库管理工具) 一.数据库部分 1.创建数据库 使用Navicat for mysql创建数据库(使用其他工具或直接使用命令行暂不介绍) 2.创建表 双击打开上步创建数据库--右击T

[读书笔记]了不起的node.js(二)

这周做项目做得比较散(应该说一直都是这样),总结就依据不同情境双开吧-这篇记录的是关于node的学习总结,而下一篇是做项目学到的web前端的知识. 1.HTTP篇 node的HTTP模块在第一篇时接触过,这里来学习几个例程中出现的API. 1 var qs = require('querystring'); 2 3 require('http').createServer(function(req, res){ 4 if('/' == req.url){ 5 res.writeHead(200,

ufldl学习笔记与编程作业:Logistic Regression(逻辑回归)

ufldl出了新教程,感觉比之前的好,从基础讲起,系统清晰,又有编程实践. 在deep learning高质量群里面听一些前辈说,不必深究其他机器学习的算法,可以直接来学dl. 于是最近就开始搞这个了,教程加上matlab编程,就是完美啊. 新教程的地址是:http://ufldl.stanford.edu/tutorial/ 本节学习链接:http://ufldl.stanford.edu/tutorial/supervised/LogisticRegression/ 有了线性回归的基础再来学

ufldl学习笔记与编程作业:Softmax Regression(vectorization加速)

ufldl出了新教程,感觉比之前的好,从基础讲起,系统清晰,又有编程实践. 在deep learning高质量群里面听一些前辈说,不必深究其他机器学习的算法,可以直接来学dl. 于是最近就开始搞这个了,教程加上matlab编程,就是完美啊. 新教程的地址是:http://ufldl.stanford.edu/tutorial/ 本节是对ufldl学习笔记与编程作业:Softmax Regression(softmax回归)版本的改进. 哈哈,把向量化的写法给写出来了,尼玛好快啊.只需要2分钟,2

【C语言天天练(二二)】位操作

C的位运算符 1.二进制反码或按位取反:~ ~(10011010) = (01100101). 假设val是一个unsigned char,~val不改名原来val的值. 2.位与:& 二进制运算符&通过对两个操作数逐位进行比较产生一个新值. (10010011)&(00111101)=(00010001). C中的一个组合的位与赋值运算符:&=. 3.位或:| 二进制运算符|通过对两个操作数逐位进行比较产生一个新值. (10010011)|(00111101)=(1011

项目ITP(二) 二维码 拿起你的手机装一装,扫一扫 【每日一搏】

前言 系列文章:[传送门] 五一,期待的两天假期.我的生日,happy. [吐槽] 学校真恶心,半月前让我给他搞个东西,md,课题不加人.后来又来求,说钱(钱,咱不需要:我猜也不多).到现在,又来了,我晕.直接一句话,加我给做,时间问题,有可能只是半成品.不加不做. 正文 上文,我们讲了 二维码生成 解码 这次我们就用 android 来扫一下,实现android 扫二维码. 下载扫一扫 包结构-介绍 #com.zxing.xx 这是 zxing 库提供的 #BarCodeActivity 实战

ufldl学习笔记和编程作业:Softmax Regression(softmax回报)

ufldl学习笔记与编程作业:Softmax Regression(softmax回归) ufldl出了新教程.感觉比之前的好,从基础讲起.系统清晰,又有编程实践. 在deep learning高质量群里面听一些前辈说,不必深究其它机器学习的算法,能够直接来学dl. 于是近期就開始搞这个了.教程加上matlab编程,就是完美啊. 新教程的地址是:http://ufldl.stanford.edu/tutorial/ 本节学习链接:http://ufldl.stanford.edu/tutoria