机器学习-增量训练方法

1. 为什么要增量训练

做过机器学习的同学都知道,有时候训练数据是很多的,几十万几百万也是常有的事。虽然几十万几百万只看记录数不算多,但是如果有几百个特征呢,那数据集是很恐怖的,如果存成numpy.float类型,那绝对是把内存吃爆。我就是在这种情况下,开始考虑增量模型的增量训练。现在的机器都很便宜了,为什么不能放在服务器上面执行呢?我也有想过这个问题,但是在shell下操作和Windows下操作的开发测试速度差别很大啊,而且就经验来看,只要把数据准备好了,训练并不会占用太多的时间,就算内存装不下的数据,训练起来也就几分钟。没必要动用大数据集群吧。

在超大数据集上,一般有这么几种方法:1. 对数据进行降维,2. 增量训练,使用流式或类似流式处理,3. 上大机器,高内存的,或者用spark集群。

这里我要说的是增量学习。说到增量训练,其实和在线学习是一个意思,在线学习的典型代表是用SGD优化的logistics regress,先用数据初始化参数,线上来一个数据更新一次参数,虽然时间的推移,效果越来越好。这样就避免了离线更新模型的问题。

好了,说到这里你应该已经明白增量训练的主要用途了吧,主要有两个作用,一个是想办法利用全部的数据,另一个是想办法及时利用新的数据。

2. 特征的增量训练方法

特征的机器学习模型的粮食,对特征的处理同样的增量训练需要面对的问题。如果数据量很大,我们一般倾向于抽样训练,也就是在100万条数据里面抽取10万或者5万条数据做训练模型,多次抽样训练多个模型,然后对多模型的预测结果做加权。这是一个好方法,但是不是最佳的方法。

因为抽样后,数据的分布其实是被破坏了,举个例子,数据的分布是偏态,而且存在异常值,结果抽样后异常值丢失,此时数据不需要做异常值处理,而在下一次抽样因为存在异常值导致需要异常值处理,整个数据流程就会乱糟糟的。因此,需要对整个数据做数据分布分析,对特征的计算和变换应该是基于整个数据分布来,而不是抽样后的10万个样本的数据分布。

我们做特征变换,用到的主要是特征的最小值最大值,均值,方差,变异系数,k阶原点矩,k阶中心距,偏度,峰度等几个基本统计量,因此,我们需要算出每一个特征的这些统计量,在新的数据到来的时候,更新这个统计量,在训练模型之前,用全局统计量更新局部数据(抽样数据),以保证数据整体分布信息能够充分利用。

计算全量数据的基本统计量最大的问题是什么?

前面说了,内存不够啊。有什么解决办法呢?有啊,上大数据集群,spark一跑,全部出来了。

这不是我们这里要说的重点,我们要说的是,在有限的内存上,通过增量训练的方法实现特征的增量训练。核心方法是,如何只遍历一遍数据就能算出这些统计量

这就要参考阿里巴巴杨旭的著作《重构大数据统计》,在第二章中杨旭老师推导了如何只遍历一次数据就能计算大部分基本统计量的方法。下面是基本的基本统计量的计算方法:

n: 数据量
sum: 数据加和
sum2: 平方和
sum3: 立方和
sum4: 四次方和

简单求和: sum=∑Xi s u m = ∑ X i {\rm{sum}} = \sum\limits_{}^{} {{X_i}}
平方和: sum2=∑X2i s u m 2 = ∑ X i 2 sum2 = \sum\limits_{}^{} {X_i^2}
立方和: sum3=∑Xi3 s u m 3 = ∑ X i 3 sum3 = \sum\limits_{}^{} {{X_i}^3}
四次方和: sum4=∑X4i s u m 4 = ∑ X i 4 sum4 = \sum\limits_{}^{} {X_i^4}

均值: X¯=1n∑Xi=sumn X ¯ = 1 n ∑ X i = s u m n \bar X = \frac{1}{n}\sum\limits_{}^{} {{X_i}} = \frac{{sum}}{n}

方差: var=1n−1∑i=1n(Xi−X¯)2=1n−1(sum2−sum∗X¯) var = 1 n − 1 ∑ i = 1 n ( X i − X ¯ ) 2 = 1 n − 1 ( s u m 2 − s u m ∗ X ¯ ) {\mathop{\rm var}} = \frac{1}{{n - 1}}\sum\limits_{i = 1}^n {{{({X_i} - \bar X)}^2}} = \frac{1}{{n - 1}}(sum2 - sum*\bar X)

标准差: std=var−−−√ s t d = var std = \sqrt {{\mathop{\rm var}} }

变异系数: stdX¯ s t d X ¯ \frac{{std}}{{\bar X}}

2阶原点矩: 1n∑i=1nX2i=sum2n 1 n ∑ i = 1 n X i 2 = s u m 2 n \frac{1}{n}\sum\limits_{i = 1}^n {X_i^2} = \frac{{sum2}}{n}

2阶中心距: 1n∑i=1n(Xi−X¯)2=1n(sum2−sum∗X¯) 1 n ∑ i = 1 n ( X i − X ¯ ) 2 = 1 n ( s u m 2 − s u m ∗ X ¯ ) \frac{1}{n}\sum\limits_{i = 1}^n {{{({X_i} - \bar X)}^2}} = \frac{1}{n}(sum2 - sum*\bar X)

除了上面的基本统计量,最小值最大值 用得也很多,要计算也很简单,不再赘述。

好了,我们已经知道只遍历一遍数据就能计算这些统计量,接下来就是对数据进行分块,每次读取一块,然后进行特征计算,转换,之后将特征送到允许增量计算的模型中就好了。

下面是伪代码:

# 第一步:计算基本统计量的代码
# 每次读取10万行数据statistic=Nonefor sub_data in pd.read_csv(file, chunksize=100000):sub_statistic = calculate_statistic(sub_data)  # 计算update_statistic(sub_statistic)                # 更新# 第二步:用计算好的基本统计量进行特征转换,计算
for sub_data in pd.read_csv(file, chunksize=100000):new_feature = calculate_new_feature(sub_data, statistic)# 第三步:用计算好的基本统计量进行模型训练model.train_on_batchs(new_feature)

3. 模型的增量训练方法

理论上说,只要能是用SGD或者类似mini_batch SGD进行优化的模型都是可以实现增量训练的,其中以Linear ModelDeep Learning最为典型,下面就简要介绍现成工具的使用吧。

3.1. sklearn中的增量训练

sklearn中提供了很多增量学习算法。虽然不是所有的算法都可以增量学习,但是学习器提供了 partial_fit的函数的都可以进行增量学习。事实上,使用小batch的数据中进行增量学习是这种学习方式的核心,因为它能让任何一段时间内内存中只有少量的数据。
sklearn提供很多增量学习算法:

  • Classification

    • sklearn.naive_bayes.MultinomialNB
    • sklearn.naive_bayes.BernoulliNB
    • sklearn.linear_model.Perceptron
    • sklearn.linear_model.SGDClassifier
    • sklearn.linear_model.PassiveAggressiveClassifier
  • Regression
    • sklearn.linear_model.SGDRegressor
    • sklearn.linear_model.PassiveAggressiveRegressor
  • Clustering
    • sklearn.cluster.MiniBatchKMeans
  • Decomposition / feature Extraction
    • sklearn.decomposition.MiniBatchDictionaryLearning
    • sklearn.decomposition.IncrementalPCA
    • sklearn.decomposition.LatentDirichletAllocation
  • sklearn.cluster.MiniBatchKMeans

对于sklearn的学习方法,我们要做的主要就是实现上面说的特征增量训练的方法即可。

3.2. lightGBM的增量训练方法

lightGBM作为xgboost的有力竞争者,其增量训练用起来也是很方便呢,lightGBM设计之初也考虑到了超大数据集的应用,除了分布式训练支持外,也提供了增量训练的方法。
下面是回归预测的增量训练方法:

# 第一步,初始化模型为None,设置模型参数
gbm=None
params = {'task': 'train','application': 'regression','boosting_type': 'gbdt','learning_rate': 0.2,'num_leaves': 31,'tree_learner': 'serial','min_data_in_leaf': 100,'metric': ['l1','l2','rmse'],  # l1:mae, l2:mse'max_bin': 255,'num_trees': 300}
# 第二步,流式读取数据(每次10万)
i=1
for sub_data in pd.read_csv(file, chunksize=100000)# 区分特征x和结果Yx_data = sub_data[x_cols]y_data = sub_data[y_col]# 创建lgb的数据集lgb_train = lgb.Dataset(x_data, y_data.values)lgb_eval = lgb.Dataset(test[x_cols], test[y_col].values, reference=lgb_train)# 第三步:增量训练模型# 重点来了,通过 init_model 和 keep_training_booster 两个参数实现增量训练gbm = lgb.train(params,lgb_train,num_boost_round=1000,valid_sets=lgb_eval,init_model=gbm,             # 如果gbm不为None,那么就是在上次的基础上接着训练feature_name=x_cols,early_stopping_rounds=10,verbose_eval=False,keep_training_booster=True) # 增量训练 # 输出模型评估分数score_train = dict([(s[1], s[2]) for s in gbm.eval_train()])score_valid = dict([(s[1], s[2]) for s in gbm.eval_valid()])print('当前模型在训练集的得分是:mae=%.4f, mse=%.4f, rmse=%.4f'%(score_train['l1'], score_train['l2'], score_train['rmse']))print('当前模型在测试集的得分是:mae=%.4f, mse=%.4f, rmse=%.4f' % (score_valid['l1'], score_valid['l2'], score_valid['rmse']))i += 1

从打印结果看,效果并不理想,也许是特征不给力。在例子中我每次读取10万数据,也许这10万数据已经能代表整个数据集了吧。

[2018.08.06 15:08:30] 第 1 批次数据训练模型
[2018.08.06 15:08:30] 当前模型在训练集的得分是:mae=3.8034, mse=186.6290, rmse=13.6612
[2018.08.06 15:08:30] 当前模型在测试集的得分是:mae=4.1202, mse=376.4796, rmse=19.4031
[2018.08.06 15:08:46] 第 2 批次数据训练模型
[2018.08.06 15:08:46] 当前模型在训练集的得分是:mae=4.0651, mse=346.0248, rmse=18.6017
[2018.08.06 15:08:46] 当前模型在测试集的得分是:mae=4.1143, mse=370.8750, rmse=19.2581
[2018.08.06 15:09:02] 第 3 批次数据训练模型
[2018.08.06 15:09:02] 当前模型在训练集的得分是:mae=3.9073, mse=502.9016, rmse=22.4255
[2018.08.06 15:09:02] 当前模型在测试集的得分是:mae=4.1229, mse=373.6719, rmse=19.3306
[2018.08.06 15:09:19] 第 4 批次数据训练模型
[2018.08.06 15:09:19] 当前模型在训练集的得分是:mae=4.2073, mse=525.9846, rmse=22.9344
[2018.08.06 15:09:19] 当前模型在测试集的得分是:mae=4.1955, mse=415.1820, rmse=20.3760
[2018.08.06 15:09:37] 第 5 批次数据训练模型
[2018.08.06 15:09:37] 当前模型在训练集的得分是:mae=3.8308, mse=208.4748, rmse=14.4387
[2018.08.06 15:09:37] 当前模型在测试集的得分是:mae=4.1798, mse=389.9514, rmse=19.7472
[2018.08.06 15:09:55] 第 6 批次数据训练模型
[2018.08.06 15:09:55] 当前模型在训练集的得分是:mae=4.0334, mse=226.4893, rmse=15.0496
[2018.08.06 15:09:55] 当前模型在测试集的得分是:mae=4.1701, mse=374.0758, rmse=19.3410
[2018.08.06 15:10:14] 第 7 批次数据训练模型
[2018.08.06 15:10:14] 当前模型在训练集的得分是:mae=4.3154, mse=459.7565, rmse=21.4419
[2018.08.06 15:10:14] 当前模型在测试集的得分是:mae=4.1799, mse=382.8426, rmse=19.5664
[2018.08.06 15:10:26] 第 8 批次数据训练模型
[2018.08.06 15:10:26] 当前模型在训练集的得分是:mae=4.2520, mse=293.1202, rmse=17.1208
[2018.08.06 15:10:26] 当前模型在测试集的得分是:mae=4.1891, mse=382.2080, rmse=19.5501

3.3. keras的增量训练方法

keras作为深度学习的API封装,同样实现了增量训练的方法。在深度学习中,由于训练周期很长,几周甚至一两个月很常见,如果因为异常退出要重新训练是很可怕的。因此,在TensorFlow等框架中,都会有从上次异常退出的地方接着训练的方法。
其实也很好理解,比如每训练1万次,就把训练过程的网络结构参数和权重保存下来,如果没问题就接着训练,有问题就把最近一次保存的参数和权重加载进来初始化网络,接着训练。这样即使出错问题也不大。
keras中的增量训练方法也是一个道理,每次将一小部分数据丢进网络训练,然后保存网络,下次新的数据过来再加载网络接着训练。

下面是模型伪代码:

import kerasdef normalize(array, min_, max_):"""使用全局统计量对局部数据进行归一化"""write your normalize function heredef y_normaloize(array, min_, max_):"""使用全局统计量对局部数据进行归一化"""write your normalize function here# 第一步:初始化模型为None,设置模型保存路径
model = None
model_file = r'E:\data_tmp\nn_data\nn_model_last_train.h5'# 第二步:每次读取10万行数据
i = 1
for train in pd.read_csv(file, chunksize=100000):# 第三步:# 区分特征X和目标Yx_data = train[x_cols]y_data = train[[y_col]]# 归一化,神经网络的输入需要归一化# 这里的归一化使用了特征增量训练的结果(特征全局基本统计信息)# (1)对X进行归一化for col in x_data.columns:min_, max_ = df_min[col], df_max[col]x_data[col] = normalize(x_data[col], min_, max_)# (2)对y进行归一化col = y_colmin_, max_ = df_min[col], df_max[col]y_data[y_col] = y_normaloize(y_data[col], min_, max_)# 第四步:增量训练# 判断是否需要创建模型,如果网络不是None,那么加载上次保存的网络,接着训练就好了if model == None:k = len(x_cols)model = Sequential()model.add(Dense(80, activation='relu', input_shape=(k,), ))model.add(Dropout(0.5))model.add(Dense(32, activation='relu'))model.add((Dropout(0.5)))model.add(Dense(8, activation='relu'))model.add(Dropout(0.5))model.add(Dense(1, activation='linear'))model.summary()print('完成构建网络')model.compile(loss='mape', optimizer='adam', metrics=['mse', 'mae', 'mape'])print('编译网络')else:model = load_model(model_file)print('加载上次训练的网络')# 第五步:训练网络os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'history = model.fit(x=x_data, y=y_data[y_col], batch_size=5000, epochs=20,verbose=10000)log.info('完成训练网络')# 第六步:保存网络model.save(model_file)# 第七步:查看模型评分# 查看模型评分loss = history.history['loss'][-1]mse = history.history['mean_squared_error'][-1]mae = history.history['mean_absolute_error'][-1]mape = history.history['mean_absolute_percentage_error'][-1]print('第%2d 批次数据,loss=%.4f, mse=%.4f, mae=%.4f, mape=%.4f' % (i, loss, mse, mae, mape))i += 1

可以看到,loss一直在下降,说明增量训练是有用的。

[2018.08.07 14:15:23] 第 1 批次数据,loss=0.0225, mse=0.0225, mae=0.1221, mape=13.5878
[2018.08.07 14:16:37] 第 2 批次数据,loss=0.0028, mse=0.0028, mae=0.0379, mape=4.4841
[2018.08.07 14:17:50] 第 3 批次数据,loss=0.0023, mse=0.0023, mae=0.0325, mape=3.7667
[2018.08.07 14:19:06] 第 4 批次数据,loss=0.0023, mse=0.0023, mae=0.0324, mape=3.7899
[2018.08.07 14:20:20] 第 5 批次数据,loss=0.0021, mse=0.0021, mae=0.0313, mape=3.5558
[2018.08.07 14:21:37] 第 6 批次数据,loss=0.0022, mse=0.0022, mae=0.0323, mape=3.7333
[2018.08.07 14:22:56] 第 7 批次数据,loss=0.0023, mse=0.0023, mae=0.0322, mape=5.5631
[2018.08.07 14:23:46] 第 8 批次数据,loss=0.0024, mse=0.0024, mae=0.0332, mape=3.8871

参考:
Strategies to scale computationally: bigger data
使用sklearn进行增量学习
lightGBM keep_training_booster
Incremental learning using Dataset.subset LightGBM 2.1.1 python API
如何加载训练好的Keras模型并继续训练
How to Check-Point Deep Learning Models in Keras
Keras中文文档

关注我的专栏,期待更有有意义的知识分享。

机器学习-增量训练方法相关推荐

  1. 这应该是全网最好的「机器学习」科普文

    2019-03-07 22:12:56 预计阅读时间:8分钟 本文首发在 easyAI 人工智能知识库 目录 机器学习和人工智能是什么关系? 什么是机器学习? 监督学习.非监督学习.强化学习 机器学习 ...

  2. 快速掌握 机器学习(Machine Learning) 常用概念术语,常用算法

    1.什么是机器学习? 机器学习的概念: 传统上如果我们想让计算机工作,我们给它一串指令,然后它遵照这个指令一步步执行下去.有因有果,非常明确.这样的方式计算机是无法执行固定流程之外的东西的 . 但是现 ...

  3. 这可能是2019年全网最好的「机器学习」科普文

    机器学习.人工智能.深度学习是什么关系? 本文首发在 easyAI--人工智能知识库 1956 年提出 AI 概念,短短3年后(1959) Arthur Samuel 就提出了机器学习的概念: Fie ...

  4. 增量训练求解麦克斯韦方程族

    原始的PINNs(Physics-Informed Neural Networks, PINNs)方法不具备求解一类方程的能力.当方程中的特征参数(如介电系数等)发生变化时需要重新训练,增加了求解时间 ...

  5. 四两拨千斤!深度主动学习综述2020

    本文转载自知乎,为最近新出的论文 A Survey of Deep Active Learning 中文版介绍,原文作者调研了189 篇文献综述了深度主动学习的进展.文章较长,建议先收藏再阅读. ht ...

  6. 【Active Learning - 13】总结与展望 参考文献的整理与分享(The End...)

    写在前面: 本篇博文将作为"主动学习系列"博文的结尾.目前,本人在职的相关工作暂无与主动学习相关的需求.因此,之后大概率是不会再更新相关的博文了.能够分享的内容和资料,我都分享在这 ...

  7. 深度主动学习综述(Deep Active Learning)

    原文 Abstract 主动学习试图通过标记最少量的样本使得模型的性能收益最大化.而深度学习则对数据比较贪婪,需要大量的数据供给来优化海量的参数,从而使得模型学会如何提取高质量的特征.近年来,由于互联 ...

  8. 深度主动学习综述2020

    A Survey of Deep Active Learning 中文版仅作参考,以正式的pdf版为主. https://arxiv.org/pdf/2009.00236.pdf西北大学等最新< ...

  9. 数据挖掘课程笔记--分类(4)朴素贝叶斯

    零.前言 在很多应用中,属性集和类变量之间的关系是不确定的.换句话说,尽管测试记录的属性集和某些训练样例相同,但是也不能正确地预测它的类标号.这种情况产生的原因可能是噪声,或者出现了某些影响分类的因素 ...

最新文章

  1. java中treemap_Java中TreeMap集合讲解
  2. 病毒c语言代码大全,谁有C语言编写的病毒源代码?
  3. python字典的应用实验报告_Python字典应用的一个例子
  4. 在 Mac 上的 Keynote 中如何先排版再选照片?
  5. D-Link DWA-160 wifi抓包
  6. 微机原理-80386(1)
  7. 设计模式----建造者模式 Builder 适配器 Adapter桥接 BridgeCommand命令
  8. 中文语音合成软件Ekho(余音)的使用教程
  9. 2022版Maven教程 - 第八章 微服务架构案例
  10. 基于连续优化的规划算法:以二次规划为例
  11. 一段很有意思的代码!!
  12. 计算机无法显示输入法怎么办,win7系统电脑输入法选项不见了导致无法输入中文怎么办...
  13. MAC 无法重启或关机
  14. 腾讯云服务器80等端口无法访问
  15. VS x86 x64 anycpu 编译运行对照表
  16. 酷派t2co1怎么升Android5,而且双清不了怎么办Coolpad725?酷 – 手机爱问
  17. 硬件配置部分——从无到有自主搭建视觉惯性VI-SLAM(vins-mono)平台
  18. Rails 用 RJS 简单有效的实现页面局部刷新
  19. 独家!天才少年 Vitalik:“中国开发者应多关注以太坊!”
  20. 传奇开个服大概需要多少费用?

热门文章

  1. 不符合正态分布的配对数据也有自己的统计方法。
  2. 用计算机怎么计算税率表,5000元新个税计算器公式税率表
  3. 卸甲归田——回顾CSP2019
  4. 从蓝桥杯丢盔卸甲,来学一点javaScript
  5. python 3.0 实现多级反馈队列进程调度算法
  6. OCR学习之一:OCR简介
  7. 开关稳压电源和线性稳压电源
  8. CG学习记录(四)函数
  9. sqlserver日期推算(年,季度,月,星期推算)
  10. 利用简单的爬虫获取CV顶会论文