假设我们有一个用于分类任务的预训练 Gluon 模型:
>>> import mxnet as mx
>>> net = mx.gluon.nn.HybridSequential()
>>> net.add(mx.gluon.nn.Conv2D(channels=6, kernel_size=5, padding=2, activation='sigmoid'))
>>> net.add(mx.gluon.nn.MaxPool2D(pool_size=2, strides=2))
>>> net.add(mx.gluon.nn.Flatten())
>>> net.add(mx.gluon.nn.Dense(units=10))
>>> net.collect_params()
hybridsequential0_ (
Parameter conv0_weight (shape=(6, 0, 5, 5), dtype=<class 'numpy.float32'>)
Parameter conv0_bias (shape=(6,), dtype=<class 'numpy.float32'>)
Parameter dense0_weight (shape=(1, 0), dtype=float32)
Parameter dense0_bias (shape=(1,), dtype=float32)
)
为了微调这个卷积网络,我们想要冻结除 之外的所有块Dense
。
首先,回想一下该collect_params
方法接受一个正则表达式字符串,以通过名称(或前缀; 、 或任何其他 Gluon(混合)块的参数)选择特定的块参数prefix
。默认情况下,前缀是类名,即如果一个块是,那么前缀是或等等。此外,返回一个实例,它有方法。Conv2D
Dense
Conv2D
conv0_
conv1_
collect_params
mxnet.gluon.parameter.ParameterDict
setattr
解决方案:
>>> conv_params = net.collect_params('(?!dense).*')
>>> conv_params.setattr('grad_req', 'null')
或者干脆
>>> net.collect_params('(?!dense).*').setattr('grad_req', 'null')
在这里,我们排除所有匹配的参数dense
以仅获取conv
块并将其grad_req
属性设置为'null'
. 现在,训练模型net
将mxnet.gluon.Trainer
只更新dense
参数。
拥有一个带有表示特定块的单独属性的预训练模型更方便,例如特征块、锚生成器等。在我们的例子中,我们有一个卷积网络,它提取特征并将它们传递给输出块。
class ConvNet(mx.gluon.nn.HybridSequential):
def __init__(self, n_classes, params=None, prefix=None):
super().__init__(params=params, prefix=prefix)
self.features = mx.gluon.nn.HybridSequential()
self.features.add(mx.gluon.nn.Conv2D(channels=6, kernel_size=5, padding=2,
activation='sigmoid'))
self.add(mx.gluon.nn.MaxPool2D(pool_size=2, strides=2))
self.add(mx.gluon.nn.Flatten())
self.output = mx.gluon.nn.Dense(units=n_classes)
def hybrid_forward(self, F, x):
x = self.features(x)
return self.output(x)
通过这个 convnet 声明,我们不必使用正则表达式来访问所需的块:
>>> net = ConvNet(n_classes=10)
>>> net.features.collect_params().setattr('grad_req', 'null')
Gluon CV 模型完全遵循这种模式。请参阅所需模型的文档并选择要冻结的块。如果文档为空,则运行
collect_params
以查看所有参数并使用正则表达式过滤掉要微调的参数并将返回的参数设置grad_req
为'null'
.