**

搜狗新闻语料文本分类实践

**


本文作为曾经在实验室工作的少许经验,记录当初对文本分类方面的部分实践过程。文本语料来自搜狗语料库中的新闻语料,文章采用scikit-learn、gensim和jieba库提供的函数实现各类文本处理过程,最终在LR模型下取得97%~98%的分类正确率。

本文中语料来源于搜狗语料库,文章会详细的讲述对该文本库处理时需要的步骤和踩过的坑。要注意本文使用的是python 3.7版本,所有的字符编码均为UTF-8。实验前需要下载一些库,都使用pip直接安装即可。

pip install jieba
pip install gensim

另外scikit- learn的安装可以参考其他博文,这里推荐直接安装 anaconda3 ,里面自带python3环境和各种机器学习库,方便今后学习使用,而且今后也可以使用conda安装其他库。

首先对搜狗语料库的样例文件进行分析。搜狗语料库由搜狗实验室提供,我们使用搜狗新闻语料库,下载地址在:http://www.sogou.com/labs/resource/cs.php。分析语料格式时先下载迷你版分析。

具体实践时可以使用完整版或者精简版。下载后看到搜狗新闻语料的格式为:

<doc>
<url>http://gongyi.sohu.com/20120706/n347457739.shtml</url>
<docno>98590b972ad2f0ea-34913306c0bb3300</docno>
<contenttitle>深圳地铁将设立VIP头等车厢 买双倍票可享坐票</contenttitle>
<content>南都讯 记者刘凡 周昌和 任笑一 继推出日票后,深圳今后将设地铁VIP头等车厢,设坐票制。昨日,《南都METRO》创刊仪式暨2012年深港地铁圈高峰论坛上透露,在未来的11号线上将增加特色服务,满足不同消费层次的乘客的不同需求,如特设行李架的车厢和买双倍票可有座位坐的VIP车厢等。论坛上,深圳市政府副秘书长、轨道交通建设办公室主任赵鹏林透露,地铁未来的方向将分等级,满足不同层次的人的需求,提供不同层次的有针对的服务。其中包括一些档次稍微高一些的服务。“我们要让公共交通也能满足档次稍高一些的服务”。比如,尝试有座位的地铁票服务。尤其是一些远道而来的乘客,通过提供坐票服务,让乘坐地铁也能享受到非常舒适的体验。他说,这种坐票的服务有望在地铁3期上实行,将加挂2节车厢以实施花钱可买座位的服务。“我们希望轨道交通和家里开的车一样,分很多种。”赵鹏林说,比如有些地铁是“观光线”,不仅沿途的风光非常好,还能凭一张票无数次上下,如同旅游时提供的“通票服务”。再比如,设立可以放大件行李的车厢,今后通过设专门可放大件行李的座位,避免像现在放行李不太方便的现象。“未来地铁初步不仅在干线上铺设,还会在支线、城际线上去建设。”“觉得如果车费不太贵的话,还是愿意考虑的。”昨日市民黄小姐表示,尤其是从老街到机场这一段,老街站每次上下客都很多人,而如果赶上上下班高峰期,特别拥挤,要一路从老街站站到机场,40、50分钟还是挺吃力的,宁愿多花点钱也能稍微舒适一点。但是白领林先生则表示,自己每天上下班都要坐地铁,出双倍车资买坐票费用有点高。</content>
</doc>

我们做文本分类时需要样本和标签,样本数据来源在这里可以是新闻标题+新闻内容,本文中做简化仅使用新闻内容,而该新闻的类别标签则可以来源于该新闻的URL,即:gongyi.sohu.com,从该URL可以看出该新闻属于类别《公益》。这些工作在我们使用完整版时通过代码编写正则表达式来完成(和写爬虫时提取网页内容差不多)。
然后下载完整版预料,完整版文件组织形式:

于是样本提取的代码逻辑很清晰了,先逐个读入这些txt文件内容,然后正则表达匹配出URL(新闻类别)和content(新闻内容),然后根据URL将content存入不同文件夹/文件中。正则匹配表达式如下:

<url>(.*?)</url> # 匹配URL
<content>(.*?)</content>  # 匹配content

具体代码如下

SamplesGen.py

# -*- coding: utf-8 -*-
'''
该脚本用于将搜狗语料库新闻语料
转化为按照URL作为类别名、
content作为内容的txt文件存储
'''
import os
import re'''生成原始语料文件夹下文件列表'''
def listdir(path, list_name):for file in os.listdir(path):file_path = os.path.join(path, file)if os.path.isdir(file_path):listdir(file_path, list_name)else:list_name.append(file_path)'''字符数小于这个数目的content将不被保存'''
threh = 30
'''获取所有语料'''
list_name = []
listdir('SogouCS.reduced/',list_name)'''对每个语料'''
for path in list_name:print(path)file = open(path, 'rb').read().decode("utf8")'''正则匹配出url和content'''patternURL = re.compile(r'<url>(.*?)</url>', re.S)patternCtt = re.compile(r'<content>(.*?)</content>', re.S)classes = patternURL.findall(file)contents = patternCtt.findall(file)'''# 把所有内容小于30字符的文本全部过滤掉'''for i in range(contents.__len__())[::-1]:if len(contents[i]) < threh:contents.pop(i)classes.pop(i)'''把URL进一步提取出来,只提取出一级url作为类别'''for i in range(classes.__len__()):patternClass = re.compile(r'http://(.*?)/',re.S)classi = patternClass.findall(classes[i])classes[i] = classi[0]'''按照RUL作为类别保存到samples文件夹中'''for i in range(classes.__len__()):file = 'samples/' + classes[i] + '.txt'f = open(file,'a+',encoding='utf-8')f.write(contents[i]+'\n')   #加\n换行显示

使用该脚本时注意点:

  1. 该代码运行时仅能 decode UTF-8字符集,而搜狗语料库上下载的语料用记事本打开可以看出是ASCII编码方式。这里推荐可以用软件将多个语料文件转换编码为UTF-8方便今后处理。
  2. 语料库文件打开后可以看到许多内容不全、内容过少甚至为空的语料,这部分语料通过一个threshold来控制,本文中采用30,即新闻内容少于50个字符时整个新闻内容和URL都不被记录为样本。
  3. 另外内容不全的样本被剔除时不可简单地在for循环中使用pop()方法,因为 for i in range(len(vec)) 方法中使用pop方法时会导致数组 vec 的维度被减小,而 for 循环又不知道这个减小的事件,因此for还会尝试对len(vec) - 1等下标的访问,而此时该下标的数组元素已经不在,就会报出越界错误(别问我怎么知道的,写代码不小心真的要付出很大代价才能debug出来)。本文中采用for i in range(contents.len())[::-1]:从右向左取数即可。

处理后的语料被按照类别保存为txt文档:

样本已经处理完毕,接下来需要使用这些样本进行分类器的训练。实验时仅使用两个类别先搭建整体代码框架,这份代码可以稍加修改后直接改成多个类别。

(1)从samples文件夹中读取所有样本文件

这个没什么好说的,还是utf-8,直接贴代码。这里我用一个脚本保存所有想要使用的函数。
textProcess.py (辅助函数保存脚本)

'''生成原始语料文件夹下文件列表'''
def listdir(path, list_name):for file in os.listdir(path):file_path = os.path.join(path, file)if os.path.isdir(file_path):listdir(file_path, list_name)else:list_name.append(file_path)list_name = []
listdir('samples/', list_name)
for path in list_name[0:2]:print(path)file = open(path, 'rb').read().decode('utf-8').split('\n')class_count = 0for text in file:...

(2)分词
本文处理的语料均为中文,因此处理中文分词需要使用结巴分词。结巴分词的原理大致是:先使用正则表达式粗略划分,然后基于trie树高速扫描,将每个句子构造有向无环图,使用动态规划查找最大概率路径,基于词频寻找最佳切分方案,最后对于未登录的单词(词表里没有的词语),采用HMM模型,维特比算法划分。分词代码:

            content = text# 分词word_list = list(jieba.cut(content, cut_all=False))

(3)去停用词
中文中很多词语虽然在文章中大量出现,但对文章分类并没有什么实际意义。比如“只”、“的”、“应该”这样的词语,对它们的计算既浪费空间时间也可能影响最终分类结果。因此需要先建立一个词表,将样本语料分词后出现在该词表中的单词去掉。停用词表按行存储一个单词:

去停用词代码:

def get_stop_words():path = "stop_words"file = open(path, 'rb').read().decode('utf-8').split('\r\n')return set(file)def rm_stop_words(word_list):word_list = list(word_list)stop_words = get_stop_words()# 这个很重要,注意每次pop之后总长度是变化的for i in range(word_list.__len__())[::-1]:# 去停用词if word_list[i] in stop_words:word_list.pop(i)#  去数字elif word_list[i].isdigit():word_list.pop(i)return word_list

此外,如果统计词频的时候一个单词出现的次数过少,也不用统计这个词

def rm_word_freq_so_little(dictionary, freq_thred):small_freq_ids = [tokenid for tokenid, docfreq in dictionary.dfs.items() if docfreq < freq_thred ]dictionary.filter_tokens(small_freq_ids)dictionary.compactify()

这些函数都被写在textProcess.py辅助函数脚本中

(4)统计词频,生成词袋

对每个文章:

       for text in file:# 打标签class_count = class_count + 1content = text# 分词word_list = list(jieba.cut(content, cut_all=False))# 去停用词word_list = rm_stop_words(word_list)dictionary.add_documents([word_list])'''转化成词袋gensim包中的dic实际相当于一个mapdoc2bow方法,对没有出现过的词语,在dic中增加该词语如果dic中有该词语,则将该词语序号放到当前word_bow中并且统计该序号单词在该文本中出现了几次'''word_bow = dictionary.doc2bow(word_list)bow.append(word_bow)

这里用gensim提供的 dictionary ,它相当于一个map,每个单词如果出现在里面,那么就会在当前文章向量中记录该单词在词典中的序号和在该文章中的频率。生成词向量仅需要调用dictionary.doc2bow()方法即可生成。注意这里保存的是稀疏矩阵。具体格式为:

  1. 单个词向量:( 5 , 2 )
    5是该单词在dictionary中的序号为5,2是在这篇文章中出现了两次。
  2. 一篇文章矩阵: [ (5 ,2) , (3 , 1) ]
    在该文章中出现了5号单词两次,3号单词1次。

(5)生成TF-IDF矩阵
TF-IDF相比One-hot编码方式更加合理,它是根据该单词在当前文章中出现的频率和该单词在所有语料中出现的频率评估一个单词的重要性,当一个单词在这篇文章中出现的次数很多的时候,这个词语更加重要;但如果它在所有文章中出现的次数都很多,那么它就显得不那么重要。统计TF-IDF可以使用gensim提供的方法。

    # 去除过少单词 ps:可能导致维数不同rm_word_freq_so_little(dictionary,freq_thred)dictionary.save('dicsave.dict')corpora.MmCorpus.serialize('bowsave.mm', bow)tfidf_model = models.TfidfModel(corpus=bow,dictionary=dictionary)with open('tfidf_model.pkl', 'wb') as f2:pickle.dump(tfidf_model, f2)'''训练tf-idf模型'''corpus_tfidf = [tfidf_model[doc] for doc in bow]'''将gensim格式稀疏矩阵转换成可以输入scikit-learn模型格式矩阵'''data = []rows = []cols = []line_count = 0for line in corpus_tfidf:for elem in line:rows.append(line_count)cols.append(elem[0])data.append(elem[1])line_count += 1print(line_count)tfidf_matrix = csr_matrix((data,(rows,cols))).toarray()count = 0for ele in tfidf_matrix:# print(ele)# print(count)count = count + 1

注意gensim生成的数据格式和scikit-learn中模型所能接受的数据格式不同,因此需要使用csr_matrix方法将格式转换成[[0, 0, 0.1], [0.2, 0, 0]] 这样的格式。

代码运行过程中对所有生成的模型等进行保存,方便今后训练和测试时调用。可以使用pickle 进行保存,也可以使用gensim自带的方式序列化保存:

    dictionary.save('dicsave.dict')corpora.MmCorpus.serialize('bowsave.mm', bow)

(6)输入模型训练/预测

进行训练前需要生成样本的标签值,可以先统计各个类别样本的数目,如样本0数目100,样本1数目100,则生成
[ 0 , 0, 0, …, 0, 1 , 1 , … , 1]样本向量,其中0有100个,1有100个。

    # cut label 1 mil label 0'''生成labels'''labels = np.zeros(sum(labels_count) + 1)for i in range(labels_count[0]):labels[i] = 1

然后将样本划分为训练集和测试集,划分比例为8:2或7:3。有两种划分代码,一种自己写:

    '''分割训练集和测试集'''rarray=np.random.random(size=line_count)x_train = []y_train = []x_test = []y_test = []for i in range(line_count-1):if rarray[i]<0.8:x_train.append(tfidf_matrix[i,:])y_train.append(labels[i])else:x_test.append(tfidf_matrix[i,:])y_test.append(labels[i])

或者调用scikit-learn自带的划分函数:

x_train,x_test,y_train,y_test = train_test_split(tfidf_matrix,labels,test_size=0.3,random_state=0)

不知道为什么用这个自带的方法有时候会报memery error,而用自己写的方式就不会报错。

接下来直接输入model即可。这里先采用scikit-learn中的LR模型,logistic-regression 是一种线性模型:
H(x) = w x + b,计算结果输入softmax函数,将H映射到 0-1 的区间作为概率预测, 可以采用梯度下降算法进行模型训练。
调用模型的代码:

    '''LR模型分类训练'''classifier=LogisticRegression()classifier.fit(x_train, y_train)## with open('LR_model.pkl', 'wb') as f:#     pickle.dump(classifier, f)print(classification_report(y_test,classifier.predict(x_test)))

最终分类效果为:

这里贴上所有代码:

textClsfy.py:

'''
本文档负责实际读取语料库文件
训练LR模型
过程中保存词典、语料和训练后的模型
'''
import numpy as np
from sklearn.linear_model.logistic import  *
from gensim import corpora, models, similarities
import jieba
from sklearn.model_selection import train_test_split
import pickle
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from learnTextClsf.textProcess import *
from scipy.sparse import csr_matrix
from sklearn.metrics import classification_reportif __name__ == '__main__':freq_thred = 10        # 当一个单词在所有语料中出现次数小于这个阈值,那么该词语不应被计入词典中# 字典dictionary = corpora.Dictionary()# 词袋bow  = []labels_count = []list_name = []listdir('samples/', list_name)count = 0for path in list_name[0:2]:print(path)file = open(path, 'rb').read().decode('utf-8').split('\n')class_count = 0for text in file:# 打标签class_count = class_count + 1content = text# 分词word_list = list(jieba.cut(content, cut_all=False))# 去停用词word_list = rm_stop_words(word_list)dictionary.add_documents([word_list])'''转化成词袋gensim包中的dic实际相当于一个mapdoc2bow方法,对没有出现过的词语,在dic中增加该词语如果dic中有该词语,则将该词语序号放到当前word_bow中并且统计该序号单词在该文本中出现了几次'''word_bow = dictionary.doc2bow(word_list)bow.append(word_bow)labels_count.append(class_count-1)# with open('dictionary.pkl', 'wb') as f1:#     pickle.dump(dictionary, f1)# 去除过少单词 ps:可能导致维数不同rm_word_freq_so_little(dictionary,freq_thred)# dictionary.save('dicsave.dict')# corpora.MmCorpus.serialize('bowsave.mm', bow)tfidf_model = models.TfidfModel(corpus=bow,dictionary=dictionary)# with open('tfidf_model.pkl', 'wb') as f2:#     pickle.dump(tfidf_model, f2)'''训练tf-idf模型'''corpus_tfidf = [tfidf_model[doc] for doc in bow]'''将gensim格式稀疏矩阵转换成可以输入scikit-learn模型格式矩阵'''data = []rows = []cols = []line_count = 0for line in corpus_tfidf:for elem in line:rows.append(line_count)cols.append(elem[0])data.append(elem[1])line_count += 1print(line_count)tfidf_matrix = csr_matrix((data,(rows,cols))).toarray()count = 0for ele in tfidf_matrix:# print(ele)# print(count)count = count + 1# cut label 1 mil label 0'''生成labels'''labels = np.zeros(sum(labels_count) + 1)for i in range(labels_count[0]):labels[i] = 1'''分割训练集和测试集'''rarray=np.random.random(size=line_count)x_train = []y_train = []x_test = []y_test = []for i in range(line_count-1):if rarray[i]<0.8:x_train.append(tfidf_matrix[i,:])y_train.append(labels[i])else:x_test.append(tfidf_matrix[i,:])y_test.append(labels[i])# x_train,x_test,y_train,y_test = train_test_split(tfidf_matrix,labels,test_size=0.3,random_state=0)'''LR模型分类训练'''classifier=LogisticRegression()classifier.fit(x_train, y_train)## with open('LR_model.pkl', 'wb') as f:#     pickle.dump(classifier, f)print(classification_report(y_test,classifier.predict(x_test)))

搜狗新闻语料文本分类实践相关推荐

  1. 【NLP】BERT 模型与中文文本分类实践

    简介 2018年10月11日,Google发布的论文<Pre-training of Deep Bidirectional Transformers for Language Understan ...

  2. 数据挖掘:基于朴素贝叶斯分类算法的文本分类实践

    前言: 如果你想对一个陌生的文本进行分类处理,例如新闻.游戏或是编程相关类别.那么贝叶斯分类算法应该正是你所要找的了.贝叶斯分类算法是统计学中的一种分类方法,它利用概率论中的贝叶斯公式进行扩展.所以, ...

  3. 从0到1构建新闻长文本分类系统

    新闻分类系统概述 新闻分类系统,顾名思义,就是对于一片新闻或者是一片文章,进行自动的分类,例如政治,财经,娱乐等等 从技术角度讲,其实属于自然语言处理中比较经典的文本分类问题.当然在一个工业级别的分类 ...

  4. NLP实践九:HAN原理与文本分类实践

    文章目录 HAN原理 代码实践 HAN原理 参考多层注意力模型 用于文本分类的注意力模型 整个网络结构包括五个部分: 1)词序列编码器 2)基于词级的注意力层 3)句子编码器 4)基于句子级的注意力层 ...

  5. 科大讯飞NLP算法赛baseline:文本分类实践+0.79

    比赛题目:学术论文分类挑战赛 比赛链接:https://challenge.xfyun.cn/topic/info?type=academic-paper-classification&ch= ...

  6. 二分类问题:基于BERT的文本分类实践!附完整代码

    Datawhale 作者:高宝丽,Datawhale优秀学习者 寄语:Bert天生适合做分类任务.文本分类有fasttext.textcnn等多种方法,但在Bert面前,就是小巫见大巫了. 推荐评论展 ...

  7. 句子分类_Bert做新闻标题文本分类

    本文介绍一下如何使用bert_seq2seq框架很轻松的做文本分类任务-框架地址在: https://github.com/920232796/bert_seq2seq​github.com 上面还有 ...

  8. 【自然语言处理NLP】中文语料整理【情感分析、文本分类、摘要、实体分析】

    中文NLP语料整理 新闻文本分类语料 情感分析语料 实体分析语料 垃圾分类语料 个人开发在做很多NLP相关任务的时候,语料的寻找十分头疼. 有很多公开的语料,被他人收费,或要积分下载等等. 对平时开发 ...

  9. 今晚8点直播 | 详讲NLP的经典应用实践——文本分类

    文本分类问题是企业在 NLP 领域中处理文本数据时经常会遇到的一个问题,很多时候,我们需要将文本信息进行分类,或提相关的接口以供外部进行文本上传,在针对于用户所上传的文档信息就需要进行文档内容的分类, ...

  10. 免费报名 | WPS专家教你文本分类在企业中的应用实践

    文本分类问题是企业在 NLP 领域中处理文本数据时经常会遇到的一个问题,很多时候,我们需要将文本信息进行分类,或提相关的接口以供外部进行文本上传,在针对于用户所上传的文档信息就需要进行文档内容的分类, ...

最新文章

  1. 80070583类不存在_原创 | 类应该是匀称和均匀的
  2. CF思维联系–CodeForces - 222 C Reducing Fractions(数学+有技巧的枚举)
  3. 损失函数(Loss function) 和 代价函数(Cost function)
  4. Python 中argparse模块的使用
  5. 第二章 信息的表示和处理
  6. 抢鲜体验:openGauss从源码到主备
  7. redis哨兵模式原理_Redis的哨兵 (sentinal) 机制的工作原理
  8. 软件物料清单 (SBOM):从透明度理念到代码落地
  9. 适用于stuido one的虚拟贝斯手插件:UJAM Virtual Bassist ROYAL for Mac
  10. 为VMware虚拟机内安装的Ubuntu 16.04设置静态IP地址
  11. 什么是数据分析方法论
  12. IT大学生成长周报 | 第 2 期
  13. 【C#】打印机ZPL指令,打印文本,中文,条码,图片
  14. 【实践案例分享】58的商业DMP数据管理平台的架构与实践
  15. 再也不用担心MMD模型压缩包乱码啦 免费压缩软件Bandizip介绍
  16. 企业微信三方开发(三):网页授权登录
  17. 海胆状聚苯乙烯与α-氧化铁复合结构微球/聚苯乙烯/氧化石墨烯/CNTs复合微球研究方式
  18. 美团后台笔试2020-08-22
  19. 分布式数据库稳定性资料整理
  20. 爬取起点小说+mongoDB存储

热门文章

  1. 良心到难以置信的网站推(转自b站up主lks)
  2. 一文读懂电感器的原理、结构、作用及分类
  3. 【测试工具】xenu检查网站死链接工具
  4. 关于我的 “二进制部署 kubernetes 集群” 的体验
  5. Android实现推送PushService通知Notification
  6. 计算机怎么找不到视频文件格式,电脑打不开mp4格式的视频怎么办
  7. 苹果电脑在哪里改计算机id,苹果电脑改密码怎么改(教你两分钟快速解决)
  8. 计算机重装后如何添加打印机,系统重装后,电脑无法连接打印机怎么办?
  9. 猴子摘香蕉-人工智能实验的思考
  10. 大学生论文发表的费用需要多少