Scikit learn scikit学习中的多目标岭回归是如何工作的?

Scikit learn scikit学习中的多目标岭回归是如何工作的?,scikit-learn,linear-regression,grid-search,regularized,multitargeting,Scikit Learn,Linear Regression,Grid Search,Regularized,Multitargeting,我正在努力理解以下内容: Scikit learn为岭回归提供了一个多输出版本,只需交付一个2D数组[n_样本,n_目标],但它是如何实现的 假设每个目标的每个回归都是独立的是否正确?在这些情况下,我如何调整它,以便为每个回归使用单独的alpha正则化参数?如果我使用GridSeachCV,我必须交出一个可能的正则化参数矩阵,或者这是如何工作的 提前感谢-我已经搜索了几个小时,但找不到关于这个主题的任何内容。我将尝试一下,因为我一直在为自己的工作研究这个问题。我会把问题分解成几个部分,这样你就

我正在努力理解以下内容:

Scikit learn为岭回归提供了一个多输出版本,只需交付一个2D数组[n_样本,n_目标],但它是如何实现的

假设每个目标的每个回归都是独立的是否正确?在这些情况下,我如何调整它,以便为每个回归使用单独的alpha正则化参数?如果我使用GridSeachCV,我必须交出一个可能的正则化参数矩阵,或者这是如何工作的


提前感谢-我已经搜索了几个小时,但找不到关于这个主题的任何内容。

我将尝试一下,因为我一直在为自己的工作研究这个问题。我会把问题分解成几个部分,这样你就只能看你感兴趣的部分了:

问题1: 多重输出岭回归中每个目标(aka输出)的回归是否独立

A1: 我认为典型的M个输出的多输出线性回归与M个独立的单输出线性回归是一样的。我认为是这样的,因为多重输出情况下的普通最小二乘表达式与M个独立的单输出情况下的(和)表达式相同。为了激励这一点,让我们考虑一个愚蠢的,二元输出的情况,没有正则化。< /P>

考虑两个列向量输入<强> x 1,<强> x 2,相应的权重向量<强> W 1,<强> W < /强> 2。

这给了我们两个单变量输出,y1=x1w1T+e1和y2=x2w2T+e2,其中es是独立误差

误差平方之和表示为:

e12+e22=(y1-x1w1T)2+(y2-x2w2T)2

我们可以看到,这只是两个独立回归的平方误差之和。现在,为了优化,我们根据权重进行区分并设置为零。由于y1不依赖于w2,y2和w1也不依赖于y1,因此可以针对每个目标单独进行优化

我在这里考虑了一个示例作为说明,但更多示例没有太大变化。你可以自己写出来。以|w1 |或|w2 |的形式添加惩罚项也不会改变这一点,因为对于y1,w2仍然没有依赖性,对于y2和w1也没有依赖性

好的,这样的证明会给你一个C-(有一位慷慨的教授)。 鉴于这反映了sklearn,手动实现独立回归和内置的多重输出支持将给出相同的结果。因此,让我们用一些代码来检查一下(我将py2.7与Jupyter一起使用):

我们需要的东西

 import numpy as np
 import matplotlib.pyplot as plt
 from sklearn import linear_model, model_selection
设置数据

## set up some test data
# T samples, K features, M outputs (aka targets)
T = 1000
K = 100
M = 500

#get the samples from independent, multivariate normal
means_X = np.zeros(K)
cov_X = np.identity(K) 
X = np.random.multivariate_normal(means_X,cov_X,T)

#Make up some random weights. 
#Here I use an exponential form since that means some would be quite small, and thus regularization is likely to help
#However for the purposes of the example it doesn't really matter

#exponential weights
W = 2.0 ** np.random.randint(-10,0,M * K) 

#shape into a weight matrix correctly
W = W.reshape((K,M))

# get the ouput - apply linear transformation
Y = np.matmul(X, W)

# add a bit of noise to the output
noise_level = 0.1
noise_means = np.zeros(M)
noise_cov = np.identity(M) 

Y_nse = Y + noise_level * np.random.multivariate_normal(noise_means,noise_cov,T)

# Start with one alpha value for all targets 
alpha = 1
使用sklearn内置的多输出支持

#%%timeit -n 1 -r 1
# you can uncomment the above to get timming but note that this runs on a seperate session so 
# the results won't be available here 
## use built in MIMO support 

built_in_MIMO = linear_model.Ridge(alpha = alpha)
built_in_MIMO.fit(X, Y_nse)
独立运行输出的优化

# %%timeit -n 1 -r 1 -o
## manual mimo
manual_MIMO_coefs = np.zeros((K,M))

for output_index in range(M):
    
    manual_MIMO = linear_model.Ridge(alpha = alpha)
    manual_MIMO.fit(X, Y_nse[:,output_index]) 
    manual_MIMO_coefs[:,output_index] = manual_MIMO.coef_
比较估算值(不包括图)

输出为(此处不包括绘图)

因此,我们看到内置的sklearn估算与我们的手动估算相同。然而,内置的方法要快得多,因为它使用矩阵代数一次性解决整个问题,而不是我在这里使用的循环(有关岭正则化的矩阵公式,请参阅关于Tikhonov正则化的wiki)。您可以通过取消对上面%%timeit magics)的注释来自己检查这一点)

问题2: 我们如何为每个回归使用单独的alpha正则化参数

A2: sklearn Ridge为每个输出(目标)接受不同的正则化。例如,继续上面的代码,为每个输出使用不同的字母:

# now try different alphas for each target.
# Simply randomly assign them between min and max range 
min_alpha = 0
max_alpha = 50
alphas = 2.0 ** np.random.randint(min_alpha,max_alpha,M)
built_in_MIMO = linear_model.Ridge(alpha = alphas)
built_in_MIMO.fit(X, Y_nse) 
如果将其与手动实现的M个独立回归进行比较,每个回归都有自己的alpha:

manual_MIMO_coefs = np.zeros((K,M))

for output_index in range(M):
    
    manual_MIMO = linear_model.Ridge(alpha = alphas[output_index])
    manual_MIMO.fit(X, Y_nse[:,output_index]) 
    manual_MIMO_coefs[:,output_index] = manual_MIMO.coef_
你会得到同样的结果:

manual_MIMO_coefs_T = manual_MIMO_coefs.T

## check the weights. 
print('Average diff between manual and built in weights is %f ' % ((built_in_MIMO.coef_.flatten()-manual_MIMO_coefs_T.flatten()) ** 2).mean())

Average diff between manual and built in weights is 0.000000 
所以这些都是一样的

然而在这种情况下,性能在很大程度上取决于解算器(正如@Vivek Kumar的直觉所示)

默认情况下,Ridge.fit()使用Cholesky分解(至少对于非稀疏数据),在github上查找该分解的代码(_solve_Cholesky in),我发现当为每个目标分别选择Alpha时,sklearn实际上会分别拟合它们。我不知道这是Cholesky固有的还是只是一个实现的东西(我感觉是后者)

但是,对于不同的解算器,例如基于SVD的(_solve_SVD()),代码似乎已经将不同的字母组合到问题的矩阵公式中,因此整个过程只运行一次。这意味着,当为每个输出分别选择Alpha时,并且当有多个输出时,svd解算器可以快得多

问题3:如何使用GridSeachCV?我是否要交出一个可能的正则化参数矩阵

A3: 我没有使用内置的网格搜索,因为它不太适合我的问题。然而,如上所述,实现这一点很简单;只需使用sklearn.model_selection.KFold()或类似工具获取一些CV折叠,然后使用不同的字母对每个折叠进行训练:

from sklearn import metrics, model_selection
# just two folds for now
n_splits = 2
#logarithmic grid
alphas = 2.0 ** np.arange(0,10) 
kf = model_selection.KFold(n_splits=n_splits)

# generates some folds
kf.get_n_splits(X)

# we will keep track of the performance of each alpha here
scores = np.zeros((n_splits,alphas.shape[0],M))

#loop over alphas and folds
for j,(train_index, test_index) in enumerate(kf.split(X)):
    
    for i,alpha in enumerate(alphas):
        
        cv_MIMO = linear_model.Ridge(alpha = alpha)
        cv_MIMO.fit(X[train_index,:], Y_nse[train_index,:]) 
        cv_preds = cv_MIMO.predict(X[test_index,:])
        scores[j,i,:] = metrics.r2_score(Y_nse[test_index,:], cv_preds, multioutput='raw_values')

## mean CV score  
mean_CV_score = scores.mean(axis = 0)
# best alpha for each target
best_alpha_for_target = alphas[np.argmax(mean_CV_score,axis = 0)]
我写这封信相当匆忙,所以仔细检查一下。请注意,我们需要使用公制模块,因为内置的Ridge.score()平均了所有目标,这在这里是不需要的。通过使用multioutput='raw_values'选项,我们为每个目标保留原始值


希望有帮助

从文档中可以看出,它具有内置支持。因此,它们可能不是独立的(可能对某些解算器是独立的,但并非对所有解算器都是独立的)。你应该在github的scikit learn邮件列表上问这个问题。谢谢,我已经订阅了这个列表并通过电子邮件发送给他们。如果有人知道发生了什么,我们将非常感谢任何额外的帮助!
manual_MIMO_coefs_T = manual_MIMO_coefs.T

## check the weights. 
print('Average diff between manual and built in weights is %f ' % ((built_in_MIMO.coef_.flatten()-manual_MIMO_coefs_T.flatten()) ** 2).mean())

Average diff between manual and built in weights is 0.000000 
from sklearn import metrics, model_selection
# just two folds for now
n_splits = 2
#logarithmic grid
alphas = 2.0 ** np.arange(0,10) 
kf = model_selection.KFold(n_splits=n_splits)

# generates some folds
kf.get_n_splits(X)

# we will keep track of the performance of each alpha here
scores = np.zeros((n_splits,alphas.shape[0],M))

#loop over alphas and folds
for j,(train_index, test_index) in enumerate(kf.split(X)):
    
    for i,alpha in enumerate(alphas):
        
        cv_MIMO = linear_model.Ridge(alpha = alpha)
        cv_MIMO.fit(X[train_index,:], Y_nse[train_index,:]) 
        cv_preds = cv_MIMO.predict(X[test_index,:])
        scores[j,i,:] = metrics.r2_score(Y_nse[test_index,:], cv_preds, multioutput='raw_values')

## mean CV score  
mean_CV_score = scores.mean(axis = 0)
# best alpha for each target
best_alpha_for_target = alphas[np.argmax(mean_CV_score,axis = 0)]