首先普及一下数据评估方法都有哪些:
1.留出法
留出法是将数据集D划分为两个互斥的集合,其中一个集合作为训练集S,另一个作为测试集T,即D=S∪T,S∩T=空集,在S上训练出模型后,用T来评估其测试误差,作为对泛化误差的估计。
在采样的过程中,为了保证数据分布的一致性,比如在分类任务中保证类别比例的采样方式叫做“分层采样”。
另外单次的使用留出法得到的结果往往不够稳定可靠,在使用留出法时,一般采用若干次随机划分,重复进行实验评估后取平均值作为留出法的评估结果。
2.自助法(bootstrapping)
以自助采样为基础(从自助采样延申出的Bagging方法,继而可以引出随机森林的采样方法,这里不做详细讲解)。
每次随机从D中挑选一个样本,将其拷贝放入D‘,然后再将该样本放回初始数据集D中,使得样本在下次采样时仍然有可能被采集到,循环执行m遍,就得到了m个样本的数据集D’,这就是自助采样的结果。
3.交叉验证法
通常把交叉验证法叫做K折交叉验证法,其先将数据集D划分为 k个大小相似的互斥子集,即D=D1∪D2∪…∪Dk,Di∩Dj=空集(i≠j),每个自己Di都尽可能保持数据分布的一致性,即从D中通过分层采样得到。
比较常用的是10折交叉验证法,示意图如下:
总结:
自助法在数据集较小、难以有效划分训练/测试集时很有用;此外,自助法能从初始数据集中产生多个不同的训练集,这对集成学习(随机森林用到)等方法有很大的好处。然而,自助法产生的数据集改变了初始数据集的分布,这会引入估计偏差。因此,在初始数据量足够时,留出法和交叉验证法更常用一些
提前说一下,这部分提到的函数其实在代码中,只是将其实例化作为一个参数使用。
在这里终于要回归题目,在sklearn中有很多交叉验证的库进行数据的划分,分别是:
KFold,GroupKFold,StratifiedKFold,LeaveOneGroupOut,LeavePGroupsOut,LeaveOneOut,LeavePOut,ShuffleSplit,GroupShuffleSplit,StratifiedShuffleSplit(train_test_split,个人理解这个方法和StratifiedShuffleSplit作用是相同的),PredefinedSplit,TimeSeriesSplit。
从原理上来分类:
KFold,GroupKFold,StratifiedKFold,RepeatedKFold
LeaveOneGroupOut,LeavePGroupsOut,LeaveOneOut,LeavePOut
ShuffleSplit,GroupShuffleSplit,StratifiedShuffleSplit
从应用上来分类
StratifiedKFold ,StratifiedShuffleSplit
GroupKFold,LeaveOneGroupOut,LeavePGroupOut,GroupShuffleSplit
TimeSeriesSplit
在这里我具体说一下StratifiedShuffleSplit,后面实例会用到,其他可以到sklearn官网进行学习:
一共有四个参数
Parameters:
原文如下:
n_splitsint, default=10
Number of re-shuffling & splitting iterations.
test_size, float or int, default=None
If float, should be between 0.0 and 1.0 and represent the proportion of the dataset to include in the test split. If int, represents the absolute number of test samples. If None, the value is set to the complement of the train size. If train_size is also None, it will be set to 0.1.()
train_size, float or int, default=None
If float, should be between 0.0 and 1.0 and represent the proportion of the dataset to include in the train split. If int, represents the absolute number of train samples. If None, the value is automatically set to the complement of the test size.
random_state, int or RandomState instance, default=None
Controls the randomness of the training and testing indices produced. Pass an int for reproducible output across multiple function calls. See Glossary.
解释:
n_splitsint, default=10
(重新洗牌和拆分迭代次数,也就是默认会把原数据集D分成10组和进行10折交叉验证)
test_size和train_size
就是简单的训练和测试数据集的比例,整数代表具体数量,百分比代表占总数据集的多少比例
random_state
这个值在参数估计很重要,我们需要将其设置为一个大于0的整数,这样对于在不同的参数估计中,可以保证每次k折划分的数据都是相同的;如果设置为0,那么每次进行交叉验证进行数据划分结果是不相同的,这样没办法保证参数评估的准确性)
举例:
>>> import numpy as np
>>> from sklearn.model_selection import StratifiedShuffleSplit
>>> X = np.array([[1, 2], [3, 4], [1, 2], [3, 4], [1, 2], [3, 4]])
>>> y = np.array([0, 0, 0, 1, 1, 1])
>>> sss = StratifiedShuffleSplit(n_splits=5, test_size=0.5, random_state=0)
>>> sss.get_n_splits(X, y)
5
>>> print(sss)
StratifiedShuffleSplit(n_splits=5, random_state=0, ...)
>>> for train_index, test_index in sss.split(X, y):
... print("TRAIN:", train_index, "TEST:", test_index)
... X_train, X_test = X[train_index], X[test_index]
... y_train, y_test = y[train_index], y[test_index]
TRAIN: [5 2 3] TEST: [4 1 0]
TRAIN: [5 1 4] TEST: [0 2 3]
TRAIN: [5 0 2] TEST: [4 3 1]
TRAIN: [4 1 0] TEST: [2 3 5]
TRAIN: [0 5 1] TEST: [3 4 2]
使用这个函数是为了保证每个分类样本中每个类别的占比相同,避免出现某一个类别过多或者过少的情况,train_test_split
作用也一样
列举sklearn中三种模型评估方法:
这里举例说一下cross_val_score函数:
原文:(有点多,可跳过看后面解释)
Parameters:
estimator: estimator object implementing ‘fit’
The object to use to fit the data.
X: array-like of shape (n_samples, n_features)
The data to fit. Can be for example a list, or an array.
y: array-like of shape (n_samples,) or (n_samples, n_outputs), default=None
The target variable to try to predict in the case of supervised learning.
groups: array-like of shape (n_samples,), default=None
Group labels for the samples used while splitting the dataset into train/test set. Only used in conjunction with a “Group” cv instance (e.g., GroupKFold).
scoring: str or callable, default=None
A str (see model evaluation documentation) or a scorer callable object / function with signature scorer(estimator, X, y) which should return only a single value.
Similar to cross_validate but only a single metric is permitted.
If None, the estimator’s default scorer (if available) is used.
cv: int, cross-validation generator or an iterable, default=None
Determines the cross-validation splitting strategy. Possible inputs for cv are:
None, to use the default 5-fold cross validation,
int, to specify the number of folds in a (Stratified)KFold,
CV splitter,
An iterable yielding (train, test) splits as arrays of indices.
For int/None inputs, if the estimator is a classifier and y is either binary or multiclass, StratifiedKFold is used. In all other cases, KFold is used.
Refer User Guide for the various cross-validation strategies that can be used here.
Changed in version 0.22: cv default value if None changed from 3-fold to 5-fold.
n_jobs: int, default=None
The number of CPUs to use to do the computation. None means 1 unless in a joblib.parallel_backend context. -1 means using all processors. See Glossary for more details.
verbose: int, default=0
The verbosity level.
fit_params: dict, default=None
Parameters to pass to the fit method of the estimator.
pre_dispatch: int or str, default=’2*n_jobs’
Controls the number of jobs that get dispatched during parallel execution. Reducing this number can be useful to avoid an explosion of memory consumption when more jobs get dispatched than CPUs can process. This parameter can be:
None, in which case all the jobs are immediately created and spawned. Use this for lightweight and fast-running jobs, to avoid delays due to on-demand spawning of the jobs
An int, giving the exact number of total jobs that are spawned
A str, giving an expression as a function of n_jobs, as in ‘2*n_jobs’
error_score: ‘raise’ or numeric, default=np.nan
Value to assign to the score if an error occurs in estimator fitting. If set to ‘raise’, the error is raised. If a numeric value is given, FitFailedWarning is raised. This parameter does not affect the refit step, which will always raise the error.
在这里!
对于这个函数cross_val_score,其实用到参数不多,大多是默认。
cross_val_score(clf, x, y, cv=cv),大概就是这四个参数,
第一个是机器算法,看要对什么算法进行模型评估,我用的是决策树,后面有代码样例;
第二个参数和第三个参数就是训练和测试数据的list
第四个参数最重要
:
确定交叉验证拆分策略。cv的可能输入包括:
对于int/None输入,如果估计器是分类器且y是二进制或多类,则使用StratifiedKFold。在所有其他情况下,使用KFold。
最后,本人也是在摸着石头过河,对于第二部分和第三部分本人不敢保证说的很全,或者说没有瑕疵,如果有,欢迎大家评论区指正,这些是在大量查阅资料后总结的,算是综合众多大佬的智慧结晶,可查看最后一部分的参考文章进行加深阅读理解。
或者直接看后续样例代码更容易理解
data = np.genfromtxt(data, delimiter='\t')
x = data[:, 1:]
y = data[:, 0]
x[:, 0], x[:, 1], x[:, 2], x[:, 3], x[:, 4], x[:, 5] = \
x[:, 0] * 0.2, x[:, 1] * 0.2, x[:, 2] * 0.1, x[:, 3] * 0.1, x[:, 4] * 0.2, x[:, 5] * 0.2
主要为数据加了权重(对于决策树不需要标准化)
在进行参数分析时,就需要知道哪些参数重要,哪些参数不重要。而分类决策树总共有12个参数可以自己调整,这么多参数一个个记起来太麻烦,我们可以把这些参数分成三个类别。
criterion
(划分标准):有两个参数 ‘entropy’(熵) 和 ‘gini’(基尼系数)可选,默认为gini。max_depth
(树的最大深度):默认为None,此时决策树在建立子树的时候不会限制子树的深度。也可以设置具体的整数,一般来说,数据少或者特征少的时候可以不管这个值。如果模型样本量多,特征也多的情况下,推荐限制这个最大深度,具体的取值取决于数据的分布。常用的可以取值10-100之间。min_samples_split
(分割内部节点所需的最小样本数):意思就是只要在某个结点里有k个以上的样本,这个节点才需要继续划分,这个参数的默认值为2,也就是说只要有2个以上的样本被划分在一个节点,如果这两个样本还可以细分,这个节点就会继续细分min_samples_leaf
(叶子节点上的最小样本数):当你划分给某个叶子节点的样本少于设定的个数时,这个叶子节点会被剪枝,这样可以去除一些明显异常的噪声数据。默认为1,也就是说只有有两个样本类别不一样,就会继续划分。如果是int,那么将min_samples_leaf视为最小数量。如果为float,则min_samples_leaf为分数,ceil(min _ samples _ leaf * n _ samples)为每个节点的最小样本数。以上就是分类决策树12个参数的介绍。
说完了分类决策树,回归决策树就自然而然懂了,它和分类决策树在参数上的区别只有两个地方:
1) ‘criterion’: 评价划分节点质量的参数,类似于分类决策树的熵,有三个参数可选,‘mse’(default)均方误差;'friedman_mse’均方误差近似,最小化L2 loss;'mae’平均绝对误差,最小化L1 loss。
2)少了’class_weight’: None,当然,回归问题也就不存在给每个类别加不同的权重了。
在知道了哪些是重要的参数,那么就可以开始调参了,分别是:
1)确定criterion参数(决策树划分标准):这里可以简单比较一下。
2)通过绘制得分曲线缩小max_depth(树的最大深度)的搜索范围,得到一个暂定的max_depth。
之所以第一个参数调max_depth,是因为模型得分一般随着max_depth单调递增,之后会区域稳定。
3)利用暂定的max_depth参数,绘制曲线,观察得分随着min_samples_split(分割内部节点所需的最小样本数)的变化规律,从而确定min_samples_split参数的大概范围。
因为随着min_samples_split的增大,模型会倾向于向着简单的方向发展。所以如果模型过拟合,那么随着min_samples_split的增大,模型得分会先升高后下降,我们选取得分最高点附近的min_samples_split参数;如果模型欠拟合,那么随着min_samples_split的增大,模型得分会一直下降,接下来调参时只需要从默认值2开始取就好。
4)利用暂定的max_depth和min_samples_split参数,绘制曲线,观察得分随着min_samples_leaf(叶子节点上应有的最少样例数)的变化规律,从而确定min_samples_leaf参数的大概范围。该参数的范围确定方法同上。
5)利用网格搜索,在一个小范围内联合调max_depth、min_samples_split和min_samples_leaf三个参数,确定最终的参数。
对《3.开始调参》中步骤一一填写代码
在这里特别强调一下random_state
,一定要将其设置为一个大于0的整数,否则参数评估中每次数据都不相同,那就没有评估的意义了!具体多少没有要求
调用所需库:
from sklearn import tree
from sklearn.model_selection import StratifiedShuffleSplit, GridSearchCV, cross_val_score
import matplotlib.pyplot as plt
import numpy as np
1)确定criterion参数(决策树划分标准):这里可以简单比较一下。
cv = StratifiedShuffleSplit(n_splits=10, test_size=0.1, random_state=42)
clf = tree.DecisionTreeClassifier(criterion='entropy', random_state=42)
score = cross_val_score(clf, x, y, cv=cv).mean()
print(score)
clf = tree.DecisionTreeClassifier(criterion='gini', random_state=42)
score = cross_val_score(clf, x, y, cv=cv).mean()
print(score)
0.764375
0.7543749999999999
有的说gini效果好一点,但我数据跑出来entropy效果更好一点
2)通过绘制得分曲线缩小max_depth(树的最大深度)的搜索范围,得到一个暂定的max_depth。
之所以第一个参数调max_depth,是因为模型得分一般随着max_depth单调递增,之后会区域稳定。
ScoreAll = []
cv = StratifiedShuffleSplit(n_splits=10, test_size=0.1, random_state=42)
for i in range(5, 100, 5):
clf = tree.DecisionTreeClassifier(criterion='entropy', max_depth=i, random_state=42)
score = cross_val_score(clf, x, y, cv=cv).mean()
ScoreAll.append([i, score])
ScoreAll = np.array(ScoreAll)
max_score = np.where(ScoreAll == np.max(ScoreAll[:, 1]))[0][0] # 找出最高得分对应的索引
print("最优参数以及最高得分:", ScoreAll[max_score])
plt.figure(figsize=[20, 5])
plt.plot(ScoreAll[:, 0], ScoreAll[:, 1])
plt.show()
输出:
最优参数以及最高得分: 最优参数以及最高得分: [10. 0.78375]
不要忘记,我们还需要在10左右进行具体的分析
ScoreAll = []
cv = StratifiedShuffleSplit(n_splits=10, test_size=0.1, random_state=42)
for i in range(5, 15):
clf = tree.DecisionTreeClassifier(criterion='entropy', max_depth=i, random_state=42)
score = cross_val_score(clf, x, y, cv=cv).mean()
ScoreAll.append([i, score])
ScoreAll = np.array(ScoreAll)
max_score = np.where(ScoreAll == np.max(ScoreAll[:, 1]))[0][0] # 找出最高得分对应的索引
print("最优参数以及最高得分:", ScoreAll[max_score])
plt.figure(figsize=[20, 5])
plt.plot(ScoreAll[:, 0], ScoreAll[:, 1])
plt.show()
输出:
最优参数以及最高得分: [7. 0.791875]
我们暂定树的高度为7,达到了0.791875
3)利用暂定的max_depth参数,绘制曲线,观察得分随着min_samples_split(分割内部节点所需的最小样本数)的变化规律,从而确定min_samples_split参数的大概范围。
ScoreAll = []
cv = StratifiedShuffleSplit(n_splits=10, test_size=0.1, random_state=42)
for i in range(5, 15):
clf = tree.DecisionTreeClassifier(criterion='entropy', max_depth=9, min_samples_split=i, random_state=42)
score = cross_val_score(clf, x, y, cv=cv).mean()
ScoreAll.append([i, score])
ScoreAll = np.array(ScoreAll)
max_score = np.where(ScoreAll == np.max(ScoreAll[:, 1]))[0][0] # 找出最高得分对应的索引
print("最优参数以及最高得分:", ScoreAll[max_score])
plt.figure(figsize=[20, 5])
plt.plot(ScoreAll[:, 0], ScoreAll[:, 1])
plt.show()
输出:
最优参数以及最高得分: [5. 0.79375]
4)利用暂定的max_depth和min_samples_split参数,绘制曲线,观察得分随着min_samples_leaf(叶子节点上应有的最少样例数)的变化规律,从而确定min_samples_leaf参数的大概范围。该参数的范围确定方法同上。
ScoreAll = []
cv = StratifiedShuffleSplit(n_splits=10, test_size=0.1, random_state=42)
for i in range(5, 15):
clf = tree.DecisionTreeClassifier(criterion='entropy', min_samples_leaf=i, max_depth=7, min_samples_split=5, random_state=42)
score = cross_val_score(clf, x, y, cv=cv).mean()
ScoreAll.append([i, score])
ScoreAll = np.array(ScoreAll)
max_score = np.where(ScoreAll == np.max(ScoreAll[:, 1]))[0][0] # 找出最高得分对应的索引
print("最优参数以及最高得分:", ScoreAll[max_score])
plt.figure(figsize=[20, 5])
plt.plot(ScoreAll[:, 0], ScoreAll[:, 1])
plt.show()
输出:
最优参数以及最高得分: [2. 0.795]
5)利用网格搜索,在一个小范围内联合调max_depth、min_samples_split和min_samples_leaf三个参数,确定最终的参数。
cv = StratifiedShuffleSplit(n_splits=10, test_size=0.1, random_state=42)
param_grid = {
'max_depth': np.arange(2, 10),
'min_samples_leaf': np.arange(1, 8),
'min_samples_split': np.arange(2, 10)}
clf = tree.DecisionTreeClassifier(criterion='entropy', random_state=42)
GS = GridSearchCV(clf, param_grid, cv=cv)
GS.fit(x, y)
print(GS.best_params_)
print(GS.best_score_)
输出:
{
'max_depth': 9, 'min_samples_leaf': 7, 'min_samples_split': 2}
0.796875
可以看到,最终模型提高到了0.796875。这里我们可以看到,上述三个参数和我们前边所暂定的参数都不一样。证明三者之间确实是相互影响的!
GridSearchCV():穷尽的网格搜索
RandomizedSearchCV():随机参数的优化
我这里使用了网格搜索,想尝试的可以试试RandomizedSearchCV()
选数据划分算法
,我是使用了交叉验证中的一个算法参数估计和模型评估
,通过不同的参数选择,使用交叉验证进行模型评估,从而选出最优的参数正由于bagging的训练过程旨在降低方差,而boosting的训练过程旨在降低偏差
,过程影响类的参数能够引起整体模型性能的大幅度变化。一般来说,在此前提下,我们继续微调子模型影响类的参数,从而进一步提高模型的性能。2020/12/6补充
随机森林