FastText 词向量全景指南(没那么全)
1. FastText 词向量是什么
• 模型:CBOW + Skip-gram 的扩展,把 字符 n-gram 也作为特征,解决未登录词(OOV)。
• 输出:
– .vec
文件:纯文本,每行一个词 + 浮点向量
– .bin
文件:二进制,含模型参数,可继续训练
• 维度:常见 100/300,官方中文 300 维
• 优势:轻量、跨语言、支持子词
2. 训练自己的词向量
2.1 安装
pip install fasttext
2.2 最小训练脚本
import fasttext
model = fasttext.train_unsupervised(input='corpus.txt', # 一行一句model='skipgram',dim=300,minCount=5,epoch=10,thread=8
)
model.save_model('my_zh.bin')
model.words # 词表
model['竹语'] # 直接取向量
2.3 关键超参数
参数 说明
minCount
过滤低频词
wordNgrams
词级 n-gram(默认 1)
minn/maxn
字符 n-gram 长度(中文用 3-6)
3. 迁移学习:用官方预训练向量
3.1 下载
wget https://dl.fbaipublicfiles.com/fasttext/vectors-zh/wiki.zh.vec
3.2 直接加载
import gensim.downloader as api
wv = api.load('fasttext-wiki-news-subwords-300') # gensim 包装
3.3 迁移到下游任务
import numpy as np
embed_matrix = np.zeros((vocab_size, 300))
for word, idx in word2idx.items():embed_matrix[idx] = wv[word] if word in wv else np.random.randn(300)
即可把预训练向量 冻结 作为 Embedding 初始化。
4. 微调 & 增量更新
4.1 继续训练
old_model = fasttext.load_model('my_zh.bin')
old_model.train_unsupervised('new_corpus.txt', epoch=5)
old_model.save_model('my_zh_finetuned.bin')
• 旧向量会随新语料 增量更新。
• 若只想追加词而不改动已训练向量,可设 lr=0.01
。
4.2 子词微调(解决新词)
old_model.get_subwords('元宇宙') # 查看子词
训练后新词即可获得向量,无需重新跑全量数据。
5. 下游任务接入
5.1 词相似度 & 类比
model.wv.most_similar('开心', topn=5)
model.wv.doesnt_match(['北京','上海','天津','苹果'])
5.2 文本分类(无需额外特征)
model.supervised = fasttext.train_supervised(input='train.txt', # 格式:__label__pos 文本pretrainedVectors='my_zh.vec', # 迁移epoch=25, lr=0.1
)
model.test('test.txt')
5.3 语义检索(Faiss 加速)
import faiss, numpy as np
vecs = np.vstack([model.get_sentence_vector(s) for s in sentences])
index = faiss.IndexFlatIP(300)
index.add(vecs)
D, I = index.search(query_vec, k=5)
6. 线上部署与压缩
方案 大小 代码
原始 300 维 .bin
2 GB —
量化 int8 500 MB fasttext quantize
降维 PCA 100 维 150 MB sklearn.decomposition.PCA
子词剪枝(减少 n-gram) 80 MB 自定义裁剪
6.1 量化示例
fasttext quantize -input corpus.txt -output tiny_zh -qnorm -retrain
部署时直接 load_model('tiny_zh.ftz')
,CPU 推理延迟 <1 ms。
7. 常见坑与排查
现象 原因 解决
OOV无向量 未加载子词 检查 minn/maxn
训练过慢 线程不足 设置 thread=cpu_count()
结果漂移 增量学习 lr 过高 降低 lr 或冻结旧词
高维稀疏 维度太高 PCA 或 用 100 维
PCA 降维与「直接用 100 维」的对比与实战
1 为什么需要降维
• 原始 300 维向量 → 2 GB 内存,线上 GPU/CPU 吃不消
• 高维稀疏 → 距离计算慢,存储膨胀
• 降维后能保留 90 % 以上语义信息,同时体积×1/3,速度×2
2 两条降维路线
┌──────────────┐ ┌──────────────┐
│ A. 重新训练 │ vs │ B. 训练后降维 │
│ dim=100 │ │ 300→100 PCA │
└──────────────┘ └──────────────┘
A 重新训练 100 维
优点:一步到位,无信息损失;子词共享权重天然适应 100 维
缺点:需重新跑语料,耗时
B 训练后 PCA
优点:无需重训,30 秒出结果;可与任何 .vec
搭配
缺点:只压缩已有向量,新词需回退到子词平均
3 PCA 三步实战(以 gensim 为例)
from sklearn.decomposition import PCA
import numpy as np
import gensim# 1) 载入 300 维向量
w2v = gensim.models.KeyedVectors.load_word2vec_format('wiki.zh.vec', binary=False)
vectors = w2v.vectors # shape=(V, 300)# 2) 拟合 PCA 并保留 95 % 方差
pca = PCA(n_components=100, svd_solver='full')
pca.fit(vectors)# 3) 降维并保存
low_dim = pca.transform(vectors) # (V, 100)
np.save('wiki_zh_100.npy', low_dim)
np.save('wiki_zh_words.npy', w2v.index_to_key)
4 线上调用
word2idx = {w: i for i, w in enumerate(np.load('wiki_zh_words.npy', allow_pickle=True))}
vectors_100 = np.load('wiki_zh_100.npy')def get_vec(word):idx = word2idx.get(word)return vectors_100[idx] if idx is not None else vectors_100.mean(axis=0)
5 何时选 PCA,何时选重训
• 语料固定 + 想最快上线 → PCA
• 语料持续新增 + 可接受重训练 → 直接 dim=100
• 极端内存限制(移动端)→ PCA 后量化到 int8 再减半
一句话总结
“想快用 PCA,想准再训练,100 维就能跑,显存立省 2/3。”