使用HuggingFace的Transformers库加载预训练模型来处理下游深度学习任务很是方便,然而加载预训练模型的方法多种多样且过程比较隐蔽,这在一定程度上会给人带来困惑。因此,本篇文章主要讲一下使用不同方法加载本地预训练模型的区别、加载预训练模型及其配置的过程,藉此做个记录,也可供参考。
加载预训练模型的过程
transformers里主要使用 from_pretrained 方法来加载预训练模型,调用这个方法时会执行以下过程:
- 加载预训练模型:from_pretrained方法可以选择从本地加载已下载的预训练模型,或者提供模型名称从HuggingFace的模型仓库下载预训练模型权重;
- 加载配置:from_pretrained方法会加载预训练模型的配置文件,配置包含了模型的名称、架构、参数等信息。这些配置参数用来定义模型的结构;
- 初始化模型:使用配置文件中的参数初始化模型,构建模型的各个层和结构;
- 载入权重:将预训练模型权重载入到初始化的模型结构中;
- 创建实例:返回加载了权重的模型实例,利用这个实例进一步完成特定的下游任务。
加载预训练模型
要加载预训练模型,可以使用transformers库的 AutoModel 方法或具体模型对应的方法(比如要加载的是Ernie模型,则可以直接使用 ErnieModel )。这两者有区别吗?
先说结论:AutoModel.from_pretrained 与 ErnieModel.from_pretrained 方法本质上是一样的。
本文以 shibing624/text2vec-base-chinese-paraphrase 模型为例,可以下载到本地以作示例之用。注意至少需要下载 pytorch_model.bin 、config.json 、vocab.txt 三个文件,其中前两个文件加载预训练模型会用到,第三个文件加载切词器会用到,否则会报错。
图1
#需要先安装transformers模块(pip install transformers -i https://pypi.tuna.tsinghua.edu.cn/simple/)
from transformers import AutoModel, ErnieModel
#下载的预训练模型的路径(按自己存放位置修改)
model_path = './pretrain_models/shibing624-text2vec-base-chinese-paraphrase'
model = AutoModel.from_pretrained(model_path)
将鼠标悬停在上述代码的 from_pretrained 上,可以看到 AutoModel.from_pretrained 其实是调用了其父类 _BaseAutoModelClass 里面的from_pretrained方法,如图2所示。
具体过程:
- AutoModel.from_pretrained里面的 AutoModel 实际是在文件 transformers/models/auto/modeling_auto.py 的类 AutoModelWithLMHead 里且继承自父类 _AutoModelWithLMHead 里的from_pretrained:
图3
- 再来看 _AutoModelWithLMHead ,它继承自 _BaseAutoModelClass 类,因此说 AutoModel.from_pretrained 其实是调用了 _BaseAutoModelClass 里的from_pretrained方法。类 _BaseAutoModelClass 存在于文件 transformers/models/auto/auto_factory.py 中。
图4
我们已知 AutoModel.from_pretrained 实际上调用了类 _BaseAutoModelClass 里的 from_pretrained 方法,下面来看看它会返回什么。
图5是截取了类 _BaseAutoModelClass 的 from_pretrained 方法里获取预训练模型的片段代码,如果加载的是本地下载的预训练模型,走的是 elif 这条路,更详细的不赘述了,描述一下大概流程:走这条路会利用下载的预训练模型里的配置文件 config.json (稍后会讲它的加载过程)来匹配要加载的模型,具体来说,会用到配置文件里 architectures (配置文件参数如图6所示) 这个属性来匹配模型(通过调用 _get_model_class 方法)。
比如对于 ErnieModel ,查找到的模型(model_class)会是:transformers.models.ernie.modeling_ernie.ErnieModel
再结合返回值 return model_class.from_pretrained(pretrained_model_name_or_path, *model_args, config=config, **hub_kwargs, **kwargs)
因此通过最初的 AutoModel.from_pretrained(model_path) ,最终会和直接使用 ErnieModel.from_pretrained(model_path) 得到相同的结果,即会调用 transformers.models.ernie.modeling_ernie 里的 ErnieModel ,如图7所示,此谓之殊途同归。
加载配置文件
再来看配置文件的加载机制,transformers里预训练模型配置文件的加载分为显式加载和隐式加载。
显式加载
from transformers import AutoConfig, ErnieConfig, AutoModel, ErnieModel
#显式加载
config = AutoConfig.from_pretrained(model_path)
model = AutoModel.from_pretrained(model_path, config = config)
隐式加载
from transformers import AutoConfig, ErnieConfig, AutoModel, ErnieModel
#隐式加载
model = AutoModel.from_pretrained(model_path)
对于隐式加载,由于未在 AutoModel.from_pretrained 里指定 config 参数,因此会在上面加载模型时用的类 _BaseAutoModelClass 里的 from_pretrained 方法里面进行配置文件的加载,片段代码如图8所示:
具体过程:
- AutoConfig.from_pretrained 里的 AutoConfig 实际上是在文件 transformers/models/auto/configuration_auto.py 里,AutoConfig.from_pretrained 方法本质上是通过 PretrainedConfig.get_config_dict 方法来加载配置文件,如图9所示。 PretrainedConfig.get_config_dict 来自文件 transformers/configuration_utils.py ;
- PretrainedConfig.get_config_dict 会利用 AutoModel.from_pretrained 中传入的模型本地路径 model_path 拼接上 CONFIG_NAME(来自:transformers/utils/init.py里面的CONFIG_NAME = “config.json”),从而取到本地预训练模型的配置文件 config.json ,至此,可以成功加载配置文件。
图9
上面是隐式加载配置文件过程,再来看看显式加载过程。
图10可以看出 AutoConfig.from_pretrained(model_path) 里的 AutoConfig 来自 transformers.models.auto.configuration_auto ,所以 AutoConfig.from_pretrained(model_path) 引用的实际上是 configuration_auto.py 里的 from_pretrained 方法,这就回到了图9里面的函数,因此,通过 AutoConfig.from_pretrained(model_path) 这种显式加载配置文件,其实也是和隐式一样到了相同的归处。
更进一步,再来看下使用具体模型的加载方法: ErnieConfig.from_pretrained(model_path) ,ErnieConfig 本身没有 from_pretrained 方法,但它继承了 PretrainedConfig ,如图11所示。PretrainedConfig 在文件 transformers/configuration_utils.py 里,然后通过其中的 from_pretrained 方法加载配置文件(函数返回配置文件),如图12所示,返回值的类型被设置成 PretrainedConfig (因此如果显式加载了,就不会走图8那步了) 。
具体引用的PretrainedConfig.get_config_dict 见图13,其中注意一下绿色方框里面的警告:如果配置文件"config.json"里面的 model_type 属性值与cls(cls代表PretrainedConfig类)的"model_type"属性(实际上是在transformers/models/ernie/configuration_ernie.py的ErnieConfig类中定义的,随模型的改变而变化)不一样时会出现像图14的提示。
通过以上探查可以得到结论:对于配置文件的加载,无论是显式(AutoConfig.from_pretrained或ErnieConfig.from_pretrained)还是隐式加载,最终都会到达 transformers/configuration_utils.py 里的 from_pretrained 方法并且使用里面的 get_config_dict 方法来加载配置文件。
模型的初始化和预训练权重载入,下篇见!