python3 seq2seq_model.py 对应代码解读抽取式提取+生成式提取摘要代码解读------摘要代码解读5------第二章

摘要代码阅读5


接上一章节
上一章节调用完了adam优化器,这一章节重点介绍AutoSummary类别seq2seq解码器的操作

class AutoSummary(AutoRegressiveDecoder):

继承了之前的AutoRegressiveDecoder的类别
首先我们去查看AutoRegressiveDecoder类的示例

from bert4keras.snippets import AutoRegressiveDecoder

这里自定义的时候调用

autosummary = AutoSummary(
	start_id = tokenizer._token_start_id,
	end_id = tokenizer._token_end_id,
	maxlen = maxlen // 2
)

进入初始化AutoRegressiveDecoder的类别之中

class AutoRegressiveDecoder(object):
    """通用自回归生成模型解码基类
    包含beam search和random sample两种策略
    """
    def __init__(self, start_id, end_id, maxlen, minlen=1):
        self.start_id = start_id
        self.end_id = end_id
        self.maxlen = maxlen
        self.minlen = minlen
        self.models = {
    
    }
        if start_id is None:
            self.first_output_ids = np.empty((1, 0), dtype=int)
        else:
            self.first_output_ids = np.array([[self.start_id]])

这里的
start_id = 2
end_id = 3
self.maxlen = 512
(不明白这里为什么self.maxlen要除以2)。
由于AutoSummary(AutoRegressiveDecoder)类之中包含的函数众多,所以这里我们需要先看看有哪些函数被调用过
仔细打印对应的输出内容发现,调用了相应的AutoRegressiveDecoder wraps函数内容
AutoRegressiveDecoder warps前面加上了静态函数
使用__init__查看AutoSummary的初始化过程之后,发现了先调用的AutoRegressiveDecoder.wraps函数,然后才调用的初始化过程
调用AutoRegressiveDecoder.wraps函数在定义类别的过程中直接就进行调用了

@AutoRegressiveDecoder.wraps(default_rtype='logits',use_states=True)

这里直接调用AutoRegressiveDecoder中的wraps,因为wraps
之前AutoRegressiveDecoder中的wraps函数就被定义为@staticmethod的静态方法,所以这里可以通过类直接调用

@AutoRegressiveDecoder.wraps(default_rtype='logits', use_states=True)

然后再进行初始化

autosummary = AutoSummary(
	start_id = tokenizer._token_start_id,
	end_id = tokenizer._token_end_id,
	maxlen = maxlen//2
)

后面这些初始化的内容也没有用上,直接开始训练了2个epochs的训练集合数据

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

这里感觉不完善,应该是训练完每一个epoch之后就进行预测,保存在测试集合上最高的权重。

seq2seq调用文本的过程

首先训练数据生成的过程

token_ids,segment_ids = tokenizer.encode(
	source,target,maxlen=maxlen,pattern='S*ES*E'
)

这里token_ids就是正常的切分词,注意segment_ids之中,原来文本的segment_id内容为0,后面生成的摘要segment_id内容为1
接下来进行随机mask的操作

masked_token_ids = random_masking(token_ids)

这里如果概率小于0.15会被随机替换掉,如果概率大于0.15会保持原样
接下来进行生成复制

def random_masking(token_ids):
	rands = np.random.random(len(token_ids))
  return [
     t if r > 0.15 else np.random.choice(token_ids)
     for r, t in zip(rands, token_ids)
  ]

接下来进行source_labels和target_labels的标记处理

source_labels,target_labels = generate_copy_labels(
	masked_token_ids[:idx],token_ids[idx:]
)

这里生成复制的标签内容

mapping = longest_common_subsequence(source,target)[1]
from collections import defaultdict
def longest_common_subsequence(source, target):
    """最长公共子序列(source和target的最长非连续子序列)
    返回:子序列长度, 映射关系(映射对组成的list)
    注意:最长公共子序列可能不止一个,所返回的映射只代表其中一个。
    """
    c = defaultdict(int)
    for i, si in enumerate(source, 1):
        for j, tj in enumerate(target, 1):
            if si == tj:
                c[i, j] = c[i - 1, j - 1] + 1
            elif c[i, j - 1] > c[i - 1, j]:
                c[i, j] = c[i, j - 1]
            else:
                c[i, j] = c[i - 1, j]
    l, mapping = c[len(source), len(target)], []
    i, j = len(source) - 1, len(target) - 1
    while len(mapping) < l:
        if source[i] == target[j]:
            mapping.append((i, j))
            i, j = i - 1, j - 1
        elif c[i + 1, j] > c[i, j + 1]:
            j = j - 1
        else:
            i = i - 1
    return l, mapping[::-1]
a1,a2 = longest_common_subsequence('老婆老婆我爱你颖颖颖颖颖','我是老婆老婆颖颖')

比如说输入

a1,a2 = longest_common_substring('老婆老婆我爱你颖颖颖颖颖','我是老婆老婆颖颖')

此时输出的结果为

a1 = 6
a2 = [(0, 2), (1, 3), (2, 4), (3, 5), (10, 6), (11, 7)]

计算出最长公共子序列有对应的公式,使用的是动态规划,这里就暂时跳过
调用一波苏神的讲解
苏神讲解图片

猜你喜欢

转载自blog.csdn.net/znevegiveup1/article/details/120956120