【NLP】用ML实现中文短文本分类(二分类)

2023-05-16

1.用ML实现外卖评论的分类

步骤

  • 语料加载、
  • 分词、
  • 去停用词、
  • 抽取词向量特征、
  • 分别进行算法建模和模型训练、
  • 评估、计算AUC值、
  • 模型对比

1.进行语料加载。 在此之前,引入python依赖的包,并将全部语料和停用词dict读入内存中。

setp 1:引入依赖库:random随机数库、jieba分词库、pandas库
import random
import jieba
import pandas as pd
setp 2: 加载停用词字典 停用词词典为 stopwords.txt 文件,可以根据场景自己在该文本里面添加要去除的词(比如冠词、人称、数字等特定词)
stopwords = pd.read_csv('~/Desktop/stopWords_3.txt',index_col=False,quoting=3,sep='\t',names=['stopword'],encoding='utf-8')
stopwords = stopwords['stopword'].values
step 3:加载语料,语料是4个已经分好类的 csv 文件,直接用 pandas 加载即可,加载之后可以首先删除 nan 行,并提取要分词的 content 列转换为 list 列表
waimai_df = pd.read_csv('~/Downloads/ChineseNlpCorpus-master/datasets/waimai_10k/waimai_10k.csv', encoding='utf-8', sep=',')

#删除语料的nan行
waimai_df.dropna(inplace=True)
#转换成列表形式
waimai = waimai_df.review.values.tolist()
label_id = waimai_df.label.values.tolist()

2. 分词和去停用词

step 1:定义分词、去停用词的函数,函数包含2个参数:content_lines 参数为语料列表;sentences 参数为预先定义的 list,用来存储分词并打标签后的结果;
def preprocess_text(content_lines, sentences):
    for line in content_lines:
        try:
            segs=jieba.lcut(line)
            segs = [v for v in segs if not str(v).isdigit()]#去数字
            segs = list(filter(lambda x:x.strip(), segs))   #去左右空格
            segs = list(filter(lambda x:len(x)>1, segs)) #长度为1的字符
            segs = list(filter(lambda x:x not in stopwords, segs)) #去掉停用词
            sentences.append(" ".join(segs))
        except Exception:
            continue 
step 2:调用函数、生成训练数据
sentences = []
preprocess_text(waimai,sentences)

sentences = list(zip(sentences,label_id))
# print(sentences)
Building prefix dict from the default dictionary ...
Loading model from cache /tmp/jieba.cache
Loading model cost 0.726 seconds.
Prefix dict has been built succesfully.
step 3:将得到的数据集打散,生成更可靠的训练集分布,避免同类数据分布不均匀:
random.shuffle(sentences)
#在控制台输出前10条数据,观察一下
for sentence in sentences[:10]:
    print(sentence[0], sentence[1]) 
一般 服务 差到 至极 饭量 要死 0
好吃 卫生 满意 1
味道 送餐 太慢 饭菜 全凉 0
送餐 米饭 三盒 两盒 两盒 0
两份 筷子 0
太坑 图片 很大 可怜 肥肉 肘子 0
好吃 准时 1
猪肉 堡里 酱流 满意 猪肉 1
送来 没人接 电话 0
太贵 八个 饺子 大小 包子 几口 1

3. 抽取词向量特征。

step 1:抽取特征,我们定义文本抽取词袋模型特征
from sklearn.feature_extraction.text import CountVectorizer
vec = CountVectorizer(
    analyzer='word', # tokenise by character ngrams
    max_features=None,  # keep the most common 1000 ngrams
    vocabulary=None
)
step 2:把语料数据切分,用 sk-learn 对数据切分,分成训练集和测试集
from sklearn.model_selection import train_test_split

x, y = zip(*sentences)
x_train, x_test, y_train, y_test = train_test_split(x, y)
step 3:把训练数据转换为词袋模型
vec.fit(x_train)
CountVectorizer(analyzer='word', binary=False, decode_error='strict',
                dtype=<class 'numpy.int64'>, encoding='utf-8', input='content',
                lowercase=True, max_df=1.0, max_features=None, min_df=1,
                ngram_range=(1, 1), preprocessor=None, stop_words=None,
                strip_accents=None, token_pattern='(?u)\\b\\w\\w+\\b',
                tokenizer=None, vocabulary=None)

4.分别进行算法建模和模型训练。

setp 1:定义SVM模型,然后对训练集进行模型训练,直接使用 sklearn 中的 svm.SVC
from sklearn.svm import SVC
# svm = SVC(kernel='rbf',C=1000,gamma=0.001)
svm = SVC(kernel='linear') #0.8401735068401736
# svm = SVC(kernel='poly',gamma=1000)
svm.fit(vec.transform(x_train), y_train)
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
    decision_function_shape='ovr', degree=3, gamma='auto_deprecated',
    kernel='linear', max_iter=-1, probability=False, random_state=None,
    shrinking=True, tol=0.001, verbose=False)

5. 评估、计算 AUC 值。

setp 1:上面步骤1-4完成了从语料到模型的训练,训练之后,我们要用测试集来计算 AUC 值:
 print(svm.score(vec.transform(x_test), y_test))
0.8468468468468469
step 2:进行测试集的预测:
pre = svm.predict(vec.transform(x_test))

6. 模型对比。

整个模型从语料到训练评估 步骤1-5就完成了,接下来看看,改变特征向量模型和训练模型对结果有什么变化。
6-1 改变特征向量模型

下面可以把特征做得更强一点,尝试加入抽取 2-gram 和 3-gram 的统计特征,把词库的量放大一点。

from sklearn.feature_extraction.text import CountVectorizer
vec = CountVectorizer(
    analyzer='word', # tokenise by character ngrams
    ngram_range=(1,4),  # use ngrams of size 1 and 2
    max_features=20000,  # keep the most common 1000 ngrams
)
vec.fit(x_train)

from sklearn.svm import SVC
# svm = SVC(kernel='rbf',C=1000,gamma=0.001)
svm = SVC(kernel='linear') #0.8401735068401736
# svm = SVC(kernel='poly',gamma=1000)
svm.fit(vec.transform(x_train), y_train)
print(svm.score(vec.transform(x_test), y_test))
0.8375041708375042
6-2 改变训练模型
1 使用朴素贝叶斯训练 直接使用 sklearn 中的 MultinomialNB
from sklearn.model_selection import train_test_split

x, y = zip(*sentences)
x_train, x_test, y_train, y_test = train_test_split(x, y)

vec.fit(x_train)

from sklearn.naive_bayes import MultinomialNB
classifier = MultinomialNB()
classifier.fit(vec.transform(x_train), y_train)
print(classifier.score(vec.transform(x_test), y_test))
0.8495161828495161
2 使用XGBoost训练
import xgboost as xgb  
from sklearn.model_selection import StratifiedKFold  
import numpy as np
# xgb矩阵赋值  

xgb_train = xgb.DMatrix(vec.transform(x_train), label=y_train)  
xgb_test = xgb.DMatrix(vec.transform(x_test))


params={'booster':'gbtree',
    'objective': 'binary:logistic',
    'eval_metric': 'auc',
    'max_depth':4,
    'lambda':10,
    'subsample':0.75,
    'colsample_bytree':0.75,
    'min_child_weight':2,
    'eta': 0.025,
    'seed':0,
    'nthread':8,
     'silent':1}

watchlist = [(xgb_train,'train')]
bst=xgb.train(params,xgb_train,num_boost_round=5,evals=watchlist)
#输出概率
ypred=bst.predict(xgb_test)

# 设置阈值, 输出一些评价指标,选择概率大于0.5的为1,其他为0类
y_pred = (ypred >= 0.5)*1

from sklearn import metrics
print ('AUC: %.4f' % metrics.roc_auc_score(y_test,ypred))
print ('ACC: %.4f' % metrics.accuracy_score(y_test,y_pred))
print ('Recall: %.4f' % metrics.recall_score(y_test,y_pred))
print ('F1-score: %.4f' %metrics.f1_score(y_test,y_pred))
print ('Precesion: %.4f' %metrics.precision_score(y_test,y_pred))
print(metrics.confusion_matrix(y_test,y_pred))

[0]	train-auc:0.682634
[1]	train-auc:0.701378
[2]	train-auc:0.70118
[3]	train-auc:0.701877
[4]	train-auc:0.702868
AUC: 0.7019
ACC: 0.7501
Recall: 0.4863
F1-score: 0.5622
Precesion: 0.6662
[[1767  241]
 [ 508  481]]

2. 何为AUC值和ROC曲线?

参考百度百科及以下这篇博文:
https://baijiahao.baidu.com/s?id=1597939133517926460&wfr=spider&for=pc

AUC(Area Under Curve被定义为ROC曲线下的面积。
我们往往使用AUC值作为模型的评价标准是因为很多时候ROC曲线并不能清晰的说明哪个分类器的效果更好,而作为一个数值,对应AUC更大的分类器效果更好。

其中,ROC曲线全称为受试者工作特征曲线 (receiver operating characteristic curve),它是根据一系列不同的二分类方式(分界值或决定阈),以真阳性率(敏感性)为纵坐标,假阳性率(1-特异性)为横坐标绘制的曲线。

具体到机器学习的理论中,ROC曲线该怎么理解呢?首先,需要指出的是,ROC分析的是二元分类模型,也就是输出结果只有两种类别的模型,比如:(阳性/阴性)(有病/没病)(垃圾邮件/非垃圾邮件)。在二分类问题中,数据的标签通常用(0/1)来表示,在模型训练完成后进行测试时,会对测试集的每个样本计算一个介于0~1之间的概率,表征模型认为该样本为阳性的概率,我们可以选定一个阈值,将模型计算出的概率进行二值化,比如选定阈值=0.5,那么当模型输出的值大于等于0.5时,我们就认为模型将该样本预测为阳性,也就是标签为1,反之亦然。选定的阈值不同,模型预测的结果也会相应地改变。二元分类模型的单个样本预测有四种结果:

  • 真阳性(TP):判断为阳性,实际也是阳性。
  • 伪阳性(FP):判断为阴性,实际却是阳性。
  • 真阴性(TN):判断为阴性,实际也是阴性。
  • 伪阴性(FN):判断为阴性,实际却是阳性。

这四种结果可以画成2 × 2的混淆矩阵:
在这里插入图片描述
有了混淆矩阵,就可以定义ROC曲线了。ROC曲线将假阳性率(FPR)定义为 X 轴,真阳性率(TPR)定义为 Y 轴。其中:
TPR:在所有实际为阳性的样本中,被正确地判断为阳性的样本比率。
FPR:在所有实际为阴性的样本中,被错误地判断为阳性的样本比率。
TPR = TP / (TP + FN)
FPR = FP / (FP + TN)
给定一个二分类模型和它的阈值,就可以根据所有测试集样本点的真实值和预测值计算出一个 (X=FPR, Y=TPR) 坐标点,这也就是绘制单个点的方法。那整条ROC曲线又该怎么画呢?具体方法如下:
在我们训练完一个二分类模型后,可以使用该模型对测试集中的全部样本点计算一个对应的概率值,每个值都介于0~1之间。假设测试集有100个样本点,我们可以对这100个样本的预测值从高到低排序,然后依次取每个值作为阈值,一旦阈值确定我们就可以绘制ROC曲线上的一个点,按照这种方法依次将100个点绘制出来,再将各个点依次连接起来,就得到了我们想要的ROC曲线!

然后再回到最初的问题,
AUC就是衡量学习器优劣的一种性能指标,可通过对ROC曲线下各部分的面积求和而得。当我们绘制出ROC曲线之后,AUC的值自然也就计算好啦。

为什么需要用AUC值表征模型的好坏?
在进行学习器的比较时,若一个学习器的ROC曲线被另一个学习器的曲线完全“包住”,则可断言后者的性能优于前者;若两个学习器的ROC曲线发生交叉,则难以一般性的断言两者孰优孰劣。此时如果一定要进行比较,则比较合理的判断依据是比较ROC曲线下的面积,即AUC(Area Under ROC Curve)。

从AUC 判断分类器(预测模型)优劣的标准:

AUC = 1,是完美分类器。
AUC = [0.85, 0.95], 效果很好
AUC = [0.7, 0.85], 效果一般
AUC = [0.5, 0.7],效果较低,但用于预测股票已经很不错了
AUC = 0.5,跟随机猜测一样(例:丢铜板),模型没有预测价值。
AUC < 0.5,比随机猜测还差;但只要总是反预测而行,就优于随机猜测。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【NLP】用ML实现中文短文本分类(二分类) 的相关文章

随机推荐