6.Model
更新时间:2021-03-23
简介
在文心中,我们把深度学习任务中对神经网络的基本操作进行了统一封装,称为Model。一个Model实例中定义的操作包括前向传播网络(forward)、优化策略(optimizer)、指标评估(get_metrics)等部分,这些部分除了可以直接使用文心预置的方法之外,均可实现自定义。
基本结构
每一个Model实例中,都需要实现3个基本功能:搭建前向传播网络、选定优化策略、确定指标评估的方式。这里以一个CNN分类任务来举例说明:
- 搭建前向传播网络:核心内容是模型的前向计算的组网部分(使用飞桨的接口进行组网)和损失函数的计算。输出即为对输入数据执行变换计算后的结果。
点击查看代码块
def forward(self, fields_dict, phase):
"""前向计算组网部分包括loss值的计算,必须由子类实现
:param: fields_dict: 序列化好的id
:param: phase: 当前调用的阶段,如训练、预测,不同的阶段组网可以不一样
:return: 一个dict数据,存放TARGET_FEED_NAMES, TARGET_PREDICTS, PREDICT_RESULT,LABEL,LOSS等所有你希望获取的数据
"""
## 从入参的dict中解析出由Reader构造出来的tensor数据。
fields_dict = self.fields_process(fields_dict, phase)
instance_text_a = fields_dict["text_a"]
record_id_text_a = instance_text_a[InstanceName.RECORD_ID]
text_a = record_id_text_a[InstanceName.SRC_IDS]
text_a_lens = record_id_text_a[InstanceName.SEQ_LENS]
instance_label = fields_dict["label"]
record_id_label = instance_label[InstanceName.RECORD_ID]
label = record_id_label[InstanceName.SRC_IDS]
dict_dim = self.model_params.get('vocab_size', 33261)
emb_dim = self.model_params.get('emb_dim', 128)
hid_dim = self.model_params.get('hid_dim', 128)
hid_dim2 = self.model_params.get('hid_dim2', 96)
win_size = self.model_params.get('win_size', 3)
num_labels = self.model_params.get('num_labels', 2)
unpad_data = fluid.layers.sequence_unpad(text_a, length=text_a_lens)
## 对样本数据构造embedding(语义表示)向量
emb = fluid.layers.embedding(input=unpad_data, size=[dict_dim, emb_dim])
## 构造好的embedding接入CNN结构层。
conv = fluid.nets.sequence_conv_pool(
input=emb,
num_filters=hid_dim,
filter_size=win_size,
act="tanh",
pool_type="max")
## 接入全连接层,进行降维
fc_1 = fluid.layers.fc(input=[conv], size=hid_dim2)
## 接入全连接层,降维到与训练集中的label数目相同的维度,并采用softmax进行概率映射。
predictions = fluid.layers.fc(input=[fc_1], size=num_labels, act="softmax")
if phase == InstanceName.SAVE_INFERENCE:
"""保存模型时需要的入参:表示模型预测时需要输入的变量名称和顺序"""
target_feed_name_list = [text_a.name, text_a_lens.name]
"""保存模型时需要的入参:表示预测时最终输出的结果"""
target_predict_list = [predictions]
forward_return_dict = collections.OrderedDict()
forward_return_dict[InstanceName.TARGET_FEED_NAMES] = target_feed_name_list
forward_return_dict[InstanceName.TARGET_PREDICTS] = target_predict_list
return forward_return_dict
## 损失函数采用交叉熵计算
cost = fluid.layers.cross_entropy(input=predictions, label=label)
avg_cost = fluid.layers.mean(x=cost)
"""PREDICT_RESULT,LABEL,LOSS 是关键字,必须要赋值并返回"""
forward_return_dict = collections.OrderedDict()
forward_return_dict[InstanceName.PREDICT_RESULT] = predictions
forward_return_dict[InstanceName.LABEL] = label
forward_return_dict[InstanceName.LOSS] = avg_cost
return forward_return_dict- 选定优化策略:设置优化器,如Adam,Adagrad,SGD等,优化器部分详见Optimizer。
点击查看代码块
def optimizer(self, loss, is_fleet=False):
"""
:param loss: 前向计算得到的损失值
:param is_fleet: 是否采用了飞桨的fleet模式
:return: OrderedDict: 该dict中放的是需要在运行过程中fetch出来的tensor
"""
opt_param = self.model_params.get('optimization', None)
if opt_param:
lr = opt_param.get('learning_rate', 2e-5)
else:
lr = 2e-5
## 调用飞桨提供的优化器相关的API。
optimizer = fluid.optimizer.Adam(learning_rate=lr)
if is_fleet:
optimizer = fleet.distributed_optimizer(optimizer)
optimizer.minimize(loss)
optimizer_output_dict = collections.OrderedDict()
return optimizer_output_dict- 确定指标评估的方式:训练过程中某一时刻模型的指标评估部分的动态计算和打印。
点击查看代码块
def get_metrics(self, forward_return_dict, meta_info, phase):
"""指标评估部分的动态计算和打印
:param forward_return_dict: executor.run过程中fetch出来的forward中定义的tensor
:param meta_info:常用的meta信息,如step, used_time, gpu_id等
:param phase: 当前调用的阶段,包含训练和评估
:return:
"""
predictions = forward_return_dict[InstanceName.PREDICT_RESULT]
label = forward_return_dict[InstanceName.LABEL]
## acc计算
metrics_acc = metrics.Acc()
acc = metrics_acc.eval([predictions, label])
## precision计算
metrics_pres = metrics.Precision()
precision = metrics_pres.eval([predictions, label])
if phase == InstanceName.TRAINING:
step = meta_info[InstanceName.STEP]
time_cost = meta_info[InstanceName.TIME_COST]
logging.debug("phase = {0} acc = {1} precision = {2} step = {3} time_cost = {4}".format(
phase, round(acc, 3), round(precision, 3), step, time_cost))
if phase == InstanceName.EVALUATE or phase == InstanceName.TEST:
time_cost = meta_info[InstanceName.TIME_COST]
logging.debug("phase = {0} acc = {1} precision = {2} time_cost = {3}".format(
phase, round(acc, 3), round(precision, 3), time_cost))
metrics_return_dict = collections.OrderedDict()
metrics_return_dict["acc"] = acc
metrics_return_dict["precision"] = precision
return metrics_return_dict文心中的预置Model
文心提供了丰富的预置Model,支持常见的NLP领域的经典任务,包括文本分类、文本匹配、序列标注、信息抽取等,所有的预置Model文件位于./wenxin/models/目录下,部分Model如下所示:
.
├── __init__.py
├── model.py ## 所有model的基类
├── cnn_classification.py ## 基于CNN的单分类网络
├── ernie_cnn_classification.py ## 在ERNIE基础上进行微调的CNN的单分类网络
├── ....
├── cnn_matching_pairwise.py ## 基于CNN的pairwise匹配网络
├── cnn_matching_pointwise.py ## 基于CNN的pointwise匹配网络
├── ernie_matching_fc_pointwise.py ## 在ERNIE基础上进行微调的pointwise匹配网络
├── ernie_matching_siamese_pairwise.py ## 在ERNIE基础上进行微调的pairwise匹配网络
├── ....
├── crf_sequence_label.py ## 基于crf结构的序列标注网络
├── ernie_crf_sequence_label.py ## 在ERNIE基础上进行微调的基于crf结构的序列标注网络
├── ernie_fc_sequence_label.py ## 在ERNIE基础上进行微调的最简单的序列标注网络
├── ....
├── ernie_one_sent_classification.py ## 在ERNIE基础上进行微调的单句分类网络
├── ernie_mrc.py ## 在ERNIE基础上进行微调的阅读理解任务的网络
├── ernie_hierar_label_classification.py ## 在ERNIE基础上进行微调的层次化分类网络
├── ernie_multi_label_classification.py ## 在ERNIE基础上进行微调的多标签分类网络
├── textcnn_hierar_label_classification.py ## 基于TextCNN结构的层次化分类网络
├── ernie_two_sent_classification.py ## 在ERNIE基础上进行微调的句对分类网络
└── ....进阶使用
文心中提供了NLP领域比较通用的经典网络,如果用户需要针对自己的业务场景进行自定义优化使用的话,请参考详细的接口设计与自定义核心接口Model设计。
