bert
用于mrc任务的fine tuning
脚本
google-research/bert
A single training/test example for simple sequence classification.
For examples without an answer, the start and end position are -1.
读取Squad格式数据,一个query-answer为一个样本
__str__
调用__repr__
,print(object)
时的输出
__repr__
拼接字符串
A single set of features of data.
Read a SQuAD json file into a list of SquadExample.
使用tf.gfile
操作文件。依次读取json文件,data -> entry -> paragraphs -> context/qas(-> id question answers text answer_start)(Squad2
中包含is_impossible
字段);最后将每个样本保存为SquadExample
类型对象。
详细处理包括:
char_to_word_offset
用于根据答案和答案开始位置确定答案的起始位置(即模型的输出)tokenization.whitespace_tokenize
对原始答案进行取空白符\s
处理,判断能否从document
获取答案,不能则跳过(避免weird Unicode stuff
)paragraph_text = ‘This is a test, good luck!\r’
doc_tokens = [‘This’, ‘is’, ‘a’, ‘test,’, ‘good’, ‘luck!’]
char_to_word_offset = [0, 0, 0, 0, 0, 1, 1, 1, 2, 2,
3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5]
Loads a data file into a list of
InputBatch
s.
将每个样本从SquadExample
类型转为InputFeatures
类型。
question_text
用tokenizer.tokenize
处理。max_query_length
判断,超过最大长度则截断tokenizer.tokenize
处理doc_span
,将超过最大长度的文件进行窗口移动截断成多个片段[CLS]
+ query + [SEP]
+ context + [SEP]
,问题放在前面,可以利用BERTNext Sentence Predict
的训练方式获得更丰富的语义信息input_ids
使用tokenizer.convert_tokens_to_ids(tokens)
将词用词表中的id表示input_mask
词用1表示,填充用0表示segment_ids
文章中词用0表示,问题中词用1表示output_fn(feature)
进行run callback
,回调函数主要作用是进行特征写入
input_ids, input_mask, segment_ids
都用0进行填充
Returns tokenized answer spans that better match the annotated answer.
主要是将 (1895-1943) 处理为 ( 1895 - 1943 )
Check if this is the ‘max context’ doc span for the token.
当使用sliding window
方法后,
Doc: the man went to the store and bought a gallon of milk
Span A: the man went to the
Span B: to the store and bought
Span C: and bought a gallon of
要获得一个词的最大上下文,比如bought
在B中有4个左上下文和0个右上下文,而在C中有1个左上下文和3个右上下文,最终选择片段C。
Create a classification model
Bert fine tuning:
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)
# 得到词向量输出(run_classifier.py中model.get_pooled_output()是一维句子向量)
final_hidden = model.get_sequence_output()
# 输出维度为(batch_size, seq_length, word_vector_shape)
final_hidden_shape = modeling.get_shape_list(final_hidden, expected_rank=3)
batch_size = final_hidden_shape[0]
seq_length = final_hidden_shape[1]
hidden_size = final_hidden_shape[2]
# 获得weights和bias变量
output_weights = tf.get_variable(
"cls/squad/output_weights", [2, hidden_size],
initializer=tf.truncated_normal_initializer(stddev=0.02))
output_bias = tf.get_variable(
"cls/squad/output_bias", [2], initializer=tf.zeros_initializer())
final_hidden_matrix = tf.reshape(final_hidden,
[batch_size * seq_length, hidden_size])
# 全连接层:matmul + bias
logits = tf.matmul(final_hidden_matrix, output_weights, transpose_b=True)
logits = tf.nn.bias_add(logits, output_bias)
# 维度转换与句子分解
logits = tf.reshape(logits, [batch_size, seq_length, 2])
logits = tf.transpose(logits, [2, 0, 1])
unstacked_logits = tf.unstack(logits, axis=0)
# 模型输出
(start_logits, end_logits) = (unstacked_logits[0], unstacked_logits[1])
Returns
model_fn
closure for TPUEstimator.
The
model_fn
for TPUEstimator.
Creates an
input_fn
closure to be passed to TPUEstimator.
设计一个模型的输出函数,完成读取tf.record文件,反序列化样本获得原始的样本,如果是训练的话,则打乱数据集,获取batch量的样本集
Decodes a record to a TensorFlow example.
The actual input function.
Write final predictions to the json file and log-odds of null if needed.
根据模型输出的预测概率,寻找答案的最优起始点。由于长度限制而进行了文本分片,因此每个片段有多个起始点,需要选择有最大上下文的片段。
Project the tokenized prediction back to the original text.
pred_text = steve smith
orig_text = Steve Smith's
Get the n-best logits from a list.
模型最后输出两个数组,分别为样本中每个词作为答案开始和结束的概率。需要找到开始和结束概率最大的前n个词,然后进行组合得到最终的答案。
Compute softmax probability over raw logits.
对答案组合计算softmax得分。
Writes InputFeature to TF example file
临时特征文件存储。
Write a InputFeature to the TFRecordWriter as a tf.train.Example.
features = collections.OrderedDict()
features['key'] = create_int_feature(value)
#...
# 定义一个Example,包含若干个feature,每个feature是key-value结构
tf_example = tf.train.Example(features=tf.train.Features(feature=features))
# 将样本序列化(压缩)保存到tf.record文件中
self.__writer.write(tf_example.SerializeToString())
Validate the input FLAGS or throw an exception
关于命令行输出参数的异常判断。
主体函数的调用,源代码中在预测处理时进行了较多的细节操作,此处省略。
model_fn_builder
中调用create_model
创建网络模型(fine tuning)
checkpoint
,并根据tf.estimator.ModeKeys
,进行具体的训练和预测操作;训练包括定义loss
函数和优化函数;预测则直接得到预测结果estimator = tf.contrib.TPUEstimator(model_fn)
,创建时输入网络模型estimator.train(train_input_fn)
estimator.predict(predict_input_fn)
train_examples = read_squad_examples
读取样本数据,返回为SquadExample
对象train_writer = FeatureWriter()
,特征写入器
tf.train.Example()
convert_examples_to_features(train_exampes,train_writer.process_feature)
SquadExample
对象解析为InputFeatures
对象train_writer.process_feature
,将转换的InputFeatures
特征写入文件train_input_fn = input_fn_builder(train_writer.filename)
,用于训练时特征读取器
unique_ids
,input_ids
,input_mask
,segment_ids
tf.data.TFRecordDataset()
的创建和读取estimator.train(train_input_fn)
,使用特征读取器训练eval_examples = read_squad_examples()
读取测试数据,返回为SquadExample
对象eval_writer = FeatureWriter()
,特征写入器convert_examples_to_features(eval_examples,tokenizer,append_feature)
eval_examples
tokenizer
append_feature
,回调函数,保存到eval_features
中(便于得到预测结果);用eval_writer.process_feature
写入文件predict_input_fn = input_fn_builder(eval_writer.filename)
,用于预测时的特征读取器estimator.predict(predict_input_fn)
,使用特征读取器预测write_predictions(eval_examples, eval_features, all_result)
,得到预测结果解析并保存文件create_model(bert_config, is_training, input_ids, input_mask, segment_ids)
is_training
,do_train
时为True
,否则为False
input_ids
,input_mask
,segment_ids
,输入模型的特征向量Ref
用于添加命令行参数
# 定义参数
tf.flags.DEFINE_string("strParam", value, "This is a string param")
#tf.flags.DEFINE_bool/integer/float/
# 使用参数
tf.flags.FLAGS.strParam
# 命令行输入,更换参数
# python file.py --strParam strname
Ref
TensorFlow的文件操作,包括但不限于:
代码中使用了大量的嵌套函数,主要用于Estimator回调;可以使用functools
库直接实现
import functools
train_inpf = functools.partial(input_fn, param1, param2)