Redian新闻
>
【手撕代码】当我让深度学习模型吃下一本医学书后,他竟学会了如何“看病”!

【手撕代码】当我让深度学习模型吃下一本医学书后,他竟学会了如何“看病”!

公众号新闻

大家好,我是鑫仔。就在3月15日, GPT-4震撼发布。为了让大家感受一下语言模型的魅力,就在那一天鑫仔决定以最优雅的方式为大家手撕一下ChatGPT模型(不是)——的老祖宗——Word2vec。

目的很简单,就是让模型吃下一本书《临床药物治疗学》,我们可以输入一些疾病或药物,找到与之相近的药物或疾病,谁曾想这一优雅就优雅了将近两周…
不过呢,跑出结果的那一刻,心中还是喜出望外的。现在,让我们抛开复杂的数学原理,单纯的手撕python代码,去实现这个任务。同时呢,大家完全可以换一本其它领域的医学书来少量参数,自己也玩一下。
那么我们现在就开始吧!

医学语料文件获取

step1: 将pdf格式的《临床药物治疗学》转换为txt格式并保存   

这本《临床药物治疗学》是我在这个网站(https://github.com/scienceasdf/medical-books/releases/latest)下载的,感谢作者的辛勤付出,我也建议大家下载这个网站的其它教材玩一玩这个模型。


向上滑动阅览


1.import pdfplumber
 2.import os
 3.from tqdm import tqdm
 4.  
 5.# 将当前工作目录修改为指定目录
 6. path ='/home/jinlimed/Downloads'
 7. os.chdir(path)
 8.  
 9.# 设置文件名前缀
10. phar ='pharmacotherapeutics'
   

向上滑动阅览


 1.# 打开PDF文件并提取页数
 2.with pdfplumber.open(phar +".pdf")as pdf:
 3.     page_num = len(pdf.pages)
 4.    
 5.     # 遍历PDF文件中的所有页面
 6.     for i in tqdm(range(page_num), desc="正在读写", unit="页"):
 7.         # 设置当前页面
 8.         page = pdf.pages[i]
 9.        
10.         # 提取当前页面文本
11.         text = page.extract_text()
12.        
13.         # 如果文本不为空,则将其写入TXT文件
14.         if text !=None:
15.             # 打开TXT文件
16.             with open(phar +".txt","a", encoding="utf-8")as f:
17.                 # 写入TXT文件
18.                 f.write(text)
19.                
20.# 写入完毕
21.print("写入完成")


下方会显示进度条:
这样我们的txt文件就下载完成啦!

文档预处理

step2: 将每一行文本依次储存到一个list中  

         

向上滑动阅览


 1.# 定义存储成list后的文本列表
 2. fileTrainRead =[]
 3.  
 4.# 打开txt格式文档
 5.with open(phar+'.txt', encoding='utf-8')as txt:
 6.  
 7.     # 循环txt文件的每一行
 8.     for words in txt:
 9.        
10.         # 对末尾出现换行符\n的行进行拆分
11.         words.split(r'\n')
12.        
13.         # 去掉附录和参考文献
14.         if'附录 参考⽂献'in words:
15.             break
16.        
17.         # 逐行拼接
18.         fileTrainRead.append(words)
19.            
20.# print(len(fileTrainRead))
21.# print(type(fileTrainRead))
22.# # 打印后200行,看下附录有没有被删掉
23.# print(fileTrainRead[-200:])

step3: 去除数字,字母,标点符号,以及特殊字符  

     

向上滑动阅览


 1.import re
 2.importstring
 3.fromstringimport punctuation
 4.  
 5.# 定义标点符号集合,去掉星号
 6. punctuation_set =set(string.punctuation.replace('*',''))
 7.  
 8.# 定义清洗后的文本列表
 9. fileTrainClean =[]
10.  
11.# 遍历原始文本列表
12.for i in fileTrainRead:
13.     # 替换换行符为空格
14.     tmp = re.sub(r'\n+',' ', i)
15.    
16.     # 替换数字和英文字母为空格
17.     tmp = re.sub(r'[a-zA-Z0-9]',' ', tmp)
18.     # print(tmp)打印看一下有哪些没去掉的字符,手动去除
19.    
20.     # 替换多余字符为空格
21.     tmp = re.sub(r'[:∶( . )《》①②③④⑤⑥⑦⑧⑨⑩⑪;“”〜。,+、%‰℃ⅠⅡⅢⅣⅦⅨⅩ【】fifl  √μ><≥α β γ 𝑡 ’?]',' ', tmp)
22.    
23.     # 替换标点符号为空格
24.     tmp =''.join([if c notin punctuation_set else' 'for c in tmp])
25.    
26.     # 将多个空格替换成一个空格
27.     tmp =' '.join(tmp.split())
28.    
29.     # 如果清洗后的文本不为空,则添加到列表中
30.     if tmp !='':
31.         fileTrainClean.append(tmp)
32.# print(fileTrainClean)
            

文档分词

step4 停用词设置   

停用词文档下载地址:https://github.com/goto456/stopwords


向上滑动阅览


 1.# 定义停用词列表
 2. stopwords =[]
 3.  
 4.# 打开停用词文件
 5.with open('stopwords.txt','r', encoding='utf-8')as f:
 6.     # 遍历文件中的每一行
 7.     for words in f:
 8.         # 将每一行按照换行符进行拆分
 9.         words = words.strip()
10.        
11.         # 替换多余换行符为空格
12.         words = re.sub(r'\n+',' ', words)
13.        
14.         # 将清洗后的文本添加到停用词列表中
15.         stopwords.append(words)

      

step5.1 利用jieba进行文档分词  

         
分词算法对医学人还是比较玄学,这个有时间深入了解一下,目前用的最多的总问分词工具就是jieba,其它的分词工具还有pynlpir,清华的THULAC,哈工大LTP等。
我们先来试试用jieba进行分词(官网:https://github.com/fxsjy/jieba)!
其中,本段代码的自用词典下载地址为(http://thuocl.thunlp.org/message)。

向上滑动阅览


1.import jieba
 2.  
 3.# 定义分词后的列表
 4. fileTrainSeg =[]
 5.  
 6.# 定义用户自定义词典的文件名
 7. file_userDict ='THUOCL_medical.txt'
 8.  
 9.# 加载用户自定义词典
10. jieba.load_userdict(file_userDict)
11.  
12.# 遍历每一篇文章
13.for words in fileTrainClean:
14.     # 使用jieba分词工具对当前文章进行分词
15.     tmp = list(jieba.cut(words, HMM =False, cut_all =False))
16.            
17.     # 定义结果字符串
18.     result =''
19.    
20.     # 遍历当前文章的每一个词语
21.     for i, word in enumerate(tmp):
22.         # 如果当前词语不为空字符串、不在停用词列表中,则将其添加到结果字符串中
23.         if word.strip()and word notin stopwords:
24.             result += word +'/'
25.        
26.         # 处理分词结果中最后一个词语的斜杠问题
27.         if i == len(tmp)-1and result.endswith('/'):
28.             result = result[:-1]
29.            
30.     # 如果分词结果不为空,则将其添加到列表中
31.     if result !='':
32.         fileTrainSeg.append(result)
33.    
34.     # 将分词后的结果添加到列表中
35.     fileTrainSeg.append(result)
36.  
37.     # 打印输出分词后的结果
38.     print(result)

         

结果显示


从结果来看,jieba进行医学词汇分词的效果很一般。不但对硝普纳这样的药物无法进行分词,对高血压的样的常见疾病会分成“高血/压”,经过各种调试目前已经是分词效果较好的情况了。
这里有同学就问了,不是已经导入了THUOCL的医学词典作为自用词典了嘛,为什么效果还是这么差?
这里猜测是jieba内部词典表的频率远高于THUOCL医学词典表的频率,所以仅仅在词典中的一部分词才能在分词过程中产生效果。如果大家想到什么好方法解决这个问题还请多多交流~
随后我又试了另一种分词方法,利用pynlpir库(官网:https://github.com/tsroten/pynlpir)。不过这个库非常之迷,第一次的分词效果是明显好于jieba的,之后再跑他只给我一个回应:

下午突然又能再次跑出一次出结果,之后又死了。这里给大家做备选方法:

step5.2 利用pynlpir进行文档分词  

         

向上滑动阅览


1.import pynlpir
 2.  
 3.# 定义分词后的列表
 4. fileTrainSeg =[]
 5.  
 6.# 遍历每一篇文章
 7.for words in fileTrainClean:
 8.     # 使用pynlpir分词工具对当前文章进行分词
 9.     tmp = pynlpir.segment(words, pos_tagging=False)
10.    
11.     # 定义结果字符串
12.     result =''
13.    
14.     # 遍历当前文章的每一个词语
15.     for i, word in enumerate(tmp):
16.         # 如果当前词语不在停用词列表中,则将其添加到结果字符串中
17.         if word.strip()and word notin stopwords:
18.             result += word +'/'
19.        
20.         # 处理分词结果中最后一个词语的斜杠问题
21.         if i == len(tmp)-1and result.endswith('/'):
22.             result = result[:-1]
23.    
24.     # 将分词后的结果添加到列表中
25.     fileTrainSeg.append(result)
26.  
27.     # 打印输出分词后的结果
28.     print(result)
29.  
30.# 关闭pynlpir分词工具
31. pynlpir.close()

模型训练

step6 利用Geisum包进行模型训练  

(Geinsum官网:https://radimrehurek.com/gensim/auto_examples/tutorials/run_word2vec.html#sphx-glr-download-auto-examples-tutorials-run-word2vec-py
 

向上滑动阅览


1.import gensim
 2.  
 3.# 构建词汇表
 4. sentences =[sentence.split('/')for sentence in fileTrainSeg]
 5. model = gensim.models.Word2Vec(sentences, vector_size=100, sg=1, window=5, min_count=1, workers=4)
 6.  
 7.# 训练模型
 8. model.train(sentences, total_examples=model.corpus_count, epochs=10)
 9.  
10.# 保存模型
11. model.save("my_word2vec_model.model")


模型参数

sentences:传入的句子集合          
vector_size:是指训练的词向量的维度          
sg:为 0 表示训练采用 CBOW 算法,1 表示采用 skip-gram 算法          
window:窗口数,即当前词与预测词的最大距离          
min_count:舍弃掉那些词频比该值小的词。一定要想清楚是否要过滤掉频率较小的词!如果你还希望做句子相似性的时候,如果舍弃了一部分单词,有可能会出现句子中的词不在词汇表中的情况!!!
worker:线程数,使用多线程来训练模型
至此,模型训练完毕!


应用举例

step7 查找相似词  

利用Word2vec自带的wv函数,查询与肺炎相关的前10个相似词:
1. similar_words = model.wv.most_similar("肺炎", topn=10)
2.for i in similar_words:
3.     print(i)

结果显示

结果上来看,前十个词基本都与肺炎相关,分词效果看起来那么差,得到这样的结果还是十分开心~
再玩一个:
1. similar_words = model.wv.most_similar("利尿药", topn=10)
2.for j in similar_words:
3.     print(j)

结果显示

看得出都与肾脏和一些与利尿药相关的药物相关。

Step8 进行类比预测   

通过词向量之间的运算来推测词语之间关系的方法被称为词向量类比。这种方法基于词向量的向量空间模型,将每个词语表示为一个向量,且保持词语之间的语义关系在向量空间中得到保留。


因此,可以通过对词向量之间的运算来推测词语之间的关系,例如:男人 - 女人 = 国王 - 王后。在这个例子中,通过对词向量进行加减法运算,推测出“女人”与“王后”之间的关系,即它们是同一类别的概念。
我们看看我们的模型能不能得出类似的结果:
1. result = model.wv.most_similar(positive=['肺炎','军团菌'], negative=['脑炎'], topn=10)
2.for m in result:
3.     print(m)  

结果显示

效果也还ok?

训练结果可视化

Step9 t-SNE降维  

         

向上滑动阅览


 1.#-*- coding: utf-8 -*-
 2.import numpy as np
 3.import sklearn
 4.# pip install scikit-learn
 5.import matplotlib.pyplot as plt
 6.from sklearn.manifold import TSNE
 7.  
 8.# 这个要找一个系统字体库有的字体
 9.# 如果和我一样用的linux系统
10.# 可以用fc-list :lang=zh命令在terminal查找linux系统中有的字体
11. mpl.rcParams['font.family']=['WenQuanYi Micro Hei']
12.  
13.# 加载模型
14. model = gensim.models.Word2Vec.load("my_word2vec_model.model")
15.  
16.# 获取所有词向量
17. X = model.wv.vectors
18.  
19.# 使用t-SNE进行降维
20. tsne = TSNE(n_components=2, random_state=0)
21. X_tsne = tsne.fit_transform(X)
22.  
23.# 可视化结果
24. fig, ax = plt.subplots()
25. ax.scatter(X_tsne[:,0], X_tsne[:,1], s=1, alpha=0.5, c='yellow')
26.  
27.# 在图中显示每个词
28.for i, word in enumerate(model.wv.index_to_key):
29.     if i %100==0:  # 每100个词显示一次
30.         ax.annotate(word,(X_tsne[i,0], X_tsne[i,1]), fontsize=4)
31.  
32.# 保存图像
33. plt.savefig('word2vec.png', dpi=1200, bbox_inches='tight')
34. plt.show()

         
         
我们可以观察图片上举例相近的中文词是否被聚类到一起:我们发现,‘肥胖’‘梗死’聚类在一起,‘戒烟’‘精神紧张’‘依赖’也同样被聚类。 
循环更多的词效果更显著一点,比如‘头孢他定’‘保泰松’‘布洛芬’就被聚类在一起,‘胃部’‘小肠’也被聚类在一起。虽然效果不是特别好,不过仔细观察一下还是蛮好玩的。
好啦,今天的分享就到这里了。
大家有空的话也可以换本书复制复制代码玩一下。在这个AI兴盛的大时代,作为医生我们也能用感兴趣的方式去体验自然语言模型,还是蛮有趣的!
同时,也可以将自然语言处理基础应用于医学研究领域,挖掘和分析大量的医学文本数据,可以助力科研以及简化平时的工作。

参考资料

书籍=>https://github.com/scienceasdf/medical-books/releases/latest
停用词=> https://github.com/goto456/stopwords
Jieba官网=>https://github.com/fxsjy/jieba
清华自用词典=>http://thuocl.thunlp.org/message
pynlpir库=>https://github.com/tsroten/pynlpir
Geinsum官网=>https://radimrehurek.com/gensim/auto_examples/tutorials/run_word2vec.html#sphx-glr-download-auto-examples-tutorials-run-word2vec-py

END

撰文丨鑫    仔

排版丨顶    顶


往期推荐

勇往直前!新的风暴已经出现,ChatGPT+医学影像=顶刊新玩法?


·
我就知道你在看

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
【手撕代码】医学影像报告自动生成(2) 数据探索性分析及可视化深度学习刷SOTA的trick盘点迷失的时间(5)--- 泰戈尔之光TPAMI 2023 | 无创解码大脑信号语义,中科院自动化所研发脑-图-文多模态学习模型闲说跨年在墨西哥城 (5) - 阿拉米达+革命塔如何填写【海关指尖码】?北美候鸟赴美回国攻略淘宝放券了!狮王/蓝月亮/全棉时代...【隐藏优惠代码】在这里!超参数科技招聘:强化学习研究员、深度学习工程师、后台开发工程师等高启强在《孙子兵法》里究竟学到了什么?ChatGPT 幕后:深度学习崛起的这十年 | 文末赠书【手慢无】这套大奖绘本,版权到期,特价清库存,先到先得!一文详解缺陷检测的传统算法与深度学习算法(内附16篇前沿论文)深度学习先驱者 Geoffrey Hinton 发布新深度学习算法老照片 | 外国摄影师镜头下的1983年严打时期的中国读完 RocketMQ 源码,我学会了如何优雅的创建线程细数NLP与CV的融合创新:盘点多模态深度学习这几年趣图:当我让客户把数据发给我后首次发现!数据异构影响联邦学习模型,关键在于表征维度坍缩 | ICLR 2023澳洲看病要给钱了?澳洲许多华人发现,本来免费GP开始收费!澳洲免费GP降至历史低位!新西兰网友:“看GP还有不要钱的?”【Hypertension】深度学习 | 收缩压和心血管风险之间是单一线性关系招聘 | 蚂蚁集团-NLP-大模型/深度学习/数字人算法-3个岗位-社招当AI学会了自己写游戏,当我们可以同时和所有大模型对话瞭望|金融改革化险应“防未病”“治已病”有效结合超越核方法的量子机器学习,量子学习模型的统一框架一文梳理缺陷检测的深度学习和传统方法留学生采访 | 从大二【转码】到拿下【思科】OFFER,留学让我更敢去想下一代听歌识曲技术——从信号处理到深度学习你在学校里究竟学到了什么?ChatGPT 幕后:深度学习崛起的这十年【手办新品】BINDing 狐之女神,Vibrastar 冒失娘女仆开订!CV发论文的机会来了!南洋理工项目招生(仅限深度学习,AI,机器学习,迁移学习方向)机器学习模型以出色的精度进行有机反应机理分类如何提升深度学习算法效率,谷歌有这些绝招胡鑫宇案汇总:到底是自杀还是他杀?美国入境档案--蒋彝
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。