该repo是我在学习CS231n过程中,对所有notes和homework中包含的代码的编写,并加入了详细的comments。
README中是我的一些笔记和感想
完成Note1_NN,所有部分都做了详细标注,学习了怎么导入数据,发现python挺慢的
完成assignment1中的部分KNN
其中计算distance部分,完全不用loop没有想出来,借鉴一下网上的答案,本质上用了一个数学上平方展开加上矩阵广播,比较巧妙。
学习numpy库中的:
np.argsort(将矩阵中的元素按行或列从小到大排列,输出其对应的标号值)
np.bincount(输入一维矩阵,输出矩阵各元素出现的次数)
np.argmax(返回对应维度的最大索引)
np.linalg.normal(求输入矩阵的二范数,默认)
完成knn.ipynb
学习numpy库中的:
np.split(x,n), (x, [])(将行矩阵平均分成n个,或者按照list分成几份)
np.hstack()(水平的按列对数组或者矩阵进行堆叠)
np.vstack()(竖直的按行对数组或者矩阵进行堆叠)
制作交叉验证集时注意使用list嵌套list
时刻注意函数的输入 是什么,矩阵的形状
SVM分类器的损失函数鼓励正确的分类的分值比其他分类的分值高出至少一个边界值。
而softmax是计算所有类别的可能性,同时softmax分类器对于分数是永远不会满意的:正确分类总能得到更高的可能性,错误分类总能得到更低的可能性
个人觉得SVM方法在实现的过程中还是有些绕的,特别是理论求导修正W的部分,容易弄错
寻找矩阵中的元素可采用 a[list1, list2] 的方法
linear_svm时,发现对其中dW的计算理解的还不是很到位,注意每一个样本进去,所有的W都将更新,而不仅仅更新W[:, y[i]]
(a>0).astype(int) / (float), 让矩阵中a>0的设为1,小于0的设为0
在写的过程中用矩阵实现计算梯度始终不是很会,借鉴了别人的写法,但还是很难想
counts = (margin > 0).astype(int)
counts[range(N), y] = - np.sum(counts, axis = 1)
dW += np.dot(X.T, counts) / N + reg * W
np.random.choice在使用时先求随机索引会方便很多
np.argmax() 寻找矩阵中的最大值索引
svm.ipynb中源代码好像不太对,for x in results()改成for x in results.values(),访问存放的具体值
很明显能感觉到softmax的编程实现比SVM清晰很多,不需要一个一个对比
发现网上别人开源的作业源码的一处错误,很奇怪他是怎么编译成功的
for j in range(num_class):
if j == y[i]:
continue
dW[:, j] += X[i].T * np.exp(score[j]) / np.sum(np.exp(score))
原作者没有加转置符号
注意理解反向传播中的局部过程:
每个门单元都会得到一些输入并立即计算两个东西:1. 这个门的输出值和2.其输出值关于输入值的局部梯度。门单元完成这两件事是完全独立的,它不需要知道计算线路中的其他细节。而之后反向传播时门先得到最终输出关于该门输出的梯度,再用链式法则求关于上一级输出的梯度
sigmoid函数求导为 (1-sigmoid)*sigmoid
numpy.random.rand():根据给定维度生成[0,1)之间的数,且正太分布
numpy.random.random(()):根据给定维度生成[0,1)之间的随机浮点
np.random.normal(均值,方差,大小):和上面一个相比多了几个参数
sigmoid函数使用缺点:
激活值在接近0,1时,梯度几乎为0,即局部梯度几乎为0,导致反向传播时使总的梯度接近0
这样初始权重的时候也要特别注意,如果初始的过大,就有很多的神经元饱和,不再学习
sigmoid函数输出不是零中心的,这样接下来的输入也不是零中心的
relu变得流行:f(x) = max(0, x)
对随机梯度下降有巨大加速作用,但缺点是比较脆弱,注意设置好学习率
单层神经网络可以近似任何连续函数,但深层网络明显更好,同时正则化可以很好的防止过拟合
进行预处理很重要的一点是:任何预处理策略(比如数据均值)都只能在训练集数据上进行计算,算法训练完毕后再应用到验证集或者测试集上。
注意权重不能进行全零初始化,而是小随机数初始化,W = 0.01 * np.random.randn(D,H)
两层的神经网络完成过程中,弄错了W的大小,预先看好维度很重要!!!
按照作业的要求尝试调了一下参,感觉还是比较没有头绪的,只能根据前一次的结果缩小范围,但这对复杂庞大的数据集来说肯定不是好办法。
开始学习assignmen2
学习np库
np.prod((数字))表示连乘
np.linspace(a, b, c)从a到b均匀返回c个数字
python中的匿名函数
体会到神经网络中的模块化编写带来的便利!但要注意反向传播时dw要加上 reg×w
在完成任意隐藏层数的网络设计时,发现真的要考虑的因素多了很多,光是初始化就不一样,先把所有的层数存储在一个list,然后用循环来初始化;然后一开始就要把可能的操作用true或者false导入,代码中要判断是否使用。
batch normalize:在每一层relu输入之前加入归一化层,归一化成均值为0,标准差为1。然后引入可学习参数gamma,beta。然后通过gamma × x + beta就可以恢复原来的值
dropout:让每一层的神经元按一定概率随机失活,减少计算量
网络模块化过程中,先把所有要传递的参数放在一个字典里
在完成全连接网络的过程中深刻感受到熟知网络结构的重要性,否则虽然模块都集成好了,序号之类的还是很难懂,同时forward和backward是正好对称的。
预处理中的归一化等都是对每个像素点做的,而不是对每个样本做的!!
反向传播的时候正则化的求导项是没有加上的,最后dw要加上reg*W
得到的一些小trick:
- 先在小数据集上跑模型,看是否过拟合,若是则说明模型没问题
- 对于softmax,通过将正则化强度设置为0,看初始的loss是否为-ln(1/num_class)
- 在训练过程中,需要对loss值进行实时地打印,可以判断当前训练的状态:高的学习率高会使损失值下降很快,然后停止在一个比较高的位置。过于低的学习率会导致损失值下降很慢,训练过程太长。
- 判断过拟合程度,若训练集准确率一直大幅度高于验证集,说明此时模型过拟合。可以增大正则化强度,如增大L2正则惩罚,增加dropout的随机失活率等。如果训练集一直小幅度低于验证集,说明此时稍微过拟合,而如果训练集和验证集的准确率不相上下,说明此时模型有点欠拟合。
- 对于可能的梯度消散,可以打印前几层网络权值参数w的更新比例,经验结论是这个更新比例在1e-3比较比较好,若这个值太大,说明学习率太高;若这个值很小到1e-7,说明参数w基本上不会变,发生了梯度消散,解决方法为:1)使用Batch Normalization归一化每层之间的输出,2)激活函数改用线性ReLU,3)还有可能是学习率太低,4)减少网络层数
- 判断训练过程的有效性,把第一层结果可视化,看是否学习到了有效的特征。
- 实践中,先进行初略范围(比如10 ** [-6, 1])搜索,然后根据好的结果出现的地方,缩小范围进行搜索。进行粗搜索的时候,让模型训练一个周期就可以了,因为很多超参数的设定会让模型没法学习,或者突然就爆出很大的损失值。第二个阶段就是对一个更小的范围进行搜索,这时可以让模型运行5个周期,而最后一个阶段就在最终的范围内进行仔细搜索,运行很多次周期。
- 推荐的两个更新方法是SGD+Nesterov动量方法,或者Adam方法。
- 随着训练进行学习率衰减。比如,在固定多少个周期后让学习率减半,或者当验证集准确率下降的时候。
动量+SGD:SGD的问题在于每次更新有很多噪音,而动量的方法就是加入先前的梯度,增加稳定性,一般一开始梯度比较大,衰减率取0.5,,后期取0.9
神经网络:输出数据体在空间上的尺寸可以通过输入数据体尺寸(W),卷积层中神经元的感受野尺寸(F),步长(S)和零填充的数量(P)的函数来计算。输出数据体的空间尺寸为(W-F +2P)/S+1。常见的设置是F=3,S=1,P=1。使用零填充使输出和输入维度相同,当步长S=1时,P=(F-1)/2
注意几个概念:
- 感受野(receptive field):就是滤波器尺寸
- 零填充(zero—padding)
- 深度列(depth column):沿着深度方向排列、感受野相同的神经元集合
- 参数共享:每个深度切片上使用相同的滤波器(权重)
卷积神经网络最常见的形式就是将一些卷积层和ReLU层放在一起,其后紧跟汇聚层,然后重复如此直到图像在空间上被缩小到一个足够小的尺寸。
几个小滤波器卷积层的组合比一个大滤波器卷积层好:前者可以表达出输入数据中更多个强力特征,使用的参数也更少。唯一的不足是,在进行反向传播时,中间的卷积层可能会导致占用更多的内存。
层的尺寸设置规律:
- 输入层(包含图像的)应该能被2整除很多次。常用数字包括32(比如CIFAR-10),64,96(比如STL-10)或224(比如ImageNet卷积神经网络),384和512。
- 卷积层应该使用小尺寸滤波器(比如3x3或最多5x5),使用步长S=1。还有就是对输入数据进行零填充,这样卷积层就不会改变输入数据在空间维度上的尺寸,只负责对输入数据体的深度进行变换。
- 汇聚层负责对输入数据的空间维度进行降采样。最常用的设置是用用2x2感受野(即F=2)的最大值汇聚,步长为2(S=2)。
CNN的反向传播理解起来比较复杂,然后还有很注意每层过后的大小。
发现cnn.py里卷积层和池化层中的stride,padding大小没有交代清楚,在初始化w时就会有问题。
空间批量归一化:把(N,C,H,W)变换为(NHW,C),然后调用普通归一化
终于把assignment2完成,感觉整个作业的量还是非常大的,很大的收获是在更加理解卷积网络的同时,学习了模块化构建网络,可以很方便的调整参数
后面的课程主要是RNN,语言处理等,下一步会再学习下本课程中对深度框架的介绍,assignment3暂时不会进行。