Python3 使用fastText进行文本分类 新闻分类

2021-12-22 00:05:14 浏览数 (3)

背景

❝We can train fastText on more than one billion words in less than ten minutes using a standard multicore CPU, and classify half a million sentences among 312K classes in less than a minute.

首先引用论文中的一段话来看看作者们是怎么评价fasttext模型的表现的。

这篇论文的模型非常之简单,之前了解过word2vec的同学可以发现这跟CBOW的模型框架非常相似。

对应上面这个模型,比如输入是一句话,到就是这句话的单词或者是n-gram。每一个都对应一个向量,然后对这些向量取平均就得到了文本向量,然后用这个平均向量取预测标签。当类别不多的时候,就是最简单的softmax;当标签数量巨大的时候,就要用到「hierarchical softmax」了。

模型真的很简单,也没什么可以说的了。下面提一下论文中的两个tricks:

  • 「hierarchical softmax」 类别数较多时,通过构建一个霍夫曼编码树来加速softmax layer的计算,和之前word2vec中的trick相同
  • 「N-gram features」 只用unigram的话会丢掉word order信息,所以通过加入N-gram features进行补充 用hashing来减少N-gram的存储

简介

这篇博客将会简要记录使用python版本的fastText对不同类别新闻进行分类,中间会使用结巴分词,pandas的数据处理。新闻数据可以使用清华的新闻数据。

安装依赖

Python版本:3.6 安装结巴分词以及fasttext

代码语言:javascript复制
pip install jieba
pip install fasttext

分词处理

分词过程中会删除一些常用的停用词,停用词可以使用https://github.com/dongxiexidian/Chinese/tree/master/dict

segmentation.py

代码语言:javascript复制
import jieba
import pandas as pd
import codecs
import math
import random

stopwords_set = set()
basedir = '/Users/derry/Desktop/Data/'

# 分词结果文件
train_file = codecs.open(basedir   "news.data.seg.train", 'w', 'utf-8')
test_file = codecs.open(basedir   "news.data.seg.test", 'w', 'utf-8')

# 停用词文件
with open(basedir   'stop_text.txt', 'r', encoding='utf-8') as infile:
    for line in infile:
        stopwords_set.add(line.strip())

train_data = pd.read_table(basedir   'News_info_train.txt', header=None, error_bad_lines=False)
label_data = pd.read_table(basedir   'News_pic_label_train.txt', header=None, error_bad_lines=False)

train_data.drop([2], axis=1, inplace=True)
train_data.columns = ['id', 'text']
label_data.drop([2, 3], axis=1, inplace=True)
label_data.columns = ['id', 'class']
train_data = pd.merge(train_data, label_data, on='id', how='outer')

for index, row in train_data.iterrows():
    # 结巴分词
    seg_text = jieba.cut(row['text'].replace("t", " ").replace("n", " "))
    outline = " ".join(seg_text)
    outline = " ".join(outline.split())

    # 去停用词与HTML标签
    outline_list = outline.split(" ")
    outline_list_filter = [item for item in outline_list if item not in stopwords_set]
    outline = " ".join(outline_list_filter)

    # 写入
    if not math.isnan(row['class']):
        outline = outline   "t__label__"   str(int(row['class']))   "n"
        train_file.write(outline)
        train_file.flush()

        # 划分数据集
        # if random.random() > 0.7:
        #     test_file.write(outline)
        #     test_file.flush()
        # else:
        #     train_file.write(outline)
        #     train_file.flush()

train_file.close()
test_file.close()

分类预测

这里使用fasttext进行训练的时候调整了一下参数word_ngrams,原本默认值为1,效果可能会好一点。不过要在后面加上bucket=2000000(默认值) ,不然会出错,在issue里面查了一下,好像是Python版本的fasttext版本比较旧,使用官方C 版就不会出现这个问题了。

classification.py

代码语言:javascript复制
import logging
import fasttext
import pandas as pd
import codecs

basedir = '/Users/derry/Desktop/Data/'
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)

# 训练
classifier = fasttext.supervised(basedir   "news.data.seg.train", basedir   "news.dat.seg.model", label_prefix="__label__", word_ngrams=3, bucket=2000000)

# 测试并输出 F-score
result = classifier.test(basedir   "news.data.seg.test")
print(result.precision * result.recall * 2 / (result.recall   result.precision))

# 读取验证集
validate_texts = []
with open(basedir   'news.data.seg.validate', 'r', encoding='utf-8') as infile:
    for line in infile:
        validate_texts  = [line]

# 预测结果
labels = classifier.predict(validate_texts)

# 结果文件
result_file = codecs.open(basedir   "result.txt", 'w', 'utf-8')

validate_data = pd.read_table(basedir   'News_info_validate.txt', header=None, error_bad_lines=False)
validate_data.drop([2], axis=1, inplace=True)
validate_data.columns = ['id', 'text']

# 写入
for index, row in validate_data.iterrows():
    outline = row['id']   't'   labels[index][0]   'tNULLtNULLn'
    result_file.write(outline)
    result_file.flush()

result_file.close()

最后附上GitHub地址:https://github.com/DerryChan/CSIT6000/tree/master/Derry

常用方法

训练参数

代码语言:javascript复制
def train_supervised(input, lr=0.1, dim=100, 
                   ws=5, epoch=5, minCount=1, 
                   minCountLabel=0, minn=0, 
                   maxn=0, neg=5, wordNgrams=1, 
                   loss="softmax", bucket=2000000, 
                   thread=12, lrUpdateRate=100,
                   t=1e-4, label="__label__", 
                   verbose=2, pretrainedVectors=""):
  """
  训练一个监督模型, 返回一个模型对象

  @param input: 训练数据文件路径
  @param lr:              学习率
  @param dim:             向量维度
  @param ws:              cbow模型时使用
  @param epoch:           次数
  @param minCount:        词频阈值, 小于该值在初始化时会过滤掉
  @param minCountLabel:   类别阈值,类别小于该值初始化时会过滤掉
  @param minn:            构造subword时最小char个数
  @param maxn:            构造subword时最大char个数
  @param neg:             负采样
  @param wordNgrams:      n-gram个数
  @param loss:            损失函数类型, softmax, ns: 负采样, hs: 分层softmax
  @param bucket:          词扩充大小, [A, B]: A语料中包含的词向量, B不在语料中的词向量
  @param thread:          线程个数, 每个线程处理输入数据的一段, 0号线程负责loss输出
  @param lrUpdateRate:    学习率更新
  @param t:               负采样阈值
  @param label:           类别前缀
  @param verbose:         ??
  @param pretrainedVectors: 预训练的词向量文件路径, 如果word出现在文件夹中初始化不再随机
  @return model object

  """

模型保存与加载

代码语言:javascript复制
# 保存模型
model.save_model("fasttext.model.bin")
# 压缩模型
model.quantize(input=train_data, qnorm=True, retrain=True, cutoff=100000)
print_results(*model.test(valid_data))
model.save_model("fasttext.model.ftz") # 保存压缩后的模型
# 加载模型
model= fasttext.load_model("fasttext.model.bin",label_prefix = "__label__")

词向量训练

代码语言:javascript复制
def train_unsupervised(input, model="skipgram", lr=0.05, dim=100, 
                   ws=5, epoch=5, minCount=5, 
                   minCountLabel=0, minn=3, 
                   maxn=6, neg=5, wordNgrams=1, 
                   loss="ns", bucket=2000000, 
                   thread=12, lrUpdateRate=100,
                   t=1e-4, label="__label__", 
                   verbose=2, pretrainedVectors=""):
  """
  训练词向量,返回模型对象
  输入数据不要包含任何标签和使用标签前缀

  @param model: 模型类型, cbow/skipgram两种
  其他参数参考train_supervised()方法
  @return model
  """

参考资料

  1. http://blog.csdn.net/lxg0807/article/details/52960072
  2. http://www.52nlp.cn/fasttext
  3. https://pypi.org/project/fasttext/
  4. https://cloud.tencent.com/developer/article/1598230
  5. https://www.pythonheidong.com/blog/article/441426/f8a7fb2ba8ca225ab292/
  6. https://webcache.googleusercontent.com/search?q=cache:CPFHb3JAk2wJ:https://www.codeleading.com/article/22283127900/ &cd=3&hl=zh-CN&ct=clnk&gl=hk

0 人点赞