第四节:论文种类分类-学习笔记

2023-11-04

任务说明

  • 学习主题:论文分类(数据建模任务),利用已有数据建模,对新论文进行类别分类;
  • 学习内容:使用论文标题完成类别分类;
  • 学习成果:学会文本分类的基本方法、TF-IDF等;

数据处理步骤

在原始arxiv论文中论文都有对应的类别,而论文类别是作者填写的。在本次任务中我们可以借助论文的标题和摘要完成:

  • 对论文标题和摘要进行处理;
  • 对论文类别进行处理;
  • 构建文本分类模型;

文本分类思路

  • 思路1:TF-IDF+机器学习分类器

直接使用TF-IDF对文本提取特征,使用分类器进行分类,分类器的选择上可以使用SVM、LR、XGboost等

  • 思路2:FastText

FastText是入门款的词向量,利用Facebook提供的FastText工具,可以快速构建分类器

  • 思路3:WordVec+深度学习分类器

WordVec是进阶款的词向量,并通过构建深度学习分类完成分类。深度学习分类的网络结构可以选择TextCNN、TextRnn或者BiLSTM。

  • 思路4:Bert词向量

Bert是高配款的词向量,具有强大的建模学习能力。

具体代码实现以及讲解

为了方便大家入门文本分类,我们选择思路1和思路2给大家讲解。首先完成字段读取:

# 导入所需的package
import seaborn as sns #用于画图
from bs4 import BeautifulSoup #用于爬取arxiv的数据
import re #用于正则表达式,匹配字符串的模式
import requests #用于网络连接,发送网络请求,使用域名获取对应信息
import json #读取数据,我们的数据为json格式的
import pandas as pd #数据处理,数据分析
import matplotlib.pyplot as plt #画图工具
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 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)
data.head()
id title categories abstract
0 0704.0001 Calculation of prompt diphoton production cros... hep-ph A fully differential calculation in perturba...
1 0704.0002 Sparsity-certifying Graph Decompositions math.CO cs.CG We describe a new algorithm, the $(k,\ell)$-...
2 0704.0003 The evolution of the Earth-Moon system based o... physics.gen-ph The evolution of Earth-Moon system is descri...
3 0704.0004 A determinant of Stirling cycle numbers counts... math.CO We show that a determinant of Stirling cycle...
4 0704.0005 From dyadic $\Lambda_{\alpha}$ to $\Lambda_{\a... math.CA math.FA In this paper we show how to compute the $\L...

为了方便数据的处理,我们可以将标题和摘要拼接一起完成分类。

# 拼接标题和摘要
data['text'] = data['title'] + data['abstract']
data.head()
# 如果某一列是非str类型的数据,那么需用map(str)将那列数据类型做转换
# df["newColumn"] = df["trans_cd"].map(str) + df["trans_class"]
id title categories abstract text
0 0704.0001 Calculation of prompt diphoton production cros... hep-ph A fully differential calculation in perturba... Calculation of prompt diphoton production cros...
1 0704.0002 Sparsity-certifying Graph Decompositions math.CO cs.CG We describe a new algorithm, the $(k,\ell)$-... Sparsity-certifying Graph Decompositions We d...
2 0704.0003 The evolution of the Earth-Moon system based o... physics.gen-ph The evolution of Earth-Moon system is descri... The evolution of the Earth-Moon system based o...
3 0704.0004 A determinant of Stirling cycle numbers counts... math.CO We show that a determinant of Stirling cycle... A determinant of Stirling cycle numbers counts...
4 0704.0005 From dyadic $\Lambda_{\alpha}$ to $\Lambda_{\a... math.CA math.FA In this paper we show how to compute the $\L... From dyadic $\Lambda_{\alpha}$ to $\Lambda_{\a...
# 去掉换行符
data['text'] = data['text'].apply(lambda x: x.replace('\n',' ')) 
 # 全变成小写
data['text'] = data['text'].apply(lambda x: x.lower())
# 删掉标题和摘要列,text列已经包含了它们
data = data.drop(['abstract', 'title'], axis=1) 

由于原始论文有可能有多个类别,所以也需要处理:

# 多个类别,包含子分类
data['categories'] = data['categories'].apply(lambda x : x.split(' '))

# 取出大类别,不包含子分类
data['categories_big'] = data['categories'].apply(lambda x : [xx.split('.')[0] for xx in x])
data.head()
id categories text categories_big
0 0704.0001 [hep-ph] calculation of prompt diphoton production cros... [hep-ph]
1 0704.0002 [math.CO, cs.CG] sparsity-certifying graph decompositions we d... [math, cs]
2 0704.0003 [physics.gen-ph] the evolution of the earth-moon system based o... [physics]
3 0704.0004 [math.CO] a determinant of stirling cycle numbers counts... [math]
4 0704.0005 [math.CA, math.FA] from dyadic $\lambda_{\alpha}$ to $\lambda_{\a... [math, math]

然后将类别进行编码,这里类别是多个,所以需要多编码:

多标签二值化:
sklearn.preprocessing.MultiLabelBinarizer(classes=None, sparse_output=False)
classes_属性:若设置classes参数时,其值等于classes参数值,否则从训练集统计标签值
①classes默认值,classes_属性值从训练集中统计标签值
②设置classes参数,classes_属性值等于classes参数值

classes是设置label位置,为了好理解举个例子

若 classes= [2,3,4,5,6,1] ,则2值的索引为0,3值的索引为1,依次类推

所以当y=[(1, 2), (3,4),(5,)] , 1值在classes中的索引为5,所以经fit_transform后变成[0,0,0,0,1],对应索引为5的位置为1

2值在classes中的索引为0,所以经fit_transform后变成[1,0,0,0,0],对应索引为0的位置为1

from sklearn.preprocessing import MultiLabelBinarizer
mlb = MultiLabelBinarizer(classes = [2,3,4,5,6,1])
mlb.fit_transform([(1, 2), (3,4),(5,)])
array([[1, 0, 0, 0, 0, 1],
       [0, 1, 1, 0, 0, 0],
       [0, 0, 0, 1, 0, 0]])
mlb.classes_
array([2, 3, 4, 5, 6, 1])
# 多标签分类
from sklearn.preprocessing import MultiLabelBinarizer 
mlb = MultiLabelBinarizer() # 从训练集统计标签值
data_label = mlb.fit_transform(data['categories_big'].iloc[:])# 大类所有的值

思路1

使用TFIDF提取特征,限制最多4000个单词:

什么是TF-IDF

TF-IDF(term frequency-inverse document frequency)词频-逆向文件频率。在处理文本时,如何将文字转化为模型可以处理的向量呢?TF-IDF就是这个问题的解决方案之一。字词的重要性与其在文本中出现的频率成正比(TF),与其在语料库中出现的频率成反比(IDF)。

TF:词频。TF(w)=(词w在文档中出现的次数)/(文档的总词数)

IDF:逆向文件频率。有些词可能在文本中频繁出现,但并不重要,也即信息量小,如is,of,that这些单词,这些单词在语料库中出现的频率也非常大,我们就可以利用这点,降低其权重。IDF(w)=log_e(语料库的总文档数)/(语料库中词w出现的文档数)

将上面的TF-IDF相乘就得到了综合参数:TF-IDF=TF*IDF

# sklearn文本特征提取——TfidfVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer(max_features=4000) # 制最多4000个单词
data_tfidf = vectorizer.fit_transform(data['text'].iloc[:])# 摘要所有值

由于这里是多标签分类,可以使用sklearn的多标签分类进行封装:

sklearn.model_selection.train_test_split(*arrays, **options)

train_test_split里面常用的因数(arguments)介绍:

arrays:分割对象同样长度的列表或者numpy arrays,矩阵。

test_size:两种指定方法。1:指定小数。小数范围在0.0~0.1之间,它代表test集占据的比例。2:指定整数。整数的大小必须在这个数据集个数范围内,总不能指定一个数超出了数据集的个数范围吧。要是test_size在没有指定的场合,可以通过train_size来指定。(两个是对应关系)。如果train_size也没有指定,那么默认值是0.25.

train_size:和test_size相似。

random_state:这是将分割的training和testing集合打乱的个数设定。如果不指定的话,也可以通过numpy.random来设定随机数。

shufflestraify不常用。straify就是将数据分层。

# 划分训练集和验证集
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 # 对每一个目标值 target 拟合一个分类器.
from sklearn.naive_bayes import MultinomialNB
clf = MultiOutputClassifier(MultinomialNB()).fit(x_train, y_train)

分类报告:

sklearn.metrics.classification_report(y_true, y_pred, labels=None, target_names=None,sample_weight=None, digits=2),显示主要的分类指标,返回每个类标签的精确、召回率及F1值

主要参数说明:

labels:分类报告中显示的类标签的索引列表

target_names:显示与labels对应的名称

digits:指定输出格式的精确度

精度(precision) = 正确预测的个数(TP)/被预测正确的个数(TP+FP)

召回率(recall)=正确预测的个数(TP)/预测个数(TP+FN)

F1 = 2精度召回率/(精度+召回率)

注意:在二分类中,真正例率也称灵敏度,真负例率也称特效性

from sklearn.metrics import classification_report # 分类报告
print(classification_report(y_test, clf.predict(x_test)))
             precision    recall  f1-score   support

          0       0.95      0.85      0.89      7925
          1       0.85      0.79      0.82      7339
          2       0.77      0.72      0.74      2944
          3       0.00      0.00      0.00         4
          4       0.72      0.48      0.58      2123
          5       0.51      0.66      0.58       987
          6       0.86      0.38      0.52       544
          7       0.71      0.69      0.70      3649
          8       0.76      0.61      0.68      3388
          9       0.85      0.88      0.87     10745
         10       0.46      0.13      0.20      1757
         11       0.79      0.04      0.07       729
         12       0.45      0.35      0.39       507
         13       0.54      0.36      0.43      1083
         14       0.69      0.14      0.24      3441
         15       0.84      0.20      0.33       655
         16       0.93      0.16      0.27       268
         17       0.87      0.43      0.58      2484
         18       0.82      0.38      0.52       692

avg / total       0.80      0.65      0.69     51264

思路2

思路2使用深度学习模型,单词进行词嵌入然后训练。将数据集处理进行编码,并进行截断:

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)
# parameter
max_features= 500
max_len= 150
embed_size=100
batch_size = 128
epochs = 5

from keras.preprocessing.text import Tokenizer

# pad_sequences(sequences, maxlen, padding=’pre’, truncating=’pre’, value=0.) 
# 将序列填充到maxlen长度,padding取值有pre|post,value指定用何值填充的值
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)

No module named 'keras’解决办法 ,pip install --upgrade keras

版本太高,pip install keras==2.2 -i https://pypi.douban.com/simple

如果conda list 没有tensorflow

需要 pip install tensorflow -i https://pypi.douban.com/simple

然后报错哪个不能升级就直接pip install xx --upgrade --ignore-installed xx

再拒绝访问就pip install tensorflow -i https://pypi.douban.com/simple --user

注意,每次强制升级包完成后都要重新去pip install tensorflow

定义模型并完成训练:

# LSTM model
# Keras Layers:
# 因为报错,没有get_default_graph属性,所以我在子类keras前面增加了它的大类tensorflow
from tensorflow.keras.layers import Dense,Input,LSTM,Bidirectional,Activation,Conv1D,GRU
from tensorflow.keras.layers import Dropout,Embedding,GlobalMaxPooling1D, MaxPooling1D, Add, Flatten
from tensorflow.keras.layers import GlobalAveragePooling1D, GlobalMaxPooling1D, concatenate, SpatialDropout1D# Keras Callback Functions:
from tensorflow.keras.callbacks import Callback
from tensorflow.keras.callbacks import EarlyStopping,ModelCheckpoint
from tensorflow.keras import initializers, regularizers, constraints, optimizers, layers, callbacks
from tensorflow.keras.models import Model
from tensorflow.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, 
          batch_size=batch_size, 
          validation_split=0.2,
          epochs=epochs)
Epoch 1/5
625/625 [==============================] - 1179s 2s/step - loss: 0.2136 - accuracy: 0.4146 - val_loss: 0.1181 - val_accuracy: 0.6545
Epoch 2/5
625/625 [==============================] - 1195s 2s/step - loss: 0.1153 - accuracy: 0.6699 - val_loss: 0.1046 - val_accuracy: 0.6932
Epoch 3/5
625/625 [==============================] - 1261s 2s/step - loss: 0.1048 - accuracy: 0.6917 - val_loss: 0.1003 - val_accuracy: 0.7099
Epoch 4/5
625/625 [==============================] - 1268s 2s/step - loss: 0.0998 - accuracy: 0.7055 - val_loss: 0.0966 - val_accuracy: 0.7132
Epoch 5/5
625/625 [==============================] - 1268s 2s/step - loss: 0.0961 - accuracy: 0.7156 - val_loss: 0.0951 - val_accuracy: 0.7265





<tensorflow.python.keras.callbacks.History at 0x1f46920a630>

温馨提示:每个都要加

报错:AttributeError: module ‘tensorflow’ has no attribute ‘get_default_graph’

tensorflow模块没有get_default_graph属性

解决方法:

from tensorflow import keras

而不是直接使用:import keras

同样,在要使用keras下的其他模块时:

from tensorflow.keras import layers

而不是使用 from keras import layers

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

第四节:论文种类分类-学习笔记 的相关文章