完整流程可以分以下几步

  • 数据整理
  • word2vec
  • 构建transformer模型
  • 训练模型
  • 测试模型
  • 资源下载介绍

若懒得看程序,也可以直接下载全部程序,在最后一部分进行了资源的介绍。
【MIMIC-IV/pytorch实战】基于word2vec、transformer进行英文影像报告文本分类

数据整理

MIMIC-IV中的文本数据在MIMIC-CXR模块中,影像报告以txt格式储存,每份报告为一个txt文件,如下
病人与报告之间通过一个cxr-study-list表相关,通过这张表我们可以获取报告对应病人的其他信息,如是否死亡、患病等情况作为标签。
这部分程序主要完成两步,

  1. 一是通过疾病筛选患者,因为都是胸片报告,因此我筛选出肺癌患者
  2. 二是提取findings部分文本,并删除所有换行符,这步主要程序可见这篇博客: python导入txt文件并删除换行符并提取部分内容—MIMIC-IV/MIMIC-CXR文本报告预处理

最终获得的文件如下图,标签为是否发生院内死亡。

word2vec

这部分是为了将文本转化成向量,以输入神经网络,主要分为以下几步

  1. 删除标点、分词
  2. 使用word2vec进行向量化,gensim库
  3. 形成可以输入神经网络的向量,embedding层

(新手仔细看,老手掠过下面这部分)
我们首先要明白,文本是如何输入到pytorch的神经网络中的。
文本需要形成一个词表,尽可能包含输入的所有词,每个词对应一个id,和一个vector。加入输入的是i love you,那么可以有如下一个表:

id word
1 i
2 love
3 you
这样,输入的词就可以变成[1,2,3]这样一个由id构成的表
放入pytorch 的embedding层中,便可以被转换为自定义维度的tensor。
但这样获得的tensor通常是随机的,因此我们可以使用word2vec来定义每个词的向量。
使用word2vec可以获得一个类似于下面这样的表。使用id-vec两列初始化embedding层,即可将每个词按这个表转化成tensor。
id word
1 i
2 love
3 you
下面放实战代码。
def tokenizer(ori_list):       #去除标点符号,分词SYMBOLS = re.compile('[\s;\"\",_().!?\\/\[\]]+')new_list = []for q in ori_list:words = SYMBOLS.sub(' ',q).lower().strip() #符号换成空格words = words.split(' ')                   #按空格分词new_list.append(words)return new_listdef word2vec_train(sentences,vector_size=64, min_count=1, negative=5, epochs=5, name="word2vec.model"):    #输入,分词好的句子样本logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)model = Word2Vec(sentences, vector_size=vector_size, min_count=min_count, negative=negative, epochs=epochs)# sentences = word2vec.Text8Corpus(sentences)  # 一种输入数据的方法#https://www.cnblogs.com/MaggieForest/p/13583447.htmlmodel.save(name)    #保存模型if __name__ =="__main__":findings = pd.read_csv(r"findings_list.csv")  #study_list文件目录label = findings['hospital_expire_flag']text = list(findings['findings'])name = "../saved_modal/word2vec.model"   #word2vec保存名称sentences = tokenizer(text) word2vec_train(sentences,vector_size=64, min_count=1, negative=5, epochs=5, name=name)

这样就获得了一个word2vec模型,这个模型本质上可以理解为一个包含id、word、vec的表。通常,这个模型需要大规模语料库进行训练。

下面,就要利用这个表初始化embeding层,并将每个句子形成一个tensor,初始话embeding的表是一个array形式的,一列是id,一列是vec。因为不是每个词都有相对应的vec,也不是每个句子都有同样多的词。因此,在此表中加入一个pading(全0向量)补全全短的句子,加入全1向量用于此表中没有的词。

def get_index(sentence):         #将样本中的词替换为idsequence = []for word in sentence:try:sequence.append(word2id[word]+2)   #  +2 是因为加了pading和unknowexcept KeyError:sequence.append(1)    # 匹配不到的 给id=1return sequencedef get_tensor(data,w2v_model):   global word2idword2id = w2v_model.wv.key_to_index   #获取词-id表X_data = list(map(get_index, data))X_pad = pad_sequences(X_data, maxlen=None,padding='post')     # 短的都补0X_data_tensor = torch.tensor(X_pad)     # 形成tensorreturn X_data_tensordef get_weight(w2v_model):    #获取初始化embedding层的表id2vec = w2v_model.wv.vectors       #获取id-embedding表id2vec = np.insert(id2vec, 0, values=np.zeros(id2vec.shape[1]), axis=0) # pading掉的id为0,向量为 全0id2vec = np.insert(id2vec, 1, values=np.ones(id2vec.shape[1]), axis=0)  # 匹配不到的id为1 , 向量为 全1weight = torch.from_numpy(id2vec)return weightif __name__ == '__main__':train_data, train_label,test_data, test_label = pro(path=r"../data/findings_list.csv",seed=2,rat=0.7)w2v_model = Word2Vec.load("../saved_model/word2vec.model")X_data_tensor = get_tensor(train_data,w2v_model)

最后形成的X_data_tensor,是将词替换成id的形式。
使用一个embedding进行测试,将id2embed词表初始化embedding层,获得的a向量即为将所有id替换成向量的结果,这个embedding层放入神经网络中即可。

weight = torch.from_numpy(id2embed)
a = nn.Embedding.from_pretrained(weight,freeze=True)(X_data_tensor)

构建transformer模型

transformer模型的构建仅使用了transformer的encoder部分,参考ViT的结构,完整ViT的实现可以参考这篇博客
Vision Transformer(ViT)PyTorch代码全解析(附图解)
在次基础上去掉了图像编码的部分,加入了文字的embedding层,及将前面获得的id转化为相对应的向量。

class ViT(nn.Module):def __init__(self, id2vec, num_classes, dim, depth, heads, mlp_dim, pool, dim_head, dropout = 0., emb_dropout=0. ):super().__init__()assert pool in {'cls', 'mean'}, 'pool type must be either cls (cls token) or mean (mean pooling)'self.word_embeds = nn.Embedding.from_pretrained(id2vec,freeze=True)    #加入这一层,将词的id转化为向量,freeze冻结此层self.cls_token = nn.Parameter(torch.randn(1, 1, dim))self.dropout = nn.Dropout(emb_dropout)self.transformer = Transformer(dim, depth, heads, dim_head, mlp_dim, dropout)  #dim:输入每个特征维度,dim_head,q和k维度self.pool = poolself.to_latent = nn.Identity()self.mlp_head = nn.Sequential(nn.LayerNorm(dim),nn.Linear(dim, num_classes),nn.LogSoftmax(dim=1))def forward(self, x):       #b为批大小,x(batch,feature_n)  x为word对应的id,在embeding层之后才变成(batch,feature_n, dim)x = self.word_embeds(x)     b,c,d =x.shapecls_tokens = repeat(self.cls_token, '() n d -> b n d', b = b)   # x = torch.unsqueeze(x, 2)   #输入特征是1维的,所以要增添这一维度,否则不用x = torch.cat((cls_tokens, x), dim=1)   #(1, 65, 1024)  65:加cls之后的feature数,1024是每个特征的维度x += nn.Parameter(torch.randn(1, c+1, d))[:, :(c+1)]     #加位置编码x = self.dropout(x)x = self.transformer(x)x = x.mean(dim = 1) if self.pool == 'mean' else x[:, 0]x = self.to_latent(x)return self.mlp_head(x)

另外,最后一层输入分类概率,原博客使用的是linear层,这样可以根据两类值的大小判断分类。但我想计算AUC,需要知道分类为1的概率,因此需要加入softmax层。但如果我在测试时使用nn.CrossEntropyLoss()当损失函数,就会重复softmax。
因为nn.CrossEntropyLoss()相当于softmax+log+NLLLoss,也就相当于logSoftmax+NLLLoss,所以我加了一层logSoftmax,并用NLLLoss作为损失函数进行训练。
这样模型就可以输出分类的概率,并且在训练时依旧相当于使用了CrossEntropyLoss()交叉熵损失函数。
这部分借鉴了以下两篇博客

  1. pytorch损失函数之nn.CrossEntropyLoss()【内含softmax推导】
  2. Pytorch中Softmax、Log_Softmax、NLLLoss以及CrossEntropyLoss的关系与区别详解

训练模型

训练函数代入了搭建的模型,可以更改模型中的各个参数

def train_ViT(id2vec,       #id2vec词表train_data,    #训练数据train_label,   #训练标签num_classes,   #分类数量num_epochs,    #训练轮数batch_size,    #批大小learning_rate,  #学习率dim = 1     ,   #每个特征的维度depth= 1,          #encoder层数heads=1,       #注意力头数mlp_dim=32,    #encoder MLP 输出pool = 'cls',  #分类方式 cls meandim_head = 32,  #注意力输出向量维度dropout = 0.,     #transformer里的emb_dropout = 0.   #emb的): torch_dataset = Data.TensorDataset(train_data, train_label)loader = Data.DataLoader(          #批训练数据加载器dataset=torch_dataset,batch_size=batch_size,shuffle=True,  # 每次训练打乱数据, 默认为Falsenum_workers=0,  # 使用多进行程读取数据, 默认0,为不使用多进程drop_last=False)# 构造vit模型model = allmodel.ViT(id2vec, num_classes, dim, depth, heads, mlp_dim, pool , dim_head   , dropout  , emb_dropout )print(model)model.train()# Loss and optimizercriterion = nn.NLLLoss()optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)# 训练模型total_step = math.ceil(len(train_data)/batch_size)for epoch in range(num_epochs):for i, (input_data, labels) in enumerate(loader):# 前向传播outputs = model(input_data)loss = criterion(outputs, labels.long())# 后向优化optimizer.zero_grad()loss.backward()optimizer.step()# 每一百步打印一次if (i + 1) % 10 == 0:print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'.format(epoch + 1, num_epochs, i + 1, total_step, loss.item()))print("ViT训练完毕")# 保存训练完的模型torch.save(model, '../saved_model/ViT.pth')

测试模型

def test_ViT(test_data, test_label):model = torch.load('../saved_model/ViT.pth')model.eval()with torch.no_grad():outputs = model(test_data)predict_rate = outputs[:, 1]_, predicted = torch.max(outputs.data, 1)predicted = predicted.numpy()labels = test_label.numpy()Acc = metrics.accuracy_score(labels, predicted)Precision = metrics.precision_score(y_true = labels, y_pred = predicted, zero_division=0)Auc = metrics.roc_auc_score(labels, predict_rate)F1 = metrics.f1_score(labels, predicted)Recall = metrics.recall_score(labels, predicted)print('Acc: {} %, Auc: {} %, Pre: {} %, F1: {} %, Recall: {} %'.format(100 * Acc, 100 * Auc, 100 * Precision, 100 * F1 , 100 * Recall ))

资源下载介绍

若懒得看以上内容,可以下载资源

【MIMIC-IV/pytorch实战】基于word2vec、transformer进行英文影像报告文本分类

提示,本资源仅为demo,为经过深度调试。在搭建时为了节约时间,仅使用了少量数据进行训练和测试。在应用时,请自行增加数据量,并根据需要更改模型结构。
本资源包括三个文件夹

  1. data文件夹中包括三个文件,其中findings_list为直接应用于程序的数据,其他是在预处理是产生的数据。
  2. saved_model文件夹中包含训练产生的word2vec和transformer模型。
  3. code文件夹中包含所有程序代码
    main 可以直接运行,完成word2vec和transformer模型的训练与测试
    提取文本代码用于从数据库中提取文本,得到findings_list文件
    data_processing用于划分训练集测试集并进行分词
    word2vec用于训练word2vec模型
    embedding用于根据word2vec产生词表和产生可输入模型的tensor
    allmodel用于搭建transformer
    train 用于训练模型
    test_model用于测试模型

【MIMIC-IV/pytorch实战】基于word2vec、transformer进行英文影像报告文本分类相关推荐

  1. 今晚8点:基于强化学习的关系抽取和文本分类 | PhD Talk #18

    「PhD Talk」是 PaperWeekly 的学术直播间,旨在帮助更多的青年学者宣传其最新科研成果.我们一直认为,单向地输出知识并不是一个最好的方式,而有效地反馈和交流可能会让知识的传播更加有意义 ...

  2. 直播预告:基于强化学习的关系抽取和文本分类 | PhD Talk #18

    「PhD Talk」是 PaperWeekly 的学术直播间,旨在帮助更多的青年学者宣传其最新科研成果.我们一直认为,单向地输出知识并不是一个最好的方式,而有效地反馈和交流可能会让知识的传播更加有意义 ...

  3. 使用Memory-driven Transformer生成医疗影像报告

    ©PaperWeekly 原创 · 作者|李东明 学校|香港中文大学(深圳)本科生 研究方向|文本生成 论文标题: Generating Radiology Reports via Memory-dr ...

  4. 基于动态路由的胶囊网络在文本分类上的探索

    摘要 简介 模型 2.1 N-gram 卷积层 2.2 初级胶囊层 2.2.1 孩子-父母(部分-整体)关系 2.3 动态路由 孤立类别 Leaky-Softmax 参数修正 实验 3.1 实验数据集 ...

  5. pytorch 定义torch类型数据_PyTorch 使用TorchText进行文本分类

    本教程演示如何在 torchtext 中使用文本分类数据集,包括 - AG_NEWS, - SogouNews, - DBpedia, - YelpReviewPolarity, - YelpRevi ...

  6. pytorch 定义torch类型数据_PyTorch 使用 TorchText 进行文本分类

    本教程介绍了如何使用torchtext中的文本分类数据集,包括- AG_NEWS, - SogouNews, - DBpedia, - YelpReviewPolarity, - YelpReview ...

  7. 基于tensorflow+CNN的新浪新闻文本分类

    2018年10月4日笔记 tensorflow是谷歌google的深度学习框架,tensor中文叫做张量,flow叫做流. CNN是convolutional neural network的简称,中文 ...

  8. 基于tensorflow+RNN的新浪新闻文本分类

    2018年10月11日笔记 tensorflow是谷歌google的深度学习框架,tensor中文叫做张量,flow叫做流. RNN是recurrent neural network的简称,中文叫做循 ...

  9. 6.自然语言处理学习笔记:Multi-head-self-attention 和Transformer基础知识 和BERT文本分类原理

    Multi-head-self-attention: 可以更细致的去发现局部信息. Transformer:   BERT文本分类原理:  

最新文章

  1. 资料分享:送你一本《机器学习实战》电子书!
  2. 蓝牙4.0BLE抓包(二) – 广播包解析
  3. java栈的底层实现_JVM 底层原理总结
  4. Java垃圾回收机制分析
  5. U-Mail邮件服务器教您揭穿冒充发件人的伎俩
  6. paip.c++ 常用类库attilax总结
  7. 方便自己的一些学习科研的记录 【小神器】
  8. MAX232芯片的引脚图和电脑串口的连接电路及RS232引脚定义详细说明
  9. 《好战略,坏战略》 摘记
  10. SpringBoot整合mybatis出现BindingException: Invalid bound statement (not found)问题解决
  11. 阿里热修复方案Sophix
  12. 输入法的一些设置,以及解决 输入法 ctrl+c 等快捷键不能操作问题?
  13. 算法总结-UT哈希算法
  14. 关于DIY电池均衡器--被动均衡---蓄电池--电瓶车电池组电压均衡的经历
  15. SpringBoot——Thymeleaf常见属性-使用th:each遍历数组、List、Map
  16. YOLOV8:FileNotFoundError: train: No labels found in /home/smy/new-yolov5/ultralytic
  17. 再见了仪表盘!数据该有更好的“归宿”
  18. 一阶电路暂态响应的结果分析。_线性动态电路可视化分析
  19. 以萨技术在科创板IPO终止:计划募资15亿元,实控人为李凡平
  20. VBA一键汇总多个工作簿-名称相同的工作表-的指定区域数据

热门文章

  1. ziven中文怎么读_朴振英那首《你住的那所房子》韩文用中文怎么读,不是翻译!请教...
  2. seqkit根据基因id_fasta序列操作神器——seqkit
  3. nuc972 watchdog 测试
  4. Unity常见纹理压缩格式
  5. Unity中图片压缩格式
  6. Unity学习笔记--自定义文件的处理:Scripted Importers的简单使用
  7. 中央处理器——数据通路之专用通路结构
  8. 用四阶龙格-库塔方法求微分方程组
  9. 性能优化:swift三方库Kingfisher图片加载库属性maxMemoryCost失效原因分析
  10. EPLAN报表生成标题页和目录