この記事は次のように構成されています。
- 固有表現抽出 (NER) とは
- どのように識別しますか?
cs224d 7日目: プロジェクト2 - DNNを使用してNER問題を解決する コース プロジェクトの説明 住所
NERとは何ですか? 固有表現抽出 (NER) とは、主に人名、地名、機関名、固有名詞など、テキスト内の特定の意味を持つエンティティを識別することを指します。固有表現認識は、情報抽出、質問応答システム、構文解析、機械翻訳などの応用分野における重要な基本ツールであり、構造化された情報を抽出する上で重要なステップです。 BosonNLPより抜粋 どのように識別しますか?まず問題解決のロジックを説明し、次にメインコードを説明します。興味のある方は、完全なコードを見るためにここを参照してください。 このコードは、NER 問題を処理するために、Tensorflow の下で 1 つの隠し層のみを持つ DNN を構築します。 1. 問題の特定: NER は分類問題です。 単語が与えられたら、文脈に基づいて次の 4 つのカテゴリのどれに属するかを判断する必要があります。次の 4 つのカテゴリのいずれにも属さない場合、カテゴリは 0 となり、エンティティではないことを意味します。したがって、これは 5 つのカテゴリに分割する必要がある問題です。 - • 人(PER)
- • 組織(ORG)
- • 場所 (LOC)
- • その他(MISC)
トレーニング データには 2 つの列があり、最初の列は単語、2 番目の列はラベルです。 - EU 組織
- 拒否する
- ドイツ語その他
- ピーター・パー
- ブリュッセル LOC
2. モデル:
次に、ディープニューラルネットワークを使用してトレーニングします。 モデルは次のとおりです。 入力層の x^(t) は、x_t を中心としたウィンドウ サイズ 3 のコンテキストです。x_t はワンホット ベクトルです。x_t と L を適用すると、対応する単語ベクトルになり、単語ベクトルの長さは d = 50 になります。 隠し層が 1 つだけのニューラル ネットワークを構築します。隠し層の次元は 100、y^ は予測値、次元は 5 です。 クロスエントロピーを使用してエラーを計算します。 J は各パラメータに関して微分されます。 次の導出式が得られます。 TensorFlow では、導出は自動的に実装されます。ここでは、Adam 最適化アルゴリズムを使用して勾配を更新し、収束するまで継続的に反復して損失を小さくしていきます。 3. 具体的な実施def test_NER() では、max_epochs の反復を実行します。毎回、トレーニング データを使用してモデルをトレーニングし、train_loss と train_acc のペアを取得します。次に、このモデルを使用して検証データを予測し、val_loss と予測のペアを取得します。最小の val_loss を選択し、対応するパラメーターの重みを保存します。最後に、これらのパラメーターを使用して、テスト データのカテゴリ ラベルを予測します。 - テスト_NER()を定義します:
- config = 設定()
- tf.Graph().as_default() を使用する場合:
- model = NERModel(config) # メインクラス
-
- 初期化 = tf.initialize_all_variables()
- セーバー = tf.train.Saver()
-
- tf.Session() をセッションとして使用:
- best_val_loss = float ( 'inf' ) # 最良値、その損失、反復回数、エポック
- ベストバリューエポック = 0
-
- セッションの実行(初期化)
- xrange(config.max_epochs) のエポックの場合:
- 'Epoch {}'を印刷します。.format(epoch)
- 開始 = 時間.時間()
- ###
- train_loss、train_acc = model.run_epoch(セッション、model.X_train、
- model.y_train) # 1.訓練データを反復処理に投入し、損失と精度を取得します
- val_loss, predictions = model.predict(session, model.X_dev, model.y_dev) # 2.このモデルを使用して開発データを予測し、損失と予測を取得します
- 'トレーニング損失: {}'を印刷します。.format(train_loss)
- 'トレーニング acc: {}'を印刷します。.format(train_acc)
- '検証損失: {}'を印刷します。.format(val_loss)
- if val_loss < best_val_loss: # valデータの損失を使用して最小損失を見つける
- ベストバリューロス = バリューロス
- best_val_epoch = エポック
- os.path.exists( "./weights" )が存在しない場合は:
- os.makedirs( "./weights" )
-
- saver.save(session, './weights/ner.weights' ) # 最小損失に対応する重みを保存します
- epoch - best_val_epoch > config.early_stoppingの場合:
- 壊す
- ###
- 混乱 = calculate_confusion(config, predictions, model.y_dev) # 3. devラベルデータを入れて予測の混乱を計算する
- print_confusion(混乱、モデル.num_to_tag)
- '合計時間: {}'を印刷します。.format(time.time() - start)
-
- saver.restore(session, './weights/ner.weights' ) # 保存した重みを再度読み込み、テストデータを使用して予測を行い、予測結果を取得します
- 'テスト'を印刷する
- '=-=-='を印刷
- '予測をq2_test.predictedに書き込んでいます' と印刷します
- _、予測 = model.predict(セッション、model.X_test、model.y_test)
- save_predictions(predictions, "q2_test.predicted" ) # 予測結果を保存する
-
- __name__ == "__main__"の場合:
- テスト_NER()
4. モデルはどのようにトレーニングされますか? まず、データのトレーニング、検証、テストをインポートします。 - # トレーニングセットをロードする
- docs = du.load_dataset( 'data/ner/train' )
-
- # 開発セットをロードする(ハイパーパラメータの調整用)
- docs = du.load_dataset( 'data/ner/dev' )
-
- # テストセットをロードする(ダミーラベルのみ)
- docs = du.load_dataset( 'data/ner/test.masked' )
単語をワンホットベクトルに変換した後、単語ベクトルに変換します。 - デフadd_embedding( self ):
-
- tf.device( '/cpu:0' ) の場合:
-
- embedding = tf.get_variable( 'Embedding' , [len( self .wv), self .config.embed_size])
- window = tf.nn.embedding_lookup(embedding, self .input_placeholder)
- ウィンドウ = tf.reshape(
- ウィンドウ、[- 1 、 self .config.window_size * self .config.embed_size])
-
- 返品期間
最初のレイヤーを初期化するために xavier を使用し、L2 正則化とドロップアウトを使用してオーバーフィッティングを減らすなど、ニューラル レイヤーを構築します。 - def add_model(自己、 ウィンドウ):
-
- tf.variable_scope( 'Layer1' , initializer=xavier_weight_init()) をスコープとして:
- W = tf.get_variable(
- 'W' , [ self .config.window_size * self .config.embed_size,
- 自己.config.hidden_size])
- b1 = tf.get_variable( 'b1' , [ self.config.hidden_size ])
- h = tf.nn.tanh(tf.matmul(ウィンドウ、W) + b1)
- もし self .config.l2:
- tf.add_to_collection( 'total_loss' , 0.5 * self.config.l2 * tf.nn.l2_loss(W))
-
- スコープとして tf.variable_scope( 'Layer2' , initializer=xavier_weight_init()) を使用します。
- U = tf.get_variable( 'U' , [ self .config.hidden_size, self .config.label_size])
- b2 = tf.get_variable( 'b2' , [ self.config.label_size ])
- y = tf.matmul(h, U) + b2
- もし 自己.config.l2:
- tf.add_to_collection( 'total_loss' 、 0.5 * self .config.l2 * tf.nn.l2_loss(U))
- output = tf.nn.dropout(y, self .dropout_placeholder)
-
- 出力を返す
L2 正則化とドロップアウトとは何か、またオーバーフィッティングの問題を軽減する方法の詳細については、これらを簡潔かつ明確にまとめたこのブログ投稿をお読みください。 クロスエントロピーを使用して損失を計算します。 - def add_loss_op(自己, y):
-
- cross_entropy = tf.reduce_mean(
- tf.nn.softmax_cross_entropy_with_logits(y, self .labels_placeholder))
- tf.add_to_collection( 'total_loss' , cross_entropy)
-
- loss = tf.add_n(tf.get_collection( 'total_loss' ))
-
- リターンロス
次に、Adam Optimizer を使用して損失を最小限に抑えます。 - def add_training_op(自己、 損失 ):
-
- オプティマイザー = tf.train.AdamOptimizer( self .config.lr)
- global_step = tf.Variable( 0 、名前 = 'global_step' 、トレーニング可能 = False )
- train_op = optimizer.minimize(loss, global_step=global_step)
-
- train_opを返す
各トレーニングの後、損失を最小限に抑える対応する重みが得られます。 このようにして、NER分類の問題は解決されます。もちろん、精度やその他の問題を改善するためには、引き続き文献を参照して研究する必要があります。次回はまずRNNを実装します。 |