一、FastText
1.1、介绍
FastText 是由 Facebook AI Research(FAIR)团队开发的一个快速、高效的词向量和文本分类工具。它是基于Word2Vec的扩展和优化,支持生成词向量和执行文本分类任务,具有简单、高效、易用的特点。FastText 的实现经过优化,在大型语料上训练速度比传统方法快数倍,且内存占用更低。
1.2、子词机制
子词机制(Subword Mechanism) 是FastText的核心创新,这一机制通过将单词拆分为子词(subwords)来生成词向量。它解决了传统词向量模型(如 Word2Vec)的一些局限性,例如无法处理未登录词和对形态学信息的忽略。
在 FastText 中,每个单词会被分解成多个子词。模型不仅为整个单词创建向量,还为其子词创建向量,并将它们组合起来表示这个单词。
FastText和Word2Vec中的模型很相似,也支持CBOW和Skip-gram,我们以CBOW举例:CBOW读取句子,预测中心词;FastText读取单词,预测子词。
子词机制的优点
捕捉形态信息
英文中,FastText 可以捕捉词缀和前缀的语义。例如 "playing" 和 "played" 会共享子词 "play"。中文中,单个汉字常带有丰富的语义。例如 "质量" 和 "量化" 都包含 "量",会共享部分语义信息。
处理未登录词
即使一个单词没有出现在训练语料中,只要它的子词在语料中出现过,就可以通过子词生成词向量。 例如:生僻词 "高质感" 可以通过子词 "高", "质", "感" 得到表示。
更好的泛化能力
传统的 Word2Vec 会为每个单词生成独立的向量,而 FastText 可以通过子词共享信息,增强对罕见词的理解能力。对于存在大量新词或生僻词的任务(如社交媒体分析),FastText 的子词机制具有显著优势。
子词增加了额外的存储需求,但提升了泛化能力。
1.3、子词机制的工作原理
子词分解
在子词分解中,有两个参数minn
和 maxn
,用于定义子词的最小和最大长度。例如分解英文单词playing
,设置minn=3
,maxn=4
,则会分解为:
- 3-grams:
["pla", "lay", "ayi", "yin", "ing"]
- 4-grams:
["play", "layi", "ayin", "ying"]
如果分解的是中文单词高富帅
,设置minn=1
,maxn=2
,则会分解为:
- 1-grams:
["高", "富","帅"]
- 2-grams:
["高富","富帅"]
推荐设置:
- 中文:
minn=1
,maxn=3
- 英文:
minn=3
,maxn=6
子词向量
每个子词都有一个独立的向量,FastText 将这些子词的向量加和作为单词的表示。
假设:
- 子词 "质" 的向量是 v1
- 子词 "量" 的向量是 v2
- 子词 "高" 的向量是 v3
那么单词 "高质量" 的最终表示为:v高质量=v3+v1+v2
未登录词处理
- 如果某个单词未出现在训练语料中,FastText 会通过其子词来生成表示。
- 例如,假设单词 "高质量" 不在训练词表中,但其子词 "高","质量" 存在,FastText 会通过这些子词的向量组合生成 "高质量" 的表示。
二、案例
安装工具包
pip install jieba=0.42.1
pip install gensim==4.3.1
导入模块包
from gensim.models import FastText
import jieba # jieba分词器
import re # 正则表达式
分词
f = open("sanguo.txt", 'r',encoding='utf-8') #读入文本
lines = []
for line in f: #分别对每段分词
temp = jieba.lcut(line) #结巴分词 精确模式
words = []
for i in temp:
#过滤掉所有的标点符号
i = re.sub("[\s+\.\!\/_,$%^*(+\"\'””《》]+|[+——!,。?、~@#¥%……&*():;‘]+", "", i)
if len(i) > 0:
words.append(i)
if len(words) > 0:
lines.append(words)
print(lines[0:5])#预览前5行分词结果
模型训练
# 训练 FastText 模型
model = FastText(
sentences=lines, # 预处理后的语料
vector_size=20, # 词向量的维度
window=5, # 窗口大小
min_count=3, # 最低词频,低于此值的词将被忽略
sg=1, # Skip-gram 模型 (1 表示 Skip-gram, 0 表示 CBOW)
epochs=10, # 训练轮次
workers=4, # 并行线程数
min_n=2, # 子词的最小长度
max_n=4 # 子词的最大长度
)
检测模型效果
# 输出词向量
print("主公的词向量:\n",model.wv.get_vector("主公"))
# 输出与特定词语相关度比较高的词
print("和主公相关性最高词语:")
print(model.wv.most_similar("主公"))
print("和荆州相关性最高词语:")
print(model.wv.most_similar("荆州"))
完整代码
# 导入所需库
from gensim.models import FastText # FastText词向量模型
import jieba # 中文分词库
import re # 正则表达式库
# 读取文本文件
f = open("sanguo.txt", 'r', encoding='utf-8') # 以utf-8编码打开三国演义文本
# 初始化存储分词结果的列表
lines = []
# 逐行处理文本
for line in f: # 遍历文件的每一行
# 使用jieba进行精确模式分词
temp = jieba.lcut(line) # 返回分词后的列表
# 初始化存储过滤后词语的列表
words = []
# 过滤每个词语
for i in temp:
# 使用正则表达式去除标点符号和特殊字符
# 匹配各种中文/英文标点符号和特殊字符
i = re.sub("[\s+\.\!\/_,$%^*(+\"\'””《》]+|[+——!,。?、~@#¥%……&*():;‘]+", "", i)
# 只保留长度大于0的非空词语
if len(i) > 0:
words.append(i)
# 只添加非空的行
if len(words) > 0:
lines.append(words)
# 打印前5行分词结果(用于检查预处理效果)
print("前5行分词结果:")
print(lines[0:5])
# 训练FastText模型
model = FastText(
sentences=lines, # 预处理后的分词语料
vector_size=20, # 词向量维度(通常50-300,这里设为20演示用)
window=5, # 上下文窗口大小(考虑前后5个词)
min_count=3, # 词语最小出现次数(低于3次会被忽略)
sg=1, # 训练算法:1=skip-gram(更适合小数据), 0=CBOW
epochs=10, # 训练迭代次数
workers=4, # 并行线程数(提高训练速度)
min_n=2, # 字符级n-gram最小长度(用于处理未登录词)
max_n=4 # 字符级n-gram最大长度
)
# 获取并打印"主公"的词向量(20维)
print("\n'主公'的词向量:")
print(model.wv.get_vector("主公"))
# 查找与"主公"最相似的词语
print("\n与'主公'最相似的词语:")
print(model.wv.most_similar("主公", topn=5)) # 默认返回10个,这里显示5个
# 查找与"荆州"最相似的词语
print("\n与'荆州'最相似的词语:")
print(model.wv.most_similar("荆州", topn=5))
主公的词向量:
[ 0.1157713 -1.0269296 -0.29581293 0.2543293 0.3448897 0.24345848
-0.51907265 0.7143039 -0.22339359 0.5440311 0.7463962 -0.23664966
0.7180554 -0.02237542 0.4524613 0.54626554 0.31623515 -0.38442203
-0.49574775 -0.7358438 ]
和主公相关性最高词语:
[('成事', 0.9346866607666016), ('烦', 0.9330105185508728), ('机会', 0.9306012988090515), ('主上', 0.9269402623176575), ('今事', 0.9214110374450684), ('有何不可', 0.9199930429458618), ('切不可', 0.9187008142471313), ('蒙', 0.9183181524276733), ('良策', 0.9159600138664246), ('臣料', 0.9139387607574463)]
和荆州相关性最高词语:
[('徐州', 0.9036551117897034), ('东川', 0.9007480144500732), ('江夏', 0.8960878849029541), ('西川', 0.8957638740539551), ('取川', 0.8716447949409485), ('襄阳', 0.8640182018280029), ('川', 0.8624700903892517), ('公安', 0.8566825985908508), ('袁绍处', 0.8560354113578796), ('屯小沛', 0.8489747047424316)]