房产聊天问答匹配高分方案学习1

高分方案上

Post training(预训练的后操作)

首先需要提取出一个新的词表
这里面提取词表采用的是最小熵原理
具体的操作步骤可以参考苏神的博客
最小熵原理
调用的时候很简单

train_data = load_data('train')
test_data = load_data('test')

获得train_data和test_data的字符串数组

[......
 ......
'可以贷公积金吗纯公积金吗可以的可以留个电话吗?我们录入,我不给你打电话的我们联系还是用这个不太大了,已经很便宜了', 
'您好,我正在看尚林家园的房子看房吗有啊我带你看看', 
'今天可以安排看房子吗?我约下房东,稍后回你可以看,你几点有时间过来呢?好的,那咱们在一号门口这碰头?']

接下来直接调用库生成new_words

from nlp_zero import *
f = Word_Finder(min_proba=1e-5)
f.train(G())
f.find(G())
# 长度为2~5 且不在jieba 词典内的词
new_words = [w for w, _ in f.words.items() if len(w) > 2 and len(w) < 5 and len(jieba.lcut(w, HMM=False)) > 1]

with open('new_dict.txt', 'w') as f:
    f.write('\n'.join(new_words))

进行point-post-training-wwm-sop.py和pair-post-training-wwm-sop.py的训练

python point-post-training-wwm-sop.py
python pair-post-training-wwm-sop.py

1.分析point-post-training-wwm-sop.py文件

这里load_data之后

train_data = load_data('train')
test_data = load_data('test')
data = train_data+test_data

构成的list的内容为

d = 
[['采荷一小是分校吧', '杭州市采荷第一小学钱江苑校区,杭州市钱江新城实验学校。', '是的', '这是5楼'],
['毛坯吗?', '因为公积金贷款贷的少', '是呢', '这套一楼带院的,您看看', '房本都是五年外的', '好的??,您先看下'],
['你们的佣金费大约是多少和契税是多少。', '您是首套还是二套呢?', '所有费用下来654万', '包含着税费和我们的服务费和房款', '好的', '链家天鸿美域店NAME,电话是PHONE(同微信号),随时联系?'],
......]

接下来采用jieba切割词语的方法
因为nezha采用的是jieba分割词语的方法,所以这里也采用分割词语的方法

words_data = [[jieba.lcut(line) for line in sen] for sen in data]

获得切分之后的words_data

[
[['采荷', '一小', '是', '分校', '吧'], ['杭州市', '采荷', '第一', '小学', '钱江苑', '校区', ',', '杭州市', '钱江', '新城', '实验学校', '。'], ['是', '的'], ['这是', '5', '楼']], 
[['毛坯吗', '?'], ['因为', '公积金', '贷款', '贷', '的', '少'], ['是', '呢'], ['这套', '一楼', '带院的', ',', '您看看'], ['房本', '都', '是', '五年', '外', '的'], ['好', '的', '?', '?', ',', '您先看下']], 
[['你们', '的', '佣金', '费', '大约', '是多少', '和', '契税', '是多少', '。'], ['您', '是首套', '还是二套', '呢', '?'], ['所有', '费用', '下来', '654', '万'], ['包含', '着', '税费', '和', '我们', '的', '服务费', '和', '房款'], ['好', '的'], ['链家', '天鸿美域', '店', 'NAME', ',', '电话', '是', 'PHONE', '(', '同微信', '号', ')', ',', '随时联系', '?']]
......
]

接下来不管中间的各种函数,直接进入正文模型以及损失函数的构建

bert,train_model,loss = build_transformer_model_with_mlm()

进入到build_transformer_model_with_mlm函数之中查看模型的构建

bert = build_transformer_model(
	config_path,
	with_mlm='linear',
	with_nsp=True,
	model='nezha',
	return_keras_model=False
)

首先我们仔细分析NEZHA模型

# add External knowledge
if self.external_embedding_size:
    external_embedding = self.apply(x,
                                    Embedding,
                                    name='Embedding-External',
                                    input_dim=self.vocab_size,
                                    output_dim=self.external_embedding_size,
                                    weights=[self.external_embedding_weights]
                                    )
    if self.external_embedding_size != self.embedding_size:
        external_embedding = self.apply(external_embedding,
                                        Dense,
                                        name='External-Embedding-Mapping',
                                        units=self.embedding_size,
                                        kernel_initializer=self.initializer)
    x = self.apply([token_with_segment, external_embedding], Add, name='Embedding-Token-Segment-External')
else:
    x = token_with_segment

这里面引入了外部的embedding参数的内容
查看作者的原版的论文引入外部embedding的说明
引入外部embedding网络层的图片这里调用模型的输出分为两部分

bert = build_transformer_model(
        config_path,
        with_mlm='linear',
        with_nsp=True,
        model='nezha',
        return_keras_model=False
        #return_keras_model=True
    )
proba = bert.model.output

输出的proba的内容为

proba = 
[<tf.Tensor 'NSP-Proba/Softmax:0' shape=(?, 2) dtype=float32>, 
<tf.Tensor 'MLM-Activation/MLM-Activation/Identity:0' shape=(?, ?, 21128) dtype=float32>]

然后这里面封装的是几种损失函数

def mlm_loss(inputs):
    """计算loss的函数,需要封装为一个层
    """
    y_true, y_pred, mask = inputs
    _, y_pred = y_pred
    loss = K.sparse_categorical_crossentropy(
        y_true, y_pred, from_logits=True
    )
    loss = K.sum(loss * mask) / (K.sum(mask) + K.epsilon())
    return loss

def nsp_loss(inputs):
    """计算nsp loss的函数,需要封装为一个层
    """
    y_true, y_pred = inputs
    y_pred, _ = y_pred
    loss = K.sparse_categorical_crossentropy(
        y_true, y_pred
    )
    loss = K.mean(loss)
    return loss

def mlm_acc(inputs):
    """计算准确率的函数,需要封装为一个层
    """
    y_true, y_pred, mask = inputs
    _, y_pred = y_pred
    y_true = K.cast(y_true, K.floatx())
    acc = keras.metrics.sparse_categorical_accuracy(y_true, y_pred)
    acc = K.sum(acc * mask) / (K.sum(mask) + K.epsilon())
    return acc

def nsp_acc(inputs):
    """计算准确率的函数,需要封装为一个层
    """
    y_true, y_pred = inputs
    y_pred, _ = y_pred
    y_true = K.cast(y_true, K.floatx())
    acc = keras.metrics.sparse_categorical_accuracy(y_true, y_pred)
    acc = K.mean(acc)
    return acc

接下来主要看sentence loss是如何调用的

batch_token_ids = 
[[101, 6435, 7309, 3300, 2791, 1377, 809, 4692, 4692, 1408, 102, 2644, 4924, 5023, 102, 2644, 1962, 102, 7987, 1814, 103, 103, 3300, 697, 1947, 1377, 809, 4692, 102]]
batch_segment_ids = 
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
batch_target_ids = 
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1724, 2108, 0, 0, 0, 0, 0, 0, 0]]
batch_is_masked = 
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0]]
batch_nsp = 
[[0]]

这里的遮盖掩码是103,这个103会放入batch_token_ids的内容之中,batch_segment_ids正常的区分问题和答案,batch_target_ids除了带标记的为需要预测的之外,其他的为0,batch_is_masked标记遮盖了的内容为1,其他部分的内容为0,batch_nsp标记是否为下一个句子。
这里重点看一下nsp(下一个句子损失)的训练

label = 1
p = np.random.random()
if p < 0.5:
	label = 0
	item = shuffle_reply(item)

注意这里的shuffle_reply只打乱回答部分,不打乱第一句的问句
接下来由于是按照切词构成的list数组,所以随机掩码的时候random_masking即可

source_tokens,target_tokens,segment_ids = random_masking(item)

next_sentence_prediction struct

if self.with_pool:
    # pooler 提取cls向量
    x = self.apply(x, layer=Lambda, name='Pooler', function=lambda x: x[:, 0])
    pool_activation = 'tanh' if self.with_pool is True else self.with_pool
    x = self.apply(x,
                   layer=Dense,
                   name='Pooler-Dense',
                   units=self.hidden_size,
                   activation=pool_activation,
                   kernel_initializer=self.initializer)
    if self.with_nsp:
        # Next sentence prediction
        x = self.apply(x,
                       layer=Dense,
                       name='NSP-Proba',
                       units=2,
                       activation='softmax',
                       kernel_initializer=self.initializer)

    outputs.append(x)

mlm struct

if self.with_mlm:
    # Mask language model, Dense --> Norm --> Embedding --> Biasadd --> softmax
    x = outputs[0]
    x = self.apply(x,
                   layer=Dense,
                   name='MLM-Dense',
                   units=self.embedding_size,
                   activation=self.hidden_act,
                   kernel_initializer=self.initializer)
    x = self.apply(inputs=self.simplify([x, z]),
                   layer=LayerNormalization,
                   conditional=(z is not None),
                   condition_hidden_units=self.layer_norm_conds[1],
                   condition_hidden_activation=self.layer_norm_conds[2],
                   condition_hidden_initializer=self.initializer,
                   name='MLM-Norm',
                   )
    # 重用embedding-token layer
    x = self.apply(x, Embedding, 'Embedding-Token', arguments={'mode': 'dense'})
    x = self.apply(x, BiasAdd, 'MLM-Bias')
    mlm_activation = 'softmax' if self.with_mlm is True else self.with_mlm
    x = self.apply(x, Activation, 'MLM-Activation', activation=mlm_activation)
    outputs.append(x)

pair-post-training-wwm-sop.py

读取代码的部分

train_data = load_data('train')
test_data = load_data('test')

获得的train_data和test_data的结果

[['采荷一小是分校吧', '杭州市采荷第一小学钱江苑校区,杭州市钱江新城实验学校。', '是的', '这是5楼'],
 ['毛坯吗?', '因为公积金贷款贷的少', '是呢', '这套一楼带院的,您看看', '房本都是五年外的', '好的??,您先看下']
 ......
]

这里的item和label的关键在于句子的顺序换不换,我们通过循环来进行具体的查看

for is_end,item in self.get_sample(shuffle):
	label = 1
	p = np.random.random()
	if p < 0.5:
		label = 0
		item = item[::-1]

这里如果p < 0.5的时候,原文内容

item = 
[['哪里呢'], ['您', '这边', '考虑', '的', '是', '总价', '在', '70', '.', '80万', '左右', '么']]

经过变换之后的内容

[['您', '这边', '考虑', '的', '是', '总价', '在', '70', '.', '80万', '左右', '么'], ['哪里呢']]

每一个问句匹配一个答句,当p<0.5的时候句子交换顺序,当p>0.5的时候句子不交换顺序
注意next_sentence_loss中的标签内容一定要注意到
正常情况下句子的标签内容为1,如果调换顺序之后句子的标签内容为0
后续的操作大同小异,这里就不赘述了

pet post-training后续的操作

三种sentence-loss的总结

1.第一种sentence-loss:将同一对话作为同一篇文档顺序排列

比如如下对话:
采荷一小是分校吧。是的。
还有可能更换顺序:
是的。采荷一小是分校吧。

2.第二种sentence-loss:将问题作为单独文档,同一问题的所有回答作为单独文档。

比如如下的整个对话:
采荷一小是分校吧。
是的 杭州市采荷第一小学钱江苑校区,杭州市钱江新城实验学校。  这是五楼
还有可能更换顺序:
采荷一小是分校吧。
杭州市采荷第一小学钱江苑校区,杭州市钱江新城实验学校。  是的	  这是五楼

3.将问题和回答同时拆分为左右两个部分做nsp任务
这一部分跟作者交流了一下,属于作者的创意部分,也是使用pair的方法

采荷一小
是分校吧。

是
的

杭州市采荷第一小学钱江苑校区,
杭州市钱江新城实验学校。

得到的pair部分(举例子)

是分校吧。采荷一小[sep]的是[sep]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值