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-12
ein 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.py
Verzeichnis ein Verzeichnis auf derselben Ebene wie die Datei: ner_dataset/ner
Entpacken 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'