前言
這個(gè)項(xiàng)目充分利用了Google的Bert模型,這是一種基于Attention的大規(guī)模語料預(yù)訓(xùn)練模型,以及LSTM命名實(shí)體識(shí)別網(wǎng)絡(luò)。項(xiàng)目的目標(biāo)是設(shè)計(jì)一套通用的問答系統(tǒng)處理邏輯,以實(shí)現(xiàn)智能問答任務(wù)。
首先,我們采用了Bert模型,這是一種在自然語言處理領(lǐng)域非常強(qiáng)大的預(yù)訓(xùn)練模型。它具備對上下文的深刻理解和信息抽取能力,有助于理解復(fù)雜的自然語言問題。
接著,我們構(gòu)建了一個(gè)LSTM命名實(shí)體識(shí)別網(wǎng)絡(luò)。這個(gè)網(wǎng)絡(luò)可以識(shí)別文本中的命名實(shí)體,例如人名、地名、組織機(jī)構(gòu)等。這對于問答系統(tǒng)的準(zhǔn)確性非常重要,因?yàn)樗兄谧R(shí)別問題中提到的實(shí)體,并提供相關(guān)信息。
在項(xiàng)目的設(shè)計(jì)中,我們著重考慮了通用的處理邏輯,使得問答系統(tǒng)能夠適應(yīng)各種不同領(lǐng)域和主題的問題。這個(gè)通用邏輯包括問題解析、文本理解、命名實(shí)體識(shí)別、答案生成等步驟。
最終,我們成功地實(shí)現(xiàn)了一個(gè)智能問答系統(tǒng),它可以接受用戶提出的問題,并基于Bert模型和LSTM命名實(shí)體識(shí)別網(wǎng)絡(luò),理解問題并提供精確的答案。這個(gè)系統(tǒng)的通用性使得它在多個(gè)領(lǐng)域和應(yīng)用中都具有廣泛的潛力,從解答常見問題到處理專業(yè)領(lǐng)域的知識(shí)查詢。
總體設(shè)計(jì)
本部分包括系統(tǒng)整體結(jié)構(gòu)圖和系統(tǒng)流程圖。
系統(tǒng)整體結(jié)構(gòu)圖
系統(tǒng)整體結(jié)構(gòu)如圖所示。
系統(tǒng)流程圖
系統(tǒng)流程如圖所示。
Neo4j數(shù)據(jù)庫流程如圖所示。
運(yùn)行環(huán)境
本部分包括 Python 環(huán)境和服務(wù)器環(huán)境。
Python 環(huán)境
需要Python 3.7及以上配置,在Windows環(huán)境下載Anaconda完成Python所需的配置,下載地址為https://www.anaconda.com/,也可下載虛擬機(jī)在Linux環(huán)境下運(yùn)行代碼。TensorFlow 1.0, NumPy, py-Levenshtein, jieba, Scikit-learn 依據(jù)根目錄文件requirement.txt下載。
pip install -r requirement.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
服務(wù)器環(huán)境
Mac/Windows 10用戶可直接從終端通過SSH (Secure Shell)訪問服務(wù)器。Windows 7用戶可安裝OpenSSH訪問。
OpenSSH是SSH協(xié)議的免費(fèi)開源實(shí)現(xiàn),可以進(jìn)行遠(yuǎn)程控制,或在計(jì)算機(jī)之間傳送文件。實(shí)現(xiàn)此功能的傳統(tǒng)方式,會(huì)使用明文傳送密碼。缺點(diǎn):Telnet(終端仿真協(xié)議)、RCP、FTP、 Login、 RSH不安全。
OpenSSH提供了服務(wù)器端后臺(tái)程序和客戶端工具,用來加密遠(yuǎn)程控制和文件傳輸過程中的數(shù)據(jù),并由此代替原來的類似服務(wù)。下 載地址為https://www.mls-software.com/opensshd.html,下載后按照默認(rèn)完成安裝即可。打開cmd命令窗口即可遠(yuǎn)程操作,如圖所示。
模塊實(shí)現(xiàn)
本項(xiàng)目包括5個(gè)模塊:構(gòu)造數(shù)據(jù)集、識(shí)別網(wǎng)絡(luò)、命名實(shí)體糾錯(cuò)、檢索問題類別、查詢結(jié)果,下面分別給出各模塊的功能介紹及相關(guān)代碼。
1. 構(gòu)造數(shù)據(jù)集
數(shù)據(jù)是從北京郵電大學(xué)圖書館網(wǎng)站爬取,主要包含教師的電話、研究方向、性別,以及課程的學(xué)分、開設(shè)學(xué)期等信息。通過循環(huán)語句按照中文習(xí)慣將爬取的信息構(gòu)造為問句的形式,并對構(gòu)造的語句進(jìn)行標(biāo)注,無用實(shí)體標(biāo)記為0,將有用實(shí)體分為三類: TEA (老師)、COU (課程)、DIR (研究方向)。標(biāo)注方式為實(shí)體開頭B——實(shí)體類別標(biāo)注,非實(shí)體開頭為I——實(shí)體類別標(biāo)注,訓(xùn)練集數(shù)據(jù)如圖所示。
加載訓(xùn)練集相關(guān)代碼如下:
def _read_data(cls, input_file):
#讀取數(shù)據(jù)集文件
with codecs.open(input_file,'r',encoding='utf-8') as f:
lines = []
words = []
labels = []
for line in f:
contends = line.strip()
tokens = contends.split('\t')
if len(tokens) == 2:
words.append(tokens[0])
labels.append(tokens[1])
else:
if len(contends) == 0:
l=''.join([label for label in labels if len(label) > 0])
w = ' '.join([word for word in words if len(word) > 0])
lines.append([l, w])
words = []
labels = []
continue
if contends.startswith("-DOCSTART-"):
words.append('')
continue
return lines
#讀取訓(xùn)練集
def get_train_examples(self, data_dir):
return self._create_example(
self._read_data(os.path.join(data_dir, "train.txt")), "train"
)
#讀取驗(yàn)證集
def get_dev_examples(self, data_dir):
return self._create_example(
self._read_data(os.path.join(data_dir,"dev.txt")),"dev"
)
#讀取測試集
def get_test_examples(self, data_dir):
return self._create_example(
self._read_data(os.path.join(data_dir, "test.txt")), "test")
2. 識(shí)別網(wǎng)絡(luò)
使用Google的Bert,調(diào)用LSTM模型代碼,加以修改,進(jìn)行訓(xùn)練。
def train_ner(): #定義訓(xùn)練
import os
from bert_base.train.train_helper import get_args_parser
from bert_base.train.bert_lstm_ner import train
args = get_args_parser()
if True:
import sys
param_str = '\n'.join(['%20s = %s' % (k, v) for k, v in sorted(vars(args).items())])
print('usage: %s\n%20s %s\n%s\n%s\n' % (' '.join(sys.argv), 'ARG', 'VALUE', '_' * 50, param_str))
print(args)
os.environ['CUDA_VISIBLE_DEVICES'] = args.device_map
train(args=args)
#數(shù)據(jù)處理代碼
def convert_single_example(ex_index, example, label_list, max_seq_length, tokenizer, output_dir, mode):
#將一個(gè)樣本進(jìn)行分析,字和標(biāo)簽轉(zhuǎn)化為ID,結(jié)構(gòu)化到輸入特征對象中
label_map = {}
#1表示從1開始對標(biāo)簽進(jìn)行索引化
for (i, label) in enumerate(label_list, 1):
label_map[label] = i
#保存label->index 的映射
if not os.path.exists(os.path.join(output_dir, 'label2id.pkl')):
with codecs.open(os.path.join(output_dir,'label2id.pkl'),'wb')as w:
pickle.dump(label_map, w)
textlist = example.text.split(' ')
labellist = example.label.split(' ')
tokens = []
labels = []
for i, word in enumerate(textlist):
#分詞,不在BERT的vocab.txt中,則進(jìn)行WordPiece處理,分字可替換為list(input)
token = tokenizer.tokenize(word)
tokens.extend(token)
label_1 = labellist[i]
for m in range(len(token)):
if m == 0:
labels.append(label_1)
else: #一般不會(huì)出現(xiàn)else分支
labels.append("X")
#tokens = tokenizer.tokenize(example.text)
#序列截?cái)?/span>
if len(tokens) >= max_seq_length - 1:
tokens = tokens[0:(max_seq_length - 2)]
#-2的原因是因?yàn)樾蛄行枰右粋€(gè)句首和句尾標(biāo)志
labels = labels[0:(max_seq_length - 2)]
ntokens = []
segment_ids = []
label_ids = []
ntokens.append("[CLS]") #句子開始設(shè)置CLS標(biāo)志
segment_ids.append(0)
#append("O") or append("[CLS]") not sure!
label_ids.append(label_map["[CLS]"])
#O或者CLS會(huì)減少標(biāo)簽個(gè)數(shù),但句首和句尾使用不同的標(biāo)志標(biāo)注
for i, token in enumerate(tokens):
ntokens.append(token)
segment_ids.append(0)
label_ids.append(label_map[labels[i]])
ntokens.append("[SEP]") #句尾添加[SEP]標(biāo)志
segment_ids.append(0)
#append("O") or append("[SEP]") not sure!
label_ids.append(label_map["[SEP]"])
input_ids = tokenizer.convert_tokens_to_ids(ntokens)
#將序列中的字(ntokens)轉(zhuǎn)化為ID形式
input_mask = [1] * len(input_ids)
#label_mask = [1] * len(input_ids)
#使用padding
while len(input_ids) < max_seq_length:
input_ids.append(0)
input_mask.append(0)
segment_ids.append(0)
label_ids.append(0)
ntokens.append("**NULL**")
#label_mask.append(0)
#print(len(input_ids))
assert len(input_ids) == max_seq_length
assert len(input_mask) == max_seq_length
assert len(segment_ids) == max_seq_length
assert len(label_ids) == max_seq_length
#assert len(label_mask) == max_seq_length
#打印部分樣本數(shù)據(jù)信息
if ex_index < 5:
tf.logging.info("*** Example ***")
tf.logging.info("guid: %s" % (example.guid))
tf.logging.info("tokens: %s" % " ".join(
[tokenization.printable_text(x) for x in tokens]))
tf.logging.info("input_ids:%s"% " ".join([str(x) for x in input_ids]))
tf.logging.info("input_mask: %s" % " ".join([str(x) for x in input_mask]))
tf.logging.info("segment_ids: %s" % " ".join([str(x) for x in segment_ids]))
tf.logging.info("label_ids: %s" % " ".join([str(x) for x in label_ids]))
# tf.logging.info("label_mask: %s" % " ".join([str(x) for x in label_mask]))
#結(jié)構(gòu)化為一個(gè)類
feature = InputFeatures(
input_ids=input_ids,
input_mask=input_mask,
segment_ids=segment_ids,
label_ids=label_ids,
#label_mask = label_mask
)
#mode='test'的時(shí)候才有效
write_tokens(ntokens, output_dir, mode)
return feature
3. 命名實(shí)體糾錯(cuò)
對識(shí)別到的課程實(shí)體進(jìn)行糾錯(cuò),依據(jù)為course.txt
中存儲(chǔ)的所有課程全稱,采用最短編輯距離匹配法與包含法相結(jié)合。
class Select_course:
def __init__(self):
self.f = csv.reader(open('QA/dict/course.txt','r'))
self.course_name = [i[0].strip() for i in self.f]
self.led = 3
self.limit_num = 10
self.select_word = []
self.is_same = False
self.have_same_length = False
self.input_word = ''
self.is_include = False
#print(self.course_name)
#print('列表創(chuàng)建完畢....')
#包含搜索
def select_first(self, input_word):
self.select_word = []
self.is_same = False
self.is_include = False
self.have_same_length = False
self.input_word = input_word
if input_word in self.course_name:
self.is_same = True
self.select_word.append(input_word)
if self.is_same == False:
for i in self.course_name:
mark = True
for one_word in input_word:
if not one_word in i:
mark = False
if mark:
self.select_word.append(i)
if len(self.select_word) != 0:
self.is_include = True
#print('第一輪篩選:')
#print(self.select_word)
#模糊搜索
def select_second(self):
self.led = 3
if self.is_same or self.is_include:
return
for name in self.course_name:
ed = ls.distance(self.input_word, name)
if ed <= self.led:
self.led = ed
self.select_word.append(name)
select_word_copy1 = copy.deepcopy(self.select_word)
for name in select_word_copy1:
ed = ls.distance(self.input_word, name)
if ed > self.led:
self.select_word.remove(name)
if ed == self.led and len(name) == len(self.input_word):
self.hava_same_length = True
#print('第二輪篩選:')
#print(self.select_word)
對識(shí)別到的老師實(shí)體進(jìn)行糾錯(cuò),依據(jù)為teacher.csv中存儲(chǔ)的所有老師姓名全稱,基于最短編輯距離匹配法,并使糾錯(cuò)邏輯符合用戶輸入錯(cuò)誤姓名的規(guī)律。
class Select_name:
def __init__(self): #定義初始化
self.f = csv.reader(open('QA/dict/teacher.csv','r'))
self.teacher_name = [i[0] for i in self.f]
self.led = 3
self.limit_num = 10
self.select_word = []
self.have_same_length = False
self.is_same = False
self.input_word = ''
#print(self.teacher_name)
#print('列表創(chuàng)建完畢....')
def select_first(self, input_word): #定義首選
self.select_word = []
self.have_same_length = False
self.is_same = False
self.input_word = input_word
if input_word in self.teacher_name:
self.is_same = True
self.select_word.append(input_word)
if self.is_same == False:
for name in self.teacher_name:
ed = ls.distance(self.input_word, name)
if ed <= self.led:
self.led = ed
self.select_word.append(name)
select_word_copy1 = copy.deepcopy(self.select_word)
for name in select_word_copy1:
ed = ls.distance(self.input_word, name)
if ed > self.led:
self.select_word.remove(name)
if ed == self.led and len(name) == len(self.input_word):
self.hava_same_length = True
#print('第一輪篩選:')
#print(self.select_word)
return
def select_second3(self): #定義后續(xù)篩選
if self.is_same == True or len(self.input_word) != 3:
return
select_word_copy2 = copy.deepcopy(self.select_word)
if self.hava_same_length:
for name in select_word_copy2:
if len(self.input_word)!=len(name):
self.select_word.remove(name)
#print('第二輪篩選:')
#print(self.select_word)
def select_third3(self):
if self.is_same == True or len(self.input_word) != 3:
return
select_word_copy3 = copy.deepcopy(self.select_word)
self.select_word = []
for name in select_word_copy3:
if name[0] == self.input_word[0] and name[2] == self.input_word[2]:
self.select_word.append(name)
for name in select_word_copy3:
if not(name[0]==self.input_word[0]and name[2]== self.input_word[2]):
self.select_word.append(name)
#print('第三輪篩選:')
#print(self.select_word)
def limit_name_num(self):
while(len(self.select_word)>self.limit_num):
self.select_word.pop()
#print('列表大小限制:')
#print(self.select_word)
4. 檢索問題類別
以下為三個(gè)類別的關(guān)鍵詞列表:
self.direction_qwds
= [“ 做什么”“干什么”“專長”“專攻”“興趣”“方向”“方面”“研究”“科研”]
self.location_qwds
= [“地址”“地點(diǎn)”“地方”“在哪”去哪”“到哪”找到”“辦公室”“位置”“見到”]
self.telephone_qwds
= [“ 座機(jī)”“固話”“電話”“號碼”“聯(lián)系”]
通過識(shí)別到的實(shí)體類別和檢索到的關(guān)鍵詞進(jìn)行問題分類,相關(guān)代碼如下:
if self.check_words(self.direction_qwds,question)and('teacher' in types): question_type = 'teacher_direction'
question_types.append(question_type)
if self.check_words(self.location_qwds, question)and ('teacher' in types): question_type = 'teacher_location'
question_types.append(question_type)
if self.check_words(self.telephone_qwds,question)and ('teacher' in types): question_type = 'teacher_telephone'
question_types.append(question_type)
5. 查詢結(jié)果
根據(jù)識(shí)別到的具體問題類別,將問句翻譯成數(shù)據(jù)庫查詢語句,相關(guān)代碼如下:
if final_question_type == 'teacher_direction':
sql = "MATCH (m:Teacher) where m.name = '{0}' return m.name, m.research_direction".format(i)
if final_question_type == 'teacher_location':
sql = "MATCH (m:Teacher) where m.name = '{0}' return m.name, m.office_location".format(i)
if final_question_type == 'teacher_telephone':
sql = "MATCH (m:Teacher) where m.name = '{0}' return m.name, m.telephone".format(i)
#連接數(shù)據(jù)庫
def __init__(self):
self.g = Graph(
"http://10.3.55.50:7474/browser",
user="********",
password="********")
self.num_limit = 30
#查詢結(jié)果并返回編寫的模版答案語句
def search_main(self, sqls, final_question_types):
final_answers = []
temp_data = []
data = []
for i in sqls:
for one_sql in i:
temp_data.append(self.g.run(one_sql).data()[0])
#print(temp_data)
data.append(temp_data)
temp_data = []
#print(data)
temp_answer = []
answer = []
for i in zip(final_question_types, data):
for one_type_and_data in zip(i[0],i[1]):
temp_answer.append(self.answer_prettify(one_type_and_data[0],one_type_and_data[1]))
answer.append(temp_answer)
temp_answer = []
return answer
重復(fù)詢問以剔除錯(cuò)誤的備選,例如,識(shí)別到用戶輸入的老師姓名為王紅,但查詢到北京郵電大學(xué)沒有王紅,存在王春紅、王小紅,此時(shí)重復(fù)詢問用戶以確定唯一實(shí)體對象。
ask_again = ''
final_question_types = []
for i in zip(tags, pre_words):
#print(i)
if len(i[1]) == 1:
final_question_types.append(classifier.classify(text, i[0]))
final_words.append(i[1][0])
if len(i[1]) > 1:
print('>1')
if i[0] == 'teacher':
ask_again = '請問您要詢問的是哪個(gè)老師的信息:{0}'.format(','.join(i[1]))
if i[0] == 'course':
ask_again = '請問您要詢問的是哪門課程的信息:{0}'.format(','.join(i[1]))
#print(ask_again)
answer_again = input(ask_again)
final_words.append(answer_again)
final_question_types.append(classifier.classify(text, i[0]))
系統(tǒng)測試
本部分包括命名實(shí)體識(shí)別網(wǎng)絡(luò)測試和知識(shí)圖譜問答系統(tǒng)整體測試。
1. 命名實(shí)體識(shí)別網(wǎng)絡(luò)測試
輸入常用問句,從測試結(jié)果可知,測試基本能實(shí)現(xiàn)老師、課程實(shí)體的識(shí)別,模型訓(xùn)練效果如圖所示。
2. 知識(shí)圖譜問答系統(tǒng)整體測試
輸入常用問句,從問答系統(tǒng)返回的答案可知,系統(tǒng)運(yùn)行狀態(tài)良好,基本能回答用戶提出的問題,效果如圖所示。
工程源代碼下載
詳見本人博客資源下載頁文章來源:http://www.zghlxwxcb.cn/news/detail-702112.html
其它資料下載
如果大家想繼續(xù)了解人工智能相關(guān)學(xué)習(xí)路線和知識(shí)體系,歡迎大家翻閱我的另外一篇博客《重磅 | 完備的人工智能AI 學(xué)習(xí)——基礎(chǔ)知識(shí)學(xué)習(xí)路線,所有資料免關(guān)注免套路直接網(wǎng)盤下載》
這篇博客參考了Github知名開源平臺(tái),AI技術(shù)平臺(tái)以及相關(guān)領(lǐng)域?qū)<遥篋atawhale,ApacheCN,AI有道和黃海廣博士等約有近100G相關(guān)資料,希望能幫助到所有小伙伴們。文章來源地址http://www.zghlxwxcb.cn/news/detail-702112.html
到了這里,關(guān)于基于Bert+Attention+LSTM智能校園知識(shí)圖譜問答推薦系統(tǒng)——NLP自然語言處理算法應(yīng)用(含Python全部工程源碼及訓(xùn)練模型)+數(shù)據(jù)集的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!