基于规则嵌入的论文比对系统--算法
构建训练数据+模型搭建
构建训练数据
之前我们已经分别得到了五个子空间上的训练集的论文编号的样本对(共200个,100个正样本,100个负样本)接下来需要将根据得到的论文编号的样本对进行序列化处理:
index_list=list(word_index.keys())
#子空间0的100个正样本的句子序列化(借助word_index)
list0_index_pos_first=[]
for each in Max100_0_pos_list:
temp_str=SubSpace0_dict[each[0]]
temp_list_word=temp_str.split(" ")
temp_list_index=[]
for i in temp_list_word:
if i in index_list:
temp_list_index.append(word_index[i])
list0_index_pos_first.append(temp_list_index)
pos_index_pad_array0_first = pad_sequences(list0_index_pos_first, maxlen=150)
list0_index_pos_sec=[]
for each in Max100_0_pos_list:
temp_str=SubSpace0_dict[each[1]]
temp_list_word=temp_str.split(" ")
temp_list_index=[]
for i in temp_list_word:
if i in index_list:
temp_list_index.append(word_index[i])
list0_index_pos_sec.append(temp_list_index)
pos_index_pad_array0_second = pad_sequences(list0_index_pos_sec, maxlen=150)
#子空间0的100个负样本的句子序列化(借助word_index)
list0_index_neg_first=[]
for each in Min100_0_neg_list:
temp_str=SubSpace0_dict[each[0]]
temp_list_word=temp_str.split(" ")
temp_list_index=[]
for i in temp_list_word:
if i in index_list:
temp_list_index.append(word_index[i])
list0_index_neg_first.append(temp_list_index)
neg_index_pad_array0_first = pad_sequences(list0_index_neg_first, maxlen=150)
list0_index_neg_sec=[]
for each in Min100_0_neg_list:
temp_str=SubSpace0_dict[each[1]]+" "+SubSpace0_dict[each[1]]
temp_list_word=temp_str.split(" ")
temp_list_index=[]
for i in temp_list_word:
if i in index_list:
temp_list_index.append(word_index[i])
list0_index_neg_sec.append(temp_list_index)
neg_index_pad_array0_second = pad_sequences(list0_index_neg_sec, maxlen=150)
index_pad_array0_first=np.concatenate((pos_index_pad_array0_first,neg_index_pad_array0_first),axis=0)
index_pad_array0_second=np.concatenate((pos_index_pad_array0_second,neg_index_pad_array0_second),axis=0)
因为其他同学负责的规则部分的处理还在进行中,所以这里先随机生成了三个数,后续这里会在重新处理:
threeRules=[[2]*3]*200
threeRules=np.array(threeRules)
y的处理:
pos_list=[[1,0]]*100
neg_list=[[0,1]]*100
y=pos_list+neg_list
y_train= np.asarray(y).astype('float32')
建模
print('building model...')
embedding_layer = Embedding(len(word_index) + 1,
256,
weights=[embedword_matrix],
input_length=150, trainable=True)
sequence_input1 = Input(shape=(150,), name="first_paper")
sequence_input2 = Input(shape=(150,), name="second_paper")
sequence_input3=Input(shape=(3,),name="rule")
embedded_sequences1 = embedding_layer(sequence_input1)
embedded_sequences2 = embedding_layer(sequence_input2)
LSTM_Left1 = LSTM(512, implementation=2, return_sequences=True, go_backwards=False)(embedded_sequences1)
LSTM_Right1 = LSTM(512, implementation=2, return_sequences=True, go_backwards=True)(embedded_sequences1)
concat1 = merge([LSTM_Left1,LSTM_Right1], mode='concat', concat_axis=-1)
LSTM_Left2 = LSTM(512, implementation=2, return_sequences=True, go_backwards=False)(embedded_sequences2)
LSTM_Right2 = LSTM(512, implementation=2, return_sequences=True, go_backwards=True)(embedded_sequences2)
concat2 = merge([LSTM_Left2,LSTM_Right2], mode='concat', concat_axis=-1)
z1 = Dense(512, activation='tanh')(concat1)
z2 = Dense(512, activation='tanh')(concat2)
concat=merge([z1_MaxPool,z2_MaxPool], mode='concat', concat_axis=-1)
model_final = Dense(7, activation='relu')(concat)
model_final=merge([model_final,sequence_input3],mode='concat',concat_axis=-1)
model_final = Dropout(0.5)(model_final)
model_final = Dense(2, activation='softmax')(model_final)
model = Model(input=[sequence_input1, sequence_input2,sequence_input3],
outputs=model_final)
adam = optimizers.adam(lr=0.0001)
model.compile(loss='binary_crossentropy',
optimizer=adam,
metrics=['accuracy'])
print(model.summary())
cur_time = time.strftime('%Y-%m-%d-%H-%M', time.localtime(time.time()))
checkpointer = ModelCheckpoint(filepath="model/_model-{epoch:02d}.hdf5", period=1)
model.fit([index_pad_array0_first,index_pad_array0_second,threeRules],y,
epochs=3, batch_size=128, verbose=1,
callbacks=[checkpointer])
model.save("model/final_model" + '.h5')
输入论文id,输出五个子空间的分布情况
import json
import numpy as np
import config
from gensim import corpora, models, similarities
paperID_list=[]
with open(config.ARTICLES, 'r') as f:
for index, line in enumerate(f.readlines()):
line = line.strip('\n').split("\t")
paperId=int(line[-1])
paperID_list.append(paperId)
lines=[]
with open(config.SENTENCE_TEXTCNN, 'r') as f:
for index, line in enumerate(f.readlines()):
line = line.strip('\n')
lines.append(line)
sentence_textcnn_list=[]
for eachline in lines:
temp=json.loads(eachline)
temp_arr=[]
temp_arr.append(int(temp['paper_id']))
temp_arr.append(int(temp['textcnn_label']))
sentence_textcnn_list.append(temp_arr)
sentence_textcnn_array=np.array(sentence_textcnn_list)
PaperId_Subspace=[]
for each in paperID_list:
temp_li=[each]
temp={0:0,1:0,2:0,3:0,4:0}
for id_label in sentence_textcnn_list:
if id_label[0]==each:
if id_label[1]==0:
temp[0]=temp[0]+1
elif id_label[1]==1:
temp[1]=temp[1]+1
elif id_label[1]==2:
temp[2]=temp[2]+1
elif id_label[1]==3:
temp[3]=temp[3]+1
elif id_label[1]==4:
temp[4]=temp[4]+1
temp_li.append(temp[0])
temp_li.append(temp[1])
temp_li.append(temp[2])
temp_li.append(temp[3])
temp_li.append(temp[4])
PaperId_Subspace.append(temp_li)
PaperId_Subspace_dict={}
for each in PaperId_Subspace:
temp_list=[]
temp_list.append(each[1])
temp_list.append(each[2])
temp_list.append(each[3])
temp_list.append(each[4])
temp_list.append(each[5])
PaperId_Subspace_dict[each[0]]=temp_list
np.save('data/PaperId_Subspace_dict.npy', PaperId_Subspace_dict)
PaperId_Subspace_dict = np.load('data/PaperId_Subspace_dict.npy').item()
上述内容详见:
https://blog.csdn.net/qq_43665502
到公共父节点的最短距离
CSS分类(需要构建一棵树)(全局的特征)
要求: 输入两篇论文的名字(或其他标识符)就可以获得这两篇论文的分类所在节点到公共父节点的最短距离。
首先我们研究树中求得两个节点X,Y到最近公共父节点的距离。
方法一:暴力求解
思路:
利用BFS或者DFS,从一个节点出发,沿着树的结构一层层查找,知道到达另一个节点,此时的距离为最短距离。
分析:
时间复杂度O(n)
方法二:简单LCA
思路:
d
=
d
(
x
,
r
o
o
t
)
+
d
(
y
,
r
o
o
t
)
2
∗
d
(
z
,
r
o
o
t
)
d
d=d(x,root)+d(y,root)2∗d(z,root)d
d=d(x,root)+d(y,root)2∗d(z,root)d
其中,x,y为两个待求节点,z为最近公共祖先。
我们需要提前预处理的是求得所有的
d
(
∗
,
r
o
o
t
)
d(∗,root)
d(∗,root),这样,根据公式,我们只需要知道那个节点是z就可以根据公式求出d。
LCA假设永远满足
d
(
x
,
r
o
o
t
)
<
d
(
y
,
r
o
o
t
)
d(x,root)<d(y,root)
d(x,root)<d(y,root) ,若不满足,则将x,y互换即可。
- 步骤一:预处理深度数组。
维护数组 d e p [ ∗ ] dep[∗] dep[∗],使得 d e p [ ∗ ] = d ( ∗ , r o o t ) dep[∗]=d(∗,root) dep[∗]=d(∗,root),做查表用。 - 步骤二:跳到相同高度。
当 d ( x , r o o t ) < d ( y , r o o t ) d(x,root)<d(y,root) d(x,root)<d(y,root) 时, y = f a [ y ] y=fa[y] y=fa[y]( a [ y ] a[y] a[y]数组存储的是y的直接父亲节点),即当x,y不“同级”时, 将 f a [ y ] fa[y] fa[y]赋值给y,y“向上跳了一级”。 - 步骤三:xy同时跳。
当 d ( x , r o o t ) = d ( y , r o o t ) d(x,root)=d(y,root) d(x,root)=d(y,root) 后,我们使x和y同时“向上跳”。即,当 x ! = y x!=y x!=y 时: x = f a [ x ] x=fa[x] x=fa[x], y = f a [ y ] y=fa[y] y=fa[y]。
找到z之后,就可以套公式求出距离。当然,我们也可以直接在每次跳的时候都 d = d + 1 d=d+1 d=d+1(假设边权重为1)。
分析:
步骤一时间复杂度为
O
(
n
)
O(n)
O(n),执行一次步骤二三的时间复杂度为
l
o
g
n
logn
logn ,因此,对于需求为“树结构基本不变,需要多次查询”的任务来说,时间复杂度为
O
(
q
∗
l
o
g
n
+
n
)
O(q∗logn+n)
O(q∗logn+n)(q为查询次数)
时间复杂度的分析是很有必要的,因为我们最终是以网页的形式呈现的,我们需要点击后的快速响应,而对于较为庞大的数据量来说,时间复杂度非常重要。
简单LCA对于结构较为“平衡”的树来说比较友好,但是对于比较“偏”的树来说步骤二三的时间复杂度可能上升为O(n)O(n)O(n),因此,我们需要进一步加快“跳”的过程。我们利用“倍增”的思想来实现算法的改进。
方法三:改进LCA
思路:
在简单LCA的基础上改进“跳”的过程。
- 步骤一:预处理深度数组。
同上 - 步骤二:跳到相同高度。
已知任何一个十进制的自然数都能拆分成若干个2的幂的和。比如:17= 2 4 2^4 24+ 2 0 2^0 20
,那么我们是不是可以利用这种性质进行优化?已知 f a fa fa表示的是节点i的直接父亲节点,我们假设 f a [ i ] [ j ] fa[i][j] fa[i][j]表示节点i向root方向走 2 j 2^j 2j 个边到达的父节点。我们需要做的预处理还有维护数组 f a [ i ] [ j ] fa[i][j] fa[i][j]。代码如下:
void GET(){
for(int j=1;j<=23;j++){
for(int i=1;i<=n;i++){
fa[i][j]=fa[fa[i][j-1]][j-1];
}
}
}
- 步骤三:xy同时跳。
假设树结构如下图:
则执行完步骤二时,x,yx,yx,y已经同深度了。此时我们需要做的是“同时跳”。
for(int i=23;i>=0;i--){
if(fa[x][i]!=fa[y][i]){
x=fa[x][i];
y=fa[y][i];
}
}
一旦
f
a
[
x
]
[
i
]
=
f
a
[
y
]
[
i
]
fa[x][i]=fa[y][i]
fa[x][i]=fa[y][i],那么我们有两种情况:
1.恰好是LCA
2.跳过了,是LCA的父节点
该循环运行完后的结果是图中红圈圈出的:
显而易见,我们要求的LCA是图中红色节点的直接父亲,因此
f
a
[
x
]
[
0
]
fa[x][0]
fa[x][0]或者
f
a
[
y
]
[
0
]
fa[y][0]
fa[y][0]就是LCA。
找到对应的节点后,我们就可以套公式求出ddd。
完整代码:
class Tree:
def __init__(self, n):
self.n = n
self.Graph = [[] for i in range(n + 1)]
def insert(self, x, y):
self.Graph[x].append(y)
self.Graph[y].append(x)
#
# class Node:
# def __init__(self, id):
# self.id = id
def dfs(now, father, deep):
fa[now][0] = father
dep[now] = deep
for v in tree.Graph[now]:
if v != father:
dfs(v, now, deep + 1)
def get_fa():
for j in range(1, S + 1):
for i in range(1, n + 1):
fa[i][j] = fa[fa[i][j - 1]][j - 1]
def get_same(a, t):
for i in range(S + 1):
if t & (1 << i):
a = fa[a][i]
return a
def LCA(a, b):
if dep[a] < dep[b]:
a, b = (b, a)
a = get_same(a, dep[a] - dep[b])
if a == b:
return a
for i in range(S, -1, -1):
if fa[a][i] != fa[b][i]:
a = fa[a][i]
b = fa[b][i]
return fa[a][0]
n, m, s = input().split()
n = int(n)
m = int(m)
s = int(s)
S = 25
tree = Tree(n)
fa = [[0 for _ in range(S + 1)] for __ in range(n + 1)]
dep = [0 for _ in range(n + 1)]
for _ in range(n - 1):
x, y = input().split()
x = int(x)
y = int(y)
tree.insert(x, y)
dfs(s, s, 0)
get_fa()
for _ in range(m):
x, y = input().split()
x = int(x)
y = int(y)
print(LCA(x, y))
上述内容详见:
https://me.csdn.net/QinxiEva
论文 jaccard相似度
实现:
每次输入两个论文的名字,然后通过查询node.list找到相应的id,然后在inlinks.list中找到他引用的所有论文集合,然后计算jaccard相似度。
不足之处:
在遇到大量的查询两个论文的jaccard相似度时,可能会速度比较慢,想预先计算好相似度,或者保存好每篇论文所以引用的论文集合。
上述内容详见:
https://me.csdn.net/qq_41887130