【第一期AI夏令营丨自然语言处理】任务一:文本二分类_&&Baseline代码分析及改进

发表于 2023-10-24更新于 2023-10-24字数统计 1.6k阅读时长 11m阅读次数



一、问题分析

从论文标题、摘要作者等信息,判断该论文是否属于医学领域的文献。
针对文本分类任务,可以提供两种实践思路,一种是使用传统的特征提取方法(如TF-IDF/BOW)结合机器学习模型,另一种是使用预训练的BERT模型进行建模。使用特征提取 + 机器学习的思路步骤如下:

  1. 数据预处理 :首先,对文本数据进行预处理,包括文本清洗(如去除特殊字符、标点符号)、分词等操作。可以使用常见的NLP工具包(如NLTK或spaCy)来辅助进行预处理。
  2. 特征提取:使用TF-IDF(词频-逆文档频率)或BOW(词袋模型)方法将文本转换为向量表示。TF-IDF可以计算文本中词语的重要性,而BOW则简单地统计每个词语在文本中的出现次数。可以使用scikit-learn库的TfidfVectorizer或CountVectorizer来实现特征提取。
  3. 构建训练集和测试集:将预处理后的文本数据分割为训练集和测试集,确保数据集的样本分布均匀。
  4. 选择机器学习模型:根据实际情况选择适合的机器学习模型,如朴素贝叶斯、支持向量机(SVM)、随机森林等。这些模型在文本分类任务中表现良好。可以使用scikit-learn库中相应的分类器进行模型训练和评估。
  5. 模型训练和评估:使用训练集对选定的机器学习模型进行训练,然后使用测试集进行评估。评估指标可以选择准确率、精确率、召回率、F1值等。
  6. 调参优化:如果模型效果不理想,可以尝试调整特征提取的参数(如词频阈值、词袋大小等)或机器学习模型的参数,以获得更好的性能。

二、Baseline代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 导入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('/home/aistudio/data/data231041/train.csv')
train['title'] = train['title'].fillna('') # 缺失值填充为一个空字符串
train['abstract'] = train['abstract'].fillna('')

test = pd.read_csv('/home/aistudio/data/data231041/test.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('')+ ' ' + train['Keywords'].fillna('')


'''
vector = CountVectorizer().fit(train['text']): 它使用了 CountVectorizer 对象对文本数据进行了分词、去除停用词、统计词频等操作,将每个文本转换为一个向量。
最后,使用 fit 方法根据训练数据构建词表,并将其保存在 CountVectorizer 对象中,以便后续使用。
'''
vector = CountVectorizer().fit(train['text']) # 输出: CountVectorizer()
# print (vector)
train_vector = vector.transform(train['text']) # 每个文本转换为一个向量。这里使用了 transform 方法,是为了将训练数据转换为向量形式
print ('train_vector',type(train_vector),train_vector) # 输出结果: (0, 2345) 2 意思是 (0, 2345) 2 表示第一个文本中词表中编号为 2345 的单词出现了两次。

feature_names = vector.get_feature_names_out() # 获取词表中每个单词的名称,然后查找对应编号的单词。
print('feature_names',feature_names[2345]) # 查找2345编号的单词为access,在第一个文本中出现两次。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
'''
输出结果如下
'''
train_vector <class 'scipy.sparse._csr.csr_matrix'> (0, 1469) 1
(0, 2345) 2 # "(0, 2345) 2 "表示第一个文本中词表中编号为 2345 的单词出现了两次。
(0, 2348) 1
(0, 2349) 4
(0, 3869) 1
(0, 4268) 1
(0, 4382) 15
(0, 5112) 3
(0, 5290) 1
(0, 5505) 5
(0, 5575) 1
(0, 5585) 1
(0, 5586) 4
(0, 5891) 1
(0, 6096) 3
(0, 6106) 1
(0, 6224) 1
(0, 6545) 1
(0, 7019) 1
(0, 7233) 1
(0, 8463) 3
: :
(5999, 66915) 1
(5999, 67033) 1
feature_names access # # 查找2345编号的单词为access,在第一个文本中出现两次。

于是我去数据集中的第一个文本中进行验证发现,access确实是出现了两次(如下图所示)。

image.png

引入模型及标签预测:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
test_vector = vector.transform(test['text'])


# 引入模型
model = LogisticRegression()

# 开始训练,这里可以考虑修改默认的batch_size与epoch来取得更好的效果
model.fit(train_vector, train['label'])

# 利用模型对测试集label标签进行预测
test['label'] = model.predict(test_vector)
print("test['label']",test['label'])
print('test\n:',test)
# 生成任务一推测结果
test[['uuid', 'Keywords', 'label']].to_csv('submit_task1.csv', index=None)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
'''
输出结果如下:
'''
test['label'] 0 1
1 0
2 0
3 0
4 0
..
2353 1
2354 0
2355 0
2356 0
2357 1
Name: label, Length: 2358, dtype: int64
test:
uuid title author abstract Keywords text label
0 0 Monitoring Changes in ...
1 1 Source Printer Classification ... ... ... ... ...
2 2 Plasma-processed CoSn/RGO
3 3 Immediate Antiretroviral ...
4 4 Design and analysis of an ...
[2358 rows x 7 columns]

三、代码修改

在跑通Baseline代码后,得到的分数为:0.99384。 以下是尝试的几种修改策略。

  1. 将CountVectorizer替换为TfidfVectorizer

    未设置参数时得到的评分为:0.97655
    设置参数后得到的评分为:0.98171

1
2
3
4
5
6
7
8
# 使用TfidfVectorizer进行文本向量化
from sklearn.feature_extraction.text import TfidfVectorizer

# 定义TfidfVectorizer,设置参数,例如调整ngram范围,最大特征数等
# vector = TfidfVectorizer().fit(train['text']) # 未设置参数时得到的评分为:0.97655
vector = TfidfVectorizer(ngram_range=(1, 2), max_features=5000).fit(train['text']) # 设置参数后得到的评分为:0.98171
train_vector = vector.transform(train['text'])
test_vector = vector.transform(test['text'])
  1. 尝试使用SVM模型

    使用SVM模型得到的评分为:0.99489

1
2
3
4
5
6
7
8
from sklearn.svm import SVC

# 尝试使用SVM模型
model = SVC(kernel='linear')
model.fit(train_vector, train['label'])

# 进行预测
test['label'] = model.predict(test_vector)
  1. 尝试使用随机森林模型

    使用随机森林模型得到的评分为:0.98995

1
2
3
4
5
6
7
8
from sklearn.ensemble import RandomForestClassifier

# 尝试使用随机森林模型
model = RandomForestClassifier(n_estimators=100)
model.fit(train_vector, train['label'])

# 进行预测
test['label'] = model.predict(test_vector)

【参考】

手把手打一场NLP赛事
赛事链接:基于论文摘要的文本分类与关键词抽取挑战赛