数据挖掘之文本分类挖掘
文本分类是NLP领域最经典的使用场景之一,它涉及到自动将文本数据分配到预设的类别或标签中。这项技术在多个应用场景中扮演着重要角色,例如情感分析、新闻归类、主题识别以及垃圾邮件检测等。
文本分类任务可根据是否使用深度学习方法将文本分类主要分为以下两大类:
基于传统机器学习的文本分类,如 TF-IDF文本分类 。
基于深度学习的文本分类,如Facebook开源的FastText文本分类 ,Text-CNN 文本分类 ,Text-CNN 文本分类 等。
在本实验中将通过传统机器学习和深度学习两个方面实现文本分类挖掘任务。
数据集介绍
选用大众点评评价数据,该数据集是一个中文的用于文本分类的二分类数据集,包含了用户对服务和产品的评价。实验的目的是通过文本分类来判断用户评价的情感倾向,即判断情感是正面的还是负面的。数据集链接为:大众点评评价数据 · 数据集 (modelscope.cn)
基于机器学习的文本分类挖掘
数据加载
首先读取数据集,并查看数据集的具体内容。训练集读取如下:
1 2 from modelscope.msdatasets import MsDatasetds = MsDataset.load('DAMO_NLP/yf_dianping' , subset_name='default' , split='train' )
查看数据集的格式,可以看到主要分为三列,一列是sentence表示评价内容;一列lable表示情感偏向;一列是dataset表示数据集来源。
对验证集进行相同处理
1 2 3 from modelscope.msdatasets import MsDatasetds_val = MsDataset.load('DAMO_NLP/yf_dianping' , subset_name='default' , split='validation' ) next (iter (ds))
文本处理
然后将数据集转化为dataframe格式。
1 2 3 import pandas as pdcolumn_names = ['sentence' , 'label' , 'dataset' ] df = pd.DataFrame(ds, columns = column_names)
之前观察到数据集一共有三列,由于dataset列内容相同且对分类结果没有影响,所以仅保留sentence和label列。
1 df = df.drop('dataset' ,axis=1 )
然后判断label和sentence是否含有空值,删除含有空值的行。
1 2 df = df.dropna(subset=['label' ]) df = df.dropna(subset=['sentence' ])
将label列从float类型转化为int类型。
1 df['label' ] = df['label' ] .astype (int)
查看处理后的数据:
对验证集进行相同的处理:
处理后的训练集有44984条数据,验证集有5016条数据。
数据分布
将训练集和验证集的数据拼接在一起,查看lable分布情况,可以看到1为27623条,0为27393条,数量接近。
1 2 3 4 import matplotlib.pyplot as pltdf = pd.concat([df,df_val]) lable_counts = df['label' ].value_counts() lable_counts
绘制柱形图直观展现正负标签分布情况,可以看到数据分布非常均匀
1 2 3 4 5 6 7 plt.figure(figsize=(10 ,6 )) lable_counts.plot(kind='bar' ) plt.title('Distribution of Positive and Negative Saples' ) plt.xlabel('Lable' ) plt.ylabel('Count' ) plt.legend(title='Lable' ) plt.show()
把文本转为向量形式
TF-IDF是一种用于信息检索与文本挖掘的常用加权技术。 TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。TF-IDF的主要思想是:如果某个单词在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。
为了进行文本分类任务,需要将文本转化为向量,查看哪些词语的tfidf较高。
首先需要初始化并应用 TF-IDF 向量化器,查看转换后数据的维度
1 2 3 4 5 6 7 from sklearn.feature_extraction.text import TfidfVectorizerfrom sklearn.model_selection import train_test_splitx = df['sentence' ] y = df['label' ] vectorizer = TfidfVectorizer() X = vectorizer.fit_transform(x) X.shape
查看TF-IDF值最高的词语
1 2 3 4 data1 = {'word' : vectorizer.get_feature_names_out(), 'tfidf' : X.toarray().sum (axis=0 ).tolist()} df1 = pd.DataFrame(data1).sort_values(by='tfidf' ,ascending=False ,ignore_index=True ) df1.head(10 )
训练模型以及评估
划分训练集和测试集,查看划分后的数据情况
1 2 X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.2 ,stratify=y,random_state=0 ) X_train.shape,X_test.shape, y_train.shape, y_test.shape
模型构建,model1 是一个多项式朴素贝叶斯分类器的实例。
model2 是一个逻辑回归分类器的实例,其中正则化强度
C为10的10次方,迭代次数设置为10000。
model3 是一个K近邻分类器的实例,设置邻居数为50。
model4` 是一个决策树分类器的实例,随机状态设置为77。
1 2 3 4 5 6 7 8 9 10 11 12 13 from sklearn.naive_bayes import MultinomialNBfrom sklearn.tree import DecisionTreeClassifierfrom sklearn.linear_model import LogisticRegressionfrom sklearn.neighbors import KNeighborsClassifierfrom sklearn.tree import DecisionTreeClassifiermodel1 = MultinomialNB() model2 = LogisticRegression(C=1e10 ,max_iter=10000 ) model3 = KNeighborsClassifier(n_neighbors=50 ) model4 = DecisionTreeClassifier(random_state=77 ) model_list=[model1,model2,model3,model4] model_name=['NativeBayes' ,'LogisticRegression' ,'KNeighbors' ,'DecisionTree' ]
查看模型准确率,可以看到贝叶斯模型的准确率最高,K近邻模型准确率最低,但是整体都在百分之六十多左右,相差不大。
1 2 3 4 5 6 7 8 scores=[] for i in range (len (model_list)): model_C=model_list[i] name=model_name[i] model_C.fit(X_train, y_train) s=model_C.score(X_test,y_test) scores.append(s) print (f'{name} 方法在测试集的准确率为{round (s,3 )} ' )
建立一个多项式朴素贝叶斯模型,然后在测试数据上生成并显示该模型的接收器操作特征(ROC)曲线
1 2 3 4 5 6 7 8 9 import numpy as npfrom sklearn.metrics import RocCurveDisplayfrom sklearn.naive_bayes import MultinomialNBmodel = MultinomialNB() model.fit(X_train,y_train) RocCurveDisplay.from_estimator(model,X_test,y_test) x = np.linspace(0 ,1 ,100 ) plt.plot(x,x,"k--" ,linewidth=1 ) plt.title('ROC Curve(Test Set)' )
然后查看数据集不平衡时的实验效果,选择标签为0的随机十分之一。
1 2 3 4 5 6 sample_size=len (df[df['label' ]==0 ]) // 10 sampled_df = df[df['label' ] == 0 ].sample(n=sample_size) df_one = df[df['label' ]==1 ] df_new = pd.concat([sampled_df,df_one]) df_new = df_new.sample(frac=1 ) print (df_new)
构建训练集和测试集,构建模型,查看准确率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 x = df_new['sentence' ] y = df_new['label' ] vectorizer = TfidfVectorizer() X = vectorizer.fit_transform(x) X.shape X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.2 ,stratify=y,random_state=0 ) X_train.shape,X_test.shape, y_train.shape, y_test.shape model1 = MultinomialNB() model_list=[model1] model_name=['NativeBayes' ] scores=[] for i in range (len (model_list)): model_C=model_list[i] name=model_name[i] model_C.fit(X_train, y_train) s=model_C.score(X_test,y_test) scores.append(s) print (f'{name} 方法在测试集的准确率为{round (s,3 )} ' )
基于深度学习的文本分类挖掘
数据加载和文本处理与基于机器学习的文本分类挖掘任务相同。
分词并删除停用词
导入库并获取中文停用词
1 2 3 4 import jiebafrom stopwordsiso import stopwordsstop_words = stopwords("zh" ) stop_words
定义文本预处理函数,进行分词并过滤停用词。
1 2 3 4 def preprocess_text (text ): words = jieba.cut(text) filtered_words = [word for word in words if word not in stop_words] return ' ' .join(filtered_words)
使用更新后的preprocess_text
函数处理你的DataFrame中的文本列
1 2 df['sentence' ] = df['sentence' ].apply(preprocess_text) df_val['sentence' ] = df_val['sentence' ].apply(preprocess_text)
查看处理后的数据
导入必要的库
1 2 3 4 5 import numpy as npfrom tensorflow.keras.preprocessing.text import Tokenizerfrom tensorflow.keras.preprocessing.sequence import pad_sequencesfrom keras.models import Sequentialfrom keras.layers import Embedding, LSTM, Dense, Dropout
使用 Keras 的 Tokenizer
来对文本进行分词并构建词汇表。此外需求将文本转换为序列,然后对其进行填充以确保输入神经网络的文本长度一致。
1 2 3 4 5 6 7 8 9 10 11 12 tokenizer = Tokenizer(num_words=25000 , oov_token="<OOV>" ) tokenizer.fit_on_texts(df['sentence' ]) train_sequences = tokenizer.texts_to_sequences(df['sentence' ]) val_sequences = tokenizer.texts_to_sequences(df_val['sentence' ]) max_length = 280 train_padded = pad_sequences(train_sequences, maxlen=max_length, padding='post' , truncating='post' ) test_padded = pad_sequences(val_sequences, maxlen=max_length, padding='post' , truncating='post' )
构建CNN网络,首先使用一个词嵌入层,其目的是将输入的词汇索引转换成固定大小的稠密向量,词汇表大小为25,000,输出维度为150。然后使用一维卷积层来处理嵌入向量,卷积层有128个过滤器,每个过滤器的宽度为5。卷积层后面是一个最大池化层,采用池化窗口大小为5。之后是全局最大池化层。后续是全连接层和Dropout层,全连接层有32个神经元,并使用ReLU激活函数。紧接着是一个Dropout层,dropout率设为0.5。最后,使用一个包含两个神经元的全连接层和softmax激活函数。
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 from keras.models import Sequentialfrom keras.layers import Embedding, Conv1D, MaxPooling1D, GlobalMaxPooling1D, Dense, Dropoutmodel = Sequential([ Embedding(input_dim=25000 , output_dim=150 , input_length=max_length), Conv1D(128 , 5 , activation='relu' ), MaxPooling1D(5 ), GlobalMaxPooling1D(), Dense(32 , activation='relu' ), Dropout(0.5 ), Dense(2 , activation='softmax' ) ]) model.compile (loss='categorical_crossentropy' , optimizer='adam' , metrics=['accuracy' ]) model.summary()
模型训练,epoch为15
1 2 import tensorflow as tfhistory = model.fit(train_padded, tf.keras.utils.to_categorical(df['label' ]), epochs=15 , batch_size=128 )
模型准确率
查看模型训练的loss损失
1 2 3 4 5 6 7 8 9 10 import matplotlib.pyplot as pltplt.figure(figsize=(10 , 6 )) plt.plot(history.history['loss' ], label='train loss' ) plt.title('Training Loss' ) plt.xlabel('Epoch' ) plt.ylabel('Loss' ) plt.legend() plt.show()