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を使って「チキンスープ記事」を書き、大勢の人を騙してテクノロジーの見出しを飾った

ブログ    
ブログ    
ブログ    
ブログ    

推薦する

AIは追いつこうと努力しているが、5Gはカーブで追い越しつつある。トランプ氏が不安にならないわけがない。

[[263771]] 5Gの進歩に伴い、コスト面でも速度面でも、中国の5Gなしでは5Gを推進するの...

中国の良き叔父から12歳の開発者Jing Kunまで:DuerOSはすべての開発者に平等に力を与えます

スマート音声開発者はAIの「ゴールドラッシュ」を先導しています。 7月4日、第2回百度AI開発者会議...

アメリカの科学者が、将来AI人工知能に代わるAGIの概念を提唱しました!

人工知能の分野の中心にあるのは、いつの日か人間と同じくらい賢い機械を作ることができるようになるという...

ロボットが商品を移動、無人仕分け、梱包作業員が異動・昇進…「ダブル11」の裏側にあるサプライチェーンアップグレード戦争

「ダブル11」は10年以上前から存在しており、大半の「買い物中毒者」は巨大プラットフォームでの数千億...

...

これはGPT-4が愚かである理由についての新たな説明である

かつては世界で最も強力だと考えられていたGPT-4も、リリース以来、いくつかの「信頼の危機」を経験し...

中国移動研究所のチャン・ヤオビン氏:AI時代の技術マネージャーとして、戦闘能力とは何でしょうか?

[[260907]] [[260908]] AIはさまざまな産業に大きな変化をもたらします。よりイ...

ブースティングとバギング: 堅牢な機械学習アルゴリズムを開発する方法

導入機械学習とデータ サイエンスでは、単にデータを Python ライブラリに投入してその結果を活用...

階段を登るための最小コストを使用するデータ構造とアルゴリズム

[[443068]]最小限のコストで階段を登るLeetCode の問題へのリンク: https://...

...

...

...

AIが医薬品開発において適切な医薬品成分の特定にどのように役立つか

[[378110]]デジタル技術の導入に関しては、製薬業界では導入が遅れる傾向にあります。これまで、...

ビッグデータと人工知能を活用して英語教育の問題を解決する

1. 英語教育と学習の現状現在、我が国の英語教育は大きな進歩を遂げていますが、依然として我が国の発展...