【学习笔记】cs231n-assignment2-FullyConnectedNets
前言
大家好,我是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相关推荐
- 转载CS231n课程学习笔记
CS231n课程学习笔记 CS231n网易云课堂链接 CS231n官方笔记授权翻译总集篇发布 - 智能单元 - 知乎专栏 https://zhuanlan.zhihu.com/p/21930884 C ...
- CS231n 学习笔记(2)——神经网络 part2 :Softmax classifier
*此系列为斯坦福李飞飞团队的系列公开课"cs231n convolutional neural network for visual recognition "的学习笔记.本文主要 ...
- CS231n 学习笔记(1)——神经网络 part1 :图像分类与数据驱动方法
*此系列为斯坦福李飞飞团队的系列公开课"cs231n convolutional neural network for visual recognition "的学习笔记.本文主要 ...
- 深度学习总结——CS231n课程深度学习(机器视觉相关)笔记整理
深度学习笔记整理 说明 基本知识点一:模型的设置(基本) 1. 激活函数的设置 2. 损失函数的设置 (1) 分类问题 (2) 属性问题 (3) 回归问题 3. 正则化方式的设置 (1) 损失函数添加 ...
- cs231n学习笔记——图像分类
cs231n学习笔记--图像分类及代码实现 写在前面的废话 1.图像分类 2.数据驱动 3.图形分类流程 4.L1距离(曼哈顿距离) 5.L2距离(欧氏距离) 6. Nearest Neighbor分 ...
- CS231n 学习笔记(2)——神经网络 part2 :线性分类器,SVM
*此系列为斯坦福李飞飞团队的系列公开课"cs231n convolutional neural network for visual recognition "的学习笔记.本文主要 ...
- 深度学习入门之PyTorch学习笔记:深度学习介绍
深度学习入门之PyTorch学习笔记:深度学习介绍 绪论 1 深度学习介绍 1.1 人工智能 1.2 数据挖掘.机器学习.深度学习 1.2.1 数据挖掘 1.2.2 机器学习 1.2.3 深度学习 第 ...
- 三元组法矩阵加法java_计算机视觉学习笔记(2.1)-KNN算法中距离矩阵的计算
本笔记系列以斯坦福大学CS231N课程为大纲,海豚浏览器每周组织一次授课和习题答疑.具体时间地点请见微信公众号黑斑马团队(zero_zebra)和QQ群(142961883)发布.同时课程通过腾讯课堂 ...
- cs224n学习笔记
*. Tips: loga(x)log_a(x)loga(x)在机器学习中默认为ln(x)ln(x)ln(x) 0. 主要参考: CS224n专栏 AILearners/blog/nlp/cs224 ...
- 斯坦福cs224n教程--- 学习笔记1
一.前言 自然语言是人类智慧的结晶,自然语言处理是人工智能中最为困难的问题之一,而对自然语言处理的研究也是充满魅力和挑战的. 通过经典的斯坦福cs224n教程,让我们一起和自然语言处理共舞!也希望大家 ...
最新文章
- 如何科学地完成一场 AR 发布会?
- MyBatis关键配置-接口注入使用
- 【模板】并查集 两种路径压缩写法(类模板和函数模板)
- elasticsearch的分布式架构原理
- 新书正式定名《互联网运营智慧》
- opensaml2.0 java例子_OpenSAML 使用引导 I : 简介——关于OpenSAML你所需知道的一切
- Yann LeCun说是时候放弃概率论了,因果关系才是理解世界的基石
- 《数字图像处理 第三版》(冈萨雷斯)——第八章 图像压缩
- ecosys m5021cdn_京瓷ECOSYS M5021cdn驱动
- Git常见问题:fatal: Unable to create index.lock File exists 错误的解决办法
- 经纬财富:包头怎么炒现货白银
- ARM ADS中的AXD 调试经验集锦
- PPT图片怎么排列?
- 喜马拉雅随车听开通啦
- Oracle database TSPITR(TableSpace Point-In-Time Recovery) 表空间基于时间点的恢复
- vue 身份证校验、识别性别/生日/年龄
- 新BOS2.0物流业务逻辑
- 顾大嫂贴肉藏了尖刀 水浒传
- vulnhub_GoldenEye: 1
- 基于android的ipcamera编程,spydroid-ipcamera-master完整实现源码