我们使用的算法模型中大都是批量学习(Batch Learning)模式,即:假设在训练之前所有训练样本一次都可以得到,学习这些样本之后,学习过程就终止了,不再学习新的知识。
在有些场景下,训练样本通常不可能一次全部得到,而是随着时间逐步得到的,并且样本反映的信息也可能随着时间产生了变化。如果新样本到达后要重新学习全部数据,需要消耗大量时间和空间,因此批量学习的算法不能满足这种需求。
这就像人在成长过程中,每天学习和接收新的事物,学习是逐步进行的,而且,对已经学习到的知识,人类一般是不会遗忘的。我们希望学习模型也能够不断地从新样本中学习新的知识,并且保存大部分以前已经学习到的知识,这就是增量学习。
增量学习主要有两方面的应用:
- 用于非常大的数据;
- 用于随着时间不断变化的数据。
1. partial_fit 和 fit
在 scikit-learn 中支持增量学习的算法模型(并不是所有的方法都支持增量学习)都通过 partial_fit 方法来实现增量运算。
模型的 partial_fit 和 fit 方法进行训练时有什么不同?
其实 fit 方法内部调用了 partial_fit 方法,不同的是直接调用 partial_fit 方法,其 max_iter 的值为 1,也就是说对于给定的样本只迭代一次。而 fit 方法的 max_iter 默认值是 1000,当然该值在创建模型的时候是可以自己指定,如下图所示:
左侧是 fit 函数中调用 _patial_fit 方法时传递的 max_iter=1000 ,右侧是 partial_fit 调用 _patial_fit 时候传递的 max_iter=1。
两者另外的不同是 fit 函数每次调用时,在 _partial_fit 调用前默认会将模型参数设置为 None,即:从零开始训练,而 patial_fit 则不会进行如此设置。如下图所示:
从这里可以看到如果要进行增量学习的话,调用 partial_fit 方法更适合。另外,从上面的截图代码中,看到 warm_start 属性,它似乎和模型参数有关?它的作用是什么?
warm_start 参数默认为 False,当它设置为 True 时,从上面代码可以看出,它会将以前训练得到的参数保留下来,而不是抹掉。也就是说,如果前面使用 fit 方法训练了一次,此时 设置 warm_start=True 并再使用 fit 方法进行第二次训练, 则模型就不会从零开始训练,而是在以前参数的基础上重新训练,这会加快模型的收敛。
上面的代码在 fit 函数中会出现,但是在 patial_fit 方法中是没有增加该逻辑的。也就是说,partial_fit 认为无论你之前训练参数是什么,我都在你的基础上进行增量训练。
2. 增量学习 API
下面列表中是 scikit-learn 中各个支持增量学习,不同任务的 API:
- Classification(分类)
sklearn.naive_bayes.MultinomialNB
sklearn.naive_bayes.BernoulliNB
sklearn.linear_model.Perceptron
sklearn.linear_model.SGDClassifier
sklearn.linear_model.PassiveAggressiveClassifier
sklearn.neural_network.MLPClassifier
- Regression(回归)
sklearn.linear_model.SGDRegressor
sklearn.linear_model.PassiveAggressiveRegressor
sklearn.neural_network.MLPRegressor
- Clustering(聚类)
sklearn.cluster.MiniBatchKMeans
sklearn.cluster.Birch
- Decomposition / feature Extraction(分解/特征提取)
sklearn.decomposition.MiniBatchDictionaryLearning
sklearn.decomposition.IncrementalPCA
sklearn.decomposition.LatentDirichletAllocation
- Preprocessing(预处理)
sklearn.preprocessing.StandardScaler
sklearn.preprocessing.MinMaxScaler
sklearn.preprocessing.MaxAbsScaler
我们以 SGDRegressor 为例:
import math from sklearn.linear_model import SGDRegressor from sklearn.datasets import load_boston from sklearn.model_selection import train_test_split import numpy as np import warnings warnings.filterwarnings('ignore') # 由于数据中可能存在黑人歧视,boston 数据将会在 1.2 版本移除 boston_data = load_boston() x, y = boston_data.data, boston_data.target # 数据集分割 x_train, x_test, y_train, y_test = \ train_test_split(x, y, test_size=0.2, random_state=666) # 全量训练 def test01(): estimator = SGDRegressor(max_iter=1000, random_state=0) estimator.fit(x_train, y_train) score = estimator.score(x_test, y_test) print('全量训练:', score) # 加载 mini batch 数据 def load_data(dataset, labels): batch_size = 2 batch_num = math.ceil(len(labels) / 4) for index in range(batch_num): start = index * batch_size end = start + batch_size yield dataset[start: end], labels[start: end] # 增量训练 def test02(): global x_train, y_train x_train, x_incr, y_train, y_incr = \ train_test_split(x_train, y_train, train_size=0.5, random_state=0) # 使用少量训练集进行训练 estimator = SGDRegressor(max_iter=1000, random_state=0) estimator.fit(x_train, y_train) score = estimator.score(x_test, y_test) print('基础训练:', score) # 增量训练 for data, label in load_data(x_incr, y_incr): estimator.partial_fit(data, label) score = estimator.score(x_test, y_test) print('增量训练:', score) if __name__ == '__main__': test01() test02()
程序执行结果:
全量训练: -7.505174669422246e+26 基础训练: -1.0685014075494617e+27 增量训练: -1.3018409378020246e+26
这个结果并不重要,增量学习并不会保证效果一定比全量学习要好,我们需要选择在合适的场景来使用。