我正在尝试将 PyTorch 功能合并到
scikit-learn
环境中(特别是 Pipelines 和 GridSearchCV),因此一直在研究
skorch
神经网络的标准文档示例看起来像
import torch.nn.functional as F
from torch import nn
from skorch import NeuralNetClassifier
class MyModule(nn.Module):
def __init__(self, num_units=10, nonlin=F.relu):
super(MyModule, self).__init__()
self.dense0 = nn.Linear(20, num_units)
self.nonlin = nonlin
self.dropout = nn.Dropout(0.5)
...
...
self.output = nn.Linear(10, 2)
...
...
,您通过将输入和输出维度硬编码到构造函数中来显式传递它们。然而,这并不是
scikit-learn
接口真正的工作方式,其中输入和输出维度是通过
fit
方法导出的,而不是显式传递给构造函数。作为一个实际的例子,除了变压器中任何地方都必须指定输入和输出维度这一事实之外,在模型之前
# copied from the documentation
net = NeuralNetClassifier(
MyModule,
max_epochs=10,
lr=0.1,
# Shuffle training data on each epoch
iterator_train__shuffle=True,
)
# any general Pipeline interface
pipeline = Pipeline([
('transformation', AnyTransformer()),
('net', net)
])
gs = GridSearchCV(net, params, refit=False, cv=3, scoring='accuracy')
gs.fit(X, y)
应用的变压器可能会改变训练集的维度(想想降维和类似),因此在神经网络构造函数中硬编码输入和输出是行不通的。 我是否误解了它应该如何工作或者建议的解决方案是什么(我正在考虑将构造函数指定为 您确实有
可用于拟合的方法,但我不确定这是一个好的做法)?
forward
method where you do have
X
available for fit already, but I am not sure this is good practice)?
你对如何在 scikit-learn 环境中使用
skorch
的理解是正确的。你不应该将输入维度硬编码到你的 PyTorch 模块中,因为这会阻止你在管道中使用它,在管道中,维度可能在拟合时之前是未知的。
你有几个选择可以将输入维度传递给你的
skorch
包装器:
1. 使用
input_dim
参数(推荐):
skorch
的
NeuralNetClassifier
和
NeuralNetRegressor
有一个
module__
前缀,允许你将参数传递给底层 PyTorch 模块。你可以使用它在实例化你的模型时传递输入维度:
net = NeuralNetClassifier(
MyModule,
module__input_dim=20, # 将输入维度传递给 MyModule
max_epochs=10,
lr=0.1,
iterator_train__shuffle=True,
)
在你的
MyModule
中,你可以像这样访问
input_dim
:
class MyModule(nn.Module):
def __init__(self, input_dim, num_units=10, nonlin=F.relu):
super(MyModule, self).__init__()
self.dense0 = nn.Linear(input_dim, num_units)
# ... 其他层 ...
此方法使你的代码更具可读性和灵活性,因为它使输入维度成为
NeuralNetClassifier
的显式参数。
2. 在
fit
方法中设置输入维度:
你可以在
MyModule
的
fit
方法中访问输入数据的形状,并使用该信息来初始化你的第一层。
class MyModule(nn.Module):
def __init__(self, num_units=10, nonlin=F.relu):
super(MyModule, self).__init__()
self.num_units = num_units
self.nonlin = nonlin
# ... 其他层 ...
self.first_layer = None
def forward(self, X):
if self.first_layer is None:
self.first_layer = nn.Linear(X.shape[1], self.num_units)
# ... 其他层 ...
这种方法不是很干净,因为它使你的
forward
方法更复杂,并且偏离了 PyTorch 模块的典型初始化模式。
3. 使用灵活的层,例如
nn.Flatten
:
如果你使用
nn.Flatten
作为你的第一个层,它可以处理任何输入维度,你就不需要显式地传递输入维度。
class MyModule(nn.Module):
def __init__(self, num_units=10, nonlin=F.relu):
super(MyModule, self).__init__()
self.flatten = nn.Flatten()
self.dense0 = nn.Linear(num_units, num_units)
# ... 其他层 ...
这种方法很简单,但如果你需要在第一层之前对输入维度执行特定操作,则可能不适合。
总的来说,建议使用
input_dim
参数,因为它可以提供清晰度和灵活性,而不会使你的代码过于复杂。
这使你可以在
skorch
包装器中利用
scikit-learn
管道的优势,而无需硬编码输入维度。