task_seq2seq_autotitle_csl代码解读

本文详细解读了task_seq2seq_autotitle_csl.py代码,主要涉及BERT模型在seq2seq任务中的应用,包括数据预处理、模型搭建与参数加载、模型训练与评估。在数据预处理阶段,讲解了如何将title和content转换为token_ids和segment_ids。在模型搭建部分,重点解析了Bert4Seq2seq模型中compute_attention_mask()函数的修改,以及它在seq2seq任务中的作用。文章还讨论了attention_mask在原版BERT模型和seq2seq任务中的差异,并解释了为何seq2seq任务需要这样的修改。
摘要由CSDN通过智能技术生成

task_seq2seq_autotitle_csl.py代码解读

此文档是对bert4keras工具中seq2seq任务的示例代码的一个解读,尽可能看的细致是为了以后修改起来更加顺手。有些代码和操作的理解可能还会有一些错误,正在不断的完善当中。

0.主要部分

从主函数入口可以了解该程序主要包括以下三个部分:

evaluator = Evaluate()
train_generator = data_generator(train_data, batch_size)
model.fit_generator(train_generator.forfit(),steps_per_epoch=len(train_generator),
                            epochs=epochs, callbacks=[evaluator])

其中evaluator使用来评估模型和保存模型参数的; data_generator是用来生成训练数据的,将文本数据转换成对应的token_id,方便模型计算; model.fit_generator() 使用了使用训练数据训练模型。

所以,改程序可以概括为三个主要部分: 数据预处理,模型搭建与参数加载,训练模型并且评估模型。

1.数据预处理阶段

在执行data_generator() 之前,还需要做以下准备。通过load_data()将文本数据读取到列表中,并且加载bert模型文件中的中文字表,在调用Tokenizer()函数,返回tokenizer对象,为了下一步将输入文本转换为token:

# 加载数据集
train_data = load_data("train.data")
valid_data = load_data("valid.data")
test_data = load_data("test.data")
# 加载并精简词表,建立分词器
token_dict, keep_tokens = load_vocab(
    dict_path=dict_path,
    simplified=True,
    startwith=['[PAD]', '[UNK]', '[CLS]', '[SEP]'],
)
# 转换为token
tokenizer = Tokenizer(token_dict, do_lower_case=True)

接下来开始执行data_generator(),传入参数为train_data和batch_size:

class data_generator(DataGenerator):
    """数据生成器"""
    def __iter__(self, random=False):
        idxs = list(range(len(self.data)))
        if random:
            np.random.shuffle(idxs)
        batch_token_ids, batch_segment_ids = [], []
        for i in idxs:
            title, content = self.data[i]
            # 将数据转换成ID,将content和title转换成同一个token_ids
            token_ids, segment_ids = tokenizer.encode(content,title,max_length=maxlen)
            batch_token_ids.append(token_ids)
            batch_segment_ids.append(segment_ids)
            if len(batch_token_ids) == self.batch_size or i == idxs[-1]:
                batch_token_ids = sequence_padding(batch_token_ids)
                batch_segment_ids = sequence_padding(batch_segment_ids)
                yield [batch_token_ids, batch_segment_ids], None
                batch_token_ids, batch_segment_ids = [], []

将数据进行随机洗牌之后,调用tokenizer.encode()对数据进行转换,输入文本内容和标题内容,以及最大句子长度,这里要注意一个地方,本来读取的数据第一句是title,第二句是content,在调用tokenizer.encode()函数时,先输入的是content,其次才输入的title,转换完成之后返回token_idssegment_ids,然后按照batch_size进行打包返回。这里最值得注意的地方是tokenizer.encode()中发生了什么?

def encode(self,first_text,second_text=None,max_length=None,first_length=None,
           second_length=None):
    """输出文本对应token id和segment id如果传入first_length,则强行padding第一个句子到指定长度;同理,如果传入second_length,则强行padding第二个句子到指定长度。"""
    if is_string(first_text):
        first_tokens = self.tokenize(first_text)
    else:
        first_tokens = first_text
    if second_text is None:
        second_tokens = None
    elif is_string(second_text):
        second_tokens = self.tokenize(second_text, add_cls=False)
    else:
        second_tokens = second_text
    if max_length is not None:
        self.truncate_sequence(max_length, first_tokens, second_tokens, -2)
    first_token_ids = self.tokens_to_ids(first_tokens)
    if first_length is not None:
        first_token_ids = first_token_ids[:first_length]
        first_token_ids.extend([self._token_pad_id] *
                               (first_length - len(first_token_ids)))
    first_segment_ids = [0] * len(first_token_ids)

    if second_text is not None:
        second_token_ids = self.tokens_to_ids(second_tokens)
        if second_length is not None:
            second_token_ids = second_token_ids[:second_length]
            second_token_ids.extend([self._token_pad_id] *
                (second_length - len(second_token_ids)))
        second_segment_ids = [1] * len(second_token_ids)
        first_token_ids.extend(second_token_ids)
        first_segment_ids.extend(second_segment_ids)
    return first_token_ids, first_segment_ids

前半部分都是对文本的token化和padding操作,由最后三句可以看到,程序将second_token_ids拼接到了first_token_ids后面,对应文本就是将训练数据的title拼接到了content之后,返回了first_token_ids,对于segment_ids,content部分的词标记为"0",title部分的词标记为"1",将title的segment_ids拼接到content的segment_ids之后,最后返回token_ids和segment_ids,所以segment_ids的数据就形如[0,0,0,0,1,1]

接下来将数据按照batch_size打包到列表中,得到batch_token_ids和batch_segment_ids,至此,数据预处理部分执行完毕。

2.模型搭建与参数加载

2.1关于Bert针对seq2seq任务的代码修改部分

接下来对首先对bert模型进行加载,输入参数config_path表示bert模型参数的配置文件,checkpoint_path表示模型参数文件路径,application表示应用的任务类型。对于不同用途,要对bert模型进行不同的修改。

# 构建bert模型,并且加载模型参数
model = build_bert_model(
    config_path,
    checkpoint_path,
    application='seq2seq',
    keep_tokens=keep_tokens,  # 只保留keep_tokens中的字,精简原字表
)
# 此行代码输出模型各层的参数状况
model.summary()

application='seq2seq’参数表示: 对于seq2seq任务模型加载的是继承自BertModel父类的Bert4Seq2seq子类,对于这个Bert4Seq2seq子类,仅仅实现了不同的compute_attention_mask() 函数。

def compute_attention_mask(self, layer_id, segment_ids):
    """为seq2seq采用特定的attention mask """
    # segment_ids是2D张量 形如[[0, 0, 0, 0, 0, 1, 1, 1]] 这种,其中数值0,1表示词到底属于哪个句	子,0表示词属于content,1表示当前词输入title。
    # 这个函数被调用12次 其中seq2seq_attention_mask()函数被调用一次创建了a_mask张量,剩下11次的数据		应该都是使用第一次创建的a_mask,并没有产生新的张量
    # 这里的layer_id 表示的就是层数编号,在起初定义的时候,作者的想法是“定义每一层的Attention Mask,		来实现不同的功能”,但是在实际实现的时候,并没有用到层数编号这个参数。
    # 源码文件bert.py中第 139 行 开始调用此函数,并返回attention_mask,其中输入的参数为 层数编号i 和 		segment_ids(s_in)
    if self.attention_mask is None:
        def seq2seq_attention_mask(s):
            # 这个函数被调用一次
            import tensorflow as tf
            # 得到句子长度(first_seq_length + second_seq_length)
            seq_len = K.shape(s)[1]
            with K.name_scope('attention_mask'):
                # 生成数值为1的 4维张量
                ones = K.ones((1, 1, seq_len, seq_len))
            # 在这部操作以后,全为1的4为张量,变成了下三角为1 上三角为0的对角矩阵
            a_mask = tf.linalg.band_part(ones, -1, 0)
            # 将segment_ids变成 [batch_size, 1,1, seq_length] 形状的张量 数值大小不变
            s_ex12 = K.expand_dims(K.expand_dims(s, 1), 2)
            # 将segment_ids变成 [
### 回答1: ethercat injection_seq_rt和injection_seq_fsm是与车辆发动系统相关的两个技术术语。 EtherCAT(以太网控制自动化技术)是一种高性能实时以太网技术,用于工业自动化中的实时控制和数据交换。其中的injection_seq_rt是指以太网注入序列实时模块,通常用于汽车发动机控制系统中的稳态控制。 另一方面,汽车发动机控制需要的很多功能都需要状态机(FSM)来实现。FSM是一种数学模型,用于描述系统的状况和它们之间的转移。因此,injection_seq_fsm就是描述汽车发动机控制系统中燃油注入状态机的模块。 总的来说,ethercat injection_seq_rt和injection_seq_fsm是两个关键技术,在汽车发动机控制系统中有着重要的作用。它们能为整个系统的运行提供稳定性和高效性,使得汽车在行驶过程中更加安全和可靠。同时,它们的应用也能够缩短汽车开发周期和提高生产效率。 ### 回答2: ethercat injection_seq_rt injection_seq_fsm是指在以太网CAT网络中,实时传输控制器(RT)和状态机(FSM)使用注入序列(injection_seq)来确定数据的传输顺序。 以太网CAT网络是一种高性能、实时性强的工业以太网,广泛应用于现代工业自动化控制系统中。传输控制器(RT)是其中的重要组成部分,它负责控制数据的传输和处理。而状态机(FSM)则是用来管理和维护实时性。 在以太网CAT网络中,注入序列(injection_seq)是用来确定数据包的发送顺序的。通过注入序列的使用,可以使数据包按照正确的顺序进行传输,并确保实时性的同时保证数据的准确性,以满足现代工业自动化控制系统对数据传输要求的严格要求。 综上所述,ethercat injection_seq_rt injection_seq_fsm是以太网CAT网络中实时传输控制器(RT)和状态机(FSM)使用注入序列(injection_seq)来确定数据的传输顺序的技术术语。它确保了数据包的实时性、正确性和可靠性,并广泛应用于现代工业自动化控制系统之中。 ### 回答3: EtherCAT是一种高性能的实时以太网通信协议,在工业控制领域得到广泛应用。其中,injection_seq_rt和injection_seq_fsm是EtherCAT通信中的两个重要概念。 injection_seq_rt是EtherCAT通信协议中的实时数据传输机制,用于实现对实时数据的高效传输。它是通过将数据分割为若干个包进行传输,并在接收端进行实时重构来实现的。 而injection_seq_fsm则是EtherCAT通信协议中的有限状态机,用于控制数据包的发送和接收。它可以根据实时网络负载和数据缓存状态,动态调整数据发送和接收的速率,从而保证数据传输的稳定性和实时性。 总的来说,EtherCAT通信协议的实时性和高效性得益于injection_seq_rt和injection_seq_fsm这两个重要机制的支持,它们共同构成了EtherCAT的核心技术。在工业控制领域,EtherCAT通信协议已经成为了众多领域专业人士和企业所信赖的通信协议,正在为工业自动化领域的发展做出日益重要的贡献。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值