自然语言处理系列-2-文本分类-传统机器学习方法

文档分类是指给定文档p(可能含有标题t),将文档分类为n个类别中的一个或多个,本文以人机写作为例子,针对有监督学习简单介绍传统机器学习方法。 文档分类的常见应用:

新闻分类: 也就是给新闻打标签,一般标签有几千个,然后要选取k个标签,多分类问题,可见2017知乎看山杯比赛该比赛是对知乎的问题打标签;
人机写作判断: 判断文章是人写的还是机器写的,二分类问题,可见CCF2017的360人机大战题目;
情感识别: 例如判断豆瓣影评中的情感是正向、负向、中立,这个问题很常见而且应用场景很广泛;

使用传统机器学习方法解决文档分类问题一般分为:

  1. 文档预处理;
  2. 特征提取;
  3. 分类器选取;
  4. 模型训练及验证;

1. 文本预处理

分词:中文任务分词必不可少,一般使用jieba分词,工业界的翘楚。

词性标注:在分词后判断词性(动词、名词、形容词、副词…),在使用jieba分词的时候设置参数就能获取。

WORD—EMBEDDING:通过词与上下文、上下文与词的关系,有效地将词映射为低维稠密的向量,可以很好的表示词,一般是把训练和测试的语料都用来做word-embedding。可以把word-embedding作为传统机器学习算法的特征,同时也是深度学习方法必不可少的步骤(深度学习中单字和词的embedding都需要)。 本文使用Word2Vector实现Word Embedding,参数设置情况如下:

size=256 : Word Embedding的维度,如果是词的话一般设置为256,字的话设置为100就差不多,毕竟汉字数量为9w左右常用字7000左右;
window=5: 滑动窗口的大小,词一般设置为5左右,表示当前词加上前后词数量为5,如果为字的话可以设置大一点;
min_count=5: 最小词频,超过该词频的才纳入统计,字的话词频可以设置高一点;
workers=15: 线程数量,加速处理;

分词、Word Embedding训练的代码如下,推荐使用pickle进行中间数据存储:

import pickle
import codecs
import jieba
import multiprocessing
import codecs
import pandas as pd
from gensim.models.word2vec import Word2Vec

train_file = "train.csv"
test_file = "test.csv"
train_file = codecs.open(train_file, 'r', 'utf-8')
train_lines = train_file.readlines()
test_file = codecs.open(test_file, 'r', 'utf-8')
test_lines = test_file.readlines()

label = []
train_title = []
train_content = []
train_title_cut = []
train_content_cut = []

test_id = []
test_title = []
test_content = []
test_title_cut = []
test_content_cut = []

print("Segment train title/content...")
for i in range(len(train_lines)):
    if i % 10000 == 0:
        print(i)
    if len(train_lines[i].split('\t')) != 4:
        continue
    article_id, title, content, l = train_lines[i].split('\t')
    if 'NEGATIVE' in l:
        label.append(0)
    else:
        label.append(1)

    train_title.append(title)
    train_content.append(content)
    train_title_cut.append(' '.join(jieba.cut(title.strip('\n'), cut_all=False)))
    train_content_cut.append(' '.join(jieba.cut(content.strip('\n'), cut_all=False)))

print("Segment train completed.")
print("Segment test title/content...")
for i in range(len(test_lines)):
    if i % 10000 == 0:
        print(i)
    if len(test_lines[i].split('\t')) != 3:
        continue
    article_id, title, content = test_lines[i].split('\t')

    test_id.append(article_id)

    test_title.append(title)
    test_content.append(content)
    test_title_cut.append(' '.join(jieba.cut(title.strip('\n'), cut_all=False)))
    test_content_cut.append(' '.join(jieba.cut(content.strip('\n'), cut_all=False)))
print("Segment test completed.")

pickle.dump(label, open('train_label.p', 'wb'))
pickle.dump(train_title, open('train_title.p', 'wb'))
pickle.dump(train_content, open('train_content.p', 'wb'))
pickle.dump(train_title_cut, open('train_title_cut.p', 'wb'))
pickle.dump(train_content_cut, open('train_content_cut.p', 'wb'))
pickle.dump(test_id, open('test_id.p', 'wb'))
pickle.dump(test_title, open('test_title.p', 'wb'))
pickle.dump(test_content, open('test_content.p', 'wb'))
pickle.dump(test_title_cut, open('test_title_cut.p', 'wb'))
pickle.dump(test_content_cut, open('test_content_cut.p', 'wb'))

corpus = train_title_cut + train_content_cut + test_title_cut + test_content_cut

class CorpusData:
    def __init__(self, corpus):
        self.corpus = corpus
    def __iter__(self):
        for doc in corpus:
            origin_words = doc.split(' ')
            yield origin_words

print("Train word to vector...")
corpus_data = CorpusData(corpus)
model = Word2Vec(corpus_data, size=256, window=5, min_count=5, workers=15)
model.save('w2v_model_s256_w5_m5.save')
print("Train w2v completed.")

2. 特征提取

可以说提取的特征决定了整个任务分数的上限,强的或者说敏感的特征对文档分类有及其大的影响,而弱特征的组合有时候也能发挥意向不到的效果,提取过程一般是选取文档的常规特征、针对具体任务设计的特征、对特征的强度计算和筛选。

2.1 常规特征

TF-IDF:词频-逆文档频率,用以评估词对于一个文档集或一个语料库中的其中一个文档的重要程度,计算出来是一个DxN维的矩阵,其中D为文档的数量,N为词的个数,通常会加入N-gram,也就是计算文档中N个相连词的的TF-IDF。一般用sklearn的库函数计算,具体用法详见sklearn.feature_extraction.text.TfidfVectorizer。在人机写作判断的问题来看,TF-IDF是很强的一个特征。
LDA(文档的话题):可以假设文档集有T个话题,一篇文档可能属于一个或多个话题,通过LDA模型可以计算出文档属于某个话题的概率,这样可以计算出一个D * T的矩阵。LDA特征在文档打标签等任务上表现很好。
LSI(文档的潜在语义):通过分解文档-词频矩阵来计算文档的潜在语义,和LDA有一点相似,都是文档的潜在特征。
词性的TD—IDF:以词的词性表示词,再次计算其tf-idf,由于词性种类很有限,矩阵比较小。

2.2 针对具体任务设计特征

本文是以人机写作判断为例子,为此设计了以下特征,其中每种特征都选取最大值、最小值、平均值、中位数、方差:

句子长度:文档短句之后,统计句子长度;
标点数:文档断句之后,每个句子中的标点个数;
jaccard相似度:分词后的每个句子与分词后的标题的jaccard距离;
重复句子:文档中是否有重复句子;
英文、数字个数:断句后句子中的英文、数字个数;

2.3 特征的强度计算和筛选

我们要尽可能选取任务敏感的特征,也就是特征足够强可以影响分类的结果,一般用树模型判断特征的重要程度,xgboost的get_fscore就可以实现这一功能。计算特征强度之后,选取较强的特征,摒弃弱特征。可以尝试组合不同的特征来构造新的特征,然后测试新特征的强弱,反复如此获取更多的强特征。

3. 分类器选取

特征提取相当于构造了一个D * F的矩阵,其中D为文档数量,F为特征数量,前人给我留下了诸如朴素贝叶斯、逻辑回归、SVM、决策树、神经网络等分类算法,sklearn等机器学习库将其封装为简单容易上手的API,供我们选择。

朴素贝叶斯:以前很多人拿到数据就会马上用朴素贝叶斯验证一下数据集,但是往往由于各个特征不是独立同分布的所以朴素贝叶斯的效果一般不好,但是也可以尝试一下,如果效果不错作为最后模型融合的一个模型。针对人机写作判断的任务来说效果太差了,就没有记录。

逻辑回归:用来解决回归问题的方法,分类问题可以看做回归问题的特例,也能尝试用此方法。但是结果和朴素贝叶斯差不多的样子。

SVM:在神经网络火起来之前,可以说SVM撑起了一半分类问题的解决方案,SVM的想法很质朴:找出两类点之间的最大间隔(也包括软间隔,即间隔中有样本点)把样本点分类。但是在人机写作判断的问题上,SVM的表现也不好。

树模型:树结构很适合文档分类,使用LightGBM模型对文档进行分类,其中调参的经验是树的深度不要太深、让树尽可能矮宽,这样分类比较充分,一般深度为4~6就行。树模型的调参是一件很痛苦的事情,推荐基于贝叶斯优化的调参方法。有熟悉XGBoost的同学也可以尝试,但是总体来说LGB比XGB速度要快好几倍更适合验证特征以及调参。

4. Adaboost多次训练

这是原文针对人机写作判断问题,根据Adaboost算法设计的一个小Trick。如果每次分类结果中,把大量的负样本分为正样本,或者大量负样本分为正样本,就根据正样本和负样本的错误率调整正负样本的权重,例如正负样本的错误率分别为P(正)、P(负),当前权重分别为W(正)、W(负),则根据以下方式调整

W(正) = P(正)+ [1 - abs(P(正) + P(负)) / 2]
W(负) = P(负)+ [1 - abs(P(正) + P(负)) / 2]

根据这种动态调整权重的方法,可以充分发挥树模型在分类问题上的优势。

小结

深度学习的模型,明显优于传统机器学习模型。而且传统机器学习方法中的特征提取环节太过费时费力,而且经常很不讨好。建议刚接触文档分类或者其他机器学习任务,可以尝试传统的机器学习方法(主要是统计学习方法),可以体验一下这几个过程,但是如果想取得好成绩最好还是尝试深度学习。

参考链接:

  1. https://zhuanlan.zhihu.com/p/31963565
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在信号处理领域,DOA(Direction of Arrival)估计是一项关键技术,主要用于确定多个信号源到达接收阵列的方向。本文将详细探讨三种ESPRIT(Estimation of Signal Parameters via Rotational Invariance Techniques)算法在DOA估计中的实现,以及它们在MATLAB环境中的具体应用。 ESPRIT算法是由Paul Kailath等人于1986年提出的,其核心思想是利用阵列数据的旋转不变性来估计信号源的角度。这种算法相比传统的 MUSIC(Multiple Signal Classification)算法具有较低的计算复杂度,且无需进行特征值分解,因此在实际应用中颇具优势。 1. 普通ESPRIT算法 普通ESPRIT算法分为两个主要步骤:构造等效旋转不变系统和估计角度。通过空间平移(如延时)构建两个子阵列,使得它们之间的关系具有旋转不变性。然后,通过对子阵列数据进行最小二乘拟合,可以得到信号源的角频率估计,进一步转换为DOA估计。 2. 常规ESPRIT算法实现 在描述中提到的`common_esprit_method1.m`和`common_esprit_method2.m`是两种不同的普通ESPRIT算法实现。它们可能在实现细节上略有差异,比如选择子阵列的方式、参数估计的策略等。MATLAB代码通常会包含预处理步骤(如数据归一化)、子阵列构造、旋转不变性矩阵的建立、最小二乘估计等部分。通过运行这两个文件,可以比较它们在估计精度和计算效率上的异同。 3. TLS_ESPRIT算法 TLS(Total Least Squares)ESPRIT是对普通ESPRIT的优化,它考虑了数据噪声的影响,提高了估计的稳健性。在TLS_ESPRIT算法中,不假设数据噪声是高斯白噪声,而是采用总最小二乘准则来拟合数据。这使得算法在噪声环境下表现更优。`TLS_esprit.m`文件应该包含了TLS_ESPRIT算法的完整实现,包括TLS估计的步骤和旋转不变性矩阵的改进处理。 在实际应用中,选择合适的ESPRIT变体取决于系统条件,例如噪声水平、信号质量以及计算资源。通过MATLAB实现,研究者和工程师可以方便地比较不同算法的效果,并根据需要进行调整和优化。同时,这些代码也为教学和学习DOA估计提供了一个直观的平台,有助于深入理解ESPRIT算法的工作原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值