codename.py 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. # -*- coding: utf-8 -*-
  2. """``CodeNamePredict`` — 编号名称模型。
  3. Phase 5 从 ``interface/predictor.py``(约 217-794 行)迁出。
  4. 原 ``from common.Utils import *`` / ``from interface.modelFactory import *``
  5. 已替换为显式 import;``os.path.dirname(__file__)`` 路径引用替换为
  6. ``predictors._common.INTERFACE_DIR``。
  7. """
  8. from __future__ import absolute_import
  9. import os
  10. import re
  11. import json
  12. import numpy as np
  13. import tensorflow as tf
  14. import requests
  15. from BiddingKG.dl.common.logging import log
  16. from BiddingKG.dl.common.Utils import load
  17. from BiddingKG.dl.common.context_utils import find_index
  18. from BiddingKG.dl.model_runtime.viterbi import viterbi_decode
  19. from BiddingKG.dl.model_runtime.embed import getLazyLoad, embedding_word
  20. from BiddingKG.dl.interface.Entitys import Entity
  21. from BiddingKG.dl.services.external_inference.paieas import (
  22. USE_API,
  23. API_URL,
  24. USE_PAI_EAS,
  25. tf_predict_pb2,
  26. vpc_requests,
  27. codeclasses_url,
  28. codeclasses_authorization,
  29. limitRun,
  30. )
  31. from BiddingKG.dl.predictors._common import INTERFACE_DIR
  32. from keras.preprocessing.sequence import pad_sequences
  33. __all__ = ["CodeNamePredict"]
  34. # 编号名称模型
  35. class CodeNamePredict():
  36. def __init__(self,EMBED_DIM=None,BiRNN_UNITS=None,lazyLoad=getLazyLoad(),config=None):
  37. self.model = None
  38. self.MAX_LEN = None
  39. self.model_code = None
  40. if EMBED_DIM is None:
  41. self.EMBED_DIM = 60
  42. else:
  43. self.EMBED_DIM = EMBED_DIM
  44. if BiRNN_UNITS is None:
  45. self.BiRNN_UNITS = 200
  46. else:
  47. self.BiRNN_UNITS = BiRNN_UNITS
  48. self.filepath = os.path.join(INTERFACE_DIR, "..", "projectCode", "models", "model_project_"+str(self.EMBED_DIM)+"_"+str(self.BiRNN_UNITS)+".hdf5")
  49. #self.filepath = "../projectCode/models/model_project_60_200_200ep017-loss6.456-val_loss7.852-val_acc0.969.hdf5"
  50. self.filepath_code = os.path.join(INTERFACE_DIR, "..", "projectCode", "models", "model_code.hdf5")
  51. vocabpath = os.path.join(INTERFACE_DIR, "codename_vocab.pk")
  52. classlabelspath = os.path.join(INTERFACE_DIR, "codename_classlabels.pk")
  53. self.vocab = load(vocabpath)
  54. self.class_labels = load(classlabelspath)
  55. #生成提取编号和名称的正则
  56. id_PC_B = self.class_labels.index("PC_B")
  57. id_PC_M = self.class_labels.index("PC_M")
  58. id_PC_E = self.class_labels.index("PC_E")
  59. id_PN_B = self.class_labels.index("PN_B")
  60. id_PN_M = self.class_labels.index("PN_M")
  61. id_PN_E = self.class_labels.index("PN_E")
  62. self.PC_pattern = re.compile(str(id_PC_B)+str(id_PC_M)+"*"+str(id_PC_E))
  63. self.PN_pattern = re.compile(str(id_PN_B)+str(id_PN_M)+"*"+str(id_PN_E))
  64. # print("pc",self.PC_pattern)
  65. # print("pn",self.PN_pattern)
  66. self.word2index = dict((w,i) for i,w in enumerate(np.array(self.vocab)))
  67. self.inputs = None
  68. self.outputs = None
  69. self.sess_codename = tf.Session(graph=tf.Graph(),config=config)
  70. self.sess_codesplit = tf.Session(graph=tf.Graph(),config=config)
  71. self.inputs_code = None
  72. self.outputs_code = None
  73. if not lazyLoad:
  74. self.getModel()
  75. self.getModel_code()
  76. def getModel(self):
  77. '''
  78. @summary: 取得编号和名称模型
  79. '''
  80. if self.inputs is None:
  81. log("get model of codename")
  82. with self.sess_codename.as_default():
  83. with self.sess_codename.graph.as_default():
  84. meta_graph_def = tf.saved_model.loader.load(self.sess_codename, ["serve"], export_dir=os.path.join(INTERFACE_DIR, "codename_savedmodel_tf"))
  85. signature_key = tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY
  86. signature_def = meta_graph_def.signature_def
  87. self.inputs = self.sess_codename.graph.get_tensor_by_name(signature_def[signature_key].inputs["inputs"].name)
  88. self.inputs_length = self.sess_codename.graph.get_tensor_by_name(signature_def[signature_key].inputs["inputs_length"].name)
  89. self.keepprob = self.sess_codename.graph.get_tensor_by_name(signature_def[signature_key].inputs["keepprob"].name)
  90. self.logits = self.sess_codename.graph.get_tensor_by_name(signature_def[signature_key].outputs["logits"].name)
  91. self.trans = self.sess_codename.graph.get_tensor_by_name(signature_def[signature_key].outputs["trans"].name)
  92. return self.inputs,self.inputs_length,self.keepprob,self.logits,self.trans
  93. else:
  94. return self.inputs,self.inputs_length,self.keepprob,self.logits,self.trans
  95. '''
  96. if self.model is None:
  97. self.model = self.getBiLSTMCRFModel(self.MAX_LEN, self.vocab, self.EMBED_DIM, self.BiRNN_UNITS, self.class_labels,weights=None)
  98. self.model.load_weights(self.filepath)
  99. return self.model
  100. '''
  101. def getModel_code(self):
  102. if self.inputs_code is None:
  103. log("get model of code")
  104. with self.sess_codesplit.as_default():
  105. with self.sess_codesplit.graph.as_default():
  106. meta_graph_def = tf.saved_model.loader.load(self.sess_codesplit, ["serve"], export_dir=os.path.join(INTERFACE_DIR, "codesplit_savedmodel"))
  107. signature_key = tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY
  108. signature_def = meta_graph_def.signature_def
  109. self.inputs_code = []
  110. self.inputs_code.append(self.sess_codesplit.graph.get_tensor_by_name(signature_def[signature_key].inputs["input0"].name))
  111. self.inputs_code.append(self.sess_codesplit.graph.get_tensor_by_name(signature_def[signature_key].inputs["input1"].name))
  112. self.inputs_code.append(self.sess_codesplit.graph.get_tensor_by_name(signature_def[signature_key].inputs["input2"].name))
  113. self.outputs_code = self.sess_codesplit.graph.get_tensor_by_name(signature_def[signature_key].outputs["outputs"].name)
  114. self.sess_codesplit.graph.finalize()
  115. return self.inputs_code,self.outputs_code
  116. else:
  117. return self.inputs_code,self.outputs_code
  118. '''
  119. if self.model_code is None:
  120. log("get model of model_code")
  121. with self.sess_codesplit.as_default():
  122. with self.sess_codesplit.graph.as_default():
  123. self.model_code = models.load_model(self.filepath_code, custom_objects={'precision':precision,'recall':recall,'f1_score':f1_score})
  124. return self.model_code
  125. '''
  126. def getBiLSTMCRFModel(self,MAX_LEN,vocab,EMBED_DIM,BiRNN_UNITS,chunk_tags,weights):
  127. '''
  128. model = models.Sequential()
  129. model.add(layers.Embedding(len(vocab), EMBED_DIM, mask_zero=True)) # Random embedding
  130. model.add(layers.Bidirectional(layers.LSTM(BiRNN_UNITS // 2, return_sequences=True)))
  131. crf = CRF(len(chunk_tags), sparse_target=True)
  132. model.add(crf)
  133. model.summary()
  134. model.compile('adam', loss=crf.loss_function, metrics=[crf.accuracy])
  135. return model
  136. '''
  137. input = layers.Input(shape=(None,))
  138. if weights is not None:
  139. embedding = layers.embeddings.Embedding(len(vocab),EMBED_DIM,mask_zero=True,weights=[weights],trainable=True)(input)
  140. else:
  141. embedding = layers.embeddings.Embedding(len(vocab),EMBED_DIM,mask_zero=True)(input)
  142. bilstm = layers.Bidirectional(layers.LSTM(BiRNN_UNITS//2,return_sequences=True))(embedding)
  143. bilstm_dense = layers.TimeDistributed(layers.Dense(len(chunk_tags)))(bilstm)
  144. crf = CRF(len(chunk_tags),sparse_target=True)
  145. crf_out = crf(bilstm_dense)
  146. model = models.Model(input=[input],output = [crf_out])
  147. model.summary()
  148. model.compile(optimizer = 'adam', loss = crf.loss_function, metrics = [crf.accuracy])
  149. return model
  150. #根据规则补全编号或名称两边的符号
  151. def fitDataByRule(self,data):
  152. symbol_dict = {"(":")",
  153. "(":")",
  154. "[":"]",
  155. "【":"】",
  156. ")":"(",
  157. ")":"(",
  158. "]":"[",
  159. "】":"【"}
  160. leftSymbol_pattern = re.compile("[\((\[【]")
  161. rightSymbol_pattern = re.compile("[\))\]】]")
  162. leftfinds = re.findall(leftSymbol_pattern,data)
  163. rightfinds = re.findall(rightSymbol_pattern,data)
  164. result = data
  165. if len(leftfinds)+len(rightfinds)==0:
  166. return data
  167. elif len(leftfinds)==len(rightfinds):
  168. return data
  169. elif abs(len(leftfinds)-len(rightfinds))==1:
  170. if len(leftfinds)>len(rightfinds):
  171. if symbol_dict.get(data[0]) is not None:
  172. result = data[1:]
  173. else:
  174. #print(symbol_dict.get(leftfinds[0]))
  175. result = data+symbol_dict.get(leftfinds[0])
  176. else:
  177. if symbol_dict.get(data[-1]) is not None:
  178. result = data[:-1]
  179. else:
  180. result = symbol_dict.get(rightfinds[0])+data
  181. return result
  182. def decode(self,logits, trans, sequence_lengths, tag_num):
  183. viterbi_sequences = []
  184. for logit, length in zip(logits, sequence_lengths):
  185. score = logit[:length]
  186. viterbi_seq, viterbi_score = viterbi_decode(score, trans)
  187. viterbi_sequences.append(viterbi_seq)
  188. return viterbi_sequences
  189. def predict(self,list_sentences,list_entitys=None,MAX_AREA = 5000, doctitle = ""):
  190. #@summary: 获取每篇文章的code和name
  191. # pattern_score = re.compile("工程|服务|采购|施工|项目|系统|招标|中标|公告|学校|[大中小]学校?|医院|公司|分公司|研究院|政府采购中心|学院|中心校?|办公室|政府|财[政务]局|办事处|委员会|[部总支]队|警卫局|幼儿园|党委|党校|银行|分行|解放军|发电厂|供电局|管理所|供电公司|卷烟厂|机务段|研究[院所]|油厂|调查局|调查中心|出版社|电视台|监狱|水厂|服务站|信用合作联社|信用社|交易所|交易中心|交易中心党校|科学院|测绘所|运输厅|管理处|局|中心|机关|部门?|处|科|厂|集团|图书馆|馆|所|厅|楼|区|酒店|场|基地|矿|餐厅|酒店")
  192. pattern_score = re.compile('建设项目|服务项目|工程项目|工程施工|建设工程|服务中心|基础设施|物业管理|工程设计|妇幼保健|咨询服务|管理系统|管理中心|改建工程|配套工程|公安局|幼儿园|管理局|使用权|办公楼|教育局|管理处|图书馆|经营权|项目|采购|工程|改造|服务|设备|中心|医院|系统|建设|监理|施工|维修|学院|安装|设计|关于|标段|招标|技术|询价|管理|学校|小学|中学|平台|提升|设施|检测|整治|社区|装修|政府|绿化|物资|租赁|地块|医疗|编制|公开|规划|监控|教育|维护|校区|治理|升级|安置|竞价|购置|评估|勘察|承包|实验|大学|材料|生产|耗材|招租|硬化|维保|用地|消防|审计|拍卖|物业|入围|养护|机关|企业|用房|出让|资产|分局|验收|宣传|处置|校园|研究|咨询|修缮|更换|装饰|劳务|保养|物流|出租|局|院')
  193. result = []
  194. index_unk = self.word2index.get("<unk>")
  195. # index_pad = self.word2index.get("<pad>")
  196. if list_entitys is None:
  197. list_entitys = [[] for _ in range(len(list_sentences))]
  198. for list_sentence,list_entity in zip(list_sentences,list_entitys):
  199. if len(list_sentence)==0:
  200. result.append([{"code":[],"name":""}])
  201. continue
  202. doc_id = list_sentence[0].doc_id
  203. # sentences = []
  204. # for sentence in list_sentence:
  205. # if len(sentence.sentence_text)>MAX_AREA:
  206. # for _sentence_comma in re.split("[;;,\n]",sentence):
  207. # _comma_index = 0
  208. # while(_comma_index<len(_sentence_comma)):
  209. # sentences.append(_sentence_comma[_comma_index:_comma_index+MAX_AREA])
  210. # _comma_index += MAX_AREA
  211. # else:
  212. # sentences.append(sentence+"。")
  213. list_sentence.sort(key=lambda x:len(x.sentence_text),reverse=True)
  214. _begin_index = 0
  215. item = {"code":[],"name":""}
  216. code_set = set()
  217. dict_name_freq_score = dict()
  218. name_list = []
  219. while(True):
  220. MAX_LEN = len(list_sentence[_begin_index].sentence_text)
  221. if MAX_LEN>MAX_AREA:
  222. MAX_LEN = MAX_AREA
  223. _LEN = MAX_AREA//MAX_LEN
  224. #预测
  225. x = [[self.word2index.get(word,index_unk)for word in sentence.sentence_text[:MAX_AREA]]for sentence in list_sentence[_begin_index:_begin_index+_LEN]]
  226. # x = [[getIndexOfWord(word) for word in sentence.sentence_text[:MAX_AREA]]for sentence in list_sentence[_begin_index:_begin_index+_LEN]]
  227. x_len = [len(_x) if len(_x) < MAX_LEN else MAX_LEN for _x in x]
  228. x = pad_sequences(x,maxlen=MAX_LEN,padding="post",truncating="post")
  229. if USE_API:
  230. requests_result = requests.post(API_URL + "/predict_codeName", json={"inouts": x.tolist(), "inouts_len": x_len},verify=True)
  231. predict_y = json.loads(requests_result.text)['result']
  232. # print("cost_time:", json.loads(requests_result.text)['cost_time'])
  233. # print(MAX_LEN,_LEN,_begin_index)
  234. else:
  235. with self.sess_codename.as_default():
  236. t_input,t_input_length,t_keepprob,t_logits,t_trans = self.getModel()
  237. _logits,_trans = self.sess_codename.run([t_logits,t_trans],feed_dict={t_input:x,
  238. t_input_length:x_len,
  239. t_keepprob:1.0})
  240. predict_y = self.decode(_logits,_trans,x_len,7)
  241. # print('==========',_logits)
  242. '''
  243. for item11 in np.argmax(predict_y,-1):
  244. print(item11)
  245. print(predict_y)
  246. '''
  247. # print(predict_y)
  248. for sentence,predict in zip(list_sentence[_begin_index:_begin_index+_LEN],np.array(predict_y)):
  249. pad_sentence = sentence.sentence_text[:MAX_LEN]
  250. join_predict = "".join([str(s) for s in predict])
  251. # print(pad_sentence)
  252. # print(join_predict)
  253. code_x = []
  254. code_text = []
  255. pre_text = []
  256. temp_entitys = []
  257. for iter in re.finditer(self.PC_pattern,join_predict):
  258. get_len = 40
  259. if iter.span()[0]<get_len:
  260. begin = 0
  261. else:
  262. begin = iter.span()[0]-get_len
  263. end = iter.span()[1]+get_len
  264. code_x.append(embedding_word([pad_sentence[begin:iter.span()[0]],pad_sentence[iter.span()[0]:iter.span()[1]].replace(",",""),pad_sentence[iter.span()[1]:end]],shape=(3,get_len,60)))
  265. code_text.append(pad_sentence[iter.span()[0]:iter.span()[1]].replace(",", ""))
  266. pre_text.append(pad_sentence[begin:iter.span()[0]])
  267. _entity = Entity(doc_id=sentence.doc_id,entity_id="%s_%s_%s_%s"%(sentence.doc_id,sentence.sentence_index,iter.span()[0],iter.span()[1]),entity_text=pad_sentence[iter.span()[0]:iter.span()[1]].replace(",",""),entity_type="code",sentence_index=sentence.sentence_index,begin_index=0,end_index=0,wordOffset_begin=iter.span()[0],wordOffset_end=iter.span()[1],in_attachment=sentence.in_attachment)
  268. temp_entitys.append(_entity)
  269. #print("code",code_text)
  270. if len(code_x)>0:
  271. code_x = np.transpose(np.array(code_x,dtype=np.float32),(1,0,2,3))
  272. if USE_PAI_EAS:
  273. request = tf_predict_pb2.PredictRequest()
  274. request.inputs["input0"].dtype = tf_predict_pb2.DT_FLOAT
  275. request.inputs["input0"].array_shape.dim.extend(np.shape(code_x[0]))
  276. request.inputs["input0"].float_val.extend(np.array(code_x[0],dtype=np.float64).reshape(-1))
  277. request.inputs["input1"].dtype = tf_predict_pb2.DT_FLOAT
  278. request.inputs["input1"].array_shape.dim.extend(np.shape(code_x[1]))
  279. request.inputs["input1"].float_val.extend(np.array(code_x[1],dtype=np.float64).reshape(-1))
  280. request.inputs["input2"].dtype = tf_predict_pb2.DT_FLOAT
  281. request.inputs["input2"].array_shape.dim.extend(np.shape(code_x[2]))
  282. request.inputs["input2"].float_val.extend(np.array(code_x[2],dtype=np.float64).reshape(-1))
  283. request_data = request.SerializeToString()
  284. list_outputs = ["outputs"]
  285. _result = vpc_requests(codeclasses_url, codeclasses_authorization, request_data, list_outputs)
  286. if _result is not None:
  287. predict_code = _result["outputs"]
  288. else:
  289. with self.sess_codesplit.as_default():
  290. with self.sess_codesplit.graph.as_default():
  291. predict_code = self.getModel_code().predict([code_x[0],code_x[1],code_x[2]])
  292. else:
  293. with self.sess_codesplit.as_default():
  294. with self.sess_codesplit.graph.as_default():
  295. inputs_code,outputs_code = self.getModel_code()
  296. predict_code = limitRun(self.sess_codesplit,[outputs_code],feed_dict={inputs_code[0]:code_x[0],inputs_code[1]:code_x[1],inputs_code[2]:code_x[2]})[0]
  297. #predict_code = self.sess_codesplit.run(outputs_code,feed_dict={inputs_code[0]:code_x[0],inputs_code[1]:code_x[1],inputs_code[2]:code_x[2]})
  298. #predict_code = self.getModel_code().predict([code_x[0],code_x[1],code_x[2]])
  299. for h in range(len(predict_code)):
  300. if predict_code[h][0]>0.5:
  301. the_code = self.fitDataByRule(code_text[h])
  302. # print(the_code)
  303. #add code to entitys
  304. list_entity.append(temp_entitys[h])
  305. in_att = 1 if temp_entitys[h].in_attachment else 0 # 是否在附件
  306. if re.search(',|/|;|、|,', the_code) and len(the_code)>25:
  307. for it in re.split(',|/|;|、|,', the_code):
  308. if len(it) > 8:
  309. if re.search("(项目编[号码]|招标编[号码]):?$", pre_text[h]):
  310. item['code'].append((it, in_att, 0, sentence.sentence_index))
  311. elif re.search('采购(计划)?编[号码]:?$', pre_text[h]):
  312. item['code'].append((it, in_att, 1, sentence.sentence_index))
  313. elif re.search('(询价|合同)编[号码]:?$', pre_text[h]):
  314. item['code'].append((it, in_att, 2, sentence.sentence_index))
  315. elif re.search('(询价|合同|采购|招标|项目)[单标]号:?$', pre_text[h]):
  316. item['code'].append((it, in_att, 2.5, sentence.sentence_index))
  317. else:
  318. item['code'].append((it, in_att, 3, sentence.sentence_index))
  319. elif len(item['code']) > 0:
  320. new_it = item['code'][-1][0] + re.search(',|/|;|、|,', the_code).group(0) + it
  321. if re.search("(项目编[号码]|招标编[号码]):?$", pre_text[h]):
  322. item['code'][-1] = (new_it, in_att, 0, sentence.sentence_index)
  323. elif re.search('采购(计划)?编[号码]:?$', pre_text[h]):
  324. item['code'][-1] = (new_it, in_att, 1, sentence.sentence_index)
  325. elif re.search('(询价|合同)编[号码]:?$', pre_text[h]):
  326. item['code'][-1] = (new_it, in_att, 2, sentence.sentence_index)
  327. elif re.search('(询价|合同|采购|招标|项目)[单标]号:?$', pre_text[h]):
  328. item['code'].append((new_it, in_att, 2.5, sentence.sentence_index))
  329. else:
  330. item['code'][-1] = (new_it, in_att, 3, sentence.sentence_index)
  331. else:
  332. if re.search("(项目编[号码]|招标编[号码]):?$", pre_text[h]):
  333. item['code'].append((the_code, in_att, 0, sentence.sentence_index))
  334. elif re.search('采购(计划)?编[号码]:?$', pre_text[h]):
  335. item['code'].append((the_code, in_att, 1, sentence.sentence_index))
  336. elif re.search('(询价|合同)编[号码]:?$', pre_text[h]):
  337. item['code'].append((the_code, in_att, 2, sentence.sentence_index))
  338. elif re.search('(询价|合同|采购|招标|项目)[单标]号:?$', pre_text[h]):
  339. item['code'].append((the_code, in_att, 2.5, sentence.sentence_index))
  340. else:
  341. item['code'].append((the_code, in_att, 3, sentence.sentence_index))
  342. break
  343. else:
  344. if len(the_code)<5: # 避免510545935 这种把 招标项目编[号码]:2024年第二期 只提取2024
  345. continue
  346. # item['code'].append(the_code)
  347. if re.search("(项目编[号码]|招标编[号码]):?$", pre_text[h]):
  348. item['code'].append((the_code, in_att, 0, sentence.sentence_index))
  349. elif re.search('采购(计划)?编[号码]:?$', pre_text[h]):
  350. item['code'].append((the_code, in_att, 1, sentence.sentence_index))
  351. elif re.search('(询价|合同)编[号码]:?$', pre_text[h]):
  352. item['code'].append((the_code, in_att, 2, sentence.sentence_index))
  353. elif re.search('(询价|合同|采购|招标|项目)[单标]号:?$', pre_text[h]):
  354. item['code'].append((the_code, in_att, 2.5, sentence.sentence_index))
  355. else:
  356. item['code'].append((the_code, in_att, 3, sentence.sentence_index))
  357. for iter in re.finditer(self.PN_pattern,join_predict):
  358. raw_name = pad_sentence[iter.start():iter.end()]
  359. _name = self.fitDataByRule(pad_sentence[iter.span()[0]:iter.span()[1]])
  360. if len(_name)>200: # 避免模型预测类似 202750503 这种很长重复字很多的错误项目名称
  361. continue
  362. elif '公司:你单位在' in _name: # 避免类似 339900030 这种作为项目名称,导致中标角色作为招标角色
  363. continue
  364. elif _name.endswith('公司') and len(_name)<20: # 修复 456957250 雄县辉茂纸塑包装制品销售有限公司 作为项目名称
  365. continue
  366. elif re.search('(工程|项目)$', raw_name) and re.match('(有限)?公司', pad_sentence[iter.end():]) and re.search(
  367. '(项目|工程)名称:', pad_sentence[max(0, iter.start()-8):iter.start()])==None: # 修复 150904150 威海经济技术开发区园林绿化工程公司 中的 威海经济技术开发区园林绿化工程 为项目名称
  368. continue
  369. elif re.search('恭喜中标', raw_name): # 修复 536826334 type:name,text:浙江德邦印务有限公司恭喜中标标段1
  370. continue
  371. #add name to entitys
  372. _entity = Entity(doc_id=sentence.doc_id,entity_id="%s_%s_%s_%s"%(sentence.doc_id,sentence.sentence_index,iter.span()[0],iter.span()[1]),entity_text=_name,entity_type="name",sentence_index=sentence.sentence_index,begin_index=0,end_index=0,wordOffset_begin=iter.span()[0],wordOffset_end=iter.span()[1],in_attachment=sentence.in_attachment)
  373. list_entity.append(_entity)
  374. # w = 1 if re.search('(项目|工程|招标|合同|标项|标的|计划|询价|询价单|询价通知书|申购)(名称|标题|主题)[::\s]', pad_sentence[iter.span()[0]-10:iter.span()[0]])!=None else 0.5
  375. # w = 1 if re.search('(项目|工程|招标|采购|合同|标项|标的|计划|询价|询价单|询价通知书|申购)(名称|标题|主题|项目)[::\s]', pad_sentence[iter.span()[0]-10:iter.span()[0]])!=None else 0.5
  376. # if _name not in dict_name_freq_score:
  377. # # dict_name_freq_score[_name] = [1,len(re.findall(pattern_score,_name))+len(_name)*0.1]
  378. # len_name = len(_name) if len(_name) <50 else 100-len(_name) # 2023/03/02 超出50长度的逐渐递减
  379. # dict_name_freq_score[_name] = [1, (len(re.findall(pattern_score, _name)) + len_name * 0.05), w]
  380. # else:
  381. # dict_name_freq_score[_name][0] += 1
  382. # if w > dict_name_freq_score[_name][2]:
  383. # dict_name_freq_score[_name][2] = w
  384. score = 1
  385. if re.search('项目名称[::\s]', pad_sentence[iter.span()[0]-8:iter.span()[0]]):
  386. score += 1
  387. elif re.search('(工程|招标|采购|合同|标项|标的|计划|询价|询价[单书]|询价通知书|申购)(名称|标题|主题|项目)[::\s]', pad_sentence[iter.span()[0]-8:iter.span()[0]]):
  388. score += 0.5
  389. elif re.search('(招标|采购)(条件|范围|内容)|合同包|合同段|采购包', pad_sentence[iter.span()[0]-20:iter.span()[0]]):
  390. score += 0.49
  391. if raw_name in doctitle:
  392. score += 0.4 + len(raw_name)/len(doctitle)/10
  393. score += (1-sentence.sentence_index/len(list_sentence))/10
  394. if sentence.in_attachment:
  395. score /= 2
  396. name_list.append((_name, score))
  397. '''
  398. for iter in re.finditer(self.PN_pattern,join_predict):
  399. print("name-",self.fitDataByRule(pad_sentence[iter.span()[0]:iter.span()[1]]))
  400. if item[1]['name']=="":
  401. for iter in re.finditer(self.PN_pattern,join_predict):
  402. #item[1]['name']=item[1]['name']+";"+self.fitDataByRule(pad_sentence[iter.span()[0]:iter.span()[1]])
  403. item[1]['name']=self.fitDataByRule(pad_sentence[iter.span()[0]:iter.span()[1]])
  404. break
  405. '''
  406. if _begin_index+_LEN>=len(list_sentence):
  407. break
  408. _begin_index += _LEN
  409. list_name_freq_score = []
  410. # print('模型预测项目名称:', dict_name_freq_score)
  411. # 2020/11/23 大网站规则调整
  412. # if len(dict_name_freq_score) == 0:
  413. if len(name_list) == 0:
  414. # name_re1 = '(项目|工程|招标|合同|标项|标的|计划|询价|询价单|询价通知书|申购)(名称|标题|主题)[::\s]+([^,。:;]{2,60})[,。]'
  415. name_re1 = '(项目|工程|招标|采购(条目)?|合同|标项|标的|计划|询价|询价单|询价通知书|申购单|申购)(名称|标名|标题|主题)[::\s]+(?P<name>[^,。:;]{2,60})[,。]'
  416. name_re2 = '(合同|采购)包\d((?P<name>[^,。:;]{2,60}))[:,。]' # 20241202 补充合同包 包名表达 558410976
  417. for sentence in list_sentence:
  418. # pad_sentence = sentence.sentence_text
  419. othername = re.search(name_re1, sentence.sentence_text)
  420. if othername == None:
  421. othername = re.search(name_re2, sentence.sentence_text)
  422. if othername != None:
  423. project_name = othername.group('name')
  424. if re.search('[\u4e00-\u9fa5]+', project_name) == None: # 没有中文的项目名称去除
  425. # log('没有中文的项目名称去除')
  426. continue
  427. beg = find_index([project_name], sentence.sentence_text)[0]
  428. end = beg + len(project_name)
  429. raw_name = sentence.sentence_text[beg:end]
  430. _name = self.fitDataByRule(sentence.sentence_text[beg:end])
  431. # print('规则召回项目名称:', _name)
  432. # add name to entitys
  433. _entity = Entity(doc_id=sentence.doc_id, entity_id="%s_%s_%s_%s" % (
  434. sentence.doc_id, sentence.sentence_index, beg, end), entity_text=_name,
  435. entity_type="name", sentence_index=sentence.sentence_index, begin_index=0,
  436. end_index=0, wordOffset_begin=beg, wordOffset_end=end,in_attachment=sentence.in_attachment)
  437. list_entity.append(_entity)
  438. # w = 1
  439. # if _name not in dict_name_freq_score:
  440. # # dict_name_freq_score[_name] = [1,len(re.findall(pattern_score,_name))+len(_name)*0.1]
  441. # dict_name_freq_score[_name] = [1, (len(re.findall(pattern_score, _name)) + len(_name) * 0.05), w]
  442. # else:
  443. # dict_name_freq_score[_name][0] += 1
  444. score = 1
  445. if re.search('项目名称[::\s]', sentence.sentence_text[beg - 8:beg]):
  446. score += 1
  447. elif re.search('(工程|招标|采购|合同|标项|标的|计划|询价|询价[单书]|询价通知书|申购)(名称|标题|主题|项目)[::\s]',
  448. sentence.sentence_text[beg - 8:beg]):
  449. score += 0.5
  450. elif re.search('(招标|采购)(条件|范围|内容)|合同包|合同段|采购包', sentence.sentence_text[beg - 20:beg]):
  451. score += 0.49
  452. if raw_name in doctitle:
  453. score += 0.4 + len(raw_name) / len(doctitle) / 10
  454. score += (1 - sentence.sentence_index / len(list_sentence)) / 10
  455. if sentence.in_attachment:
  456. score /= 2
  457. name_list.append((_name, score))
  458. # othername = re.search(name_re1, sentence.sentence_text)
  459. # if othername != None:
  460. # _name = othername.group(3)
  461. # if _name not in dict_name_freq_score:
  462. # dict_name_freq_score[_name] = [1, len(re.findall(pattern_score, _name)) + len(_name) * 0.1]
  463. # else:
  464. # dict_name_freq_score[_name][0] += 1
  465. # for _name in dict_name_freq_score.keys():
  466. # list_name_freq_score.append([_name,dict_name_freq_score[_name]])
  467. # # print(list_name_freq_score)
  468. # if len(list_name_freq_score)>0:
  469. # list_name_freq_score.sort(key=lambda x:x[1][0]*x[1][1]*x[1][2],reverse=True)
  470. # item['name'] = list_name_freq_score[0][0]
  471. # for it in list_name_freq_score:
  472. # print('项目名称及分值:',it[0],it[1], it[1][0]*it[1][1])
  473. # if list_name_freq_score[0][1][0]>1:
  474. # item[1]['name'] = list_name_freq_score[0][0]
  475. # else:
  476. # list_name_freq_score.sort(key=lambda x:x[1][1],reverse=True)
  477. # item[1]["name"] = list_name_freq_score[0][0]
  478. if name_list:
  479. name_list.sort(key=lambda x: x[1], reverse=True)
  480. item['name'] = name_list[0][0]
  481. #下面代码加上去用正则添加某些识别不到的项目编号
  482. if item['code'] == []:
  483. for sentence in list_sentence:
  484. # othercode = re.search('(采购计划编号|询价编号)[\))]?[::]?([\[\]a-zA-Z0-9\-]{5,30})', sentence.sentence_text)
  485. # if othercode != None:
  486. # item[1]['code'].append(othercode.group(2))
  487. # 2020/11/23 大网站规则调整
  488. in_att = 1 if sentence.in_attachment else 0
  489. othercode = re.search('(项目|采购|招标|品目|询价|竞价|询价[单书]|磋商|订单|账单|交易|文件|计划|场次|标的|标段|标包|分包|标段\(包\)|招标文件|合同|通知书|公告|工程|寻源|标书|包件|谈判|申购)(单据?号|编号|标号|编码|代码|备案号|号)[::\s]+(?P<code>[^,。;:、]{6,30}[a-zA-Z0-9\号期])[\),。\u4e00-\u9fa5]', sentence.sentence_text)
  490. if othercode != None:
  491. # item['code'].append(othercode.group('code'))
  492. if re.search("(项目编[号码]|招标编[号码]):?", othercode.group(0)):
  493. item['code'].append((othercode.group('code'), in_att, 0, sentence.sentence_index))
  494. elif re.search('采购(计划)?编[号码]:?', othercode.group(0)):
  495. item['code'].append((othercode.group('code'), in_att, 1, sentence.sentence_index))
  496. elif re.search('(询价|合同)编[号码]:?', othercode.group(0)):
  497. item['code'].append((othercode.group('code'), in_att, 2, sentence.sentence_index))
  498. elif re.search('(询价|合同|采购|招标|项目)[单标]号:?', othercode.group(0)):
  499. item['code'].append((othercode.group('code'), in_att, 2.5, sentence.sentence_index))
  500. else:
  501. item['code'].append((othercode.group('code'), in_att, 3, sentence.sentence_index))
  502. # print('规则召回项目编号:', othercode.group('code'))
  503. # item['code'] = [code for code in item['code'] if len(code)<500]
  504. # item['code'].sort(key=lambda x:len(x),reverse=True)
  505. item['code'] = [code for code in item['code'] if len(code[0]) < 500]
  506. item['code'].sort(key=lambda x: [x[1],x[2],x[3]])
  507. code_list = []
  508. for it in item['code']:
  509. if it[0] not in code_list:
  510. code_list.append(it[0])
  511. # item['code'] = [it[0] for it in item['code']]
  512. item['code'] = code_list
  513. result.append(item)
  514. list_sentence.sort(key=lambda x: x.sentence_index,reverse=False)
  515. return result
  516. '''
  517. #当数据量过大时会报错
  518. def predict(self,articles,MAX_LEN = None):
  519. sentences = []
  520. for article in articles:
  521. for sentence in article.content.split("。"):
  522. sentences.append([sentence,article.id])
  523. if MAX_LEN is None:
  524. sent_len = [len(sentence[0]) for sentence in sentences]
  525. MAX_LEN = max(sent_len)
  526. #print(MAX_LEN)
  527. #若为空,则直接返回空
  528. result = []
  529. if MAX_LEN==0:
  530. for article in articles:
  531. result.append([article.id,{"code":[],"name":""}])
  532. return result
  533. index_unk = self.word2index.get("<unk>")
  534. index_pad = self.word2index.get("<pad>")
  535. x = [[self.word2index.get(word,index_unk)for word in sentence[0]]for sentence in sentences]
  536. x = pad_sequences(x,maxlen=MAX_LEN,padding="post",truncating="post")
  537. predict_y = self.getModel().predict(x)
  538. last_doc_id = ""
  539. item = []
  540. for sentence,predict in zip(sentences,np.argmax(predict_y,-1)):
  541. pad_sentence = sentence[0][:MAX_LEN]
  542. doc_id = sentence[1]
  543. join_predict = "".join([str(s) for s in predict])
  544. if doc_id!=last_doc_id:
  545. if last_doc_id!="":
  546. result.append(item)
  547. item = [doc_id,{"code":[],"name":""}]
  548. code_set = set()
  549. code_x = []
  550. code_text = []
  551. for iter in re.finditer(self.PC_pattern,join_predict):
  552. get_len = 40
  553. if iter.span()[0]<get_len:
  554. begin = 0
  555. else:
  556. begin = iter.span()[0]-get_len
  557. end = iter.span()[1]+get_len
  558. code_x.append(embedding_word([pad_sentence[begin:iter.span()[0]],pad_sentence[iter.span()[0]:iter.span()[1]],pad_sentence[iter.span()[1]:end]],shape=(3,get_len,60)))
  559. code_text.append(pad_sentence[iter.span()[0]:iter.span()[1]])
  560. if len(code_x)>0:
  561. code_x = np.transpose(np.array(code_x),(1,0,2,3))
  562. predict_code = self.getModel_code().predict([code_x[0],code_x[1],code_x[2]])
  563. for h in range(len(predict_code)):
  564. if predict_code[h][0]>0.5:
  565. the_code = self.fitDataByRule(code_text[h])
  566. if the_code not in code_set:
  567. code_set.add(the_code)
  568. item[1]['code'] = list(code_set)
  569. if item[1]['name']=="":
  570. for iter in re.finditer(self.PN_pattern,join_predict):
  571. #item[1]['name']=item[1]['name']+";"+self.fitDataByRule(pad_sentence[iter.span()[0]:iter.span()[1]])
  572. item[1]['name']=self.fitDataByRule(pad_sentence[iter.span()[0]:iter.span()[1]])
  573. break
  574. last_doc_id = doc_id
  575. result.append(item)
  576. return result
  577. '''