机器学习基本步骤

基本流程

0x00 概念定义

  • 在学习相关的知识后才能对任务有一定的了解,例如了解模型的目标、数据集的构成等
    • 数据属性:定性的(标称,二值,序数) 定量的(区间的,比率的);离散的 连续数据;独热码(使标量距离相等)
    • 数据相似度、相异度、邻近度等形成的距离;相关性分析
  • 需要对目标和具体的概念进行数学化的定义,如何定义问题,了解该如何确定样本特征信息(特别是有效特征)和标签。良好的特征数据可以减少很多地预处理和模型调优的工作,所有特征提取和变量挖掘是开始解决问题之前很关键的环节。
  • 在实践过程中,可以通过可视化操作增加对数据内容的理解和对数据分布特征的把握。根据特征情况,选择合适的模型结构或者算法。

0x01 数据预处理

将数据处理成符合计算的统一模式,需要清理、转换、合并、重塑,以提高数据质量:正确性(包含错误和噪音)、完整性(属性缺失)、一致性(编码,格式不一致)、时效性、可信性、可解释

建议封装成函数或者单独放在一个文件中,便于调用和管理

  • 读入数据并 划分数据集
    • 一般划分为训练集、验证集和测试集,前两个用于模型训练和训练时的泛化能力验证,测试集用于整体的泛化能力测试。如果训练数据较少,可以使用交叉验证的方式划分数据集。
  • 数据清洗与整理
    • 缺失值、重复值与异常值处理,去除Nan类型或不符合要求的数据: fillna 用指定的值(全局设定值、属性均值)或插值法(预测属性值 )去填充缺失。notnullisnull 用于判断是否为缺失。dropna用于过滤缺失值。pandas.drop_duplicates() 移除重复行。
    • 建议对训练数据进行类别的平衡 imbalance learning ,避免应部分类别过多而使得训练效果不佳。 参考
      • 常见的方法是,对数据进行 重采样 ,通过减少多数类样本(欠采样 Down Sampling , 如 ENN )或者通过复制、样本生成等方法增加少数类样本(过采样 Over Sampling ,如 SMOTE 、 SMOTEBagging ),强制数据从不平衡转为平衡,一般重采样常使用随机采样(不平衡程度越高越不推荐)或者基于距离建立邻域关系来采样,这些方法可以考虑使用 imblearn 库(集成类方法不太好用,多维度数据不能直接使用)。需要注意的是这种方法会改变原始数据的分布,训练时可以重采样,但是验证和测试的时候还应该保持原始分布,所以应该先划分训练测试集,再在训练集上进行重采样。
      • 或者使用代价敏感学习(cost-sensitive learning)调整目标函数,赋予少数类更高的权重(类加权,常常设置为归一化的不同类别样本数量比),让分错少数类的代价更高,从而使其对少数类更加敏感,这种方式并不增加训练的计算复杂度,能较好适应多分类问题。
      • 一般来说,类加权不会丢失类别的比例信息,相较于欠采样更加稳定。但是欠采样在实现类别平衡的同时,还能减少训练样本量(减少计算开支),去除了大量的冗余数据(噪声),尽可能使用了有价值的样本。
      • 集成学习类方法
      • 实践中可以考虑合理设置超参数,综合两者的优势,如将不平衡比例 20:1 ,通过欠采样降低到 4:1 ,再进行类加权的代价敏感学习,设置权重分别为 {0.2, 0.8} 。
      • 不平衡的数据分类效果一般不仅仅采用分类准确率 acc ,而更多的观察 AUCPRC 等综合了精度和召回率的指标。
      • 如果可以更多地获取新的有效特征,可能可以高效地分离数据,而不需要数据平衡,故而根据目标进行特征挖掘和选择是一个更重要的内容。
      • 当面对不平衡类问题时,批量训练分类器的「分层抽样」技术(通过消除批次内的比例差异)可使训练过程更加稳定。
    • 规格化、归一化处理;小波变换、傅里叶变换;数据压缩。如sklearn.preprocessing.StandardScaler支持标准化的操作。
    • 数据扩充:对样本做一些随机的变化,产生相似但又不完全相同的样本。主要作用是扩大训练数据集,抑制过拟合,提升模型的泛化能力。如图像通过随机改变亮暗、对比度和颜色属性,随机填充、裁剪或缩放,随机翻转等
  • 把训练样本乱序,生成批次数据
    • random.shuffle(index_list) 先将样本按顺序进行编号,建立ID集合index_list。然后将index_list乱序,最后按乱序后的顺序读取数据;打乱效率较高。
    • 通过大量实验发现,模型对最后出现的数据印象更加深刻。训练数据导入后,越接近模型训练结束,最后几个批次数据对模型参数的影响越大。为了避免模型记忆影响训练效果,需要进行样本乱序操作。
    • 生成批次数据: 先设置合理的batch_size,再将数据转变成符合模型输入要求的np.array格式返回。同时,在返回数据时将Python生成器设置为yield模式,以减少内存占用。
    • 如果数据量不能等分batch,最后剩余数据的数目小于BATCHSIZE,则剩余数据一起构成一个mini-batch
  • 校验数据有效性
    • 使用assert语句校验样本的数量和标签数据是否一致,类型是否符合要求。
  • 异步数据读取
    • 对于样本量较大、数据读取较慢的场景,建议采用异步数据读取方式。异步读取数据时,数据读取和模型训练并行执行,从而加快了数据读取速度,牺牲一小部分内存换取数据读取效率的提升

0x02 模型设计

  • 设计网络结构
    • 网络层的拼接,如卷积层、池化层、全连接层
    • 自定义的网络层类
    • 在网络层中引入非线性激活函数,从而增加神经网络的非线性能力。
      • Sigmoid:常用于二分类,曲线趋势很符合由量变到质变的过程的模式,有助于模型提升边界区域的分辨率。但Sigmoid函数在反向传播过程中,容易造成梯度的衰减。
      • Softmax函数:每个输出的范围均在0~1之间,且所有输出之和等于1,常用于多分类
      • relu
      • tanh
  • 设计损失函数/优化目标
    • 交叉熵误差(常用于分类任务的损失函数)$L=−[\sum_{k=1}^{n}t_k*log(y_k)+(1−t_k)log(1−y_k)]$
    • 均方误差(常用于回归问题)

0x03 训练配置

  • 选择优化算法
    • 四种比较成熟的优化算法:SGD、Momentum、AdaGrad和Adam
    • SGD: 随机梯度下降算法,每次训练少量数据,抽样偏差导致的参数收敛过程中震荡。
    • Momentum: 引入物理“动量”的概念,给梯度下降的过程加入一定的“惯性”累积,就可以减少更新路径上的震荡,使参数更新的方向更稳定。
    • AdaGrad: 根据不同参数距离最优解的远近,动态调整学习率。学习率逐渐下降,依据各参数变化大小调整学习率。参数更新的步长应该随着优化过程逐渐减少,减少的程度与当前梯度的大小有关。
    • Adam: 由于Momentum和AdaGrad两个优化思路是正交的,因此可以将两个思路结合起来,这就是当前广泛应用的算法。
  • 设置学习率
    • 通常使用标准的随机梯度下降算法更新参数,学习率代表参数更新幅度的大小,即步长。
    • 学习率不是越小越好,也不是越大越好。学习率越小,损失函数的变化速度越慢,意味着我们需要花费更长的时间进行收敛;只根据总样本集中的一个批次计算梯度,抽样误差会导致计算出的梯度不是全局最优的方向,且存在波动。在接近最优解时,过大的学习率会导致参数在最优解附近震荡,损失难以收敛
    • 可以在训练时可以尝试调小或调大,通过观察Loss下降的情况判断合理的学习率,或者设置学习率变化策略

0x04 训练加速

  • 多线程读取数据
  • GPU加速
  • 分布式训练
    • 模型并行:将一个网络模型拆分为多份,拆分后的模型分到多个设备上(GPU)训练,每个设备的训练数据是相同的。模型并行的实现模式可以节省内存,但是应用较为受限。一般适用于模型架构过大,网络模型的结构设计相对独立的网络。
    • 数据并行:每次读取多份数据,读取到的数据输入给多个设备(GPU)上的模型,每个设备上的模型是完全相同的。现在大多数情况下使用数据并行的方式。除此之外,还需要一个梯度同步机制,保证每个设备的梯度是完全相同的。梯度同步有两种方式:PRC通信方式和NCCL2通信方式(Nvidia Collective multi-GPU Communication Library)
    • 相比PRC通信方式,使用NCCL2(Collective通信方式)进行分布式训练,不需要启动Parameter server进程,每个Trainer进程保存一份完整的模型参数,在完成梯度计算之后通过Trainer之间的相互通信,Reduce梯度数据到所有节点的所有设备,然后每个节点在各自完成参数更新。

0x05 训练评估

判断模型训练的效果是否符合预期,反思存在的不足和改进的方法,避免过拟合。

  • 观测模型训练效果
    • 损失函数只是作为优化目标,一般使用准确率等作为衡量训练效果的函数
    • 训练效果可视化:观察训练效果变化曲线,权重矩阵的分布,模型的结构框架
  • 测试集评估,验证泛化能力(表示模型在没有见过的样本上的有效程度)。由于深度学习训练代价较高,一般不采用K-Fold交叉验证的方法,而是使用Hold-out(train/valid/test)验证

  • 常用评估指标

    参考使用 sklearn.metrics 模块的功能

    • ROC 与 AUC
    from sklearn import metrics
    fpr, tpr, thresholds = metrics.roc_curve(y, pred, pos_label=2) # fpr False Positive Rate ; tpr True Positive Rate
    roc_auc = metrics.auc(fpr, tpr)
    plt.plot(fpr, tpr, color='darkorange',
             lw = 2, label='ROC curve (area = %0.2f)' % roc_auc)
    
    • 混淆矩阵、精度、召回率和 F1
      • 准确率(accuracy)就是预测正确的数量除以预测总数,考察的目标是所有类别(大多数都找的对)。 confusion_matrix(y_true, y_pred, labels=None, sample_weight=None)
      • 类别精度(precision)= TP/(TP+FP) ,就是判断模型预测某样本属于该类的可信程度(找的对),考察的目标是所指定类别,如果是二分类问题,一般指的是正类。 precision_score(y_true, y_pred, labels=None, pos_label=1, average=‘binary’,)
      • 类别召回率(recall)= TP/(TP+FN) ,就是判断模型能够检测出该类的比率(找的全)。 recall_score(y_true, y_pred, labels=None, pos_label=1, average=‘binary’, sample_weight=None)
      • 类别的 F1 分数 = F1 = 2 * (precision * recall) / (precision + recall) ,是精度和召回率的调和平均值。 f1_score(y_true, y_pred, labels=None, pos_label=1, average=‘binary’, sample_weight=None)
  • 避免模型过拟合
    • 如果模型在训练集和测试集上均损失较大,则称为欠拟合;欠拟合表示模型还不够强大。如果在训练集上的损失小,在验证集或测试集上的损失较大,则称为过拟合;过拟合表示模型过于敏感或复杂。
    • 造成过拟合的原因是模型过于敏感或复杂,而训练数据量太少或其中的噪音太多。
    • 优化算法中加入正则化项(加入对参数规模的惩罚项):通过限制参数的数量或可能取值(参数值尽量小)实现降低模型的复杂度。
    • 批归一化(Batch Normalization)对神经网络中间层的输出进行标准化处理,使得中间层的输出更加稳定。使学习快速进行(能够使用较大的学习率)降低模型对初始值的敏感性,从一定程度上抑制过拟合。
    • 丢弃法(Dropout)是深度学习中一种常用的抑制过拟合的方法
      • 其做法是在神经网络学习过程中,随机删除一部分神经元。训练时,随机选出一部分神经元,将其输出设置为0,这些神经元将不对外传递信号。而在预测场景时,会向前传递所有神经元的信号。
      • 由于训练时由于部分神经元被随机丢弃了,输出数据的总大小会变小,故而训练时以比例p随机丢弃一部分神经元,不向后传递它们的信号;预测时向后传递所有神经元的信号,但是将每个神经元上的数值乘以 (1−p)。

0x06 模型保存与加载

  • 模型加载及恢复训练
    • 进行恢复训练的程序不仅要保存模型参数,还要保存优化器参数。

0x07 效果优化

围绕训练的目标,从以下几个方面展开优化思考

  • 样本:数据增强的方法
  • 假设:改进网络模型
  • 损失:尝试各种 Loss
  • 优化:尝试各种优化算法和学习率

其他技巧

  • 模型复用时,当几个模型的准确率在测试集上差距不大时,尽量选择网络结构相对简单的模型。往往越精巧设计的模型和方法,越不容易在不同的数据集之间迁移。
  • 数据存在限制条件时,可以通过含有可学习的参数的函数来表示数据,通过函数值域的方式来表现条件或者部分关系。
  • 特征融合是一种常用的特征增强手段,通过结合不同特征的长处,达到取长补短的目的。简单的融合方法有:特征(加权)相加、特征级联、特征正交等等。
  • Max-pooling会直接过滤掉模型认为不重要特征
  • 自动生成标签:如何定义标签?如何使标签合理有效。
  • 把字典中Lookup的过程转换为几个张量计算,从而可以使用GPU硬件的加速。
  • 论文中出现的Ground Truth部分可以理解为真值、真实的有效值或者是标准的答案。
  • 每次循环结束时删除 loss, 可以节约很少显存, 但聊胜于无.

模型压缩和加速

模型变小,运行速度变快。

  • 参数修剪和共享(parameter pruning and sharing)去除冗余和不重要的项。如矢量量化,二进制编码和稀疏约束来执行任务
  • 低秩因子分解(low-rank factorization) 使用矩阵/张量分解(SVD分解、Tucker分解、Block Term分解)来估计深度学习模型的信息参数
  • 转移/紧凑卷积滤波器(transferred/compact convolutional filters) 设计了特殊的结构卷积滤波器来降低存储和计算复杂度。仅支持卷积层。
  • 知识蒸馏(knowledge distillation)用性能好的大网络来教小网络,使小网络具备跟大网络一样的性能,但参数规模小(更紧凑),损失函数为与大模型输出的交叉熵和与groundtruth的交叉熵的加权和。

预处理提速

  • 尽量减少每次读取数据时的预处理操作, 可以考虑把一些固定的操作, 例如 resize , 事先处理好保存下来, 训练的时候直接拿来用
  • 对于大规模的小文件读取, 建议转成单独的文件, 可以选择的格式可以考虑: TFRecord(Tensorflow) , recordIO(recordIO) , hdf5 , pth , n5 , lmdb 等等
  • 预读取下一次迭代需要的数据

文档信息

Search

    Table of Contents