基于机器学习的新闻文本分类
Task1-数据探索分析
数据存储
由于用pandas一次性读取20w条数据显示memoryerror,内存不够,所以想到把数据存到数据库中,随用随取比较简便。把训练集20w条数据存到了mongodb数据库中。
import pandas as pd
import pymongodf = pd.read_csv(r'D:\Datawhale学习资料\15期-NLP新闻文本分类\data\train_set.csv', sep='\t')
texts_num = len(df.index) # 计算出新闻文本的总数量
print(df.columns) # 读取到训练集的列名['label','text']texts_arr = []
for i in range(texts_num):dict1 = dict()dict1['label'] = int(df.iloc[i, 0])dict1['text'] = str(df.iloc[i, 1])texts_arr.append(dict1)
print(len(texts_arr)) # 验证arr的准确性
# print(texts_arr[:5])# 建立mongodb数据库
client = pymongo.MongoClient('localhost', 27017)
db = client['newsTextClassification']
table = db['train_set']table.insert(texts_arr)
print(table.find().count())
# 关闭连接
client.close()
mongodb的数据操作详见资料,这里使用insert函数一次性把20w条数据添加到集合train_set中,注意texts_arr是列表,元素是字典型数据。
数据探索
新闻类别分布
统计新闻文本类别的分布,探究数据集的均衡性:
labels_iterator = table.find({}, {'label': 1, '_id': 0})
labels = map(lambda x: int(x['label']), labels_iterator)
labels_series = pd.Series(list(labels))
# print(texts_series.describe()) # 输出describe信息
label_counts = labels_series.groupby(labels_series.values).count() # 对series对象进行分组统计
技术知识:
- mongodb数据库的find()函数,1)第一个参数是“where”,第二个参数是显示哪些标签,注意除了“_id”,剩下的标签不能是相反的数值。2)本例中返回的是游标对象Cursor,是迭代器,元素是字典型数据。
- map()函数生成的对象是map对象,为迭代器。
- describe()函数针对的是Series对象和DataFrame对象,np.array数组没有describe()函数;
- Series对象的groupby()函数,参数需要指定为Series对象的数值
并结合pyecharts库可视化展现类别分布情况:
from pyecharts.charts import Bar, Line
from pyecharts import options as opts
b = (Bar().add_xaxis(list(label_counts.index)).add_yaxis("新闻文本类别数量", list(map(lambda x: int(x), label_counts.values)), bar_width=20)# .set_series_opts(label_opts=opts.LabelOpts(is_show=False))# .set_global_opts(# xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-15)),# title_opts=opts.TitleOpts(title="Bar-旋转X轴标签", subtitle="解决标签名字过长的问题"),.render("label_counts_graph.html")
)
注意,pyecharts库更新后,采用一键式制作图表,详细内容见中文文档:
从上图的统计结果可以看到,第0、1、2类占了大部分,这就很容易对这三类的数据过拟合,而其他类会欠拟合,是一个典型的非均衡数据集,在后续模型训练中需要考虑分类标签的权重,或者训练时采用分层抽样的方法,进行K折交叉验证,以最大程度消除局部标签过拟合的问题。
新闻文本长度(字符数目)分布
# 统计新闻文本长度分布
texts_iterator = table.find({}, {'text': 1, '_id': 0})
texts_length = map(lambda x: len(x['text'].split(' ')), texts_iterator)
texts_series = pd.Series(list(texts_length))
# print(texts_series.describe()) # 输出describe信息
length_counts = texts_series.groupby(texts_series.values).count() # 对series对象进行分组统计
知识:
- 有超长文本(5万字符以上),但这部分数据很少,所以可以通过截取的方法来进行处理。
- 中位数是676个字符,平均数是907个字符;但这是未处理过的文本长度,处理后会进一步缩减(几百个字符实在太多了,BERT最多才能处理512个字符的长度,所以要经过去停留词、去高频词等方法来减少字符)。
并结合pyecharts库制作条形图和文本数目累计占比折线图,为后续构建td-idf和词向量事设置max_feature参数提供参考:
可以看到95%以上的新闻文本长度都在3000词以内,所以大致可以限制在3000以内(在处理高频词和低频词等停用词后再进行统计会更精准)
统计每类新闻中出现最多的字符(作业2)
标点符号筛选
由于对新闻文本做了匿名处理,无法直接判断出标点符号等停用词,只能通过高文本覆盖率来筛选出可能是标点符号的字符。
Tips:对于本项目的停用词来说,应包含两部分,一是高覆盖率的字符,二是超低频次的字符,前者主要指标点符号,后者主要指无价值字符。
所以在统计出现最多的字符之前,先筛选出高覆盖率的字符,这里覆盖率从80%~100%依次进行探索:
from collection import Counter
texts_iterator = table.find({}, {'text': 1, '_id': 0})
texts_unique = list(map(lambda x: ' '.join(list(set(x['text'].split(' ')))), texts_iterator))
unique_allines = ' '.join(texts_unique)
word_count = Counter(unique_allines.split(' '))
# print(len(word_count)) # 共有6869个不重复字符
word_count = sorted(word_count.items(), key=lambda d: int(d[1]), reverse=True)
example = range(80, 100)
dict1 = {}
for i in example:stopwords = []for word in word_count:if word[1] >= int(0.01 * i * 200000):stopwords.append(word[0])else:breakdict1['{}%'.format(i)] = len(stopwords)
技术知识:
- set()函数可以直接去重得到无重复的元素集合。
- Count()函数返回的是Counter对象,类似于字典型数据。详细内容见资料:
- sorted()函数的参数设置详细见资料:
并结合pyecharts库制作折线变化图:
index = [x for x in dict1.keys()]
num = [x for x in dict1.values()]
line2 = (Line().add_xaxis(index).add_yaxis(series_name="字符个数与文本覆盖率的关系",y_axis=num,label_opts=opts.LabelOpts(is_show=False),).set_global_opts(xaxis_opts=opts.AxisOpts(type_="category"),yaxis_opts=opts.AxisOpts(name='字符个数', type_='value'))
)
可以看到覆盖率在90%以上时只有2~3个字符,并且保持了比较高的平稳性,所以选择超过90%覆盖率的这三个字符作为标点符号,分别是‘3750’、‘900’、‘648’。
知识点:其实筛选标点符号,不能只关注文本覆盖率,也要关注频次,即采用tf-idf的方法衡量字符的重要性,从重要性的角度来筛选标点符号
统计字符数目
def mostcommon_cate(cate):iterator = table.find({'label': cate})texts_cate = map(lambda x: x['text'], iterator)texts_str = ' '.join(list(texts_cate)).replace(' 3750', '').replace(' 900', '').replace(' 648', '') # 去掉停用词mostcommon_word = Counter(texts_str.split(' ')).most_common(1)return cate, mostcommon_wordindex = [int(x) for x in np.arange(14)] # 这里需要进行强制转换类型,将numpy.int64转变成int型,因为pymongo读取不能读取numpy对象
result = map(mostcommon_cate, index)
for i in result:print(i[0], i[1][0])
得到结果如下:
统计新闻文本句子数量分布(作业1)
根据假定的标点符号(通过作业2中的覆盖率探索,也可以判断出假定的三个标点符号具有较高的准确度),对新闻文本进行分割:
texts_iterator = table.find({}, {'text': 1, '_id': 0}) # 疑问:为什么当这里用上面的texts_iterator就取不到数了?
texts_map = map(lambda x: x['text'], texts_iterator)# 假定字符3750、900、648是标点符号
def sentence_count(text_str):sentence_list = re.split(' 3750 | 900 | 648 ', text_str)count = len(sentence_list)return countsentence_counts = list(map(sentence_count, texts_map))
sentence_count_series = pd.Series(sentence_counts)
print(sentence_count_series.describe())
sentence_counts_distru = sentence_count_series.groupby(sentence_count_series.values).count()
技术知识:
- 从mongodb数据库中采用find()函数取出数据后,得到的是Cursor迭代器对象,如果对Cursor进行操作后,在后续二次操作时,需要重新取数,否则数据为0??
- 单纯的str.split()函数中,只能有1个分割符号,所以采用re.split()函数,用‘|’把多个分隔符号隔开即可。
并结合pyecharts库进行可视化:
可以看到98%左右的新闻文本句子数目都在300以内。
Task 2 基于机器学习的文本分类
1 文本向量化
首先从mongodb数据库中读取出20w条训练数据集,采用TfidfVectorizer对文档进行向量化表示,代码如下:
client = pymongo.MongoClient('localhost', 27017)
db = client['newsTextClassification']
table = db['train_set']
data_all = table.find({}, {"_id": 0})
data_df = pd.DataFrame(data_all)
X_data = data_df.text
tf_idf = TfidfVectorizer(ngram_range=(1, 3), max_features=2000)
X = tf_idf.fit_transform(X_data)
注意返回的X是一个稀疏矩阵,在后续训练集拆分时需要用X.toarray()转换为数组来运算。
为了防止每次tfidf向量化耗费大量时间,将训练好的tfidf模型保存在本地,采用pickle方法:
# 把训练好的tfidf保存到本地
with open('tfidf_word.pkl', 'wb') as f:pickle.dump(X, f)
# 把本地保存的tfidf载入
X = pickle.load(open('tfidf_word.pkl', "rb"))
2 训练集拆分
这里使用了K折交叉验证的方法对训练集进行拆分,并且注意到训练集中的新闻文本是一个高度不均衡的数据集,所以在拆分时想要将训练集和验证集按照源文本中的类别比例划分,这里采用StratifiedKFold方法,来进行分层抽样:
# 训练集:验证集=4:1,并且进行5次模型训练
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=0)
X = X.toarray() # 注意需要强制转换一下
for train_index, verify_index in skf.split(X, y):X_train, y_train = X[train_index], y[train_index]X_verify, y_verify = X[verify_index], y[verify_index]
朴素贝叶斯文本分类
直接采用sklearn库中的多项式朴素贝叶斯模型进行文本分类:
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import f1_scoreclf = MultinomialNB(fit_prior=True)
clf.fit(X_train, y_train)
val_pred = clf.predict(X_verify)
f1_res = f1_score(y_verify, val_pred, average='macro')
由此得到了f1值,因为做了K折交叉验证,所以要运行5次,最后得到f1的平均值。
结果如下:
五次的f1值分别为:0.831、0.833、0.828、0.829、0.833
平均f1值为0.831.
基于机器学习的新闻文本分类相关推荐
- 【项目实战课】NLP入门第1课,人人免费可学,基于TextCNN的新闻文本分类实战...
欢迎大家来到我们的项目实战课,本期内容是<基于TextCNN的新闻文本分类实战>. 所谓项目课,就是以简单的原理回顾+详细的项目实战的模式,针对具体的某一个主题,进行代码级的实战讲解,可以 ...
- 自然语言处理(二)基于CNN的新闻文本分类
自然语言处理(二) 1.Task1 数据集探索 1.1下载数据集 1.2数据集的描述 1.3 数据的预处理 1.4 CNN卷积神经网络 1.5 训练与验证 2.IMDB 2.1下载 IMDB 数据集 ...
- 基于BERT的新闻文本分类
2017年Transformer模型横空出世,encoder-decoder的创新效果显著,2018年Google又提出了BERT预训练模型,可谓是大大推动了NLP的进步,关于transformer和 ...
- 基于 LSTM-Attention 的中文新闻文本分类
1.摘 要 经典的 LSTM 分类模型,一种是利用 LSTM 最后时刻的输出作为高一级的表示,而另一种是将所有时刻的LSTM 输出求平均作为高一级的表示.这两种表示都存在一定的缺陷,第一种缺失了前面的 ...
- 基于朴素贝叶斯和LSTM的两种新闻文本分类方法
新闻文本分类 文章目录 新闻文本分类 一.项目背景 二.数据处理与分析 三.基于机器学习的文本分类--朴素贝叶斯 1. 模型介绍 2. 代码结构 3. 结果分析 四.基于深度学习的文本分类--LSTM ...
- 2021-4月Python 机器学习——中文新闻文本标题分类
试题说明 试题说明 任务描述 基于THUCNews数据集的文本分类, THUCNews是根据新浪新闻RSS订阅频道2005~2011年间的历史数据筛选过滤生成,包含74万篇新闻文档,参赛者需要根据新闻 ...
- 【文本分类】基于BERT预训练模型的灾害推文分类方法、基于BERT和RNN的新闻文本分类对比
·阅读摘要: 两篇论文,第一篇发表于<图学学报>,<图学学报>是核心期刊:第二篇发表于<北京印刷学院学报>,<北京印刷学院学报>没有任何标签. ·参考文 ...
- 新闻文本分类之旅 机器学习
天池-零基础入门NLP 新闻文本分类 导入相关库 读入数据 文本表示 训练模型 输出上传文件 存在问题 新闻文本分类 比赛地址 文本分类的任务是将给定的文本划分到事先规定的文本类别. 赛题难度: 匿名 ...
- 新闻文本分类--任务5 基于深度学习的文本分类2
Task5 基于深度学习的文本分类2 在上一章节,我们通过FastText快速实现了基于深度学习的文本分类模型,但是这个模型并不是最优的.在本章我们将继续深入. 基于深度学习的文本分类 本章将继续学习 ...
最新文章
- 卡尔曼算法笔记---思想和实际应用物理含义的理解
- Vue-路由模式 hash 和 history
- 一张图看懂阿里云网络产品【十五】IPv6 解决方案
- c语言通讯录以及写入文件,学C三个月了,学了文件,用C语言写了个通讯录程序...
- c#3.0关于JSON简单操作的实用帮助类(泛型实现)
- java 加法 溢出_StackOverflow热帖:Java整数相加溢出怎么办?Java8一步搞定~
- 高效便捷地创建单元格数据图表
- 深度学习笔记(23) 卷积维度
- 深度之眼-科赛网二分类大赛入门之路
- 电容充放电原理图_HDI滤波电容FANOUT案例
- e的n次方要怎么用计算机计算,Excel函数公式大全,使用EXP函数计算常数e的n次方....
- 老男孩教育46期-丁温郝
- Wince6.0nbsp;s3c6410触摸屏驱动分析
- python3.8零基础入门教程_正版 Python 3.8编程快速入门 针对wan全零基础入门的读者 采用*小化安装+极简代码的教学...
- 做好产品需求文档的这十步
- Scroller简介
- php编程模块英文缩写_工作中常用的英文单词缩写
- Hadoop HDFS启动报异常:We expected txid 130043, but got txid 229381
- TB6612FNG与直流电机控制教程
- MySQL免安装版配置部署详细教程
热门文章
- 支付宝产品签约-“系统综合评估签约条件不满足”解决办法
- [编程入门]自定义函数求一元二次方程
- 笔记:for循环,物理知识煮鸡蛋
- asp+excel通用成绩查询系统 v6.8 工资查询物业费水电费查询通用哦
- 计算机专业产品开发译码,计算机专业前景可好
- ROM修改---修改CPU信息和GPU信息
- SWIFT是什么意思?
- 即时聊天表情功能的实现
- python3一键上网认证的图形化exe程序(下篇)
- 电子工程师的出路在哪里?干了两年电子工程师(硬件开发),不知道以后方向在哪里,挺迷茫!