Skip to content

2Elian/Drug-Recommend

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CausalLM-Drug-Recommend

Discharge Medication Recommendation System for Metabolic Diseases
Based on Chinese Electronic Medical Records (EMR) and GLM4-8B-Chat


🔍 Overview

CausalMed-GLM4 is a research-oriented system that leverages large language models (GLM4-8B-Chat) to generate personalized discharge medication recommendations for patients with metabolic diseases.
It integrates causal reasoning, reinforcement refinement, and Chinese EMR understanding.


🏗️ Task Pain points

1. max_seq_length

训练数据集的token分布如下:

统计项 数值
样本总数 3602
平均token数 1531.57
最大token数 8095
最小token数 379
平均标签数 6.08
最少标签数 1
最多标签数 21
分位数 token数量 标签数量
P50 1422.00 6.00
P80 1922.60 9.00
P90 2247.90 11.00
P95 2600.75 12.00
P99 3715.92 15.00

测试数据集的token分布如下

统计项 数值
平均token数 1519.56
最大token数 6644
最小token数 456
分位数 token数量
P50 1415.00
P80 1909.60
P90 2232.80
P95 2574.85
P99 3628.44

从上表中可以观察到,大约80%样本的token数量都是小于1922.6的。所以,在我baseline训练的时候,我把max_seq_length设置成了2000,即也就是意味着有20%的训练样本会缺失上下文信息。而我目前手里面空闲的卡只有2张A6000 48GB, 所以max_seq_length=2000已经是极限,这还是在per_device_train_batch_size=1, gradient_accumulation_steps=2的前提下。再大就会OOM了。

而这里的痛点在于:

(1) max_seq_length=2000一定是有20%的样本缺失一部分上下文信息,模型遇到超过2000的测试样本的时候,泛化性能会下降。这里max_seq_length的理想值应该是4096左右(后续我会想办法,搞定它!比如用一下TP、SP或者是序列打包)
(2) 如果我想动一动模型,加大一点参数量或者是加大一点batch_size,那么就会OOM,算力上比较焦虑。

所以综上两点,我们训练的时候,序列必须是必须的。这里感谢Xtuner-V0.1架构,我们基于该架构的SP机制实现了我们的pipeline。序列大小设置为比较理想的4000

2. 长尾分布long-tail distribution

训练数据集中,药品标签出现的次数分布如下

top20排名 药物名称 出现次数
1 阿托伐他汀钙片 1298
2 阿司匹林肠溶片 991
3 阿卡波糖 884
4 二甲双胍 609
5 碳酸钙片 517
6 甘精胰岛素 500
7 硝苯地平控释片 479
8 氨氯地平片 478
9 甲钴胺 470
10 门冬胰岛素 381
11 瑞巴派特片 374
12 依帕司他 373
13 雷贝拉唑 369
14 美托洛尔缓释片 338
15 兰索拉唑 307
16 阿法骨化醇 306
17 瑞舒伐他汀 296
18 氯吡格雷 286
19 银杏叶软胶囊 267
20 左甲状腺素钠片 253
-top20排名 药物名称 出现次数
1 脑脉利颗粒 1
2 富马酸福伏诺拉生片 1
3 非奈利酮 1
4 拉坦前列素滴眼液 1
5 尼洛替尼 1
6 美西律片 1
7 甲硫咪唑 1
8 安脑丸 1
9 洛芬待因片 1
10 福辛普利 1
11 头孢克肟 1
12 硫必利片 1
13 孢克洛缓释片 1
14 干扰素凝胶 1
15 卡马西平 1
16 坦索罗欣胶囊 1
17 美金刚 1
18 艾曲波帕 1
19 艾拉莫德 1
20 参仙升脉口服液 1

头部类占比: 9.90%, 平均频次: 260.7 中部类占比: 80.03%, 平均频次: 14.3 尾部类占比: 10.07%, 平均频次: 1.0

也就是说,模型更倾向于预测中高频药物,低频药物梯度贡献小,几乎不被学习(这在医学推荐中特别危险,因为低频药物往往是特殊并发症或罕见病药物,临床意义反而更大)。如何解决这个问题?

数据的边际收益递减效应:当某个类别的数据样本非常多时,继续增加同类样本对模型性能的提升会越来越小(收益递减

头部类别的冗余信息: 重复样本会导致模型过度偏向该类(冗余信息)

标签共现: 在多标签任务中,一个样本可能同时属于多个标签,而且某些标签经常一起出现,这种“同时出现”的关系就叫 标签共现, 例如:一个病人的处方中可能同时包含:「二甲双胍」 + 「阿卡波糖」 + 「阿托伐他汀钙片」这几个标签往往是 一起出现的,说明它们之间存在共现关系(共现频率高)。为什么共现是个问题?因为如果模型学到:“只要出现二甲双胍,就预测阿卡波糖也出现”,那么模型会“偷懒”,不去真正理解样本内容,而是仅仅靠标签之间的统计关系预测,这样模型泛化性差。Distribution-balanced Loss(DB Loss)就是通过重新加权(rebalanced weighting)来减少这种“共现带来的偏差”,让模型更关注独立的标签特征。

此外,多标签场景中,一个包含多个标签的样本可能会被过度采样(常见长尾分布算法中的问题):指在重采样(resampling)时,如果你不加区分地对每个标签都按照“样本越少权重越大”的原则来采样,带有多个稀有标签的样本会被重复采样多次,导致训练集偏向它。举个例子: 假设有如下数据:样本 A [阿托伐他汀钙片, 阿司匹林] 样本 B [脑脉利颗粒, 美西律片]这里,“脑脉利颗粒”和“美西律片”都是非常稀有的标签(出现次数=1)。 如果你按单标签的平衡采样策略(1/频率)来重采样,那么: 样本 A 的两个标签都常见,采样概率小; 样本 B 的两个标签都极少见 → 各自都有很高的采样权重。 于是样本 B 会被采样两次高权重叠加,导致它被选中的概率远大于别的样本。 这就是“过度采样”——同一个样本因为有多个稀有标签而被采太多次。

3. 药品之间的相互排斥性和药品之间的共现

简单的基础模型+分类层无法充分考虑药品之间的排斥问题,在实际应用场景中,如果预测的药物列表中含有相互排斥的药物是及其不安全的。

基于以上考虑,我们有必要在模型中加以限制,充分考虑药品之间的相互排斥性。

具体而言,我们基于这651个药品语料库,训练了一个分数矩阵A,A[i][j]的取值范围是0-1,0代表完全不排斥,1代表完全排斥。

共现矩阵形状: (586, 586) 平均共现次数: 0.44 最大共现次数: 621.00

TOTO:如何训练这个分数矩阵A呢?基于图谱还是基于什么来训呢?

药物 - 药物相互作用(DDI)建模

图正则化损失函数

1. 基于公开数据库(DrugBank、BioGRID 或中国 NMPA 数据库)构建 DDI 图。
2. 构建邻接矩阵A[651×651] → 若两种药物存在已知相互作用,矩阵元素为 1,否则为 0。
3. 为存在冲突的药物添加成对排斥约束:
def ddi_loss(y_pred, A, lambda_ddi=0.1):
    y_pred = y_pred.sigmoid()  # 将模型输出转换为概率
    ddi_pairs = y_pred @ A @ y_pred.T  # 计算推荐药物组合中的相互作用程度
    return lambda_ddi * ddi_pairs.mean()  # DDI损失(λ_ddi为权重超参数)

总损失 = 二元交叉熵损失(BCE) + λ_ddi × DDI损失

4. 多任务学习

输入字段:

{
"患者序号": 2,
"就诊标识": "2-1",
"性别": "女",
"出生日期": "1940-12",
"民族": "汉族",
"BMI": 27.3,
"就诊时间": "2015-03",
"诊疗过程描述": "门诊查尿酮体:+,白细胞:250/ul、镜下:7-9/Hp。入院后查:尿常规:白细胞+++/ul、尿白细胞62.25/HP↑,口服补液后复查尿常规白细胞及酮体(-)。馒头餐试验结果回报:糖化血红蛋白...... ",
"入院情况": "患者以\"烦渴、多饮、多尿5年,尿痛伴血糖控制不佳2个月。\"为主诉入院重要查体:T36.6℃,P76次/分,R22次/分,BP160/80mmHg......",
"现病史": "患者5年前无明显诱因出现烦渴、多饮、多尿症状,遂于某医院就诊,测空腹血糖16.7mmol/l,予患者二甲双胍、瑞格列奈、阿卡波糖片控制血糖...... ",
"既往史": "否认冠心病病史,否认有肝炎、结核、疟疾等传染病史,否认食物、药物过敏史,否认外伤、手术史,否认输血史,预防接种史不详。",
"主诉": "烦渴、多饮、多尿5年,尿痛伴血糖控制不佳2个月。",
"出院诊断": ["2型糖尿病", "糖尿病酮症", "泌尿系感染", "糖尿病大血管病变",...]
"出院带药列表": [ "阿卡波糖", "瑞格列奈", "瑞舒伐他汀", "替米沙坦", "氨氯地平片", "碳酸钙片" ]
}

我们的最终任务目标是预测"出院带药列表",即做一个多标签预测的任务。输入是所有字段信息的拼接(除了"出院带药列表")。

但是从以上数据来看,一个好的模型,应该可以依据患者基本信息和临床信息判断出"出院诊断".

所以,除了预测多标签分类以外,是否可以加一个LM任务?预测"出院诊断"?

此外,掩码机制是否可以基础信息+临床信息做一个双向注意力,而出院诊断那边做一个因果掩码呢?

5. 多特征预测分类

如果4能够实现,且效果优秀的话,我们是否可以把分类分成2个阶段:

第一阶段,基础信息+临床信息的hidden的平均作为embedding,做分类?(这里取平均是因为什么呢?)

第二阶段:正常的最后一个token作为embedding,做分类(因为,最后一个token能包含前面所有的信息,因果掩码的机制)

6. 训练集标签不完备

训练集标签不完备的意思是:主办方给的药品列表里面有651种药,我们做的也是651分类。但实际上训练集中标签只有586个,也就是有65个类别得不到训练。

但是训练集里面一定包含这65个类别

换句话说,也就是训练未见过的标签,如何在测试集中表现的好呢?

About

Drug recommendation competition realizes warehouse

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors