• Python实现的互联网新闻情感分析


    互联网新闻情感分析

    一、任务描述

    1.1 赛题背景

    随着各种社交平台的兴起,网络上用户的生成内容越来越多,产生大量的文本信息,如新闻、微博、博客等,面对如此庞大且富有情绪表达的文本信息,完全可以考虑通过探索他们潜在的价值为人们服务。因此近年来情绪分析受到计算机语言学领域研究者们的密切关注,成为一项进本的热点研究任务。

    本赛题目标为在庞大的数据集中精准的区分文本的情感极性,情感分为正中负三类。面对浩如烟海的新闻信息,精确识别蕴藏在其中的情感倾向。

    1.2 任务要求

    对官方提供的新闻数据进行情感极性分类,其中正面情绪对应0,中性情绪对应1以及负面情绪对应2。根据提供的训练数据,通过算法或模型判断出测试集中新闻的情感极性。

    1.3 数据描述

    数据包由两个csv文件组成:第一个是Train_Dataset,包含7360条新闻的id号,新闻标题和新闻内容。第二个是Train_Dataset_Label,包含了Dataset中新闻的id号,以其新闻的情感得分(用0,1,2表示)。

    二、实施方案

    该问题实质上为对信息的分类处理,所以核心内容是使用一个合适的分类器。其次,由于新闻是由文本构成的语言,一条新闻的情感通常可以由文本中词语的情感性决定。于是,另一个重要的内容是如何将数据进行预处理,即删除无用文字,并将新闻文本切分成一个个中文词语。

    2.1 数据预处理

    观察训练集中新闻的内容,发现新闻文本乱七八糟,有各种不属于中文词库的符号。所以预处理的第一步就是将不属于中文的文本删除(包括各种标点符号)。预处理的第二步是将修正后的文本进行词语的切分,从而将一整段话切分为一个个词语。

    2.2 分类器选择

    情感标签有三种赋值:积极、中立和消极。于是所有的二分类器就不可以使用,比如标准意义下的SVM支持向量机等。考虑到运行时间和效率,我们将选择朴素贝叶斯分类器作为首选(事实上,测试结果也表明朴素贝叶斯分类器是效率和正确率均较高的分类器)

    三、具体实现

    实现细节,模型设计和选择,数据预处理方式

    以下是整个处理过程的具体实现:

    3.1 数据清理

    对非中文无用数据的清理,需要将以下类别的数据从训练集和测试集中清除:

    html  = re.compile('<.*?>')
        http  = re.compile(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+')
        src   = re.compile(r'\b(?!src|href)\w+=[\'\"].*?[\'\"](?=[\s\>])')
        space = re.compile(r'[\\][n]')
        ids   = re.compile('[(]["微信"]?id:(.*?)[)]')
        wopen = re.compile('window.open[(](.*?)[)]')
        english = re.compile('[a-zA-Z]')
        others= re.compile(u'[^\u4e00-\u9fa5\u0041-\u005A\u0061-\u007A\u0030-\u0039\u3002\uFF1F\uFF01\uFF0C\u3001\uFF1B\uFF1A\u300C\u300D\u300E\u300F\u2018\u2019\u201C\u201D\uFF08\uFF09\u3014\u3015\u3010\u3011\u2014\u2026\u2013\uFF0E\u300A\u300B\u3008\u3009\!\@\#\$\%\^\&\*\(\)\-\=\[\]\{\}\\\|\;\'\:\"\,\.\/\<\>\?\/\*\+\_"\u0020]+')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    需要将html链接、数据来源、用户名、英语字符和其他单个非中文字符清除,采用以上正则表达式描述需要删除的类别,配合sub命令将其从训练集和数据集中删除。

    3.2 数据划分

    数据划分使用的库为jieba分词,具体的操作如下:

    if __name__ =='__main__':
        jieba.load_userdict('dict.txt')
        jieba.enable_parallel(2)
        print("Processing: cutting train data...")
        cut_Train_Data = cutData('Train/preprocessed_train_data.csv')
        cut_Train_Data.to_csv('Train/preprocessed_train_data.csv')
        print("Processing: cutting test data...")
        cut_Test_Data = cutData('Test/Test_DataSet_P.csv')
        cut_Test_Data.to_csv('Test/result.csv')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    我们使用的词典并非jieba的内置词典,而是用户自定义词典dict.txt,分词过程开双线程优化。

    分词函数cutData的定义:

    def cutData(filePath):
        cutData = pd.read_csv(filePath,index_col=0)
        cutData['title'] = pd.DataFrame(cutData['title'].astype(str))   
        cutData['title'] = cutData['title'].apply(lambda x: cut_char(x))
        cutData['content'] = pd.DataFrame(cutData['content'].astype(str))   
        cutData['content'] = cutData['content'].apply(lambda x: cut_char(x))
        cutData['combine'] = cutData['content']+'/'+70*(cutData['title']+'/')
        print(cutData.head())
        return cutData
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    分别对数据集的title和content进行分词,并在处理结束之后将二者重新拼接,中间用“/”隔开。在后序的实验中我们发现,标题比文章更能准确地表述新闻的情感(在不碰到UC浏览器的新闻的情况下),所以使用70次重复标题中词语的内容来加大重要性(即其出现频率)。分词方式使用jieba的全精度分词方式。

    3.3 构建词频矩阵

    经过以上两步,我们已经将文本成功分割为独立的中文词语,接下来需要统计每个词出现的频率及分布。

    stop_words=get_custom_stopwords('ChineseStopWords.txt')
    
    • 1

    首先需要获得停用词表。这里我们使用的是百度停用词表、哈工大停用词表、中文停用词表等多个词表的综合结果。

    Vectorizer = CountVectorizer( max_df = 0.8,
                                      min_df = 2,
                                      token_pattern = u'(?u)\\b[^\\d\\W]\\w+\\b',
                                      stop_words =frozenset(stop_words) 
                                      )
    
        Vectorizer_Title = CountVectorizer( max_df = 0.8,
                                      min_df = 3,
                                      token_pattern = u'(?u)\\b[^\\d\\W]\\w+\\b',
                                      stop_words =frozenset(stop_words) )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    CountVectorizer是通过fit_transform函数将文本中的词语转换为词频矩阵,矩阵元素a[i][j] 表示j词在第i个文本下的词频。即各个词语出现的次数,通过get_feature_names()可看到所有文本的关键字,通过toarray()可看到词频矩阵的结果。

    X = trainData['content'].astype('U')
        y = trainData.label
        X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=2019)
    
    test = pd.DataFrame(Vectorizer.fit_transform(X_train).toarray(), columns=Vectorizer.get_feature_names())
    
    • 1
    • 2
    • 3
    • 4
    • 5

    先对文本内容进行词频统计。值得一提的是,在后序的操作中我们相继进行了对标题的词频统计和综合标题和文本的词频统计,并针对这三个矩阵的统计结果进行了三次贝叶斯分类。

    此处需要说明的是,我们将训练集中随机选出的80%数据用于分类器的训练,剩余20%用于测试训练的结果。

    3.4 朴素贝叶斯分类

    朴素贝叶斯方法是基于贝叶斯定理的一组有监督学习算法,即“简单”地假设每对特征之间相互独立。 给定一个类别
    ­
    在这里插入图片描述

    回到原问题,考虑到我们分类的目标不是某一个词语,而是基于文本生成的词向量,于是在选择具体的分类器时我们选择了多项分布朴素贝叶斯。多项分布朴素贝叶斯的特征是分布参数由每类
    在这里插入图片描述

    中出现所有特征的计数总和。这样一来,我们可以通过一条新闻内出现各种词的频率和后验概率对该新闻进行情感分类。

    nb = MultinomialNB()
        X_train_vect = Vectorizer.fit_transform(X_train)
        nb.fit(X_train_vect, y_train)
        train_score = nb.score(X_train_vect, y_train)
        print("content train score is : ",train_score)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    接下来进行多项分布朴素贝叶斯的训练。

    Content train score反映的是分类器在训练数据上的得分情况(以分类正确的比例为参考)

    3.5 将训练数据应用到测试集上

    X_test_vect = Vectorizer.transform(X_test)
        print("content test score is : ", nb.score(X_test_vect, y_test))
    
        y_predict = nb.predict(Vectorizer.transform(X_test))
        print("content test macro f1_score:",sklearn.metrics.f1_score(y_test, y_predict, average='macro'))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Content test score反映的是分类器在其他随机选出的20%训练集数据上的得分情况(以分类正确的比例为参考)

    给出训练器在训练集中的20%测试集上的F1-score。F1-score : 2(PR)/(P+R)

    其中各符号的含义:

    • 准确率§ : TP/ (TP+FP)

    • 召回率® : TP(TP + FN)

    • 真阳性(TP): 预测为正, 实际也为正

    • 假阳性(FP): 预测为正, 实际为负

    • 假阴性(FN): 预测为负,实际为正

    • 真阴性(TN): 预测为负, 实际也为负

    from sklearn.metrics import confusion_matrix
        cm = confusion_matrix(y_test, y_predict)
        print(cm)
    
    • 1
    • 2
    • 3

    打印分类器在训练集中的20%测试集上分类的混淆矩阵。

    3.6 对目标集进行分类

    print("Apply to Test Data...")
        testData = pd.read_csv('Test/result.csv',index_col=0)
        testResult = nb.predict(Vectorizer.transform(testData['content'].astype('U')))
        testData['label_content'] = testResult
    
    • 1
    • 2
    • 3
    • 4

    四、实验结果及分析

    4.1 数据集划分方式

    数据集的划分采用朴素的随机采样方式,选取随机种子,将样本训练集的80%用来训练,剩余20%用来测试训练后分类器的性能。

    4.2 处理结果

    对新闻文本进行训练的结果

    在这里插入图片描述

    可以看出,仅对文本进行训练的效果不是很好,对训练集自身的正确分类率有79%,对保留出来的测试集的正确分类率也只有70%,F1得分为0.66。从混淆矩阵中可以看出,分类器对中立态度的分类情况不甚理想。

    对新闻标题进行训练的结果

    在这里插入图片描述

    仅对新闻标题进行训练,性能有较大提升。对训练集自身的正确分类率提高11个百分点,对保留测试集的提升也有6%,F1得分有0.012的提高,变动不大。分析混淆矩阵,发现新闻标题的分类在中性和负面情绪的分类性能有较大提升,但对正面情绪的分类效果较差(甚至和随机分类差不多)。

    对新闻文本和新闻标题联合训练的结果

    由上面两次训练的结果可知,文本分类的缺点在中性情绪,标题分类的缺点在正面情绪,所以按权重将文本和标题联合训练应该会将二者的问题避免。考虑到新闻标题的长度与新闻文本的长度相去甚远,我们采用了在数据预处理时将新闻标题多次重复出现的方式适当提高新闻标题中词语的词频(我们使用的是重复70次)。

    在这里插入图片描述

    经过修正后,对训练集自身的正确分类率达到94.5%,对保留测试集的分类达到76.3%,F1得分为0.72,达到了较为理想,平均分类水平差距较小的结果。

  • 相关阅读:
    node.js+Express框架,前端自己创建接口
    vue echarts 镂空饼图配置
    「Django秘境探险:揭开Web开发的神秘面纱」
    单片机C语言实例:23、串口通讯
    C++ -------- 智能指针
    [数据分析与可视化] 基于Python绘制简单动图
    【编程题 】 CD108 反转部分单向链表(详细注释 易懂)
    Transformers in Remote Sensing: A Survey
    单商户商城系统功能拆解36—分销应用—分销商
    面试题:linux的常用命令!!!
  • 原文地址:https://blog.csdn.net/sheziqiong/article/details/126070442