机器学习算法——监督学习1
模型复杂度与数据集大小的关系
需要注意,模型复杂度与训练数据集中输入的变化密切相关:数据集中包含的数据点的变化范围越大,在不发生过拟合的前提下你可以使用的模型就越复杂。通常来说,收集更多的数据点可以有更大的变化范围,所以更大的数据集可以用来建更复杂的模型。但是,仅复制相同的数据点或收集非常相似的数据是无济于事的。
收集更多数据,适当构建更复杂的模型,对监督学习任务往往特别有用。本书主要关注固定大小的数据集。在现实世界中,你往往能够决定收集多少数据,这可能比模型调参更为有效。永远不要低估更多数据的力量!
一、KNN—sklearn.neighbors
KNN分类模型(KNeighborsClassifier)
虽然现实世界的图像很少有非常平滑的,但我们仍可以看出过拟合与欠拟合的一些特征(注意,由于更少的邻居对应更复杂的模型,所以此图相对于图 2-1 做了水平翻转)。仅考虑单一近邻时,训练集上的预测结果十分完美。但随着邻居个数的增多,模型变得更简单,训练集精度也随之下降。单一邻居时的测试集精度比使用更多邻居时要低,这表示单一近邻的模型过于复杂。与之相反,当考虑 10 个邻居时,模型又过于简单,性能甚至变得更差。最佳性能在中间的某处,邻居个数大约为 6。不过最好记住这张图的坐标轴刻度。最差的性能约为 88% 的精度,这个结果仍然可以接受。
KNN回归模型(KNeighborsRegression)
sklearn.neighbors.KNeighborsRegressor(n_neighbors=5, *, weights='uniform', algorithm='auto', leaf_size=30, p=2, metric='minkowski', metric_params=None, n_jobs=None, **kwargs)[source]
KNN优点、缺点和参数
一般来说, KNeighbors 分类器有 2 个重要参数:邻居个数与数据点之间距离的度量方法。在实践中,使用较小的邻居个数(比如 3 个或 5 个)往往可以得到比较好的结果,但你应该调节这个参数。选择合适的距离度量方法超出了本书的范围。默认使用欧式距离,它在许多情况下的效果都很好。 k-NN 的优点之一就是模型很容易理解,通常不需要过多调节就可以得到不错的性能。 在考虑使用更高级的技术之前,尝试此算法是一种很好的基准方法。 构建最近邻模型的速度通常很快,但如果训练集很大(特征数很多或者样本数很大),预测速度可能会比较慢。 使用 k-NN 算法时,对数据进行预处理是很重要的(见第 3 章)。这一算法对于有很多特征(几百或更多)的数据集往往效果不好,对于大多数特征的大多数取值都为 0 的数据集(所谓的稀疏数据集)来说,这一算法的效果尤其不好。 虽然 k 近邻算法很容易理解,但由于 预测速度慢且不能处理具有很多特征的数据集,所以在实践中往往不会用到。下面介绍的这种方法就没有这两个缺点。
二、线性模型
回归模型—sklearn.linear_model
线性回归(LinearRegression)
sklearn.linear_model.LinearRegression(*,fit_intercept=True, normalize=False, copy_X=True, n_jobs=None)
R 2 约为 0.66,这个结果不是很好,但我们可以看到,训练集和测试集上的分数非常接近。这说明可能存在欠拟合,而不是过拟合。 对于这个一维数据集来说,过拟合的风险很小,因为模型非常简单(或受限)。然而,对于更高维的数据集(即有大量特征的数据集),线性模型将变得更加强大,过拟合的可能性也会变大。我们来看一下 LinearRegression 在更复杂的数据集上的表现,比如波士顿房价数据集。记住,这个数据集有 506 个样本和 105个导出特征。首先,加载数据集并将其分为训练集和测试集。然后像前面一样构建线性回归模型:
训练集和测试集之间的性能差异是过拟合的明显标志,因此我们应该试图找到一个可以控制复杂度的模型。标准线性回归最常用的替代方法之一就是岭回归(ridge regression),下面来看一下。
岭回归(Ridge)
sklearn.linear_model.Ridge(alpha=1.0, *, fit_intercept=True, normalize=False, copy_X=True, max_iter=None, tol=0.001, solver='auto', random_state=None)
对系数(w)的选择不仅要在训练数据上得到好的预测结果,而且还要拟合附加约束。我们还希望系数尽量小。换句话说,w 的所有元素都应接近于 0。直观上来看,这意味着每个特征对输出的影响应尽可能小(即斜率很小),同仍给出很好的预测结果。这种约束是所谓正则化(regularization)的一个例子。正则化是指对模型做显式约束,以避免过拟合。岭回归用到的这种被称为 L2 正则化。
Lasso
sklearn.linear_model.Lasso(alpha=1.0, *, fit_intercept=True, normalize=False, precompute=False, copy_X=True,max_iter=1000, tol=0.0001, warm_start=False, positive=False, random_state=None, selection='cyclic')
如你所见, Lasso 在训练集与测试集上的表现都很差。这表示存在欠拟合,我们发现模型只用到了 105 个特征中的 3个。与 Ridge 类似, Lasso 也有一个正则化参数 alpha ,可以控制系数趋向于 0 的强度。在上一个例子中,我们用的是默认值 alpha=1.0 。为了降低欠拟合,我们尝试减小 alpha 。这么做的同时,我们还需要增加 max_iter 的值(运行迭代的最大次数):
优缺点
在实践中,在两个模型中一般首选岭回归。但如果特征很多,你认为只有其中几个是重要的,那么选择 Lasso 可能更好。同样,如果你想要一个容易解释的模型, Lasso 可以给出更容易理解的模型,因为它只选择了一部分输入特征。 scikit-learn 还提供了 ElasticNet类,结合了 Lasso 和 Ridge 的惩罚项。在实践中,这种结合的效果最好,不过代价是要调节两个参数:一个用于 L1 正则化,一个用于 L2 正则化。
用于分类的线性模型
LogisticRegression和LinearSVC
sklearn.svm.LinearSVC(penalty='l2', loss='squared_hinge', *, dual=True, tol=0.0001, C=1.0, multi_class='ovr', fit_intercept=True, intercept_scaling=1, class_weight=None, verbose=0, random_state=None, max_iter=1000)
sklearn.linear_model.LogisticRegression(penalty='l2', *, dual=False, tol=0.0001, C=1.0, fit_intercept=True, intercept_scaling=1, class_weight=None, random_state=None, solver='lbfgs', max_iter=100, multi_class='auto', verbose=0, warm_start=False, n_jobs=None, l1_ratio=None)
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC
X,y=mglearn.datasets.make_forge()
fig,axes=plt.subplots(1,2,figsize=(10,3))
for model,axe in zip([LogisticRegression(),LinearSVC()],axes):
clf=model.fit(X,y)
mglearn.plots.plot_2d_separator(clf,X,fill=False,eps=0.5,ax=axe,alpha=0.7)
mglearn.discrete_scatter(X[:,0],X[:,1],y,ax=axe)
axe.set_title("{}".format(clf.__class__.__name__))
axe.set_xlabel("Feature 0")
axe.set_ylabel("Feature 1")
axes[0].legend()
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC
X,y=mglearn.datasets.make_forge()
fig,axes=plt.subplots(1,2,figsize=(10,3))
for model,axe in zip([LogisticRegression(),LinearSVC()],axes):
clf=model.fit(X,y)
mglearn.plots.plot_2d_separator(clf,X,fill=False,eps=0.5,ax=axe,alpha=0.7)
mglearn.discrete_scatter(X[:,0],X[:,1],y,ax=axe)
axe.set_title("{}".format(clf.__class__.__name__))
axe.set_xlabel("Feature 0")
axe.set_ylabel("Feature 1")
axes[0].legend
正则化C
- 对于 LogisticRegression 和 LinearSVC ,决定正则化强度的权衡参数叫作 C 。 C 值越大,对应的正则化越弱。换句话说,如果参数 C 值较大,那么 LogisticRegression 和LinearSVC 将尽可能将训练集拟合到最好,而如果 C 值较小,那么模型更强调使系数向量(w)接近于 0。
- 参数 C 的作用还有另一个有趣之处。较小的 C 值可以让算法尽量适应“大多数”数据点,而较大的 C 值更强调每个数据点都分类正确的重要性。
- 强正则化的模型会选择一条相对水平的线。
- 与回归的情况类似,用于分类的线性模型在低维空间中看起来可能非常受限,决策边界只能是直线或平面。同样,在高维空间中,用于分类的线性模型变得非常强大,当考虑更多特征时,避免过拟合变得越来越重要。
用于多分类的模型
one-vs.-rest一对其余
在“一对其余”方法中,对每个类别都学习一个二分类模型,将这个类别与所有其他类别尽量分开,这样就生成了与类别个数一样多的二分类模型。在测试点上运行所有二类分类器来进行预测。在对应类别上分数最高的分类器“胜出”,将这个类别标签返回作为预测结果。
每个类别都对应一个二类分类器,这样每个类别也都有一个系数(w)向量和一个截距(b)。下面给出的是分类置信方程,其结果中最大值对应的类别即为预测的类别标签:
w[0] * x[0] + w[1] * x[1] + … + w[p] * x[p] + b
多分类Logistic回归
多分类 Logistic 回归背后的数学与“一对其余”方法稍有不同,但它也是对每个类别都有一个系数向量和一个截距,也使用了相同的预测方法。
线性模型的优缺点、参数总结
参数
线性模型的主要参数是正则化参数,在回归模型中叫作 alpha ,在 LinearSVC 和 LogisticRegression 中叫作 C 。 alpha 值较大或 C 值较小,说明模型比较简单。
L1 正则化/ L2 正则化:如果你假定只有几个特征是真正重要的,那么你应该用L1 正则化,否则应默认使用 L2 正则化。如果模型的可解释性很重要的话,使用 L1 也会有帮助。
优点
- 训练速度快、预测速度也快;
- 可以应用于非常大的数据集,对稀疏矩阵也很有效;
- 可以应用于高维数据(特征较多的数据),因为训练其他模型可能并不行,但在更低维的空间,其他模型泛化性能可能会更好。