摘要代码阅读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)]
计算出最长公共子序列有对应的公式,使用的是动态规划,这里就暂时跳过
调用一波苏神的讲解