項目:寵物醫療知識圖譜創建
項目背景
利用科技手段提高傳統寵物醫療行業的智能化水平。傳統的寵物醫療知識比較分散,專業化程度低,寵物醫院及醫生的醫療技術水平高低不一,寵主在寵物生病時不能準確判斷並採取措施等等。此項目旨在創建一套專業的醫療技術知識圖譜,包含寵物種類、品種、年齡、常見癥狀、檢查結果、處方、醫囑、診斷、疾病及疾病詳情卡片等維度。在此知識圖譜的基礎上,可以建立諸如智能診斷,臨床決策支持,智能問答等上層應用。
項目設計
- 線上病歷含有癥狀描述和診斷結果兩個欄位,可以用來創建醫療知識圖譜,可能準確度有待提高
- 初期使用少量的病歷測試數據並只使用常見癥狀和疾病兩個維度創建醫療知識圖譜,並在此基礎上創建一個簡單的demo應用,可以達到由癥狀查詢到可能犯有的疾病並按照可能機率進行排列的目標。
- 在demo應用的基礎上,開始使用大量的病歷正式數據進行模型訓練,逐步提高知識圖譜的準確度,並豐富demo應用。
- 利用一些專業化數據(醫療專家使用輔助系統填寫的疾病和癥狀對應關係/從專業化網站上爬取數據)對知識圖譜進一步訓練,儘可能的提高準確度
- 找到醫療技術人員協助測試,重複4,直到達到醫療技術人員認為可用為止。
技術選型
Python
Neo4j 圖資料庫,儲存知識圖譜,僅儲存一些關聯關係數據
Mysql 儲存癥狀歸類,疾病歸類以及一些其它信息
Flask demo應用
py2neo python來操作neo4j
jieba 分詞
項目細節
- 常見癥狀歸類獲取,最開始的時候對病歷中的所有癥狀描述進行分詞然後,計算詞頻,通過篩選高頻詞獲取常見癥狀,後來由於干擾因素太多了,這種計算詞頻的方式就放棄了。後來參考了《Differential Diagnosis in Small Animal Medicine》一書中的癥狀分類,作為該知識圖譜中的常見癥狀,後續也可以補充,保存在mysql中,包括一些附帶信息(癥狀部位等)。
- 常見疾病獲取,獲取高頻詞的方法不可用,最後採用了醫療技術部提供的三級病種統計,作為知識圖譜中的疾病,保存在mysql中,包括一些附帶信息(疾病科屬等),
- 關聯演算法,用病歷的癥狀描述與常見癥狀庫中的癥狀進行匹配,然後用該病歷的診斷與疾病庫中的疾病進行匹配,如果兩者都能匹配上,則建立一條癥狀到疾病的關聯關係,多次匹配成功則增加權重數量。
- 匹配演算法,考慮了向量的方式,由於疾病名稱和診斷描述都是很短的短句,向量判斷近似的方式誤差較大。我使用了jieba對疾病名稱和診斷採用了同一種分詞規則進行了分詞,兩者中相同的詞語數量/疾病名稱分詞的數量 = 相似比 ,相似比大於某個值以後認為兩者描述的是一種癥狀,我經過對10萬條真實病曆數據與疾病的關聯匹配得出一個結果,相似比在0.6的時候開始收斂,相似數量急劇下降,說明準確度開始達標。經過幾輪測試,最終選定0.6為標準,相似比>0.6則認為兩者相同。
部分代碼
相似比
def is_same_sort(source_data,new_data): """ 判斷一致性 :param source_data: :param new_data: :return:0表示不一致,1表示一致 """ if len(source_data) == 0 : return 0 else: proportion = round(float(len(set(source_data) & set(new_data))) / float(len(source_data)),2) if proportion >= 0.6: return 1 else: return 0
相似比分析
def sort_disease(): """ 分析相似比收斂性 :return: """ symp_querys = db.session.query(DiseaseWords).filter_by(level=2).all() symp_category = [] all_words = [] for symp in symp_querys: word = symp.word new_category_list = jieba_app(word) all_words += new_category_list symp_category.append(new_category_list) #開始分類 with open(data/cem.json) as f: cem_data = json.load(f) num = 0 count = 0 for cem in cem_data: id = cem[uid] zhusu2 = cem[uzhusu2] mainsymptom = cem[umainsymptom] yjcontent2 = cem[uyjcontent2] hjcontent2 = cem[uhjcontent2] count += 1 print count====,count,num new_category_list = jieba_app(mainsymptom) for symp in symp_category: is_same = is_same_sort(symp,new_category_list) if is_same == 1: num += 1 print num====,num
使用jieba分詞
def jieba_app(word): """ 用於疾病分類,需要排除忽略詞 :param word: :return: """ pass_word = [疾病,性,病,症,和,發性,不良,綜合,綜合征] #忽略詞 seg_list = jieba.cut(word) category_str = " ".join(seg_list) category_list = category_str.encode(utf8).split( ) new_category_list = [] for category in category_list: if category and category not in new_category_list: if category not in pass_word: new_category_list.append(category) return new_category_list
錄入neo4j
def insert_neo(): dis_querys = db.session.query(DiseaseWords).filter_by(level=2).all() symp_querys = db.session.query(SymptomWords).all() #開始分類 with open(data/cem.json) as f: cem_data = json.load(f) num = 0 #癥狀匹配 for cem in cem_data: id = cem[uid] zhusu2 = cem[uzhusu2] mainsymptom = cem[umainsymptom] yjcontent2 = cem[uyjcontent2] hjcontent2 = cem[uhjcontent2] # zhusu2_list = jieba_app(zhusu2) mainsymptom_list = jieba_app(mainsymptom) num += 1 print num===========,num temp_dict = {} #疾病匹配 for symp in symp_querys: symp_id = symp.id symp_word = symp.word # symp_word_list = jieba_app(symp.word) # symp_is_same = is_same_sort(symp_word_list,zhusu2_list) if symp_word in zhusu2: for dis in dis_querys: dis_id = dis.id dis_word = dis.word dis_word_list = jieba_app(dis.word) dis_is_same = is_same_sort(dis_word_list,mainsymptom_list) if dis_is_same == 1: #可以錄入到neo4j 建立癥狀和疾病的關聯關係 symptom = graph.find_one(label=SymptomOgm,property_key=word,property_value=symp_word) disease = graph.find_one(label=DiseaseOgm,property_key=word,property_value=dis_word) relation = graph.match_one(start_node=symptom,end_node=disease,rel_type=SYMPTOM_TO) count = 1 #修改權重 if relation: count = relation.get(count) + 1 symptom = SymptomOgm.select(graph,symp_word).first() disease = DiseaseOgm.select(graph,dis_word).first() symptom.symptom_to.add(disease,{count:count}) graph.push(symptom)
demo應用查詢
def diagnose(symp_list): """ 診斷說明 :param symp_list: 癥狀詞語 :return: 疾病結果 """ symp_source_dict = {} possible_list = [] for i in symp_list: symp_source_dict[i] = {} symptom = graph.find_one(label=SymptomOgm,property_key=word,property_value=i) disease_list = [] for sr in graph.match(start_node=symptom,rel_type=SYMPTOM_TO): disease_word = sr.end_node()[word] disease_list.append(disease_word) symp_source_dict[i][disease_word] = sr.get(count) possible_list.append(disease_list) result_list = [] for dis_list in possible_list: if result_list: result_list = list(set(result_list) & set(dis_list)) else: result_list = dis_list #計算髮生機率 final_list = [] for i in symp_source_dict.keys(): symp_source_dict[i][all] = 0 for j in symp_source_dict[i].keys(): symp_source_dict[i][all] += int(symp_source_dict[i][j]) for i in result_list: dis_dict = {} dis_dict[word] = i prop = 1 for j in symp_list: prop *= float(symp_source_dict[j][i])/symp_source_dict[j][all] if symp_source_dict[j][all]!=0 else 0 dis_dict[proportion] = prop final_list.append(dis_dict) sum_prop = 0 for i in final_list: sum_prop += i[proportion] new_list = [] for i in final_list: i[proportion] = round(i[proportion]/sum_prop,4)*100 if i[proportion] > 1: new_list.append(i) for i in new_list: dis_word = i[word] disease = graph.find_one(label=DiseaseOgm,property_key=word,property_value=dis_word) symp_list = [] for sr in graph.match(end_node=disease,rel_type=SYMPTOM_TO): symp_dict = {} symp_dict[word] = sr.start_node()[word] symp_dict[count] = sr.get(count) symp_list.append(symp_dict) symp_list.sort(key=lambda x-x[count])) i[symps] = symp_list[0:6] if len(symp_list)>6 else symp_list new_list.sort(key=lambda x-x[proportion])) return new_list
部分截圖
項目遠沒有結束,此處只是做了一個簡單的demo應用,後續知識圖譜及上層應用都需要進行優化。知識圖譜是機器學習的一個應用,這塊涉及的知識點有很多,自然語言識別,搜索查詢演算法,分詞,相似度匹配等等。數據方面,我現在使用的是現成的線上病歷,400萬條真實病歷只有80萬條有使用價值,更加準確的數據需要後期醫療專家或者專業文檔來提供。建立知識圖譜是一項細緻活,怎樣排除錯誤數據,怎麼使圖譜數據更準確等等問題。這塊我也是剛剛起步,還需要深刻學習,努力研究才行。
推薦閱讀:
※為什麼知識圖譜終於火了?|甲子光年
※報告 | 肖仰華:知識圖譜研究的回顧與展望
※AAAI 2018論文解讀 | 基於置信度的知識圖譜表示學習框架
※如何用知識圖譜識別欺詐行為