1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 【天池学习赛】零基础入门NLP - 新闻文本分类

【天池学习赛】零基础入门NLP - 新闻文本分类

时间:2021-03-19 00:27:17

相关推荐

【天池学习赛】零基础入门NLP - 新闻文本分类

一、赛题描述

赛题数据为新闻文本,并按照字符级别进行匿名处理。整合划分出14个候选分类类别:财经、彩票、房产、股票、家居、教育、科技、社会、时尚、时政、体育、星座、游戏、娱乐的文本数据。

赛题任务:赛题以自然语言处理为背景,要求选手对新闻文本进行分类,这是一个典型的字符识别问题

赛题数据由以下几个部分构成:训练集20w条样本,测试集A包括5w条样本,测试集B包括5w条样本。为了预防选手人工标注测试集的情况,我们将比赛数据的文本按照字符级别进行了匿名处理。处理后的赛题训练数据如下:

在数据集中标签的对应的关系如下:

{'科技': 0, '股票': 1, '体育': 2, '娱乐': 3, '时政': 4, '社会': 5, '教育': 6, '财经': 7, '家居': 8, '游戏': 9, '房产': 10, '时尚': 11, '彩票': 12, '星座': 13}

赛题数据来源为互联网上的新闻,通过收集并匿名处理得到。因此选手可以自行进行数据分析,可以充分发挥自己的特长来完成各种特征工程,不限制使用任何外部数据和模型。

数据列使用\t进行分割,Pandas读取数据的代码如下:

train_df = pd.read_csv('../input/train_set.csv', sep='\t')

二、评测标准

评价标准为类别f1_score的均值,选手提交结果与实际测试集的类别进行对比,结果越大越好。

可以通过sklearn完成f1_score计算:

from sklearn.metrics import f1_scorey_true = [0, 1, 2, 0, 1, 2]y_pred = [0, 2, 1, 0, 0, 1]f1_score(y_true, y_pred, average='macro')

三、解题思路

赛题思路分析:赛题本质是一个文本分类问题,需要根据每句的字符进行分类。但赛题给出的数据是匿名化的,不能直接使用中文分词等操作,这个是赛题的难点。

因此本次赛题的难点是需要对匿名字符进行建模,进而完成文本分类的过程。由于文本数据是一种典型的非结构化数据,因此可能涉及到特征提取分类模型两个部分。为了减低参赛难度,我们提供了一些解题思路供大家参考:

思路1:TF-IDF + 机器学习分类器

直接使用TF-IDF对文本提取特征,并使用分类器进行分类。在分类器的选择上,可以使用SVM、LR、或者XGBoost。

思路2:FastText

FastText是入门款的词向量,利用Facebook提供的FastText工具,可以快速构建出分类器。

思路3:WordVec + 深度学习分类器

WordVec是进阶款的词向量,并通过构建深度学习分类完成分类。深度学习分类的网络结构可以选择TextCNN、TextRNN或者BiLSTM。

思路4:Bert词向量

Bert是高配款的词向量,具有强大的建模学习能力。

四、相关知识 -文本表示方法

One-hot

这里的One-hot与数据挖掘任务中的操作是一致的,即将每一个单词使用一个离散的向量表示。具体将每个字/词编码一个索引,然后根据索引进行赋值。

One-hot表示方法的例子如下:

句子1:我 爱 北 京 天 安 门句子2:我 喜 欢 上 海

首先对所有句子的字进行索引,即将每个字确定一个编号:

{'我': 1, '爱': 2, '北': 3, '京': 4, '天': 5,'安': 6, '门': 7, '喜': 8, '欢': 9, '上': 10, '海': 11}

在这里共包括11个字,因此每个字可以转换为一个11维度稀疏向量:

我:[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]爱:[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]...海:[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]

Bag of Words

Bag of Words(词袋表示),也称为Count Vectors,每个文档的字/词可以使用其出现次数来进行表示。

句子1:我 爱 北 京 天 安 门句子2:我 喜 欢 上 海

直接统计每个字出现的次数,并进行赋值:

句子1:我 爱 北 京 天 安 门转换为 [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0]句子2:我 喜 欢 上 海转换为 [1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]

在sklearn中可以直接CountVectorizer来实现这一步骤:

from sklearn.feature_extraction.text import CountVectorizercorpus = ['This is the first document.','This document is the second document.','And this is the third one.','Is this the first document?',]vectorizer = CountVectorizer()vectorizer.fit_transform(corpus).toarray()

N-gram

N-gram与Count Vectors类似,不过加入了相邻单词组合成为新的单词,并进行计数。

如果N取值为2,则句子1和句子2就变为:

句子1:我爱 爱北 北京 京天 天安 安门句子2:我喜 喜欢 欢上 上海

TF-IDF

TF-IDF 分数由两部分组成:第一部分是词语频率(Term Frequency),第二部分是逆文档频率(Inverse Document Frequency)。其中计算语料库中文档总数除以含有该词语的文档数量,然后再取对数就是逆文档频率。

TF(t)= 该词语在当前文档出现的次数 / 当前文档中词语的总数IDF(t)= log_e(文档总数 / 出现该词语的文档总数)

五、代码展示

jupyter notebook代码如下:

import pandas as pdimport numpy as npimport osfrom collections import Counterfrom sklearn.feature_extraction.text import TfidfVectorizerfrom sklearn.linear_model import RidgeClassifierfrom sklearn.metrics import f1_scoreos.getcwd()# 读取数据path='/Users/weiyi/Applications/jupyter_work/ml/datasets/news_text_classification/'train = pd.read_csv(path+'train_set.csv', sep='\t')test = pd.read_csv(path+'test_a.csv', sep='\t')# 1.数据分析此步骤我们读取了所有的训练集数据,在此我们通过数据分析希望得出以下结论:赛题数据中,新闻文本的长度是多少?赛题数据的类别分布是怎么样的,哪些类别比较多?赛题数据中,字符分布是怎么样的?# 数据探索train.head()test.head()### 1.1句子长度分析在赛题数据中每行句子的字符使用空格进行隔开,所以可以直接统计单词的个数来得到每个句子的长度。统计并如下:%pylab inlinetrain['text_len'] = train['text'].apply(lambda x: len(x.split(' ')))print(train['text_len'].describe())对新闻句子的统计可以得出,本次赛题给定的文本比较长,每个句子平均由907个字符构成,最短的句子长度为2,最长的句子长度为57921。下图将句子长度绘制了直方图,可见大部分句子的长度都几乎在2000以内。_ = plt.hist(train['text_len'], bins=200)plt.xlabel('Text length')plt.title("Histogram of text length")plt.xlim(-0.5,7000) # x轴度量范围### 1.2新闻类别分布接下来可以对数据集的类别进行分布统计,具体统计每类新闻的样本个数。train['label'].value_counts().plot(kind='bar')plt.title('News class count')plt.xlabel("category")在数据集中标签的对应的关系如下:{'科技': 0, '股票': 1, '体育': 2, '娱乐': 3, '时政': 4, '社会': 5, '教育': 6, '财经': 7, '家居': 8, '游戏': 9, '房产': 10, '时尚': 11, '彩票': 12, '星座': 13}从统计结果可以看出,赛题的数据集类别分布存在较为不均匀的情况。在训练集中科技类新闻最多,其次是股票类新闻,最少的新闻是星座新闻。### 1.3字符分布统计接下来可以统计每个字符出现的次数,首先可以将训练集中所有的句子进行拼接进而划分为字符,并统计每个字符的个数。从统计结果中可以看出,在训练集中总共包括6869个字,其中编号3750的字出现的次数最多,编号3133的字出现的次数最少。all_lines = ' '.join(list(train['text']))word_count = Counter(all_lines.split(" "))word_count# dict.items() 返回可遍历的(键, 值) 元组数组# key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。word_count = sorted(word_count.items(), key=lambda x:x[1], reverse = True) # reverse = True降序print(len(word_count))print(word_count[0])print(word_count[-1])word_count这里还可以根据字在每个句子的出现情况,反推出标点符号。下面代码统计了不同字符在句子中出现的次数(新闻次数),其中字符3750,字符900和字符648在20w条新闻的覆盖率接近99%,很有可能是标点符号。train['text_unique'] = train['text'].apply(lambda x: ' '.join(list(set(x.split(' ')))))all_lines = ' '.join(list(train['text_unique']))word_count = Counter(all_lines.split(" "))word_count = sorted(word_count.items(), key=lambda d:int(d[1]), reverse = True)print(word_count[0])print(word_count[1])print(word_count[2])# 问题1: 假设字符900是句号,分析赛题每篇新闻平均由多少个句子构成? len() 返回元素个数或字符长度train['text_juzilen'] = train['text'].apply(lambda x:len(x.split(' 900 ')))train['text_juzilen'].mean()# 问题2: 统计每类新闻中出现次数最多的字符label_text = train.groupby('label')['text'].apply(lambda x: ' '.join(list(x)))# 聚合之后为series,不能进行split操作,先转dataframnew_label_text = label_text.reset_index()new_label_textlabel_word_count = new_label_text['text'].apply(lambda x:Counter(x.split(' '))) # Series# label_word_count = sorted(label_word_count.items(), key=lambda x:x[1], reverse = True)# label_word_count### 数据分析的结论通过上述分析我们可以得出以下结论:赛题中每个新闻包含的字符个数平均为1000个,还有一些新闻字符较长;赛题中新闻类别分布不均匀,科技类新闻样本量接近4w,星座类新闻样本量不到1k;赛题总共包括7000-8000个字符;通过数据分析,我们还可以得出以下结论:每个新闻平均字符个数较多,可能需要截断;由于类别不均衡,会严重影响模型的精度;# 2.模型预测# 方法1:# ngram_range: tuple 有时候我们觉得单个的词语作为特征还不足够,能够加入一些词组更好,就可以设置这个参数,如(1,2)下面允许词表使用1个词语,或者2个词语的组合# max_feature: int 在大规模语料上训练TFIDF会得到非常多的词语,如果再使用了上一个设置加入了词组,那么我们词表的大小就会爆炸。出于时间和空间效率的考虑,可以限制最多使用多少个词语,模型会优先选取词频高的词语留下tfidf = TfidfVectorizer(ngram_range=(1,3), max_features=3000) train_test = tfidf.fit_transform(train['text'])clf = RidgeClassifier()clf.fit(train_test[:10000], train['label'].values[:10000])val_pred = clf.predict(train_test[10000:])print(f1_score(train['label'].values[10000:], val_pred, average='macro'))# 方法2: (利用网格搜索)tfidf = TfidfVectorizer(ngram_range=(1,3), max_features=3000) X_train = tfidf.fit_transform(train['text'])# 训练集、验证集划分from sklearn.model_selection import train_test_splitx_train, x_test, y_train, y_test = train_test_split(X_train, train['label'], test_size=0.2)# 模型训练clf = RidgeClassifier()clf.fit(x_train, y_train)# 模型预测,利用验证集预测y_pred = clf.predict(x_test)# F1评价函数print(f1_score(y_test, y_pred, average='macro'))

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。