Task4:论文种类分类
# -*- coding: utf-8 -*-
"""
Created on Sun Jan 17 10:16:52 2021
@author: Zoey
"""
##### Task4:论文种类分类(数据建模任务),利用已有数据建模,对新论文进行类别分类
#### 4.1 导入package并读取原始数据
import os
import seaborn as sns
from bs4 import BeautifulSoup #用于爬取arxiv数据
import re #用于正则表达式,匹配字符串的模式
import requests #用于网络连接,发送网络请求,使用域名获取对应信息
import json
import pandas as pd
import matplotlib.pyplot as plt
os.chdir('D:/Zoey/datawhale_20210110')
def readArxivFile(path,columns=['id', 'submitter', 'authors', 'title', 'comments',
'journal-ref', 'doi','report-no', 'categories', 'license', 'abstract', 'versions',
'update_date', 'authors_parsed'],count=None):
'''
定义读取文件的函数
path: 文件路径
columns: 需要选择的列
count: 读取行数
'''
data = []
#使用with语句优势:1.自动关闭文件句柄;2.自动显示(处理)文件读取数据异常
with open(path, 'r') as f:
for idx, line in enumerate(f):
if idx == count:
break
d = json.loads(line)
d = {col:d[col] for col in columns}
data.append(d)
data = pd.DataFrame(data)
return data
data = readArxivFile('arxiv-metadata-oai-snapshot.json', ['id','title','categories','abstract'], 200000)
#### 4.2 对论文标题和摘要进行处理
# 拼接标题和摘要
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)
#### 4.3 对论文类别进行处理
# 多个类别,包含子分类
data['categories'] = data['categories'].apply(lambda x: x.split(' '))
# 单个类别,不包含子分类
data['categories_big'] = data['categories'].apply(lambda x: [xx.split('.')[0] for xx in x])
# 多标签分类---多编码
# 参考 https://blog.csdn.net/qq_40859560/article/details/104335719
from sklearn.preprocessing import MultiLabelBinarizer
mlb = MultiLabelBinarizer() #有多个1,而独热编码只有一个1
data_label = mlb.fit_transform(data['categories_big'].iloc[:])
data_label.shape
#### 4.4 构建文本分类模型
#### 4.4.1 思路1:TF-IDF+机器学习分类器
# 参考文献:https://www.cnblogs.com/caiyishuai/p/9511567.html
# 直接使用TF-IDF对文本提取特征,使用分类器进行分类,分类器的选择上可以使用SVM、LR、XGboost等
# TF(词频)-IDF(倒文本频率)技术可以根据字词的在文本中出现的次数和在整个语料中出现的文档频率来计算一个字词在整个语料中的重要程度。
# 它的优点是能过滤掉一些常见的却无关紧要本的词语,同时保留影响整个文本的重要字词。
# 使用TFIDF提取特征,限制最多4000个单词
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer(max_features=4000) #TfidfTransformer + CountVectorizer = TfidfVectorizer
data_tfidf = vectorizer.fit_transform(data['text'].iloc[:]) #将词频矩阵统计成TF-IDF值
vectorizer.get_feature_names() #获取词袋中所有文本关键词
data_tfidf.toarray() #查看数据结构 tfidf[i][j]表示i类文本中的tf-idf权重
# 由于这里是多标签分类,可以使用sklearn的多标签分类进行封装
# 划分训练集和验证集
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)
# 构建多标签分类模型
from sklearn.multioutput import MultiOutputClassifier
from sklearn.naive_bayes import MultinomialNB
# from sklearn.tree import DecisionTreeClassifier
# from xgboost import XGBClassifier
# clf = MultiOutputClassifier(XGBClassifier()).fit(x_train, y_train)
clf = MultiOutputClassifier(MultinomialNB()).fit(x_train, y_train)
# 验证模型精度
from sklearn.metrics import classification_report
print(classification_report(y_test, clf.predict(x_test)))
# 列表左边的一列为分类的标签名
# 右边support列为每个标签的出现次数
# 微平均值:micro average,所有数据结果的平均值
# 宏平均值:macro average,所有标签结果的平均值
# 加权平均值:weighted average,所有标签结果的加权平均值
#### 4.4.2 思路2:深度学习模型
# 思路2使用深度学习模型,单词进行词嵌入然后训练。首先按照文本划分数据集:
'''
1. Keras分词器Tokenizer
Tokenizer是一个用于向量化文本,或将文本转换为序列(即单个字词以及对应下标构成的列表,
从1算起)的类。是用来文本预处理的.
参考链接:https://blog.csdn.net/wcy23580/article/details/84885734
keras.preprocessing.text.Tokenizer(num_words=None,
filters='!"#$%&()*+,-./:;<=>?@[\]^_`{|}~\t\n',
lower=True,
split=" ",
char_level=False)
以tokenizer对象为基础,经常会使用texts_to_sequences()方法 和 序列预处理方法一起使用
2.序列预处理pad_sequences()序列填充
keras只能接受长度相同的序列输入。因此如果目前序列长度参差不齐,这时需要使用pad_sequences()。
该函数是将序列转化为经过填充以后的一个长度相同的新序列。
参考链接:https://blog.csdn.net/wcy23580/article/details/84957471
pad_sequences(sequences, maxlen=None,dtype='int32',padding='pre',truncating='pre', value=0.)
将序列填充到maxlen长度,padding取值有pre|post,value指定用何值填充的值
text = ['今天 北京 下雨 了', '我 今天 加班']
tokenizer = Tokenizer(num_words = 2)
tokenizer.fit_on_texts(text)
tokenizer.word_counts
tmp = tokenizer.texts_to_sequences(text)
tmp
'''
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(data['text'],data_label,test_size=0.2,random_state=1)
# 将数据集处理进行编码,并进行截断:
# 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) #返回最常见、出现频率最高的num_words个字词
tokens.fit_on_texts(list(x_train)+list(x_test)) #texts:要用以训练的文本列表
# tokens.word_counts #保存每个word在所有文档中出现的次数
# tokens.word_index #保存所有word对应的编号id,从1开始
# tokens.word_docs #保存每个word出现的文档的数量
# tokens.index_docs #保存word的id出现的文档的数量
x_sub_train = tokens.texts_to_sequences(x_train) #返回序列的列表,列表中每个序列对应于一段输入文本
x_sub_test = tokens.texts_to_sequences(x_test) #返回序列的列表
# maxlen:None或整数,为序列的最大长度。大于此长度的序列将被截短,小于此长度的序列将在后部填0
x_sub_train = sequence.pad_sequences(x_sub_train, maxlen=max_len) #默认在序列起始填充或截断
x_sub_test = sequence.pad_sequences(x_sub_test, maxlen=max_len)
x_sub_train.shape #(16000, 150)
x_train.shape #(16000,)
y_train.shape #(16000, 19)
x_sub_test.shape #(4000, 150)
data_label.shape #(20000, 19)
# 定义模型并完成训练
# 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 = False)(sequence_input) #前两个参数为输入维度、输出维度
x = SpatialDropout1D(0.2)(x) #与Dropout的作用类似,但它断开的是整个1D特征图,而不是单个神经元
# GRU的结构与LSTM很相似,LSTM有三个门,而GRU只有两个门且没有细胞状态,简化了LSTM的结构
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]) #将特征联合
# 二分类sigmoid,多分类softmax
preds = Dense(19, activation='softmax')(x)
model = Model(sequence_input, preds) #全连接层
# 二分类用binary_crossentropy
model.compile(loss='categorical_crossentropy', optimizer=Adam(lr=1e-3), metrics=['accuracy'])
# validation_split用于在没有提供验证集的时候,按一定比例从训练集中取出一部分作为验证集
model.fit(x_sub_train, y_train, batch_size=batch_size, validation_split=0.2, epochs=epochs)
model.fit(x_sub_train, y_train, batch_size=batch_size, validation_data=(x_sub_test,y_test), epochs=epochs)
#### 4.4.3 WordVec+深度学习分类器
# WordVec是进阶款的词向量,并通过构建深度学习分类完成分类。深度学习分类的网络结构可以选择TextCNN、TextRnn或者BiLSTM。
#### 4.4.4 思路4:Bert词向量
# Bert是高配款的词向量,具有强大的建模学习能力。
# # loc与iloc区别
# import numpy as np
# data = pd.DataFrame(np.random.randn(3,3),index=list('abc'),columns=list('dee'))
# # loc works on labels in the index. loc就根据index来索引对应的行
# # iloc works on the positions in the index (so it only takes integers) iloc是根据行号来索引
# data.iloc[0]
# data.loc['a']