lesson10 Wrapping up our CNN 重构CNN
目前很困难的部分是软件工程师的部分,如果没有做过软件工程的,在一开始可能会觉得课程很难。
我们要在软件工程的基础上重构大部分pytorch的功能。
- 05a_foundations.ipynb
- 用回调函数callback做按钮交互和消息响应:两个参数、*args、**kwargs
- __dunder__ 双下划线函数:类似于重载操作符
- 方差variance对异常值过于敏感;平均绝对偏差,也许更好。
- softmax的局限性:总要输出一个类别;会放大原来的差距;——更好的解决办法是:exp() / ( 1+exp() )
- 05b_early_stopping.ipynb
- lr finder:会提前终止训练,重构Runner和Callback,加入Exception
- 重构Runner,将原来的call写入了callback;加入Exception,实现cancel batch、cancel epoch、cancel train三个级别中的任何一个stop
- 在fit中加入了exception,有了Exception才能真正的停止
- 实现lr finder中,lr在[min_lr, max_lr]中,是log线性增长,而不是线性。是看随着lr的变化,我们的loss的变化情况,来寻找最好的learning rate。
- Cuda:把数据data和模型model都放在cuda上面。
- Hooks:重构pytorch的hooks,同时实现删除/析构函数,用于管理内存。
- ① 没有很好初始化的网络,激活元会不断的尖峰、掉落、再尖峰、掉落。如此往复。模型学习不到任何东西。且几乎大部分的激活元都处于无用状态。
- ② 使用kaiming_normal初始化,可怕的震荡消失了。但是并不是所有的激活元都在好好工作,虽然std的曲线图还不错,但是激活元直方图表明大部分的激活元90%处于非激活状态。此时虽然使用了kaiming_normal,但是lr=0.9,还是在前部分震荡的很凶的。
- ③ 所以建立了新的relu类,然后初始化使用了a=0.1 kaiming_normal;对relu的微小改进,以及kaiming_normal 1=0.1,使得激活元全部都活跃起来了。
最后的效果是,前面可怕的震荡减少了很多了,只有很少的尖峰了!!!
- lr finder:会提前终止训练,重构Runner和Callback,加入Exception
- 07_batchnorm.ipynb 如何更好的初始化来得到好的结果,这部分我们已经达到了极限。为了更进一步,我们使用归一化方法。
- Batch Normalization
目前还差几个模块:
- ①
- 使用了一个类来作为cb,cb有init,有call。
def slow_calculation(cb=None):
res = 0
for i in range(5):
res += i*i
sleep(1)
if cb: cb(i)
return res
class ProgressShowingCallback():
def __init__(self, exclamation="Awesome"): self.exclamation = exclamation
def __call__(self, epoch): print(f"{self.exclamation}! We've finished epoch {epoch}!")
cb = ProgressShowingCallback("Just super")
slow_calculation(cb)
Just super! We've finished epoch 0!
Just super! We've finished epoch 1!
Just super! We've finished epoch 2!
Just super! We've finished epoch 3!
Just super! We've finished epoch 4!
-
0.1 `*args`和`**kwargs`两个参数
- **kwargs 用的太多,虽然对开发人员很方便,但是对于用户来说很烦人。
-
0.2 __dunder__ 双下划线函数:类似于C++重载操作符
- __add__ 是操作符 + 的实现方法。
- __getitem__是[]运算符
- __init__ 是构造函数
- __del__ 是析构函数
- __repr__ 是str() 表示函数
- __len__ 是长度
-
0.3 Variance
- 1,2,4,18 四个数的标准差为std=6.89, var=std^2=47.1875;
- 但mean=6.25,(|1-6.25|+|2-6.25|+|4--6.25|+|18--6.25|)/ 4 = 5.87
- abs(X-u).mean 对异常值18不敏感,而方差variance对异常值很敏感。——只要有一个异常值,方差就变得很离谱。
- 在机器学习中,经常使用平均绝对偏差。但是平均绝对偏差没有得到充分的使用,实际上它是一个非常好用的方法。
- 异常值18在,pow(2)方差公式中会被放大。
- Jeremy注意到,在很多地方将 variance用 abs_mean替换,通常后者的效果会更好。
- 虽然有一种假设:我们应该总是使用方差variance,但实际上不是这样的。
-
(t-m).pow(2).mean().sqrt() tensor(6.8693) (t-m).abs().mean() tensor(5.8750)
协方差:当x和y具有较大的量级差异时,数值容易受绝对数值的影响, 无法衡量二者的相对相似性。
-
1. Softmax
- 什么应该是使用softmax?什么时候不该使用softmax?
- Image1和Image2的输出概率不应该是一样的,所以这里存在一些问题。
- 由于output中fish的值略高于其它的类别,经过exp()放大之后,其概率推高的更多了
- 不应该增加missing类,或者背景类。——因为non object类,根本不会起作用。
-
2. LrFinder
- 重构Runner,使得Runner可以在需要的时候停止回调。
run.fit(3, learn)
- __call__写在Callback类中更有意义。
- 原来的Callback始终会return True,没有False。导致Exception无法真正的stop。
- 把call写在callback类中,可以raise Exception
- 原来的Runner的fit()等函数中,没有Exception的代码,所以不会真正的stop
- 超级巧妙的方式:没有stack track,没有error,只有Exception作为control flow,也不是error处理技术。
- 我们可以用callback实现cancel batch、cancel epoch、cancel train三个级别中的任何一个stop
左侧为新的Runner类的设计,新Runner中的Call函数也不一样了。
-
实现lr finder
,i为当前的迭代batch,n为总共有多少个batch。 这个是当前batch应该设置的学习率lr。
- 为什么[min_lr, max_lr]中间,是靠上面的公式实现的?lr曲线是一个递增曲线,前面的lr是小,后半部分的lr很大。
,所以其实是[logmin, log max]的一个区间。
本来是一个线性增长,结果是exp(线性增长),所以超过了1后,指数增长的过快。在lr∈[1e-6,1]之前,都是exp(线性),增长率较慢的。
-
3. CUDA
- 把parameters(weights)放在GPU上。还有activations需要放在GPU上。
- self.model把模型放在cuda中,tensor放在GPU上。
- self.run.xb,self.run.yb把数据放在GPU上。
-
4. Refactor model 重构模型
- ① 把conv2d和relu结合起来,构成一个新的模块。
- ② 增加一个callback,实现每一个batch数据都能transform为【28,28】的格式,方便做卷积层的输入。这部分做一个BatchTransformXCallback,order=2.
- ③ 第一层的kernel=5,而不是3。原因在于要避免无效的运算。
-
5. Hooks——自定义的Hook,与pytorch的Hook对照,重构pytorch的Hook
- 我们想要知道模型里面是不是训练的很稳定?如何能让训练更快更好?我们想要看模型里面到底发生了什么?怎样能看到每层的activations激活元是怎样的状态呢?
- 我们已经知道不同的初始化方式会改变不同层的方差,我们如何能找出某一层的激活元是不是已经饱和了?如果饱和了会发生什么?
- 如果我们用自己的序列模型替换掉nn.Sequential会怎么样?
- 第一层的3×3的卷积核,有点浪费信息。因为你在为相同的信息占用更多的空间!
- 神经网络的全部意义在于提取一些有趣的特征,所以希望拥有较少的激活元。
- 所以在同一层增加同样意义的激活元是没有意义的;只有增加激活元的层数才能产生更多的多样性。增加同一层的冗余基函数是没有意义的!浪费激活元、浪费内存、也浪费了大量的计算!
Pytorch的Hooks是如何实现的呢?
增加一个hook类来对hook进行管理!
- 此处对Hooks加入了迭代器等的管理。
Hook类有enter和exit方法,有了这两个方法后,就可以使用上下文管理器,即with块。确保进入with块后,可以使用enter方法;退出with块后,可以使用exit方法来remove所有的hook,这样就不会污染我们的内存了。免得内存管理混乱。
6. BatchNormalization 小批量归一化
2023年3月16日10:03:39 update
在BatchNorm的加持下,训练出的每一层的权重的分布直方图如下图所示。①希望均值为0,方差在1附近;②但又不能全为0,权重为0的比例要小,dead activations。
从下面可以看出来:加了GeneralReLU后,分布更广;加了BactchNorm后,得到了很大的改变。
从下面可以看出来:加了GeneralReLU+BactchNorm后,得到了很大的改变。直方图发生了较大的改变,同时分布也发生了变化。BN的效果是显著的。
加入了1cycle,lr退火,得到了8个epoch的acc==98%,所以batch_normalization非常棒。
More norms
- Layer Norm:
Batchnorm在大多数情况下运行良好。但是不能应用于在线学习任务,或者batch比较小的超大型分布式模型上也不能使用。- online learning,比如机器人。在运行的同时训练。
- BatchNorm不适合于小批量batch。BN也不适合于RNN。
- 如果batch=1, 方差就是Inf。
因为Batch Normalization会有一个除以方差的操作;如果batch太小的话,方差为Inf,BN层就没有意义了!!!!!
所有的输入x除以一个Inf,整个层的激活元就变得没有意义了!!! 训练就会不稳定,或者完全不可能训练了!!!