Erfahren Sie, wie Sie mit BERT die NER-Erkennung benannter Entitäten durchführen

1. Laden Sie den Bert-Quellcode und das Modell von GitHub herunter

Die ausführliche Einführung von Bert und wie man es herunterlädt, wird hier nicht wiederholt. Die Github-Bert-URL lautet wie folgt: GitHub-Bert , laden Sie den Bert-Code und das Bert-Modell herunter.

1. Laden Sie den Bert-Code herunter

Terminal führt aus:git clone https://github.com/google-research/bert.git

2. Laden Sie das chinesische Basismodell herunter

Chinesische Bert-Base-Modelladresse
Entpacken Sie das Modell in prev_trained_model/chinese_L-12_H-768_A-12ein Verzeichnis

Zweitens: Laden Sie Daten herunter

1. Laden Sie den Demodatensatz herunter

Bert Model-Ner Task-Demo-Datensatz, Download-Adresse: https://download.csdn.net/download/TFATS/23512817

2. Erstellen Sie ein Datensatzspeicherverzeichnis

Erstellen Sie im run_classifier.pyVerzeichnis ein Verzeichnis auf derselben Ebene wie die Datei: ner_dataset/nerEntpacken Sie den heruntergeladenen Demo-Datensatz in dieses Verzeichnis. Dieses Verzeichnis kann auch frei angegeben werden.

Drittens erstellen Sie die Datei run_classifier.sh

Erstellen Sie zum Starten und Optimieren von Aspekten eine run_classifier.sh-Datei.
Hinweis: Der Ladepfad des Vortrainingsmodells, der Ladepfad jedes Datensatzes und der Ausgabeprüfpunktpfad in dieser Datei müssen dem tatsächlichen Pfad entsprechen.

#!/usr/bin/env bash
# @Author: nijiahui
# @Date:   2021-09-07 14:19:36

TASK_NAME="ner"
MODEL_NAME="chinese_L-12_H-768_A-12"

CURRENT_DIR=$(cd -P -- "$(dirname -- "$0")" && pwd -P)
# # 【horovod】get GPU - number
# gpu_num=$(nvidia-smi --query-gpu=name --format=csv,noheader | wc -l)

export PRETRAINED_MODELS_DIR=$CURRENT_DIR/prev_trained_model
export BERT_BASE_DIR=$PRETRAINED_MODELS_DIR/$MODEL_NAME
export DATA_DIR=$CURRENT_DIR/ner_dataset
# 【减少打印】
export OMPI_MCA_btl_vader_single_copy_mechanism='none'

# run task
cd $CURRENT_DIR
echo "Start running..."

python run_classifier.py \
  --task_name=$TASK_NAME \
  --do_train=true \
  --do_eval=true \
  --do_predict=true \
  --data_dir=$DATA_DIR/$TASK_NAME \
  --vocab_file=$BERT_BASE_DIR/vocab.txt \
  --bert_config_file=$BERT_BASE_DIR/bert_config.json \
  --init_checkpoint=$BERT_BASE_DIR/bert_model.ckpt \
  --max_seq_length=48 \
  --train_batch_size=32 \
  --learning_rate=2e-5 \
  --num_train_epochs=1.0 \
  --output_dir=$CURRENT_DIR/${TASK_NAME}_output \

Viertens: Ändern Sie die Prozessoren

Da wir benutzerdefinierte Änderungen am Format und der Beschriftung der Eingabedaten vorgenommen haben, wird das Prozessorobjekt in der Datei run_classifier.py wie folgt geändert:

1. Passen Sie NerProcessor an
def main(_):
    tf.logging.set_verbosity(tf.logging.INFO)

    # 【一,数据构造器 - 定义 NerProcessor 】
    processors = {
    
    
      "ner": NerProcessor
    }
    ... ...
2. Ändern Sie die NerProcessor-Klasse
class NerProcessor(DataProcessor):
    """Processor for the XNLI data set."""
    ... ...
    def get_labels(self):
    	clue_labels = ['address', 'book', 'company', 'game', 'government', 'movie', 'name', 'organization', 'position', 'scene']
        res = ['O'] + [p + '-' + l for p in ['B', 'M', 'E', 'S'] for l in clue_labels]
        # 此处也可改为从label.csv文件中读取,方便后面做模型推理,这里为方便理解,做如上修改。
        return res

    def _create_examples(self, lines):
        """See base class."""
        examples = []
        for (i, line) in enumerate(lines):
            guid = "%s" % (i) if 'id' not in line else line['id']
            text_a = tokenization.convert_to_unicode(line['text'])
            label = ['O'] * len(text_a)
            if 'label' in line:
                for l, words in line['label'].items():
                    for word, indices in words.items():
                        for index in indices:
                            if index[0] == index[1]:
                                label[index[0]] = 'S-' + l
                            else:
                                label[index[0]] = 'B-' + l
                                label[index[1]] = 'E-' + l
                                for i in range(index[0] + 1, index[1]):
                                    label[i] = 'M-' + l
            examples.append(
                InputExample(guid=guid, text_a=text_a, label=label))
        return examples

    def _create_examples_train(self, lines):
        """See base class."""
        examples = []
        for (i, line) in enumerate(lines):
            guid = "%s" % (i) if 'id' not in line else line['id']
            text_a = tokenization.convert_to_unicode(line['text'])
            label = ['O'] * len(text_a)
            if 'label' in line:
                for l, words in line['label'].items():
                    for word, indices in words.items():
                        for index in indices:
                            if index[0] == index[1]:
                                label[index[0]] = 'S-' + l
                            else:
                                label[index[0]] = 'B-' + l
                                label[index[1]] = 'E-' + l
                                for i in range(index[0] + 1, index[1]):
                                    label[i] = 'M-' + l
            examples.append(
                InputExample(guid=guid, text_a=text_a, label=label))
        return examples

Fünftens: Ändern Sie das Etikett

1. Ändern Sie die Methode „convert_single_example“.

Der Teil mit dem Wort [modify] ist der geänderte Teil; das Wort [add] ist der neue Teil.

def convert_single_example(ex_index, example, label_list, max_seq_length,
                           tokenizer):
  if isinstance(example, PaddingInputExample):
    return InputFeatures(
        input_ids=[0] * max_seq_length,
        input_mask=[0] * max_seq_length,
        segment_ids=[0] * max_seq_length,
        # label_id=0,   【modify】
        label_id=[0] * max_seq_length,	# 【add】
        is_real_example=False)

  ... ... 
  
  tokens = []
  segment_ids = []
  label_ids = []    # 【add 】
  
  tokens.append("[CLS]")
  segment_ids.append(0)
  label_ids.append(0)	# 【add 】
  
  # for token in tokens_a:	【modify】
  #   tokens.append(token)	
  #   segment_ids.append(0)	
  for i, token in enumerate(tokens_a):	# 【add 】
      tokens.append(token)
      segment_ids.append(0)
      try:
          label_ids.append(label_map[example.label[i]])
      except Exception as e:
          print("[debug1]",e)
          label_ids.append(0)
  label_ids.append(0)	

  ... ...

  # Zero-pad up to the sequence length.
  while len(input_ids) < max_seq_length:
    input_ids.append(0)
    input_mask.append(0)
    segment_ids.append(0)
    label_ids.append(0)		# 【add】

  assert len(input_ids) == max_seq_length
  assert len(input_mask) == max_seq_length
  assert len(segment_ids) == max_seq_length

  # label_id = label_map[example.label]		  # 【modify】

  if ex_index < 5:
    tf.logging.info("*** Example ***")
    tf.logging.info("guid: %s" % (example.guid))
    tf.logging.info("tokens: %s" % " ".join(
        [tokenization.printable_text(x) for x in tokens]))
    tf.logging.info("input_ids: %s" % " ".join([str(x) for x in input_ids]))
    tf.logging.info("input_mask: %s" % " ".join([str(x) for x in input_mask]))
    tf.logging.info("segment_ids: %s" % " ".join([str(x) for x in segment_ids]))
    tf.logging.info("label: %s (id = %s)" % (example.label, label_ids)) # 【modify】

  feature = InputFeatures(
      input_ids=input_ids,
      input_mask=input_mask,
      segment_ids=segment_ids,
      label_id=label_ids,   # 【modify】
      is_real_example=True)
  return feature
2. Ändern Sie die Methode file_based_convert_examples_to_features
def file_based_convert_examples_to_features(
    examples, label_list, max_seq_length, tokenizer, output_file):
		... ...
        features["segment_ids"] = create_int_feature(feature.segment_ids)
        # features["label_ids"] = create_int_feature([feature.label_id])    # 【modify】
        features["label_ids"] = create_int_feature(feature.label_id) 	# 【add】
		... ...
    writer.close()
3. Ändern Sie die Methode file_based_input_fn_builder
def file_based_input_fn_builder(input_file, seq_length, is_training,
                                drop_remainder):
  name_to_features = {
    
    
      "input_ids": tf.FixedLenFeature([seq_length], tf.int64),
      "input_mask": tf.FixedLenFeature([seq_length], tf.int64),
      "segment_ids": tf.FixedLenFeature([seq_length], tf.int64),
      # "label_ids": tf.FixedLenFeature([], tf.int64),  # 【modify】
      "label_ids": tf.FixedLenFeature([seq_length], tf.int64),  # 【add】
      "is_real_example": tf.FixedLenFeature([], tf.int64),
  }
  ... ...

Sechstens: Ändern Sie create_model

def create_model(bert_config, is_training, input_ids, input_mask, segment_ids,
                 labels, num_labels, use_one_hot_embeddings):
  """Creates a classification model."""
  model = modeling.BertModel(
      config=bert_config,
      is_training=is_training,
      input_ids=input_ids,
      input_mask=input_mask,
      token_type_ids=segment_ids,
      use_one_hot_embeddings=use_one_hot_embeddings)

  # output_layer = model.get_pooled_output()	# Ner任务不从池化层取数据
  ### output_layers .shape: [batch_size, seq_length, hidden_size]
  output_layers = model.get_sequence_output()
  
  seq_length = output_layer.shape[1].value
  hidden_size_double = output_layer.shape[2].value
  
  ### output_layer : [batch_size*seq_length, hidden_size]
  output_layer = tf.reshape(output_layer, shape=(-1, hidden_size_double))

  # 定义全连接的 weight , bias .
  output_weights = tf.get_variable(
      "output_weights", [num_labels, hidden_size_double],
      initializer=tf.truncated_normal_initializer(stddev=0.02))

  output_bias = tf.get_variable(
      "output_bias", [num_labels], initializer=tf.zeros_initializer())

  with tf.variable_scope("loss"):
    if is_training:
      output_layer = tf.nn.dropout(output_layer, keep_prob=0.9)

    ### logits.shape : [batch_size*seq_length, num_labels]
    logits = tf.matmul(output_layer, output_weights, transpose_b=True)
    logits = tf.nn.bias_add(logits, output_bias)
    
    ### logits.shape : [batch_size, seq_length, num_labels]
    logits = tf.reshape(logits, shape=(-1, seq_length, num_labels))

    # 分别计算出 【probabilities 、 log_probs】
    probabilities = tf.nn.softmax(logits, axis=-1)
    log_probs = tf.nn.log_softmax(logits, axis=-1)

    # 将 softmax 的输出结果转化为 onehot 编码
    one_hot_labels = tf.one_hot(labels, depth=num_labels, dtype=tf.float32)

    # 每个step的 loss 损失数
    per_example_loss = -tf.reduce_sum(one_hot_labels * log_probs, axis=-1)
    # 全局 loss 损失数
    loss = tf.reduce_mean(per_example_loss)

    return (loss, per_example_loss, logits, probabilities)

Sieben, modifizieren Sie das Horovod-Multikartentraining

Der Autor hat es bereits zuvor vorgestellt, daher werde ich es hier nicht wiederholen. Basierend auf TensorFlow1 wird Horovod verwendet, um das Multi-GPU-Kartentraining von BERT auf einem einzelnen Knoten zu realisieren.

Acht: Verwenden Sie gemischtes Präzisionstraining und reduzieren Sie den Protokolldruck

1. Verwenden Sie gemischtes Präzisionstraining

Ändern Sie die Datei „optimierung.py“.

os.environ['TF_AUTO_MIXED_PRECISION_GRAPH_REWRITE_IGNORE_PERFORMANCE'] = '1'  #在导包后加入代码

...

optimizer = AdamWeightDecayOptimizer(
        learning_rate=learning_rate * sqrt(hvd.size()),
        # learning_rate=learning_rate,
        weight_decay_rate=0.01,
        beta_1=0.9,
        beta_2=0.999,
        epsilon=1e-6,
        exclude_from_weight_decay=["LayerNorm", "layer_norm", "bias"])
        
    # 使用混合精度的优化器
    optimizer = tf.train.experimental.enable_mixed_precision_graph_rewrite(optimizer) # 【add】
    # 使用horovod多卡训练
    optimizer = hvd.DistributedOptimizer(optimizer)	# 【add】

Beachten:

  • Es wird empfohlen, TF 1.14 und höher zu verwenden.
  • Durch direktes Festlegen der Umgebungsvariablen TF_ENABLE_AUTO_MIXED_PRECISION wird keine gemischte Präzision aktiviert.
  • Bei cuda9.0 gibt es fast keinen Beschleunigungseffekt, und der Beschleunigungseffekt ist nur bei cuda9.2 und höher verfügbar. cuda9.2 und höher enthalten die cuBLAS-Bibliothek, ein Optimierungsframework mit gemischter Präzision für Deep Learning.
2. Reduzieren Sie den Protokolldruck

Fügen Sie der Datei run_classifier.sh Code hinzu:

export OMPI_MCA_btl_vader_single_copy_mechanism='none'

おすすめ

転載: blog.csdn.net/TFATS/article/details/120241412