Keras+LSTM+CRF を使用した固有表現抽出 NER の練習

Keras+LSTM+CRF を使用した固有表現抽出 NER の練習

[[339715]]

テキスト分割、品詞タグ付け、固有表現認識は、自然言語処理の分野では非常に基本的なタスクです。それらの精度が下流のタスクの精度を決定します。実は、私はこれまで固有表現認識の仕事にあまり触れたことがありませんでした。大学院時代には断続的にそのようなプロジェクトに参加していましたが、卒業後は表面的な理解しかしていないと感じていました。最近、再び取り組んで、実践を主な学習手段として、固有表現認識などのタスクを比較的体系的に理解、学習、適用したいと思っています。

今日のアプリケーションには、ディープラーニングが関与しないタスクはほとんどありません。多くのサブタスクの開発履歴は驚くほど似ています。当初、研究とアプリケーションのほとんどは機械学習の分野に集中していました。その後、ディープラーニングモデルの開発に伴い、広く使用されるようになりました。名前付きエンティティ認識などのシーケンスラベリングタスクも当然例外ではありません。LSTM + CRFに基づくディープラーニングエンティティ認識に関する関連研究はありましたが、以前の方向性と一致しなかったため、あまり注意を払っていませんでした。最近、たまたまNERを学んでいました。以前の関連記事では、機械学習手法に基づいて簡単な名前付きエンティティ認識を実践しました。ここでは、ディープラーニングモデルに基づいてNERを実装しています。

名前付きエンティティの認識は、シーケンスのラベル付けタスクに属しますが、これは実際には分類タスクに似ています。NER は、テキスト内の定義済みのエンティティ タイプを識別することです。

NER はシーケンス ラベリングの問題であるため、データのラベリング方法もシーケンス ラベリングの問題の方法 (主に BIO と BIOES) に従います。ここではBIOESが直接紹介されています。BIOESを理解すれば、BIOもマスターできます。

まず、BIOES の意味を列挙してみましょう。

  1. B、つまりBeginは開始を意味します 
  2. Iは中級を意味します。  
  3. EはEndの略で、終わりを意味します 
  4. SはSingleの略で、1文字を意味します。  
  5. O は Other (その他) の略で、無関係な文字をマークするために使用されます。

たとえば、次の文の場合:

  1. ヤオミンはバスケットボールをするためにハルビン工業大学体育館へ行った

注釈結果は次のとおりです。

  1. ヤオミンはバスケットボールをするためにハルビン工科大学スタジアムへ行った 
  2. B-PER E-PER O B-ORG I-ORG I-ORG I-ORG I-ORG I-ORG E-ORG B-LOC I-LOC E-LOC O O O

これで簡単なレビューは終わりです。次に、この記事の実践的な部分に移ります。まず、データセットはインターネットから取得します。以下に示すように、サンプルデータを見てみましょう。

train_data のサンプルデータは次のとおりです。

  1. Oが 
  2. シー・オー 
  3. 希望 
  4. 仕事 
  5. チェン・オー 
  6. 保存O  
  7. ヘルプO  
  8.  
  9. 百O  
  10. 10,000 オー 
  11. 子供 
  12. チェン・オー 
  13. ロングO  
  14. Oから 
  15. おいでよ 
  16. 、O  
  17. セクションO  
  18. ティーチO  
  19. 星澳 
  20. 国O  
  21. ウェイ・オー 
  22. はい 
  23. チェン・オー 
  24.  
  25. 時間 
  26. 、O  
  27. 今日O  
  28. O日目 
  29. はい 
  30. Oを集める 
  31. 隠されたO  
  32. 価格O  
  33. 値O  
  34.  
  35. ブックO  
  36. あなた 
  37. いいえ 
  38. Oを購入 
  39. 、O  
  40. ミン・オー 
  41. O日目 
  42. ただO  
  43. お電話ください 
  44. あなた 
  45. 後悔 
  46. いいえ 
  47. Oが 
  48. イニシャルO  
  49. !お

test_data のサンプルデータは次のとおりです。

  1. ハイO  
  2. Oを上げる 
  3.  
  4. 国O  
  5. メインO  
  6. とO  
  7. 社会 
  8. 意思 
  9. メインO  
  10.  
  11. 2つのO  
  12. ヌードルO  
  13. 旗O  
  14. 旗O  
  15. 、O  
  16. グループO  
  17. 結び目 
  18. オールO  
  19. ボディO  
  20. チェン・オー 
  21. メンバーO  
  22.  
  23. とO  
  24.  
  25. ユニオンO  
  26. システムO  
  27.  
  28. Oに戻る 
  29. 華僑
  30. 、O  
  31. 華僑 
  32. 依存 
  33. 、O  
  34.  
  35. ヤン・オ 
  36.  
  37. 国O  
  38. レザー 
  39. ライフO  
  40.  
  41. ライトO  
  42. ロン・オー 
  43. バイオグラフィー 
  44. システムO  
  45. 、O  
  46. Oの場合 
  47. システムO  
  48. ワンオー 
  49. 祖澳 
  50. 国O  
  51. 、O  
  52. 振動O  
  53. 星澳 
  54. 中型B-LOC  
  55. 華I-LOC  
  56. そしてO  
  57. ニュー 
  58. フォースO  
  59. 熱烈な 
  60. ファイトO  
  61. ;お

トレーニング セットとテスト セットのデータ構造を簡単に理解したら、後続のデータ処理に進むことができます。主な目的は、特徴データを生成することです。コア コードの実装は次のとおりです。

  1. open('test_data.txt', encoding = 'utf-8' ) を f として実行します:  
  2. test_data_list = [one.strip().split('\t') は、f.readlines() 内の one に対して、one.strip() の場合に実行されます]  
  3. open('train_data.txt', encoding = 'utf-8' ) を f として実行します:  
  4. train_data_list = [one.strip().split('\t') は、f.readlines() 内の one に対して、one.strip() の場合に実行されます]  
  5. char_list = [test_data_list 内の 1 つの場合は one[0]] + [train_data_list 内の 1 つの場合は one[0]]  
  6. label_list = [test_data_list 内の 1 つの場合は one[-1]] + [train_data_list 内の 1 つの場合は one[-1]]  
  7. print('char_list_length: ', len(char_list))  
  8. print('label_list_length: ', len(label_list))  
  9. print('char_num: ', len(list(set(char_list))))  
  10. print('label_num: ', len(list(set(label_list))))
  11. 文字数、ラベル数={},{}  
  12. #文字頻度統計 
  13. char_list に 1 つの場合:  
  14. char_count に 1 がある場合:  
  15. char_count[1]+=1  
  16. それ以外:  
  17. char_count[1]=1  
  18. label_list 内の 1 つ:  
  19. label_count に 1 つある場合:  
  20. ラベル数[1]+=1  
  21. それ以外:  
  22. ラベル数[1]=1  
  23. # 頻度の降順で並べ替え 
  24. ソート済みsorted_char = sorted(char_count.items(), key = lambda e:e[1], reverse = True )  
  25. ソート済みsorted_label = sorted(label_count.items(), key = lambda e:e[1], reverse = True )  
  26. #文字IDマッピング関係の構築 
  27. char_map_dict = {}  
  28. ラベルマップディクショナリ= {}  
  29. i が範囲(len(sorted_char))内にある場合:  
  30. char_map_dict[ソートされた文字[i][0]]=i  
  31. char_map_dict[str(i)]=ソートされた文字[i][0]  
  32. i が範囲(len(sorted_label))内にある場合:  
  33. ラベルマップ辞書[ソートされたラベル[i][0]]=i  
  34. label_map_dict[str(i)]=ソートされたラベル[i][0]  
  35. #結果の保存 
  36. open('charMap.json','w') を f として実行します:  
  37. f.write(json.dumps(char_map_dict))  
  38. open('labelMap.json','w') を f として実行します:  
  39. f.write(json.dumps(label_map_dict))

コードは非常に明確で、重要な部分には対応するコメントがあります。ここでは説明しません。中心となるアイデアは、文字またはタグ カテゴリ データを対応するインデックス データにマッピングすることです。ここでは頻度のフィルタリングしきい値を設定していません。実装によっては、1 回だけ表示されるデータをフィルタリングするものもあります。必要に応じて変更できます。

charMap データのサンプルは次のとおりです。

labelMap データのサンプルは次のとおりです。

上記のマッピング データを生成した後、元のテキスト データを変換して計算し、必要な特徴データを生成できます。コア コードの実装は次のとおりです。

  1. X_train、y_train、X_test、 y_test = [],[],[],[]  
  2. #トレーニングデータセット 
  3. i が範囲(len(trainData))内である場合:  
  4. one_sample = [one.strip().split('\t') は trainData[i] 内の 1 つのサンプルです]  
  5. char_list = [O[0] (Oはone_sample内)]  
  6. label_list = [O[1] は one_sample 内の O の場合]  
  7. char_vec = [char_map_dict[char_list[v]] 範囲内のv(len(char_list))]  
  8. label_vec = [label_map_dict[label_list[l]] l が範囲(len(label_list))内にある場合]  
  9. X_train.append(char_vec)  
  10. y_train.append(ラベルベクトル)  
  11. #テストデータセット 
  12. i が範囲(len(testData))内にある場合:  
  13. one_sample = [one.strip().split('\t') は、testData[i] 内の 1 つのサンプルに対して実行されます。
  14. char_list = [O[0] (Oはone_sample内)]  
  15. label_list = [O[1] は one_sample 内の O の場合]  
  16. char_vec = [char_map_dict[char_list[v]] 範囲内のv(len(char_list))]  
  17. label_vec = [label_map_dict[label_list[l]] l が範囲(len(label_list))内にある場合]  
  18. X_test.append(char_vec)  
  19. y_test.append(ラベルベクトル)  
  20. 機能={}  
  21. 特徴['X_train']、特徴['y_train']=X_train、y_train  
  22. 特徴['X_test']、特徴['y_test']=X_test、y_test  
  23. #結果の保存 
  24. open('feature.json','w') を f として実行します:  
  25. f.write(json.dumps(機能))

この時点で、必要な特徴データを取得し、テスト セット データとトレーニング セット データを分割しました。

次に、モデルを構築します。実装を簡素化するために、ネイティブ Tensorflow フレームワークよりも入門レベルが低い Keras フレームワークを使用します。コア コードの実装は次のとおりです。

  1. # データセットをロードする 
  2. open('feature.json') を f として実行します:  
  3. f = json.load (f) です。  
  4. X_train、X_test、y_train、 y_test = F ['X_train']、F['X_test']、F['y_train']、F['y_test']  
  5. #データ整列操作
  6.   X_train = pad_sequences (X_train、 maxlen = max_len = 0 )  
  7. y_train = pad_sequences (y_train、 maxlen = max_len = -1)  
  8. y_train = np.expand_dims (y_train、2)  
  9. X_test = pad_sequences (X_test、 maxlen = max_len = 0 )  
  10. y_test = pad_sequences (y_test、 maxlen = max_len = -1)  
  11. y_test = np.expand_dims (y_test, 2)  
  12. #モデルの初期化、トレーニング 
  13. os.path.exists(saveDir) が存在しない場合は:  
  14. os.makedirs(ディレクトリを保存)  
  15. #モデルの初期化 
  16. モデル=シーケンシャル()
  17. model.add(埋め込み(voc_size, 256, mask_zero = True ))  
  18. モデルを追加します(双方向(LSTM(128, return_sequences = True )))  
  19. model.add(ドロップアウト(レート= 0 .5))  
  20. model.add(Dense(tag_size))  
  21. crf = CRF (タグサイズ、スパースターゲット= True )  
  22. モデルを追加します(crf)  
  23. モデル.要約()
  24. model.compile('adam',損失= crf .loss_function,メトリック= [crf.accuracy])  
  25. # トレーニングとフィッティング 
  26. 履歴=モデル.fit(X_train,y_train,バッチサイズ= 100 ,エポック= 500 ,検証データ=[X_test,y_test])  
  27. モデルを保存(saveDir+'model.h5')  
  28. #モデル構造の可視化 
  29. 試す:
  30.   plot_model(モデル、 to_file = saveDir + "model_structure.png"、 show_shapes = True )  
  31. except 例外を e として:  
  32. print('例外: ', e)
  33.   #結果の視覚化 
  34. plt.clf()  
  35. plt.plot(履歴.履歴['acc'])  
  36. plt.plot(履歴.履歴['val_acc'])  
  37. plt.title('モデルの精度')  
  38. plt.ylabel('精度')  
  39. plt.xlabel('エポック')  
  40. plt.legend(['train','test'], loc = '左上' )  
  41. plt.savefig(saveDir+'train_validation_acc.png')  
  42. plt.clf()  
  43. plt.plot(履歴.履歴['損失'])  
  44. plt.plot(履歴.履歴['val_loss'])  
  45. plt.title('モデル損失')  
  46. plt.ylabel('損失')  
  47. plt.xlabel('エポック')  
  48. plt.legend(['train', 'test'], loc = '左上' )  
  49. plt.savefig(saveDir+'train_validation_loss.png')  
  50. スコア=モデル.evaluate(X_test,y_test,詳細= 0 )  
  51. print("精度: %.2f%%" % (スコア[1]*100))  
  52. モデルmodel_json = model.to_json()  
  53. open(saveDir+'structure.json','w') を f として実行します:  
  54. f.write(モデルjson)  
  55. model.save_weights(saveDir+'weight.h5')  
  56. print('===終了====')

トレーニングが完了すると、構造ディレクトリのファイル構造は次のようになります。

モデル構造図は以下のとおりです。

トレーニング中の精度曲線は次のとおりです。

トレーニング中の損失値曲線は次のとおりです。

トレーニングの計算リソースが比較的大きく、時間も比較的長いため、ここでは単純に 20 回の計算反復を設定しました。実際の状況に応じて、さまざまなニーズを満たすために反復回数を高くしたり低くしたりできます。

簡単な予測例を以下に示します。

この記事の実践はこれで終わりです。後で時間があるときに、さらに詳しく勉強し続けます。皆さんのお役に立てれば幸いです。良い仕事と実りある勉強を祈っています!

<<:  IDC: 人工知能への世界的支出は4年で倍増すると予想

>>:  「AIライター」が人気です!大学生がGPT-3を使って「チキンスープ記事」を書き、大勢の人を騙してテクノロジーの見出しを飾った

ブログ    
ブログ    
ブログ    

推薦する

ちょうど今、OpenAIはマスク氏を反論する記事を公式に発表し、過去8年間の電子メールのやり取りのスクリーンショットを公開した。

最も注目されているテクノロジー企業OpenAIと世界一の富豪マスク氏との壮大な戦いは新たなレベルに達...

今後、セキュリティ分野で顔認識技術はどのように発展していくのでしょうか?

顔認識とは、顔の特徴情報の本人分析を利用して本人認証を行う生体認証技術を指します。人気の生体認証技術...

人工知能技術はCOVID-19の流行との戦いで重要な役割を果たしてきた

ハイテクである人工知能(AI)は、医療と健康の分野、特にCOVID-19の流行との闘いにおいて非常に...

第4回パラダイム NeurIPS 2020: ナレッジグラフ埋め込みの自動化

少し前に、Fourth Paradigm の上級研究員である Quanming Yao 博士が、Ne...

人工知能は医療と健康分野に破壊的な革命をもたらすだろう

ヘルスケア分野への人工知能 (AI) の導入は、今日の国際医療における最も先進的な取り組みの 1 つ...

ロボットの台頭:伝統産業を変革する新技術

アルゴリズムの時代が到来しました。 Google、Amazon、AppleなどのIT大手が開発した、...

...

コードで機械の心を構築するまで、どれくらい時間がかかるのでしょうか?

[[242009]]この記事の著者は、Microsoft Internet Engineering...

AI界のお笑い王に100万の賞金!北京郵電大学、南洋理工大学などが「砂像動画」データセットを公開 FunQA:アルゴリズムで人間のユーモアを学習

人は直感に反する動画(ユーモラスで独創的で視覚的に魅力的な動画)から容易に喜びを得ることができます。...

企業はどのように AI を活用してビジネスの成長を促進できるのでしょうか?

人工知能 (AI) の導入によって得られる潜在的な利益を考えると、企業は傍観者でいるわけにはいきませ...

...

ついに誰かが説明可能な機械学習を明らかにした

[[443127]]ビッグデータの時代において、機械学習は製品の売上向上や人間の意思決定の支援に大き...

2023年に出現するサイバー脅威、AI、量子コンピューティング、データ汚染まで

ハッカーや詐欺師が新しいテクノロジーを入手したり、古い脆弱性を悪用する新しい方法を考え出したりするに...

金融ロボアドバイザーは3つのトレンドによって増加傾向にある

編集者注: ロボット アドバイザーの登場により、従来のアドバイザーはどこへ向かうのでしょうか。これは...