Ahogrammer

Deep Dive Into NLP, ML and Cloud

生体医療分野のテキストのためのアノテーションツール

EMNLP 2019より以下の論文を紹介。

この論文では、MedCatTrainerと呼ばれる生体医療分野のテキストに対する固有表現認識とエンティティリンキングを行うためのアノテーションツールを提案している。特徴としては、アクティブラーニングを使うことが可能な点で、モデルによって自動的に認識されたエンティティを人間が修正することになる。

言うよりは見るが早しということで、以下にアノテーション画面を示す。画面の左側にテキストとエンティティが表示されている。サイドバーにはエンティティのメタデータが表示されている。おそらく、サイドバーの右上にあるチェックマークやバツマークからアクティブラーニングで付けた結果を承認する仕組みになっている。

f:id:Hironsan:20191001071746p:plain
アノテーション画面

サイドバーからはコンセプトを追加することができる。

f:id:Hironsan:20191001072923p:plain
コンセプトの追加

別のアノテーション画面。右上のはてなボタンからアノテーションガイドラインを確認できる。「Incomplete」ボタンは後で見直すことを示している。Submitボタンで対象ドキュメントのアノテーションが完了したことを示す。

f:id:Hironsan:20191001071848p:plain
カスタマイズ

感想

自分もアノテーションツールを作っているので、興味があって読んだ論文。以下のリポジトリからコードを確認することができる。この手の論文で作られたプロジェクトには珍しく、現在も開発中であることがわかる。

github.com

ElasticsearchとBERTを組み合わせて類似文書検索

本記事ではElasticsearchとBERTを組み合わせて類似文書検索を行う方法について紹介します。Elasticsearchでは最近、ベクトルに対する類似文書検索の機能が実装されました。一方、BERTを使うことでテキストを固定長のベクトルに変換することができます。つまり、BERTを使ってテキストをベクトルに変換すれば、Elasticsearchを使って類似文書検索ができるということになります。

本記事では以下のアーキテクチャでElasticsearchとBERTを組み合わせた検索システムを実現します。Dockerを使ってアプリケーション、BERT、Elasticsearchのコンテナを分けることでそれぞれをスケールしやすくする狙いがあります。記事中では重要な部分のみ提示しますが、システム全体はdocker-composeのファイルとして記述しこちらのリポジトリに置いてるので、参照してください。

f:id:Hironsan:20190930070748p:plain
システムアーキテクチャ

実装は以下の手順で行います。

  1. 学習済みモデルのダウンロード
  2. 環境変数の設定
  3. Dockerコンテナの起動
  4. インデックスの作成
  5. ドキュメントの作成
  6. ドキュメントのインデックス
  7. ブラウザを開く

Step 1: 学習済みモデルのダウンロード

まずは、以下のリストから学習済みのBERTのモデルをダウンロードします。

BERT-Base, Uncased12-layer, 768-hidden, 12-heads, 110M parameters
BERT-Large, Uncased24-layer, 1024-hidden, 16-heads, 340M parameters
BERT-Base, Cased12-layer, 768-hidden, 12-heads , 110M parameters
BERT-Large, Cased24-layer, 1024-hidden, 16-heads, 340M parameters
BERT-Base, Multilingual Cased (New)104 languages, 12-layer, 768-hidden, 12-heads, 110M parameters
BERT-Base, Multilingual Cased (Old)102 languages, 12-layer, 768-hidden, 12-heads, 110M parameters
BERT-Base, ChineseChinese Simplified and Traditional, 12-layer, 768-hidden, 12-heads, 110M parameters
$ wget https://storage.googleapis.com/bert_models/2018_10_18/cased_L-12_H-768_A-12.zip
$ unzip cased_L-12_H-768_A-12.zip

Step 2: 環境変数の設定

次に、ダウンロードしたBERTのモデルとElasticsearchのインデックス名を環境変数に設定します。

$ export PATH_MODEL=./cased_L-12_H-768_A-12
$ export INDEX_NAME=jobsearch

Step 3: Dockerコンテナの起動

Docker composeを使ってDockerコンテナを起動します。ここで起動するコンテナは、アプリケーションコンテナ、BERTコンテナ、Elasticsearchコンテナの3つです。詳細についてはdocker-compose.yamlを確認してください。

$ docker-compose up

注意としては、Dockerに割り当てるメモリの設定をある程度大きくしておかないと、BERTがいつまで経っても使用可能にならない点です。8GB程度は確保しておきましょう。

Step 4: インデックスの作成

コンテナを起動したら、Elasticsearchのインデックスを作成します。たとえば、titletexttext_vectorの3つのフィールドを持つjobsearchインデックスを作成するためには以下のコマンドを実行します。

$ python example/create_index.py --index_file=example/index.json --index_name=jobsearch
# index.json
{
  "settings": {
    "number_of_shards": 2,
    "number_of_replicas": 1
  },
  "mappings": {
    "dynamic": "true",
    "_source": {
      "enabled": "true"
    },
    "properties": {
      "title": {
        "type": "text"
      },
      "text": {
        "type": "text"
      },
      "text_vector": {
        "type": "dense_vector",
        "dims": 768
      }
    }
  }
}

ここで、text_vectorフィールドのdimsの値は学習済みBERTモデルの次元数と等しくなることに注意してください。

Step 5: ドキュメントの作成

インデックスを作成したら、ドキュメントを格納します。ここでのポイントは、BERTを使ってテキストをベクトルに変換することです。BERTで得られたベクトルはtext_vectorフィールドに格納されます。以下ではCSVファイルをJSONドキュメントに変換しています。

$ python example/create_documents.py --data=example/example.csv --index_name=jobsearch
# example/example.csv
"Title","Description"
"Saleswoman","lorem ipsum"
"Software Developer","lorem ipsum"
"Chief Financial Officer","lorem ipsum"
"General Manager","lorem ipsum"
"Network Administrator","lorem ipsum"

スクリプトの実行が終わると、以下のようなJSONファイルが得られます。

# documents.jsonl
{"_op_type": "index", "_index": "jobsearch", "text": "lorem ipsum", "title": "Saleswoman", "text_vector": [...]}
{"_op_type": "index", "_index": "jobsearch", "text": "lorem ipsum", "title": "Software Developer", "text_vector": [...]}
{"_op_type": "index", "_index": "jobsearch", "text": "lorem ipsum", "title": "Chief Financial Officer", "text_vector": [...]}
...

Step 6: ドキュメントのインデックス

データをJSONに変換したら、指定したインデックスに格納します。

$ python example/index_documents.py

Step 7: ブラウザを開く

データをインデックスしたら、ブラウザでhttp://127.0.0.1:5000を開きます。以下の画像は求人検索を行っている例です。「I'm looking for a lawyer」というクエリに対して、「Legal assistant」や「Lawyer」の求人を返していることが確認できます。

f:id:Hironsan:20190930074416p:plain
求人検索の例

ソースコード

おわりに

今回はElasticsearchとBERTを使って類似文書検索を行ってみました。BERTの実行速度には課題がありますが、今回のようにBERTを独立したコンテナとして扱うことでスケールさせやすくなるので、課題は解決できると思います。今後、BERTのようなモデルを検索システムに使うときに本記事の内容が役に立てば幸いです。

あわせて読む

hironsan.hatenablog.com

参考文献

日本語の電子健康記録のテキストを匿名化する手法

EMNLP 2018より以下の論文を紹介。

この論文では、日本語の電子健康記録(EHRs: Electronic Health Records)のテキストを匿名化する手法を提案している。近年、医療関連のデータが増加し、その重要性が増している。研究開発面から言うと、2018年に「医療分野の研究開発に資するための匿名加工医療情報に関する法律」が施行されたことにより、特定の機関がEHRsを扱えるようになったが、個人情報保護の観点から匿名化は欠かせない技術となっている。論文では匿名化を行うための手法として、ルールベース、CRF、LSTMの3つを提案している。2つのデータセットで検証した結果、ルールベースとLSTMが良い結果となった。

匿名化を行うための手法としては、ルールベース、CRF、LSTMベースの3つの手法を用いている。CRFとLSTMベースの手法は系列ラベリングの分野でよく使われるモデルで、高い性能を出すことが知られている。ルールベース手法ではagehospitalsextimeの4クラスに対してルールを書いて情報を抽出している。以下にageクラスに対するルールの一部を示した。

f:id:Hironsan:20190927081842p:plain
ageクラスに対するルール(一部)

実験はMedNLP-1と自前で作成したデータセット(dummy EMRs)を使って行っている。MedNLP-1には2,244文が含まれ、dummy EMRsには8327文が含まれているので、規模としてはdummy EMRsがMedNLP-1の約4倍となっている。CRFとLSTMに関しては2つのデータセットを混ぜたデータセット(Mix)での学習も行っている。

以下はMedNLP-1に対する評価結果を示している。結果を見ると、ルールベースの手法が最も良い性能であることがわかる。これは、ルールベースの手法で使っているルールがMedNLP-1に合わせて作られたからとのこと。CRFとLSTMも悪くないが、データセットを混ぜて使うと性能が大きく低下していることが確認できる。

f:id:Hironsan:20190927081011p:plain
MedNLP1に対する評価結果

以下はdummy EMRsに対する評価結果を示している。結果を見ると、データセットを混ぜてLSTMベースの手法で学習させた場合が最も良い性能であることがわかる。また、MedNLP-1では良かったルールベースの手法の性能が非常に低いことがわかる。MedNLP-1の結果と合わせて考えると、ルールがMedNLP-1に過適合しているのだと考えられる。

f:id:Hironsan:20190927081050p:plain
ダミーのEMRsに対する評価結果

感想

固有表現認識の応用の一つとして興味があったので読んだ論文。手法としては2016年くらいのモデルを使っているので、改善の余地は大きいというのが感想。

たとえば、この分野のテキストは医者がラベル付けするけど、医者も忙しいのでそんなに大量のラベルを付けてられないという問題がある。そういう場合は、以下の記事で紹介したような教師なしデータを使って事前学習済みの言語モデルをFine-tuningする手法が有効である可能性がある。

hironsan.hatenablog.com

また、この論文では2つのデータセットを単純に結合して学習させているが、MedNLP-1の結果を見て分かる通り、そういったテクニックはラベル分布が異なったりすると有効でない場合がある。この辺は工夫の余地があると思う。また、同じようなラベル付けのデータセットを用意するのは大変なので、以下の記事で紹介したような手法を使って、アノテーションスキーマが異なるデータセットを使うというのも一つの手。

hironsan.hatenablog.com

また、病院名の認識であれば知識ベースと組み合わせるのも面白いかもしれない。医療機関の名称は厚生局が情報を持っているので、知識ベースを作りやすいというメリットもある。