Sklearn官方文档中文整理4——随机梯度下降和最近邻篇

1. 监督学习

1.5. 随机梯度下降

随机梯度下降(SGD) 是一种简单但又非常高效的方法,主要用于凸损失函数下线性分类器的判别式学习,例如(线性) 支持向量机 和 Logistic 回归 。 尽管 SGD 在机器学习社区已经存在了很长时间, 但是最近在 large-scale learning (大规模学习)方面 SGD 获得了相当大的关注。

SGD 已成功应用于在文本分类和自然语言处理中经常遇到的大规模和稀疏的机器学习问题。对于稀疏数据,本模块的分类器可以轻易的处理超过 10^5 的训练样本和超过 10^5 的特征。

  • Stochastic Gradient Descent (随机梯度下降法)的优势:
    高效。
    易于实现 (有大量优化代码的机会)。

  • Stochastic Gradient Descent (随机梯度下降法)的劣势:
    SGD 需要一些超参数,例如 regularization (正则化)参数和 number of iterations (迭代次数)。
    SGD 对 feature scaling (特征缩放)敏感。

1.5.1. 分类【linear_model.SGDClassifier】

警告:

在拟合模型前,确保你重新排列了(打乱)你的训练数据,或者使用 shuffle=True 在每次迭代后打乱训练数据。

SGDClassifier 类实现了一个简单的随机梯度下降学习例程, 支持分类问题不同的损失函数和正则化方法。
在这里插入图片描述
作为另一个 classifier (分类器), 拟合 SGD 我们需要两个 array (数组):保存训练样本的 size 为 [n_samples, n_features] 的数组 X 以及保存训练样本目标值(类标签)的 size 为 [n_samples] 的数组 Y

>>> from sklearn.linear_model import SGDClassifier
>>> X = [[0., 0.], [1., 1.]]
>>> y = [0, 1]
>>> clf = SGDClassifier(loss="hinge", penalty="l2")
>>> clf.fit(X, y)
SGDClassifier(alpha=0.0001, average=False, class_weight=None,
              early_stopping=False, epsilon=0.1, eta0=0.0, fit_intercept=True,
              l1_ratio=0.15, learning_rate='optimal', loss='hinge',
              max_iter=1000, n_iter_no_change=5, n_jobs=None, penalty='l2',
              power_t=0.5, random_state=None, shuffle=True, tol=0.001,
              validation_fraction=0.1, verbose=0, warm_start=False)

拟合后,我们可以用该模型来预测新值:

>>> clf.predict([[2., 2.]])
array([1])

SGD 通过训练数据来拟合一个线性模型。成员 coef_ 保存模型参数:

>>> clf.coef_                                         
array([[ 9.9...,  9.9...]])

成员intercept_保存 intercept(截距) (又称作 offset(偏移)或 bias(偏差)):

>>> clf.intercept_                                    
array([-9.9...])

模型是否使用 intercept(截距), 即 a biased hyperplane(一个偏置的超平面), 是由参数fit_intercept 控制的。

使用 SGDClassifier.decision_function 来获得到此超平面的 signed distance (符号距离)

>>> clf.decision_function([[2., 2.]])                 
array([ 29.6...])

具体的 loss function(损失函数) 可以通过 loss 参数来设置。 SGDClassifier 支持以下的 loss functions(损失函数):

  • loss="hinge": (soft-margin) linear Support Vector Machine ((软-间隔)线性支持向量机),
  • loss="modified_huber": smoothed hinge loss (平滑的 hinge 损失),
  • loss="log": logistic regression (logistic 回归),
  • and all regression losses below(以及所有的回归损失)。

前两个 loss functions(损失函数)是懒惰的,只有一个例子违反了 margin constraint(边界约束),它们才更新模型的参数, 这使得训练非常有效率,即使使用了 L2 penalty(惩罚)我们仍然可能得到稀疏的模型结果。

使用loss="log" 或者loss="modified_huber" 来启用 predict_proba 方法, 其给出每个样本 x 的概率估计 P(y|x) 的一个向量:

>>> clf = SGDClassifier(loss="log").fit(X, y)
>>> clf.predict_proba([[1., 1.]])                      
array([[ 0.00...,  0.99...]])

具体的惩罚方法可以通过 penalty 参数来设定。 SGD 支持以下 penalties(惩罚):

  • penalty="l2": L2 norm penalty on coef_.
  • penalty="l1": L1 norm penalty on coef_.
  • penalty="elasticnet": Convex combination of L2 and L1(L2 型和 L1 型的凸组合); (1 - l1_ratio) * L2 + l1_ratio * L1.

默认设置为 penalty="l2" 。 L1 penalty (惩罚)导致稀疏解,使得大多数系数为零。 Elastic Net(弹性网)解决了在特征高相关时 L1 penalty(惩罚)的一些不足。参数l1_ratio 控制了 L1 和 L2 penalty(惩罚)的 convex combination (凸组合)。

SGDClassifier通过利用 “one versus all” (OVA)方法来组合多个二分类器,从而实现多分类。对于每一个 K 类, 可以训练一个二分类器来区分自身和其他 K-1 个类。在测试阶段,我们计算每个分类器的 confidence score(置信度分数)(也就是与超平面的距离),并选择置信度最高的分类。下图阐释了基于 iris(鸢尾花)数据集上的 OVA 方法。虚线表示三个 OVA 分类器; 不同背景色代表由三个分类器产生的决策面。
在这里插入图片描述
multi-class classification (多类分类)的情况下, coef_shape=[n_classes, n_features] 的一个二维数组,intercept_shape=[n_classes] 的一个一维数组。 coef_ 的第 i 行保存了第 i 类的 OVA 分类器的权重向量;类以升序索引 (参照属性 classes_ )。 注意,原则上,由于它们允许创建一个概率模型,所以 loss="log"loss="modified_huber" 更适合于 one-vs-all 分类。

SGDClassifier通过拟合参数 class_weightsample_weight 来支持 weighted classes (加权类)和 weighted instances(加权实例)。更多信息请参照下面的示例和 SGDClassifier.fit 的文档。

SGDClassifier 支持 averaged SGD (ASGD)Averaging(均值化)可以通过设置 average=True 来启用。AGSD 工作原理是在普通 SGD 的基础上,对每个样本的每次迭代后的系数取均值。当使用 ASGD 时,学习速率可以更大甚至是恒定,在一些数据集上能够加速训练过程。

对于带 logistic loss(logistic 损失)的分类,在LogisticRegression 中提供了另一个采取 averaging strategy(平均策略)的 SGD 变体,其使用了随机平均梯度 (SAG) 算法。

sklearn.linear_model.SGDClassifier

参数 解释
loss:str, default=’hinge’ 要使用的损失函数。默认为“hinge”,这将提供线性支持向量机。可能的选项有'hinge''log''modified_huber''squared_hinge''perceptron'或回归损失:'squared_loss''huber'epsilon_unsensitive''squared_epsilon_unsensitive'。“log’”损失给出了logistic回归,一种概率分类器。“modified_huber”是另一种平滑损失,它带来了对异常值的容忍度以及概率估计。“squared_hinge”与hinge类似,但受到二次惩罚。“perceptron”是感知器算法使用的线性损耗。其他损失是为回归设计的,但也可以用于分类
penalty:{‘l2’, ‘l1’, ‘elasticnet’}, default=’l2’ 要使用的惩罚(又名正则化术语)。默认为“l2”,这是线性SVM模型的标准正则化器。“l1”和“elasticnet”可能会给模型(特征选择)带来“l2”无法实现的稀疏性。
alpha:float, default=0.0001 乘以正则项的常数。值越大,正则化越强。当学习率设为“optimal”时,也用于计算学习率。
l1_ratio:float, default=0.15 弹性净混合参数,0 <= l1_ratio <= 1. l1_ratio=0对应于L2惩罚,l1_ratio=1到l1。仅当penalty为“elasticnet”时使用。
fit_intercept:bool, default=True 是否应该估计截距。如果为False,则假定数据已经居中。
max_iter:int, default=1000 通过训练数据的最大次数(又名epochs)。它只影响拟合方法中的行为,而不影响部分拟合方法中的行为。
tol:float, default=1e-3 停止标准
shuffle:bool, default=True 是否在每个epoch之后对训练数据进行洗牌。
verbose:int, default=0 详细程度。
epsilon:float, default=0.1 对于“huber”,它决定了一个阈值,在这个阈值下,准确的预测就变得不那么重要了。对于epsilon-insensitive,如果当前预测和正确标签之间的任何差异小于此阈值,则忽略它们。
n_jobs:int, default=None 线程数
random_state:int, RandomState instance, default=None 当shuffle设置为True时,用于洗牌数据。
learning_rate:str, default=’optimal’ 学习率:‘constant’: eta = eta0;‘optimal: eta = 1.0 / (alpha * (t + t0))其中t0由Leon Bottou提出的启发式方法选择;‘invscaling’: eta = eta0 / pow(t, power_t);‘adaptive’: eta = eta0,
eta0:double, default=0.0 ‘constant’, ‘invscaling’ 或‘adaptive’计划的初始学习速率。默认值为0.0,因为默认计划“optimal”未使用eta0。
power_t:double, default=0.5 反向缩放学习速率的指数[默认值为0.5]。
early_stopping:bool, default=False 验证分数没有提高时,是否使用提前停止终止培训。如果设置为True,它将自动将训练数据的分层部分作为验证,并且当分数方法返回的验证分数对于n_iter_no_change连续时间段没有至少提高tol时终止训练。
validation_fraction:float, default=0.1 作为早期停机验证设置的培训数据的比例。必须介于0和1之间。仅在“早停”为真时使用。
n_iter_no_change:int, default=5 在提前停止之前没有改进的迭代次数。
class_weight:dict, {class_label: weight} or “balanced”, default=None 预设class_weight参数,与类关联的权重。如果没有给出,所有的类都应该有一个权重。
warm_start:bool, default=False 当设置为True时,将上一个调用的解决方案重用为fit作为初始化,否则,只需删除以前的解决方案。
average:bool or int, default=False 当设置为True时,计算所有更新的averaged SGD权重,并将结果存储在coef_ 属性中。如果设置为大于1的整数,则当看到的样本总数达到平均值时,将开始平均。所以average=10将在看到10个样本后开始平均。
属性 解释
coef_:ndarray of shape (1, n_features) if n_classes == 2 else (n_classes, n_features) 为特征指定的权重。
intercept_:ndarray of shape (1,) if n_classes == 2 else (n_classes,) 决策函数中的常数。
n_iter_:int 达到停止标准之前的实际迭代次数。对于多类拟合,它是每个二进制拟合的最大值。
loss_function_concrete LossFunction
classes_array of shape (n_classes,)
t_:int 训练期间进行的体重更新次数。
方法 解释
decision_function(X) 预测样本的置信度得分。
densify() 将系数矩阵转换为密集数组格式。
fit(X, y[, coef_init, intercept_init, …]) 用随机梯度下降拟合线性模型。
get_params([deep]) 获取此估计器的参数。
predict(X) 预测
score(X, y[, sample_weight]) 返回预测的决定系数R^2。
set_params(**params) 设置此估计器的参数。
sparsify() 将系数矩阵转换为稀疏格式。

例:

>>> import numpy as np
>>> from sklearn.linear_model import SGDClassifier
>>> from sklearn.preprocessing import StandardScaler
>>> from sklearn.pipeline import make_pipeline
>>> X = np.array([[-1, -1], [-2, -1], [1, 1], [2, 1]])
>>> Y = np.array([1, 1, 2, 2])
>>> # Always scale the input. The most convenient way is to use a pipeline.
>>> clf = make_pipeline(StandardScaler(),
...                     SGDClassifier(max_iter=1000, tol=1e-3))
>>> clf.fit(X, Y)
Pipeline(steps=[('standardscaler', StandardScaler()),
                ('sgdclassifier', SGDClassifier())])
>>> print(clf.predict([[-0.8, -1]]))
[1]

1.5.2. 回归【linear_model.SGDRegressor】

SGDRegressor类实现了一个简单的随机梯度下降学习例程,它支持用不同的损失函数和惩罚来拟合线性回归模型。 SGDRegressor 非常适用于有大量训练样本(>10.000)的回归问题,对于其他问题,我们推荐使用 RidgeLasso ,或ElasticNet

具体的损失函数可以通过loss 参数设置。 SGDRegressor 支持以下的损失函数:

  • loss="squared_loss": Ordinary least squares(普通最小二乘法),
  • loss="huber": Huber loss for robust regression(Huber回归),
  • loss="epsilon_insensitive": linear Support Vector Regression(线性支持向量回归).

Huberepsilon-insensitive 损失函数可用于 robust regression(鲁棒回归)。不敏感区域的宽度必须通过参数 epsilon 来设定。这个参数取决于目标变量的规模。

SGDRegressor 支持 ASGD(平均随机梯度下降) 作为 SGDClassifier。 均值化可以通过设置 average=True 来启用。

对于利用了 squared loss(平方损失)和 l2 penalty(l2惩罚)的回归,在 Ridge 中提供了另一个采取 averaging strategy(平均策略)的 SGD 变体,其使用了随机平均梯度 (SAG) 算法。

sklearn.linear_model.SGDRegressor

参数 解释
loss:str, default=’squared_loss’ 要使用的损失函数。默认为“squared_loss”,这将提供线性支持向量机。可能的选项有'hinge''log''modified_huber''squared_hinge''perceptron'或回归损失:'squared_loss''huber'epsilon_unsensitive''squared_epsilon_unsensitive'。“log’”损失给出了logistic回归,一种概率分类器。“modified_huber”是另一种平滑损失,它带来了对异常值的容忍度以及概率估计。“squared_hinge”与hinge类似,但受到二次惩罚。“perceptron”是感知器算法使用的线性损耗。其他损失是为回归设计的,但也可以用于分类
penalty:{‘l2’, ‘l1’, ‘elasticnet’}, default=’l2’ 要使用的惩罚(又名正则化术语)。默认为“l2”,这是线性SVM模型的标准正则化器。“l1”和“elasticnet”可能会给模型(特征选择)带来“l2”无法实现的稀疏性。
alpha:float, default=0.0001 乘以正则项的常数。值越大,正则化越强。当学习率设为“optimal”时,也用于计算学习率。
l1_ratio:float, default=0.15 弹性净混合参数,0 <= l1_ratio <= 1. l1_ratio=0对应于L2惩罚,l1_ratio=1到l1。仅当penalty为“elasticnet”时使用。
fit_intercept:bool, default=True 是否应该估计截距。如果为False,则假定数据已经居中。
max_iter:int, default=1000 通过训练数据的最大次数(又名epochs)。它只影响拟合方法中的行为,而不影响部分拟合方法中的行为。
tol:float, default=1e-3 停止标准
shuffle:bool, default=True 是否在每个epoch之后对训练数据进行洗牌。
verbose:int, default=0 详细程度。
epsilon:float, default=0.1 对于“huber”,它决定了一个阈值,在这个阈值下,准确的预测就变得不那么重要了。对于epsilon-insensitive,如果当前预测和正确标签之间的任何差异小于此阈值,则忽略它们。
random_state:int, RandomState instance, default=None 当shuffle设置为True时,用于洗牌数据。
learning_rate:str, default=’optimal’ 学习率:‘constant’: eta = eta0;‘optimal: eta = 1.0 / (alpha * (t + t0))其中t0由Leon Bottou提出的启发式方法选择;‘invscaling’: eta = eta0 / pow(t, power_t);‘adaptive’: eta = eta0,
eta0:double, default=0.0 ‘constant’, ‘invscaling’ 或‘adaptive’计划的初始学习速率。默认值为0.0,因为默认计划“optimal”未使用eta0。
power_t:double, default=0.25 反向缩放学习速率的指数[默认值为0.25]。
early_stopping:bool, default=False 验证分数没有提高时,是否使用提前停止终止培训。如果设置为True,它将自动将训练数据的分层部分作为验证,并且当分数方法返回的验证分数对于n_iter_no_change连续时间段没有至少提高tol时终止训练。
validation_fraction:float, default=0.1 作为早期停机验证设置的培训数据的比例。必须介于0和1之间。仅在“早停”为真时使用。
n_iter_no_change:int, default=5 在提前停止之前没有改进的迭代次数。
warm_start:bool, default=False 当设置为True时,将上一个调用的解决方案重用为fit作为初始化,否则,只需删除以前的解决方案。
average:bool or int, default=False 当设置为True时,计算所有更新的averaged SGD权重,并将结果存储在coef_ 属性中。如果设置为大于1的整数,则当看到的样本总数达到平均值时,将开始平均。所以average=10将在看到10个样本后开始平均。
属性 解释
coef_:ndarray of shape (n_features,) 为特征指定的权重。
intercept_:ndarray of shape (1,) 决策函数中的常数。
average_coef_:ndarray of shape (n_features,) 分配给特征的平均权重。仅当average=True时可用。
average_intercept_:ndarray of shape (1,) 平均截距项。仅当average=True时可用。
n_iter_:int 达到停止标准之前的实际迭代次数。对于多类拟合,它是每个二进制拟合的最大值。
t_:int 训练期间进行的体重更新次数。
方法 解释
densify() 将系数矩阵转换为密集数组格式。
fit(X, y[, coef_init, intercept_init, …]) 用随机梯度下降拟合线性模型。
get_params([deep]) 获取此估计器的参数。
partial_fit(X, y[, sample_weight]) 对给定样本执行一个随机梯度下降的epoch。
predict(X) 预测
score(X, y[, sample_weight]) 返回预测的决定系数R^2。
set_params(**params) 设置此估计器的参数。
sparsify() 将系数矩阵转换为稀疏格式。

例:

>>> import numpy as np
>>> from sklearn.linear_model import SGDRegressor
>>> from sklearn.pipeline import make_pipeline
>>> from sklearn.preprocessing import StandardScaler
>>> n_samples, n_features = 10, 5
>>> rng = np.random.RandomState(0)
>>> y = rng.randn(n_samples)
>>> X = rng.randn(n_samples, n_features)
>>> # Always scale the input. The most convenient way is to use a pipeline.
>>> reg = make_pipeline(StandardScaler(),
...                     SGDRegressor(max_iter=1000, tol=1e-3))
>>> reg.fit(X, y)
Pipeline(steps=[('standardscaler', StandardScaler()),
                ('sgdregressor', SGDRegressor())])

1.5.3. 稀疏数据的随机梯度下降

注意

由于在截距部分收敛学习速率的差异,稀疏实现与密集实现相比产生的结果略有不同。

在 scipy.sparse 支持的格式中,任意矩阵都有对稀疏数据的内置支持方法。但是,为了获得最高的效率,请使用 scipy.sparse.csr_matrix 中定义的 CSR 矩阵格式.

1.5.4. 复杂度

SGD 主要的优势在于它的高效性,对于不同规模的训练样本,处理复杂度基本上是线性的。假如 X 是 size 为 (n, p) 的矩阵,训练成本为 O ( k n p ˉ ) O(k n \bar p) O(knpˉ),其中 k 是迭代次数, p ˉ \bar p pˉ 是每个样本非零特征的平均数。

但是,最近的理论结果表明,得到期望优化精度的运行时间并不会随着训练集规模扩大而增加。

1.5.5. 停止判据

SGDClassifierSGDRegressor 类提供了两个判据,当达到给定的收敛水平时停止算法:

  • early_stopping = True时,输入数据分为训练集和验证集。该模型在训练集拟合,停止判据是基于验证集上的预测分数。改变参数validation_fraction可以调整验证集的大小。

  • early_stop = False时,模型对整个输入数据进行拟合,停止判据基于整个输入数据上的目标函数来计算。

在这两种情况下,判据在每个epoch计算一次,当判据没有改变的次数超过参数n_iter_no_change的值时,算法将停止。改进是用一个容错参数tol来评估的,并且算法在最大迭代次数max_iter之后停止。

1.5.6. 实用小贴士

随机梯度下降法对 feature scaling (特征缩放)很敏感,因此强烈建议您缩放您的数据。例如,将输入向量 X 上的每个特征缩放到 [0,1] 或 [- 1,+1], 或将其标准化,使其均值为 0,方差为 1。请注意,必须将 相同 的缩放应用于对应的测试向量中,以获得有意义的结果。使用 StandardScaler能很容易做到这一点:

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(X_train) # Don’t cheat - fit only on training data
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test) # apply same transformation to test data

假如你的 attributes (属性)有一个固有尺度(例如 word frequencies (词频)或 indicator features(指标特征))就不需要缩放。

最好使用 GridSearchCV 找到一个合理的 regularization term (正则化项) α \alpha α , 它的范围通常在 10.0 ∗ ∗ − n p . a r a n g e ( 1 , 7 ) 10.0**-np.arange(1,7) 10.0np.arange(1,7)

经验表明,SGD 在处理约 10^6 训练样本后基本收敛。因此,对于迭代次数第一个合理的猜想是 n_iter = np.ceil(10**6 / n),其中 n 是训练集的大小。

假如将 SGD 应用于使用 PCA 提取的特征,我们发现通过某个常数 c 来缩放特征值是明智的,比如使训练数据的 L2 norm 平均值为 1。

我们发现,当特征很多或 eta0 很大时, ASGD(平均随机梯度下降) 效果更好

1.5.7. 数学描述

给定一组训练样本 ( x 1 , y 1 ) , … , ( x n , y n ) (x_1, y_1), \ldots, (x_n, y_n) (x1,y1),,(xn,yn) ,其中 x i ∈ R m , y i ∈ − 1 , 1 , 我 们 的 目 标 是 一 个 线 性 s c o r i n g f u n c t i o n ( 评 价 函 数 ) f ( x ) = w T x + b x_i \in \mathbf{R}^m , y_i \in {-1,1}, 我们的目标是一个线性 scoring function(评价函数) f(x) = w^T x + b xiRmyi1,1线scoringfunctionf(x)=wTx+b,其中模型参数 w ∈ R m w \in \mathbf{R}^m wRm ,截距 b ∈ R b \in \mathbf{R} bR。为了做预测, 我们只需要看 f(x) 的符号。找到模型参数的一般选择是通过最小化由以下式子给出的正则化训练误差
E ( w , b ) = 1 n ∑ i = 1 n L ( y i , f ( x i ) ) + α R ( w ) E(w,b) = \frac{1}{n}\sum_{i=1}^{n} L(y_i, f(x_i)) + \alpha R(w) E(w,b)=n1i=1nL(yi,f(xi))+αR(w)
其中 L 衡量模型(mis)拟合程度的损失函数,R 是惩罚模型复杂度的正则化项(也叫作惩罚); α > 0 \alpha > 0 α>0 是一个非负超平面。

L 的不同选择产生不同的分类器,例如

  • Hinge: (软-间隔) 支持向量机。
  • Log: Logistic 回归。
  • Least-Squares: 岭回归。
  • Epsilon-Insensitive: (软-间隔) 支持向量回归。

所有上述损失函数可以看作是错误分类误差的上限(0 - 1损失),如下图所示。
在这里插入图片描述
比较流行的正则化项 R 包括:

  • L2 norm: R ( w ) : = 1 2 ∑ i = 1 n w i 2 R(w) := \frac{1}{2} \sum_{i=1}^{n} w_i^2 R(w):=21i=1nwi2,
  • L1 norm: R ( w ) : = ∑ i = 1 n ∣ w i ∣ R(w) := \sum_{i=1}^{n} |w_i| R(w):=i=1nwi, 这导致了稀疏解。
  • Elastic Net: R ( w ) : = ρ 2 ∑ i = 1 n w i 2 + ( 1 − ρ ) ∑ i = 1 n ∣ w i ∣ R(w) := \frac{\rho}{2} \sum_{i=1}^{n} w_i^2 + (1-\rho) \sum_{i=1}^{n} |w_i| R(w):=2ρi=1nwi2+(1ρ)i=1nwi, l2和l1的凸组合, 其中 ρ \rho ρ 1 − l 1 r a t i o 1 - l1_ratio 1l1ratio给出.

下图显示当 R(w) = 1 时参数空间中不同正则项的轮廓。
在这里插入图片描述

1.5.7.1. SGD

随机梯度下降法是一种无约束优化问题的优化方法。与(批量)梯度下降法相反,SGD 通过一次只考虑单个训练样本来近似 E(w,b) 真实的梯度。

SGDClassifier 类实现了一个first-order SGD learning routine (一阶 SGD 学习程序)。 算法在训练样本上遍历,并且对每个样本根据由以下式子给出的更新规则来更新模型参数。
w ← w − η ( α ∂ R ( w ) ∂ w + ∂ L ( w T x i + b , y i ) ∂ w ) w \leftarrow w - \eta (\alpha \frac{\partial R(w)}{\partial w}+ \frac{\partial L(w^T x_i + b, y_i)}{\partial w}) wwη(αwR(w)+wL(wTxi+b,yi))
其中 η \eta η 是在参数空间中控制步长的 learning rate (学习速率)。 intercept(截距) b 的更新类似但不需要正则化。

学习率 η \eta η 可以恒定或者逐渐减小。对于分类来说, 默认的学习率设定方案 (learning_rate=‘optimal’)由下式给出。
η ( t ) = 1 α ( t 0 + t ) \eta^{(t)} = \frac {1}{\alpha (t_0 + t)} η(t)=α(t0+t)1
其中 t 是时间步长(总共有 (n_samples * n_iter)时间步长), t 0 t_0 t0 是由 Léon Bottou 提出的启发式算法决定的,比如预期的初始更新可以设定为权重的期望大小(假设训练样本的范数近似1)。在 BaseSGD 中的 _init_t 中可以找到确切的定义。

对于回归来说,默认的学习率是反向缩放 (learning_rate=‘invscaling’),由下式给出
η ( t ) = e t a 0 t p o w e r t \eta^{(t)} = \frac{eta_0}{t^{power_t}} η(t)=tpowerteta0
其中 e t a 0 eta_0 eta0 p o w e r t power_t powert 是用户通过 e t a 0 eta0 eta0 p o w e r t power_t powert 分别选择的超参数。

使用固定的学习速率则设置 learning_rate=‘constant’ ,或者设置 eta0 来指定学习速率。

模型参数可以通过成员 KaTeX parse error: Expected group after '_' at position 5: coef_̲KaTeX parse error: Expected group after '_' at position 10: intercept_̲ 来获得:

  • 成员 coef_ holds the weights(控制权重) w w w
  • 成员 intercept_ holds b b b

1.5.8. 实现细节

SGD 的实现受到了 Léon Bottou Stochastic Gradient SVM 的影响。类似于 SvmSGD,权重向量表达为一个标量和一个向量的内积,这样保证在使用L2正则项时可以高效更新权重。 在 sparse feature vectors (稀疏特征向量)的情况下, intercept (截距)是以更小的学习率(乘以 0.01)更新的,这导致了它的更新更加频繁。训练样本按顺序选取并且每处理一个样本就要降低学习速率。我们采用了 Shalev-Shwartz 等人2007年提出的的学习速率设定方案。 对于多类分类,我们使用了 “one versus all” 方法。 我们在 L1 正则化(和 Elastic Net )中使用 Tsuruoka 等人2009年提出的 truncated gradient algorithm (截断梯度算法)。代码是用 Cython 编写的。

1.6. 最近邻

sklearn.neighbors 提供了 neighbors-based (基于邻居的) 无监督学习以及监督学习方法的功能。 无监督的最近邻是许多其它学习方法的基础,尤其是 manifold learning (流形学习)spectral clustering (谱聚类)。 neighbors-based (基于邻居的) 监督学习分为两种: classification (分类)针对的是具有离散标签的数据,regression (回归)针对的是具有连续标签的数据。

最近邻方法背后的原理是从训练样本中找到与新点在距离上最近的预定数量的几个点,然后从这些点中预测标签。 这些点的数量可以是用户自定义的常量(K-最近邻学习), 也可以根据不同的点的局部密度(基于半径的最近邻学习)确定。距离通常可以通过任何度量来衡量: standard Euclidean distance(标准欧式距离)是最常见的选择。Neighbors-based(基于邻居的)方法被称为 非泛化 机器学习方法, 因为它们只是简单地”记住”了其所有的训练数据(可能转换为一个快速索引结构,如 Ball Tree 或 KD Tree)。

尽管它简单,但最近邻算法已经成功地适用于很多的分类和回归问题,例如手写数字或卫星图像的场景。 作为一个 non-parametric(非参数化)方法,它经常成功地应用于决策边界非常不规则的分类情景下。

sklearn.neighbors 可以处理 Numpy 数组或 scipy.sparse 矩阵作为其输入。 对于密集矩阵,大多数可能的距离度量都是支持的对于稀疏矩阵,支持搜索任意的 Minkowski 度量

许多学习路径/方法都是依赖最近邻作为核心。 一个例子是 核密度估计 , 在 密度估计 章节中有讨论。

1.6.1. 无监督最近邻【neighbors.NearestNeighbors】

NearestNeighbors (最近邻)实现了 unsupervised nearest neighbors learning(无监督的最近邻学习)。 它为三种不同的最近邻算法提供统一的接口:BallTree, KDTree, 还有基于 sklearn.metrics.pairwise 的 brute-force 算法。算法的选择可通过关键字'algorithm'来控制, 并必须是['auto', 'ball_tree', 'kd_tree', 'brute']其中的一个。当设置为默认值 ‘auto’ 时,算法会尝试从训练数据中确定最佳方法。有关上述每个选项的优缺点,参见 Nearest Neighbor Algorithms_ 。

警告

关于最近邻算法,如果邻居 k+1 和邻居 k 具有相同的距离,但具有不同的标签, 结果将取决于训练数据的顺序。

sklearn.neighbors.NearestNeighbors

参数 解释
n_neighbors:int, default=5 默认情况下用于kneighbors查询的邻居数。
radius:float, default=1.0 默认情况下用于radius_neighbors查询的参数空间范围。
algorithm:{‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’}, default=’auto’ 用于计算最近邻的算法:['auto', 'ball_tree', 'kd_tree', 'brute']其中的一个
leaf_size:int, default=30 传递给BallTree或KDTree的叶大小。这会影响构造和查询的速度,以及存储树所需的内存。最佳值取决于问题的性质。
metric:str or callable, default=’minkowski’ 要用于树的距离度量。默认的度量是minkowski,p=2等于标准的欧几里得度量。有关可用度量的列表,请参阅DistanceMetric文档。若metric是“precomputed”则假设X是距离矩阵,并且在拟合期间必须是平方的。X可能是一个稀疏图,在这种情况下,只有“nonzero” 元素可以被视为邻居。
p:int, default=2 当p=1时,这相当于使用曼哈顿距离(l1),欧几里德距离(l2)表示p=2。对于任意的p,使用minkowskiïu距离(lïp)。
metric_params:dict, default=None 度量函数的其他关键字参数。
n_jobs:int, default=None 线程数
属性 解释
effective_metric_:str 用于计算到邻居距离的度量。
effective_metric_params_:dict 用于计算到邻居的距离的度量的参数。
n_samples_fit_:int 拟合数据中的样本数。
方法 解释
fit(X[, y]) 从训练数据集中拟合最近邻估计量。
get_params([deep]) 获取此估计器的参数。
kneighbors([X, n_neighbors, return_distance]) 找到点的K-邻域。
kneighbors_graph([X, n_neighbors, mode]) 计算X中点的k-邻域的(加权)图
radius_neighbors([X, radius, …]) 查找一个或多个点的给定半径内的邻居。
radius_neighbors_graph([X, radius, mode, …]) 计算X中点的邻域(加权)图
set_params(**params) 设置此估计器的参数。

例:

>>> import numpy as np
>>> from sklearn.neighbors import NearestNeighbors
>>> samples = [[0, 0, 2], [1, 0, 0], [0, 0, 1]]
>>>
>>> neigh = NearestNeighbors(n_neighbors=2, radius=0.4)
>>> neigh.fit(samples)
NearestNeighbors(...)
>>>
>>> neigh.kneighbors([[0, 0, 1.3]], 2, return_distance=False)
array([[2, 0]]...)
>>>
>>> nbrs = neigh.radius_neighbors(
...    [[0, 0, 1.3]], 0.4, return_distance=False
... )
>>> np.asarray(nbrs[0][0])
array(2)
                ('sgdregressor', SGDRegressor())])

1.6.1.1. 找到最近邻

为了完成找到两组数据集中最近邻点的简单任务, 可以使用 sklearn.neighbors 中的无监督算法:

>>> from sklearn.neighbors import NearestNeighbors
>>> import numpy as np
>>> X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
>>> nbrs = NearestNeighbors(n_neighbors=2, algorithm='ball_tree').fit(X)
>>> distances, indices = nbrs.kneighbors(X)
>>> indices                                           
array([[0, 1],
 [1, 0],
 [2, 1],
 [3, 4],
 [4, 3],
 [5, 4]]...)
>>> distances
array([[0.        , 1.        ],
       [0.        , 1.        ],
       [0.        , 1.41421356],
       [0.        , 1.        ],
       [0.        , 1.        ],
       [0.        , 1.41421356]])

因为查询集匹配训练集,每个点的最近邻点是其自身,距离为0。

还可以有效地生成一个稀疏图来标识相连点之间的连接情况:

>>> nbrs.kneighbors_graph(X).toarray()
array([[ 1.,  1.,  0.,  0.,  0.,  0.],
 [ 1.,  1.,  0.,  0.,  0.,  0.],
 [ 0.,  1.,  1.,  0.,  0.,  0.],
 [ 0.,  0.,  0.,  1.,  1.,  0.],
 [ 0.,  0.,  0.,  1.,  1.,  0.],
 [ 0.,  0.,  0.,  0.,  1.,  1.]])

我们的数据集是结构化的,因此按索引顺序的相邻点就在参数空间相邻,从而生成了近似 K-nearest neighbors(K-近邻)的块对角矩阵。 这种稀疏图在各种的利用点之间的空间关系进行无监督学习的情况下都很有用:特别地可参见 sklearn.manifold.Isomap, sklearn.manifold.LocallyLinearEmbedding, 和 sklearn.cluster.SpectralClustering。

1.6.1.2. KDTree 和 BallTree 类【neighbors.KDTree,neighbors.BallTree】

另外,我们可以使用 KDTreeBallTree 来找最近邻。 这是上文使用过的 NearestNeighbors 类所包含的功能。 KDTreeBallTree 具有相同的接口; 我们将在这里展示使用 KDTree 的例子:

>>> from sklearn.neighbors import KDTree
>>> import numpy as np
>>> X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
>>> kdt = KDTree(X, leaf_size=30, metric='euclidean')
>>> kdt.query(X, k=2, return_distance=False)          
array([[0, 1],
 [1, 0],
 [2, 1],
 [3, 4],
 [4, 3],
 [5, 4]]...)

sklearn.neighbors.KDTree

参数 解释
X:array-like of shape (n_samples, n_features) n_samples是数据集中的点数,n_features是参数空间的维数。注意:如果X是双精度的C-连续数组,则不会复制数据。否则,将制作内部副本。
leaf_size:positive int, default=40 切换到 brute-force的点数。更改leaf_size不会影响查询结果,但会显著影响查询速度和存储所构造树所需的内存。存储树规模所需的内存量大约为n_samples / leaf_size。对于指定的叶大小,保证叶节点满足leaf_size <= n_points <= 2 * leaf_size除非 n_samples < leaf_size
metric:str or DistanceMetric object 要用于树的距离度量。Default=‘minkowski’,p=2(即欧几里德度量)。
属性 解释
data:memory view 训练数据
方法 解释
get_arrays(self) 获取数据和节点数组。
get_n_calls(self) Get number of calls.
get_tree_stats(self) 获取树状态。
kernel_density(self, X, h[, kernel, atol, …]) 使用树创建时指定的距离度量,使用给定的核在点X处计算核密度估计值。
query(X[, k, return_distance, dualtree, …]) 查询树中的k个最近邻居
query_radius(X, r[, return_distance, …]) 在树上查询半径r内的邻居
reset_n_calls(self) 将呼叫数重置为0。
two_point_correlation(X, r[, dualtree]) 计算两点相关函数

例:
查询k近邻

>>> import numpy as np
>>> rng = np.random.RandomState(0)
>>> X = rng.random_sample((10, 3))  # 10 points in 3 dimensions
>>> tree = KDTree(X, leaf_size=2)              
>>> dist, ind = tree.query(X[:1], k=3)                
>>> print(ind)  # indices of 3 closest neighbors
[0 3 1]
>>> print(dist)  # distances to 3 closest neighbors
[ 0.          0.19662693  0.29473397]

sklearn.neighbors.BallTree

参数 解释
X:array-like of shape (n_samples, n_features) n_samples是数据集中的点数,n_features是参数空间的维数。注意:如果X是双精度的C-连续数组,则不会复制数据。否则,将制作内部副本。
leaf_size:positive int, default=40 切换到 brute-force的点数。更改leaf_size不会影响查询结果,但会显著影响查询速度和存储所构造树所需的内存。存储树规模所需的内存量大约为n_samples / leaf_size。对于指定的叶大小,保证叶节点满足leaf_size <= n_points <= 2 * leaf_size除非 n_samples < leaf_size
metric:str or DistanceMetric object 要用于树的距离度量。Default=‘minkowski’,p=2(即欧几里德度量)。
属性 解释
data:memory view 训练数据
方法 解释
get_arrays(self) 获取数据和节点数组。
get_n_calls(self) Get number of calls.
get_tree_stats(self) 获取树状态。
kernel_density(self, X, h[, kernel, atol, …]) 使用树创建时指定的距离度量,使用给定的核在点X处计算核密度估计值。
query(X[, k, return_distance, dualtree, …]) 查询树中的k个最近邻居
query_radius(X, r[, return_distance, …]) 在树上查询半径r内的邻居
reset_n_calls(self) 将呼叫数重置为0。
two_point_correlation(X, r[, dualtree]) 计算两点相关函数

例:
查询k近邻

>>> import numpy as np
>>> rng = np.random.RandomState(0)
>>> X = rng.random_sample((10, 3))  # 10 points in 3 dimensions
>>> tree = BallTree(X, leaf_size=2)              
>>> dist, ind = tree.query(X[:1], k=3)                
>>> print(ind)  # indices of 3 closest neighbors
[0 3 1]
>>> print(dist)  # distances to 3 closest neighbors
[ 0.          0.19662693  0.29473397]

1.6.2. 最近邻分类【neighbors.KNeighborsClassifier,neighbors.RadiusNeighborsClassifier】

最近邻分类属于 基于实例的学习非泛化学习 :它不会去构造一个泛化的内部模型,而是简单地存储训练数据的实例。 分类是由每个点的最近邻的简单多数投票中计算得到的:一个查询点的数据类型是由它最近邻点中最具代表性的数据类型来决定的。

scikit-learn 实现了两种不同的最近邻分类器: 基于每个查询点的 k 个最近邻实现,其中 k 是用户指定的整数值。RadiusNeighborsClassifier 基于每个查询点的固定半径 r 内的邻居数量实现, 其中 r 是用户指定的浮点数值。

k -邻居分类是KNeighborsClassifie的技术中比较常用的一种。 值的最佳选择是高度依赖数据的:通常较大的 k 是会抑制噪声的影响,但是使得分类界限不明显。

如果数据是不均匀采样的,那么 RadiusNeighborsClassifier 中的基于半径的近邻分类可能是更好的选择。用户指定一个固定半径 ,使得稀疏邻居中的点使用较少的最近邻来分类。对于高维参数空间,这个方法会由于所谓的 “维度灾难” 而变得不那么有效。

基本的最近邻分类使用统一的权重:分配给查询点的值是从最近邻的简单多数投票中计算出来的。 在某些环境下,最好对邻居进行加权,使得更近邻更有利于拟合。可以通过 weights 关键字来实现。默认值 weights = 'uniform' 为每个近邻分配统一的权重。而 weights = 'distance'分配权重与查询点的距离成反比。 或者,用户可以自定义一个距离函数用来计算权重。
在这里插入图片描述
在这里插入图片描述
sklearn.neighbors.KNeighborsClassifier

参数 解释
n_neighbors:int, default=5 默认情况下用于kneighbors查询的邻居数。
weights:{‘uniform’, ‘distance’} or callable, default=’uniform’ 用于预测的权重函数。可能值:‘uniform’ : 统一的权重。每个邻域中的所有点的权重相等。‘distance’ : 按距离的倒数加权点。在这种情况下,查询点的近邻会比距离较远的邻居影响更大。[callable]: 一个用户定义的函数,它接受一个距离数组,并返回一个包含权重的形状相同的数组。
algorithm:{‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’}, default=’auto’ 用于计算最近邻的算法:['auto', 'ball_tree', 'kd_tree', 'brute']其中的一个
leaf_size:int, default=30 传递给BallTree或KDTree的叶大小。这会影响构造和查询的速度,以及存储树所需的内存。最佳值取决于问题的性质。
p:int, default=2 当p=1时,这相当于使用曼哈顿距离(l1),欧几里德距离(l2)表示p=2。对于任意的p,使用minkowskiïu距离(lïp)。
metric:str or callable, default=’minkowski’ 要用于树的距离度量。默认的度量是minkowski,p=2等于标准的欧几里得度量。有关可用度量的列表,请参阅DistanceMetric文档。若metric是“precomputed”则假设X是距离矩阵,并且在拟合期间必须是平方的。X可能是一个稀疏图,在这种情况下,只有“nonzero” 元素可以被视为邻居。
metric_params:dict, default=None 度量函数的其他关键字参数。
n_jobs:int, default=None 线程数
属性 解释
classes_:array of shape (n_classes,) 分类器已知的类标签
effective_metric_:str or callble 用于计算到邻居距离的度量。
effective_metric_params_:dict 用于计算到邻居的距离的度量的参数。
n_samples_fit_:int 拟合数据中的样本数。
outputs_2d_:bool 如果y的形状在拟合期间为(n_samples,)或(n_samples,1),则为False,否则为True。
方法 解释
fit(X[, y]) 从训练数据集拟合k-最近邻分类器。
get_params([deep]) 获取此估计器的参数。
kneighbors([X, n_neighbors, return_distance]) 找到点的K-邻域。
kneighbors_graph([X, n_neighbors, mode]) 计算X中点的k-邻域的(加权)图
predict(X) 预测
predict_proba(X) 测试数据X的返回概率估计。
score(X, y[, sample_weight]) 返回给定测试数据和标签的平均精度。
set_params(**params) 设置此估计器的参数。

例:

>>> X = [[0], [1], [2], [3]]
>>> y = [0, 0, 1, 1]
>>> from sklearn.neighbors import KNeighborsClassifier
>>> neigh = KNeighborsClassifier(n_neighbors=3)
>>> neigh.fit(X, y)
KNeighborsClassifier(...)
>>> print(neigh.predict([[1.1]]))
[0]
>>> print(neigh.predict_proba([[0.9]]))
[[0.66666667 0.33333333]]

sklearn.neighbors.RadiusNeighborsClassifier

参数 解释
radius:float, default=1.0 默认情况下用于radius_neighbors查询的参数空间范围。
weights:{‘uniform’, ‘distance’} or callable, default=’uniform’ 用于预测的权重函数。可能值:‘uniform’ : 统一的权重。每个邻域中的所有点的权重相等。‘distance’ : 按距离的倒数加权点。在这种情况下,查询点的近邻会比距离较远的邻居影响更大。[callable]: 一个用户定义的函数,它接受一个距离数组,并返回一个包含权重的形状相同的数组。
algorithm:{‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’}, default=’auto’ 用于计算最近邻的算法:['auto', 'ball_tree', 'kd_tree', 'brute']其中的一个
leaf_size:int, default=30 传递给BallTree或KDTree的叶大小。这会影响构造和查询的速度,以及存储树所需的内存。最佳值取决于问题的性质。
p:int, default=2 当p=1时,这相当于使用曼哈顿距离(l1),欧几里德距离(l2)表示p=2。对于任意的p,使用minkowskiïu距离(lïp)。
metric:str or callable, default=’minkowski’ 要用于树的距离度量。默认的度量是minkowski,p=2等于标准的欧几里得度量。有关可用度量的列表,请参阅DistanceMetric文档。若metric是“precomputed”则假设X是距离矩阵,并且在拟合期间必须是平方的。X可能是一个稀疏图,在这种情况下,只有“nonzero” 元素可以被视为邻居。
outlier_label:{manual label, ‘most_frequent’}, default=None 异常值样本的标签(给定半径内没有相邻的样本)。manual label:str或int label(应与y的类型相同)或手动标签列表(如果使用多个输出)。“most_frequency”:为离群值指定最频繁的y标签。None:当检测到任何异常值时,将引发ValueError。
metric_params:dict, default=None 度量函数的其他关键字参数。
n_jobs:int, default=None 线程数
属性 解释
classes_:array of shape (n_classes,) 分类器已知的类标签
effective_metric_:str or callble 用于计算到邻居距离的度量。
effective_metric_params_:dict 用于计算到邻居的距离的度量的参数。
n_samples_fit_:int 拟合数据中的样本数。
outlier_label_:int or array-like of shape (n_class,) 为离群样本(给定半径上没有邻居的样本)指定的标签。
outputs_2d_:bool 如果y的形状在拟合期间为(n_samples,)或(n_samples,1),则为False,否则为True。
方法 解释
fit(X[, y]) 从训练数据集拟合k-最近邻分类器。
get_params([deep]) 获取此估计器的参数。
predict(X) 预测
predict_proba(X) 测试数据X的返回概率估计。
radius_neighbors([X, radius, …]) 查找一个或多个点的给定半径内的邻居。
radius_neighbors_graph([X, radius, mode, …]) 计算X中点的邻域(加权)图
score(X, y[, sample_weight]) 返回给定测试数据和标签的平均精度。
set_params(**params) 设置此估计器的参数。

例:

>>> X = [[0], [1], [2], [3]]
>>> y = [0, 0, 1, 1]
>>> from sklearn.neighbors import RadiusNeighborsClassifier
>>> neigh = RadiusNeighborsClassifier(radius=1.0)
>>> neigh.fit(X, y)
RadiusNeighborsClassifier(...)
>>> print(neigh.predict([[1.5]]))
[0]
>>> print(neigh.predict_proba([[1.0]]))
[[0.66666667 0.33333333]]

1.6.3. 最近邻回归【neighbors.KNeighborsRegressor,neighbors.RadiusNeighborsRegressor】

最近邻回归是用在数据标签为连续变量,而不是离散变量的情况下。分配给查询点的标签是由它的最近邻标签的均值计算而来的。

scikit-learn 实现了两种不同的最近邻回归:KNeighborsRegressor基于每个查询点的 k 个最近邻实现, 其中 k 是用户指定的整数值。RadiusNeighborsRegressor 基于每个查询点的固定半径 r 内的邻点数量实现, 其中 r 是用户指定的浮点数值。

基本的最近邻回归使用统一的权重:即,本地邻域内的每个邻点对查询点的分类贡献一致。 在某些环境下,对邻点加权可能是有利的,使得附近点对于回归所作出的贡献多于远处点。 这可以通过 weights 关键字来实现。默认值 eights = 'uniform'为所有点分配同等权重。 而 weights = 'distance' 分配的权重与查询点距离呈反比。 或者,用户可以自定义一个距离函数用来计算权重。
在这里插入图片描述
使用多输出的最近邻进行回归分析 Face completion with a multi-output estimators。 |

利用多输出估计器,演示了多输出最近邻回归方法在人脸补全中的应用。在这个例子中,输入 X 是脸上半部分像素,输出 Y 是脸下半部分像素。
在这里插入图片描述
sklearn.neighbors.KNeighborsRegressor

参数 解释
n_neighbors:int, default=5 默认情况下用于kneighbors查询的邻居数。
weights:{‘uniform’, ‘distance’} or callable, default=’uniform’ 用于预测的权重函数。可能值:‘uniform’ : 统一的权重。每个邻域中的所有点的权重相等。‘distance’ : 按距离的倒数加权点。在这种情况下,查询点的近邻会比距离较远的邻居影响更大。[callable]: 一个用户定义的函数,它接受一个距离数组,并返回一个包含权重的形状相同的数组。
algorithm:{‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’}, default=’auto’ 用于计算最近邻的算法:['auto', 'ball_tree', 'kd_tree', 'brute']其中的一个
leaf_size:int, default=30 传递给BallTree或KDTree的叶大小。这会影响构造和查询的速度,以及存储树所需的内存。最佳值取决于问题的性质。
p:int, default=2 当p=1时,这相当于使用曼哈顿距离(l1),欧几里德距离(l2)表示p=2。对于任意的p,使用minkowskiïu距离(lïp)。
metric:str or callable, default=’minkowski’ 要用于树的距离度量。默认的度量是minkowski,p=2等于标准的欧几里得度量。有关可用度量的列表,请参阅DistanceMetric文档。若metric是“precomputed”则假设X是距离矩阵,并且在拟合期间必须是平方的。X可能是一个稀疏图,在这种情况下,只有“nonzero” 元素可以被视为邻居。
metric_params:dict, default=None 度量函数的其他关键字参数。
n_jobs:int, default=None 线程数
属性 解释
effective_metric_:str or callble 用于计算到邻居距离的度量。
effective_metric_params_:dict 用于计算到邻居的距离的度量的参数。
n_samples_fit_:int 拟合数据中的样本数。
方法 解释
fit(X[, y]) 从训练数据集拟合k-最近邻分类器。
get_params([deep]) 获取此估计器的参数。
kneighbors([X, n_neighbors, return_distance]) 找到点的K-邻域。
kneighbors_graph([X, n_neighbors, mode]) 计算X中点的k-邻域的(加权)图
predict(X) 预测
score(X, y[, sample_weight]) 返回给定测试数据和标签的平均精度。
set_params(**params) 设置此估计器的参数。

例:

>>> X = [[0], [1], [2], [3]]
>>> y = [0, 0, 1, 1]
>>> from sklearn.neighbors import KNeighborsRegressor
>>> neigh = KNeighborsRegressor(n_neighbors=2)
>>> neigh.fit(X, y)
KNeighborsRegressor(...)
>>> print(neigh.predict([[1.5]]))
[0.5]

sklearn.neighbors.RadiusNeighborsRegressor

参数 解释
radius:float, default=1.0 默认情况下用于radius_neighbors查询的参数空间范围。
weights:{‘uniform’, ‘distance’} or callable, default=’uniform’ 用于预测的权重函数。可能值:‘uniform’ : 统一的权重。每个邻域中的所有点的权重相等。‘distance’ : 按距离的倒数加权点。在这种情况下,查询点的近邻会比距离较远的邻居影响更大。[callable]: 一个用户定义的函数,它接受一个距离数组,并返回一个包含权重的形状相同的数组。
algorithm:{‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’}, default=’auto’ 用于计算最近邻的算法:['auto', 'ball_tree', 'kd_tree', 'brute']其中的一个
leaf_size:int, default=30 传递给BallTree或KDTree的叶大小。这会影响构造和查询的速度,以及存储树所需的内存。最佳值取决于问题的性质。
p:int, default=2 当p=1时,这相当于使用曼哈顿距离(l1),欧几里德距离(l2)表示p=2。对于任意的p,使用minkowskiïu距离(lïp)。
metric:str or callable, default=’minkowski’ 要用于树的距离度量。默认的度量是minkowski,p=2等于标准的欧几里得度量。有关可用度量的列表,请参阅DistanceMetric文档。若metric是“precomputed”则假设X是距离矩阵,并且在拟合期间必须是平方的。X可能是一个稀疏图,在这种情况下,只有“nonzero” 元素可以被视为邻居。
metric_params:dict, default=None 度量函数的其他关键字参数。
n_jobs:int, default=None 线程数
属性 解释
effective_metric_:str or callble 用于计算到邻居距离的度量。
effective_metric_params_:dict 用于计算到邻居的距离的度量的参数。
n_samples_fit_:int 拟合数据中的样本数。
方法 解释
fit(X[, y]) 从训练数据集拟合k-最近邻分类器。
get_params([deep]) 获取此估计器的参数。
predict(X) 预测
radius_neighbors([X, radius, …]) 查找一个或多个点的给定半径内的邻居。
radius_neighbors_graph([X, radius, mode, …]) 计算X中点的邻域(加权)图
score(X, y[, sample_weight]) 返回给定测试数据和标签的平均精度。
set_params(**params) 设置此估计器的参数。

例:

>>> X = [[0], [1], [2], [3]]
>>> y = [0, 0, 1, 1]
>>> from sklearn.neighbors import RadiusNeighborsRegressor
>>> neigh = RadiusNeighborsRegressor(radius=1.0)
>>> neigh.fit(X, y)
RadiusNeighborsRegressor(...)
>>> print(neigh.predict([[1.5]]))
[0.5]

1.6.4. 最近邻算法

1.6.4.1. 暴力计算

最近邻的快速计算是机器学习中一个活跃的研究领域。最简单的近邻搜索的实现涉及数据集中所有成对点之间距离的暴力计算: 对于 D 维度中的 N 个样本来说, 这个方法的复杂度是 O [ D N 2 ] O[D N^2] O[DN2]。 对于小数据样本,高效的暴力近邻搜索是非常有竞争力的。 然而,随着样本数 N 的增长,暴力方法很快变得不切实际了。在 sklearn.neighbors 类中, 暴力近邻搜索通过关键字 algorithm = 'brute'来指定,并通过 sklearn.metrics.pairwise 中的例程来进行计算。

1.6.4.2. K-D 树

为了解决效率低下的暴力计算方法,已经发明了大量的基于树的数据结构。总的来说, 这些结构试图通过有效地编码样本的 aggregate distance (聚合距离) 信息来减少所需的距离计算量。 基本思想是,若 A 点距离 B 点非常远,B 点距离 C 点非常近, 可知 A 点与 C 点很遥远,不需要明确计算它们的距离。 通过这样的方式,近邻搜索的计算成本可以降低为 O [ D N log ⁡ ( N ) ] O[D N \log(N)] O[DNlog(N)] 或更低。 这是对于暴力搜索在大样本数 N 中表现的显著改善。

利用这种聚合信息的早期方法是 KD tree 数据结构( K-dimensional tree 的简写), 它将二维 Quad-trees 和三维 Oct-trees 推广到任意数量的维度. KD 树是一个二叉树结构,它沿着数据轴递归地划分参数空间,将其划分为嵌入数据点的嵌套的各向异性区域。 KD 树的构造非常快:因为只需沿数据轴执行分区, 无需计算 D-dimensional 距离。 一旦构建完成, 查询点的最近邻距离计算复杂度仅为 O [ log ⁡ ( N ) ] O[\log(N)] O[log(N)]虽然 KD 树的方法对于低维度 (D < 20) 近邻搜索非常快, 当 D 增长到很大时, 效率变低: 这就是所谓的 “维度灾难” 的一种体现。 在 scikit-learn 中, KD 树近邻搜索可以使用关键字 algorithm = 'kd_tree' 来指定, 并且使用类 KDTree 来计算。

1.6.4.3. Ball 树

为了解决 KD 树在高维上效率低下的问题, ball 树 数据结构就被研发出来了. 其中 KD 树沿笛卡尔轴(即坐标轴)分割数据, ball 树在沿着一系列的 hyper-spheres 来分割数据. 通过这种方法构建的树要比 KD 树消耗更多的时间, 但是这种数据结构对于高结构化的数据是非常有效的, 即使在高维度上也是一样.

ball 树将数据递归地划分为由质心 C 和半径 R 定义的节点,使得节点中的每个点位于由 r 和 C 定义的 hyper-sphere 内. 通过使用 triangle inequality(三角不等式) 减少近邻搜索的候选点数:
∣ x + y ∣ ≤ ∣ x ∣ + ∣ y ∣ |x+y| \leq |x| + |y| x+yx+y
通过这种设置, 测试点和质心之间的单一距离计算足以确定距节点内所有点的距离的下限和上限. 由于 ball 树节点的球形几何, 它在高维度上的性能超出 KD-tree, 尽管实际的性能高度依赖于训练数据的结构. 在 scikit-learn 中, 基于 ball 树的近邻搜索可以使用关键字 algorithm = 'ball_tree'来指定, 并且使用类 sklearn.neighbors.BallTree来计算. 或者, 用户可以直接使用 BallTree 类.

1.6.4.4. 最近邻算法的选择

对于给定数据集的最优算法是一个复杂的选择, 并且取决于多个因素:

样本数量 N (i.e. n_samples) 和维度 D (例如. nfeatures)

Brute force 查询时间以 O [ D N ] O[D N] O[DN] 增长

Ball tree 查询时间大约以 O [ D log ⁡ ( N ) ] O[D \log(N)] O[Dlog(N)] 增长

KD tree 的查询时间 的变化是很难精确描述的.对于较小的 D (小于20) 的成本大约是 O [ D log ⁡ ( N ) ] O[D\log(N)] O[Dlog(N)], 并且 KD 树更加有效.对于较大的 D 成本的增加接近 O [ D N ] O[DN] O[DN], 由于树结构引起的开销会导致查询效率比暴力还要低.

对于小数据集 (n小于30), log(N)相当于N, 暴力算法比基于树的算法更加有效. KDTree 和 BallTree 通过提供一个 leaf size 参数来解决这个问题: 这控制了查询切换到暴力计算样本数量. 使得两种算法对较小的 N的效率都能接近于暴力计算.

数据结构: 数据的 intrinsic dimensionality (本征维数) 和/或数据的 sparsity (稀疏度). 本征维数是指数据所在的流形的维数 d ≤ D d \le D dD, 在参数空间可以是线性或非线性的. 稀疏度指的是数据填充参数空间的程度(这与“稀疏”矩阵中使用的概念不同, 数据矩阵可能没有零项, 但是从这个意义上来讲,它的 structure 仍然是 “稀疏” 的)。

Brute force (暴力查询)时间不受数据结构的影响。
Ball tree 和 KD tree 的数据结构对查询时间影响很大. 一般地, 小维度的 sparser (稀疏) 数据会使查询更快. 因为 KD 树的内部表现形式是与参数轴对齐的, 对于任意的结构化数据它通常不会表现的像 ball tree 那样好.
在机器学习中往往使用的数据集是非常结构化的, 而且非常适合基于树结构的查询。

query point(查询点)所需的近邻数 k 。

  • Brute force 查询时间几乎不受 k 值的影响. > * Ball tree 和 KD tree 的查询时间会随着 k 的增加而变慢. 这是由于两个影响: 首先, k 的值越大在参数空间中搜索的部分就越大. 其次, 使用 k > 1 进行树的遍历时, 需要对内部结果进行排序.

当 k 相比 N 变大时, 在基于树的查询中修剪树枝的能力是减弱的. 在这种情况下, 暴力查询会更加有效.

query points(查询点)数. ball tree 和 KD Tree 都需要一个构建阶段. 在许多查询中分摊时,这种结构的成本可以忽略不计。 如果只执行少量的查询, 可是构建成本却占总成本的很大一部分. 如果仅需查询很少的点, 暴力方法会比基于树的方法更好.

一般地, 如果 k >= N/2, 或输入是稀疏矩阵,或’effective_metric_’ 不在 ‘kd_tree’ 或 ‘ball_tree’ 的 ‘VALID_METRICS’ 列表中,那么algorithm = 'auto’选择 ‘brute’。 如果 k < N/2 并且 ‘effective_metric_’ 在 ‘kd_tree’ 的 ‘VALID_METRICS’ 列表中,那么 algorithm = 'auto’选择 ‘kd_tree’。

如果 k < N/2 并且 ‘effective_metric_’ 在 ‘ball_tree’ 的 ‘VALID_METRICS’ 列表中,那么 algorithm = 'auto’选择 ‘ball_tree’。 这种选择基于以下假设: 查询点的数量与训练点的数量至少在相同的数量级, 并且 leaf_size 接近其默认值 30.

1.6.4.5. leaf_size 的影响

如上所述, 对于小样本暴力搜索是比基于树的搜索更有效的方法. 这一事实在 ball 树和 KD 树中被解释为在叶节点内部切换到暴力搜索. 该开关的级别可以使用参数 leaf_size 来指定. 这个参数选择有很多的效果:

构造时间: 更大的leaf_size 会导致更快的树构建时间, 因为需要创建更少的节点.

查询时间:一个大或小的leaf_size 可能会导致次优查询成本. 当 leaf_size 接近 1 时, 遍历节点所涉及的开销大大减慢了查询时间. 当 leaf_size, 接近训练集的大小,查询变得本质上是暴力的. 这些之间的一个很好的妥协是 leaf_size = 30, 这是该参数的默认值.

内存:随着leaf_size的增加,存储树结构所需的内存减少。 对于存储每个节点的D维质心的ball tree,这点至关重要。 针对 BallTree 所需的存储空间近似于 1 / leaf_size 乘以训练集的大小.

leaf_size 不被 brute force queries(暴力查询)所引用.

1.6.5. 最近质心分类【neighbors.NearestCentroid】

NearestCentroid 分类器是一个简单的算法, 通过其成员的质心来表示每个类。 实际上, 这使得它类似于 sklearn.KMeans 算法的标签更新阶段. 它也没有参数选择, 使其成为良好的基准分类器. 然而,它确实受到非凸类的影响,即当类有显著不同的方差时。所以这个分类器假设所有维度的方差都是相等的。 对于没有做出这个假设的更复杂的方法, 请参阅线性判别分析 (sklearn.discriminant_analysis.LinearDiscriminantAnalysis) 和二次判别分析 (sklearn.discriminant_analysis.QuadraticDiscriminantAnalysis). 默认的 NearestCentroid 用法示例如下:

>>> from sklearn.neighbors.nearest_centroid import NearestCentroid
>>> import numpy as np
>>> X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
>>> y = np.array([1, 1, 1, 2, 2, 2])
>>> clf = NearestCentroid()
>>> clf.fit(X, y)
NearestCentroid(metric='euclidean', shrink_threshold=None)
>>> print(clf.predict([[-0.8, -1]]))
[1]

sklearn.neighbors.NearestCentroid

参数 解释
metric:str or callable 计算要素阵列中实例之间的距离时使用的度量。如果metric是字符串或可调用的,则它必须是metrics.pairwise.pairwise_distances度量参数。对应于每个类的样本的质心是属于该特定类的所有样本的距离之和(根据度量)最小化的点。如果提供了“manhattan” 度量,则该质心是中值,对于所有其他度量,质心现在设置为平均值。
shrink_threshold:float, default=None 缩小质心以删除特征的阈值。
属性 解释
centroids_:array-like of shape (n_classes, n_features) 每个类的质心。
classes_:array of shape (n_classes,) 唯一类标签。
方法 解释
fit(X[, y]) 从训练数据集拟合k-最近邻分类器。
get_params([deep]) 获取此估计器的参数。
predict(X) 预测
score(X, y[, sample_weight]) 返回给定测试数据和标签的平均精度。
set_params(**params) 设置此估计器的参数。

1.6.5.1. 最近缩小质心

NearestCentroid 分类器有一个 shrink_threshold 参数, 它实现了 nearest shrunken centroid 分类器. 实际上, 每个质心的每个特征的值除以该特征的类中的方差. 然后通过 shrink_threshold 来减小特征值. 最值得注意的是, 如果特定特征值过0, 则将其设置为0. 实际上,这个方法移除了影响分类器的特征。 这很有用, 例如, 去除噪声特征.

在以下例子中, 使用一个较小的 shrink 阀值将模型的准确度从 0.81 提高到 0.82.

在这里插入图片描述
在这里插入图片描述

1.6.6 邻域成分分析【neighbors.NeighborhoodComponentsAnalysis】

邻域成分分析(NCA, NeighborhoodComponentsAnalysis)是一种距离度量学习算法,其目的是提高最近邻分类相对于标准欧氏距离的准确性。该算法直接最大化训练集上k近邻(KNN)得分的随机变量,还可以拟合数据的低维线性投影,用于数据可视化和快速分类
在这里插入图片描述
在这里插入图片描述
在上图中,我们考虑随机生成的数据集中的一些点。重点研究了3号样本点的随机KNN分类问题.样本3和另一个点之间的链路厚度与它们之间的距离成正比,可以看作是随机最近邻预测规则分配给该点的相对权重(或概率)。在原始空间中,样本3有许多来自不同类的随机邻居,因此正确的分类不太可能。然而,在NCA学习的投影空间中,唯一权重不可忽略的随机邻域与样本3属于同一类,保证了样本3的分类良好。

sklearn.neighbors.NeighborhoodComponentsAnalysis

参数 解释
n_components:int, default=None 投影空间的首选维度。如果None,它将被设置为n_features
init:{‘auto’, ‘pca’, ‘lda’, ‘identity’, ‘random’} or ndarray of shape (n_features_a, n_features_b), default=’auto’ 线性变换的初始化。可能的选项有“auto”、“pca”、“lda”、“identity”、“random”和形状(n_features_a, n_features_b)的numpy数组。
warm_start:bool, default=False 如果之前调用过True和fit,则使用上一次调用fit的解作为初始线性变换(忽略n_components和init)。
max_iter:int, default=50 优化中的最大迭代次数。
tol:float, default=1e-5 优化的收敛容限。
callback:callable, default=None 如果不是None,则在优化器每次迭代后调用此函数,并将当前解决方案(展平变换矩阵)和迭代次数作为参数。如果要检查或存储每次迭代后发现的转换,这可能很有用。
verbose:int, default=0 如果为0,则不会打印进度消息。如果为1,则进度消息将打印到标准输出。如果>1,将打印进度消息,并scipy.optimize.minimize将设置为verbose - 2
random_state:int or numpy.RandomState, default=None 一个伪随机数生成器对象或它的种子(如果int)
属性 解释
components_:ndarray of shape (n_components, n_features) 拟合过程中学习的线性变换。
n_iter_:int 统计优化器执行的迭代次数。
random_state_:numpy.RandomState 初始化期间使用的伪随机数生成器对象。
方法 解释
fit(X[, y]) 拟合数据
fit_transform(X[, y]) 拟合并转换数据
get_params([deep]) 获取此估计器的参数。
set_params(**params) 设置此估计器的参数。
transform(X) 将学习的转换应用于给定数据。

1.6.6.1. 分类

与最近邻分类器(KNeighborsClassifier)相结合,NCA是一种有效的分类算法,因为它可以自然地处理多类问题,而不需要增加模型的大小,并且不引入需要用户进行微调的额外参数。

NCA分类在不同规模和难度的数据集的实际应用中显示出良好的效果。与线性判别分析等相关方法相比,NCA没有对类的分布做任何假设。而最近邻分类自然会产生高度不规则的决策边界。

要使用这个模型进行分类,需要将一个NeighborhoodComponentsAnalysis实例与一个KNeighborsClassifier实例结合起来,NeighborhoodComponentsAnalysis实例拟合最优转换,KNeighborsClassifier实例在投影空间中执行分类。下面是一个使用这两个类的例子:

>>> from sklearn.neighbors import (NeighborhoodComponentsAnalysis,
... KNeighborsClassifier)
>>> from sklearn.datasets import load_iris
>>> from sklearn.model_selection import train_test_split
>>> from sklearn.pipeline import Pipeline
>>> X, y = load_iris(return_X_y=True)
>>> X_train, X_test, y_train, y_test = train_test_split(X, y,
... stratify=y, test_size=0.7, random_state=42)
>>> nca = NeighborhoodComponentsAnalysis(random_state=42)
>>> knn = KNeighborsClassifier(n_neighbors=3)
>>> nca_pipe = Pipeline([('nca', nca), ('knn', knn)])
>>> nca_pipe.fit(X_train, y_train)
Pipeline(...)
>>> print(nca_pipe.score(X_test, y_test))
0.96190476...

在这里插入图片描述
在这里插入图片描述

1.6.6.2. 降维

NCA可用于进行监督降维。输入数据被投影到一个由最小化NCA目标的方向组成的线性子空间上。可以使用参数n_components设置所需的维数。例如,下图显示了主成分分析的降维(sklearn.decomposition.PCA),线性判别分析(klearn.discriminant_analysis.LinearDiscriminantAnalysis)和邻域成分分析(NeighborhoodComponentsAnalysis)在一个64个特征,1797个样本的数字数据集的降维结果。数据集被划分为大小相同的训练集和测试集,然后进行标准化。为了评价该方法的分类精度,对每种方法找到的二维投影点进行了3-最近邻分类精度的计算。每个数据样本属于10个类中的一个。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.6.6.3 数学公式

NCA的目标是拟合一个尺寸为(n_components, n_features)的最优线性变换矩阵,使所有被正确分类的概率样本的和最大,即:
arg ⁡ max ⁡ L ∑ i = 0 N − 1 p i \underset{L}{\arg\max} \sum\limits_{i=0}^{N - 1} p_{i} Largmaxi=0N1pi
其中N是样本数, p i pi pi是第i个样本在学习的嵌入空间中,根据随机近邻规则正确分类的可能性:
p i = ∑ j ∈ C i p i j p_{i}=\sum\limits_{j \in C_i}{p_{i j}} pi=jCipij
其中 C i Ci i是第i个样本被分类到的点集, P i j Pij ij为嵌入空间中欧氏距离上的归一化指数(softmax)值:
p i j = exp ⁡ ( − ∣ ∣ L x i − L x j ∣ ∣ 2 ) ∑ k ≠ i exp ⁡ − ( ∣ ∣ L x i − L x k ∣ ∣ 2 ) , p i i = 0 p_{i j} = \frac{\exp(-||L x_i - L x_j||^2)}{\sum\limits_{k \ne i} {\exp{-(||L x_i - L x_k||^2)}}} , \quad p_{i i} = 0 pij=k=iexp(LxiLxk2)exp(LxiLxj2),pii=0

1.6.6.3.1 Mahalanobis距离

NCA可以看作是拟合一个(平方)Mahalanobis距离矩阵:

其中 ∣ ∣ L ( x i − x j ) ∣ ∣ 2 = ( x i − x j ) T M ( x i − x j ) || L(x_i - x_j)||^2 = (x_i - x_j)^TM(x_i - x_j) L(xixj)2=(xixj)TM(xixj),

M = L T L M = L^T L M=LTL是大小为(特征数,特征数)对称正半定矩阵.

1.6.6.4 实现

对于优化方法,目前使用的是scipyL-BFGS-B,每次迭代都进行全梯度计算,以避免调整学习速度,提供稳定的拟合。

1.6.6.5 复杂度

1.6.6.5.1 训练

NCA存储一对距离矩阵,占用了(n_samples ** 2)的内存。时间复杂度取决于优化算法的迭代次数。但是,可以使用参数max_iter设置迭代的最大次数。对于每个迭代,时间复杂度为O(n_components x n_samples x min(n_samples, n_features))

1.6.6.5.2 变形

这里变形操作返回值为LXT,因此它的时间复杂度等于n_components x n_features x n_samples_test。操作中没有增加空间复杂度。

猜你喜欢

转载自blog.csdn.net/qq_42946328/article/details/111868627