news 2026/10/8 7:44:58

从零训练自定义词向量:在 nlp-recipes 中实战 Word2Vec、GloVe 与 fastText

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零训练自定义词向量:在 nlp-recipes 中实战 Word2Vec、GloVe 与 fastText
  • NLP

【免费下载链接】nlp-recipes

Natural Language Processing Best Practices & Examples

项目地址:https://gitcode.com/gh_mirrors/nl/nlp-recipes
点击查看免费下载

导读

本文基于 nlp-recipes 仓库中 examples/embeddings 目录的官方示例,系统讲解如何用 Word2Vec、GloVe 与 fastText 三种经典方法,在自定义语料上从零训练词向量(Word Embedding)。当通用预训练模型(如基于 Wikipedia、Common Crawl 训练的词向量)无法覆盖领域专属语言或小语种场景时,这套流程可以直接落地。读完本文,你将掌握:数据预处理工具链的调用方式、gensim 训练 Word2Vec/fastText 的关键参数、GloVe 四步 C 程序流水线,以及如何保存、加载与检查训练产出的词向量。

为什么要自己训练词向量?

词向量(Word Embedding)是把词汇表(vocabulary)中的词或短语映射为实数向量的技术,学习到的向量表示能够捕获词之间的句法关系与语义关系,因此对句子相似度(sentence similarity)、文本分类(text classification)等下游任务非常有用。

社区中已有大量开箱即用的预训练模型:Word2Vec、GloVe、fastText 三种方法均发布了基于 Wikipedia、Common Crawl 等通用语料训练的公开版本。但通用模型在两类场景下并不适用:

  • 领域专属语言问题:例如医学、法律、代码等垂直领域的行话与专有表达,通用语料中的统计分布无法覆盖;
  • 无预训练模型的语言:某些小语种没有公开可用的预训练词向量。

此时,利用自己的语料从零训练词向量是标准解法。本仓库在 examples/embeddings/embedding_trainer.ipynb 中给出了完整可运行的 Jupyter Notebook 示例,覆盖数据加载、预处理、三种模型训练与结果检查的完整链路,数据集选用 STS Benchmark(英文)。

示例速览

Notebook环境说明数据集语言
Developing Word Embeddings本地演示用 Word2Vec、fastText 与 GloVe 学习词表示STS Benchmark dataseten

环境准备与路径规划

Notebook 的第一步是导入依赖并规划目录结构。训练词向量主要依赖gensim(提供 Word2Vec 与 FastText 实现)以及仓库自带的工具模块:

import gensim import sys import os # Set the environment path sys.path.append("../..") import numpy as np from utils_nlp.dataset.preprocess import ( to_lowercase, to_spacy_tokens, rm_spacy_stopwords, ) from utils_nlp.dataset import stsbenchmark from utils_nlp.common.timer import Timer from gensim.models import Word2Vec from gensim.models.fasttext import FastText

路径配置上,Notebook 定义了三个关键目录:

# Set the path for where your repo is located NLP_REPO_PATH = os.path.join('..','..') # Set the path for where your datasets are located BASE_DATA_PATH = os.path.join(NLP_REPO_PATH, "data") # Set the path for location to save embeddings SAVE_FILES_PATH = os.path.join(BASE_DATA_PATH, "trained_word_embeddings") if not os.path.exists(SAVE_FILES_PATH): os.makedirs(SAVE_FILES_PATH)
  • NLP_REPO_PATH:仓库根目录,用于定位utils_nlp包;
  • BASE_DATA_PATH:数据集存放目录,原始数据会被下载到data/raw下;
  • SAVE_FILES_PATH:所有训练产出的词向量与中间文件(语料、词汇表、共现矩阵)统一保存到data/trained_word_embeddings。

仓库中的底层支撑模块

  • utils_nlp/common/timer.py:提供Timer类,封装timeit.default_timer,通过start()/stop()或with Timer() as t两种用法计时,print(t)直接输出秒数(保留 4 位小数),用于对比三种模型的训练耗时;
  • utils_nlp/dataset/stsbenchmark.py:负责 STS Benchmark 的下载、解压与 DataFrame 化;
  • utils_nlp/dataset/preprocess.py:提供小写化、spaCy 分词、停用词过滤等预处理函数。

数据加载与预处理

加载 STS Benchmark

使用仓库封装好的stsbenchmark模块加载训练集,并清洗掉无关元数据列:

# Produce a pandas dataframe for the training set train_raw = stsbenchmark.load_pandas_df(BASE_DATA_PATH, file_split="train") # Clean the sts dataset sts_train = stsbenchmark.clean_sts(train_raw)

stsbenchmark.load_pandas_df会从官方地址下载Stsbenchmark.tar.gz并解压到BASE_DATA_PATH/raw下,然后读取sts-train.csv(通过file_split参数还可加载dev与test分片)。清洗后的 DataFrame 只保留三列:

scoresentence1sentence2
5.00A plane is taking off.An air plane is taking off.
3.80A man is playing a large flute.A man is playing a flute.

训练集清洗后规模为5749 行 × 3 列(sts_train.shape的输出),即 5749 对带相似度打分的句子。

底层实现细节:clean_sts会丢弃原始的column_0~column_3(体裁、标注来源等元数据),仅保留score、sentence1、sentence2三列,见 utils_nlp/dataset/stsbenchmark.py。

训练集预处理

对句子对执行三步标准预处理,全部来自 utils_nlp/dataset/preprocess.py:

# Convert all text to lowercase df_low = to_lowercase(sts_train) # Tokenize text sts_tokenize = to_spacy_tokens(df_low) # Tokenize with removal of stopwords sts_train_stop = rm_spacy_stopwords(sts_tokenize)
  • to_lowercase(df, column_names=[]):将所有字符串列转为小写(不传列名时作用于整个 DataFrame);
  • to_spacy_tokens(df):用 spaCy 的en_core_web_sm模型把sentence1、sentence2分词,产出sentence1_tokens、sentence2_tokens两列(每列元素为 token 列表);
  • rm_spacy_stopwords(df):在上一步 token 列基础上过滤掉停用词,产出sentence1_tokens_rm_stopwords、sentence2_tokens_rm_stopwords;该函数还支持通过custom_stopwords参数向 spaCy 模型注册自定义停用词(nlp.vocab[csw].is_stop = True),适合领域术语过滤。

随后把两列 token 拼接展平,得到模型训练所需的句子列表,并过滤掉空句:

all_sentences = sts_train_stop[["sentence1_tokens_rm_stopwords", "sentence2_tokens_rm_stopwords"]] # Flatten two columns into one list and remove all sentences that are size 0 sentences = [i for i in all_sentences.values.flatten().tolist() if len(i) > 0]

预处理后共得到11498 条句子(len(sentences)),句子长度统计为:最短 1 个 token、最长 43 个 token、中位数 6 个 token。抽样查看前 10 条,可以看到停用词已被移除:

[['plane', 'taking', '.'], ['air', 'plane', 'taking', '.'], ['man', 'playing', 'large', 'flute', '.'], ...]

方法一:Word2Vec

原理

Word2Vec 是一种**预测式(predictive)**词向量学习方法。其核心思想是:在向量空间中,语料中共享上下文(context)的词彼此靠近。它有两种经典模型架构:

  • CBOW(continuous bag-of-words):用窗口内周围的词("上下文")预测当前词;
  • Skip-gram:用当前词预测周围的上下文词。

关键参数

gensim 的Word2Vec参数很多,Notebook 中特别标注了最常用的五个:

参数含义默认值
size词向量维度100
window被预测词与当前词之间的最大距离(上下文窗口大小)5
min_count忽略出现频率低于该值的所有词5
workers训练使用的 worker 线程数3
sg训练算法,1 为 skip-gram,0 为 CBOW0

训练与计时

t = Timer() t.start() # Train the Word2vec model word2vec_model = Word2Vec(sentences, size=100, window=5, min_count=5, workers=3, sg=0) t.stop() print("Time elapsed: {}".format(t)) # Time elapsed: 0.3194

在 11498 条句子的小语料上,Word2Vec 模型训练耗时约0.32 秒(Notebook 中训练单元的运行结果)。

检查与保存

训练完成后,可以完成三件事:

# 1. 查询某个词的词向量(通过 wv 属性以词为 key 访问) print("Embedding for apple:", word2vec_model.wv["apple"]) # 2. 查看模型词汇表(wv.vocab 的 key) print("\nFirst 30 vocabulary words:", list(word2vec_model.wv.vocab)[:20]) # 3. 保存词向量:二进制格式(节省空间)或 ASCII 格式 word2vec_model.wv.save_word2vec_format(SAVE_FILES_PATH+"word2vec_model", binary=True) # binary word2vec_model.wv.save_word2vec_format(SAVE_FILES_PATH+"word2vec_model", binary=False) # ASCII

Notebook 输出显示,apple的词向量是 100 维浮点数组;词汇表前 20 个词为['plane', 'taking', '.', 'air', 'man', 'playing', 'large', 'flute', 'spreading', 'cheese', 'pizza', 'men', 'seated', 'fighting', 'smoking', 'piano', 'guitar', 'singing', 'woman', 'person']——可见训练语料主题集中在图片描述类句子。save_word2vec_format输出的文本/二进制格式可被gensim.models.KeyedVectors.load_word2vec_format重新加载。

方法二:fastText

原理

fastText 是 Facebook Research 提出的无监督词向量学习算法。它与 Word2Vec、GloVe 的本质区别在于最小单元:

  • Word2Vec 与 GloVe 把每个词当作不可再分的最小单元;
  • fastText 假设词由字符 n-gram组成。例如单词 "language" 的 2-gram 为 {la, an, ng, gu, ua, ag, ge},词的嵌入由这些字符 n-gram 的向量求和得到。

这一设计带来的直接收益是:稀有词与词表外(OOV)词仍可被拆解为字符 n-gram 而得到向量,因此在小数据集上 fastText 通常比 Word2Vec 和 GloVe 表现更好。

关键参数

gensim 的FastText参数与 Word2Vec 基本一致,另多出iter:

参数含义默认值
size词向量维度100
window上下文窗口大小5
min_count忽略频率低于该值的词5
workersworker 线程数3
sg1 为 skip-gram,0 为 CBOW0
iter训练轮数(epochs)5

训练与检查

t.start() # Train the FastText model fastText_model = FastText(size=100, window=5, min_count=5, sentences=sentences, iter=5) t.stop() print("Time elapsed: {}".format(t)) # Time elapsed: 9.3665

由于需要额外学习字符 n-gram 的表示,fastText 在同一语料上耗时显著高于 Word2Vec(本示例约9.37 秒)。由于两者同源于 gensim 包,可以使用完全相同的 API 检查与保存:

print("Embedding for apple:", fastText_model.wv["apple"]) print("\nFirst 30 vocabulary words:", list(fastText_model.wv.vocab)[:20]) fastText_model.wv.save_word2vec_format(SAVE_FILES_PATH+"fastText_model", binary=True) # binary fastText_model.wv.save_word2vec_format(SAVE_FILES_PATH+"fastText_model", binary=False) # ASCII

注意:fastText 的save_word2vec_format保存的是词向量本身;若需保留字符 n-gram 信息以支持 OOV 词查询,应使用 gensim 的完整模型保存方式(model.save())。

方法三:GloVe

原理与工程背景

GloVe(Global Vectors for Word Representation)是 Stanford NLP 团队提出的无监督词向量算法。它基于词-词共现统计(word-word co-occurrence statistics)训练,学习目标是让两个词的向量点积等于它们的共现概率,从而同时利用全局统计信息与局部上下文信息。

关键工程背景:gensim 没有实现 GloVe 模型,而其他 Python 包实现不稳定,因此本仓库直接借用了 Stanford NLP 官方 GloVe 仓库 模块(包含src/glove.c、src/vocab_count.c、src/cooccur.c、src/shuffle.c四个 C 源文件与 Makefile)。

训练前需要先编译 C 程序:

# Define path glove_model_path = os.path.join(NLP_REPO_PATH, "utils_nlp", "models", "glove") # Execute shell commands !cd $glove_model_path && make

make会根据 Makefile 依次编译出build/glove、build/shuffle、build/cooccur、build/vocab_count四个可执行文件,编译参数为-lm -pthread -Ofast -march=native -funroll-loops。编译过程中会出现fread返回值未检查的 warning,属于官方源码的已知现象,不影响使用。

Step 0:准备数据

GloVe 的 C 工具链要求语料为纯文本文件:所有词以 1 个以上空格或 Tab 分隔,每个文档/句子以换行符分隔。将前面预处理得到的句子列表写入文件:

training_corpus_file_path = os.path.join(SAVE_FILES_PATH, "training-corpus-cleaned.txt") with open(training_corpus_file_path, 'w', encoding='utf8') as file: for sent in sentences: file.write(" ".join(sent) + "\n")

Step 1:构建词汇表(vocab_count)

运行vocab_count可执行文件统计词频并构建词汇表,可选参数:

参数含义
min-count词在数据集中出现次数的下限,低于该值的词从词汇表丢弃
max-vocab保留词汇词数的上限
verbose日志级别:0、1 或 2(默认)
vocab_count_exe_path = os.path.join(glove_model_path, "build", "vocab_count") vocab_file_path = os.path.join(SAVE_FILES_PATH, "vocab.txt") !$vocab_count_exe_path -min-count 5 -verbose 2 <$training_corpus_file_path> $vocab_file_path

Notebook 的运行输出展示了实际统计结果:共处理 85334 个 token,统计到 11716 个唯一词,按min count 5截断后词汇表规模为2943个词。

Step 2:构建共现统计(cooccur)

运行cooccur统计词对共现,主要参数:

参数含义
symmetric0 只考虑左上下文,1(默认)同时考虑左右上下文
window-size使用的上下文词数量(默认 15)
verbose0、1 或 2(默认)
vocab-fileStep 1 生成的词汇表文件路径
memory内存消耗软限制(默认 4,单位 GB)
max-product通过限定两个共现词频次乘积的最大整数值,控制稠密共现数组大小
cooccur_exe_path = os.path.join(glove_model_path, "build", "cooccur") cooccurrence_file_path = os.path.join(SAVE_FILES_PATH, "cooccurrence.bin") !$cooccur_exe_path -memory 4 -vocab-file $vocab_file_path -verbose 2 -window-size 15 <$training_corpus_file_path> $cooccurrence_file_path

Notebook 输出:窗口大小 15、对称上下文、读取 2943 个词的词汇表、处理 85334 个 token、合并共现文件共 188154 行。

Step 3:打乱共现数据(shuffle)

运行shuffle打乱共现记录,参数如下:

参数含义
verbose0、1 或 2(默认)
memory内存消耗软限制(默认 4)
array-size写盘前缓冲区的最大长度(限制每次打乱的数据块大小)
shuffle_exe_path = os.path.join(glove_model_path, "build", "shuffle") cooccurrence_shuf_file_path = os.path.join(SAVE_FILES_PATH, "cooccurrence.shuf.bin") !$shuffle_exe_path -memory 4 -verbose 2 <$cooccurrence_file_path> $cooccurrence_shuf_file_path

打乱共现数据是 SGD 训练前的必要步骤,目的是避免相邻共现记录之间的相关性干扰梯度更新。

Step 4:训练 GloVe 模型(glove)

运行glove主程序训练模型,常用参数:

参数含义
verbose0、1 或 2(默认)
vector-size词向量维度(默认 50)
threads线程数(默认 8)
iter迭代次数(默认 25)
eta学习率(默认 0.05)
binary保存格式:0 为文本(默认)、1 为二进制、2 为两者都保存
x-max加权函数的截断阈值(默认 100)
vocab-fileStep 1 生成的词汇表文件
save-file向量保存文件名
input-fileStep 3 产出的共现文件
glove_exe_path = os.path.join(glove_model_path, "build", "glove") glove_vector_file_path = os.path.join(SAVE_FILES_PATH, "GloVe_vectors") !$glove_exe_path -save-file $glove_vector_file_path -threads 8 -input-file \ $cooccurrence_shuf_file_path -x-max 10 -iter 15 -vector-size 50 -binary 2 \ -vocab-file $vocab_file_path -verbose 2

Notebook 训练日志可见关键配置:向量维度 50、词汇表 2943、x_max: 10、alpha: 0.75(加权函数指数),共迭代 15 轮,cost 从第 1 轮的 0.078545 逐步下降到第 15 轮的 0.041065。整个 GloVe 流水线(Step 0~4)在示例中耗时约3.43 秒。

检查 GloVe 词向量

GloVe 输出的是纯文本向量文件(binary=2时同时生成.txt与.bin两种格式)。Notebook 手动读取文本文件构建词向量字典:

glove_wv = {} glove_vector_txt_file_path = os.path.join(SAVE_FILES_PATH, "GloVe_vectors.txt") with open(glove_vector_txt_file_path, encoding='utf-8') as f: for line in f: split_line = line.split(" ") glove_wv[split_line[0]] = [float(i) for i in split_line[1:]]

与 gensim 模型类似,可以查询apple的 50 维向量并查看词汇表前 20 个词(['.', ',', 'man', '-', '"', 'woman', "'", 'said', 'dog', 'playing', ':', 'white', 'black', '$', 'killed', 'percent', 'new', 'syria', 'people', 'china'])。注意 GloVe 词汇表与 Word2Vec/fastText 不同——它基于全量语料统计而非仅训练词向量,且默认保留标点符号等 token。

三种方法的对比与选型建议

Notebook 的 Concluding Remarks 汇总了三种方法在本示例语料上的整体耗时:Word2Vec 约 0.39 秒、GloVe 约 8.16 秒、fastText 约 10.41 秒(这里的 GloVe 与 fastText 耗时包含完整训练流程)。

方法学习方式最小单元OOV 词支持小数据集表现本示例整体耗时
Word2Vec预测式(CBOW/Skip-gram)词否一般~0.39 s
GloVe基于全局共现统计词否一般~8.16 s
fastText预测式 + 字符 n-gram字符 n-gram是通常更好~10.41 s

选型建议(来自 Notebook 的结论):fastText 通常被视为词向量的良好基线,适合作为生成词向量的起点;其对 OOV 词和稀有词的鲁棒性,使其尤其适合词汇覆盖不足的领域语料。若数据量极大且追求训练速度,Word2Vec(CBOW)的预测式训练更具效率优势。

训练后的进阶用法

训练好的词向量并非终点。Notebook 明确指出,生成自训练词向量后,可以复用仓库中 examples/sentence_similarity/baseline_deep_dive.ipynb 的句子相似度流程——将其中使用的互联网预训练词向量替换成本文训练出的向量,即可在同一套框架下评估自训练向量的质量。

此外,仓库还提供了预训练向量的加载封装,可用于与自训练向量做对比实验:

  • utils_nlp/models/pretrained_embeddings/word2vec.py:加载 GoogleNews 预训练向量;
  • utils_nlp/models/pretrained_embeddings/glove.py:加载 GloVe 预训练向量;
  • utils_nlp/models/pretrained_embeddings/fasttext.py:加载 fastText 预训练向量。

对应的冒烟测试 tests/smoke/test_word_embeddings.py 验证了三种加载函数均能把词向量载入为gensim.models.keyedvectors.Word2VecKeyedVectors或FastText对象,并检查了下载文件大小与词汇规模——这说明仓库对"预训练向量加载"与"自训练向量"两条路径都提供了统一的数据结构支撑,训练产出可以无缝接入后续下游任务。

结语

通过 examples/embeddings/embedding_trainer.ipynb 的完整示例,可以快速掌握在自定义语料上训练词向量的三种主流方案:gensim 一行 API 即可完成 Word2Vec 与 fastText 训练,而 GloVe 需要走"语料文本 → vocab_count 建词表 → cooccur 统计共现 → shuffle 打乱 → glove 训练"的四步 C 工具链。无论选择哪种方法,核心都归结为三点:预处理质量决定输入、超参数决定形态、语料规模决定上限。当通用预训练模型无法覆盖你的领域词汇或目标语言时,这套自训练流程就是最直接的替代方案。

  • NLP

【免费下载链接】nlp-recipes

Natural Language Processing Best Practices & Examples

项目地址:https://gitcode.com/gh_mirrors/nl/nlp-recipes
点击查看免费下载
上一篇:终极Anima-LLLite姿势控制指南:10个技巧实现AI人物姿态精准生成
下一篇:gorp vs 原生SQL:为什么这个ORM-ish库能让你的代码更优雅?

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 7:43:22

OpenCV DNN C++实战:灰度图上色与饱和度参数调优

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 7:43:18

学生成绩管理系统实战:Servlet+JSP+MySQL部署与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 7:43:18

从NSL-KDD到实时检测:入侵检测项目数据预处理与建模避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 7:43:12

CTF杂项解题exe工具链全攻略:从文件识别到内存取证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华