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

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

推薦する

2021 年に知っておくべきすべての機械学習アルゴリズム

機械学習に関する知識が増えるにつれて、機械学習アルゴリズムの数も増えました。この記事では、データ サ...

...

認知科学から進化まで、強化学習における最新の2つのブレークスルーを詳しく説明します

ビッグデータダイジェスト制作編纂者:李磊、銭天培近年、深層強化学習 (Deep RL) は人工知能に...

人工知能はメタバースのビジョンの実現に役立つでしょうか?

現在、メタバースの分野は、誇大宣伝と新規プロジェクトの立ち上げ数の点で急速に成長しており、業界の市場...

...

...

MIT が夢を創るマシン「ドリーム インキュベーター」を開発、インセプションの現実版をカスタマイズ

目が覚めているのと眠っているのを同時に経験したことがありますか?実はここは現実と夢を繋ぐ中継駅なので...

ザッカーバーグは7億ドルの配当金を受け取り、Metaの株価は14%急騰、オープンソース計画は成功したのか?

メタの株価は木曜日の時間外取引で14%近く上昇し、史上最高値に達したが、同社は初の配当を発表した。最...

ネットワークケーブルに沿って登ることが現実になりました。Audio2Photorealは、対話を通じてリアルな表情や動きを生成できます

携帯電話の冷たい画面を通して友達とチャットするときは、相手の口調を推測する必要があります。彼/彼女が...

SFが現実になる?偉大な劉慈欣がAI企業に入社

[[411067]]サイエンスフィクションと現実がこれほど近づいたことはかつてありませんでした。 「...

動物の顔認識技術は何に使われますか?

動物を正確に識別できる技術は、迷子になった動物を飼い主と再会させたり、農家が家畜を監視したり、研究者...

モノのインターネットはスマートな衛生設備を創り出し、都市環境の衛生を細かく管理します

旅行のピーク時に都市環境衛生がより大きな圧力に耐えられるか?清掃車両と清掃作業員をより適切に管理する...

画像からの「テキスト生成」の難しさを克服し、同レベルの拡散モデルを粉砕せよ! TextDiffuser アーキテクチャの 2 世代の詳細な分析

近年、テキスト生成画像、特に詳細レベルでリアルな効果を示す拡散ベースの画像生成モデルの分野で大きな進...

インテルが第3四半期の財務報告を発表、人工知能と新ファウンドリ事業が注目を集める

半導体メーカーのインテルは本日第3四半期の業績を発表し、同社の株価は時間外取引で約8%上昇した。一方...