2017年9月28日木曜日

ネット記事コーパス


 抄録コーパスと同じく、学習に用いるデータを一様にするため、収集したネット記事からランダムに学習データを抽出した。その抽出した学習データプロフィールを下表に示す。


#疾患文書数総語数総異なり語数
11Net:keigan5001785467
12Net:naimaku5001793496
13Net:kinsyu5002806647
14Net:ransou5001917487
15Net:taigan5001645418

総語数の平均は3,438語(標準偏差2,721)、異なり語数の平均は503語(標準偏差77)であった。

2017年9月27日水曜日

ネット記事の疾患予測サーバの性能

ネット記事データで学習したベイズ分類器の分類実験を行った。 学習データは各カテゴリ50件,総数で250件の抄録である。



正確度(Accuracy)は88.2%であった。これはKHcoderのときよりも正確度が低くなっている。
理由としては、「taigan」の精度が46.2%と低いためであると考えられる。なお、この結果はストップワードを使用している。



また、ストップワードをかけずに分類実験を行った結果を下記に示す。



正確度は90.6%となった。

疾患予測WebAPI

疾患予測サーバが提供するWebAPIはHL7 FHIRのRiskAssessmentリソースを用いてリクエスト及びレスポンスを送る。

リクエスト

クライアントが疾患予測サーバに分類対象となるテキストを送る。以下は,リクエストを送るjavascriptプログラムの一部分。
  1. // ここで HL7 FHIR の RiskAssessment リソースで予測システム(api-predict)へリクエストを送る
  2. var riskAssessment = {
  3. "resourceType": "RiskAssessment",
  4. "status": "final",
  5. "basis": [
  6. {
  7. "reference": text
  8. }
  9. ],
  10. "comment": "Naive Bayes Classifier Ver.1.0"
  11. };
  12. $.ajax({
  13. url: 'http://192.168.11.251/~nlp_ai/api-predict.php',
  14. type: 'POST',
  15. headers: {
  16. 'Content-Type': 'application/json;charset=utf-8'
  17. },
  18. async: false,
  19. data: JSON.stringify(riskAssessment),
  20. processData: true,
  21. dataType: 'json'
  22. })
  23. .done(function(data, textStatus, jqXHR) {
  24. ・・・以下,疾患予測サーバからのレスポンスを処理する・・・

以下は,上記プログラムでサーバー側に送られたJSON形式のリクエストメッセージ。
  1. {
  2. "resourceType":"RiskAssessment",
  3. "status":"final",
  4. "basis":[
  5. {
  6. "reference":"ここに疾患分類対象のテキストを設定する。・・・"
  7. }
  8. ],
  9. "comment":"Naive Bayes Classifier Ver.1.0"
  10. }

本来,RiskAssessment.basis.referenceにはリスク評価に用いる情報(FamilyHistory, Observations, Procedures, Conditions, etc.)への参照(Reference)を記述することになっているが,ここでは,直接分類対象のテキストを設定した。

レスポンス

これに対するレスポンスは,複数のRiskAssessmentリソースをBundleリソースで束ねたものとした。
以下は,疾患予測サーバーが返すレスポンスメッセージ。
  1. {
  2. "status":"final",
  3. "resourceType":"RiskAssessment",
  4. "basis[0][reference]":"不妊症。",
  5. "comment":"Naive Bayes Classifier Ver.1.0",
  6. "bundle":{
  7. "resourceType":"Bundle",
  8. "type":"message",
  9. "entry":[
  10. {
  11. "resourceType":"RiskAssessment",
  12. "status":"final",
  13. "prediction":[
  14. {
  15. "outcome":{"text":"naimaku"},
  16. "probability":1.4142274696827
  17. }
  18. ]
  19. },{
  20. "resourceType":"RiskAssessment",
  21. "status":"final",
  22. "prediction":[
  23. {
  24. "outcome":{"text":"kinsyu"},
  25. "probability":1.398112511866
  26. }
  27. ]
  28. },{
  29. "resourceType":"RiskAssessment",
  30. "status":"final",
  31. "prediction":[
  32. {
  33. "outcome":{"text":"taigan"},
  34. "probability":0.48504086418881
  35. }
  36. ]
  37. },{
  38. "resourceType":"RiskAssessment",
  39. "status":"final",
  40. "prediction":[
  41. {
  42. "outcome":{"text":"ransou"},
  43. "probability":-0.20820912181313
  44. }
  45. ]
  46. },{
  47. "resourceType":"RiskAssessment",
  48. "status":"final",
  49. "prediction":[
  50. {
  51. "outcome":{"text":"keigan"},
  52. "probability":-0.21748840888999
  53. }
  54. ]
  55. }
  56. ]
  57. }
  58. }
Bundle.entry配列に疾患ごとのRiskAssessmentリソースで分類結果が返ってくる。疾患名は,RiskAssessment.prediction[0].outcome.textに,スコアはRiskAssessment.prediction[0].probabilityに設定されている。
下記は,上記のレスポンスを画面編集するクライアント側のプログラムの一部である。
  1. .done(function(data, textStatus, jqXHR) {
  2. var html = '<table class="table table-hover table-striped">';
  3. html += '<caption>予測結果(あくまでも予測です。気になる場合は病院に受診してください。)</caption>';
  4. html += '<tr>';
  5. html += '<th>#</th>';
  6. html += '<th>疾患</th>';
  7. html += '<th>判定スコア</th>';
  8. html += '</tr>';
  9. var i = 1;
  10. $.each(data.bundle.entry, function(index, riskAssessment){
  11. html += '<tr>';
  12. html += '<td>' + (i++) + '</td>';
  13. html += '<td>';
  14. html += DISEASE_NAME[riskAssessment.prediction[0].outcome.text];
  15. html += '</td>';
  16. html += '<td>';
  17. html += riskAssessment.prediction[0].probability;
  18. html += '</td>';
  19. html += '</tr>';
  20. });
  21. html += '</table>';
  22. $("#predict-result-list").html(html);
  23. })

2017年9月20日水曜日

考察

分類実験の結果を見ると,総語数の少ない子宮内膜症の適合率が他のカテゴリに比べて小さく,逆に再現率は大きい。なぜそうなるかを考察する。

疾患予測サーバの性能

医中誌抄録データで学習したベイズ分類器の分類実験を行った。
学習データは各カテゴリ500件,総数で2,500件の抄録である。KHcoderで抽出した複合語をMeCabのユーザ辞書に登録した。
まず,形態素解析で名詞のみ抽出した場合の混同行列を下表に示す。

疾患 keigan kinsyu naimaku ransou taigan 総計 再現率
keigan 147 14 13 8 18 200 73.5%
kinsyu 3 151 26 7 13 200 75.5%
naimaku 1 7 183 2 7 200 91.5%
ransou 9 4 13 162 12 200 81.0%
taigan 20 12 11 14 143 200 71.5%
総計 180 188 246 193 193 1000  
適合率 81.7% 80.3% 74.4% 83.9% 74.1%   78.6%

なお,ストップワードは設定していない。また,単語の出現頻度は1語以上とした。検証に用いたデータは,学習データとは異なる,ランダムに抽出した各カテゴリ200件のデータである。
正確度(Accuracy)は78.6%であった。

次にストップワードに「がん,癌,筋腫,子宮,子宮筋腫,子宮体がん,子宮体癌,子宮内膜症,子宮頸がん,子宮頸癌,腫瘍,内膜症,卵巣,卵巣腫瘍,」を設定した場合の結果を下表に示す。

疾患 keigan kinsyu naimaku ransou taigan 総計 再現率
keigan 133 16 16 9 26 200 66.5%
kinsyu 4 121 41 16 18 200 60.5%
naimaku 2 8 180 4 6 200 90.0%
ransou 9 5 17 155 14 200 77.5%
taigan 30 13 13 25 119 200 59.5%
総計 178 163 267 209 183 1000  
適合率 74.7% 74.2% 67.4% 74.2% 65.0%   70.8%

正確度は70.8%となり,8ポイント下がった。

全品詞を使った場合

次に,形態素解析で得られた全品詞を用いた場合の結果を下表に示す。
 
疾患 keigan kinsyu naimaku ransou taigan 総計 再現率
keigan 139 16 15 11 19 200 69.5%
kinsyu 3 148 27 9 13 200 74.0%
naimaku 2 9 182 1 6 200 91.0%
ransou 8 3 16 160 13 200 80.0%
taigan 23 13 11 14 139 200 69.5%
総計 175 189 251 195 190 1000  
適合率 79.4% 78.3% 72.5% 82.1% 73.2%   76.8%

正確度は名詞のみの場合に比べて2ポイントほど下がった。
次にストップワードを設定すると次のようになった。
 
疾患 keigan kinsyu naimaku ransou taigan 総計 再現率
keigan 126 19 17 11 27 200 63.0%
kinsyu 3 116 45 18 18 200 58.0%
naimaku 2 10 177 5 6 200 88.5%
ransou 9 2 20 156 13 200 78.0%
taigan 32 15 13 23 117 200 58.5%
総計 172 162 272 213 181 1000  
適合率 73.3% 71.6% 65.1% 73.2% 64.6%   69.2%

正確度が69.2%となった。

抄録コーパス

ベイズ学習器の性能は学習に用いたコーパスに大きく依存する。特に,カテゴリごとの単語数は一様である必要がある。偏りがあると特定のカテゴリ(単語数の少ないカテゴリ)に分類されてしまう傾向がある。
そこで,学習に用いるデータを一様にするため,収集した抄録からランダムに学習データを抽出することにした。こうして抽出した学習データプロフィールを下表に示す。

# 疾患 文書数   総語数 総異なり語数 (A) log(総語数) (B) log(総語数+総異なり語数) (A) -(B)
3 Shikkan:naimaku 500 0 34291 7392 10.44263821 10.63784865 -0.195210443
5 Shikkan:taigan 500 0 37736 8730 10.53836982 10.74647614 -0.208106316
4 Shikkan:ransou 500 0 35970 8326 10.49044054 10.69864966 -0.208209122
2 Shikkan:kinsyu 500 0 37964 8935 10.54439362 10.75575163 -0.211358011
1 Shikkan:keigan 500 0 35608 8651 10.48032561 10.69781402 -0.217488409

総語数の平均は36,314語(標準偏差1,537),異なり語数の平均は8,407語(標準偏差608)であった。

MeCabの辞書

形態素解析(MeCab)による過分割を押さえたり,専門用語を抽出できるようにするために,MeCabのユーザ辞書を作成する。

■HOST上のMeCab環境

  • 実行ファイル /usr/local/bin/mecab
  • システム辞書ディレクトリ /usr/local/lib/mecab/dic/ipadic
  • 辞書作成プログラム /usr/local/libexec/mecab/mecab-dict-index
  • ユーザ辞書ディレクトリ /usr/local/lib/mecab/dic/userdic/
  • MeCab設定ファイル /usr/local/etc/mecabrc 

辞書ファイルの作成

  • 辞書ファイルはKHcoderの複合語抽出機能を用いる。
    • ただし,複合語にカンマが含まれるものは除去する。
  • 抽出した複合語を所定のCSVファイルに編集してサーバへアップロードする。
    • 追加した辞書は47,674語(全抄録から複合語を作成した)
    • アップロード先:%HOME/usr/dic/abstract_utf-8n.csv
  • 辞書コンパイル
    • /usr/local/libexec/mecab/mecab-dict-index -d /usr/local/lib/mecab/dic/ipadic -u /usr/local/lib/mecab/dic/userdic/abstract.dic -f utf-8 -t utf-8 /home/nlp_ai/usr/dic/abstract_utf-8n.csv
  • ユーザ辞書をMeCabに登録する
    • /usr/local/etc/mecabrcに下記を追加
      userdic = /usr/local/lib/mecab/dic/userdic/abstract.dic