transformers-bio-nlp について
生物医学テキストを分析する研究者や開発者向けに、HuggingFace Transformers を使った生物医学 NLP を解説するガイドです。領域特化モデルを用いた抽出、分類、質問応答、生成、埋め込み、ファインチューニングを扱います。
主な機能
- 01生物医学固有表現認識
- 02抄録と文の分類
- 03抽出型質問応答
- 04生物医学テキスト生成
- 05検索用の文埋め込み
- 06ラベル付きデータでの微調整
活用シーン
- 01抄録から遺伝子、疾患、薬剤を抽出
- 02主題や臨床転帰で文献を絞り込み
- 03生物医学テキストから回答を検索
- 04意味的類似度で文献を順位付け
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jaechang-hits/ sciagent-skills --skill transformers-bio-nlpインストール後、Claude Code で/transformers-bio-nlpを入力して読み込まれたか確認します。
詳細
生物医学 NLP 向け Transformers
概要
HuggingFace Transformers では、生物医学分野の 500 以上の言語モデルを読み込み、実行し、ファインチューニングするための統一 API が提供されています。主要な生物医学モデルである BioBERT(PubMed の抄録と PMC の全文で学習)、PubMedBERT(PubMed のデータのみを使って一から学習)、BioGPT(PubMed で学習した生成モデル)、BioMedLM は、生物医学分野の NER、関係抽出、質問応答において、汎用 BERT を大幅に上回る性能を発揮します。pipeline() の抽象化により、トークン化、推論、後処理を 1 回の呼び出しで処理できます。タスク固有のラベル付きデータ(例:化学物質・疾患の NER 用 BC5CDR)であれば、単一 GPU で 1 時間以内にファインチューニングできます。datasets ライブラリを使えば、標準的な生物医学ベンチマークに直接アクセスできます。
使用する場面
- 生物医学分野の抄録から遺伝子名、疾患への言及、薬剤名、化学物質を抽出する(NER)
- 抄録をトピック別に分類するほか、臨床転帰の感情やシステマティックレビュー用の PICO 要素を分類する
- 抽出型 QA(BioASQ 形式)を使って、生物医学文献に関する具体的な質問に回答する
- BioGPT または BioMedLM を使って、生物医学テキストから仮説や要約を生成する
- 事前学習済みの生物医学モデルを、カスタムのラベル付きデータセット(例:研究室で作成したアノテーション)でファインチューニングする
- 文献全体を対象とした意味的類似性検索のために、生物医学分野の文をベクトル化する
- 高速なルール拡張 NER には spaCy + en_core_sci_lg を、依存構造解析には Stanza を使用する
前提条件
- Python パッケージ:
transformers、torch、datasets、accelerate、sentencepiece - GPU:ファインチューニングには強く推奨されます。単一のテキストであれば、CPU での推論も実用的です
- データ要件:生物医学分野のプレーンテキスト文字列。ファインチューニングには、BIO/IOB 形式のアノテーション付きデータが必要です
pip install transformers torch datasets accelerate sentencepiece
# For GPU (CUDA 11.8)
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118
クイックスタート
from transformers import pipeline
# Named entity recognition with BioBERT
ner = pipeline("ner", model="allenai/scibert_scivocab_cased",
aggregation_strategy="simple")
text = "BRCA1 mutations are associated with increased risk of breast cancer and ovarian cancer."
entities = ner(text)
for ent in entities:
print(f" {ent['word']:20s} {ent['entity_group']:10s} score={ent['score']:.3f}")
コア API
モジュール 1:固有表現認識(NER)
事前学習済みの NER モデルを使って、生物医学分野のエンティティを抽出します。
from transformers import pipeline, AutoTokenizer, AutoModelForTokenClassification
# BioBERT fine-tuned for NER (genes, diseases, chemicals)
# Common choices:
# "allenai/scibert_scivocab_cased" — scientific NER
# "d4data/biomedical-ner-all" — multi-entity biomedical NER
# "pruas/BENT-PubMedBERT-NER-Gene" — gene-specific NER
ner_pipe = pipeline(
"ner",
model="d4data/biomedical-ner-all",
aggregation_strategy="simple", # merge subword tokens into words
device=-1 # -1=CPU, 0=GPU
)
abstracts = [
"Imatinib inhibits the BCR-ABL1 tyrosine kinase and is first-line treatment for CML.",
"EGFR mutations in non-small cell lung cancer predict response to erlotinib.",
]
for text in abstracts:
entities = ner_pipe(text)
print(f"\nText: {text[:60]}...")
for e in entities:
print(f" [{e['entity_group']}] '{e['word']}' (score={e['score']:.2f})")
# Manual tokenization + inference for batch processing
from transformers import AutoTokenizer, AutoModelForTokenClassification
import torch
model_name = "allenai/scibert_scivocab_cased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForTokenClassification.from_pretrained(model_name)
model.eval()
text = "Metformin activates AMPK and reduces hepatic glucose production in type 2 diabetes."
inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=512)
with torch.no_grad():
outputs = model(**inputs)
logits = outputs.logits # shape: (1, seq_len, n_labels)
predictions = logits.argmax(dim=-1)[0]
tokens = tokenizer.convert_ids_to_tokens(inputs["input_ids"][0])
labels = [model.config.id2label[p.item()] for p in predictions]
for token, label in zip(tokens[1:-1], labels[1:-1]): # skip [CLS] and [SEP]
if label != "O":
print(f" {token:20s} {label}")
モジュール 2:テキスト分類
生物医学分野の抄録または文を分類します。
from transformers import pipeline
# Zero-shot classification — no fine-tuning needed
zs_clf = pipeline("zero-shot-classification",
model="facebook/bart-large-mnli",
device=-1)
abstract = """
This randomized controlled trial evaluated the efficacy of pembrolizumab versus
chemotherapy in patients with advanced non-small-cell lung cancer. Overall survival
was significantly improved in the pembrolizumab arm (HR=0.60, 95% CI 0.41-0.89).
"""
candidate_labels = ["clinical trial", "basic research", "meta-analysis", "review"]
result = zs_clf(abstract, candidate_labels)
print("Zero-shot classification:")
for label, score in zip(result["labels"], result["scores"]):
print(f" {label:20s}: {score:.3f}")
# Fine-tuned sentiment/outcome classification
from transformers import pipeline
# Example: classify clinical outcome sentiment
clf = pipeline("text-classification",
model="pruas/BENT-PubMedBERT-NER-Gene", # use appropriate task-specific model
device=-1)
sentences = [
"Treatment significantly improved overall survival (p<0.001).",
"No statistically significant difference was observed between groups.",
]
results = clf(sentences)
for sent, result in zip(sentences, results):
print(f" [{result['label']} | {result['score']:.2f}] {sent[:50]}...")
モジュール 3:生物医学分野の質問応答
生物医学テキストの文章から回答を抽出します。
from transformers import pipeline
# Extractive QA: find answer span within context
qa_pipe = pipeline(
"question-answering",
model="sultan/BioM-ELECTRA-Large-SQuAD2", # biomedical QA model
device=-1
)
context = """
BRCA1 is a tumor suppressor gene located on chromosome 17q21. Pathogenic variants
in BRCA1 confer a lifetime breast cancer risk of 50-72% and ovarian cancer risk
of 44-46%. BRCA1 protein functions in DNA double-strand break repair via
homologous recombination.
"""
questions = [
"What chromosome is BRCA1 located on?",
"What is the lifetime breast cancer risk from BRCA1 variants?",
"What DNA repair pathway does BRCA1 participate in?",
]
for q in questions:
result = qa_pipe(question=q, context=context)
print(f"Q: {q}")
print(f"A: {result['answer']} (score={result['score']:.3f})\n")
モジュール 4:BioGPT によるテキスト生成
生物医学テキスト、仮説、要約を生成します。
from transformers import AutoTokenizer, BioGptForCausalLM
import torch
model_name = "microsoft/biogpt"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = BioGptForCausalLM.from_pretrained(model_name)
model.eval()
prompt = "The role of VEGF in tumor angiogenesis"
inputs = tokenizer(prompt, return_tensors="pt")
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=100,
num_beams=5,
early_stopping=True,
no_repeat_ngram_size=3,
pad_token_id=tokenizer.eos_token_id,
)
generated = tokenizer.decode(outputs[0], skip_special_tokens=True)
print(f"Generated:\n{generated}")
モジュール 5:意味検索用の文の埋め込み
類似性検索やクラスタリングのために、生物医学テキストをベクトル化します。
from transformers import AutoTokenizer, AutoModel
import torch
import numpy as np
def mean_pooling(model_output, attention_mask):
"""Mean pooling across token embeddings."""
token_embeddings = model_output.last_hidden_state
input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()
return (token_embeddings * input_mask_expanded).sum(1) / input_mask_expanded.sum(1)
# PubMedBERT for biomedical sentence embeddings
model_name = "microsoft/BiomedNLP-BiomedBERT-base-uncased-abstract-fulltext"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name)
model.eval()
sentences = [
"BRCA1 is involved in DNA double-strand break repair.",
"Homologous recombination requires BRCA1 and BRCA2.",
"Metformin inhibits hepatic gluconeogenesis via AMPK.",
]
inputs = tokenizer(sentences, padding=True, truncation=True,
max_length=512, return_tensors="pt")
with torch.no_grad():
outputs = model(**inputs)
embeddings = mean_pooling(outputs, inputs["attention_mask"])
embeddings = torch.nn.functional.normalize(embeddings, p=2, dim=1).numpy()
# Compute cosine similarity
from numpy.linalg import norm
sim_01 = np.dot(embeddings[0], embeddings[1])
sim_02 = np.dot(embeddings[0], embeddings[2])
print(f"Similarity (BRCA1 repair vs. HR): {sim_01:.3f}")
print(f"Similarity (BRCA1 repair vs. Metformin): {sim_02:.3f}")
モジュール 6:カスタムデータによるファインチューニング
ラベル付き NER データセットを使って、生物医学モデルをファインチューニングします。
from transformers import (AutoTokenizer, AutoModelForTokenClassification,
TrainingArguments, Trainer, DataCollatorForTokenClassification)
from datasets import Dataset
import numpy as np
# Example: minimal NER fine-tuning setup
model_name = "microsoft/BiomedNLP-BiomedBERT-base-uncased-abstract"
label_list = ["O", "B-GENE", "I-GENE", "B-DISEASE", "I-DISEASE"]
id2label = {i: l for i, l in enumerate(label_list)}
label2id = {l: i for i, l in enumerate(label_list)}
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForTokenClassification.from_pretrained(
model_name, num_labels=len(label_list), id2label=id2label, label2id=label2id
)
# Training arguments
training_args = TrainingArguments(
output_dir="./biomed_ner_finetuned",
num_train_epochs=3,
per_device_train_batch_size=16,
per_device_eval_batch_size=32,
warmup_steps=100,
weight_decay=0.01,
logging_dir="./logs",
evaluation_strategy="epoch",
save_strategy="epoch",
load_best_model_at_end=True,
)
print(f"Model ready for fine-tuning: {model_name}")
print(f"Labels: {label_list}")
# trainer = Trainer(model=model, args=training_args, ...)
# trainer.train()
重要な概念
生物医学テキストのトークン化
生物医学テキストには、特殊なトークン(遺伝子記号、薬剤名、化学物質の SMILES、数値)が含まれており、WordPiece や BPE のトークナイザーでは予想外の分割が発生します。たとえば、「BRCA1」は ["BR", "##CA", "##1"] に分割されます。このようなサブワード分割は分類タスクには影響しませんが、NER には影響します。pipeline() で aggregation_strategy="simple" または "first" を使い、サブワードに対する予測を単語レベルにまとめ直してください。
BIO ラベリング方式
NER では BIO(Begin-Inside-Outside)タグ付けを使用します。B-GENE は遺伝子名の最初のトークン、I-GENE はその続きのトークン、O はエンティティ以外のトークンを示します。ファインチューニング時は、最初のサブワード以外のラベルを -100(損失関数で無視される値)に設定し、ラベルをサブワードトークンに対応付けます。
よくあるワークフロー
ワークフロー 1:抄録のバッチ NER とエンティティの集約
from transformers import pipeline
import pandas as pd
ner_pipe = pipeline("ner", model="d4data/biomedical-ner-all",
aggregation_strategy="simple", device=-1)
abstracts = [
"Pembrolizumab combined with chemotherapy significantly improved progression-free survival in HER2-positive breast cancer.",
"Inhibition of EGFR by gefitinib is effective in patients with activating EGFR mutations in exons 19 and 21.",
"CRISPR-Cas9 editing of the PCSK9 gene in hepatocytes reduces LDL cholesterol in murine models.",
]
records = []
for i, text in enumerate(abstracts):
entities = ner_pipe(text)
for e in entities:
records.append({
"abstract_id": i,
"entity": e["word"],
"type": e["entity_group"],
"score": round(e["score"], 3),
})
df = pd.DataFrame(records)
print(df.groupby("type")["entity"].apply(list).to_string())
df.to_csv("extracted_entities.csv", index=False)
print(f"\nExtracted {len(df)} entity mentions across {len(abstracts)} abstracts")
ワークフロー 2:文献検索用の意味的類似性ランキング
from transformers import AutoTokenizer, AutoModel
import torch
import numpy as np
model_name = "microsoft/BiomedNLP-BiomedBERT-base-uncased-abstract-fulltext"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name)
model.eval()
def embed(texts):
enc = tokenizer(texts, padding=True, truncation=True,
max_length=512, return_tensors="pt")
with torch.no_grad():
out = model(**enc)
vecs = out.last_hidden_state[:, 0, :] # [CLS] token
return torch.nn.functional.normalize(vecs, dim=1).numpy()
query = "CRISPR base editing for correction of point mutations in genetic disease"
corpus = [
"Base editing enables precise single-base changes in genomic DNA without double-strand breaks.",
"CAR-T cell therapy targets CD19 in B-cell acute lymphoblastic leukemia.",
"Prime editing uses reverse transcriptase to install targeted edits at specific loci.",
"RNA interference silences gene expression via RISC-mediated mRNA cleavage.",
]
q_emb = embed([query])
c_emb = embed(corpus)
scores = (q_emb @ c_emb.T).flatten()
ranked = sorted(zip(scores, corpus), reverse=True)
print("Top results:")
for score, text in ranked:
print(f" [{score:.3f}] {text[:70]}...")
主なパラメーター
| パラメーター | モジュール/関数 | デフォルト | 範囲/オプション | 効果 |
|---|---|---|---|---|
model | pipeline() | — | HuggingFace のモデル ID 文字列 | 読み込む事前学習済みモデルです。タスクに適合している必要があります |
aggregation_strategy | NER pipeline | "none" | "none"、"simple"、"first"、"average" | サブワードの NER 予測を結合します。単語単位の出力には "simple" を使用します |
device | pipeline() | -1 | -1(CPU)、0(GPU 0)、1(GPU 1) | 推論デバイスです |
max_length | tokenizer | 512 | 128–2048(モデルによって異なります) | 最大トークン長です。これを超える入力は切り詰められます |
max_new_tokens | model.generate() | 20 | 1–1000 | テキスト生成モデルが生成するトークン数です |
num_beams | model.generate() | 1 | 1–10 | ビームサーチの幅です。値を大きくすると品質が向上しますが、処理は遅くなります |
num_train_epochs | TrainingArguments | 3 | 1–10 | ファインチューニングのエポック数です |
per_device_train_batch_size | TrainingArguments | 8 | 4–32 | GPU ごとのバッチサイズです。メモリ不足の場合は値を小さくしてください |
weight_decay | TrainingArguments | 0.0 | 0.01–0.1 | ファインチューニング時の L2 正則化です |
ベストプラクティス
-
汎用 BERT ではなく、ドメイン特化型モデルを使用する:PubMed でゼロから学習した PubMedBERT は、生物医学分野の NER で BERT-base を 5~15%上回ります。タスク固有のデータでファインチューニングする前に、必ず生物医学分野の事前学習済みモデルから始めてください。
-
本番環境で使用する前にモデルのライセンスを確認する:一部のモデル(BioGPT、BioMedLM)は研究目的に限定されたライセンスです。商用アプリケーションにデプロイする前に、HuggingFace のモデルカードにあるライセンス欄を確認してください。
-
単語単位の NER 出力には
aggregation_strategy="simple"を使用する:デフォルトの"none"はサブワードトークンを返すため、後処理が難しくなります。"simple"は先頭トークン方式でサブワードトークンを結合します。 -
文の途中ではなく、文の境界で切り詰める:512 トークンを超える長い生物医学分野の抄録は、エンコードする前に文の境界で分割してください。文の途中で切り詰めると、切断位置付近のエンティティに対する NER の精度が低下します。
よく使われるレシピ
レシピ:PubMed の抄録から薬剤と疾患のペアを抽出する
from transformers import pipeline
from itertools import product
ner = pipeline("ner", model="d4data/biomedical-ner-all",
aggregation_strategy="simple", device=-1)
def extract_drug_disease_pairs(text):
entities = ner(text)
drugs = [e["word"] for e in entities if e["entity_group"] in ("DRUG", "CHEMICAL")]
diseases = [e["word"] for e in entities if e["entity_group"] in ("DISEASE", "CONDITION")]
return list(product(drugs, diseases))
text = "Imatinib and nilotinib both target BCR-ABL1 in chronic myeloid leukemia and Philadelphia chromosome-positive ALL."
pairs = extract_drug_disease_pairs(text)
print("Drug-Disease pairs:")
for drug, disease in pairs:
print(f" {drug} → {disease}")
レシピ:文単位で抄録をフィルタリングする
from transformers import pipeline
clf = pipeline("zero-shot-classification",
model="facebook/bart-large-mnli", device=-1)
abstracts = [
"We present a phase 3 randomized controlled trial of semaglutide in type 2 diabetes.",
"Structural analysis of the SARS-CoV-2 spike protein RBD domain by cryo-EM.",
"A retrospective cohort study of 1,200 ICU patients during the COVID-19 pandemic.",
]
label_options = ["randomized controlled trial", "observational study", "structural biology", "computational study"]
for abstract in abstracts:
result = clf(abstract, label_options)
print(f"Type: {result['labels'][0]} ({result['scores'][0]:.2f})")
print(f" {abstract[:70]}...\n")
トラブルシューティング
| 問題 | 原因 | 解決策 |
|---|---|---|
推論中に CUDA out of memory が発生する | バッチが GPU の VRAM に対して大きすぎます | バッチサイズを小さくします。CPU には device=-1 を使用します。FP16 には model.half() を使用します |
NER がサブワードトークン(##CA)を返す | aggregation_strategy が設定されていません | pipeline() で aggregation_strategy="simple" を設定します |
| モデルのダウンロードがタイムアウトする | モデルファイルが大きく(1~10 GB)、接続速度が遅いです | HF_HUB_OFFLINE=1 を設定し、huggingface-cli download で手動ダウンロードします |
| 長い抄録の末尾で NER がエンティティを見落とす | 入力が 512 トークンで切り詰められています | 抄録を文ごとに分割し、それぞれを個別に処理します |
ファインチューニングの損失が NaN になる | 学習率が高すぎるか、勾配爆発が起きています | learning_rate を 2e-5 に下げ、勾配クリッピング max_grad_norm=1.0 を有効にします |
| 特化したドメインで誤ったエンティティが返される | 汎用の生物医学モデルがサブドメインに適していません | ドメインラベル付きデータでファインチューニングし、より特化したモデル(例:遺伝子のみを対象とする NER)を使用します |
| BioGPT が同じテキストを繰り返し生成する | no_repeat_ngram_size が小さすぎます | no_repeat_ngram_size=3 または 4 に設定し、num_beams を増やします |
関連スキル
pubmed-database— 生物医学 NLP パイプラインの入力となる PubMed の抄録を取得しますbiorxiv-database— 査読前のプレプリントを取得して NLP 分析に使用しますscientific-critical-thinking— 研究上の結論に使用する前に、NLP で抽出したエビデンスの品質を評価します
参考資料
- HuggingFace Transformers のドキュメント — パイプライン、トークナイザー、トレーニング API
- BioBERT 論文:Lee et al. (2020)、Bioinformatics — PubMed と PMC を使った事前学習
- PubMedBERT 論文:Gu et al. (2021)、ACL — PubMed を使ったスクラッチからの事前学習
- BioGPT 論文:Luo et al. (2022)、Briefings in Bioinformatics — 生成型の生物医学言語モデル
- BioCreative ベンチマーク — 標準的な NER および関係抽出データセット