NBME - 使用 DeBERTa 模型分析病人病例
這篇文章不會談論怎麼寫程式,而是著重在說明如何為 DeBERTa 模型設計 Input 和 Ouput 以及如何評估模型的表現。
說明
當你去看醫生時,醫生如何解讀你的症狀會決定診斷的準確性。醫生在獲得執照之前,已經有很多撰寫病人記錄的實踐經驗,這些記錄文檔包含病人的病史、體檢結果、可能的診斷和後續護理。學習和評估撰寫病人記錄的技能需要其他醫生的反饋,這是一個耗時的過程,可以通過機器學習來改進。
直到最近,Step 2 Clinical Skills(臨床技能)考試是美國醫學執照考試(USMLE)的一個組成部分。考試要求考生與標準化病人(被訓練來扮演特定臨床病例的人)互動,並撰寫病人記錄。訓練有素的醫師評分員會根據每個病例的重要概念(稱為特徵)用量表來評分病人記錄。病人記錄中包含的這些特徵越多,得分就越高(除了其他影響考試最終得分的因素)。簡單來說,就是考生要從病例中找出可能可以作為生病的特徵。
然而,讓醫生評分病人記錄考試需要大量時間和人力、財力資源。針對這個問題,自然語言處理技術被開發出來,但病人記錄的計算評分仍然充滿挑戰,因為特徵可以用很多不同的方式表達。例如…
- 特徵“對活動失去興趣”可以表達為“不再打網球”
- 特徵“沒有寒冷不耐受、脫髮、心悸或顫抖”,這對應於關鍵的“缺乏其他甲狀腺症狀”。
在這次競賽中,你需要識別病人記錄中的具體臨床概念。具體來說,你將開發一種自動方法,將考試量表中的臨床概念(例如“食欲減退”)映射到醫學生撰寫的病人記錄中表達這些概念的各種方式(例如“吃得更少”,“衣服變得更寬鬆”)。出色的解決方案將既準確又可靠。
如果成功,你將幫助解決病人記錄評分中的最大實際障礙,使這種方法更加透明、可解釋,並減輕這類評估的開發和管理難度。結果,醫療從業者將能夠充分發掘病人記錄在臨床技能評估中揭示相關信息的潛力。
資料集
train.csv
- 用於訓練模型的訓練數據集test.csv
- 用於測試模型的測試數據集(只有五筆)patient_notes.csv
- 病人記錄的文本數據集features.csv
- 用於訓練和測試的特徵數據集
從上圖可以看到,我們透過將 train.csv
和 features.csv
進行合併,得到了一個新的數據集如上圖所示。有幾點需要注意的:
- 合併過後可能會同一個病人會有多個病例(
features_text
) - 每個病例,可能會有多個
annotation
(也就是特徵) location
是annotation
在pn_history
(病史)中的位置- 綠色是用來給模型的輸入,黃色是我們想要預測的結果
衡量
如上圖所示,對於每個實例,我們預測一組字元跨度。一個字元跨度是一對索引(114 129)表示為該內容的特徵,代表文字中的一個字元範圍。一個跨度i j代表索引為i到j的字符,左閉右開,在Python的符號中,一個跨度i j相當於一個切片 i:j 對於每個實例,都有一個基礎真實跨度的集合和一個預測跨度的集合。我們用分號來分割跨度。
方法
首先我們知道提供給模型的有以下:
pn_history
病人的病史feature_text
得什麼病
我們要預測的是:
pn_history
中哪個特徵對應到feature_text
病例的索引
因此可以知道的事,我們的 input 必須是 pn_history
和 feature_text
的結合,把他們關聯起來,然後預測 pn_history
中的特徵對應到 feature_text
的索引。步驟可以如下:
pn_history
和feature_text
的結合- 把 step 1 整合的結果經過 Embedding 送入 DeBERTa 模型
- output 的結果必須是
pn_history
經過 embedding 後,把解答的部分寫成1,不是解答的部分預測為0,其他padding為-1。
準備 input 與 label
Input
- 先找到
max_len
的 input size:從這麼多的pn_history中找最大的長度當作 input 的max_len
- 把
pn_history
+feature_text
轉換成 Token
Label
目標:把 pn_history
轉換成 {-1, 0, 1}
的 Label,-1 代表 padding,0 代表不是解答,1 代表是解答
- 先準備一個
max_len
的 array list with 0。 - 把 special token 像是
[CLS]
,[SEP]
或[PAD]
設定為 -1 - 找到 tokons 中包含 annotation 的部分,把這部分的 label 設定為 1
- 最終得到的結果可能會是:
[-1, 0, 0, 1, 1, 0, 0]
Input 準備
接下來你可能會好奇怎麼找到 max_len
可以參考下圖:
- 開始遞迴每個
pn_history
- 把
pn_history
和feature_text
結合轉換成 Token - 計算這個 token 的長度,並存長度到一個 arr 裡面
- 最後這個arr會集結所有資料的長度,找到最大的長度就是
max_len
準備Input 會如下圖所示(發現會把annotation整合進去pn_history)
Label 準備
準備Label 會如下圖所示(只需要pn_history即可)
-
我們會先創造出一個由 max_len 長度的 array。
-
把 special token 像是
[CLS]
,[SEP]
或[PAD]
設定為 -1
-
接下來就是把 annotation 的部分設定為 1
訓練
模型參數設定如下:
到時候的輸出每個 token 位置都是一組機率,我們可以設定閥值為 0.5,如果機率大於 0.5 就是 1,否則就是 0。
衡量
如果解答是 0 3; 3 5
而我預測出 2 5; 7 9; 2 3
那麼我們可以計算出以下的結果:
0 1 2 3 是解答 我預測出 2 3 4 5
- TP: 2, 3
- FP: 4, 5
- FN: 0
這樣我們就可以計算 F1 score = 2 * TP / (2 * TP + FP + FN)