开篇
这篇是基于k-nrm改进的一篇论文提出的方法。同样的作者,前后隔了一年左右的时间。前面讲k-nrm的博客我没有放出代码,这一篇我会放出一个详细的模型解读源码以供大家参考。本博客没有使用任何公司的数据,也未集成到公司的任何系统中,属于学术型文章开源。
Conv-knrm
Conv-knrm相比k-nrm,最大的改变就是它添加了n-gram的卷积,增加了原先模型的层次,这里有一个好处就是它能够捕捉更加细微的语义实体,交叉的粒度也更加细。这边我放上它完整的模型图,如果大家想更加细致的了解它的原理,建议阅读它的原始论文Convolutional Neural Networks for Soft-Matching N-Grams in Ad-hoc Search。下面我简单的讲一下整体的模型。
word embedding
首先还是embedding层,之前的博客已经讲过,就是把我们的句子转换成我们词向量组合的一个矩阵,这样就基本形成了我们的图,编程的时候要注意的是,我们期待卷积的张量是三维了,是带有通道数,原始的输入是要扩展维度的。这点在tensorflow里面尤其要注意。
query = Input(name='query', shape=(text1_maxlen,))
doc = Input(name='doc', shape=(text2_maxlen,))
embedding = Embedding(vocab_size, embed_size, weights=[embed],
trainable=True)
##这里的embed是预训练好的词向量
q_embed = embedding(query)
d_embed = embedding(doc)
Convolutional Layer
就是单纯的卷积层,图中所示的是1-gram,2-gram大小的两种卷积核。
q_convs = []
d_convs = []
for i in range(max_ngram):
c = keras.layers.Conv1D(num_filters, i + 1, activation='relu', padding='same')
q_convs.append(c(q_embed))
d_convs.append(c(d_embed))
这里使用的卷积和tensorflow不一样,它是把词向量的维度当做通道数的,所以使用的卷积是1d的。
Cross-match Layer
这里搞了一波交叉组合矩阵,计算公式依旧是cos。
for qi in range(max_ngram):
for di in range(max_ngram):
# if not corssmatch, then do not match n-gram with different length
if not if_crossmatch and qi != di:
print("non cross")
continue
q_ngram = q_convs[qi]
d_ngram = d_convs[di]
mm = Dot(axes=[2, 2], normalize=True)([q_ngram, d_ngram])
kernel pooling
这个在之前的博客里面详细的解释过,包括它公式,这里不多赘述,我们代码里面见。
def Kernel_layer(mu, sigma):
def kernel(x):
return K.tf.exp(-0.5 * (x - mu) * (x - mu) / sigma / sigma)
return Activation(kernel)
for qi in range(max_ngram):
for di in range(max_ngram):
# if not corssmatch, then do not match n-gram with different length
if not if_crossmatch and qi != di:
print("non cross")
continue
q_ngram = q_convs[qi]
d_ngram = d_convs[di]
mm = Dot(axes=[2, 2], normalize=True)([q_ngram, d_ngram])
for i in range(kernel_num):
mu = 1. / (kernel_num - 1) + (2. * i) / (kernel_num - 1) - 1.0