FastText 核心创新在于将子词(subword)信息引入神经网络语言模型,解决了传统 Word2Vec 无法处理未知词(OOV)的问题,同时显著提升了低频词的表示质量。
算法:分层 Softmax + N-gram 子词嵌入
子词(Subword)基本思想
每个单词被表示为字符级 n-gram 的集合,而非单一向量。例如单词 "apple",设定 n=3 时:
<ap, app, ppl, ple, le>
其中 <和 >为边界符号,用于区分前缀/后缀与完整词。
工程实现:
构建类别字典:
# create id to class dictionary id_to_class = {} # use enumerate(f:file) to extract all categories with open(config.class_datapath,'r',encoding='utf-8') as f: for index,class_name in enumerate(f): id_to_class[index] = class_name检查数据EDA:
counter = Counter(train_data["label"]) print(counter) for label, count in counter.items(): print(f"category:{label},amount:{count}") # get the length of text train_data["text_length"] = train_data["text"].str.len() print(train_data[["text", "text_length"]].head(5)) # optimal text length # 3σ principle : length = μ + 3σ μ = train_data['text_length'].mean() σ = train_data['text_length'].std() print("EDA check:") print(f"μ:{train_data['text_length'].mean():.2f}") print(f"σ:{train_data['text_length'].std():.2f}") print(f"max length of all texts:{train_data['text_length'].max():.2f}") print(f"min length of all texts:{train_data['text_length'].min():.2f}")对于中文版本的FastText来说,EDA是必须要做的,经过我们的EDA检查,推荐句子长度是32。
数据处理:
原始数据是text+空格+数字类别形式,而FastText需要接收的是__label__类别名+空格+文本的形式
# data pre-process and save data # read data and process our data into FastText format # char example : __label__education 华 中 科 技 大 学 2 0 1 0 年 考 研 成 绩 查 询 开 通 # word example : __label__education 南开大学 实行 学业 警示 学分 不够 将 “ 吃 黄牌 ” with open (config.train_datapath,'r',encoding='utf8') as f: datas = [] # a list for terminal data storage ★★★★ terminal data -> str for line in f: # split data -> text label # lines of raw datas are separated by '/t' text,label = line.strip().split('\t') # process text -> words:list # ★★★★★ use char segmentation words = list(text) if config.use_char_segmentation else jieba.lcut(text) text_process = " ".join(word for word in words if word.strip()) # process label -> __label__ id_to_class label_process = f"__label__{id_to_class[int(label)]}" # concat two parts of data text_label = label_process.strip() + " " + text_process datas.append(text_label)这个接口提供两个方案,如果需要以字符形式那就直接转list,如果需要分词形式则用jieba分词进行分词处理。处理好后,保存文件。
with open(output_file, 'w', encoding='utf-8') as f: for line in datas: f.write(line + '\n') # save processed datafasttext.train_supervised() 自动训练函数内置自动构建词表,我们可以直接调用api进行训练。
n-gram推荐设置为2.
# auto train model = fasttext.train_supervised(input="上一步预处理后保存的数据文件路径", autotuneValidationFile = "验证集路径", autotuneDuration = 60, maxn=32 verbose = 3) # save model model.save_model("你想保存的文件路径和格式")API做了什么?
扫描整个训练语料,按空格切分 token
区分词与标签——以 __label__为前缀的记为 label,其余记为 word
统计词频,根据 minCount(默认 1,低于此频次的词被丢弃)过滤低频词
分配词 ID,同时为每个词生成字符级 n-gram(minn~maxn),n-gram 经哈希映射到 bucket大小的哈希桶中
按照默认参数值或者传入的超参数进行训练并验证,然后自动调整每轮的参数。
训练好并保存模型,接下来我们就可以进行样本预测了:
def predict(datas): # fetch user input # load model -> fasttext.load_model() fasttext_model = fasttext.load_model("你的模型保存路径和模型文件名") ## data process -> list(text) text = " ".join(list(datas['text'])) # model predict pred = fasttext_model.predict(text) # fetch result res = pred[0][0][9:] # return result datas['result'] = res return datasif __name__ == '__main__': data = {"text":"清华大学化学系专业硕士点于2027年停止招生"} start = time.time() result = predict(data) print("time consume(s):",time.time()-start) print("result:",result)