在 fit 方法采用 3 个参数的自定义类上使用 sklearn GridSearchCV
Posted
技术标签:
【中文标题】在 fit 方法采用 3 个参数的自定义类上使用 sklearn GridSearchCV【英文标题】:Use sklearn GridSearchCV on custom class whose fit method takes 3 arguments 【发布时间】:2018-02-08 12:39:18 【问题描述】:我正在开展一个项目,该项目涉及将一些算法实现为 python 类并测试它们的性能。我决定将它们写成 sklearn 估计器,以便我可以使用 GridSearchCV
进行验证。
但是,我的 Inductive Matrix Completion 算法之一不仅仅是将 X
和 y
作为参数。这成为GridSearchCV.fit
的一个问题,因为似乎没有办法将更多的X
和y
传递给估计器的拟合方法。源代码显示GridSearchCV.fit
的以下参数:
def fit(self, X, y=None, groups=None, **fit_params):
当然,下游方法只需要这两个参数。显然,修改我的 GridSearchCV
的本地副本以满足我的需要并非易事(或可取的)。
作为参考 IMC 基本上声明 $ R \approx XW^THY^T $。所以我的 fit 方法采用以下形式:
def fit(self, R, X, Y):
因此尝试以下操作失败,因为 Y 值永远不会传递给 IMC.fit
方法:
imc = IMC()
params = ...
gs = GridSearchCV(imc, param_grid=params)
gs.fit(R, X, Y)
我通过像这样修改IMC.fit
方法为此创建了一个解决方法(这也必须插入到score
方法中):
def fit(self, R, X, Y=None):
if Y is None:
split = np.where(np.all(X == 999, axis=0))[0][0]
Y = X[:, split + 1:]
X = X[:, :split]
...
这允许我使用numpy.hstack
水平堆叠X 和Y,并在它们之间插入一列所有999
。然后可以将该数组传递给GridSearchCV.fit
,如下所示:
data = np.hstack([X, np.ones((X.shape[0],1)) * 999, Y])
gs.fit(R, data)
这种方法有效,但感觉很hacky。因此我的问题是这样的:
使用GridSearchCV
将超过 2 个参数传递给 fit 方法是否有普遍接受的方法或最佳实践?
【问题讨论】:
你自己上面提到的**fit_params有什么问题?见:scikit-learn.org/stable/modules/generated/… @MarcusV。我相信我误解了你昨天的评论。我返回并使用**fit_params
进行了测试,但最终遇到了记分员的问题。显然fit_params
不会级联到评分方法,并且由于我在课堂上使用默认的score
方法,因此不会传入其他矩阵。请参阅sklearn.model_selection._validation._score
【参考方案1】:
因此,在从朋友 (@Matthew Drury) 那里得到一些启发后,我构建了一个更优雅的解决方案。
问题又是这样的:
我有一个矩阵补全方法,它将X
、Y
和R
作为参数,并尝试构造W
和H
,以最小化R - XWHY
中所有观察到的R
索引。 fit
方法的基本实现如下所示:
def fit(X, Y, R):
W, H = do_minimization(X, Y, R)
return W, H
这不太适合标准 sklearn 模型,其中 fit 采用 X
(模型中的特征)和 y
(结果),如下所示:
def fit(X, y):
W, H = do_minimization(X, y)
return W, H
在您开始使用GridSearchCV
或其他交叉验证方法之前,这并不是真正的问题,因为他们希望数据适合后一种格式。因此,为了结合这两个概念,我需要一种将两个不同的矩阵 X
和 Y
打包成一个结构而不失去两者分离性质的方法。
在 5 分钟内,我最初必须致力于此,我想出了一个 hacky 解决方案。在矩阵R
形状n, m
中,行对应X
中的记录,列对应Y
中的记录,共有b
条目。如果我们为所有这些条目获取行和列索引,并在行上索引X
,在列上索引Y
,我们将得到X
和Y
的等长矩阵。然后可以将它们水平堆叠,由一列废话分隔,并毫无问题地传递给交叉验证方法(我们只需要在原始类中使用几个帮助方法来从之前的堆栈中重建原始的X
和 Y
合身。
这个问题的重点是找到优雅的解决方案,或者最好是现有的解决方案。情况似乎并非如此,因此我将为任何未来从 sklearn 继承而构建的估计器/分类器提出以下模型,这些估计器/分类器需要的不仅仅是一个单一的特征矩阵用于 fit 方法。
创建数据处理程序
当使用GridSearchCV
时,fit
方法会在触发对估计器fit
方法的任何调用之前进行一轮检查。其中之一确定传递的X
数组是否为indexable。该测试主要检查X
是否实现__getitem__
或iloc
并且与y
的长度相同。此长度检查要求X
具有shape
属性。此时可以按预期计算拆分索引和拟合。所以我们需要一个实现__getitem__
并具有shape
属性的包装器。
class DataHandler(object):
def __init(self, X, Y):
self.X = X
self.Y = Y
self.shape = self.X.shape
def __getitem__(self, x):
return self.X[x], self.Y[x]
就是这样!我们现在可以修改 fit
方法以匹配 sklearn 样式,但在这种情况下,X
不是一个数组,而是一个元组(__getitem__
方法返回的结果)或我们的实例DataHandler
班级。
现在GridSearchCV
将按预期工作,只需传递包含X
和Y
数组的DataHandler
实例。
【讨论】:
感谢您提供此信息。鉴于这篇文章已经有几年了,这仍然是你处理这个问题的方式吗?我对IMC.fit
签名的样子有点困惑。是def fit(self, X, y)
,但X
将成为tuple
或DataHandler
实例?你有一个回购来说明这一点吗?很抱歉造成混乱。以上是关于在 fit 方法采用 3 个参数的自定义类上使用 sklearn GridSearchCV的主要内容,如果未能解决你的问题,请参考以下文章
TypeError: fit_transform() 接受 2 个位置参数,但给出了 3 个
用于 PC Mockito 模拟验证的自定义 Hamcrest 匹配器