十分感谢DataWhale的开源分享!!!!
项目GitHub地址:https://github.com/datawhalechina/team-learning-data-mining/tree/master/AcademicTrends
DataWhale数据分析|Task4
任务介绍:
任务要求:
论文分类(数据建模任务),利用已有数据建模,对新论文进行类别分类
任务流程:
- 下载kaggle数据集 【Task1已完成】
- 安装所需package:seaborn(数据可视化),BeautifulSoup4(爬虫相关,用于爬取数据),requests(网络通信),json(json格式数据读取),pandas(大数据分析),matploblib(绘图)【Task1已完成】 新增package: scikit-learn、Keras
- 数据预处理
- 使用机器学习中的TF-IDF进行分类
- 使用深度学习中的LSTM进行分类
任务详解
1. 数据预处理
1) 准备分类的data数据
data['text'] = data['title'] + data['abstract']
data['text'] = data['text'].apply(lambda x: x.replace('\n',' '))
data['text'] = data['text'].apply(lambda x: x.lower())
data = data.drop(['abstract', 'title'], axis=1)
利用title和abstract即标题和摘要来进行分类。将列标题列和摘要列的数据相连,并去掉换行符和大小写差异。
2) 准备分类的label数据
# 多个类别,包含子分类
data['categories'] = data['categories'].apply(lambda x : x.split(' '))
# 单个类别,不包含子分类
data['categories_big'] = data['categories'].apply(lambda x : [xx.split('.')[0] for xx in x])
from sklearn.preprocessing import MultiLabelBinarizer
mlb = MultiLabelBinarizer()
data_label = mlb.fit_transform(data['categories_big'].iloc[:])
首先将多个分类处理为list格式,并去掉每个分类中的子分类,如math.CO里的CO,只保留一级分类。
随后利用sklearn库中的多标签二值化生成器MultiLabelBinarizer,来生成可供训练的标签数据
MultiLabelBinarizer:
语法:sklearn.preprocessing.MultiLabelBinarizer(classes=None, sparse_output=False)
如果指定classes,则等于classes参数值
如:
In [3]: from sklearn.preprocessing import MultiLabelBinarizer
...: mlb = MultiLabelBinarizer(classes = [2,3,4,5,6,1])
...: mlb.fit_transform([(1, 2), (3,4),(5,)])
...:
Out[3]:
array([[1, 0, 0, 0, 0, 1],
[0, 1, 1, 0, 0, 0],
[0, 0, 0, 1, 0, 0]])
In [4]: mlb.classes_
Out[4]: array([2, 3, 4, 5, 6, 1])
sparse_output
则是用于class种类很多但每行的种类很稀疏时使用,比如共有10万个类,但每一条数据包括不超过5个类时使用。
2. 使用机器学习中的TF-IDF进行分类
1)提取TF-IDF特征
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer(max_features=4000)
data_tfidf = vectorizer.fit_transform(data['text'].iloc[:])
TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。
- 词频(TF)表示词条(关键字)在文本中出现的频率,这个数字通常会被归一化(一般是词频除以文章总词数), 以防止它偏向长的文件。
- 逆向文件频率 (IDF) :某一特定词语的IDF,可以由总文件数目除以包含该词语的文件的数目,再将得到的商取对数得到。如果包含词条t的文档越少, IDF越大,则说明词条具有很好的类别区分能力。
- 某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF。因此,TF-IDF倾向于过滤掉常见的词语,保留重要的词语。
2)划分训练集和验证集
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(data_tfidf, data_label,
test_size = 0.2,random_state = 1)
train_test_split 随机将样本集合划分为训练集 和测试集,并返回划分好的训练集和测试集数据。
X_train,X_test, y_train, y_test = cross_validation.train_test_split(X,y,test_size, random_state)
参数 | 描述 |
---|---|
X | 待划分的样本特征集合 |
y | 待划分的样本标签 |
test_size | 若在0~1之间,为测试集样本数目与原始样本数目之比;若为整数,则是测试集样本的数目。 |
random_state | 随机数种子 |
X_train | 划分出的训练集数据(返回值) |
X_test | 划分出的测试集数据(返回值) |
y_train | 划分出的训练集标签(返回值) |
y_test | 划分出的测试集标签(返回值) |
3) 构建多标签分类模型
from sklearn.multioutput import MultiOutputClassifier
from sklearn.naive_bayes import MultinomialNB
clf = MultiOutputClassifier(MultinomialNB()).fit(x_train, y_train)
MultiOutputClassifier 多标签分类器,该策略包括为每个目标拟合一个分类器,用于扩展本地不支持多目标分类的分类器。
语法 MultiOutputClassifier(estimator, n_jobs=1)
参数 | 作用 |
---|---|
estimator | 指定预测方法 |
n_jobs | 指定用多少CPU,-1表示使用全部CPU |
MultinomialNB 离散型朴素贝叶斯
语法:sklearn.naive_bayes.MultinomialNB(alpha=1.0, fit_prior=True, class_prior=None)
参数 | 作用 |
---|---|
alpha : float, optional (default=1.0) | 平滑参数,0表示不平滑 |
fit_prior : boolean, optional (default=True) | 是否学习类的分布,如果false,则使用正态分布 |
class_prior : array-like, size (n_classes,), optional (default=None) | 类的先验概率,如果指定,则不会根据数据调整先验。 |
4) 使用训练好的模型进行预测
from sklearn.metrics import classification_report
print(classification_report(y_test, clf.predict(x_test)))
predict
函数给出了对于测试数据x_test
的标签推测,并与实际结果y_test
进行比较,·lassification_report
返回预测结果报告:
3. 使用深度学习中的LSTM进行分类
1) 划分数据集
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(data['text'].iloc[:100000],
data_label[:100000],
test_size = 0.95,random_state = 1)
由于深度学习训练速度较慢,因此使用前10万个数据,并只训练5%的数据,剩下的用于测试。
2) 设置参数
# parameter
max_features= 500
max_len= 150
embed_size=100
batch_size = 128
epochs = 5
from keras.preprocessing.text import Tokenizer
from keras.preprocessing import sequence
tokens = Tokenizer(num_words = max_features)
tokens.fit_on_texts(list(data['text'].iloc[:100000]))
y_train = data_label[:100000]
x_sub_train = tokens.texts_to_sequences(data['text'].iloc[:100000])
x_sub_train = sequence.pad_sequences(x_sub_train, maxlen=max_len)
max_features
设置最大特征长度
max_len
设数据置最大长度
embed_size
设置Embedding嵌入层的维度(见3)
Tokenizer
参数 | 作用 |
---|---|
num_words: int | 词汇表中可使用的最大词语数量。词汇表按照词语频次降序存储各词语,可使用的词语为前num_words。注意‘0’号为保留索引,它不会分配给任何词。 |
filters: string | 在读入文本时,文本中出现的属于该字符串的任意字符都将被替换为空格。默认情况下该字符串为所有英文标点符号(除了’) |
lower: bool | 如果为True,则文本中的大写字母被替换为小写字母。 |
char_level: bool | 如果为True,则按照字符进行分词。 |
oov_token: string | 如果该参数有值,则在词汇表中将为该值分配一个索引,所有未登录词都将替换为该字符串。 |
方法
fit_on_texts(texts)
基于文本集texts构建词汇表。在调用texts_to_sequences或texts_to_matrix方法之前,必须先调用该方法构建词汇表。
参数:
texts: (1)1个由字符串构成的列表,列表中的每个元素是一个字符串(表示一篇文本)(2)1个由字符串类型构成的生成器(当文本语料很大而不能通过列表直接加载到内存中时使用生成器一次读取一个文本到内存)(3)1个由字符串构成的列表的列表,此时一篇文本不再由一个字符串表示,而是由一个词语列表表示,词语列表中的每个元素是一个字符串(表示一个词语),整个语料由多个词语列表表示。这种参数针对需要经过特定分词器输出的文本集,比如中文文本集。
texts_to_sequences(texts)
将文本集中的每篇文本变换为词语索引序列。注意:只有属于词汇表中前num_words的词才被索引替换,其他词直接忽略。
参数:
texts: 同fit_on_texts方法的参数。
返回:词索引列表构成的文本集列表。
3) 构建LSTM模型并训练
# LSTM model
# Keras Layers:
from keras.layers import Dense,Input,LSTM,Bidirectional,Activation,Conv1D,GRU
from keras.layers import Dropout,Embedding,GlobalMaxPooling1D, MaxPooling1D, Add, Flatten
from keras.layers import GlobalAveragePooling1D, GlobalMaxPooling1D, concatenate, SpatialDropout1D# Keras Callback Functions:
from keras.callbacks import Callback
from keras.callbacks import EarlyStopping,ModelCheckpoint
from keras import initializers, regularizers, constraints, optimizers, layers, callbacks
from keras.models import Model
from keras.optimizers import Adam
sequence_input = Input(shape=(max_len, ))
x = Embedding(max_features, embed_size, trainable=True)(sequence_input)
x = SpatialDropout1D(0.2)(x)
x = Bidirectional(GRU(128, return_sequences=True,dropout=0.1,recurrent_dropout=0.1))(x)
x = Conv1D(64, kernel_size = 3, padding = "valid", kernel_initializer = "glorot_uniform")(x)
avg_pool = GlobalAveragePooling1D()(x)
max_pool = GlobalMaxPooling1D()(x)
x = concatenate([avg_pool, max_pool])
preds = Dense(19, activation="sigmoid")(x)
model = Model(sequence_input, preds)
model.compile(loss='binary_crossentropy',optimizer=Adam(lr=1e-3),metrics=['accuracy'])
model.fit(x_sub_train, y_train,
validation_split=0.2,
epochs=epochs)
Embedding
嵌入层,用于将正整数(索引值)转换为固定尺寸的稠密向量,该层只能用作模型中的第一层。嵌入层 Embedding
SpatialDropout1D
功能与 Dropout 相同,但它会丢弃整个 1D 的特征图而不是丢弃单个元素。如果特征图中相邻的帧是强相关的(通常是靠前的卷积层中的情况),那么常规的 dropout 将无法使激活正则化,且导致有效的学习速率降低。在这种情况下,SpatialDropout1D 将有助于提高特征图之间的独立性,应该使用它来代替 Dropout。
GRU
Bidirectional
RNN 的双向封装器,对序列进行前向和后向计算。
GRU
(Gated Recurrent Unit)由 Cho, et al. (2014) 提出,是LSTM的一种变体。GRU的结构与LSTM很相似,LSTM有三个门,而GRU只有两个门且没有细胞状态,简化了LSTM的结构。而且在许多情况下,GRU与LSTM有同样出色的结果。GRU有更少的参数,因此相对容易训练且过拟合问题要轻一点。
GlobalAveragePooling1D
全局平均池化
GlobalMaxPooling1D
全局最大池化
Dense
全连接层
实验结果
参考文献:
[1], sklearn.preprocessing.MultiLabelBinarizer
[2]. TF-IDF算法介绍及实现
[2]. Sklearn的train_test_split用法
[3]. sklearn.multioutput.MultiOutputClassifier
[4]. sklearn-MultinomialNB朴素贝叶斯分类器
[5]. keras-Tokenizer类
[6]. keras中文文档