原创

机器学习 之 支持向量机(SupportVectorMachine)文本算法的精确率——升级版sklearn

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://shazhenyu.blog.csdn.net/article/details/87967197

0、推荐

无意中发现了一个巨牛的人工智能教程,忍不住分享一下给大家。教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家。点这里可以跳转到教程。

1、背景

最近的项目中,用到了很多机器学习的算法,每个机器学习的算法在不同的样本下的精准率是不同的。为了验证每个算法在每种不同样本数量的能力,就做了一下实验,本文讲的是“支持向量机”在文本算法中的精准率。
支持向量机已经不陌生了,之前已经写过两篇关于SVM的文章了,本文讲的是如何调参可以让sklearn的默认精确率提高3%~5%左右方法,这样就和liblinear中的svm算法的精确率一致了。

其它两篇SVM算法的链接地址:
机器学习 之 支持向量机(SupportVectorMachine)文本算法的精确率
机器学习 之 Liblinear中的支持向量机(SupportVectorMachine)文本算法的精确率

相关其它机器学习算法的精准率:
决策树:机器学习 之 决策树(Decision Tree)文本算法的精确率
逻辑回归:机器学习 之 逻辑回归(LogisticRegression)文本算法的精确率
K近邻:机器学习 之 K近邻(K-NearestNeighbor)文本算法的精确率
朴素贝叶斯:机器学习 之 朴素贝叶斯(Naive Bayesian Model)文本算法的精确率
随机森林:机器学习 之 随机森林(Random Forest)文本算法的精确率

机器学习各个算法对比:人工智能 之 机器学习常用算法总结 及 各个常用分类算法精确率对比

2、效果图

先看一下没有任何调参的情况下的效果吧!
增加参数后的支持向量机:
在这里插入图片描述

没有任何参数的情况下的支持向量机:
在这里插入图片描述
通过以上数据可以看出在样本数量较低的情况下还不错的,相比其它模型,效果较理想。可以看出随着数据量的增加,训练时间几乎是在递增的,测试时间较短,比较适合语料。

3、本次实验整体流程

1、先把整体样本读到内存中

2、把整体样本按照8:2的比例,分为80%的训练集,20%的测试集

3、然后“训练集”的样本 先分词,再转换为词向量

4、接着把训练集的样本和标签统一的传入算法中,得到拟合后的模型

5、把“测试集”的样本 先分词,再得到词向量

6、把测试集得出的词向量丢到拟合后的模型中,看得出的结果

7、以上流程循环三次,得到平均值,得到的结果,更有说服力

8、把结果转换为准确率的形式,最后做成表格形式以便观看

4、这里不用词向量,用TF-IDF预处理后的向量效果更好

TF-IDF(词频-逆文本频率),前面的TF也就是常说到的词频,我们之前做的向量化也就是做了文本中各个词的出现频率统计,并作为文本特征,这个很好理解。关键是后面的这个IDF,即“逆文本频率”如何理解。有些句子中的词,比如说“的”,几乎所有句子都会出现,词频虽然高,但是重要性却应该比 主语、宾语等低。IDF就是来帮助我们来反应这个词的重要性的,进而修正仅仅用词频表示的词特征值。
概括来讲, IDF反应了一个词在所有文本中出现的频率,如果一个词在很多的文本中出现,那么它的IDF值应该低

加了TF-IDF处理后的效果:
在这里插入图片描述
经过TF-IDF处理后的效果更好。

    # 采用TF-IDF预处理
    isTFIDF = True

5、源代码

import jieba
import datetime
# 向量\测试集\训练集\得分比对
from sklearn.model_selection  import train_test_split
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer

# 支持向量机
from liblinearutil import *
from scipy import sparse

# 忽略警告
import warnings
warnings.filterwarnings("ignore", category=FutureWarning, module="sklearn", lineno=196)

m_count = [1000,3000,5000,8000,10000,15000,20000]

# all
m_list_allText = []
m_list_allL4ID = []
# 内容的训练集、测试集
m_text_test = []
m_text_train = []
m_label_test = []
m_label_train = []

m_map_all = {}

m_test_map = {}

# 读取文件里面数据,获取标签和内容
def getFile(filename, count):
    with open(filename, 'r' ,encoding='utf-8') as fp:
        global m_list_allL4ID,m_list_allText
        m_list_allL4ID = []
        m_list_allText = []
        for i in range(count):
            text = fp.readline()
            if ":" in text:
                L4ID = text.split(":")[-2]
                Msg = text.split(":")[-1]
                m_list_allL4ID.append(L4ID)
                m_list_allText.append(Msg)

# 随机分为 测试集 和 训练集 2-8分
def randomTestAndTrain():
    # 生成训练集和测试集
    global m_text_test, m_text_train, m_label_test, m_label_train
    m_text_train, m_text_test, m_label_train, m_label_test = train_test_split(m_list_allText, m_list_allL4ID, test_size=0.2, random_state=0)

# 结巴分词转换成向量
def jiabaToVector(list, isTest, isTFIDF = False):
    tmp_list = []
    for sentence in list:
        tmp_list.append(" ".join(jieba.cut(sentence.strip(), cut_all=True)))
    # 利用TFIDF生成词向量
    transformer = TfidfTransformer()
    if isTest:
        if isTFIDF:
            tfidf = transformer.fit_transform(vectorizer.transform(tmp_list))
        else:
            tfidf = vectorizer.transform(tmp_list)
    else:
        if isTFIDF:
            tfidf = transformer.fit_transform(vectorizer.fit_transform(tmp_list))
        else:
            tfidf = vectorizer.fit_transform(tmp_list)
    return tfidf

# 将稀疏矩阵转换为LibLinear需要的形式
# def matrixToLibLinear(vector_input):
#     print("正在进行转换……")
#     startT_NewArr = datetime.datetime.now()
#     vector_inputNew = sparse.csr_matrix(vector_input)
#     endT_NewArr = datetime.datetime.now()
#     print("稀疏矩阵转换时间Time:%ds"%(endT_NewArr - startT_NewArr).seconds)
#     return vector_inputNew

# 将L4转换为整形
def L4ToInt(m_label_l4):
    m_label_l4New = []
    for i in range(len(m_label_l4)):
        m_label_l4New.append(int(m_label_l4[i][1:]))
    return m_label_l4New

# 测试
def test(count):
    getFile("./rg_train_20190102_20181227114134.train", count)
    # print("获取全部已知数据的label和text")


    global vectorizer
    # 全局向量
    # analyzer='word',token_pattern=u"(?u)\\b\\w+\\b" ----------------目的是为了匹配单个字
    vectorizer = CountVectorizer(analyzer='word',token_pattern=u"(?u)\\b\\w+\\b")

    # 随机分为 测试集 和 训练集 2-8分
    randomTestAndTrain()

    # 不采用TF-IDF预处理
    isTFIDF = True

    # 数据大小
    lenall = len(m_list_allText)
    print("总集大小:", lenall)

    # 生成训练向量
    vector_train = jiabaToVector(m_text_train, False, isTFIDF)   # vector_train 为稀疏矩阵

    # 训练
    startT_Train = datetime.datetime.now()
    # m_label_train 转换成整型,去除L
    m_label_trainNew = L4ToInt(m_label_train)

    # print("训练前数据vector_trainNew:", vector_trainNew)
    # print("训练前数据m_label_trainNew:", m_label_trainNew)

    # -q : 安静模式(无输出信息)
    model = train(m_label_trainNew, vector_train, "-q -s 4")
    endT_Train = datetime.datetime.now()
    print("训练时间Time:%ds"%(endT_Train - startT_Train).seconds)

    # 生成测试向量
    vector_test = jiabaToVector(m_text_test, True, isTFIDF)   # vector_test 为稀疏矩阵
    # m_label_test 转换成整型,去除L
    m_label_testNew = L4ToInt(m_label_test)
    # 测试
    startT = datetime.datetime.now()
    # print("测试前数据vector_testNew:", vector_testNew)
    # print("测试前数据m_label_testNew:", m_label_testNew)
    pLabel,pAcc,pVal = predict(m_label_testNew, vector_test, model)
    endT = datetime.datetime.now()
    print("测试Time:", (endT - startT).microseconds)

    ACC,MSE,SCC = evaluations(m_label_testNew,pLabel)

    # 计算百分比
    print("准确率:", round(ACC, 3))

    map_all = {}
    m_list_all = []
    map_all["精确率"]=round(ACC, 3)
    map_all["数据量"]=lenall
    map_all["训练时间/s"]=(endT_Train - startT_Train).seconds
    map_all["测试时间/us"]=(endT - startT).microseconds
    m_list_all.append(map_all)
    m_map_all[count] = m_list_all

def runOnce(i):
    global m_map_all
    print ("-- 开始 --")
    for testC in m_count:
        test(testC)
    print ("-- 结束 --")

    # 打印表格
    print("数据量\t准确度\t训练时间/s\t测试时间/us")
    for testC in m_count:
        for key in m_map_all[testC]:
            print("%d\t%f\t%d\t%d"%(key["数据量"],key["精确率"],key["训练时间/s"],key["测试时间/us"]))
    m_test_map[i] = m_map_all
    m_map_all = {}

if __name__ =="__main__":
    # 跑n次
    runCount = 3
    for i in range(runCount):
        runOnce(i)


    # 打印n次的结果
    for i in range(runCount):
        tmp = m_test_map[i]
        # 打印表格
        print("数据量\t准确度\t训练时间/s\t测试时间/us [第%d次]"%(i+1))
        for testC in m_count:
            for key in tmp[testC]:
                print("%d\t%f\t%d\t%d"%(key["数据量"],key["精确率"],key["训练时间/s"],key["测试时间/us"]))

    # n次平均值
    print("数据量\t准确度\t训练时间/s\t测试时间/us [%d次平均值]"%(runCount))
    for testC in m_count:
        tmpMap = m_test_map[i]
        tmpList = tmpMap[testC][0]
        tmpCount = tmpList["数据量"]
        tmpScore = 0
        tmpTrainTime = 0
        tmpTestTime = 0
        for i in range(runCount):
            tmpScore += tmpList["精确率"]
            tmpTrainTime += tmpList["训练时间/s"]
            tmpTestTime += tmpList["测试时间/us"]
        print("%d\t%f\t%d\t%d"%(tmpCount,tmpScore/runCount,tmpTrainTime/runCount,tmpTestTime/runCount))

6、知识点普及

6.1支持向量机优点

SVM理论提供了一种避开高维空间的复杂性,直接用此空间的内积函数(既是核函数),再利用在线性可分的情况下的求解方法直接求解对应的高维空间的决策问题。当核函数已知,可以简化高维空间问题的求解难度。同时SVM是基于小样本统计理论的基础上的,这符合机器学习的目的,而且支持向量机比神经网络具有较好的泛化推广能力。

6.2支持向量机缺点

对于每个高维空间在此空间的映射F,如何确定F也就是核函数,现在还没有合适的方法,所以对于一般的问题,SVM只是把高维空间的复杂性的困难转为了求核函数的困难。而且即使确定核函数以后,在求解问题分类时,要求解函数的二次规划,这就需要大量的存储空间。这也是SVM的一个问题。

7、Sklearn提供的SVM横向对比(没加任何参数)

7.1 效果

在这里插入图片描述

7.2 Sklearn提供的SVM算法适不适合文本分类

官网地址:https://scikit-learn.org/stable/modules/classes.html#module-sklearn.svm
在这里插入图片描述
根据sklearn官方介绍,可以看出:
不适合的SVM算法:LinearSVR、NuSVC、NuSVR、OneClassSVM、SVR、l1_min_c
适合的SVM算法:LinearSVC、SVC

7.3 源代码

import jieba
import datetime
# 向量\测试集\训练集\得分比对
from sklearn.model_selection  import train_test_split
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics import accuracy_score
# 支持向量机
from sklearn import svm

# 忽略警告
import warnings
warnings.filterwarnings("ignore", category=FutureWarning, module="sklearn", lineno=196)

m_current_sklearn = "LinearSVC"
# 不适合的SVM算法:LinearSVR、NuSVC、NuSVR、OneClassSVM、SVR、l1_min_c
# 适合的SVM算法:LinearSVC、SVC
m_sklearn_list = ["LinearSVC","SVC"]

m_count = [1000,3000,5000,8000,10000,15000,20000]

# all
m_list_allText = []
m_list_allL4ID = []
# 内容的训练集、测试集
m_text_test = []
m_text_train = []
m_label_test = []
m_label_train = []

m_map_list = []
m_map_all = {}


# 读取文件里面数据,获取标签和内容
def getFile(filename, count):
    with open(filename, 'r' ,encoding='utf-8') as fp:
        global m_list_allL4ID,m_list_allText
        m_list_allL4ID = []
        m_list_allText = []
        for i in range(count):
            text = fp.readline()
            if ":" in text:
                L4ID = text.split(":")[-2]
                Msg = text.split(":")[-1]
                m_list_allL4ID.append(L4ID)
                m_list_allText.append(Msg)

# 随机分为 测试集 和 训练集 2-8分
def randomTestAndTrain():
    # 生成训练集和测试集
    global m_text_test, m_text_train, m_label_test, m_label_train
    m_text_train, m_text_test, m_label_train, m_label_test = train_test_split(m_list_allText, m_list_allL4ID, test_size=0.2, random_state=1)

def jiabaToVector(list, isTest, isTFIDF = False):
    tmp_list = []
    for sentence in list:
        tmp_list.append(" ".join(jieba.cut(sentence.strip())))
    # 利用TFIDF生成词向量
    transformer = TfidfTransformer()
    if isTest:
        if isTFIDF:
            tfidf = transformer.fit_transform(vectorizer.transform(tmp_list))
        else:
            tfidf = vectorizer.transform(tmp_list)
    else:
        if isTFIDF:
            tfidf = transformer.fit_transform(vectorizer.fit_transform(tmp_list))
        else:
            tfidf = vectorizer.fit_transform(tmp_list)
    return tfidf


# 创建默认参数的逻辑回归
def predict_4(X, Y):
    if m_current_sklearn == "LinearSVC":
        clf = svm.LinearSVC()
    elif m_current_sklearn == "SVC":
        clf = svm.SVC()
    clf = clf.fit(X, Y)
    return clf




def test(count):
    # getFile("./rg_test.train", count)
    getFile("./rg_train_20190102_20181227114134.train", count)
    # print("获取全部已知数据的label和text")

    # 随机分为 测试集 和 训练集 2-8分
    randomTestAndTrain()

    global vectorizer
    # 全局向量
    vectorizer = CountVectorizer()

    # 生成训练向量
    vector_train = jiabaToVector(m_text_train, False)
    # 生成训练向量-tfidf
    # vector_train = jiabaToVector(m_text_train, False, True)

    # 数据大小
    lenall = len(m_list_allText)
    print("总集大小:", lenall)

    # 训练
    startT_Train = datetime.datetime.now()
    clf = predict_4(vector_train, m_label_train)
    endT_Train = datetime.datetime.now()
    print("训练Time:", (endT_Train - startT_Train).microseconds)

    # 生成测试向量
    vector_test = jiabaToVector(m_text_test, True)
    # 生成测试向量-tfidf
    # vector_test = jiabaToVector(m_text_test, True, True)

    # 测试
    startT = datetime.datetime.now()
    result = clf.predict(vector_test)
    endT = datetime.datetime.now()
    print("测试Time:", (endT - startT).microseconds)

    # 计算百分比
    percent = accuracy_score(result, m_label_test)
    print("准确率:", round(percent, 3))

    map_all = {}
    map_all["精确率"]=round(percent, 3)
    map_all["数据量"]=lenall
    map_all["训练时间/us"]=(endT_Train - startT_Train).microseconds
    map_all["测试时间/us"]=(endT - startT).microseconds
    m_map_list.append(map_all)

def runOne():
    global m_map_list
    print ("-- 开始 --",m_current_sklearn)
    m_map_list = []
    for testC in m_count:
        test(testC)
    m_map_all[m_current_sklearn] = m_map_list
    print ("-- 结束 --",m_current_sklearn)


if __name__ =="__main__":
    for tmpItem in m_sklearn_list:
        m_current_sklearn = tmpItem
        runOne()
    # 打印表格
    for tmpItem in m_sklearn_list:
        print("当前算法:",tmpItem)
        print("数据量\t准确度\t训练时间/us\t测试时间/us")
        for key in m_map_all[tmpItem]:
            print("%d\t%f\t%d\t%d"%(key["数据量"],key["精确率"],key["训练时间/us"],key["测试时间/us"]))
    # m_sklearn_list = ["LinearSVC","LinearSVR","NuSVC","NuSVR","OneClassSVM","SVC","SVR","l1_min_c"]

8、总结

Sklearn的svm算法中,LinearSVC效果是最优的。

8.1 加入两个参数后的linearSVC效果提升了3%个点左右

回顾两个参数:
1、参数一

# 全局向量
    # analyzer='word',token_pattern=u"(?u)\\b\\w+\\b" ----------------目的是为了匹配单个字
    vectorizer = CountVectorizer(analyzer='word',token_pattern=u"(?u)\\b\\w+\\b")

2、参数二

	# cut_all=True 启用全模式,默认是精确度模式。
	# 举例:原句:清华大学,分词后:清华/ 清华大学/ 华大/ 大学(全模式) 清华大学(精确度模式)
	jieba.cut(sentence.strip(), cut_all=True)

8.2 使用TF-IDF预处理向量后,效果提升了1%~2%个点左右

# list 向量集,isTest 是测试集还是训练集,isTFIDF 是否使用TF-IDF做预处理
def jiabaToVector(list, isTest, isTFIDF = False):
    tmp_list = []
    for sentence in list:
        tmp_list.append(" ".join(jieba.cut(sentence.strip())))
    # 利用TFIDF生成词向量
    transformer = TfidfTransformer()
    if isTest:
        if isTFIDF:
            tfidf = transformer.fit_transform(vectorizer.transform(tmp_list))
        else:
            tfidf = vectorizer.transform(tmp_list)
    else:
        if isTFIDF:
            tfidf = transformer.fit_transform(vectorizer.fit_transform(tmp_list))
        else:
            tfidf = vectorizer.fit_transform(tmp_list)
    return tfidf
文章最后发布于: 2019-02-27 10:07:11
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 代码科技 设计师: Amelia_0503

分享到微信朋友圈

×

扫一扫,手机浏览