前言
本次的比赛分析是基于天池阿里云的零基础入门NLP比赛。
比赛连接:https://tianchi.aliyun.com/competition/entrance/531810/information
赛题理解
赛题以新闻数据为赛题数据,数据集报名后可见并可下载。赛题数据为新闻文本,并按照字符级别进行匿名处理。整合划分出14个候选分类类别:财经、彩票、房产、股票、家居、教育、科技、社会、时尚、时政、体育、星座、游戏、娱乐的文本数据。
赛题数据由以下几个部分构成:训练集20w条样本,测试集A包括5w条样本,测试集B包括5w条样本。为了预防选手人工标注测试集的情况,我们将比赛数据的文本按照字符级别进行了匿名处理。处理后的赛题训练数据如下:
label | text |
---|---|
6 | 57 44 66 56 2 3 3 37 5 41 9 57 44 47 45 33 13 63 58 31 17 47 0 1 1 69 26 60 62 15 21 12 49 18 38 20 50 23 57 44 45 33 25 28 47 22 52 35 30 14 24 69 54 7 48 19 11 51 16 43 26 34 53 27 64 8 4 42 36 46 65 69 29 39 15 37 57 44 45 33 69 54 7 25 40 35 30 66 56 47 55 69 61 10 60 42 36 46 65 37 5 41 32 67 6 59 47 0 1 1 68 |
本次比赛的赛题数据不同于以往的一般性中文文本问题,即题目给出的文本是经过匿名处理的数据,可以认为给出的赛题数据是以及经过分词的文本数据,每一个数字就代表着一个词语.
不同于结构化问题,这里的每一个数字是经过词典匿名处理的,所以不能将其直接带入模型进行训练,而是需要经过特征工程将非结构化数据转换为结构化数据.
大体上主要思路归为:
机器学习类方法: TF-IDF, bag-of-word
深度学习类方法: wordembedding
数据探索性分析
首先读取给定的训练集与测试集数据:
df1 = pd.read_csv("train_set.csv", sep='\t')
df2 = pd.read_csv("test_a.csv", sep='\t')
训练集label分布分析:
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei'] #指定默认字体 SimHei为黑体
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
plt.xlabel("类别")
plt.ylabel("数量")
plt.hist(df1['label'], bins=100)
plt.show()
可以明显看见样本分布明显不均匀.其中类别0到类别4占绝大部分比例,样本过度的不均匀可能会导致训练模型更加偏向于数量占比大的类别,从而降低模型的精度,可以考虑处理样本不均匀的问题.
查看文章平均长度:
print(df1['text'].apply(lambda x: len(x)).describe())
plt.hist(df1['text'].apply(lambda x: len(x)), bins=1000)
plt.xlim(0,10000)
plt.show()
可以看出文章长度成的长尾分布,模型训练精度可能会被长度较高的文章影响,可以考虑进行截断.
查看文章字符:
from collections import Counter
all_lines = ' '.join(list(df1['text']))
word_count = Counter(all_lines.split(" "))
word_count = sorted(word_count.items(), key=lambda d:d[1], reverse = True)
print(word_count[0])
print(word_count[1])
print(word_count[2])
根据统计字符频率发现’3750’, ‘648’,'900’三个字符出现次数最多,可以认为这三个字符为文章标点符号.
这样就可以通过标点符号来统计文章的句子数量
plt.hist(df1['text'].apply(lambda x: len(re.split('648|900|3750', x))), bins=1000)
plt.xlim(0,650)
plt.show()
从统计结果中可以看出文章句子数量依然为长尾分布,这也完全符合上面对文字长度的分析结果,可以采用截断的方式删减部分毕竟异常的文章.
def char_max(x):
all_lines = ' '.join(list(x['text']))
word_count = Counter(all_lines.split(" "))
word_count = sorted(word_count.items(), key=lambda d:int(d[1]), reverse = True)
print(x['label'].iloc[0], word_count[0])
df1.groupby("label").apply(char_max)
同时可以通过排除三个频率最高的标点字符后统计出每一一个类别中出现次数最高的字符.
可以通过每个类别最高频率字符在文章中出现次数来构建特征.
特征工程
为了便于训练需要将文本数据进行向量化,目前选择了TF-IDF方法.
注意:在进行tf-idf转换时首先需要对训练集进行fit_transform,即在转换训练集的同时进行标准化与归一化,此时不可将测试集与训练集一起放入转换,若一起放入可能会使模型的泛化能力下降,过拟合测试集.
即在fit_transform后单独对测试集进行transform即可.
from sklearn.feature_extraction.text import TfidfVectorizer
tfidf = TfidfVectorizer()
train = tfidf.fit_transform(df1['text'])
test = tfidf.transform(df2['text'])
模型构建
模型选择了训练速度较快的LGB分类器.
import lightgbm as lgb
gbm = lgb.LGBMClassifier()
gbm.fit(x, df1['label'])
pre = gbm.predict(test)
结果分析
最终成绩为0.923