bilm-tf
bilm-tf 是基于 TensorFlow 实现的开源项目,专门用于构建和运行双向语言模型(biLM),以生成著名的 ELMo 上下文词向量。它源自论文《Deep contextualized word representations》,旨在解决传统词嵌入技术无法根据语境动态调整词义的痛点,让同一个词在不同句子中能拥有不同的向量表示,从而显著提升自然语言处理任务的理解能力。
这款工具主要面向 AI 研究人员和深度学习开发者,特别是那些需要在 TensorFlow 1.x 环境下复现经典模型、训练自定义语言模型或将 ELMo 特征集成到下游任务中的技术团队。bilm-tf 的独特之处在于其灵活的集成策略:既支持直接从原始文本实时计算向量,也允许预计算并缓存中间结果以平衡效率与显存占用,适应从大规模数据集到小型实验的各种场景。此外,它提供了完整的训练与预测流程,并兼容 Docker 部署,方便用户在 GPU 环境中快速启动。虽然目前已有更便捷的 TensorFlow Hub 版本或 PyTorch 实现供简单调用,但 bilm-tf 依然是深入理解 ELMo 底层机制及进行定制化研究的宝贵资源。
使用场景
某金融科技公司的 NLP 团队正在构建一个智能客服系统,需要精准识别用户投诉文本中多义词(如“透支”在金融与日常语境下的不同含义)的真实意图。
没有 bilm-tf 时
- 模型只能生成静态词向量,无法区分“苹果”是指水果还是科技公司,导致意图分类准确率卡在 75% 难以突破。
- 面对新出现的金融黑话或拼写变体,由于缺乏字符级编码能力,模型直接将其视为未知词(OOV)并忽略关键信息。
- 团队若想复现论文中的 ELMo 效果,需从零搭建双向语言模型架构,耗费数周时间调试 TensorFlow 底层代码。
- 在处理长句投诉时,上下文依赖捕捉能力弱,经常误判用户的情绪倾向。
使用 bilm-tf 后
- 利用 bilm-tf 预训练的双向语言模型生成动态上下文词向量,同一词汇在不同句子中获得不同表示,意图识别准确率提升至 92%。
- 通过其内置的字符级 Batcher 处理输入,即使遇到未登录的新造词或拼写错误,也能基于字符组合提取有效特征。
- 直接加载官方提供的预训练权重文件,无需重新训练底层模型,仅需一天即可完成从数据接入到下游任务集成的全流程。
- 借助深层双向 LSTM 结构,模型能精准捕捉长距离依赖关系,显著改善了对复杂投诉逻辑的理解能力。
bilm-tf 让团队以最低成本将前沿的上下文感知能力落地,彻底解决了传统静态词向量在垂直领域语义理解上的瓶颈。
运行环境要求
- Linux
- 训练必需 NVIDIA GPU(原文提及使用 3 张 GTX 1080),需安装 nvidia-docker
- 预测未强制要求但建议使用
- CUDA 版本需与 TensorFlow 1.2 兼容
未说明

快速开始
bilm-tf
用于从论文《深度上下文词表示》(arXiv:1802.05365)中计算 ELMo 表示的预训练双向语言模型的 TensorFlow 实现。
该仓库既支持训练双向语言模型,也支持使用预训练模型进行预测。
我们还在 AllenNLP 中提供了 PyTorch 版本的实现。
如果您只是想进行预测,使用 TensorFlow Hub 提供的版本可能会更加方便。
引用:
@inproceedings{Peters:2018,
author={Peters, Matthew E. and Neumann, Mark and Iyyer, Mohit and Gardner, Matt and Clark, Christopher and Lee, Kenton and Zettlemoyer, Luke},
title={Deep contextualized word representations},
booktitle={Proc. of NAACL},
year={2018}
}
安装
请安装 Python 3.5 或更高版本、TensorFlow 1.2 和 h5py:
pip install tensorflow-gpu==1.2 h5py
python setup.py install
确保在您的环境中运行测试通过:
python -m unittest discover tests/
使用 Docker 安装
要运行镜像,您必须使用 nvidia-docker,因为此仓库需要 GPU。
sudo nvidia-docker run -t allennlp/bilm-tf:training-gpu
使用预训练模型
我们提供了多种不同的英语预训练双向语言模型供使用。每个模型由两个文件组成:一个 JSON 格式的超参数“选项”文件,以及一个 HDF5 格式的模型权重文件。预训练模型的下载链接可在 这里 找到。
根据您的应用场景,有三种方式可以将 ELMo 表示集成到下游任务中:
- 使用字符输入,从原始文本实时计算表示。这是最通用的方法,能够处理任何输入文本,但计算开销也最大。
- 预先计算并缓存与上下文无关的词元表示,然后使用双向 LSTM 对输入数据计算上下文相关的表示。这种方法比方法 1 计算开销小,但仅适用于固定且已知的词汇表。
- 预先计算整个数据集的表示,并将其保存到文件中。
我们过去曾针对不同场景使用过这三种方法。方法 1 是在测试时评估未见过的数据(例如公开的 SQuAD 排行榜)所必需的;方法 2 是处理大型数据集时的一个折中方案,当方法 3 的文件过大而不可行时适用(如 SNLI、SQuAD);方法 3 则适合较小的数据集,或者当您希望在其他框架中使用 ELMo 时。
无论采用哪种方法,流程大致相同。首先,创建一个 Batcher(或对于方法 2 使用 TokenBatcher),将分词后的字符串转换为字符(或词元)ID 的 NumPy 数组。然后,加载预训练的 ELMo 模型(BidirectionalLanguageModel 类)。最后,对于方法 1 和方法 2,使用 weight_layers 计算最终的 ELMo 表示;而对于方法 3,则使用 BidirectionalLanguageModel 将所有中间层写入文件。
形状约定
每个分词后的句子是一个 str 列表,一批句子则是分词后句子的列表(List[List[str]])。
Batcher 会将这些打包成形状为 (n_sentences, max_sentence_length + 2, 50) 的字符 ID NumPy 数组,长度不足最大值的句子会在右侧用 0 填充。每句的第一个和最后一个 token 是由 Batcher 添加的特殊句首和句尾标记。
输入字符 ID 占位符的维度可以设置为 (None, None, 50),其中批次维度(轴 0)和时间维度(轴 1)会根据每次批处理动态确定,上限由 BidirectionalLanguageModel 构造函数中指定的最大批次大小决定。
对批次运行推理后,返回的 biLM 嵌入是一个形状为 (n_sentences, 3, max_sentence_length, 1024) 的 NumPy 数组,移除了特殊的句首/句尾标记。
词汇表文件
为了提高效率,Batcher 需要一个词汇表文件作为输入。这是一个文本文件,每行一个 token,以换行符 (\n) 分隔。词汇表中的每个 token 只会被缓存一次,对应的 50 维字符 ID 序列也会被存储。由于模型完全基于字符,不在词汇表中的 token 在运行时仍能被正确处理,只是会略微降低运行速度。建议始终在词汇表中包含区分大小写的特殊标记 <S> 和 </S>。
使用字符输入的 ELMo
详细用法示例请参见 usage_character.py。
使用预先计算并缓存的与上下文无关的词元表示
为了加快具有固定词汇表的模型推理速度,可以预先计算与上下文无关的词元表示,将其保存到文件中,并在推理时重复使用。请注意,我们不支持对未登录词汇回退到字符输入,因此此方法仅适用于双向语言模型用于计算具有固定且明确词汇表的输入嵌入的情况。
使用此方法的步骤如下:
- 首先,创建一个包含数据集中所有唯一 token 的词汇表,并添加特殊的
<S>和</S>标记。 - 使用完整模型运行
dump_token_embeddings,将词元嵌入写入 HDF5 文件。 - 使用
TokenBatcher(而非Batcher)配合您的词汇表文件,在BidirectionalLanguageModel构造函数中设置use_token_inputs=False,并传入步骤 2 中生成的输出文件名。
详细用法示例请参见 usage_token.py。
将整个数据集的 biLM 嵌入转储到单个文件中
要采用此方法,请创建一个包含分词后数据集的文本文件,每行是一个分词后的句子(以空格分隔)。然后使用 dump_bilm_embeddings。
输出文件为 HDF5 格式。输入数据中的每句话都会以数据集的形式存储,键为 str(sentence_id),其中 sentence_id 是数据集文件中的行号(从 0 开始计数)。每句话的嵌入是一个形状为 (3, n_tokens, 1024) 的数组。
详细示例请参见 usage_cached.py。
在新语料上训练双向语言模型
广义上讲,训练和使用一个新的双向语言模型的流程如下:
- 准备输入数据和词汇表文件。
- 训练双向语言模型。
- 在保留数据上测试(计算困惑度)该双向语言模型。
- 将训练好的双向语言模型的权重导出到一个 HDF5 文件中。
- 参阅上述说明,了解如何在下游模型中使用第 4 步的输出。
1. 准备输入数据和词汇表文件。
要训练和评估一个双向语言模型,你需要提供:
- 词汇表文件
- 一组训练文件
- 一组保留文件
词汇表文件是一个文本文件,每行包含一个词元。该文件还必须包含特殊词元 <S>、</S> 和 <UNK>(区分大小写)。
重要提示:词汇表文件应按照训练数据中各词元的出现频率降序排列。前三行应分别为特殊词元(<S>、</S> 和 <UNK>),然后是训练数据中最常见的词元,最后是出现频率最低的词元。
注意:用于训练的词汇表文件可能与用于预测的词汇表文件不同。
训练数据应随机分割成多个训练文件,每个文件包含数据的一个子集。每个文件包含预分词且以空格分隔的文本,每行一个句子。请勿在训练数据中包含 <S> 或 </S> 词元。
所有的分词和归一化处理都应在模型训练之前完成,因此词汇表文件和训练文件都应包含归一化的词元。由于默认设置采用完全基于字符的词元表示方式,通常我们不建议进行除分词之外的其他归一化处理。
最后,从训练数据中保留一小部分作为保留数据,用于评估训练好的双向语言模型。
2. 训练双向语言模型。
用于训练 ELMo 模型的超参数可以在 bin/train_elmo.py 中找到。
ELMo 模型是在 3 个 GPU 上训练的。要使用相同的超参数训练一个新的模型,首先从 10 亿词基准 下载训练数据。然后下载 词汇表文件。最后运行以下命令:
export CUDA_VISIBLE_DEVICES=0,1,2
python bin/train_elmo.py \
--train_prefix='/path/to/1-billion-word-language-modeling-benchmark-r13output/training-monolingual.tokenized.shuffled/*' \
--vocab_file /path/to/vocab-2016-09-10.txt \
--save_dir /output_path/to/checkpoint
3. 评估训练好的模型。
使用 bin/run_test.py 来评估训练好的模型,例如:
export CUDA_VISIBLE_DEVICES=0
python bin/run_test.py \
--test_prefix='/path/to/1-billion-word-language-modeling-benchmark-r13output/heldout-monolingual.tokenized.shuffled/news.en.heldout-000*' \
--vocab_file /path/to/vocab-2016-09-10.txt \
--save_dir /output_path/to/checkpoint
4. 将 TensorFlow 检查点转换为 HDF5 格式,以便使用 bilm 或 allennlp 进行预测。
首先,为新训练的模型创建一个 options.json 文件。为此,请参考现有文件中的模板(例如原始的 options.json),并根据你的超参数进行修改。
重要提示:训练完成后,务必始终将 n_characters 设置为 262(见下文)。
然后运行以下命令:
python bin/dump_weights.py \
--save_dir /output_path/to/checkpoint
--outfile /output_path/to/weights.hdf5
常见问题及其他注意事项
能否提供训练时的 TensorFlow 检查点文件?
TensorFlow 检查点可通过下载以下文件获得:
如何在额外的无标签数据上对模型进行微调?
首先下载上述检查点文件。然后按照“在新语料库上训练 biLM”一节中的说明准备数据集,不同之处在于我们将使用现有的词汇表文件,而不是创建新的词汇表。最后,使用脚本 bin/restart.py 在新数据集上从现有检查点继续训练。
对于小型数据集(例如少于 1000 万词),我们建议仅进行少量轮次的微调,并监控保留集上的困惑度;否则模型可能会过拟合小数据集。
softmax 权重是否可用?
它们包含在上述训练检查点中。
能否提供更多关于模型训练方式的细节?
脚本 bin/train_elmo.py 中包含了用于训练模型的超参数。
原始模型是在 3 张 GTX 1080 显卡上训练了 10 个 epoch,耗时约两周。
在输入处理方面,我们使用了原始的 10 亿词基准数据集
这里,以及包含 <S>、</S> 和 <UNK> 的 793,471 个词的现有词汇表。我们的词汇表文件可在 这里 找到。
在模型输入阶段,所有文本都采用完整的字符级表示,包括不在词汇表中的词。对于 softmax 输出,我们将 OOV 词替换为 <UNK>。
模型以固定大小的 20 个词窗口进行训练。批次是通过在句子前后添加 <S> 和 </S> 来构建的,然后将来自一个或多个句子的词打包到每一行中,以完全填满每个批次。部分句子和 LSTM 状态会从一个批次传递到下一个批次,使语言模型能够跨批次利用信息来获取上下文,但反向传播会在每个批次边界处中断。
为什么对同一段文本两次运行预训练模型会得到略有不同的嵌入结果?
由于训练方法的原因(见上文),LSTM 是有状态的,其状态会从一个批次传递到下一个批次。因此,这会引入少量的非确定性,尤其是在前两个批次中。
为什么即使使用我的小数据集,训练似乎也需要很长时间?
训练过程中梯度更新的次数由以下因素决定:
- 训练数据中的词数 (
n_train_tokens) - 批次大小 (
batch_size) - 训练轮次 (
n_epochs)
请务必在 bin/train_elmo.py 中根据您的具体数据集设置这些值。
n_characters 和填充有什么关系?
在训练过程中,我们通过在每个句子前后添加 <S> 和 </S>,并将来自一个或多个句子的词打包到每一行中,以确保每个批次恰好包含 20 个词。因此,我们没有为特殊的填充标记预留空间。将词字符串转换为字符 ID 列表的 UnicodeCharsVocabulary 总是使用固定数量的字符嵌入,即 n_characters=261,所以在训练时应始终设置 n_characters=261。
然而,在预测时,我们会确保每个句子完全包含在一个批次中,因此会对不同长度的句子使用特殊的填充 ID 进行填充。这一过程发生在 Batcher 类中 参见此处。因此,在预测时,应在 options.json 中将 n_characters 设置为 262。
如何使用 ELMo 计算句子表示?
简单的方法,如对句子中单词级别的 ELMo 表示取平均值或最大池化,效果很好,通常在基准数据集上优于监督方法。详情请参阅 Perone 等人于 2018 年发表的论文《下游任务和语言学探针任务中句子嵌入的评估》arXiv 链接。
序列化模型时出现警告,这是问题吗?
可以安全地忽略以下警告:
2018-08-24 13:04:08,779 : WARNING : 序列化 lstm_output_embeddings 时遇到错误。
类型不受支持,或者项目类型与 CollectionDef 中的字段类型不匹配。
'list' 对象没有 'name' 属性
常见问题
相似工具推荐
openclaw
OpenClaw 是一款专为个人打造的本地化 AI 助手,旨在让你在自己的设备上拥有完全可控的智能伙伴。它打破了传统 AI 助手局限于特定网页或应用的束缚,能够直接接入你日常使用的各类通讯渠道,包括微信、WhatsApp、Telegram、Discord、iMessage 等数十种平台。无论你在哪个聊天软件中发送消息,OpenClaw 都能即时响应,甚至支持在 macOS、iOS 和 Android 设备上进行语音交互,并提供实时的画布渲染功能供你操控。 这款工具主要解决了用户对数据隐私、响应速度以及“始终在线”体验的需求。通过将 AI 部署在本地,用户无需依赖云端服务即可享受快速、私密的智能辅助,真正实现了“你的数据,你做主”。其独特的技术亮点在于强大的网关架构,将控制平面与核心助手分离,确保跨平台通信的流畅性与扩展性。 OpenClaw 非常适合希望构建个性化工作流的技术爱好者、开发者,以及注重隐私保护且不愿被单一生态绑定的普通用户。只要具备基础的终端操作能力(支持 macOS、Linux 及 Windows WSL2),即可通过简单的命令行引导完成部署。如果你渴望拥有一个懂你
stable-diffusion-webui
stable-diffusion-webui 是一个基于 Gradio 构建的网页版操作界面,旨在让用户能够轻松地在本地运行和使用强大的 Stable Diffusion 图像生成模型。它解决了原始模型依赖命令行、操作门槛高且功能分散的痛点,将复杂的 AI 绘图流程整合进一个直观易用的图形化平台。 无论是希望快速上手的普通创作者、需要精细控制画面细节的设计师,还是想要深入探索模型潜力的开发者与研究人员,都能从中获益。其核心亮点在于极高的功能丰富度:不仅支持文生图、图生图、局部重绘(Inpainting)和外绘(Outpainting)等基础模式,还独创了注意力机制调整、提示词矩阵、负向提示词以及“高清修复”等高级功能。此外,它内置了 GFPGAN 和 CodeFormer 等人脸修复工具,支持多种神经网络放大算法,并允许用户通过插件系统无限扩展能力。即使是显存有限的设备,stable-diffusion-webui 也提供了相应的优化选项,让高质量的 AI 艺术创作变得触手可及。
everything-claude-code
everything-claude-code 是一套专为 AI 编程助手(如 Claude Code、Codex、Cursor 等)打造的高性能优化系统。它不仅仅是一组配置文件,而是一个经过长期实战打磨的完整框架,旨在解决 AI 代理在实际开发中面临的效率低下、记忆丢失、安全隐患及缺乏持续学习能力等核心痛点。 通过引入技能模块化、直觉增强、记忆持久化机制以及内置的安全扫描功能,everything-claude-code 能显著提升 AI 在复杂任务中的表现,帮助开发者构建更稳定、更智能的生产级 AI 代理。其独特的“研究优先”开发理念和针对 Token 消耗的优化策略,使得模型响应更快、成本更低,同时有效防御潜在的攻击向量。 这套工具特别适合软件开发者、AI 研究人员以及希望深度定制 AI 工作流的技术团队使用。无论您是在构建大型代码库,还是需要 AI 协助进行安全审计与自动化测试,everything-claude-code 都能提供强大的底层支持。作为一个曾荣获 Anthropic 黑客大奖的开源项目,它融合了多语言支持与丰富的实战钩子(hooks),让 AI 真正成长为懂上
ComfyUI
ComfyUI 是一款功能强大且高度模块化的视觉 AI 引擎,专为设计和执行复杂的 Stable Diffusion 图像生成流程而打造。它摒弃了传统的代码编写模式,采用直观的节点式流程图界面,让用户通过连接不同的功能模块即可构建个性化的生成管线。 这一设计巧妙解决了高级 AI 绘图工作流配置复杂、灵活性不足的痛点。用户无需具备编程背景,也能自由组合模型、调整参数并实时预览效果,轻松实现从基础文生图到多步骤高清修复等各类复杂任务。ComfyUI 拥有极佳的兼容性,不仅支持 Windows、macOS 和 Linux 全平台,还广泛适配 NVIDIA、AMD、Intel 及苹果 Silicon 等多种硬件架构,并率先支持 SDXL、Flux、SD3 等前沿模型。 无论是希望深入探索算法潜力的研究人员和开发者,还是追求极致创作自由度的设计师与资深 AI 绘画爱好者,ComfyUI 都能提供强大的支持。其独特的模块化架构允许社区不断扩展新功能,使其成为当前最灵活、生态最丰富的开源扩散模型工具之一,帮助用户将创意高效转化为现实。
LLMs-from-scratch
LLMs-from-scratch 是一个基于 PyTorch 的开源教育项目,旨在引导用户从零开始一步步构建一个类似 ChatGPT 的大型语言模型(LLM)。它不仅是同名技术著作的官方代码库,更提供了一套完整的实践方案,涵盖模型开发、预训练及微调的全过程。 该项目主要解决了大模型领域“黑盒化”的学习痛点。许多开发者虽能调用现成模型,却难以深入理解其内部架构与训练机制。通过亲手编写每一行核心代码,用户能够透彻掌握 Transformer 架构、注意力机制等关键原理,从而真正理解大模型是如何“思考”的。此外,项目还包含了加载大型预训练权重进行微调的代码,帮助用户将理论知识延伸至实际应用。 LLMs-from-scratch 特别适合希望深入底层原理的 AI 开发者、研究人员以及计算机专业的学生。对于不满足于仅使用 API,而是渴望探究模型构建细节的技术人员而言,这是极佳的学习资源。其独特的技术亮点在于“循序渐进”的教学设计:将复杂的系统工程拆解为清晰的步骤,配合详细的图表与示例,让构建一个虽小但功能完备的大模型变得触手可及。无论你是想夯实理论基础,还是为未来研发更大规模的模型做准备
Deep-Live-Cam
Deep-Live-Cam 是一款专注于实时换脸与视频生成的开源工具,用户仅需一张静态照片,即可通过“一键操作”实现摄像头画面的即时变脸或制作深度伪造视频。它有效解决了传统换脸技术流程繁琐、对硬件配置要求极高以及难以实时预览的痛点,让高质量的数字内容创作变得触手可及。 这款工具不仅适合开发者和技术研究人员探索算法边界,更因其极简的操作逻辑(仅需三步:选脸、选摄像头、启动),广泛适用于普通用户、内容创作者、设计师及直播主播。无论是为了动画角色定制、服装展示模特替换,还是制作趣味短视频和直播互动,Deep-Live-Cam 都能提供流畅的支持。 其核心技术亮点在于强大的实时处理能力,支持口型遮罩(Mouth Mask)以保留使用者原始的嘴部动作,确保表情自然精准;同时具备“人脸映射”功能,可同时对画面中的多个主体应用不同面孔。此外,项目内置了严格的内容安全过滤机制,自动拦截涉及裸露、暴力等不当素材,并倡导用户在获得授权及明确标注的前提下合规使用,体现了技术发展与伦理责任的平衡。