社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  机器学习算法

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

解螺旋 • 1 年前 • 164 次点击  

大家好,我是鑫仔。就在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+医学影像=顶刊新玩法?


·
我就知道你在看

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/153216
 
164 次点击