赞
踩
目录
关于可视化神经网络的主要方法,Faizan Shaikh 举出了三个例子:
培养良好的工程习惯至关重要,前期可能需要一些学习成本,但到后期你会发现养成了好习惯反而能省很多时间,尤其是当任务量比较大或者是需要开展持续的工作的时候,你会发现之前的付出真的很有意义。
举个简单的例子,你如果研究目标检测,这是当前很热的领域哇,很多人都在做。要想突破可能需要更多的实验去尝试,在实验过程会用到COCO和Pascal VOC数据集,评价标准也相对复杂;同时目标检测的代码量又相对较大,为了提高运行效率部分代码甚至会用C++实现,目前google还提出了swift for TensorFlow,运行速度达到C语言级别。拿到这么冗杂的一套系统,无论是用来做研究还是做工程,都需要一套科学严谨的准则,否则到最后发现会乱成一锅粥,不知道对错。
本文就是作者总结的一些在深度学习领域一些好的规则和习惯,都是自己的踩坑心路历程。记住,播种一个行为,收获一种习惯,日积月累,必有回响。
通常我会使用一个简单的CNN模型(CV领域做5个卷积层,NLP领域做1个卷积层)将数据扔进去训练跑出一个baseline,这一步工作主要是为了验证数据集的质量。如果这个模型训练结果很差就不要先调试模型,需要检查一下你的训练集数据,看看图像的质量,图像标签是否正确,模型的代码是否正确等等,否则就是在做无用功。然后逐渐添加模型的复杂性,同时检验模型结构的每个层面(附加层、参数等)是否有效。
其次,在单个数据节点上训练模型:可以使用一两个训练数据点(data point)以确认模型是否过度拟合。神经网络应立即过度拟合,训练准确率为 100%,这表明模型符合;如果模型无法过度拟合这些数据点,就证明太小或存在 bug。
模型损失是评估模型性能的主要方式,也是模型设置重要参数以进行评估的依据,因此需要确保:
模型损失适用于任务(使用分类交叉熵损失(cross-entropy los)进行多分类问题或使用 focal loss 以解决不平衡问题);
正确衡量损失函数的重要性。如果你使用多种类型的损失函数,如 MSE、对抗性、L1、feature loss,,那么请确保所有损失以正确的方式排序。
为了调试神经网络,你需要理解神经网络内部的动态、不同中间层所起的作用,以及层与层之间是如何连接起来的。不过,你可能遇到以下问题:
不正确的梯度更新
表达式权重未得到应用
梯度消失或爆发
如果梯度值为 0,则意味着优化器中的学习率可能太小,且梯度更新的表达式不正确。除了关注梯度的绝对值之外,还要确保监视每个层匹配的激活、权重的大小。例如,参数更新的大小(权重和偏差)应为 1-e3。需要指出的是,一种称为 “Dying ReLU” 或“梯度消失”的现象中,ReLU 神经元在学习其权重的负偏差项后将输出为 0。这些神经元不会在任何数据点上得到激活。你可以采用梯度检验(gradient checking)通过数值方法逼近梯度以检验这些错误。如果它接近计算梯度,则正确实施反向传播。
初始方法:展现训练模型的整体结构,这些方法包括展示神经网络各个层的形状或过滤器(filters)以及每个层中的参数;
基于激活的方法:破译单个神经元或一组神经元的激活函数;
基于梯度的方法:在训练模型时,操作由前向或后向通道形成的梯度。
还有有许多可用的工具可用于可视化各个层的激活和连接,例如 ConX 和 Tensorboard。
调参是项技术活,调得好CVPR,调不好下海搬砖。
通常要选的超参数有卷积核大小和数目,批训练(batch size)大小,优化函数(optimizer),学习率等等,一般来说卷积核用3*3或者5*5,batch szie 用16或者32不会过拟合,optimizer用Adam(学习率建议用论文中默认的,我试过调整Adam的学习率,效果或都没有默认的好),激活函数用relu这个应该是大家的共识吧。还有就是先跑几百个epoch看loss的变化趋势。
Batch size:对于小数据量的模型,可以全量训练,这样能更准确的朝着极值所在的方向更新。但是对于大数据,全量训练将会导致内存溢出,因此需要选择一个较小的batch_size。如果这时选择batch_size为1,则此时为在线学习,每次修正方向为各自样本的梯度方向修正,难以达到收敛。batch_size增大,处理相同数据量的时间减少,但是达到相同精度的轮数增多。实际中可以逐步增大batch_size,随着batch_size增大,模型达到收敛,并且训练时间最为合适。
学习率(Learning rate):太低会导致收敛缓慢或陷入局部最小值的风险,太高则会导致优化发散。学习速率的设置第一次可以设置大一点的学习率加快收敛,后续慢慢调整;也可以采用动态变化学习速率的方式(比如,每一轮乘以一个衰减系数或者根据损失的变化动态调整学习速率)。
机器学习框架,如 Keras、Tensorflow、PyTorch、MXNet 现在都有关于使用学习率收敛缓慢文档或示例:
Keras https://keras.io/callbacks/#learningratescheduler
Tensorflow - https://www.tensorflow.org/api_docs/python/tf/train/exponential_decay
PyTorch - https://pytorch.org/docs/stable/_modules/torch/optim/lr_scheduler.html
MXNet - https://mxnet.incubator.apache.org/versions/master/tutorials/gluon/learning_rate_schedules.html
梯度剪切(Gradient clipping ):在反向传播中,用于剪切参数梯度的最大值或最大范数。
Batch normalization:用于标准化每层的输入,以对抗内部协变量移位问题。
随机梯度下降(Stochastic Gradient Descent ,SGD):使用动量、自适应学习率、Nesterov 更新。
正则化:增加了对模型复杂性或极端参数值的惩罚,l1正则化的目的是为了加强权值的稀疏性,让更多值接近于零。而l2正则化则是为了减小每次权重的调整幅度,避免模型训练过程中出现较大抖动。同时,它显著降低了模型的方差,并且不显著增加偏差。
Dropout:是另一种规范网络以防止过度拟合的技术。在训练时,以某个概率 p(超参数)保持神经元活动来实现丢失,否则将其设置为 0。结果,网络必须在每个训练 batch 中使用不同的参数子集,这减少了特定参数的变化而变得优于其他参数。数据第一次跑模型的时候可以不加dropout,后期调优的时候dropout用于防止过拟合有比较明显的效果,特别是数据量相对较小的时候。
优化器:机器学习训练的目的在于更新参数,优化目标函数,常见优化器有SGD,Adagrad,Adadelta,Adam,Adamax,Nadam。其中SGD和Adam优化器是最为常用的两种优化器,SGD根据每个batch的数据计算一次局部的估计,最小化代价函数。Adam优化器结合了Adagrad善于处理稀疏梯度和RMSprop善于处理非平稳目标的优点,能够自动调整学习速率,收敛速度更快,在复杂网络中表现更优。
变量初始化:常见的变量初始化有零值初始化、随机初始化、均匀分布初始值、正态分布初始值和正交分布初始值。一般采用正态分布或均匀分布的初始化值,有的论文说正交分布的初始值能带来更好的效果。实验的时候可以才正态分布和正交分布初始值做一个尝试。
权重初始化:始终用小random numbers来初始化权重以打破不同‘神经元’之间的对称性。
但权重应该小到多少?建议的上限是多少?什么概率分布用于生成随机数?此外,当使用sigmoid激活函数时,如果权重初始化为非常大的数字,则S形将饱和(尾部区域),那么会出现“死亡神经元”。如果权重非常小,那么渐变也会很小。因此,最好在中间范围内选择权重,以使这些权重均匀分布在平均值附近。
有关初始权重的适当值的研究很多,这对于有效收敛非常重要。要初始化均匀分布的权重,a uniform distribution可能是最佳选择之一。此外,如论文([Glorot和Bengio,2010][ http://proceedings.mlr.press/v9/glorot10a/glorot10a.pdf])所示,具有更多传入连接(fan_in)的神经元应该具有相对较小的权重。
由于有了这一系列的前人探索实验,这里总结出一个测试公式,我们可以直接使用权重初始化;
即从~ Uniform(-r, r)何处开始r=sqrt(6/(fan_in+fan_out))为tanh激活而绘制的权重,以及r=4*(sqrt(6/fan_in+fan_out))用于sigmoid激活的权重,其中fan_in前一层fan_out的大小和下一层的大小。(这个法则不错)
训练轮数:模型收敛即可停止迭代,一般可采用验证集作为停止迭代的条件。如果连续几轮模型损失都没有相应减少,则停止迭代。
预训练:对需要训练的语料进行预训练可以加快训练速度,并且对于模型最终的效果会有少量的提升,常用的预训练工具有word2vec和glove。
激活函数:常用的激活函数为sigmoid、tanh、relu、leaky relu、elu。采用sigmoid激活函数计算量较大,而且sigmoid饱和区变换缓慢,求导趋近于0,导致梯度消失。sigmoid函数的输出值恒大于0,这会导致模型训练的收敛速度变慢。tanh它解决了zero-centered的输出问题,然而,gradient vanishing的问题和幂运算的问题仍然存在。relu从公式上可以看出,解决了gradient vanishing问题并且计算简单更容易优化,但是某些神经元可能永远不会被激活,导致相应的参数永远不能被更新(Dead ReLU Problem);leaky relu有relu的所有优点,外加不会有Dead ReLU问题,但是在实际操作当中,并没有完全证明leaky relu总是好于relu。elu也是为解决relu存在的问题而提出,elu有relu的基本所有优点,但计算量稍大,并且没有完全证明elu总是好于relu。
特征学习函数:常用的特征学习函数有cnn、rnn、lstm、gru。cnn注重词位置上的特征,而具有时序关系的词采用rnn、lstm、gru抽取特征会更有效。gru是简化版的lstm,具有更少的参数,训练速度更快。但是对于足够的训练数据,为了追求更好的性能可以采用lstm模型。
特征抽取:max-pooling、avg-pooling是深度学习中最常用的特征抽取方式。max-pooling是抽取最大的信息向量,然而当存在多个有用的信息向量时,这样的操作会丢失大量有用的信息。avg-pooling是对所有信息向量求平均,当仅仅部分向量相关而大部分向量无关时,会导致有用信息向量被噪声淹没。针对这样的情况,在有多个有用向量的情形下尽量在最终的代表向量中保留这些有用的向量信息,又想在只有一个显著相关向量的情形下直接提取该向量做代表向量,避免其被噪声淹没。那么解决方案只有:加权平均,即Attention。
每轮训练数据乱序:每轮数据迭代保持不同的顺序,避免模型每轮都对相同的数据进行计算。
超参数调整:顺序网格搜索 - 随机搜索,网格搜索在传统的机器学习中一直盛行。但是,网格搜索在为DNN寻找最佳超参数方面效率并不高。主要是由于DNN在尝试使用不同的超参数组合时花费的时间。随着超参数的数量不断增加,网格搜索所需的计算也呈指数增长。
保持指数权重为2的权重维度:即使在使用最新的硬件资源处理最先进的深度学习模型时,内存管理仍然在字节级完成; 所以,将参数的大小保持为64,128(512,1024所有的权力2)更好。这可能有助于分解矩阵,权重等,从而轻微提高学习效率。尤其是处理GPU时。
关于超参数调优的一些trick,可以参考文章,如果失效了点击这里
贝叶斯优化
与我们迄今为止看到的其他方法不同,贝叶斯优化使用了算法的先前迭代的知识。使用网格搜索和随机搜索,每个超参数猜测都是独立的。但是,使用贝叶斯方法,每次我们选择并尝试不同的超参数时,表现都在一点点提升。
贝叶斯超参数调整背后的想法历史悠久且细节丰富。所以为了避免太多坑,我会在这里给你一个要点。但如果你感兴趣,一定要仔细阅读高斯过程和贝叶斯优化。
请记住,我们使用这些超参数调整算法的原因是,单独实际评估多个超参数选择是不可行的。例如,假设我们想要手动找到一个好的学习率。这将涉及设置学习率,训练模型,评估它,选择不同的学习率,再次训练你从头开始模型,重新评估它,并继续循环。
问题是,“训练你的模型”可能需要几天时间(取决于问题的复杂性)才能完成。因此,在会议提交截止日期之前,您只能尝试一些学习率。而你知道什么,你甚至没有开始设置动量。糟糕极了。
算法:贝叶斯方法试图建立一个函数(更准确地说,是关于可能函数的概率分布),用于估计模型对于某个超参数选择的好坏程度。通过使用这种近似函数(在文献中称为代理函数),您不必在设置、训练、评估的循环上花费太多时间,因为你可以优化代理函数的超参数。
例如,假设我们想要最小化此函数(将其视为模型损失函数的代理):
代理函数来自于高斯过程(注意:还有其他方法来模拟代理函数,但我将使用高斯过程)。就像我提到的那样,我不会做任何数学上的重要推导,但是所有关于贝叶斯和高斯的讨论归结为:
公式看上去很复杂。但是,让我们试着理解它。
左侧告诉你涉及概率分布(假设存在P)。在括号内看,我们可以看到它是P的概率分布,这是一个任意的函数。为什么?请记住,我们正在定义所有可能函数的概率分布,而不仅仅是特定函数。本质上,左侧表示将超参数映射到模型的度量的真实函数(如验证准确性,对数似然,测试错误率等)的概率为Fn(X),给定一些样本数据Xn等于右侧的式子。
现在我们有了优化函数,就开始进行优化吧。以下是在开始优化过程之前高斯过程的样子
在利用两个数据点迭代之前的高斯过程。
使用你最喜欢的优化器(大佬们一般喜欢最大化预期改善),但其实只需跟着信号(或梯度)引导,你还没有反应过来的时候就已经得到局部最小值。
经过几次迭代后,高斯过程在近似目标函数方面变得更好:
在利用两个数据点迭代三次之后的高斯过程。
无论你使用哪种方法,你现在都找到了代理函数最小化时的参数。那些最小化代理函数的参数居然是最优超参数(的估计)哦!好极了。
最终结果应如下所示:
在利用两个数据点迭代七次之后的高斯过程。
使用这些“最佳”超参数你的神经网络上进行训练,你应该会看到一些改进。但是,你也可以使用这些新信息重新一次又一次地重做整个贝叶斯优化过程。你可以想跑多少次这一贝叶斯循环就跑多少次,但还是要谨慎行事。你实际上在“跑钱”。你不要忘了AWS又不是免费的。
优点:贝叶斯优化比网格搜索和随机搜索提供更好的结果。
缺点:并行化并不容易。
我应该使用它吗:在大多数情况下,是的!唯一的例外是如果:
你是一个深度学习专家,你不需要一个微不足道的近似算法帮忙。
你拥有庞大的计算资源,并可以大规模并行化网格搜索和随机搜索。
如果你是一个频率论者/反贝叶斯统计书呆子。
如果你有兴趣,这儿还有一个纯pytorch编写的notebook实现。这可能会帮助你更好地了解幕后训练流程:https://colab.research.google.com/gist/iyaja/f9fc63ef65b6cc74409dc635c2d80861/hyperparameter-optimization-pytorch.ipynb
当然,所有这些算法——尽管它们都很好——并不总是在实践中起作用。在训练神经网络时还有许多其他因素需要考虑,例如你将如何预处理数据,定义模型,你还需要真的搞定足够跑这一整个流程的计算力。Nanonets提供易于使用的API来训练和部署自定义深度学习模型。它能负责所有的繁重工作,包括数据扩充,转移学习,以及超参数优化!Nanonets在其庞大的GPU集群上使用贝叶斯搜索来找到正确的超参数集,你压根不用担心得在最新的显卡上再大花一笔钱啦。一旦找到最佳模型,Nanonets就会把它放在云端,以便你使用其Web界面测试模型,或使用两行代码将其集成到你的程序中。
跟不完美模型说拜拜吧。
图像预处理的时候一般我会抽出部分图像观察,对图像中的噪声进行滤波,图像标签要验证一下,其他的预处理就结合实际情况来看了。一般来说,数据清洗的工作占比是多于写模型的工作(通常是7:3)。
常用的数据增强方法包括:图像缩放,图像翻转,图像裁剪,图像色彩的饱和度、亮度和对比度变换。
海康威视在ImageNet上曾经用过PCA Jittering方的法,但是由于这个方法的计算量过大,我没有在自己的训练中使用过。他们还使用了有监督的数据增强的方法,有兴趣的同学可以研究一下。
过采样:一种是增加样本少的类别的图像数目,可以用上述数据增强的方法。
欠采样:将样本多的类别图像数目减少,可以说是非常简单粗暴了。
权重法:增加少样本在训练时的权重,间接地增强了图像数目。
当任务变得复杂,数据规模变大时,框架提供的接口不能满足你的需求,这时你需要有自己的data generation function。例如,我使用keras时需要对输入图片进行多标签任务的训练,而keras本身不包含这样的接口,所以需要自己实现一个data generation function。通过查看官方文档和相关接口实现了一个多标签数据生成器(写这个数据生成器的时候被官方文档坑了一次,暂且不表,下次另起一文详说),代码如下:
- # 训练集/测试集数据生成器,替换flow_from_directory()
- def flow_from_2DList(directory=None, target_size=(256, 256),
- color_mode='rgb', classes=None, class_mode='categorical',
- batch_size=1, shuffle=True, seed=None, save_to_dir=None,
- save_prefix='', save_format='png', follow_links=False,
- subset=None, interpolation='nearest'):
- """
- A DirectoryIterator yielding tuples of (x, y)
- where x is a numpy array containing a batch of images
- with shape (batch_size, *target_size, channels) and
- y is a numpy array of corresponding labels.
- """
- # 每个epoch都要shuffle数据集
- random.shuffle(directory)
-
- # 参数初始化
- if directory is None: # python函数的默认参数如果是list这种可变类型,
- # 需要在函数体内进行初始化,
- # 否则会在上次的结果后继续使用list
- directory = [ [ 99999 for x in range(4) ] for y in range(batch_size) ]
-
- list_len = len(directory)
- print('\nlength of directory:', list_len, '\n\n')
- print('\nbatch_size:', batch_size, '\n\n')
- step = list_len//batch_size # 向下取整得到一个epoch需要多少个step
- print('\nsetp:',step,'\n\n')
-
- for i in range(step):
- # 每行一个记录读取训练/测试数据,返回(x,[y1,y2,y3])
- batch_images = []
-
- y_label_age = np.zeros((batch_size, 100))
- y_label_sex = np.zeros((batch_size, 2))
- y_label_sick = np.zeros((batch_size, 2))
-
- batch_directory = directory[i*batch_size : (i+1)*batch_size].copy()
-
- batch_size_num = 0 # 循环计数器
-
- for record in batch_directory:
- file_path = record[0]
- image = cv2.imread(file_path)
- image = cv2.resize(image, target_size)
-
- batch_images.append(image)
-
- age = record[1]
- sex = record[2]
- sick = record[3]
-
- # 将age,sex,sick转换成one-hot编码
- if age != 0:
- age -= 1
- age = to_categorical(age, num_classes = 100)
-
- sex = to_categorical(sex-1, num_classes = 2)
- sick = to_categorical(sick-1, num_classes = 2)
-
- y_label_age[batch_size_num,:] = age
- y_label_sex[batch_size_num,:] = sex
- y_label_sick[batch_size_num,:] = sick
-
- batch_size_num += 1
-
- batch_images = np.array(batch_images)
- y_labels = [y_label_age, y_label_sex, y_label_sick]
- data = (batch_images, y_labels)
- yield data
手动记录信息可能很难做到且进行多次实验,像 comet.ml 这样的工具可以帮助自动追踪数据集、更改代码、实验历史和生产模型,包括关于模型的关键信息,如超参数、模型性能指标和环境细节。
神经网络对数据、参数,甚至 packages 的微小变化都非常敏感,这导致了模型的性能下降。工作跟踪是标准化环境和建模工作流程的第一步。
GPU服务器编程,这个没什么好说的,肯定离不开的
第一层境界:放心大胆的使用百度或Google吧,看看有没有前人做过类似的工作;其次去知乎,CSDN,简书上搜搜看,可能会有文章带给我们启发;如果是找一些学习资料或视频直接B站或者YouTube搜索;还有就是关注一些DL技术公众号,PaperWeekly,CVer,量子位,机器之心等等..(我不是打广告,这些公众号大家可能都已经关注过)
第二层境界:等有一些基本的概念和了解之后就要去查阅相关领域的论文,去Google学术、百度学术或arxiv检索相关的文献,看论文是非常有必要和有用的,因为Paper也算是可信度高内容丰富的技术文档,一定要跳出舒适区,我几乎每天看一篇,短暂的时间看不懂很正常,但是氛围得养起来。
进阶:去github搜索相关论文的代码,论文和代码搭配使用效果更佳;大佬们的Github.io隐藏着很多干货;Kaggle或天池上面很多优秀的Kernel或许能够提供新的思路;一些大佬自己的网站上面也会有很多干货,说不定就能淘到我们想要的东西
首先当然是百度和google,github的issue,StackOverflow都是查找解决方案的好地方;如果身边有大牛的话也要虚心请教;一些技术交流QQ或微信群也是我们获取答案的好地方。比如我加了Pytorch交流群,里面很活跃,大佬们也不吝赐教,群里可能有人发一些最新资料,这对新手来讲是很好学习途径。多水一水群既可以开阔视野,同时又可以摸鱼闲聊。
在训练之前要合理规划文件夹的命名以及层级结构,使用简洁的英文单词命名文件。不要以为这是一个小事情,前期没有一个好的组织结构,没有规范的管理方式,等后期代码文件多了再想起重构是一件非常麻烦的事情。大家可以看一下Mask-RCNN仓库的组织结构,虽然项目工程很大,但整体结构井井有条。这和初高中时成绩好的同学作业本写得工整,成绩差的同学写得潦草一个道理。(我也是看到大佬们的工作才认识到自身的不足,多看牛人的代码真的是一种洗礼)
训练Loss日志:建议使用Tensorboard,因为现在tf和pytorch都支持这个工具,用起来也很简单,因此强烈推荐使用,我看到之前一些人的代码中可能写了log的方法,但现在官方提供了更强大的工具建议就不要用自己造的轮子了。注意,日志文件夹命名最好要有区别,建议使用时间戳命名,否则所有的日志混杂在一起将会乱成一锅粥。
模型存储:用一个文件夹来专门存放训练的模型,每个模型的命名建议花点心思,最好要包含有用的信息,方便查找使用
参数配置文件config:将训练网络的超参数和配置文件参数都放在专门的文件中统一管理,不要在训练主程序中东一坨西一坨,改来改去到最后很容易出现疏忽
数据集文件夹:此处建议所有的数据使用软连接的方式连接到工程文件夹,对原始数据加一些权限防止误删或更改;工程中的数据路径建议使用相对路径而非绝对路径!!绝对路径会导致你的工程文件夹换个地方就报错
函数工具包:把自己自定义的类和方法统一放到一个地方,增加模块的复用性。
训练和推断的程序:现在流行的做法就是把网络封装成一个训练器,在指定文件中配置好路径参数,直接运行程序就开始训练了,模型设计好了就专心训练和调参,可以看到keras训练和Detecron中都是这种设计模式,把数据灌进去就OK了。如果自己重新开始搭工程可以借鉴一下这种思路。
网络模型结构:当网络比较复杂的话,专门建一个包来存放网络结构
训练快照:也就是你当前训练出来模型对应的参数配置,个人觉得这点挺实用,防止最后训练了半天不知道用的啥参数啥数据集。
可视化:有一种方法可能会导致深度学习模式的训练出错。当模型被训练几个小时或几天,并且只有在训练结束后,我们才意识到出了问题。在这种情况下(这可能是非常合理的)) - 始终可视化训练过程。你可以采取最明显的一步是打印/保存日志的loss值,train error或test error等。除此之外,另一个良好的做法是使用一个可视化库来绘制几个训练样例或时代之间的权重直方图。这可能有助于跟踪深度学习模型中的一些常见问题,如梯度消失、梯度爆炸等。
总之:清爽的工程使人愉悦,杂乱的工程使人爆炸
我们开展一个任务,上手的第一个环节可能是clone别人的代码然后准备复现,Wait.第一步不要猴急猴急地去run人家的代码,我建议走下面的流程:
1.理清任务和评价标准,对算法精度有一个宏观的理解,也就是知道人家差不多能做到啥程度
2.先去看一下训练的数据,如果是图片的话先用肉眼随机抽样看一看,人类是一种非常高级和敏感的动物,说不定就会有意想不到的发现;看完了之后如果有代码基础就做一下数据统计分布,数据探索分析。
3.看一下原作者写的Readme文件,里面会概述本工程的一些组织结构和使用方法,这是很重要的一个环节。根据作者的提示可以去Run程序,训练推断都玩一玩,这样很容易让自己有成就感。
4.代码跑起来了不代表这个工程就是对的,一定要仔细地检查原作者的代码细节和实现思路,防止代码有坑,这点很关键(不要认为人家是大佬还发过论文,放出来的代码就没毛病了!)。我个人喜欢边看边注释原代码核心算法,如果搞清楚了可以先给身边的同事讲一讲,讲懂了说明自己也会了。彻底核实清楚之后才能把这份Solution作为自己的Baseline,不要等到效果迟迟不见提升回过头来才发现,哦,原来这个代码有问题啊(也不一定是人家代码有误,很可能是自己没搞懂就用和自己的任务冲突),悔恨当时怎么没注意呢>_<。
稍微了解一下框架的原理和实现,不论是对写代码还是理解DL基础知识都是很有帮助的。举个例子,你知道BN是咋实现的?反向传播是怎么搞的?看一下框架中的代码实现会帮助我们理解其中的原理,在调用函数的时候也不会犯错。
现在DL框架基本上分两大阵营(Mxnet和PP用户勿喷)——Pytorch和TF。建议看官方Document和Tutorial,这是极好的入坑指南,有精力的同学可以看一下源码的实现代码,太底层C实现的东西不建议新手玩家体验,还是先攻关主要矛盾(逃),如果修炼到一定境界还是要广泛涉猎一些的。
前期可以在baseline上做一些改动,然后看一下效果如何,分析问题出在哪里。譬如来一个连环问,是哪些类分不好?为什么分不好?是因为这个类特征难学吗?加一些什么模块或者机制能够更好的学特征?
然后多去看一些论文,汲取其中的精华,然后回来接着做实验,这是一种问题驱动型的思路。总之要养成多看论文的习惯,不论做检测、分割还是跟踪重识别,一些经典的DL论文是一定要看的,经典论文中有很多普适性的insight,多看论文才能拓宽思路有所启发。
确定好了思路开始跑实验验证,记住首先要小样本(少量的数据)测试自己的算法是否work,看一下有没有初步的效果。切忌一股脑成千上万开始训练,训了半天发现那个地方有疏忽又从头开始训练。
调参也是很重要的过程。深度学习训练说白了是一个数学优化过程,不要忽视了超参数对最终效果的影响,至于如何调参网上文章很多,比如过拟合怎么办,学习率怎么调,梯度爆炸咋处理?看再多理论没有亲身经历过永远是外行,我建议还是要亲自动手训练感受才深刻。结合数学理论宏观地分析原因,举个例子,mini-batch中的样本不平衡会如何影响优化方向?梯度爆炸的数学原因从何而来?如果Loss非凸会对优化带来什么影响?two-stage模型像不像是个条件概率?这样对自己的整体能力提升很有帮助。我个人认为,深度学习底层虽然不可知,但是从宏观上来讲还是服从统计学、优化理论的一些规则,善于从数学角度去分析模型很重要。
看大佬的代码你会发现人家不光是学术搞得好,代码敲得也贼666啊。多去看看名家的代码对自己是很有裨益的,甚至没事的时候可以跟着敲一敲练手。那么总结一下大佬的代码会发现人家的代码风格基本上遵循Google代码规范,而且有命名优雅、注释详细、函数之间耦合性低、逻辑清晰等优点。这些东西一时半会培养不来,但是平时注意培养代码风格习惯,日积月累肯定会有提高的。
不要在同一个坑里面摔倒三次(允许多摔一次)!!遇到问题及时解决和并记录,不做重复性工作。这是我多次失败的一个教训,我之前也没有做笔记的习惯,但其实人脑很容易遗忘,做笔记是一件很有必要的事情。
推荐一个适合程序员做笔记的工具Leanote,如果使用官方提供的服务器需要每月交五块钱。如果不想花钱可以将Leanote开源的的代码部署到本地服务器上
使用Wiki系统进行知识库管理。每天做完工作都要有一定的收获。我使用的是confluence,每天坚持写一写,对自己既是督促也是提升。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。