使用ai gallery sdk构建自定义模型-九游平台
ai gallery的transformers库支持部分开源的模型结构框架,并对昇腾系列显卡进行了训练/推理性能优化,可以做到开箱即用。如果你有自己从头进行预训练的模型,ai gallery也支持使用sdk构建自定义模型接入ai gallery。
transformers库介绍
ai gallery使用的transformers机器学习库是一个开源的基于transformer模型结构提供的预训练语言库。transformers库注重易用性,屏蔽了大量ai模型开发使用过程中的技术细节,并制定了统一合理的规范。使用者可以便捷地使用、下载模型。同时支持用户上传自己的预训练模型到在线模型资产仓库中,并发布上架给其他用户使用。ai gallery在原有transformers库的基础上,融入了对于昇腾硬件的适配与支持。对ai有使用诉求的企业、nlp领域开发者,可以借助这个库,便捷地使用昇腾算力进行自然语言理解(nlu)和自然语言生成(nlg)任务的sota模型开发与应用。
支持的模型结构框架
ai gallery的transformers库支持的开源模型结构框架如表1所示。
模型结构 |
pytorch |
mindspore |
gpu |
昇腾 |
---|---|---|---|---|
llama |
支持 |
不支持 |
支持 |
支持 |
bloom |
支持 |
不支持 |
支持 |
不支持 |
falcon |
支持 |
不支持 |
支持 |
不支持 |
bert |
支持 |
不支持 |
支持 |
不支持 |
mpt |
支持 |
不支持 |
支持 |
不支持 |
chatglm |
支持 |
不支持 |
支持 |
支持 |
核心基础类介绍
使用ai gallery sdk构建自定义模型,需要了解2个核心基础类“pretrainedmodel”和“pretrainedconfig”之间的交互。
- “pretrainedconfig”:预训练模型的配置基类
提供模型配置的通用属性和两个主要方法,用于序列化和反序列化配置文件。
pretrainedconfig.from_pretrained(dir) # 从目录中加载序列化对象(本地或者是url),配置文件为dir/config.json pretrainedconfig.save_pretrained(dir) # 将配置实例序列化到dir/config.json
- “pretrainedmodel”:预训练模型的基类
包含一个配置实例“config”,提供两个主要方法,用来加载和保存预训练模型。
# 1. 调用 init_weights() 来初始化所有模型权重 # 2. 从目录中(本地或者是url)中导入序列化的模型 # 3. 使用导入的模型权重覆盖所有初始化的权重 # 4. 调用 pretrainedconfig.from_pretrained(dir)来将配置设置到self.config中 pretrainedmodel.from_pretrained(dir) # 将模型实例序列化到 dir/pytorch_model.bin 中 pretrainedmodel.save_pretrained(dir) # 给定input_ids,生成 output_ids,在循环中调用 pretrainedmodel.forward() 来做前向推理 pretrainedmodel.generate()
操作步骤
本文使用newbert模型介绍构建自定义模型的流程。
- 安装ai gallery sdk。
通过pip在本地或云上开发环境安装ai gallery sdk(galleryformers)。
pip install galleryformers
建议在虚拟环境(python 3.8 )中安装ai gallery sdk,以便管理不同的项目,避免依赖项之间产生兼容性问题。
- 构建自定义模型。
- 编写自定义配置类。
模型的configuration包含了构建模型所需的所有信息的对象,需要尽可能完整。
from galleryformers import pretrainedconfig from typing import list class newbertconfig(pretrainedconfig): model_type = "bert" def __init__( self, vocab_size=30522, hidden_size=768, num_hidden_layers=12, num_attention_heads=12, intermediate_size=3072, hidden_act="gelu", hidden_dropout_prob=0.1, attention_probs_dropout_prob=0.1, max_position_embeddings=512, type_vocab_size=2, initializer_range=0.02, layer_norm_eps=1e-12, pad_token_id=0, position_embedding_type="absolute", use_cache=true, classifier_dropout=none, **kwargs, ): super().__init__(pad_token_id=pad_token_id, **kwargs) self.vocab_size = vocab_size self.hidden_size = hidden_size self.num_hidden_layers = num_hidden_layers self.num_attention_heads = num_attention_heads self.hidden_act = hidden_act self.intermediate_size = intermediate_size self.hidden_dropout_prob = hidden_dropout_prob self.attention_probs_dropout_prob = attention_probs_dropout_prob self.max_position_embeddings = max_position_embeddings self.type_vocab_size = type_vocab_size self.initializer_range = initializer_range self.layer_norm_eps = layer_norm_eps self.position_embedding_type = position_embedding_type self.use_cache = use_cache self.classifier_dropout = classifier_dropout
- 自定义配置类必须继承自“pretrainedconfig”。
- 自定义配置类的“__init__”必须接受任何“kwargs”,这些“kwargs”需要传递给“__init__”。
- 完成自定义配置类的编写后,可以使用该类创建配置实例。
newbert1_config = newbertconfig(num_hidden_layers=6, num_attention_heads=10, use_cache=false) newbert1_config.save_pretrained("mynewbert")
这一步会在本地名为mynewbert的文件夹中保存一个名为config.json的文件。
该配置实例同样可以通过调用from_pretrained方法加载。
newbert1_config.from_pretrained("mynewbert")
- 编写完配置部分,开始编写自定义模型。
下面展示了3种模型基类的代码示例,为了确保示例不过于复杂,本文对部分代码片段进行了省略展示。
- 预训练模型基类newbertpretrainedmodel
from galleryformers import pretrainedmodel from .configuration_newbert import newbertconfig class newbertpretrainedmodel(pretrainedmodel): config_class = newbertconfig load_tf_weights = load_tf_weights_in_bert base_model_prefix = "bert" supports_gradient_checkpointing = true def _init_weights(self, module): """initialize the weights""" if isinstance(module, nn.linear): module.weight.data.normal_(mean=0.0, std=self.config.initializer_range) if module.bias is not none: module.bias.data.zero_() elif isinstance(module, nn.embedding): module.weight.data.normal_(mean=0.0, std=self.config.initializer_range) if module.padding_idx is not none: module.weight.data[module.padding_idx].zero_() elif isinstance(module, nn.layernorm): module.bias.data.zero_() module.weight.data.fill_(1.0)
- 基础模型类newbertmodel:该类继承自newbertpretrainedmodel。
class newbertmodel(newbertpretrainedmodel): def __init__(self, config, add_pooling_layer=true): super().__init__(config) self.config = config self.embeddings = bertembeddings(config) self.encoder = bertencoder(config) self.pooler = bertpooler(config) if add_pooling_layer else none # initialize weights and apply final processing self.post_init() def get_input_embeddings(self): return self.embeddings.word_embeddings def set_input_embeddings(self, value): self.embeddings.word_embeddings = value def _prune_heads(self, heads_to_prune): for layer, heads in heads_to_prune.items(): self.encoder.layer[layer].attention.prune_heads(heads) def forward( self, input_ids: optional[torch.tensor] = none, attention_mask: optional[torch.tensor] = none, token_type_ids: optional[torch.tensor] = none, position_ids: optional[torch.tensor] = none, head_mask: optional[torch.tensor] = none, inputs_embeds: optional[torch.tensor] = none, encoder_hidden_states: optional[torch.tensor] = none, encoder_attention_mask: optional[torch.tensor] = none, past_key_values: optional[list[torch.floattensor]] = none, use_cache: optional[bool] = none, output_attentions: optional[bool] = none, output_hidden_states: optional[bool] = none, return_dict: optional[bool] = none, ...)
所有的模型都需要通过“forward”方法来实现自己的推理逻辑,这个方法会在执行“model(input_ids)”的时候进行调用
- 模型基类newbertforxxx:该类承自newbertpretrainedmodel。
该类可用于执行ai gallery工具链服务,此处以文本问答(question answering)的任务类型为例:
class newbertforquestionanswering(newbertpretrainedmodel): def __init__(self, config): super().__init__(config) self.num_labels = config.num_labels self.bert = bertmodel(config, add_pooling_layer=false) self.qa_outputs = nn.linear(config.hidden_size, config.num_labels) # initialize weights and apply final processing self.post_init() def forward( self, input_ids: optional[torch.tensor] = none, attention_mask: optional[torch.tensor] = none, token_type_ids: optional[torch.tensor] = none, position_ids: optional[torch.tensor] = none, head_mask: optional[torch.tensor] = none, inputs_embeds: optional[torch.tensor] = none, start_positions: optional[torch.tensor] = none, end_positions: optional[torch.tensor] = none, output_attentions: optional[bool] = none, output_hidden_states: optional[bool] = none, return_dict: optional[bool] = none, ) return_dict = return_dict if return_dict is not none else self.config.use_return_dict outputs = self.bert( input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids, position_ids=position_ids, head_mask=head_mask, inputs_embeds=inputs_embeds, output_attentions=output_attentions, output_hidden_states=output_hidden_states, return_dict=return_dict, ) sequence_output = outputs[0] logits = self.qa_outputs(sequence_output) start_logits, end_logits = logits.split(1, dim=-1) start_logits = start_logits.squeeze(-1).contiguous() end_logits = end_logits.squeeze(-1).contiguous() total_loss = none if start_positions is not none and end_positions is not none: # if we are on multi-gpu, split add a dimension if len(start_positions.size()) > 1: start_positions = start_positions.squeeze(-1) if len(end_positions.size()) > 1: end_positions = end_positions.squeeze(-1) # sometimes the start/end positions are outside our model inputs, we ignore these terms ignored_index = start_logits.size(1) start_positions = start_positions.clamp(0, ignored_index) end_positions = end_positions.clamp(0, ignored_index) loss_fct = crossentropyloss(ignore_index=ignored_index) start_loss = loss_fct(start_logits, start_positions) end_loss = loss_fct(end_logits, end_positions) total_loss = (start_loss end_loss) / 2 if not return_dict: output = (start_logits, end_logits) outputs[2:] return ((total_loss,) output) if total_loss is not none else output return questionansweringmodeloutput( loss=total_loss, start_logits=start_logits, end_logits=end_logits, hidden_states=outputs.hidden_states, attentions=outputs.attentions, )
这个多头模型的“forward”函数会先调用“self.bert.forward()”,然后再调用“self.masked_lm_head.__call__()”方法来生成最终的结果。
- 预训练模型基类newbertpretrainedmodel
- 完成了自定义模型类的编写后,可以使用该类创建一个模型实例:
newbert = newbertforquestionanswering(newbert1_config)
模型权重可以通过调用“.from_pretrained()”加载:newbert.from_pretrained(pretrained_model_name_or_path="./您的权重文件本地存储路径/.")
- 编写自定义配置类。
后续操作
自定义模型文件构建完成后,可以参考托管模型到ai gallery将模型文件托管至ai gallery。建议托管的模型文件列表参见表2。
文件名称 |
描述 |
---|---|
config.json |
模型配置文件。 |
model.safetensors或pytorch_model.bin |
预训练模型的权重文件。 |
tokenizer.json |
(可选)预处理器的词表文件,用于初始化tokenizer。 |
tokenizer_config.json |
(可选)预处理器的配置文件。 |
modeling_xxx.py |
(可选)自定义模型的代码文件,继承自pretrainedmodel,包含实现自定义推理逻辑的代码。 |
configuration_xxx.py |
(可选)自定义配置的代码文件,继承自pretrainedconfig,包含实现自定义配置的逻辑代码。 |
相关文档
意见反馈
文档内容是否对您有帮助?
如您有其它疑问,您也可以通过华为云社区问答频道来与我们联系探讨