Azure Cognitive Servicesを使って、医療ドメインのエンティティ分析をやってみた

# はじめに
今の会社で、Azure等のクラウドサービスを使って、AIをビジネス活用することが増えました。
AzureのAIサービスの中には、学習済のAIモデルを簡単に使える、Azure Cognitive Servicesがあります。
このAzure Cognitive Services等のAIクラウドサービスには、多くのサービスが提供されおり、日々アップデートされております。
このサービスをうまく活用することが、AIを使ったビジネスを成功させる第一歩であると感じております。
そこで、Azure Cognitive Servicesでできることの学習も兼ねて、今回新しく登場したサービスである、ヘルスケアエンティティ分析（Text Analytics for health ）を使ってみたいと思います。

# 対象者
- Azure Cognitive Servicesで何ができるか知りたい方
- 固有表現抽出や関係抽出などのNLPタスクに興味がある方
- 医療ドメインに興味がある方

## Azure Cognitive Servicesとは
Azure Cognitive Services は、開発者が直接的な人工知能 (AI) またはデータ サイエンスのスキルや知識がなくてもコグニティブかつインテリジェントなアプリケーションを構築できる、クラウドベースの AI サービスです。
Azure Cognitive Services を使用して、見たり、聞いたり、話したり、分析したりできるコグニティブ ソリューションを使用したコグニティブ機能をそのアプリケーションに容易に追加することができます。

データ分析には、以下の5つの段階の分析があると言われておりますが、Azure Cognitive Servicesでできることは、この中では、認知的分析に該当します。

**5つのデータ分析**
- Descriptive Analytics:記述的分析（過去から現在、どうだったか）
例：EDA（探索的データ分析）、モニタリング、異常検知

- Diagnostic Analytics:診断的分析（過去から現在、何が起こったか）
例：EDA（探索的データ分析）、要因分析

- Predictive Analytics:予測的分析（現在から未来、どうなるか）
例：将来予測、シミュレーション

- Prescriptive Analytics:処方的分析（今とるべきアクションは何か）
例：レコメンド、評価決定

- Cognitive Analytics:認知的分析。上記4つの自動化・AI化
例：画像認識AIや動画の分析、自然言語の解釈や顧客の質疑応答など


## どんなサービスがある？
Azure Cognitive Services は、次の 4 つのサービスに大別できます。

**視覚**
- Computer Vision サービスを使用すると、画像を処理して情報を返すための高度なコグニティブ アルゴリズムにアクセスできます。
- Custom Vision Service を使用すると、独自の画像分類器を構築、デプロイ、改良できます。 画像分類器は、視覚特性に基づいて画像にラベルを適用する AI サービスです。
- Face サービスは、顔属性の検出と認識を有効にする、高度な顔アルゴリズムへのアクセスを提供します。

**音声**
- Speech サービスは、音声対応の機能をアプリケーションに追加します。 Speech サービスには、音声テキスト変換、テキスト読み上げ、音声翻訳など、さまざまな機能が含まれます。


**Language**
Azure Language サービスにより、テキストを理解して分析するための複数の自然言語処理 (NLP) 機能が提供されます。
- Translator によって、ほぼリアルタイムでのマシン ベースのテキスト翻訳が提供されます。
- Language Understanding (LUIS) は、カスタムの機械学習インテリジェンスをユーザーの自然言語での会話テキストに適用して、全体の意味を予測し、関連性のある情報を引き出す、クラウドベースの会話型 AI サービスです。
- QnA Maker を使用すると、半構造化コンテンツから質疑応答サービスを構築できます。

**決定**
- Anomaly Detector では、時系列データを監視し、その中の異常を検出できます。
- Content Moderator は、不快感を与えたり、望ましくなかったり、危険であったりする可能性があるコンテンツを監視します。
- Personalizer は、ユーザーのリアルタイムの動作から学習し、ユーザーに表示する最良のエクスペリエンスを選択できるようにします。


## 新サービス:ヘルスケアエンティティ分析とは、何ができるか
ヘルスケアエンティティ分析（Text Analytics for health ）は、Languageサービスに該当します。執筆時点では比較的新しいサービスになります。

医療ドメイン内で認識されるエンティティを抽出し、入力ドキュメント内のエンティティ間のリレーションシップと、UMLS、CHV、MSH など、さまざまな既知のデータベースの既知の情報源へのリンクを識別します。

参考：https://learn.microsoft.com/ja-jp/azure/cognitive-services/language-service/text-analytics-for-health/overview?tabs=ner

医師のメモ、退院の概要、臨床文書、電子カルテなどの非構造化テキストから関連する医療情報を抽出して、以下の4タスクのラベル付けができます。

### 1.固有表現認識
Named Entity Recognition は、診断、薬の名前、症状/徴候、年齢など、1 つ以上のセマンティック タイプに関連付けることができる非構造化テキストで言及されている単語やフレーズを検出します。

### 2.関係抽出
関係抽出は、テキストで言及されている概念間の意味のある関係を識別します。たとえば、「状態の時間」関係は、状態名を時間に関連付けることによって、または略語と完全な説明の間に関連付けることによって検出されます

### 3.エンティティのリンク
エンティティ リンクは、テキストで言及されている名前付きエンティティを、Unified Medical Language System (UMLS) などの概念の定義済みデータベースにある概念に関連付けることにより、個別のエンティティを明確にします。正規化の追加形式として、医療概念にも優先命名が割り当てられます。

### 4.アサーション検出
医療コンテンツの意味は、誤って伝えられた場合に重大な意味を持つ可能性がある否定的または条件付きのアサーションなどの修飾子によって大きく影響されます。Text Analytics for health は、テキスト内のエンティティのアサーション検出の 3 つのカテゴリをサポートしています。

**※使用上の注意事項（[ドキュメントからの引用](https://learn.microsoft.com/ja-jp/azure/cognitive-services/language-service/text-analytics-for-health/overview?tabs=ner)）**
これらの機能は、専門的な医療のアドバイスや医学的意見、診断、治療、または医療専門家による医学的判断に代わるものとして実装またはデプロイするために設計されたり、それを意図されたりしたものではなく、そのようには使用しないでください。
お客様は、健康のために Text Analytics を使用することについて単独で責任を負うものとします

## 使ってみた

では、早速、ヘルスケアエンティティ分析を使っていきます。
例文は、[チュートリアル](https://learn.microsoft.com/en-us/azure/cognitive-services/language-service/text-analytics-for-health/quickstart?pivots=programming-language-python)にある文章を使用します。

~~~
例文：['Subject is taking 100mg of ibuprofen twice daily']
~~~

注: ヘルスケアエンティティ分析は、API バージョン v3.1 以降でのみ使用できます。

使用したコードは以下です。
~~~
# ヘルスケアエンティティ分析を使う準備
from azure.core.credentials import AzureKeyCredential
from azure.ai.textanalytics import TextAnalyticsClient
import pandas as pd

COG_SERVICE_KEY="your_cognitive_services_key"
COG_SERVICE_ENDPOINT="your_cognitive_services_endpoint"

credential = AzureKeyCredential(COG_SERVICE_KEY)
endpoint=COG_SERVICE_ENDPOINT

text_analytics_client = TextAnalyticsClient(endpoint, credential)

# 使用する文章
documents = ["Subject is taking 100mg of ibuprofen twice daily"]

# ヘルスケアエンティティ分析の適用
poller = text_analytics_client.begin_analyze_healthcare_entities(documents)
result = poller.result()

docs = [doc for doc in result if not doc.is_error]
~~~

では、結果を確認していきます。
各タスクごとに見ていきます。

### 固有表現認識

~~~
----------------------------------------
エンティティ: 100mg
----------------------------------------
　正規化されたテキスト: None
　カテゴリー: Dosage
　第二のカテゴリー: None
　オフセット: 18
　信頼スコア: 0.99
 
~~~

”100mg”はちゃんとDosage（投与量）にカテゴライズされています。

~~~
----------------------------------------
エンティティ: ibuprofen
----------------------------------------
　正規化されたテキスト: ibuprofen
　カテゴリー: MedicationName
　第二のカテゴリー: None
　オフセット: 27
　信頼スコア: 1.0
 
~~~

"ibuprofen"は薬品名であることが分かります。
※wikipediaからの引用 :イブプロフェンは、プロピオン酸系に分類される非ステロイド系消炎鎮痛剤 の1種である。日本では商標名ブルフェンで知られ、医療用だけでなく一般医薬品としても広く流通している。関節炎、生理痛および発熱の症状を緩和し、また炎症部位の鎮痛に用いる。

~~~
----------------------------------------
エンティティ: twice daily
----------------------------------------
　正規化されたテキスト: None
　カテゴリー: Frequency
　第二のカテゴリー: None
　オフセット: 37
　信頼スコア: 1.0
~~~

"一日2回"は頻度というカテゴリーであってそうです。

### 関係抽出

次に関係抽出の結果です。

~~~
----------------------------------------
タイプの関係: DosageOfMedication には次のロールがあります
----------------------------------------
　エンティティ '100mg'を使用した役割'Dosage'
　エンティティ 'ibuprofen'を使用した役割'Medication'
----------------------------------------
タイプの関係: FrequencyOfMedication には次のロールがあります
----------------------------------------
　エンティティ 'ibuprofen'を使用した役割'Medication'
　エンティティ 'twice daily'を使用した役割'Frequency'
~~~

エンティティ「イブプロフェン」を役割「薬」として、
エンティティ「1 日 2 回」を役割「頻度」で、
エンティティ「100mg」を役割「投与量」使ったという関係が抽出されました。


### エンティティのリンク
エンティティ「イブプロフェン」においては、エンティティのリンクも抽出されました。
~~~
----------------------------------------
エンティティ: ibuprofen
----------------------------------------
　正規化されたテキスト: ibuprofen
　カテゴリー: MedicationName
　第二のカテゴリー: None
　オフセット: 27
　信頼スコア: 1.0
　データソース:
　　エンティティID: C0020740
　　名前: UMLS
　　エンティティID: 0000019879
　　名前: AOD
　　エンティティID: M01AE01
　　名前: ATC
　　エンティティID: 0046165
　　名前: CCPSS
　　エンティティID: 0000006519
　　名前: CHV
　　エンティティID: 2270-2077
　　名前: CSP
　　エンティティID: DB01050
　　名前: DRUGBANK
　　エンティティID: 1611
　　名前: GS
　　エンティティID: sh97005926
　　名前: LCH_NW
　　エンティティID: LP16165-0
　　名前: LNC
　　エンティティID: 40458
　　名前: MEDCIN
　　エンティティID: d00015
　　名前: MMSL
　　エンティティID: D007052
　　名前: MSH
　　エンティティID: WK2XYI10QM
　　名前: MTHSPL
　　エンティティID: C561
　　名前: NCI
　　エンティティID: C561
　　名前: NCI_CTRP
　　エンティティID: 00803
　　名前: NCI_DCP
　　エンティティID: NSC0256857
　　名前: NCI_DTP
　　エンティティID: WK2XYI10QM
　　名前: NCI_FDA
　　エンティティID: CDR0000613511
　　名前: NCI_NCI-GLOSS
　　エンティティID: 002377
　　名前: NDDF
　　エンティティID: CDR0000040475
　　名前: PDQ
　　エンティティID: x02MO
　　名前: RCD
　　エンティティID: 5640
　　名前: RXNORM
　　エンティティID: E-7772
　　名前: SNM
　　エンティティID: C-603C0
　　名前: SNMI
　　エンティティID: 387207008
　　名前: SNOMEDCT_US
　　エンティティID: m39860
　　名前: USP
　　エンティティID: MTHU000060
　　名前: USPMG
　　エンティティID: 4017840
　　名前: VANDF
~~~

### アサーション検出
~~~
None
~~~
今回の例では、アサーションについては検出されませんでした。


## 活用事例

次に、活用事例として、Kaggleのコンペに適用してみます。
題材は、以前Kaggleで開催されていた医療系NLPコンペである[NBMEのカルテデータ](https://www.kaggle.com/competitions/nbme-score-clinical-patient-notes/data)を使ってみます。
ちなみに、NBMEコンペの課題は以下です。

- タスク：患者メモ（カルテ等）から、症状の特徴を表すキーワード（臨床概念）を抽出します
- 臨床概念の例：（例：「食欲不振」）と、医学生が書いた臨床患者のメモにある様々な表現方法（例：「食事量が少ない」「服がゆったり着られる」）を自動的に対応付けたい

では、適用結果です。
全て表示すると、情報量が多くなるので、エンティティのみ表示します。
使用したコードは以下です。
~~~
#データ読み込み

import pandas as pd
df = pd.read_csv("./input/nbme-score-clinical-patient-notes/patient_notes.csv")
documents = [df.pn_history[0]]

#ヘルスケアエンティティ分析の適用

poller = text_analytics_client.begin_analyze_healthcare_entities(documents)
result = poller.result()

docs = [doc for doc in result if not doc.is_error]

~~~

~~~
#ヘルスケアエンティティ分析の結果の表示

print("ヘルスケアエンティティ分析の結果:")
print("-"*40)
print(f"エンティティの総数: {len(doc.entities)}")
print("-"*40)
n=0
for idx, doc in enumerate(docs):
    for entity in doc.entities:
        print(f"エンティティ{n}: {entity.text}")
		n+=1
		
~~~

適用するカルテの内容
~~~
["17-year-old male, has come to the student health clinic complaining of heart pounding. Mr. Cleveland's mother has given verbal consent for a history, physical examination, and treatment\r\n-began 2-3 months ago,sudden,intermittent for 2 days(lasting 3-4 min),worsening,non-allev/aggrav\r\n-associated with dispnea on exersion and rest,stressed out about school\r\n-reports fe feels like his heart is jumping out of his chest\r\n-ros:denies chest pain,dyaphoresis,wt loss,chills,fever,nausea,vomiting,pedal edeam\r\n-pmh:non,meds :aderol (from a friend),nkda\r\n-fh:father had MI recently,mother has thyroid dz\r\n-sh:non-smoker,mariguana 5-6 months ago,3 beers on the weekend, basketball at school\r\n-sh:no std"]
~~~

結果
~~~
ヘルスケアエンティティ分析の結果:
----------------------------------------
エンティティの総数: 35
----------------------------------------
エンティティ0: 17-year-old
エンティティ1: male
エンティティ2: health clinic
エンティティ3: heart pounding
エンティティ4: mother
エンティティ5: physical examination
エンティティ6: treatment
エンティティ7: 2
エンティティ8: 3 months ago
エンティティ9: sudden
エンティティ10: intermittent
エンティティ11: 2 days
エンティティ12: 3
エンティティ13: 4 min
エンティティ14: dispnea
エンティティ15: stressed out
エンティティ16: heart is jumping out of his chest
エンティティ17: ros
エンティティ18: chest pain
エンティティ19: dyaphoresis
エンティティ20: wt loss
エンティティ21: chills
エンティティ22: fever
エンティティ23: nausea
エンティティ24: vomiting
エンティティ25: pedal edeam
エンティティ26: aderol
エンティティ27: friend
エンティティ28: father
エンティティ29: MI
エンティティ30: mother
エンティティ31: thyroid dz
エンティティ32: 5
エンティティ33: 6 months ago
エンティティ34: std
~~~

35個のエンティティが抽出されました。

次に、気になるエンティティの詳細を見てみます。
見慣れない言葉であるエンティティ14："dispnea"が何なのか確認していきます。
使用したコードは以下です。

~~~
print("ヘルスケアエンティティ分析の結果:")
print("-"*40)

entity = docs[0].entities[14]
print(f"エンティティの詳細: {entity.text}")
print("-"*40)

print(f"　正規化されたテキスト: {entity.normalized_text}")
print(f"　カテゴリー: {entity.category}")
print(f"　第二のカテゴリー: {entity.subcategory}")
print(f"　オフセット: {entity.offset}")
print(f"　信頼スコア: {entity.confidence_score}")
if entity.data_sources is not None:
    print("　データソース:")
    for data_source in entity.data_sources:
        print(f"　　エンティティID: {data_source.entity_id}")
        print(f"　　名前: {data_source.name}")
if entity.assertion is not None:
    print("　アサーション:")
    print(f"　　条件付き: {entity.assertion.conditionality}")
    print(f"　　確実性: {entity.assertion.certainty}")
    print(f"　　関連付け: {entity.assertion.association}")
~~~

結果
~~~
ヘルスケアエンティティ分析の結果:
----------------------------------------
エンティティの詳細: dispnea
----------------------------------------
　正規化されたテキスト: None
　カテゴリー: SymptomOrSign
　第二のカテゴリー: None
　オフセット: 302
　信頼スコア: 0.78
　アサーション:
　　条件付き: conditional
　　確実性: None
　　関連付け: None
  
~~~

カテゴリーから、"dispnea"はSymptomOrSign（症状または兆候）であることが分かりました。
ちなみに、"dispnea"は**呼吸困難**のことです。

## 感想・まとめ

Azure Cognitive Servicesの 新サービス:ヘルスケアエンティティ分析を使って、医療ドメインで、固有表現抽出や関係抽出をやってみました。
これを使うことで、薬品名とその投与量、頻度などがすぐにわかるようになります。

また、KaggleのNBMEコンペのタスクのように、症状や兆候が同じ臨床概念であることまで、抽出可能になると、医師や看護師などの医療関係者の確認作業が大幅に削減されることが期待できます。
ヘルスケアエンティティ分析は、まだそこまではできませんが、
まずは、症状や兆候を抽出することで、臨床概念を絞りこむことに役立ちそうです。

今回のヘルスケアエンティティ分析の例のように、Azure Cognitive Servicesを使うことで、時間とコストのかかるAIモデル構築をすることなく、簡単に認知的分析を実施することが可能です。ぜひ、有効活用していきたいです。