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 の応用について

未来の工場はどのようなものになるでしょうか? AI を主要な原動力として、工場はより機敏かつカスタマ...

人工知能の時代、主役となるのは誰でしょうか?

[[382357]] 1990年代、米国はコンピュータ産業の早期発展の機会をいち早く捉え、デジタル...

百度の女性デーのポスターはスマートライフの姿を描いている:人工知能は女性をより自由にする

社会の進歩と国民の意識の高まりに伴い、社会全体が女性の権利にますます注目するようになっています。 3...

...

TensorFlow で発見された脆弱性の背後にあるもの: AI セキュリティに関する私たちの愚かさと無知

AI がインターネット セキュリティに与える影響について議論してきたとき、AI 自体も安全ではないと...

機械学習の基礎チャート: 概念、原則、歴史、トレンド、アルゴリズム

1. 機械学習の概要1. 機械学習とは何ですか?機械は大量のデータを分析して学習します。たとえば、猫...

...

人間支援型人工知能の6つの利点

人工知能は最近話題になっていますが、現実には人間のように考えることができるコンピューターの実現にはま...

ヤン・ニン氏の新しい論文が突然ネイチャーのサブジャーナルに掲載された: 構造はAIの手の届かないところにある

この記事はAI新メディアQuantum Bit(公開アカウントID:QbitAI)より許可を得て転載...

...

D2C フロントエンド インテリジェンスは「がん」か「特効薬」か?

フロントエンド インテリジェンスには、その名前が示すように、「フロントエンド」と「インテリジェンス」...

ディープラーニングの最適化手法の簡単な紹介: 勾配降下法

実際、ディープラーニングは多くの厄介な最適化問題を解決しています。ニューラル ネットワークは、問題に...

...

人工知能と5Gアプリケーションはもはや単なる「紙の設計図」ではなく、デジタル経済の発展が加速している

新たな科学技術革命と産業変革が加速する中、デジタル技術がもたらす成長の配当をすべての人がいかに共有で...