基于双向 GRU 的文本分类 Python 算法实战

大家好,文本数据的处理,对于一个风控策略或者算法,我觉得是必须要掌握的技能,有人说,我的风控并不涉及到文本?我觉得这片面了,在非内容风控领域,文本知识也是非常有用的。

用户昵称、地址啥的,这种绝大部分风控场景都能遇到;关系网络的节点向量化,基本也是文本处理的思路;行为序列,也能用文本的知识去处理,能捕捉非常有趣模式。

去年就已经写的差不多了,现在整理好慢慢更新,本系列主要介绍了风控场景下文本分类的基本方法,对抗文本变异,包括传统的词袋模型、循环神经网络,也有常用于计算机视觉任务的卷积神经网络,以及 RNN + CNN,试验完一遍,基本能搞定大部分的文本分类以及文本变异对抗问题。

一、原理介绍

RNN 可能看起来很可怕,尽管它们因为复杂而难以理解,但非常有趣。RNN 模型封装了一个非常漂亮的设计,以克服传统神经网络在处理序列数据(文本、时间序列、视频、DNA 序列等)时的短板。

RNN 是一系列神经网络的模块,它们彼此连接像锁链一样,每一个都将消息向后传递,强烈推荐大家从 Colah 的博客中深入了解它的内部机制,下面的图就来源于此。

我们要处理的序列类型是文本数据。对意义而言,单词顺序很重要。RNN 考虑到了这一点,它可以捕捉长期依赖关系。

二、数据集和预处理

本文用一个风险弹幕数据集做实验,该数据集包含19670条明细数据,每一行都用 1(垃圾文本)和 0(正常文本)进行了标记。

目标:针对直播间中存在的大量涉黄涉暴弹幕,进行垃圾分类,将弹幕中不合法的内容进行识别并屏蔽。

正常弹幕示例

数据读取和查看

import os 
import pandas as pd
path  = '/Users/wuzhengxiang/Documents/DataSets/TextCnn'
os.chdir(path)
data = pd.read_csv('text_all.csv')

#对数据进行随机打乱
data = data.sample(frac=1, random_state=42)
print(data.shape)
(19670, 2)

#查看0-1的比例,可以看出来,数据集基本上平衡
data['label'].value_counts()
1    9882
0    9788

#查看前5行的数据
data.head(5)
                         text  label
17036        郑       1
5426                    葩葩葩l      0
14582               买家秀和卖家秀?0
1730                  1776    0
1444              我又没送你谢我干啥ᚠ      0

三、模型构建

from keras.preprocessing.text import Tokenizer
from keras.preprocessing.text import text_to_word_sequence
from keras.preprocessing.sequence import pad_sequences

from keras.models import Model 
from keras.models import Sequential

from keras.layers import Input, Dense, Embedding, Conv1D, Conv2D, MaxPooling1D, MaxPool2D
from keras.layers import Reshape, Flatten, Dropout, Concatenate
from keras.layers import SpatialDropout1D, concatenate
from keras.layers import GRU, Bidirectional, GlobalAveragePooling1D, GlobalMaxPooling1D

from keras.callbacks import Callback
from keras.optimizers import Adam

from keras.callbacks import ModelCheckpoint, EarlyStopping
from keras.models import load_model
from keras.utils.vis_utils import plot_model


#进行分字处理
import os
import pandas as pd
path  = '/Users/wuzhengxiang/Documents/DataSets/TextCnn'
os.chdir(path)
text_all = pd.read_csv('text_all.csv')

data = text_all
data['text'] = text_all['text'].apply(lambda x: ' '.join(x))
data.head()

x_train, x_test, y_train, y_test=\
     train_test_split(data['text'], 
                      data['label'], 
                      test_size=0.2, 
                      random_state=42
                      )
print(x_train.shape, x_test.shape, y_train.shape, y_test.shape)


x_train[45]


MAX_NB_WORDS = 80000
tokenizer=Tokenizer(num_words=MAX_NB_WORDS)
tokenizer.fit_on_texts(x_train)


tokenizer.texts_to_sequences([x_train[38]])


train_sequences = tokenizer.texts_to_sequences(x_train)
test_sequences  = tokenizer.texts_to_sequences(x_test)


MAX_LENGTH = 35
padded_train_sequences = pad_sequences(train_sequences, maxlen=MAX_LENGTH)
padded_test_sequences  = pad_sequences(test_sequences,  maxlen=MAX_LENGTH)

padded_train_sequences
array([[  0,   0,   0, ...,   2,   8,   2],
       [  0,   0,   0, ..., 387, 443,  16],
       [  0,   0,   0, ...,  64,  59,  11],
       ...,
       [  0,   0,   0, ...,  27,  27,  27],
       [  0,   0,   0, ...,   3,   9,  71],
       [  0,   0,   0, ...,   1,  16,  16]], dtype=int32)
padded_train_sequences.shape
(15736, 35)

import numpy as np

def get_simple_rnn_model():
    embedding_dim = 300
    embedding_matrix = np.random.random((MAX_NB_WORDS, embedding_dim))
    inp = Input(shape=(MAX_LENGTH, ))
    x = Embedding(input_dim = MAX_NB_WORDS, 
                  output_dim = embedding_dim, 
                  input_length = MAX_LENGTH, 
                  weights=[embedding_matrix], trainable=True)(inp)
    x = SpatialDropout1D(0.3)(x)
    x = Bidirectional(GRU(100, return_sequences=True))(x)
    avg_pool = GlobalAveragePooling1D()(x)
    max_pool = GlobalMaxPooling1D()(x)
    conc = concatenate([avg_pool, max_pool])
    outp = Dense(1, activation="sigmoid")(conc)

    model = Model(inputs=inp, outputs=outp)
    model.compile(loss='binary_crossentropy',
                  optimizer='adam',
                  metrics=['accuracy'])
    return model
rnn_simple_model = get_simple_rnn_model()

plot_model(rnn_simple_model, 
           #to_file='C:\\Users\\Desktop\\NLP任务实验\\rnn_simple_model.png', 
           show_shapes=True, 
           show_layer_names=True
          )


filepath="weights-improvement-{epoch:02d}-{val_acc:.4f}.hdf5"
checkpoint = ModelCheckpoint(filepath, 
                             monitor='val_acc', 
                             verbose=1, 
                             save_best_only=True,
                             mode='max'
                            )

batch_size = 256
epochs = 2
history = rnn_simple_model.fit(
                   x = padded_train_sequences, 
                   y = y_train, 
                   validation_data = (padded_test_sequences, y_test), 
                   batch_size = 256, 
                   #callbacks=[checkpoint], 
                   epochs = 5, 
                   verbose=1
           )

#best_rnn_simple_model = load_model('weights-improvement-01-0.8262.hdf5')

y_pred_rnn_simple = rnn_simple_model.predict(padded_test_sequences, 
                                                  verbose=1, batch_size=2048)

y_pred_rnn_simple = pd.DataFrame(y_pred_rnn_simple, columns=['prediction'])
y_pred_rnn_simple['prediction'] = y_pred_rnn_simple['prediction'].map(lambda p: 1 if p >= 0.5 else 0)

#y_pred_rnn_simple.to_csv('./predictions/y_pred_rnn_simple.csv', index=False)
#y_pred_rnn_simple = pd.read_csv('./predictions/y_pred_rnn_simple.csv')

print(accuracy_score(y_test, y_pred_rnn_simple))
0.9669547534316217

df = pd.DataFrame({
 'text':x_test,'label':y_test,'pred':y_pred_rnn_simple})
df[df['label']!=df['pred']]

相比于第二个模型(拼音+ngram+逻辑回归)的 0.95602 ,这个一下子就干到了 0.9669 ,可以看到深度学习还是非常勇猛的,在序列领域,RNN有着不可取代的地位。后续我们继续考虑变异情况,再考虑用深度学习来试验,看看能做到多少的准确率。

四、模型解释

1)模型打印

plot_model 可以打印模型的框架,我们的框架如下,可以看到,使用了一个双项的GRU,然后进行了两种不同的池化方式进行池化在拼接起来。

2)模型训练过程

我们可以看看训练过程的损失下降和准确率上升,通过曲线,可以优化我们的训练过程。

import matplotlib.pyplot as plt# 画出损失函数曲线
plt.plot(history.history['loss'],    'bo',)
plt.plot(history.history['accuracy'], 'b',)
plt.title('train loss')plt.ylabel('acc')
plt.xlabel('epoch')plt.legend()# 画出损失函数曲线
plt.plot(history.history['val_loss'],    'bo',)
plt.plot(history.history['val_accuracy'], 'b',)
plt.title('val loss')
plt.ylabel('acc')
plt.xlabel('epoch')
plt.legend()

可以看到整个下降曲线还是很平滑的,如果我们轮数增加,准确率还有上升的可能性,大家可以测试下,有点非机器,一训练就开始发热了,

先写到这里了,大家可以看到,深度学习,对于解决语言问题,还是很有优势的,就这么简简单单的一段代码,准确率有了非常大的提升,后面的文章,我们继续深入研究各种风控识别算法。

猜你喜欢

转载自blog.csdn.net/m0_59485658/article/details/126994124