Enseñarle cómo usar BERT para realizar el reconocimiento de entidades con nombre NER

1. Descargue el código fuente y el modelo de Bert desde GitHub.

La introducción detallada de Bert y cómo descargarlo no se repetirá aquí. La URL de github-bert es la siguiente: GitHub-Bert , descargue el código y el modelo de bert.

1. Descargue el código Bert

Terminal ejecuta:git clone https://github.com/google-research/bert.git

2. Descargue el modelo base chino

Dirección del modelo chino Bert-Base
Descomprima el modelo en prev_trained_model/chinese_L-12_H-768_A-12un directorio

Segundo, descargar datos

1. Descargue el conjunto de datos de demostración

Conjunto de datos de demostración de tareas Bert model-Ner, dirección de descarga: https://download.csdn.net/download/TFATS/23512817

2. Cree un directorio de almacenamiento de conjuntos de datos

Cree un directorio en run_classifier.pyel directorio al mismo nivel que el archivo:, ner_dataset/nerdescomprima el conjunto de datos de demostración descargado en este directorio, y este directorio también se puede especificar libremente.

En tercer lugar, cree el archivo run_classifier.sh.

Para iniciar y ajustar aspectos, cree un archivo run_classifier.sh.
Nota: La ruta de carga del modelo previo al entrenamiento, la ruta de carga de cada conjunto de datos y la ruta del punto de control de salida en este archivo deben corresponder a la ruta real.

#!/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 \

Cuarto, modificar los procesadores.

Dado que hemos realizado modificaciones personalizadas al formato y la etiqueta de los datos de entrada, el objeto de procesadores en el archivo run_classifier.py se modifica de la siguiente manera:

1. Personaliza NerProcessor
def main(_):
    tf.logging.set_verbosity(tf.logging.INFO)

    # 【一,数据构造器 - 定义 NerProcessor 】
    processors = {
    
    
      "ner": NerProcessor
    }
    ... ...
2. Modificar la clase NerProcessor
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

Cinco, modifica la etiqueta.

1. Modifique el método convert_single_example

La parte con la palabra [modificar] es la parte modificada; la palabra [agregar] es la parte nueva.

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. Modifique el método 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. Modifique el método 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),
  }
  ... ...

Seis, modifica 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)

Siete, modifica el entrenamiento multitarjeta de horovod

El autor ya lo ha presentado antes, por lo que no lo repetiré aquí. Basado en TensorFlow1, Horovod se utiliza para realizar el entrenamiento de tarjetas multi-GPU de BERT en un solo nodo.

Ocho, utilice entrenamiento de precisión mixto y reduzca la impresión de registros

1. Utilice entrenamiento de precisión mixto

Modificar el archivo optimización.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】

Aviso:

  • Se recomienda utilizar tf 1.14 y superior.
  • Configurar la variable de entorno TF_ENABLE_AUTO_MIXED_PRECISION directamente no habilita la precisión mixta.
  • Casi no hay efecto de aceleración en cuda9.0, y el efecto de aceleración solo está disponible en cuda9.2 y superiores. cuda9.2 y superiores incluyen la biblioteca cuBLAS, que es un marco de optimización de precisión mixta para el aprendizaje profundo.
2. Reducir la impresión de registros

Agregue código al archivo run_classifier.sh:

export OMPI_MCA_btl_vader_single_copy_mechanism='none'

Supongo que te gusta

Origin blog.csdn.net/TFATS/article/details/120241412
Recomendado
Clasificación