fasttext简介* 是自然语言处理NLP任务的开源工具包由Facebook AI ResearchFAIR开发* 它被广泛用于各种任务包括情感分析、文本分类、命名实体识别、机器翻译等* fasttext主要作用* 进行文本分类* 训练词向量* fasttext工具包优势正如其名在保持较高精度的情况下快速的进行训练和预测是fasttext的最大优势* fasttext优势原因* fasttext工具包中包含的fasttext模型模型结构简单* 使用fasttext模型训练词向量时使用层次softmax结构来提升超多类别下的模型性能* 采用负采样negative sampling每次训练仅仅更新一小部分的权重降低梯度下降过程中的计算量* fasttext模型过于简单无法捕捉词序特征可采用n-gram特征提取文本特征以弥补模型缺陷提升精度fasttext模型架构 - 三层架构* 三层架构* FastText 模型架构和 Word2Vec 中的 CBOW 模型很类似。不同之处在于FastText 预测标签而 CBOW 模型预测中间词* 输入层是对文档embedding之后的向量包含N-gram特征* 隐藏层是对输入数据的求和平均* 输出层是文档对应的label* 层次softmax(hierarchical softmax)哈夫曼树简介1. 普通 Softmax 为什么会慢如果一共有 NN 个分类标签普通 Softmax 在计算输出概率时需要对所有 NN 个类别计算指数并归一化。也就是每预测一次都要计算 NN 次。如果 NN 是几十万甚至上百万比如预测下一个词计算量会极其庞大训练速度会非常慢。2. FastText 是怎么解决并提速的结合你之前发的课件FastText 用了两种“加速包”层次 Softmax (Hierarchical Softmax)原理它把所有的类别标签构建成一棵哈夫曼树Huffman Tree。计算概率时不再是一次性计算所有类别而是从树的根节点走到叶子节点。提速效果每次预测只需要计算树深度次也就是走一条路径上的二分类计算复杂度从 O(N)O(N) 降到了 O(logN)O(logN)。额外优势哈夫曼树让高频词拥有更短的编码路径进一步加快高频词的训练速度。负采样 (Negative Sampling)原理每次训练时不更新所有类别的权重而是只针对“正确的目标类别”和随机抽取的几个“错误的类别负样本”进行权重更新。提速效果极大地减少了梯度下降过程中的计算量每次只更新一小部分权重。为什么需要负采样1. 先聊“传统训练的问题”(即为啥需要负采样)训练神经网络时例如Skip-Gram会遇到1个大麻烦计算量太大训不动。例如假设词汇表10000个词即所有的英文单词每次训练模型要算这10000个词的概率用softmax还要更新所有10000个词的权重。类似于“每次考试都需要复习整本书的所有知识点。”2. 负采样的核心思想偷懒式训练不更新所有权重只更新一小部分从而大幅减少计算量。类比传统方法 复习整本书 即更新所有权重。负采样 只复习考点 少量易错点更新一小部分权重3. 负采样的具体流程拿hello 和 man的词向量举例输入词hello - 经过one-hot编码输出词man - 正样本期望模型预测它的概率(较)高词汇表大小10000 - 输出层有10000个神经元。负采样的关键只选少量负样本(输出0的词)来更新不管9999个词。例如选了5个0(负样本)则只更新他们的权重剩下的9994个暂时不更新权重。3. 还有一个重要原因模型结构极其简单除了上面两种 Softmax 的替代方案FastText 之所以快还因为它的隐藏层非常“单薄”它的隐藏层没有非线性激活函数比如 ReLU、Sigmoid也没有复杂的网络结构。它只是把输入层的 N-gram 词向量进行求和平均直接连到输出层。这种简单的线性结构使得矩阵运算极快非常适合大规模文本分类。文本分类概念文本分类的是将文档例如电子邮件帖子文本消息产品评论等分配给一个或多个类别训练文本分类模型需要是有监督学习需要标签文本分类种类二分类文本被分类两个类别二分类交叉熵损失函数 (BCELoss)比如判断一句评论是好评还是差评Softmax() CrossEntropyLoss来解决注图中带有此红色批注单标签多分类文本可被分成多个类别中的一个类别即被打上某一个标签比如输入一个人名判断它是来自哪个国家的人名多标签多分类文本可被分成多个类别中的一多类别即被打上多个个标签比如输入一段讨论话题可以美食、体育新闻、游戏释义二选一要么是A,要么是B单选题要么是A要么是B要么是C或者其他多选题是A 也可以是B,也可以是CFastText实现 文本分类案例.案例:演示FastText实现 文本分类案例.文本分类的几种场景:二分类问题:单标签多分类:多标签多分类:一般会拆解成 多个 二分类问题解决.例如: 数据格式如下, 如何处理呢?标签1 标签2 标签3 特征数据__label__flavor __label__spices __label__chemistry Flavor and Chemical Composition of Thyme思路:转成 多个 二分类问题即可, 例如:Flavor and Chemical Composition of Thyme __label__flavorFlavor and Chemical Composition of Thyme __label__spicesFlavor and Chemical Composition of Thyme __label__chemistry细节:1. FastText确实训练和预测较快, 但是精度相对不是特别高, 我们可以优化下.2. 优化思路为(参考):1. 对数据做预处理, 统一小写, 单词和符号分离...2. 修改训练的轮数.3. 调整学习率.4. 增加N-Gram5. 调整损失函数6. 自动调参.7. 生产环境中, 可能会涉及 多标签多分类的问题, 一般会拆解成 多个二分类来解决.8. 保存和加载模型.需要模型调优* 模型调优采用一下方法# 导包import fasttext# todo 1.直接训练.def dm01_basic():# 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型.model fasttext.train_supervised(input./data/cooking_train.txt)# 2. 使用模型进行预测.# 预测第1条样本.result1 model.predict(Flavor and Chemical Composition of Thyme)print(fresult1预测结果: {result1})# 预测第2条样本result2 model.predict(How to scale up modernist Potato Puree?)print(fresult2预测结果: {result2})# 3. 测试模型.result3 model.test(./data/cooking_valid.txt)print(f模型在测试集上的测试结果: {result3}) # (验证集样本数量, 精度(准确率), 召回率)# todo 2.对数据做预处理(统一小写, 符号和单词分离...).1 数据方面重新处理数据def dm02_data_preprocess():# 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型.model fasttext.train_supervised(input./data/cooking.pre.train)# 2. 使用模型进行预测.# 预测第1条样本.result1 model.predict(Flavor and Chemical Composition of Thyme)print(fresult1预测结果: {result1})# 预测第2条样本result2 model.predict(How to scale up modernist Potato Puree?)print(fresult2预测结果: {result2})# 3. 测试模型.result3 model.test(./data/cooking.pre.valid)print(f模型在测试集上的测试结果: {result3}) # (验证集样本数量, 精度(准确率), 召回率)# todo 3.上步基础上, 增加: 训练轮数(默认5轮).2 增加训练轮数def dm03_epoch():# 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型.model fasttext.train_supervised(input./data/cooking.pre.train, epoch30)# 2. 使用模型进行预测.# 预测第1条样本.result1 model.predict(Flavor and Chemical Composition of Thyme)print(fresult1预测结果: {result1})# 预测第2条样本result2 model.predict(How to scale up modernist Potato Puree?)print(fresult2预测结果: {result2})# 3. 测试模型.result3 model.test(./data/cooking.pre.valid)print(f模型在测试集上的测试结果: {result3}) # (验证集样本数量, 精度(准确率), 召回率)# todo 4.上步基础上, 增加: 学习率3 调整学习率def dm04_learning_rate():# 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型.model fasttext.train_supervised(input./data/cooking.pre.train, epoch30, lr1)# 2. 使用模型进行预测.# 预测第1条样本.result1 model.predict(Flavor and Chemical Composition of Thyme)print(fresult1预测结果: {result1})# 预测第2条样本result2 model.predict(How to scale up modernist Potato Puree?)print(fresult2预测结果: {result2})# 3. 测试模型.result3 model.test(./data/cooking.pre.valid)print(f模型在测试集上的测试结果: {result3}) # (验证集样本数量, 精度(准确率), 召回率)# todo 5.上步基础上, 增加: n-gram4 增加n-gram特征def dm05_ngram():# 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型.model fasttext.train_supervised(input./data/cooking.pre.train, epoch30, lr1, wordNgrams2)# 2. 使用模型进行预测.# 预测第1条样本.result1 model.predict(Flavor and Chemical Composition of Thyme)print(fresult1预测结果: {result1})# 预测第2条样本result2 model.predict(How to scale up modernist Potato Puree?)print(fresult2预测结果: {result2})# 3. 测试模型.result3 model.test(./data/cooking.pre.valid)print(f模型在测试集上的测试结果: {result3}) # (验证集样本数量, 精度(准确率), 召回率)# todo 6.上步基础上, 修改损失函数, 从传统的softmax() - hs(层次softmax): 负采样 softmax 哈夫曼树5 修改损失计算方式def dm06_loss():# 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型.model fasttext.train_supervised(input./data/cooking.pre.train, epoch30, lr1, wordNgrams2, losshs)# 2. 使用模型进行预测.# 预测第1条样本.result1 model.predict(Flavor and Chemical Composition of Thyme)print(fresult1预测结果: {result1})# 预测第2条样本result2 model.predict(How to scale up modernist Potato Puree?)print(fresult2预测结果: {result2})# 3. 测试模型.result3 model.test(./data/cooking.pre.valid)print(f模型在测试集上的测试结果: {result3}) # (验证集样本数量, 精度(准确率), 召回率)# todo 7.上步基础上, 从手动调参改为自动调参.6 自动超参数调优def dm07_auto():# 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型.model fasttext.train_supervised(input./data/cooking.pre.train, # 训练集autotuneValidationFile ./data/cooking.pre.valid, # 验证集autotuneDuration120 # 自动调参的时间限制, 默认: 5分钟, 这里改成了2分钟.)# 2. 使用模型进行预测.# 预测第1条样本.result1 model.predict(Flavor and Chemical Composition of Thyme)print(fresult1预测结果: {result1})# 预测第2条样本result2 model.predict(How to scale up modernist Potato Puree?)print(fresult2预测结果: {result2})# 3. 测试模型.result3 model.test(./data/cooking.pre.valid)print(f模型在测试集上的测试结果: {result3}) # (验证集样本数量, 精度(准确率), 召回率)# todo 8.在生产环境中, 可能会涉及: 多分类多标签的问题, 解决方案如下7 实际生产中多标签多分类问题的损失计算方式def dm08_multi_label():# 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型.model fasttext.train_supervised(input./data/cooking.pre.train, # 训练集epoch25, # 训练轮数lr0.2, # 学习率wordNgrams2, # n-gramlossova # one vs all)# 2. 使用模型进行预测.# 预测第1条样本.# 参1: 预测的第1条样本, 参2: k表示预测的标签数量, 参3:阈值(只有大于该阈值的标签才会被保留)result1 model.predict(Flavor and Chemical Composition of Thyme, k3, threshold0.5)print(fresult1预测结果: {result1})# 预测第2条样本# 这里的-1含义是: 预测所有标签(尽可能多的显示)result2 model.predict(How to scale up modernist Potato Puree?, k-1)print(fresult2预测结果: {result2})# 3. 测试模型.result3 model.test(./data/cooking.pre.valid)print(f模型在测试集上的测试结果: {result3}) # ((__label__baking,), array([0.56148356]))# todo 9. 模型保存def dm09_save():# 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型.model fasttext.train_supervised(input./data/cooking.pre.train, epoch30, lr1, wordNgrams2)# 2. 保存模型.model.save_model(./model/fasttext_model.model)# 3.模型加载.model2 fasttext.load_model(./model/fasttext_model.model)# 4.使用模型进行预测.result1 model2.predict(substitutes for wheat flour and their challenges in baking, k3, threshold0.5)print(fresult1结果为: {result1})# todo 10. 测试代码.if __name__ __main__:# 1. 测试: 直接训练.# dm01_basic() # (3000, 0.12666666666666668, 0.05477872279083177)# 2. 测试: 对数据做预处理.# dm02_data_preprocess() # (3000, 0.172, 0.07438373936860314)# 3. 测试: 增加: 训练轮数.# dm03_epoch() # (3000, 0.5446666666666666, 0.2355485080005766)# 4. 测试: 添加: 学习率.# dm04_learning_rate() # (3000, 0.597, 0.25818076978520976)# 5. 测试: 添加: n-gram.# dm05_ngram() # (3000, 0.6186666666666667, 0.2675508144731152)# 6. 测试: 修改: 损失函数.# dm06_loss() # (3000, 0.603, 0.26077555139109126)# 7. 测试: 自动调参.# dm07_auto() # (3000, 0.5446666666666666, 0.2355485080005766)# 8. 测试: 多分类多标签的问题.# dm08_multi_label() # (3000, 0.6026666666666667, 0.26063139685743114)# 9. 模型保存.dm09_save()完整代码 案例: 演示FastText实现 文本分类案例. 文本分类的几种场景: 二分类问题: 单标签多分类: 多标签多分类: 一般会拆解成 多个 二分类问题解决. 例如: 数据格式如下, 如何处理呢? 标签1 标签2 标签3 特征数据 __label__flavor __label__spices __label__chemistry Flavor and Chemical Composition of Thyme 思路: 转成 多个 二分类问题即可, 例如: Flavor and Chemical Composition of Thyme __label__flavor Flavor and Chemical Composition of Thyme __label__spices Flavor and Chemical Composition of Thyme __label__chemistry 细节: 1. FastText确实训练和预测较快, 但是精度相对不是特别高, 我们可以优化下. 2. 优化思路为(参考): 1. 对数据做预处理, 统一小写, 单词和符号分离... 2. 修改训练的轮数. 3. 调整学习率. 4. 增加N-Gram 5. 调整损失函数 6. 自动调参. 7. 生产环境中, 可能会涉及 多标签多分类的问题, 一般会拆解成 多个二分类来解决. 8. 保存和加载模型. # 导包 import fasttext # todo 1.直接训练. def dm01_basic(): # 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型. model fasttext.train_supervised(input./data/cooking_train.txt) # 2. 使用模型进行预测. # 预测第1条样本. result1 model.predict(Flavor and Chemical Composition of Thyme) print(fresult1预测结果: {result1}) # 预测第2条样本 result2 model.predict(How to scale up modernist Potato Puree?) print(fresult2预测结果: {result2}) # 3. 测试模型. result3 model.test(./data/cooking_valid.txt) print(f模型在测试集上的测试结果: {result3}) # (验证集样本数量, 精度(准确率), 召回率) # todo 2.对数据做预处理(统一小写, 符号和单词分离...). def dm02_data_preprocess(): # 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型. model fasttext.train_supervised(input./data/cooking.pre.train) # 2. 使用模型进行预测. # 预测第1条样本. result1 model.predict(Flavor and Chemical Composition of Thyme) print(fresult1预测结果: {result1}) # 预测第2条样本 result2 model.predict(How to scale up modernist Potato Puree?) print(fresult2预测结果: {result2}) # 3. 测试模型. result3 model.test(./data/cooking.pre.valid) print(f模型在测试集上的测试结果: {result3}) # (验证集样本数量, 精度(准确率), 召回率) # todo 3.上步基础上, 增加: 训练轮数(默认5轮). def dm03_epoch(): # 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型. model fasttext.train_supervised(input./data/cooking.pre.train, epoch30) # 2. 使用模型进行预测. # 预测第1条样本. result1 model.predict(Flavor and Chemical Composition of Thyme) print(fresult1预测结果: {result1}) # 预测第2条样本 result2 model.predict(How to scale up modernist Potato Puree?) print(fresult2预测结果: {result2}) # 3. 测试模型. result3 model.test(./data/cooking.pre.valid) print(f模型在测试集上的测试结果: {result3}) # (验证集样本数量, 精度(准确率), 召回率) # todo 4.上步基础上, 增加: 学习率 def dm04_learning_rate(): # 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型. model fasttext.train_supervised(input./data/cooking.pre.train, epoch30, lr1) # 2. 使用模型进行预测. # 预测第1条样本. result1 model.predict(Flavor and Chemical Composition of Thyme) print(fresult1预测结果: {result1}) # 预测第2条样本 result2 model.predict(How to scale up modernist Potato Puree?) print(fresult2预测结果: {result2}) # 3. 测试模型. result3 model.test(./data/cooking.pre.valid) print(f模型在测试集上的测试结果: {result3}) # (验证集样本数量, 精度(准确率), 召回率) # todo 5.上步基础上, 增加: n-gram def dm05_ngram(): # 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型. model fasttext.train_supervised(input./data/cooking.pre.train, epoch30, lr1, wordNgrams2) # 2. 使用模型进行预测. # 预测第1条样本. result1 model.predict(Flavor and Chemical Composition of Thyme) print(fresult1预测结果: {result1}) # 预测第2条样本 result2 model.predict(How to scale up modernist Potato Puree?) print(fresult2预测结果: {result2}) # 3. 测试模型. result3 model.test(./data/cooking.pre.valid) print(f模型在测试集上的测试结果: {result3}) # (验证集样本数量, 精度(准确率), 召回率) # todo 6.上步基础上, 修改损失函数, 从传统的softmax() - hs(层次softmax): 负采样 softmax 哈夫曼树 def dm06_loss(): # 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型. model fasttext.train_supervised(input./data/cooking.pre.train, epoch30, lr1, wordNgrams2, losshs) # 2. 使用模型进行预测. # 预测第1条样本. result1 model.predict(Flavor and Chemical Composition of Thyme) print(fresult1预测结果: {result1}) # 预测第2条样本 result2 model.predict(How to scale up modernist Potato Puree?) print(fresult2预测结果: {result2}) # 3. 测试模型. result3 model.test(./data/cooking.pre.valid) print(f模型在测试集上的测试结果: {result3}) # (验证集样本数量, 精度(准确率), 召回率) # todo 7.上步基础上, 从手动调参改为自动调参. def dm07_auto(): # 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型. model fasttext.train_supervised( input./data/cooking.pre.train, # 训练集 autotuneValidationFile ./data/cooking.pre.valid, # 验证集 autotuneDuration120 # 自动调参的时间限制, 默认: 5分钟, 这里改成了2分钟. ) # 2. 使用模型进行预测. # 预测第1条样本. result1 model.predict(Flavor and Chemical Composition of Thyme) print(fresult1预测结果: {result1}) # 预测第2条样本 result2 model.predict(How to scale up modernist Potato Puree?) print(fresult2预测结果: {result2}) # 3. 测试模型. result3 model.test(./data/cooking.pre.valid) print(f模型在测试集上的测试结果: {result3}) # (验证集样本数量, 精度(准确率), 召回率) # todo 8.在生产环境中, 可能会涉及: 多分类多标签的问题, 解决方案如下 def dm08_multi_label(): # 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型. model fasttext.train_supervised( input./data/cooking.pre.train, # 训练集 epoch25, # 训练轮数 lr0.2, # 学习率 wordNgrams2, # n-gram lossova # one vs all ) # 2. 使用模型进行预测. # 预测第1条样本. # 参1: 预测的第1条样本, 参2: k表示预测的标签数量, 参3:阈值(只有大于该阈值的标签才会被保留) result1 model.predict(Flavor and Chemical Composition of Thyme, k3, threshold0.5) print(fresult1预测结果: {result1}) # 预测第2条样本 # 这里的-1含义是: 预测所有标签(尽可能多的显示) result2 model.predict(How to scale up modernist Potato Puree?, k-1) print(fresult2预测结果: {result2}) # 3. 测试模型. result3 model.test(./data/cooking.pre.valid) print(f模型在测试集上的测试结果: {result3}) # ((__label__baking,), array([0.56148356])) # todo 9. 模型保存 def dm09_save(): # 1. 获取训练模型(有监督训练: 有特征, 有标签) - 得到训练好的模型. model fasttext.train_supervised(input./data/cooking.pre.train, epoch30, lr1, wordNgrams2) # 2. 保存模型. model.save_model(./model/fasttext_model.model) # 3.模型加载. model2 fasttext.load_model(./model/fasttext_model.model) # 4.使用模型进行预测. result1 model2.predict(substitutes for wheat flour and their challenges in baking, k3, threshold0.5) print(fresult1结果为: {result1}) # todo 10. 测试代码. if __name__ __main__: # 1. 测试: 直接训练. # dm01_basic() # (3000, 0.12666666666666668, 0.05477872279083177) # 2. 测试: 对数据做预处理. # dm02_data_preprocess() # (3000, 0.172, 0.07438373936860314) # 3. 测试: 增加: 训练轮数. # dm03_epoch() # (3000, 0.5446666666666666, 0.2355485080005766) # 4. 测试: 添加: 学习率. # dm04_learning_rate() # (3000, 0.597, 0.25818076978520976) # 5. 测试: 添加: n-gram. # dm05_ngram() # (3000, 0.6186666666666667, 0.2675508144731152) # 6. 测试: 修改: 损失函数. # dm06_loss() # (3000, 0.603, 0.26077555139109126) # 7. 测试: 自动调参. # dm07_auto() # (3000, 0.5446666666666666, 0.2355485080005766) # 8. 测试: 多分类多标签的问题. # dm08_multi_label() # (3000, 0.6026666666666667, 0.26063139685743114) # 9. 模型保存. dm09_save()