一、赛题解析
【阿里云天池算法挑战赛】零基础入门NLP – 新闻文本分类-Day1-赛题理解_202xxx的博客-CSDN博客
二、数据读取与数据分析
【阿里云天池算法挑战赛】零基础入门NLP – 新闻文本分类-Day2-数据读取与数据分析_202xxx的博客-CSDN博客
三、机器学习提取文本特征方法
3.1
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]
3.2 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 CountVectorizer
corpus = [
'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()
3.3
N-gram
N-gram与Count Vectors类似,不过加入了相邻单词组合成为新的单词,并进行计数。
如果N取值为2,则句子1和句子2就变为:
句子1:我爱 爱北 北京 京天 天安 安门
句子2:我喜 喜欢 欢上 上海
3.4
TF-IDF
TF-IDF 分数由两部分组成:第一部分是
词语频率
(Term Frequency),第二部分是
逆文档频率
(Inverse Document Frequency)。其中计算语料库中文档总数除以含有该词语的文档数量,然后再取对数就是逆文档频率。
TF(t)= 该词语在当前文档出现的次数 / 当前文档中词语的总数
IDF(t)= log_e(文档总数 / 出现该词语的文档总数)
四、
基于机器学习的文本分类
使用词代模型构造3000维特征,用岭回归对特征进行分类,用f1_score计算分类的评分
# Count Vectors + RidgeClassifier
import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.linear_model import RidgeClassifier
from sklearn.metrics import f1_score
import os
data_dir = os.path.abspath("./").replace("competition", "competition_data")
train_df = pd.read_csv(os.path.join(data_dir, 'data/train_set.csv'), sep='\t', nrows=15000)
vectorizer = CountVectorizer(max_features=3000)
train_test = vectorizer.fit_transform(train_df['text'])
clf = RidgeClassifier()
clf.fit(train_test[:10000], train_df['label'].values[:10000])
val_pred = clf.predict(train_test[10000:])
print(f1_score(train_df['label'].values[10000:], val_pred, average='macro'))
# 0.74
使用TF-IDF构造3000维特征,ngram_range=(1,3)进行分词,用岭回归对特征进行分类,用f1_score计算分类的评分
# TF-IDF + RidgeClassifier
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import RidgeClassifier
from sklearn.metrics import f1_score
data_dir = os.path.abspath("./").replace("competition", "competition_data")
train_df = pd.read_csv(os.path.join(data_dir, 'data/train_set.csv'), sep='\t', nrows=15000)
tfidf = TfidfVectorizer(ngram_range=(1,3), max_features=3000)
train_test = tfidf.fit_transform(train_df['text'])
clf = RidgeClassifier()
clf.fit(train_test[:10000], train_df['label'].values[:10000])
val_pred = clf.predict(train_test[10000:])
print(f1_score(train_df['label'].values[10000:], val_pred, average='macro'))
# 0.87
四、作业思路
1. 尝试改变TF-IDF的参数,并验证精度
答:通过调整词代模型生成的训练集维度观察最后打分的变化情况以及耗时
# 作业1 调整TFIDF向量特征
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import RidgeClassifier
from sklearn.metrics import f1_score
import time
data_dir = os.path.abspath("./").replace("competition", "competition_data")
train_df = pd.read_csv(os.path.join(data_dir, 'data/train_set.csv'), sep='\t', nrows=15000)
for max_features in [30, 300, 3000, 30000]:
start = time.time()
tfidf = TfidfVectorizer(ngram_range=(1,3), max_features=max_features)
train_test = tfidf.fit_transform(train_df['text'])
clf = RidgeClassifier()
clf.fit(train_test[:10000], train_df['label'].values[:10000])
val_pred = clf.predict(train_test[10000:])
print(max_features, f1_score(train_df['label'].values[10000:], val_pred, average='macro'))
print("耗时:", str(time.time()-start))
可以看出,特征随着维度的增加,结果的精度会越高,但是耗时也会随之增加。
30 0.2189540444476877
耗时: 33.73664093017578
300 0.6718911185943914
耗时: 33.80941700935364
3000 0.8721598830546126
耗时: 35.318426847457886
30000 0.8961640609998208
耗时: 38.89586901664734
通过控制特征为度为30000,调整n-gram分词参数,观察模型评分的变化情况
# 作业1 调整TFIDF的ngram_range参数
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import RidgeClassifier
from sklearn.metrics import f1_score
import time
data_dir = os.path.abspath("./").replace("competition", "competition_data")
train_df = pd.read_csv(os.path.join(data_dir, 'data/train_set.csv'), sep='\t', nrows=15000)
for ngram_range in [1, 2, 3, 4]:
start = time.time()
tfidf = TfidfVectorizer(ngram_range=(1,ngram_range), max_features=30000)
train_test = tfidf.fit_transform(train_df['text'])
clf = RidgeClassifier()
clf.fit(train_test[:10000], train_df['label'].values[:10000])
val_pred = clf.predict(train_test[10000:])
print(ngram_range, f1_score(train_df['label'].values[10000:], val_pred, average='macro'))
print("耗时:", str(time.time()-start))
可以看出随着n-gram最大值的增加,评分逐步增加,但是耗时暴涨,并且到3之后随着n-gram最大值的增加评分变化不大。
1 0.8603325900148268
耗时: 5.810247182846069
2 0.8955102528662253
耗时: 16.896533012390137
3 0.8961640609998208
耗时: 38.89586901664734
4 0.8957080944137796
耗时: 304.3434760570526
2. 尝试使用其他机器学习模型,完成训练和验证
答:用随机森林模型替换岭回归模型进行训练
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import f1_score
import time
data_dir = os.path.abspath("./").replace("competition", "competition_data")
train_df = pd.read_csv(os.path.join(data_dir, 'data/train_set.csv'), sep='\t', nrows=15000)
start = time.time()
tfidf = TfidfVectorizer(ngram_range=(1,3), max_features=30000)
train_test = tfidf.fit_transform(train_df['text'])
#随机森林
clf = RandomForestClassifier(random_state = 100,
min_samples_split = 3,
n_estimators = 100,
oob_score = True,
verbose = 2,
class_weight = "balanced",
n_jobs = 10)#进程数
clf.fit(train_test[:10000], train_df['label'].values[:10000])
val_pred = clf.predict(train_test[10000:])
print"f1_score:", f1_score(train_df['label'].values[10000:], val_pred, average='macro'))
print("耗时:", str(time.time()-start))
随机森林耗时38秒,f1评分为0.849
f1_score: 0.8495898388161273
耗时: 38.263571977615356
切换所有数据进行训练
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import f1_score
import time
data_dir = os.path.abspath("./").replace("competition", "competition_data")
train_df = pd.read_csv(os.path.join(data_dir, 'data/train_set.csv'), sep='\t', nrows=None)
start = time.time()
tfidf = TfidfVectorizer(ngram_range=(1,3), max_features=30000)
train_test = tfidf.fit_transform(train_df['text'])
#随机森林
clf = RandomForestClassifier(random_state = 100,
min_samples_split = 3,
n_estimators = 100,
oob_score = True,
verbose = 2,
class_weight = "balanced",
n_jobs = 10)#进程数
clf.fit(train_test[:-10000], train_df['label'].values[:-10000])
val_pred = clf.predict(train_test[-10000:])
print("f1_score:", f1_score(train_df['label'].values[-10000:], val_pred, average='macro'))
print("耗时:", str(time.time()-start))
随机森林耗时1947.7秒,f1评分为0.9098
f1_score: 0.8495898388161273
耗时: 38.263571977615356
五、总结
模型优化建议,通过调整特征提取模型TF-IDF的参数,或者替换更好的特征提取模型。或者替换更好的分类器,并对分类器的参数进行调优。
六、Reference
tianchi_competition/零基础入门NLP – 新闻文本分类 at main · RxxxxR/tianchi_competition · GitHub
Datawhale零基础入门NLP赛事 – Task2 数据读取与数据分析-天池实验室-实时在线的数据分析协作工具,享受免费计算资源