TensorFlow Models BERT Legacy 实现指南:从预训练 Checkpoint 到 GLUE/SQuAD 微调的完整实战
本文基于仓库中 official/legacy/bert/README.md 的官方文档,系统讲解该目录内 TensorFlow 2.x 版 BERT legacy 实现的完整使用链路:预训练 checkpoint 与 tf.hub 模块的获取与恢复、tf-nightly/TPU 环境搭建、预训练与微调数据生成,以及用 run_classifier.py、run_squad.py 在 GPU/TPU 上完成 MRPC 句对分类和 SQuAD 1.1 问答微调。全文在继承原文档全部命令与配置的基础上,结合 official/legacy/bert 下的源码补充了关键命令行参数的默认值、取值范围与底层调用逻辑。
1. Legacy 实现定位:与 nlp/modeling 新实现的关系
需要先明确本文代码的版本定位。README 开头即给出弃用警告:该目录中的大部分代码正在被逐步弃用(deprecating),官方建议改用新教程与 nlp/modeling 中的新代码;而该 README 本身对这份 legacy 实现依然正确。
从源码结构看,legacy 目录的 BERT 模型并非独立实现,而是复用了新代码库的 Keras functional 风格网络:bert_models.py 直接 from official.nlp.modeling import models 和 from official.nlp.modeling import networks(bert_models.py#L23-L24),并引入了 official/legacy/albert/configs.py 以兼容 ALBERT 配置。这与 README 中“已切换到使用 Keras functional-style 网络”的说明一致。
README 中列出的预训练 checkpoint 与 hub 模块均由 TF 1.x 官方 BERT 仓库(google-research/bert)的 checkpoint 转换而来,以保证与 BERT 论文(arXiv: 1810.04805)结果一致。使用这份 legacy 代码的典型场景是:需要对齐论文原始实验配置、迁移旧脚本、或复用 TF1 转换出的 checkpoint 命名结构。
2. 预训练模型:Checkpoint 与 tf.hub 模块
2.1 预训练 Checkpoint 清单
Checkpoint 托管在 Google Cloud Storage 的 gs://cloud-tpu-checkpoints/bert/keras_bert 目录下,README 推荐在使用 Cloud GPU/TPU 时直接以 GCS 路径作为 BERT_DIR。完整清单如下(维度记法为 L-层数_H-隐藏维度_A-注意力头数):
| 模型 | 规格 | 参数规模 | GCS 相对位置 |
|---|---|---|---|
| BERT-Large, Uncased (Whole Word Masking) | 24 层, 1024 隐藏, 16 头 | 340M | wwm_uncased_L-24_H-1024_A-16 |
| BERT-Large, Cased (Whole Word Masking) | 24 层, 1024 隐藏, 16 头 | 340M | wwm_cased_L-24_H-1024_A-16 |
| BERT-Base, Uncased | 12 层, 768 隐藏, 12 头 | 110M | uncased_L-12_H-768_A-12 |
| BERT-Large, Uncased | 24 层, 1024 隐藏, 16 头 | 340M | uncased_L-24_H-1024_A-16 |
| BERT-Base, Cased | 12 层, 768 隐藏, 12 头 | 110M | cased_L-12_H-768_A-12 |
| BERT-Large, Cased | 24 层, 1024 隐藏, 16 头 | 340M | cased_L-24_H-1024_A-16 |
| BERT-Base, Multilingual Cased | 104 种语言, 12 层, 768 隐藏, 12 头 | 110M | multi_cased_L-12_H-768_A-12 |
每个 checkpoint 目录内含 bert_model.ckpt、bert_config.json 与 vocab.txt,后文微调命令中的 --bert_config_file、--init_checkpoint、--vocab_file 均指向这些文件。
2.2 用 tf.train.Checkpoint 恢复权重
README 给出的恢复方式是 TF2 标准的 tf.train.Checkpoint 机制:
init_checkpoint = 'the pretrained model checkpoint path.'
model = tf.keras.Model() # BERT 预训练模型作为特征提取器
checkpoint = tf.train.Checkpoint(model=model)
checkpoint.restore(init_checkpoint)
在仓库源码中可以看到训练入口实际采用的写法。run_classifier.py 在 run_keras_compile_fit 中恢复 checkpoint 时注册了两个名称(run_classifier.py#L219-L221):
if init_checkpoint:
checkpoint = tf.train.Checkpoint(model=sub_model, encoder=sub_model)
checkpoint.read(init_checkpoint).assert_existing_objects_matched()
这里把同一个子模型同时以 model 和 encoder 两个名字注册进 tf.train.Checkpoint,再用 read + assert_existing_objects_matched 做匹配校验。从源码结构看,这样命名的目的是同时兼容以 model 命名的 checkpoint 和以 encoder 命名的 tf.hub 模块导出,这正是 --init_checkpoint 与 --hub_module_url 可以互换使用的底层原因。
2.3 预训练 tf.hub 模块
SavedModel 格式的 hub 模块覆盖与 checkpoint 相同的模型矩阵,并额外提供中文模型:
| Hub 模块 | 规格 | 参数规模 |
|---|---|---|
| BERT-Large, Uncased (WWL) | 24 层, 1024 隐藏, 16 头 | 340M |
| BERT-Large, Cased (WWL) | 24 层, 1024 隐藏, 16 头 | 340M |
| BERT-Base, Uncased | 12 层, 768 隐藏, 12 头 | 110M |
| BERT-Large, Uncased | 24 层, 1024 隐藏, 16 头 | 340M |
| BERT-Base, Cased | 12 层, 768 隐藏, 12 头 | 110M |
| BERT-Large, Cased | 24 层, 1024 隐藏, 16 头 | 340M |
| BERT-Base, Multilingual Cased | 104 种语言, 12 层, 768 隐藏, 12 头 | 110M |
| BERT-Base, Chinese | 简体中文/繁体, 12 层, 768 隐藏, 12 头 | 110M |
在训练脚本中,用 --hub_module_url=<tfhub 模块路径> 替代 --init_checkpoint 即可加载 hub 模块;common_flags.py 中 hub_module_url 的定义注明“如果指定了该 flag,则不应再使用 init_checkpoint”(common_flags.py#L71-L75),且 hub_module_trainable 默认为 True,即 hub 模块中的 Keras 层默认参与训练(可微调)而非冻结。
3. 环境搭建
README 给出的环境准备步骤如下:
export PYTHONPATH="$PYTHONPATH:/path/to/models"
安装 tf-nightly 获取最新特性:
pip install tf-nightly-gpu
在 TPU 上训练时无需 GPU 支持,需要先用 ctpu 工具创建一台 tf-nightly 版 TPU 实例:
ctpu up -name <instance name> --tf-version="nightly"
然后在 VM 上安装 TF2 的 tf-nightly:
pip install tf-nightly
适用前提说明:该目录代码面向 TF 2.x + tf-nightly,README 中 pip install tf-nightly-gpu / pip install tf-nightly 与 ctpu up --tf-version=nightly 组合使用;运行入口脚本依赖 tensorflow_hub(bert_models.py 顶层 import tensorflow_hub as hub)。
4. 数据处理
4.1 预训练数据:create_pretraining_data.py
生成预训练数据的脚本没有变化,使用 official/nlp/data/create_pretraining_data.py——该脚本基本源自 BERT 研究仓库,并已适配 TF2 符号与 Python 3。脚本需要输入/输出目录和词表文件,且 max_seq_length 必须与之后运行预训练时指定的序列长度一致。
README 给出的调用示例:
export WORKING_DIR='local disk or cloud location'
export BERT_DIR='local disk or cloud location'
python models/official/nlp/data/create_pretraining_data.py \
--input_file=$WORKING_DIR/input/input.txt \
--output_file=$WORKING_DIR/output/tf_examples.tfrecord \
--vocab_file=$BERT_DIR/wwm_uncased_L-24_H-1024_A-16/vocab.txt \
--do_lower_case=True \
--max_seq_length=512 \
--max_predictions_per_seq=76 \
--masked_lm_prob=0.15 \
--random_seed=12345 \
--dupe_factor=5
各参数含义:input_file 为原始文本([CLS]/[SEP] 分隔语料),output_file 输出 tfrecord 格式样本;do_lower_case 需与所用 checkpoint 的 cased/uncased 属性匹配;max_seq_length=512 为 BERT 标准上下文长度;max_predictions_per_seq=76 对应每序列最多 76 个 masked token;masked_lm_prob=0.15 即 BERT 论文中 15% 词元被遮蔽的比例(其中 80% 替换为 [MASK]、10% 随机词、10% 保持原样);random_seed 与 dupe_factor=5 分别控制随机种子与语料重复采样轮数。
4.2 微调数据:create_finetuning_data.py
微调数据由 official/nlp/data/create_finetuning_data.py 统一生成,产出 tf_record 格式数据集与训练元数据文件(后续训练/评估脚本通过 --input_meta_data_path 读取该元数据)。任务类型由 --fine_tuning_task_type 指定,源码中可选值包括 classification、regression、retrieval、squad、tagging。
GLUE 分类任务。先下载 GLUE 任务数据并解压到 $GLUE_DIR(GLUE benchmark 官网提供下载脚本),再准备 checkpoint 目录 $BERT_DIR(可以是 GCS 路径)。以 MNLI 为例:
export GLUE_DIR=~/glue
export BERT_DIR=gs://cloud-tpu-checkpoints/bert/keras_bert/uncased_L-24_H-1024_A-16
export TASK_NAME=MNLI
export OUTPUT_DIR=gs://some_bucket/datasets
python official/nlp/data/create_finetuning_data.py \
--input_data_dir=${GLUE_DIR}/${TASK_NAME}/ \
--vocab_file=${BERT_DIR}/vocab.txt \
--train_data_output_path=${OUTPUT_DIR}/${TASK_NAME}_train.tf_record \
--eval_data_output_path=${OUTPUT_DIR}/${TASK_NAME}_eval.tf_record \
--meta_data_file_path=${OUTPUT_DIR}/${TASK_NAME}_meta_data \
--fine_tuning_task_type=classification --max_seq_length=128 \
--classification_task_name=${TASK_NAME}
其中 --classification_task_name 限定为 GLUE 任务名(如 MNLI);--max_seq_length=128 需小于或等于 checkpoint 预训练长度 512。
SQuAD 任务。所需文件为 train/dev 的 v1.1 与 v2.0 JSON 以及对应版本官方评估脚本(SQuAD 官网与评估脚本均可从 SQuAD 官方站点获取):
export SQUAD_DIR=~/squad
export SQUAD_VERSION=v1.1
export BERT_DIR=gs://cloud-tpu-checkpoints/bert/keras_bert/uncased_L-24_H-1024_A-16
export OUTPUT_DIR=gs://some_bucket/datasets
python official/nlp/data/create_finetuning_data.py \
--squad_data_file=${SQUAD_DIR}/train-${SQUAD_VERSION}.json \
--vocab_file=${BERT_DIR}/vocab.txt \
--train_data_output_path=${OUTPUT_DIR}/squad_${SQUAD_VERSION}_train.tf_record \
--meta_data_file_path=${OUTPUT_DIR}/squad_${SQUAD_VERSION}_meta_data \
--fine_tuning_task_type=squad --max_seq_length=384
注意:生成 SQuAD 2.0 微调数据需要额外加 --version_2_with_negative=True 标志,以保留“无答案”样本的 negative 标注。
5. 句子/句对分类任务微调(MRPC 示例)
本小节以在 MRPC(仅约 3,600 条样本,多数 GPU 上数分钟即可微调)上微调 BERT-Large(uncased_L-24_H-1024_A-16)为例;GPU 显存 16GB 或更小时建议改用 BERT-Base(uncased_L-12_H-768_A-12)。
5.1 GPU 训练命令(完整)
export BERT_DIR=gs://cloud-tpu-checkpoints/bert/keras_bert/uncased_L-24_H-1024_A-16
export MODEL_DIR=gs://some_bucket/my_output_dir
export GLUE_DIR=gs://some_bucket/datasets
export TASK=MRPC
python official/legacy/bert/run_classifier.py \
--mode='train_and_eval' \
--input_meta_data_path=${GLUE_DIR}/${TASK}_meta_data \
--train_data_path=${GLUE_DIR}/${TASK}_train.tf_record \
--eval_data_path=${GLUE_DIR}/${TASK}_eval.tf_record \
--bert_config_file=${BERT_DIR}/bert_config.json \
--init_checkpoint=${BERT_DIR}/bert_model.ckpt \
--train_batch_size=4 \
--eval_batch_size=4 \
--steps_per_loop=1 \
--learning_rate=2e-5 \
--num_train_epochs=3 \
--model_dir=${MODEL_DIR} \
--distribution_strategy=mirrored
不想用本地/GCS checkpoint 时,可改用 --hub_module_url=<tfhub 模块 URL 及版本号> 替代 --init_checkpoint。
5.2 关键参数与源码默认值
结合 run_classifier.py 与 common_flags.py 的 flag 定义,上例涉及参数的完整说明如下:
| 参数 | 默认值 | 说明 |
|---|---|---|
--mode |
train_and_eval |
枚举 train_and_eval / export_only / predict:边训边评、仅从 model_dir 最新 checkpoint 导出 SavedModel、恢复 checkpoint 对测试集出预测(run_classifier.py#L37-L43) |
--bert_config_file、--input_meta_data_path、--model_dir |
无 | 三个必填 flag(run_classifier.py#L511-L513) |
--train_batch_size / --eval_batch_size |
32 / 32 | 全局 batch size,内部按 replica 切分 |
--learning_rate |
5e-5 | Adam 初始学习率(common_flags.py#L57-L58) |
--num_train_epochs |
3 | 训练轮数(common_flags.py#L49-L50) |
--steps_per_loop |
按设备自动配置 | 每个 graph-mode 循环内的训练步数;循环内不触发回调(common_flags.py#L51-L56) |
--optimizer_type |
adamw |
可选 adamw 或 lamb(common_flags.py#L61-L62) |
--distribution_strategy |
无 | 可选 mirrored / tpu 等,TPU 地址由 --tpu=grpc://<IP>:8470 指定 |
--end_lr |
0.0 | 学习率衰减终点 |
源码中还有两条值得注意的计算逻辑(run_classifier.py#L373-L382):
steps_per_epoch = train_data_size // train_batch_size,且当设置--num_eval_per_epoch=n时,实际 epoch 数放大为num_train_epochs * n,每个 epoch 每处理 N/n 个样本评估一次,用于获得更细粒度的评估分数与 checkpoint;- warmup 步数固定取总步数的 10%:
warmup_steps = epochs * train_data_size * 0.1 / train_batch_size,配合 official/nlp/optimization.py 的create_optimizer生成带线性衰减的优化器。
5.3 预测与结果输出
训练完成后,设 --mode=predict 并通过 --eval_data_path 提供测试集 tfrecord 即可获取分类概率:
python official/legacy/bert/run_classifier.py \
--mode='predict' \
--input_meta_data_path=${GLUE_DIR}/${TASK}_meta_data \
--eval_data_path=${GLUE_DIR}/${TASK}_eval.tf_record \
--bert_config_file=${BERT_DIR}/bert_config.json \
--eval_batch_size=4 \
--model_dir=${MODEL_DIR} \
--distribution_strategy=mirrored
输出写入 model_dir 下的 test_results.tsv,每行对应一个样本,列为各类别的概率。这与源码中 predict 分支的行为一致:恢复 checkpoint(默认取 tf.train.latest_checkpoint(FLAGS.model_dir),也可用 --predict_checkpoint_path 指定)后,对每条样本 softmax 得到概率并逐行 tab 分隔写出(run_classifier.py#L456-L484)。
5.4 TPU 上微调
从 GPU 切到 TPU 只需切换分发策略类型、提供 TPU 地址,并将 checkpoint 存放在远程存储(GCS):
export BERT_DIR=gs://cloud-tpu-checkpoints/bert/keras_bert/uncased_L-24_H-1024_A-16
export TPU_IP_ADDRESS='???'
export MODEL_DIR=gs://some_bucket/my_output_dir
export GLUE_DIR=gs://some_bucket/datasets
export TASK=MRPC
python official/legacy/bert/run_classifier.py \
--mode='train_and_eval' \
--input_meta_data_path=${GLUE_DIR}/${TASK}_meta_data \
--train_data_path=${GLUE_DIR}/${TASK}_train.tf_record \
--eval_data_path=${GLUE_DIR}/${TASK}_eval.tf_record \
--bert_config_file=${BERT_DIR}/bert_config.json \
--init_checkpoint=${BERT_DIR}/bert_model.ckpt \
--train_batch_size=32 \
--eval_batch_size=32 \
--steps_per_loop=1000 \
--learning_rate=2e-5 \
--num_train_epochs=3 \
--model_dir=${MODEL_DIR} \
--distribution_strategy=tpu \
--tpu=grpc://${TPU_IP_ADDRESS}:8470
TPU 版将 --steps_per_loop=1000 的原因:把大量训练步封装进 tf.function 图循环执行可显著提升 TPU 利用率(代价是回调不会在循环内部触发)。训练主循环采用 Keras compile/fit + 分发策略 API,steps_per_execution 即透传该值(run_classifier.py#L225-L229)。
6. SQuAD 1.1 微调
同样以 BERT-Large(uncased_L-24_H-1024_A-16)为例;16GB 显存以内建议 BERT-Base。
6.1 GPU 训练命令(完整)
export BERT_DIR=gs://cloud-tpu-checkpoints/bert/keras_bert/uncased_L-24_H-1024_A-16
export SQUAD_DIR=gs://some_bucket/datasets
export MODEL_DIR=gs://some_bucket/my_output_dir
export SQUAD_VERSION=v1.1
python official/legacy/bert/run_squad.py \
--input_meta_data_path=${SQUAD_DIR}/squad_${SQUAD_VERSION}_meta_data \
--train_data_path=${SQUAD_DIR}/squad_${SQUAD_VERSION}_train.tf_record \
--predict_file=${SQUAD_DIR}/dev-v1.1.json \
--vocab_file=${BERT_DIR}/vocab.txt \
--bert_config_file=${BERT_DIR}/bert_config.json \
--init_checkpoint=${BERT_DIR}/bert_model.ckpt \
--train_batch_size=4 \
--predict_batch_size=4 \
--learning_rate=8e-5 \
--num_train_epochs=2 \
--model_dir=${MODEL_DIR} \
--distribution_strategy=mirrored
与分类任务一样,可用 --hub_module_url 替代 --init_checkpoint。run_squad.py 默认会对 --predict_file 写出预测结果;若将 --mode 设为 predict 并提供 SQuAD 测试数据,则生成预测 JSON 文件。
6.2 SQuAD 专属参数
run_squad_helper.py 中的 define_common_squad_flags 定义了 SQuAD 相关参数(run_squad_helper.py#L37-L92):
| 参数 | 默认值 | 说明 |
|---|---|---|
--mode |
train_and_eval |
六种取值:train_and_eval(训练、预测到 JSON 并计算指标)、train_and_predict、train、eval、predict、export_only |
--do_lower_case |
True |
输入是否小写化;uncased 模型应为 True,cased 模型应为 False |
--predict_batch_size |
8 | 预测全局 batch |
--n_best_size |
20 | 输出到 nbest_predictions.json 的 n-best 候选数 |
--max_answer_length |
30 | 允许生成的最长答案;因 start/end 预测相互独立而必须限制 |
--null_score_diff_threshold |
0.0 | 仅 SQuAD 2.0 使用:null_score - best_non_null 大于阈值时预测空答案 |
--predict_file |
无 | 支持通配符与逗号分隔的多文件模式;eval 模式只支持单个文件 |
6.3 TPU 训练与评估
切 TPU 同样只需换分发策略:
export BERT_DIR=gs://cloud-tpu-checkpoints/bert/keras_bert/uncased_L-24_H-1024_A-16
export TPU_IP_ADDRESS='???'
export MODEL_DIR=gs://some_bucket/my_output_dir
export SQUAD_DIR=gs://some_bucket/datasets
export SQUAD_VERSION=v1.1
python official/legacy/bert/run_squad.py \
--input_meta_data_path=${SQUAD_DIR}/squad_${SQUAD_VERSION}_meta_data \
--train_data_path=${SQUAD_DIR}/squad_${SQUAD_VERSION}_train.tf_record \
--predict_file=${SQUAD_DIR}/dev-v1.1.json \
--vocab_file=${BERT_DIR}/vocab.txt \
--bert_config_file=${BERT_DIR}/bert_config.json \
--init_checkpoint=${BERT_DIR}/bert_model.ckpt \
--train_batch_size=32 \
--learning_rate=8e-5 \
--num_train_epochs=2 \
--model_dir=${MODEL_DIR} \
--distribution_strategy=tpu \
--tpu=grpc://${TPU_IP_ADDRESS}:8470
dev 集预测保存到 model_dir 下的 predictions.json,随后调用 SQuAD 1.1 官方评估脚本计算 Exact Match / F1:
python $SQUAD_DIR/evaluate-v1.1.py $SQUAD_DIR/dev-v1.1.json ./squad/predictions.json
仓库内也内置了等价的评估实现:run_squad_helper.py 顶部 from official.nlp.tools import squad_evaluate_v1_1 与 squad_evaluate_v2_0(run_squad_helper.py#L31-L33),即 --mode=train_and_eval 时评估指标直接由 official/nlp/tools/squad_evaluate_v1_1.py 在进程内计算。
7. 底层实现要点与延伸阅读
综合上述脚本,该 legacy 实现的架构可以概括为:
- 模型层:bert_models.py 提供
BertPretrainLossAndMetricLayer(masked LM + 下一句预测的加权 loss 与 accuracy 指标)以及分类/问答任务模型工厂,编码器本体复用 official/nlp/modeling 中 Keras functional 风格的 BERT 网络,支持通过hub_module_url直接以 hub 模块为编码器。 - 数据层:统一走 official/legacy/bert/input_pipeline.py 读取
create_finetuning_data.py产出的 tf_record,配合元数据文件中的max_seq_length、num_labels、label_type(int/float)自动切换分类/回归分支(num_classes==1时按回归处理,使用均方误差,见 run_classifier.py#L133-L135)。 - 训练层:基于
distribute_utils.get_distribution_strategy构建mirrored/TPU 策略,Kerascompile/fit驱动训练,tf.train.CheckpointManager+SimpleCheckpoint回调管理 checkpoint(run_classifier.py#L233-L254);混合精度(fp16)通过common_flags.dtype()与performance.set_mixed_precision_policy开启。 - 测试与验证:目录内附 run_classifier 与 SQuAD 相关测试(official/legacy/bert/bert_models_test.py、official/legacy/bert/model_training_utils_test.py),可用于验证模型构建与训练工具函数行为。
如需长期维护的新项目,建议迁移到 official/nlp 下基于 gin 实验配置的训练框架;本文命令与参数仅对该 legacy 目录内的脚本适用。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00