我在保存包裹在TFBertModel
中的Keras
的权重方面遇到问题。问题描述为here in GitHub issue和here in Stack Overflow。两种情况下建议的解决方案是使用
config = BertConfig.from_pretrained(transformer_model_name)
bert = TFBertMainLayer(config=config,trainable=False)
代替
bert = TFBertModel.from_pretrained(transformer_model_name, trainable=False)
问题在于,当我将模型更改为以前的代码时,准确性降低了10%,尽管两种情况下的参数计数都相同。我想知道是什么原因,如何预防?
答案 0 :(得分:2)
由于未加载预训练的权重,因此似乎直接实例化了MainLayer
的代码片段中的性能下降。您可以通过以下任一方式加载权重:
TFBertModel.from_pretrained
并从已加载的MainLayer
抓取TFBertModel
MainLayer
,然后以与from_pretrained
类似的方式加载权重调用TFBertModel.from_pretrained
时,它使用函数TFPreTrainedModel.from_pretrained
(通过继承)处理一些事情,包括下载,缓存和加载模型权重。
class TFPreTrainedModel(tf.keras.Model, TFModelUtilsMixin, TFGenerationMixin):
...
@classmethod
def from_pretrained(cls, pretrained_model_name_or_path, *model_args, **kwargs):
...
# Load model
if pretrained_model_name_or_path is not None:
if os.path.isfile(os.path.join(pretrained_model_name_or_path, TF2_WEIGHTS_NAME)):
# Load from a TF 2.0 checkpoint
archive_file = os.path.join(pretrained_model_name_or_path, TF2_WEIGHTS_NAME)
...
resolved_archive_file = cached_path(
archive_file,
cache_dir=cache_dir,
force_download=force_download,
proxies=proxies,
resume_download=resume_download,
local_files_only=local_files_only,
)
...
model.load_weights(resolved_archive_file, by_name=True)
(如果您阅读了实际的代码,上面...
的内容已经很多了。)
但是,当您直接实例化TFBertMainLayer
时,它不会执行任何设置工作。
@keras_serializable
class TFBertMainLayer(tf.keras.layers.Layer):
config_class = BertConfig
def __init__(self, config, **kwargs):
super().__init__(**kwargs)
self.num_hidden_layers = config.num_hidden_layers
self.initializer_range = config.initializer_range
self.output_attentions = config.output_attentions
self.output_hidden_states = config.output_hidden_states
self.return_dict = config.use_return_dict
self.embeddings = TFBertEmbeddings(config, name="embeddings")
self.encoder = TFBertEncoder(config, name="encoder")
self.pooler = TFBertPooler(config, name="pooler")
... rest of the class
基本上,您需要确保已加载这些砝码。
您可以依靠Transformers.TFAutoModel.from_pretrained加载模型,然后仅从MainLayer
的特定子类中获取TFPreTrainedModel
字段。例如,如果您要访问distilbert主层,则它看起来像:
model = transformers.TFAutoModel.from_pretrained(`distilbert-base-uncased`)
assert isinstance(model, TFDistilBertModel)
main_layer = transformer_model.distilbert
您可以在modeling_tf_distilbert.html
中看到MainLayer
是模型的一个字段。
这是更少的代码和更少的重复,但是有一些缺点。更改要使用的预训练模型要容易一些,因为现在您依赖于字段名称,如果更改模型类型,则必须更改字段名称(例如{{1 }} MainLayer字段称为TFAlbertModel
)。此外,这似乎不是使用拥抱面的预期方式,因此这可能会在您的鼻子下发生变化,并且您的代码可能会因拥抱面更新而中断。
albert
class TFDistilBertModel(TFDistilBertPreTrainedModel):
def __init__(self, config, *inputs, **kwargs):
super().__init__(config, *inputs, **kwargs)
self.distilbert = TFDistilBertMainLayer(config, name="distilbert") # Embeddings
[DOCS] @add_start_docstrings_to_callable(DISTILBERT_INPUTS_DOCSTRING)
@add_code_sample_docstrings(
tokenizer_class=_TOKENIZER_FOR_DOC,
checkpoint="distilbert-base-uncased",
output_type=TFBaseModelOutput,
config_class=_CONFIG_FOR_DOC,
)
def call(self, inputs, **kwargs):
outputs = self.distilbert(inputs, **kwargs)
return outputs
您可以通过本质上复制/粘贴from_pretrained
中与加载权重有关的部分来实现。这也有一些严重的缺点,您将复制逻辑,这些逻辑可能会与拥抱库不同步。尽管您可能会以对基础模型名称更改更灵活,更健壮的方式来编写它。
理想情况下,这是拥抱面团队内部要解决的问题,方法是提供标准函数来创建MainLayer,将权重加载逻辑包装到可以调用的自身函数中,或者通过支持模型类的序列化