任务一:文献领域分类
Baseline中我们选择使用BOW将文本转换为向量表示,选择逻辑回归模型来完成训练和评估 代码演示如下:
# 导入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('./基于论文摘要的文本分类与关键词抽取挑战赛公开数据/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'])
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[['uuid', 'Keywords', 'label']].to_csv('submit_task1.csv', index=None)
BOW模型
BOW(Bag-of-Words)模型是一种常用的文本表示方法,用于将文本数据转换为数值向量形式,以便用于机器学习算法的输入。BOW模型将文本数据视为一个"袋子",忽略了文本中的词语顺序,只关注每个词语在文本中出现的频次或者出现与否。BOW模型的基本思想如下:
-
创建词汇表:首先,收集文本数据集中所有的词语,形成一个词汇表。词汇表中的每个词语都将被视为一个特征。
-
统计词频:对于每个文本样本,统计词汇表中的每个词语在文本中出现的频次。得到一个向量,向量的每个元素表示对应词语在文本中的出现次数或频率。
-
构建特征向量:将每个文本样本转换为一个特征向量,向量的长度等于词汇表的大小。每个元素表示对应词语在文本中的频次或频率。
BOW模型的主要特点是它是一种无序的、稀疏的向量表示方法。由于考虑了词语的频次或出现与否,而忽略了词语的顺序,所以BOW模型可以简化文本处理过程,并且在一些情况下表现得非常有效。然而,BOW模型忽略了词语的顺序信息,因此在某些任务中可能丢失了一些重要的语义和上下文信息。
在BOW模型的基础上,还可以使用TF-IDF(Term Frequency-Inverse Document Frequency)来进一步加权,以更好地表示词语的重要性。
TF-IDF模型
TF-IDF(Term Frequency-Inverse Document Frequency)是一种常用的文本特征提取方法,用于将文本数据转换为数值向量形式,以便用于机器学习算法的输入。TF-IDF综合考虑了词频(TF)和逆文档频率(IDF)两个因素,用于衡量一个词语在文本中的重要性。
TF(词频)是指在一个文本中某个词语出现的频次,它可以用下面的公式表示:
TF(w, d) = (词语w在文本d中出现的次数) / (文本d中所有词语的总数)
IDF(逆文档频率)是指在整个文本数据集中,某个词语在不同文本中的出现程度,它可以用下面的公式表示:
IDF(w) = log((文本数据集中的文本总数) / (包含词语w的文本数 + 1))
其中,分母中的“+1”是为了避免分母为0的情况。
TF-IDF的计算方式是将词频(TF)和逆文档频率(IDF)相乘,得到一个词语在文本中的TF-IDF值。它可以用下面的公式表示:
TF-IDF(w, d) = TF(w, d) * IDF(w)
TF-IDF模型的特点是它能够突出某个词语在当前文本中的重要性,并降低那些在整个文本数据集中普遍出现的词语的权重。这样,TF-IDF模型可以更好地捕捉词语的特征,有助于提高文本特征的区分性,同时减少对文本长度的依赖。
LogisticRegression回归模型
Logistic Regression(逻辑回归)是一种用于解决二分类问题的统计学习算法。尽管名字中包含"回归",但实际上它是一种分类算法,用于预测两个离散的类别(例如,正类和负类、是与否等)。Logistic Regression可以用于二分类问题,也可以通过一对多(One-vs-Rest)方式扩展到多分类问题。
逻辑回归的基本原理如下:
假设有一个特征向量 x,逻辑回归将其与权重向量 w 相乘,并加上偏置 b(也称为截距)得到线性输出 z: z = w^T * x + b
然后,将线性输出 z 应用于逻辑函数(也称为sigmoid函数),将其映射到概率值 p,用于预测样本属于某个类别的概率: p = sigmoid(z) = 1 / (1 + exp(-z))
sigmoid函数将z映射到(0, 1)之间,使得我们可以将其解释为概率值。对于二分类问题,通常规定当 p ≥ 0.5 时,样本属于正类,否则属于负类。
逻辑回归的目标是通过训练数据集,找到最佳的权重向量 w 和偏置 b,使得预测结果尽可能接近真实标签。通常使用最大似然估计(Maximum Likelihood Estimation,MLE)或梯度下降等优化方法来优化模型参数。
逻辑回归的优点包括计算效率高、易于实现和理解,特别适用于线性可分的二分类问题。然而,它在处理复杂的非线性问题时性能可能较差,对于多分类问题,需要扩展到一对多的方式。
from warnings import simplefilter
from sklearn.exceptions import ConvergenceWarning
simplefilter("ignore", category=ConvergenceWarning)
这段代码是用于在Python中使用Scikit-learn库进行机器学习任务时,抑制特定警告的代码片段。
在这段代码中,使用from warnings import simplefilter
导入simplefilter
函数,以及from sklearn.exceptions import ConvergenceWarning
导入ConvergenceWarning
类。
simplefilter
函数是Python中用于设置警告过滤器的函数,它可以让我们控制是否忽略或显示某些类型的警告。在这里,使用simplefilter
函数设置过滤器,以忽略来自Scikit-learn库中的ConvergenceWarning
类型的警告。
ConvergenceWarning
是一种警告类型,在使用Scikit-learn中的某些机器学习算法进行训练时可能会遇到。它表示算法可能在训练过程中没有收敛到最优解,或者需要更多的迭代次数才能达到收敛。尽管这是一个警告而不是错误,但有时可能会出现在迭代次数较少或收敛条件不严格时。
通过将simplefilter
函数的第一个参数设置为"ignore",并将第二个参数设置为ConvergenceWarning
,就可以忽略这类警告。这意味着当代码执行时,如果遇到ConvergenceWarning
类型的警告,将不会显示在屏幕上,从而避免干扰输出和执行结果。
train['title'] = train['title'].fillna('')
在这行代码中,train['title']
表示选取DataFrame train
中的'title'列,然后调用fillna('')
,将其中的缺失值用空字符串''进行填充。
vector = CountVectorizer().fit(train['text'])
CountVectorizer是Scikit-learn(sklearn)中的一个文本特征提取器,它用于将文本数据转换为文本频率矩阵(Term Frequency Matrix),也称为词频矩阵。在这个矩阵中,每个文档(样本)的每个单词(特征)在文档中出现的次数将被记录下来。
下面逐步解释这行代码:
-
CountVectorizer()
:这是创建CountVectorizer对象的构造函数调用。通过不传入任何参数,表示使用默认的参数配置来初始化CountVectorizer对象。 -
fit(train['text'])
:这是对CountVectorizer对象进行拟合(fit)的过程。train['text']
表示输入的文本数据,通常是一个包含多个文本文档的列表或Series
拟合的过程
- 构建词汇表:根据输入的文本数据,统计所有文档中出现的单词,并建立一个词汇表,用于映射每个单词到一个唯一的整数索引。
- 统计词频:计算每个文档中每个单词的出现次数,形成文本频率矩阵。
train_vector = vector.transform(train['text'])
在这行代码中,vector
是一个已经拟合(fit)过的CountVectorizer对象,而train['text']
是训练数据集中的文本数据。
vector.transform(train['text'])
的作用是将训练数据集中的文本数据转换为文本频率矩阵(Term Frequency Matrix)。文本频率矩阵是一个稀疏矩阵,其中每行代表一个文档(样本),每列表示一个在词汇表中的单词(特征),而矩阵中的元素表示对应文档中对应单词出现的次数。
拟合和转换的区别
在机器学习和文本处理中,"拟合"和"转换"是两个关键的步骤,它们通常在特征提取和数据预处理过程中使用。
-
拟合(Fit):拟合指的是使用训练数据来学习特征提取器(例如CountVectorizer、TfidfVectorizer等)的内部参数或统计信息。在拟合过程中,特征提取器会分析训练数据,并根据数据的特性建立相应的模型或统计量。对于文本特征提取器(如CountVectorizer),拟合过程将建立词汇表并统计每个文档中每个单词的出现次数。
-
转换(Transform):转换指的是使用已经拟合过的特征提取器(例如CountVectorizer、TfidfVectorizer等)来将新的数据(训练数据或测试数据)转换为特征向量或特征矩阵。在转换过程中,特征提取器将根据之前学习的模型或统计信息,对新的数据进行处理并提取相应的特征。对于文本特征提取器,转换过程将根据之前建立的词汇表和词频信息,将文本数据转换为文本频率矩阵(或TF-IDF矩阵)。
在文本处理中,通常的做法是先对训练数据进行拟合,然后再使用已经拟合过的特征提取器对训练数据和测试数据进行转换。拟合和转换是两个独立的过程,拟合只需要在训练数据上进行一次,而转换可以在训练数据和测试数据上多次进行。
任务二:关键词提取
# 引入分词器
from nltk import word_tokenize, ngrams
# 定义停用词,去掉出现较多,但对文章不关键的词语
stops = [
'will', 'can', "couldn't", 'same', 'own', "needn't", 'between', "shan't", 'very',
'so', 'over', 'in', 'have', 'the', 's', 'didn', 'few', 'should', 'of', 'that',
'don', 'weren', 'into', "mustn't", 'other', 'from', "she's", 'hasn', "you're",
'ain', 'ours', 'them', 'he', 'hers', 'up', 'below', 'won', 'out', 'through',
'than', 'this', 'who', "you've", 'on', 'how', 'more', 'being', 'any', 'no',
'mightn', 'for', 'again', 'nor', 'there', 'him', 'was', 'y', 'too', 'now',
'whom', 'an', 've', 'or', 'itself', 'is', 'all', "hasn't", 'been', 'themselves',
'wouldn', 'its', 'had', "should've", 'it', "you'll", 'are', 'be', 'when', "hadn't",
"that'll", 'what', 'while', 'above', 'such', 'we', 't', 'my', 'd', 'i', 'me',
'at', 'after', 'am', 'against', 'further', 'just', 'isn', 'haven', 'down',
"isn't", "wouldn't", 'some', "didn't", 'ourselves', 'their', 'theirs', 'both',
're', 'her', 'ma', 'before', "don't", 'having', 'where', 'shouldn', 'under',
'if', 'as', 'myself', 'needn', 'these', 'you', 'with', 'yourself', 'those',
'each', 'herself', 'off', 'to', 'not', 'm', "it's", 'does', "weren't", "aren't",
'were', 'aren', 'by', 'doesn', 'himself', 'wasn', "you'd", 'once', 'because', 'yours',
'has', "mightn't", 'they', 'll', "haven't", 'but', 'couldn', 'a', 'do', 'hadn',
"doesn't", 'your', 'she', 'yourselves', 'o', 'our', 'here', 'and', 'his', 'most',
'about', 'shan', "wasn't", 'then', 'only', 'mustn', 'doing', 'during', 'why',
"won't", 'until', 'did', "shouldn't", 'which'
]
# 定义方法按照词频筛选关键词
def extract_keywords_by_freq(title, abstract):
ngrams_count = list(ngrams(word_tokenize(title.lower()), 2)) + list(ngrams(word_tokenize(abstract.lower()), 2))
ngrams_count = pd.DataFrame(ngrams_count)
ngrams_count = ngrams_count[~ngrams_count[0].isin(stops)]
ngrams_count = ngrams_count[~ngrams_count[1].isin(stops)]
ngrams_count = ngrams_count[ngrams_count[0].apply(len) > 3]
ngrams_count = ngrams_count[ngrams_count[1].apply(len) > 3]
ngrams_count['phrase'] = ngrams_count[0] + ' ' + ngrams_count[1]
ngrams_count = ngrams_count['phrase'].value_counts()
ngrams_count = ngrams_count[ngrams_count > 1]
return list(ngrams_count.index)[:5]
## 对测试集提取关键词
test_words = []
for row in test.iterrows():
# 读取第每一行数据的标题与摘要并提取关键词
prediction_keywords = extract_keywords_by_freq(row[1].title, row[1].abstract)
# 利用文章标题进一步提取关键词
prediction_keywords = [x.title() for x in prediction_keywords]
# 如果未能提取到关键词
if len(prediction_keywords) == 0:
prediction_keywords = ['A', 'B']
test_words.append('; '.join(prediction_keywords))
test['Keywords'] = test_words
test[['uuid', 'Keywords', 'label']].to_csv('submit_task2.csv', index=None)
nltk库
NLTK(Natural Language Toolkit)是一个在Python中广泛使用的自然语言处理(NLP)库。它提供了各种用于处理文本数据的功能和工具,涵盖了词汇处理、文本分类、分词、词性标注、命名实体识别、情感分析等各种NLP任务。
ngrams_count = list(ngrams(word_tokenize(title.lower()), 2)) + list(ngrams(word_tokenize(abstract.lower()), 2))
这行代码是使用NLTK库中的ngrams
函数来生成文本中的二元词组(bigrams)列表。代码的目的是将标题(title)和摘要(abstract)中的单词组合成二元词组,并将这些二元词组存储在ngrams_count
列表中。
以下是对代码的解释:
-
word_tokenize(title.lower())
和word_tokenize(abstract.lower())
:word_tokenize
函数用于将文本转换成单词(tokens)列表,它会将输入的文本拆分成单个的单词,并返回一个包含这些单词的列表。title.lower()
和abstract.lower()
是将标题和摘要中的文本转换为小写字母,以便在计算二元词组时不区分大小写。 -
ngrams(word_tokenize(title.lower()), 2)
和ngrams(word_tokenize(abstract.lower()), 2)
:ngrams
函数用于生成指定大小的n元词组,其中第一个参数是单词列表,第二个参数是n,表示生成的n元词组的大小。在这里,我们传入单词列表和n=2,表示生成二元词组(bigrams)。 -
list(ngrams(word_tokenize(title.lower()), 2))
和list(ngrams(word_tokenize(abstract.lower()), 2))
: 由于ngrams
函数返回的是一个生成器(generator),为了方便使用和操作,我们将其转换为列表类型,并分别存储为ngrams_count
列表中。 -
ngrams_count = list(ngrams(word_tokenize(title.lower()), 2)) + list(ngrams(word_tokenize(abstract.lower()), 2))
: 将标题和摘要的二元词组列表合并为一个单独的ngrams_count
列表。这样,ngrams_count
列表中存储了标题和摘要中所有的二元词组。
ngrams_count = pd.DataFrame(ngrams_count)
这行代码将ngrams_count
列表转换为Pandas的DataFrame(数据帧)对象,以便进行后续的数据处理和分析。Pandas是一个流行的Python库,用于处理和分析结构化数据。
以下是对代码的解释:
-
pd.DataFrame(ngrams_count)
: 这里使用pd.DataFrame()
函数将ngrams_count
列表转换为Pandas的DataFrame对象。 -
DataFrame对象: DataFrame是Pandas库中的主要数据结构之一,它类似于电子表格或SQL中的数据库表,是一个二维数据结构,可以包含多个行和列。在DataFrame中,每列可以是不同的数据类型,并且每一列都有自己的列名。
-
数据转换: 在这个代码中,
ngrams_count
列表是一个存储了二元词组的Python列表,转换为DataFrame后,每个二元词组会被放入DataFrame的一个单独的列中。 -
数据索引: DataFrame对象还会自动为每行分配一个索引,用于唯一标识每一行的数据。索引在默认情况下是从0开始的整数序列。
-
列名: DataFrame的每一列都会有一个列名,但在这个代码中,由于二元词组只有一个列,因此这个列名不会被指定,通常在数据处理时可以为DataFrame的列分配有意义的列名。
ngrams_count = ngrams_count[~ngrams_count[0].isin(stops)]
这行代码是用来过滤掉DataFrame中包含在停用词列表(stops
)中的二元词组(bigrams)。
以下是对代码的解释:
-
ngrams_count[0]
: 这里ngrams_count[0]
表示DataFrame中的第一列,也就是二元词组的第一个词。 -
ngrams_count[0].isin(stops)
: 这个表达式是用来判断DataFrame中的每个二元词组是否包含在停用词列表(stops
)中,返回一个布尔类型的Series,其中元素为True表示该二元词组在停用词列表中,False表示不在。 -
~ngrams_count[0].isin(stops)
:~
符号用来对上述表达式的结果取反,即对应位置的True变为False,False变为True。所以,这个表达式返回的是一个布尔类型的Series,其中元素为True表示该二元词组不在停用词列表中,False表示在。 -
ngrams_count[~ngrams_count[0].isin(stops)]
: 最后,利用上述布尔类型的Series,我们可以对DataFrame进行过滤操作,保留不在停用词列表中的二元词组。这样,该行代码返回一个新的DataFrame,其中仅包含不在停用词列表中的二元词组。
ngrams_count = ngrams_count[~ngrams_count[1].isin(stops)]
对于二元词组的第二个词进行同上操作。
ngrams_count = ngrams_count[ngrams_count[0].apply(len) > 3]
这行代码是用来过滤掉DataFrame中第一列(包含二元词组)中长度小于等于3的二元词组。
以下是对代码的解释:
-
ngrams_count[0]
: 这里ngrams_count[0]
表示DataFrame中的第一列,也就是包含二元词组的列。 -
ngrams_count[0].apply(len)
: 这个表达式使用apply()
函数对DataFrame中的每个二元词组进行操作,len
函数用于计算每个二元词组的长度(由包含的单词数量决定),返回一个Series,其中元素是每个二元词组的长度。 -
ngrams_count[0].apply(len) > 3
: 这个表达式对上述Series进行逻辑比较,判断每个二元词组的长度是否大于3,返回一个布尔类型的Series,其中元素为True表示该二元词组的长度大于3,False表示长度小于等于3。 -
ngrams_count[ngrams_count[0].apply(len) > 3]
: 最后,利用上述布尔类型的Series,我们可以对DataFrame进行过滤操作,保留长度大于3的二元词组。这样,该行代码返回一个新的DataFrame,其中只包含长度大于3的二元词组。
该行代码用来过滤掉DataFrame中包含长度小于等于3的二元词组,从而得到一个新的DataFrame,其中只包含长度大于3的二元词组。这个操作是为了去除过短或无意义的二元词组,以便在后续的文本分析和处理中更专注于重要的词汇。
ngrams_count['phrase'] = ngrams_count[0] + ' ' + ngrams_count[1]
这行代码用于在DataFrame中创建一个新的列phrase
,将DataFrame中的两列合并成一个包含完整二元词组的新列。
以下是对代码的解释:
-
ngrams_count[0]
和ngrams_count[1]
: 这两部分分别代表DataFrame中的第一列和第二列。在这个代码中,第一列包含了二元词组的第一个词,第二列包含了二元词组的第二个词。 -
ngrams_count[0] + ' ' + ngrams_count[1]
: 这个表达式用于将DataFrame中的第一列和第二列进行合并。+
符号用于连接两列的元素,而中间的空格用于分隔二元词组的两个单词。 -
ngrams_count['phrase']
: 这部分表示创建一个新的列名为phrase
。 -
ngrams_count['phrase'] = ngrams_count[0] + ' ' + ngrams_count[1]
: 将合并后的二元词组存储在新创建的phrase
列中。
ngrams_count = ngrams_count['phrase'].value_counts()
这行代码用于统计DataFrame中phrase
列中不同二元词组出现的频次,并将结果按照频次降序排序。
以下是对代码的解释:
-
ngrams_count['phrase']
: 这部分表示选择DataFrame中的phrase
列。 -
ngrams_count['phrase'].value_counts()
: 这个表达式是Pandas的value_counts()
函数,它用于统计phrase
列中每个不同的二元词组出现的频次,并返回一个新的Series,其中索引为不同的二元词组,值为对应的频次。 -
ngrams_count = ngrams_count['phrase'].value_counts()
: 将统计得到的频次Series重新赋值给ngrams_count
,此时ngrams_count
变为一个按频次降序排列的Series。
return list(ngrams_count.index)[:5]
这行代码是将前五个二元词组的词组内容从ngrams_count
Series 中提取出来并以列表的形式返回。
以下是对代码的解释:
-
ngrams_count.index
:ngrams_count
是一个Series对象,其中索引为不同的二元词组,而值为对应的频次。ngrams_count.index
表示提取ngrams_count
Series 的索引,即不同的二元词组。 -
list(ngrams_count.index)
: 这个部分将ngrams_count.index
转换成Python列表,其中包含了所有不同的二元词组。 -
[:5]
: 这个切片操作是将列表中的前五个二元词组提取出来。 -
return list(ngrams_count.index)[:5]
: 最终,return
语句将前五个二元词组作为一个列表返回。这样,函数extract_keywords_by_freq
会返回频率最高的前五个二元词组,作为提取出来的关键词列表。