1. 学习内容
AI夏令营第三期–基于论文摘要的文本分类与关键词抽取挑战赛教程
1.1 库
本次项目主要会用到 pandas 和 scikit-learn 库。
✅ pandas
- pandas 是 Python 语言的一个扩展程序库,用于数据分析。
- pandas 一个强大的分析结构化数据的工具集,基础是 Numpy(提供高性能的矩阵运算)。
- pandas 可以从各种文件格式比如 CSV、JSON、SQL、Microsoft Excel 导入数据。
- pandas 可以对各种数据进行运算操作,比如归并、再成形、选择,还有数据清洗和数据加工特征。
✅ sklearn
- sklearn 是一个机器学习、深度学习中非常常用的 Python 第三方库,内部封装了多种机器学习算法与数据处理算法,提供了包括数据清洗、数据预处理、建模调参、数据验证、数据可视化的全流程功能,是入门机器学习的必备工具。
- 通过使用 sklearn,可以便捷地完成机器学习的整体流程,尝试使用多种模型完成训练与预测任务,而不需要再手动实现各种机器学习算法。
1.2 特征提取
在 NLP 任务中,特征提取一般需要将自然语言文本转化为数值向量表示,常见的方法包括基于 TF-IDF(词频-逆文档频率)提取或基于 BOW(词袋模型)提取等,两种方法均在 sklearn.feature_extraction 包中有所实现。
✅ 基于 TF-IDF 提取
TF-IDF(term frequency–inverse document frequency)是一种用于信息检索与数据挖掘的常用加权技术,其中,TF 指 term frequence,即词频,指某个词在文章中出现次数与文章总词数的比值;IDF 指 inverse document frequence,即逆文档频率,指包含某个词的文档数占语料库总文档数的比例。
例如,假设语料库为 {“今天 天气 很好”,“今天 心情 很 不好”, “明天 天气 不好”},每一个句子为一个文档,则“今天”的 TF 和 IDF 分别为:
✏️:这里的文本分割是以词为单元的,而不是以字为单元的。
每个词最终的 TF-IDF 即为 TF 值乘以 IDF 值。计算出每个词的 TF-IDF 值后,使用 TF-IDF 计算得到的数值向量替代原文本即可实现基于 TF-IDF 的文本特征提取。
我们可以使用 sklearn.feature_extraction.text 中的 TfidfVectorizer 类来简单实现文档基于 TF-IDF 的特征提取:
# 首先导入该类 from sklearn.feature_extraction.text import TfidfVectorizer # 假设我们已从本地读取数据为 DataFrame 类型,并已经过基本预处理,data 为已处理的 DataFrame 数据 # 实例化一个 TfidfVectorizer 对象,并使用 fit 方法来拟合数据 vector = TfidfVectorizer().fit(data["text"]) # 拟合之后,调用 transform 方法即可得到提取后的特征数据 train_vector = vector.transform()
✅ 基于 BOW 提取
BOW(Bag of Words)是一种常用的文本表示方法,其基本思想是假定对于一个文本,忽略其词序和语法、句法,仅仅将其看做是一些词汇的集合,而文本中的每个词汇都是独立的。
简单说就是讲每篇文档都看成一个袋子(因为里面装的都是词汇,所以称为词袋,Bag of words即因此而来),然后看这个袋子里装的都是些什么词汇,将其分类。具体而言,词袋模型表示一个文本,首先会维护一个词库,词库里维护了每一个词到一个数值向量的映射关系。
例如,最简单的映射关系是独热编码,假设词库里一共有四个词,今天、天气、很、不好,那么独热编码会将四个词分别编码为:
今天——(1,0,0,0)
天气——(0,1,0,0)
很 ——(0,0,1,0)
不好——(0,0,0,1)
而使用词袋模型,就会将上述这句话编码为:
我们一般使用 sklearn.feature_extraction.text 中的 CountVectorizer 类来简单实现文档基于频数统计的 BOW 特征提取,其主要方法与 TfidfVectorizer 的主要使用方法一致:
# 首先导入该类 from sklearn.feature_extraction.text import CountVectorizer # 假设我们已从本地读取数据为 DataFrame 类型,并已经过基本预处理,data 为已处理的 DataFrame 数据 # 实例化一个 CountVectorizer 对象,并使用 fit 方法来拟合数据 vector = CountVectorizer().fit(data["text"]) # 拟合之后,调用 transform 方法即可得到提取后的特征数据 train_vector = vector.transform()
✅ 停用词
停用词(Stop Words)是自然语言处理领域的一个重要工具,通常被用来提升文本特征的质量,或者降低文本特征的维度。
当我们在使用TF-IDF或者BOW模型来表示文本时,总会遇到一些问题。在特定的NLP任务中,一些词语不能提供有价值的信息作用。这种情况在生活里也非常普遍。以本次学习任务为例,我们希望医学类的词语在特征提取时被突出,对于不是医学类词语的数据就应该考虑让他在特征提取时被淡化,同时一些日常生活中使用频率过高而普遍出现的词语,我们也应该选择忽略这些词语,以防对我们的特征提取产生干扰。
举个例子:
BOW(Sentence)= Embedding(今天) + Embedding(天气) + Embedding(很) + Embedding(不好) = (1,1,1,1)
当我们需要对这句话进行情感分类时,我们就需要突出它的情感特征,也就是我们希望 “不好” 这个词在经过BOW模型编码后的值能够大一点。
但是如果我们不使用停用词,那么 “今天天气很好还是不好” 这句话经过BOW模型编码后的值就会与上面这句话的编码高度相似,从而严重影响模型判断的结果。
为此,我们将除了情感元素的词语全部停用,也就是编码时不考虑,仅保留情感词语,也就是判断句子中 “好” 这个词出现的多还是少,很明显“好”这个词出现的情感很显然就是正向的。
对于本次任务而言,日常生活中出现的词语可能都对模型分类很难有太大帮助,例如(or,again,and)。官方对此给出了 stop.txt 文件。
利用下面所示方法读取该文件:
stops =[i.strip() for i in open(r'stop.txt',encoding='utf-8').readlines()]
读取这个文件后在使用 CountVectorizer() 方法时指定 stop_words 参数为 stops 就可以了:
vector = CountVectorizer(stop_words=stops).fit(train['text']) # vector = CountVectorizer().fit(train['text'])
1.3 划分数据集
在机器学习任务中,我们一般会有三个数据集:训练集、验证集、预测集。
- 训练集为我们训练模型的拟合数据,是我们前期提供给模型的输入;
- 验证集一般是我们自行划分出来验证模型效果以选取最优超参组合的数据集;
- 测试集是最后检验模型效果的数据集。
例如在本期竞赛任务中,比赛方提供的 test.csv 就是预测集,我们最终的任务是建立一个模型在预测集上实现较准确的预测。机器学习模型一般有很多超参数,为了选取最优的超参组合,我们一般需要多次对模型进行验证,即提供一部分数据让已训练好的模型进行预测,来找到预测准确度最高的模型。
因此,我们将比赛方提供的训练集也就是 train.csv 划分,划分为训练集和验证集。我们会使用划分出来的训练集进行模型的拟合和训练,而使用划分出来的验证集验证不同参数及不同模型的效果,来找到最优的模型及参数再在比赛方提供的预测集上预测最终结果。
划分数据集的方法有很多,基本原则是同分布采样。即我们划分出来的验证集和训练集应当是同分布的,以免验证不准确(事实上,最终的预测集也应当和训练集、验证集同分布)。此处我们使用交叉验证,即对于一个样本总量为 T 的数据集,我们一般随机采样 10%-20% (也就是 0.1T~0.2T 的样本数)作为验证集,而取其他的数据为训练集。
✅ 划分代码实现
我们可以使用 sklearn.model_selection 中的 train_test_split 函数便捷实现数据集的划分:
from sklearn.model_selection import train_test_split # 该函数将会根据给定比例将数据集划分为训练集与验证集 trian_data, eval_data = train_test_split(data, test_size = 0.2) # 参数 data 为总数据集,可以是 DataFrame 类型 # 参数 test_size 为划分验证集的占比,此处选择0.2,即划分20%样本作为验证集
1.4 机器学习模型
我们可以选择多种机器学习模型来拟合训练数据,不同的业务场景、不同的训练数据往往最优的模型也不同。常见的模型包括线性模型、逻辑回归、决策树、支持向量机、集成模型、神经网络等。
Sklearn 封装了多种机器学习模型,常见的模型都可以在 sklearn 中找到,sklearn 根据模型的类别组织在不同的包中,此处介绍几个常用包:
sklearn.linear_model:线性模型,如线性回归、逻辑回归、岭回归等 sklearn.tree:树模型,一般为决策树 sklearn.neighbors:最近邻模型,常见如 K 近邻算法 sklearn.svm:支持向量机 sklearn.ensemble:集成模型,如 AdaBoost、GBDT等
Baseline 模型使用了简单但拟合效果较好的逻辑回归模型。
✅ 逻辑回归模型
逻辑回归模型,即 Logistic Regression,实则为一个线性分类器,通过 Logistic 函数(或 Sigmoid 函数),将数据特征映射到0~1区间的一个概率值(样本属于正例的可能性),通过与 0.5 的比对得出数据所属的分类。逻辑回归的数学表达式为:
f ( z ) = 1 1 + e − z f(z)=\frac{1}{1+e^{-z}}f(z)=1+e−z1
z = w T x + w 0 z=w^{T} x+w_{0}z=wTx+w0
逻辑回归模型简单、可并行化、可解释性强,同时也往往能够取得不错的效果,是较为通用的模型。
我们可以使用 sklearn.linear_model.LogisticRegression 来调用已实现的逻辑回归模型:
# 引入模型 model = LogisticRegression() # 开始训练,这里可以考虑修改默认的batch_size与epoch来取得更好的效果 # 此处的 train_vector 是已经经过特征提取的训练数据 model.fit(train_vector, train['label']) # 利用模型对测试集label标签进行预测,此处的 test_vector 同样是已经经过特征提取的测试数据 test['label'] = model.predict(test_vector)
sklearn 提供的多种机器学习模型都封装成了类似的类,绝大部分使用方法均和上述一致,即先实例化一个模型对象,再使用 fit 函数拟合训练数据,最后使用 predict 函数预测测试数据即可。
✏️:这里的 model = LogisticRegression() 可以换成 xgb.XGBClassifier() 或者 model = SVC() 等等。
2. 实践项目
任务平台
本次夏令营的代码运行平台是百度的 AI Studio,运行结果提交至讯飞开放平台进行验证评分。
题目要求
机器通过对论文摘要等信息的理解,判断该论文是否属于医学领域的文献。
任务示例
输入:
论文信息,格式如下:
Inflammatory Breast Cancer: What to Know About This Unique, Aggressive Breast Cancer.,
[Arjun Menta, Tamer M Fouad, Anthony Lucci, Huong Le-Petross, Michael C Stauder, Wendy A Woodward, Naoto T Ueno, Bora Lim],
Inflammatory breast cancer (IBC) is a rare form of breast cancer that accounts for only 2% to 4% of all breast cancer cases. Despite its low incidence, IBC contributes to 7% to 10% of breast cancer caused mortality. Despite ongoing international efforts to formulate better diagnosis, treatment, and research, the survival of patients with IBC has not been significantly improved, and there are no therapeutic agents that specifically target IBC to date. The authors present a comprehensive overview that aims to assess the present and new management strategies of IBC.,
Breast changes; Clinical trials; Inflammatory breast cancer; Trimodality care.
输出:
是(1)
赛题数据集
训练集与测试集数据为CSV格式文件,各字段分别是标题、作者、摘要、关键词。
评价指标
本次竞赛的评价标准采用F1_score,分数越高,效果越好。
3. 实践代码
baseline
# 导入pandas用于读取表格数据 import pandas as pd # 导入BOW(词袋模型),可以选择将CountVectorizer替换为TfidfVectorizer(TF-IDF(词频-逆文档频率)),注意上下文要同时修改,亲测后者效果更佳 from sklearn.feature_extraction.text import CountVectorizer # 导入LogisticRegression回归模型 from sklearn.linear_model import LogisticRegression # 过滤警告消息 from warnings import simplefilter from sklearn.exceptions import ConvergenceWarning simplefilter("ignore", category=ConvergenceWarning) # 读取数据集 train = pd.read_csv('./基于论文摘要的文本分类与关键词抽取挑战赛公开数据/train.csv') train['title'] = train['title'].fillna('') train['abstract'] = train['abstract'].fillna('') test = pd.read_csv('./基于论文摘要的文本分类与关键词抽取挑战赛公开数据/testB.csv') test['title'] = test['title'].fillna('') test['abstract'] = test['abstract'].fillna('') # 提取文本特征,生成训练集与测试集 train['text'] = train['title'].fillna('') + ' ' + train['author'].fillna('') + ' ' + train['abstract'].fillna('')+ ' ' + train['Keywords'].fillna('') test['text'] = test['title'].fillna('') + ' ' + test['author'].fillna('') + ' ' + test['abstract'].fillna('') vector = CountVectorizer().fit(train['text']) train_vector = vector.transform(train['text']) test_vector = vector.transform(test['text']) # 引入模型 model = LogisticRegression() # 开始训练,这里可以考虑修改默认的batch_size与epoch来取得更好的效果 model.fit(train_vector, train['label']) # 利用模型对测试集label标签进行预测 test['label'] = model.predict(test_vector) test['Keywords'] = test['title'].fillna('') # 生成任务一推测结果 test[['uuid', 'Keywords', 'label']].to_csv('submit_task1.csv', index=None)
我的代码
我在跑通 baseline 的基础上,尝试用 SVM、 AdaBoost 等方法。
AdaBoost:
# 导入pandas用于读取表格数据 import pandas as pd # 导入BOW(词袋模型),可以选择将CountVectorizer替换为TfidfVectorizer(TF-IDF(词频-逆文档频率)),注意上下文要同时修改,亲测后者效果更佳 from sklearn.feature_extraction.text import CountVectorizer # 过滤警告消息 from warnings import simplefilter from sklearn.exceptions import ConvergenceWarning simplefilter("ignore", category=ConvergenceWarning) # 读取数据集 train = pd.read_csv('./data/data231041/train.csv') train['title'] = train['title'].fillna('') train['abstract'] = train['abstract'].fillna('') test = pd.read_csv('./data/data231041/testB.csv') test['title'] = test['title'].fillna('') test['abstract'] = test['abstract'].fillna('') # 提取文本特征,生成训练集与测试集 train['text'] = train['title'].fillna('') + ' ' + train['author'].fillna('') + ' ' + train['abstract'].fillna('')+ ' ' + train['Keywords'].fillna('') test['text'] = test['title'].fillna('') + ' ' + test['author'].fillna('') + ' ' + test['abstract'].fillna('') # 考虑停用词 stops =[i.strip() for i in open(r'stop.txt',encoding='utf-8').readlines()] # vector = CountVectorizer().fit(train['text']) vector = CountVectorizer(stop_words=stops).fit(train['text']) train_vector = vector.transform(train['text']) test_vector = vector.transform(test['text']) # AdaBoost from sklearn.ensemble import AdaBoostClassifier from sklearn.datasets import load_digits from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score model = AdaBoostClassifier(n_estimators=100, random_state=42) # 开始训练,这里可以考虑修改默认的batch_size与epoch来取得更好的效果 model.fit(train_vector, train['label']) # 利用模型对测试集label标签进行预测 test['label'] = model.predict(test_vector) test['Keywords'] = test['title'].fillna('') test[['uuid','Keywords','label']].to_csv('submit_task_AdaBoost.csv', index=None)
4. 实践成绩
8.14 submit_task1.csv 得分:0.67116
第一次测试是基于逻辑回归模型的,没有考虑停用词;
8.16 submit_task_LR.csv 得分:0.67435
第二次测试也是基于逻辑回归模型的,但考虑了停用词;
8.16 submit_task_SVM.csv 得分:0.6778
第三次测试是基于SVM模型的,考虑了停用词;
8.16 submit_task_AdaBoost.csv 得分:0.76263
第四次测试是基于AdaBoost模型的,考虑了停用词;
目前我只是简单的导入 sklearn 模型,没有深入地优化。
今天学习的内容有点多,在慢慢消化中…