跳轉到內容

在分子、譜圖、訊號和機器人回合上訓練模型

在研究的答案上擬合 rdkit-property、peak-cnn、signal-cnn 和 episode-model,再把預測展示給標註者接受或修正。

v2.14.0 新增

本教程在 Potato 研究的科學標註上訓練四個模型。rdkit-property 回答關於分子的問題,peak-cnn 在 NMR、IR、Raman、UV-Vis 和質譜上挑選峰,signal-cnn 對睡眠 epoch 分期,或在 EEG、ECG 和感測器記錄上標出已標註的區間,episode-model 標出機器人回合的階段、結果和獎勵曲線。本頁面向已經在 Potato 中標註分子、譜圖、訊號或機器人回合,並希望用模型為其餘條目預標註的團隊。對每個訓練器,本頁給出能通過 potato validate config.yaml --strict 的配置、一次執行學到什麼、應當檢視的留出得分,以及預測如何送到標註者面前。訓練你的第一個模型用文本分類器演示了訓練頁面,本教程以該教程為前提。

四個訓練器

每個訓練器都從一個標註方案的答案中學習,並從該方案所指向的顯示或檔案讀取模型的輸入:

訓練器方案輸入安裝留出主得分
rdkit-property關於分子的 radio、select、likert 或 slidermolecule 顯示欄位,或 chemical_structure 答案potato-annotation[chemistry]macro-F1,滑塊則為精確位置準確率
peak-cnnpeak_picking來自 JCAMP-DX、mzML、CSV 或內聯資料的譜圖potato-annotation[train-vision]容差內的峰 F1
signal-cnnepoch 或 free 模式的 interval_label來自 EDF、BDF、WFDB、CSV、Parquet 或內聯資料的訊號potato-annotation[train-vision],WFDB 和 Parquet 另需 [signals]各 epoch 的 macro-F1,或 IoU 0.5 時的區間 F1
episode-modelepisode_annotation來自 LeRobot、HDF5 或 Potato 回合清單的機器人回合potato-annotation[train-vision]階段 macro-F1

rdkit-property 擬合 scikit-learn 模型,其餘三個訓練器在 model_training.device 選定的裝置上擬合小型 PyTorch 網路。每次執行只在一個方案上訓練,在該次執行留出的 val 和 test 條目上給模型評分,並且只為 writeback.schemas 下列出的方案回寫預測。屬於一次執行 train 劃分的條目永遠不會得到預標籤。peak-cnn、signal-cnn 和 episode-model 保留在 val 上得分最好的那個 epoch 的檢查點,所以這三個網路的留出得分由 Potato 取自 test。小型研究的測試條目很少,執行的 test_n 指標給出得分背後的條目數。

這三個網路把一個條目預測的置信度取為該預測中最不確定部分的置信度,即最弱的峰、區間、階段或結果。因此 writeback.min_confidence 下限會在任何一個部分低於下限時,把整個條目留下不回寫。rdkit-property 的滑塊預測沒有置信度值,總能通過下限。

用 rdkit-property 預測分子性質

rdkit-property 學習關於條目所顯示分子的問題的答案,模型的輸入是分子的結構,而不是條目的文本。每個結構先變成半徑為 2、2,048 位的 Morgan 指紋,後面接 RDKit 的 2D 描述符,例如分子量和 logP。半徑為 2 時,Morgan 指紋對應 ECFP4,即直徑為 4 個鍵的擴充套件連線性指紋。對於 radio、select 或 likert 答案,預設用邏輯迴歸擬合,對於 slider 用嶺迴歸擬合。兩者都可以改用隨機森林。請在負責訓練的伺服器上安裝 RDKit:

bash
pip install "potato-annotation[chemistry]"

下面的配置把每個化合物顯示為結構圖,並提出兩個問題,一個是 1 到 10 的滑塊,問該化合物合成有多難,另一個是是否下一步合成該化合物的是非選擇:

yaml
annotation_task_name: "Compound triage"
task_dir: .
output_annotation_dir: annotation_output
data_files: [data/molecules.jsonl]
item_properties: {id_key: id, text_key: note}
user_config:
  allow_all_users: true
 
instance_display:
  fields:
    - key: smiles                 # SMILES, a molfile, or a .mol, .sdf or .smi file
      type: molecule
      label: "Compound"
 
annotation_schemes:
  - annotation_type: slider
    name: synthesis
    description: "How hard would this compound be to make? 1 is one step from a catalog reagent, 10 is a research project."
    min_value: 1
    max_value: 10
    starting_value: 5
    step: 1
  - annotation_type: radio
    name: advance
    description: "Would you make this compound next?"
    labels: ["yes", "no"]
 
model_training:
  enabled: true
  writeback:
    schemas: [synthesis, advance]
    include_unlabeled: true
    min_confidence: 0.6

資料檔案的每一行對應一個化合物:

json
{"id": "cpd_023", "smiles": "CC(=O)Nc1ccc(O)cc1", "note": "Compound 23"}

訓練器從 instance_display 中的第一個 molecule 欄位取結構。沒有 molecule 欄位的研究可以改為在 chemical_structure 方案旁邊提問,此時訓練器從標註者畫的結構中學習。標註者畫了不同的結構時,訓練器取最常見的那個,沒有人畫時,訓練器取編輯器起始的結構(prefill_field)。只要至少 80% 的條目填寫了 molecule 欄位,訓練頁面就會為該欄位提供 rdkit-property。RDKit 無法解析的結構會被排除在訓練之外,得不到預測,並計入執行的 n_unparseable 指標。對於選擇題或 Likert 題,訓練頁面也會提供文本訓練器,因為這些條目仍然有文本。

滑塊的預測會按滑塊的 step 取整,並保持在 min_value 與 max_value 之間,所以模型提出的總是滑塊上存在的位置。滑塊的主得分是精確位置準確率,把預測為 6 而答案為 7 算作錯誤。在判斷預測是否足夠接近、可以展示之前,請把平均絕對誤差 val_mae 或 test_mae 與準確率一起檢視。執行要求 train 劃分中至少有 10 個已標註的分子。model、class_weight、radius 和 bits 等參數寫在訓練頁面的 Parameters 欄位中,或寫在 API 請求裡:

bash
curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  http://localhost:8000/admin/training/api/runs \
  -d '{"trainer": "rdkit-property", "schemas": ["advance"],
       "params": {"model": "random_forest", "class_weight": "balanced"}}'

執行日誌會給出分子數、特徵數和模型。平衡的類別權重會讓稀少的答案佔更大分量。大多數化合物得到相同答案時,請使用 class_weight: balanced。執行結束後,標註者開啟一個未標註的化合物,會看到滑塊被設到模型的值,模型的選擇也已選中,兩者都可以編輯。

用 peak-cnn 挑選譜圖的峰

peak-cnn 從標註者給出的 peak_picking 答案中學習挑選峰。訓練器讀取方案的 viewer_field 所顯示的譜圖,使用與譜圖顯示相同的讀取器。訓練目標是標註者保留的每個峰,包括接受的建議和新增的峰。標註者拒絕的建議標明該處沒有峰。模型只預測峰的位置,標註者仍然要為每個峰給出歸屬、標籤或多重性。請在負責訓練的機器上安裝 PyTorch:

bash
pip install "potato-annotation[train-vision]"

下面的配置請標註者審閱 1H NMR 譜圖上建議的峰:

yaml
annotation_task_name: "1H NMR peak picking"
task_dir: .
media_directory: media
output_annotation_dir: annotation_output
data_files: [data/spectra.jsonl]
item_properties: {id_key: id, text_key: note}
user_config:
  allow_all_users: true
 
instance_display:
  fields:
    - key: spectrum
      type: spectrum
      label: "1H NMR"
      display_options:
        prepick:
          method: auto            # item peaks, then model, then file peak table, then detector
          prominence: 0.05
          max_peaks: 30
 
annotation_schemes:
  - annotation_type: peak_picking
    name: peaks
    description: "Accept or reject each suggested peak, and add any the suggestions missed"
    viewer_field: spectrum
    peak_fields:
      - name: assignment
        placeholder: "e.g. OCH2"
    require_review: true
    label_requirement: {required: true}
 
model_training:
  enabled: true
  trainer_params:
    peak-cnn:
      tolerance: 0.02             # ppm
  writeback:
    schemas: [peaks]
    include_unlabeled: true

模型的峰只通過顯示的 prepick 設定送到標註者面前,沒有 prepick 時,顯示完全不給出建議。在 method: auto 下,顯示先給出條目自帶的 peaks,然後是模型的峰,再是檔案的峰表,最後是檢測器找到的峰。第一次執行之前,標註者審閱的是檢測器的建議,執行之後,同樣的條目無需重啟就顯示模型的峰。使用 method: model 時,顯示只給出模型的峰。想衡量在沒有混入檢測器建議的情況下標註者接受模型峰的頻率,請使用 method: model。每個模型峰在列表中帶有“· model”,顯示保留置信度最高的 max_peaks 個,接受的模型峰以 source: model 儲存。

tolerance 參數設定預測的峰與已標註的峰在 x 單位上相距多遠仍算找到。沒有 tolerance 時,執行使用譜圖 x 範圍的 0.5%,12 ppm 的 1H 視窗的 0.5% 是 0.06 ppm。在 400 MHz 下,7 Hz 的耦合使多重峰的譜線相隔 0.0175 ppm。預設容差比這個間距更寬,所以配置改設為 0.02 ppm。執行對一對一匹配的峰(最近的優先)計算精確率、召回率和 F1,並報告匹配峰的平均偏移。在標註者說沒有峰的譜圖上預測出峰,算作誤報。執行要求 train 劃分中至少有 5 條已標註的譜圖。

每條譜圖在網路看到之前都會重取樣到 length 個點,預設為 4,096。峰比 x 範圍的 1/length 更窄時,例如高解析度質譜,請調大 length。讀取 mzML 檔案還需要 spectra extra,詳見譜圖顯示頁面。

用 signal-cnn 判斷睡眠分期和訊號區間

signal-cnn 從 EEG、ECG、EMG 和加速度計資料等生理和感測器記錄上的 interval_label 答案中學習。網路是一個小型 InceptionTime 模型,由三種寬度的並行卷積堆疊而成。訓練器用時間序列顯示的讀取器讀取每段記錄,取顯示所列的通道,把每個通道重取樣到 rate(預設 100 Hz),並對每個通道做標準化。請安裝 PyTorch,WFDB 和 Parquet 記錄還要安裝 signals extra:

bash
pip install "potato-annotation[train-vision,signals]"

下面的配置在整夜多導睡眠圖上以 30 秒為一個 epoch 給睡眠評分,方式與人工評分員把每個 epoch 標為清醒、N1、N2、N3 或 REM 相同:

yaml
annotation_task_name: "Sleep staging"
task_dir: .
media_directory: media
output_annotation_dir: annotation_output
data_files: [data/psg.jsonl]
item_properties: {id_key: id, text_key: note}
user_config:
  allow_all_users: true
 
instance_display:
  fields:
    - key: signal
      type: time_series
      label: "PSG"
      display_options:
        window: 30
        channels: ["EEG Fpz-Cz", "EOG", "EMG chin"]   # also the channels signal-cnn reads
 
annotation_schemes:
  - annotation_type: interval_label
    name: stage
    description: "Sleep stage of each 30-second epoch"
    signal_field: signal
    mode: epoch
    epoch_length: 30
    require_all_epochs: true
    label_requirement: {required: true}
    labels:
      - {name: W, key_value: "1"}
      - {name: N1, key_value: "2"}
      - {name: N2, key_value: "3"}
      - {name: N3, key_value: "4"}
      - {name: REM, key_value: "5"}
 
model_training:
  enabled: true
  writeback:
    schemas: [stage]
    include_unlabeled: true

方案的 mode 決定任務。在 epoch 模式下,每個已標註的 epoch 是一個訓練樣本,模型為新記錄的每個 epoch 打標籤。最後一個 epoch 的記錄不足該 epoch 長度的一半時,不會得到標籤。留出主得分是各已標註 epoch 上的 macro-F1。Macro-F1 給 N1 這樣的稀少分期與 N2 同樣的權重。預設情況下訓練損失按標籤頻率的倒數給每個分期加權(balanced: true),因此一個 N1 epoch 比一個 N2 epoch 分量更重。每段記錄都按第一段訓練記錄的通道及其順序讀取,缺少某個訓練通道的記錄,會用一條平線代替缺失的通道。沒有列出通道的顯示會把每個通道都交給訓練器,最多 max_channels(16)個。

free 模式改為在標出的片段上訓練。請在同一個 signal_field 上再新增一個 mode: free 的 interval_label 方案,用來標出偽跡或覺醒:

yaml
  - annotation_type: interval_label
    name: artifacts
    description: "Mark artifacts"
    signal_field: signal
    mode: free
    labels:
      - {name: artifact, key_value: "a"}
      - {name: spike, key_value: "s", kind: event}

在 free 模式下,網路在隨機的 30 秒裁剪片段(window_s)上訓練,給每個 1 秒的幀(frame_s)打標籤,把帶同一標籤的連續幀合併成一個區間,並丟棄短於 min_duration 的區間。主得分是區間 F1,它把預測的區間與帶同一標籤的已標註區間在 IoU 0.5 時匹配。像上面的 spike 標籤這樣的點事件既不訓練也不預測。執行要求 train 劃分中至少有 3 段已標註的記錄,epoch 模式還要求資料中至少有兩種不同的標籤。

模型的區間以虛線預標註的形式出現在記錄上,列表中帶有“· from the model”,在標註者第一次修改或點選“Keep these”時儲存。記錄自己的標註優先。包含睡眠圖的 EDF+ 檔案顯示睡眠圖而不是模型的區間,帶 preannotation_field 區間的條目也是如此。在方案上設定 pre_annotations: false 會同時關閉這兩種。

用 episode-model 處理機器人回合

episode-model 學習 episode_annotation 答案的三個層,即階段、結果和進度獎勵曲線。模型的輸入是回合記錄的每個數值序列,例如關節位置和夾爪狀態,每幀一行。輸入包括 series_shown 沒有顯示在螢幕上的序列,不包括影片。模型無法發現只在攝像頭畫面上出現、而機器人的測量值不變的階段。請安裝 PyTorch,LeRobot 資料集還需要 pyarrow,HDF5 演示還需要 h5py:

bash
pip install "potato-annotation[train-vision]"

下面的配置在抓取放置演示上收集全部三個層,如機器人回合頁面所述:

yaml
annotation_task_name: "Robot episode review"
task_dir: .
media_directory: media
output_annotation_dir: annotation_output
data_files: [data/episodes.jsonl]
item_properties: {id_key: id, text_key: note}
user_config:
  allow_all_users: true
 
annotation_schemes:
  - annotation_type: episode_annotation
    name: episode_review
    description: "Mark the phases, judge the outcome, and draw the progress."
    source_field: episode
    layers: [phases, outcome, reward]
    phases:
      - {name: reach, key_value: "1"}
      - {name: grasp, key_value: "2"}
      - {name: transport, key_value: "3"}
      - {name: place, key_value: "4"}
      - {name: retract, key_value: "5"}
    outcomes: [success, partial, failure]
    failure_causes: [missed grasp, object slipped, collision]
    reward_range: [0.0, 1.0]
    series_shown: [gripper, wrist_force, shoulder_lift, elbow]
 
model_training:
  enabled: true
  writeback:
    schemas: [episode_review]
    include_unlabeled: true

網路先對序列做卷積,再送入帶三個頭的雙向 GRU,一個頭預測每幀的階段,一個預測回合的結果,一個預測每幀的獎勵。答案中為空的層不計入該回合的損失,所以只標了結果的回合仍然訓練結果頭。訓練回合中包含至少兩種不同的結果後,結果頭才開始訓練。獎勵頭只在標註者畫了曲線的那一段上訓練,因為在這一段之外標註者沒有給出答案。長於 max_frames(2,000)的回合在隨機裁剪的片段上訓練。

預測具有已儲存答案的形狀,對方案顯示的各層,包含來自方案列表的階段、方案中的一個結果,以及 reward_range 之內 50 個點的獎勵曲線。預測會預填時間線,在標註者編輯時間線之後才會儲存。失敗原因和指令留給標註者。留出主得分是 0.1 秒網格上的階段 macro-F1。執行還報告 IoU 0.5 時的階段片段 F1、結果準確率,以及畫了曲線處的獎勵曲線 Pearson r。沒有階段的方案以結果準確率為主得分,其次是獎勵相關性。執行要求 train 劃分中至少有 5 個已標註的回合。Potato 沒有 rollout_evaluation 的訓練器,因為 rollout 偏好比較的是影片,而 episode-model 不讀取影片。

開始一次執行並檢查結果

新增 model_training 之後重啟伺服器:

bash
potato start config.yaml -p 8000

開啟 /admin/training,選擇方案和訓練器,然後點選 Train now。該頁面只提供適合每個方案的訓練器,未安裝的訓練器會列為“not installed”,附上安裝命令。執行結束後,Potato 在 Runs 表中列出留出得分,劃分和指標顯示在提示中。執行頁面列出每個指標以及每個條目所屬的劃分。GET /admin/training/api/status 中的回寫報告統計已釋出的預測數,以及每項檢查攔下的預測數,例如 n_skipped_low_confidence。

預標籤會讓標註者錨定在模型的答案上,所以請等留出得分足以支援展示模型的答案之後,再在新研究中開啟 include_unlabeled。在環審閱模型預標籤介紹優先提供置信度最低的預標籤,以及在修正上重新訓練,配置列出每個鍵、後端和回寫檢查。

延伸閱讀

參考文獻

  • Ismail Fawaz, H., Lucas, B., Forestier, G., Pelletier, C., Schmidt, D. F., Weber, J., Webb, G. I., Idoumghar, L., Muller, P.-A., and Petitjean, F. (2020). InceptionTime: Finding AlexNet for Time Series Classification. Data Mining and Knowledge Discovery, 34(6), 1936-1962. https://doi.org/10.1007/s10618-020-00710-y
  • Rogers, D., and Hahn, M. (2010). Extended-Connectivity Fingerprints. Journal of Chemical Information and Modeling, 50(5), 742-754. https://doi.org/10.1021/ci100050t