前言

大家好,我是Kay,小白一个。以下是我完成斯坦福 cs231n-assignment2-FullyConnectedNets 这份作业的做题过程、思路、踩到的哪些坑、还有一些得到的启发和心得。希望下面的文字能对所有像我这样的小白有所帮助。

在之前我们有训练过一个两层的全连接网络,但是我们把求损失函数和求梯度放在同一个 function 里了,这不够“模块化”,等以后遇到更大型的网络这样的操作效率不高。在这份练习里,我们即将对每一层网络都分开构建前播和后播函数,再组装成一个 fully-connected nets。优化的事就交给接下来的几份训练吧。

(结尾有我个人对“神经网络”的学习心得!)

然而,首先一上来我就遇到了报错:
    run the following from the cs231n directory and try again:
    python setup.py build_ext --inplace
    You may also need to restart your iPython kernel
     
    后来发现核心的 bug 是:
    running build_ext
    building 'im2col_cython' extension
    error: Unable to find vcvarsall.bat
    这个错误怎么解决自行百度吧。哎,搞配置是一件特别令人沮丧的事情。

TODO1:完成全连接层和 ReLUs 的前播和后播函数

【思路】前播思路类似求 score 函数,后播就是 back propagation。四段代码见下,成功通过。
affine_forward: 
    x_re = np.reshape(x, (x.shape[0], -1))
    out = np.dot(x_re, w) + b

affine_backward:
    db = np.sum(dout, axis = 0)
    x_re = np.reshape(x, (x.shape[0], -1))    
    dw = np.dot(x_re.T, dout)
    dx = np.reshape(np.dot(dout, w.T), x.shape)

relu_forward:
    out = np.maximum(0, x)

relu_backward:
    dx = (x > 0) * dout

TODO2:利用"模块化"的函数,重构两层神经网络

【思路】其实还是一样的,先定义参数,然后求 scores,然后求 loss,然后求 gradient。

【开始Debug】这里和之前的作业不一样的是,W,b 和 reg 都是“全局变量”,要写成诸如 self.params['W1'] 的形式才能通过!
init:
        self.params['W1'] = weight_scale * np.random.randn(input_dim, hidden_dim)
        self.params['b1'] = np.zeros(hidden_dim)
        self.params['W2'] = weight_scale * np.random.randn(hidden_dim, num_classes)
        self.params['b2'] = np.zeros(num_classes)

scores:
        hid, cache = affine_relu_forward(X, self.params['W1'], self.params['b1'])
        scores, fc_cache = affine_forward(hid, self.params['W2'], self.params['b2'])

loss:
        loss, dout = softmax_loss(scores, y)
        loss += 0.5 * self.reg * (np.sum(self.params['W1']**2) + np.sum(self.params['W2']**2))

grads:
        d_hid, grads['W2'], grads['b2'] = affine_backward(dout, fc_cache)
        dx, grads['W1'], grads['b1'] = affine_relu_backward(d_hid, cache)
        #add reg
        grads['W1'] += self.reg * self.params['W1']
        grads['W2'] += self.reg * self.params['W2']

TODO3:阅读 solver,完成 FullyConnectedNet

【思路】solver 相当于提供了一个封装好的 API 集,输入一定的数据,就能直接得到整个数据训练后的结果。而完成全连接网络,这里有几个注意点就是:
1.如果有 n 层 hid,那么就会有 n+1 层+若干输入;
2.要处理好每个“第 i 层”的形状大小,还要处理列表和字典使用的下标的关系要对应好(i+=1)。
3. cnt 是隐藏层数、亦是 reLU 需要循环的次数;n 是总层数,亦是参数需要的下标数;在回传时,还要有个 r (= n - 当前层)。
    所以还是四步走:init, scores, loss, grads.
init:
        in_dim = input_dim #in_dim for starting loop
        lays_dims = hidden_dims + [self.num_classes]
        for i, out_dim in enumerate(lays_dims, start = 1):
            self.params["W%d" % i] = weight_scale * np.random.randn(in_dim, out_dim)
            self.params["b%d" % i] = np.zeros(out_dim)
            in_dim = out_dim

scores:
        n = self.num_layers
        cnt = n - 1 #loop reLU n-1 times
        cache = {}
        hid = X
        for i in range(cnt):
            i += 1
            hid, cache['%d'%i] = affine_relu_forward(hid, self.params['W%d'%i], self.params['b%d'%i])
        scores, fc_cache = affine_forward(hid, self.params['W%d' % n], self.params['b%d' % n])

loss & grads:
        loss, dout = softmax_loss(scores, y)

d_hid, grads['W%d'%n], grads['b%d'%n] = affine_backward(dout, fc_cache)
        for i in range(cnt):
            r = cnt-i     #reverse the parameter cnt to backpropagate the grads
            d_hid, grads['W%d'%r], grads['b%d'%r] = affine_relu_backward(d_hid, cache['%d'%r])

#add the reg!
        for i in range(n):
            i += 1
            loss += 0.5 * self.reg * np.sum(self.params['W%d' % i]**2)
            grads['W%d' % i] += self.reg * self.params['W%d' % i]

TODO4:更好的优化——SGD+momentum、RMSProp & Adam

【思路】
简单来说,SGD容易陷入局部最优解,而采用动量的形式进行更新,我们可以轻易地“滑”过局部解。
    v = config['momentum'] * v - config['learning_rate'] * dw
    next_w = w + v

RMSProp则是从另一个角度来解决这个问题的,它是在AdaGrad之上发展而来。AdaGrad是将SGD除以一个“梯度累计值“,但是顾名思义该值是”累增“的,所以会导致一个步长越来越短甚至瘫痪掉的问题。RMSProp就是用来改善AdaGrad的这个瘫痪问题,它为”梯度累计值“增加了一个衰减功能,使其不会那么快瘫痪掉。
    config['cache']=config['decay_rate']*config['cache']+(1-config['decay_rate'])*dw**2
    next_w=w-config['learning_rate']*dw/(np.sqrt(config['cache'])+config['epsilon'])

而Adam是将上方两种方法结合了在一起:它是对动量v进行带衰减的”梯度累计“。但是做到这里还不够,存在有一个小小的瑕疵:由于v是0,那么初始的几次SGD去除这个累计值可能导致步伐迈太大的问题,所以又要引入一个新的参数来解决它。
    config['m']= config['beta1']*config['m'] + (1-config['beta1'])*dw
    config['v']=config['beta2']*config['v']+(1-config['beta2'])*(dw**2)
    mt = config['m'] / (1 - config['beta1']**config['t'])
    vt = config['v'] / (1-config['beta2']**config['t'])
    next_w=w - config['learning_rate']*mt/(np.sqrt(vt)+config['epsilon'])

【开始 Debug】
这里Adam代码老是出错,百度了一下别人的代码,原来要再加一句:
    config['t'] += 1    #why?

【思考提升】我推测是用在for循环时,下标是从0开始数起,所以还要加一。

TODO5:更多的优化方案——BatchNormalization、Dropout

【思路】
BatchNormalization改变了输入的形状,使之更容易得到妥善的训练:减去均值是为了把数据都移至中心,使其对我们的分类器边缘变动不那么敏感;除以方差是为了使其正态化。简单来说就是要zero mean and unit variance。而值得注意的是,由于训练过程中的io是会改变的,所以BatchNormalization要持续每个层都做一次。

Dropout的话,则是随机丢掉某几个neurons,使整个训练过程不会过拟合。

具体操作的话,那是Q2Q3的事了,这里就不展开了。

TODO6:调节超参数

【思路】这里以最重要的一个超参数:learning rate为例,我们来讨论一下几个的调参技巧。
首先,一个核心的设计搜索思路,是持续地设定各种随机的数值,然后在整个训练过程中,要分阶段记录这个数值的各种表现情况(比如loss、validation accuracy)。这样做的意义是,很直观地看到我们这个数值的训练情况。
之后,让我们来瞧瞧这段代码:learning_rate = 10 ** uniform(-6, 1)。它有三个优点:范围广、随机、指数。
范围广的优点就不说了,要注意的点是必须大到把整个训练范围都包含住,所以要检查边缘值的表现情况。
随机的话是因为比起均匀地加减固定的值,会帮助我们更容易“踩中”更好的值。
而用指数而不是均匀搜索是因为这个超参数有乘法效应,当值是0.001时,改变0.1就会造成很大的“影响”,但是当值是10的时候改变0.1影响就会很小了。

然后,我要开始总结套路了,调参的具体做法是,跟着我四步走:建立各个超参数list,建立一个model和solver(具体需要的API在文件里有),输入solver.train(),然后比较该份acc和best_acc。

model = FullyConnectedNet([100,100], reg=0.05, weight_scale=5e-3,
                          normalization='batchnorm', dtype=np.float32, dropout=0.2)
solver = Solver(model, data,
        num_epochs=50, batch_size=100,
        update_rule='adam',
        optim_config={
            'learning_rate': 0.00075
            },
        print_every=100,
        lr_decay=0.95,
        verbose=True)
solver.train()

best_model = model

总结

到这里,我们算是大致上学完了“神经网络”的基础了。我们学习了:

  • 一个神经网络应该有三大基础东西 - 损失函数、梯度、权值(模板)。
  • 神经网络将一个函数封装成一个独立的神经元,然后将神经元分层连接,使之高效地传递信息
  • 我们认识了各种激活函数(推荐使用ReLU)
  • 我们可以对初始数据做预处理(推荐使用“减均值+除标准差”)
  • 我们还可以对隐藏层也做类似处理(Batch Normalization)
  • 我们应该对权进行初始化(推荐使用“随机数+除标准差”)
  • 如果train/val之间有gap,就是过拟合了(推荐使用“R2+Dropout”)
  • 在训练中,要对各种参数进行更新(推荐使用Adam)
  • 最后,还有对超参数进行调优的方法(推荐使用四步走)

【学习笔记】cs231n-assignment2-FullyConnectedNets相关推荐

  1. 转载CS231n课程学习笔记

    CS231n课程学习笔记 CS231n网易云课堂链接 CS231n官方笔记授权翻译总集篇发布 - 智能单元 - 知乎专栏 https://zhuanlan.zhihu.com/p/21930884 C ...

  2. CS231n 学习笔记(2)——神经网络 part2 :Softmax classifier

    *此系列为斯坦福李飞飞团队的系列公开课"cs231n convolutional neural network for visual recognition "的学习笔记.本文主要 ...

  3. CS231n 学习笔记(1)——神经网络 part1 :图像分类与数据驱动方法

    *此系列为斯坦福李飞飞团队的系列公开课"cs231n convolutional neural network for visual recognition "的学习笔记.本文主要 ...

  4. 深度学习总结——CS231n课程深度学习(机器视觉相关)笔记整理

    深度学习笔记整理 说明 基本知识点一:模型的设置(基本) 1. 激活函数的设置 2. 损失函数的设置 (1) 分类问题 (2) 属性问题 (3) 回归问题 3. 正则化方式的设置 (1) 损失函数添加 ...

  5. cs231n学习笔记——图像分类

    cs231n学习笔记--图像分类及代码实现 写在前面的废话 1.图像分类 2.数据驱动 3.图形分类流程 4.L1距离(曼哈顿距离) 5.L2距离(欧氏距离) 6. Nearest Neighbor分 ...

  6. CS231n 学习笔记(2)——神经网络 part2 :线性分类器,SVM

    *此系列为斯坦福李飞飞团队的系列公开课"cs231n convolutional neural network for visual recognition "的学习笔记.本文主要 ...

  7. 深度学习入门之PyTorch学习笔记:深度学习介绍

    深度学习入门之PyTorch学习笔记:深度学习介绍 绪论 1 深度学习介绍 1.1 人工智能 1.2 数据挖掘.机器学习.深度学习 1.2.1 数据挖掘 1.2.2 机器学习 1.2.3 深度学习 第 ...

  8. 三元组法矩阵加法java_计算机视觉学习笔记(2.1)-KNN算法中距离矩阵的计算

    本笔记系列以斯坦福大学CS231N课程为大纲,海豚浏览器每周组织一次授课和习题答疑.具体时间地点请见微信公众号黑斑马团队(zero_zebra)和QQ群(142961883)发布.同时课程通过腾讯课堂 ...

  9. cs224n学习笔记

    *. Tips: loga(x)log_a(x)loga​(x)在机器学习中默认为ln(x)ln(x)ln(x) 0. 主要参考: CS224n专栏 AILearners/blog/nlp/cs224 ...

  10. 斯坦福cs224n教程--- 学习笔记1

    一.前言 自然语言是人类智慧的结晶,自然语言处理是人工智能中最为困难的问题之一,而对自然语言处理的研究也是充满魅力和挑战的. 通过经典的斯坦福cs224n教程,让我们一起和自然语言处理共舞!也希望大家 ...

最新文章

  1. 如何科学地完成一场 AR 发布会?
  2. MyBatis关键配置-接口注入使用
  3. 【模板】并查集 两种路径压缩写法(类模板和函数模板)
  4. elasticsearch的分布式架构原理
  5. 新书正式定名《互联网运营智慧》
  6. opensaml2.0 java例子_OpenSAML 使用引导 I : 简介——关于OpenSAML你所需知道的一切
  7. Yann LeCun说是时候放弃概率论了,因果关系才是理解世界的基石
  8. 《数字图像处理 第三版》(冈萨雷斯)——第八章 图像压缩
  9. ecosys m5021cdn_京瓷ECOSYS M5021cdn驱动
  10. Git常见问题:fatal: Unable to create index.lock File exists 错误的解决办法
  11. 经纬财富:包头怎么炒现货白银
  12. ARM ADS中的AXD 调试经验集锦
  13. PPT图片怎么排列?
  14. 喜马拉雅随车听开通啦
  15. Oracle database TSPITR(TableSpace Point-In-Time Recovery) 表空间基于时间点的恢复
  16. vue 身份证校验、识别性别/生日/年龄
  17. 新BOS2.0物流业务逻辑
  18. 顾大嫂贴肉藏了尖刀 水浒传
  19. vulnhub_GoldenEye: 1
  20. 基于android的ipcamera编程,spydroid-ipcamera-master完整实现源码

热门文章

  1. 锐捷长ping_锐捷交换机常用操作命令
  2. 网络工程师还要学linux吗,网络工程师要学什么
  3. 开源字体lato fonts
  4. [答案解析]华工数电实验:简易交通灯控制电路的设计
  5. SAP MM模块之批次管理
  6. 二次拟合r方_excel曲线拟合中的决定系数R平方是如何求出来的?
  7. matlab的电路仿真,Matlab电路仿真
  8. turtle画动态时钟
  9. android 支持swf格式,安卓手机如何播放swf文件
  10. 你不知道的JavaScript(上中下三册) 电子版高清PDF -- 百度云下载