使用Tensorflow搭建並訓練TextCNN模型,對文本進行分類

最近有學習關於文本分類的深度學習模型,最先接觸的就是TextCNN模型,該模型看起來非常簡單效果也非常好,在此簡單記錄下整個模型的搭建以及訓練過程。通過本博文,你可以自己搭建並訓練一個簡單的文本分類模型,本文的代碼註釋非常詳細。

使用的開發環境:python3(Anaconda管理)、Tensorflow1.13.1

本文主要分爲以下幾個部分進行展開講解:

(1)TextCNN原理

(2)模型的搭建

(3)訓練數據的準備

(4)模型的訓練

(5)知識點補充

 

TextCNN原理

如圖所示,展示了整個textcnn的模型結構,主要流程可分爲以下幾步:

1)將中文文本通過embedding層轉換爲詞向量,圖中詞向量以三維爲例(例如“我”對應的是[0.2, 0.1, 0.3]詞向量),在本文代碼中採用的是64維。(本圖中省略了將中文文本轉換爲對應詞id的過程)

2)通過不同的滑動窗口進行卷積處理,圖中以滑動窗口分別爲2、3、4爲例,並且每種滑動窗口的卷積核個數爲2,實際使用過程中每種滑動窗口的卷積核個數可自己設定,本文代碼中採用的是64。

3)對卷積操作生成的特徵矩陣使用最大池化處理。

4)將池化後的特徵矩陣進行拼接。

5)將特徵矩陣進行扁平化或壓縮維度,圖中未繪製。

6)連接全連接層1

7)連接全連接層2,通過activation=softmax輸出每個類別的概率

 

網絡模型的搭建

在模型的搭建之前,我們先建立一個python項目,目錄結構如圖所示:

+testCnnProject
    +cnews
        -cnews.test.txt
        -cnews.train.txt
        -cnews.val.txt
        -cnews.vocab.txt
    +log
    -main.py
    -model.py
    -dataGenerator.py

其中的cnews文件夾先不用管在後面的數據準備中會進行講解,log文件夾是用來存放我們訓練過程中生成的tensorboard文件以及模型的權重,model.py是用來存放textcnn模型的文件,dataGenerator.py使用來生成數據的文件,main.py是該項目的入口用於調用訓練模型。

下面我們開始在model.py文件中編寫textcnn模型文件。

該模型使用的是tensorflow包中自帶的keras模塊,無需另外安裝keras包,代碼中有詳細的註釋,很好理解:

model.py

from tensorflow import keras

def text_cnn(seq_length, vocab_size, embedding_dim, num_cla, kernel_num):
    """
    seq_length: 輸入的文字序列長度
    vocab_size: 詞彙庫的大小
    embedding_dim: 生成詞向量的特徵維度
    num_cla: 分類類別
    kernel_num::卷積層的卷積核數
    """

    # 定義輸入層
    inputX = keras.layers.Input(shape=(seq_length,), dtype='int32')
    # 嵌入層,將詞彙的one-hot編碼轉爲詞向量
    embOut = keras.layers.Embedding(vocab_size, embedding_dim, input_length=seq_length)(inputX)

    # 分別使用長度爲3,4,5的詞窗口去執行卷積, 接着進行最大池化處理
    conv1 = keras.layers.Conv1D(kernel_num, 3, padding='valid', strides=1, activation='relu')(embOut)
    maxp1 = keras.layers.MaxPool1D(pool_size=int(conv1.shape[1]))(conv1)

    conv2 = keras.layers.Conv1D(kernel_num, 4, padding='valid', strides=1, activation='relu')(embOut)
    maxp2 = keras.layers.MaxPool1D(pool_size=int(conv2.shape[1]))(conv2)

    conv3 = keras.layers.Conv1D(kernel_num, 5, padding='valid', strides=1, activation='relu')(embOut)
    maxp3 = keras.layers.MaxPool1D(pool_size=int(conv3.shape[1]))(conv3)

    # 合併三個經過卷積和池化後的輸出向量
    combineCnn = keras.layers.Concatenate(axis=-1)([maxp1, maxp2, maxp3])
    # 扁平化
    flatCnn = keras.layers.Flatten()(combineCnn)

    # 全連接層1,節點數爲128
    desen1 = keras.layers.Dense(128)(flatCnn)
    # 在全連接層1和2之間添加dropout減少訓練過程中的過擬合,隨機丟棄25%的結點值
    dropout = keras.layers.Dropout(0.25)(desen1)
    # 爲全連接層添加激活函數
    densen1Relu = keras.layers.ReLU()(dropout)
    # 全連接層2(輸出層)
    predictY = keras.layers.Dense(num_cla, activation='sofmax')(densen1Relu)

    # 指定模型的輸入輸出層
    model = keras.models.Model(inputs=inputX, ouputs=predictY)
    # 指定loss的計算方法,設置優化器,編譯模型
    model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

    return model

訓練文件的準備

簡單介紹下數據集,數據集包含十個類別,每個文本對應一個一類。網上找的,具體來源不詳。

網盤地址:https://pan.baidu.com/s/1w452Z5eXbQSDQfgEBNUdlg ,提取密碼:8cwv

該數據集大概有66M,其中有4個文件:

cnews.train.txt (包含50000個文本,每行代表一個文本,最前面是該文本對應的標籤,標籤與文本之間用製表符隔開)

cnews.test.txt  (包含10000個測試文本,格式與trian相同)

cnews.eval.txt (包含5000個驗證文本,格式與train相同)

cnews.vocab.txt (包含一個分詞詞典,其實就是一個字典,並沒有進行分詞處理) 

下載好後按照之前講的文件結構放好文件,接着在dataGenerator.py文件中編寫用於生成數據的代碼:

dataGenerator.py

from tensorflow import keras
from sklearn.preprocessing import LabelEncoder
import random

def content2idList(content, word2id_dict):
    """
    該函數的目的是將文本轉換爲對應的漢字數字id
    content:輸入的文本
    word2id_dict:用於查找轉換的字典
    """
    idList = []
    for word in content:  # 遍歷每一個漢字
        if word in word2id_dict:  # 當剛文字在字典中時才進行轉換,否則丟棄
            idList.append(word2id_dict[word])
    return idList

def generatorInfo(batch_size, seq_length, num_classes, file_name):
    """
    batch_size:生成數據的batch size
    seq_length:輸入文字序列長度
    num_classes:文本的類別數
    file_name:讀取文件的路徑
    """
    # 讀取詞庫文件
    with open('./cnews/cnews.vocab.txt', encoding='utf-8') as file:
        vocabulary_list = [k.strip() for k in file.readlines()]
    word2id_dict = dict([(b, a) for a, b in enumerate(vocabulary_list)])

    # 讀取文本文件
    with open(file_name, encoding='utf-8') as file:
        line_list = [k.strip() for k in file.readlines()]
        data_label_list = []   # 創建數據標籤文件
        data_content_list = []   # 創建數據文本文件
        for k in line_list:
            t = k.split(maxsplit=1)
            data_label_list.append(t[0])
            data_content_list.append(t[1])
    
    data_id_list = [content2idList(content, word2id_dict) for content in data_content_list]  # 將文本數據轉換拿爲數字序列
    # 將list數據類型轉換爲ndarray數據類型,並按照seq_length長度去統一化文本序列長度,
    # 若長度超過設定值將其截斷保留後半部分,若長度不足前面補0
    data_X = keras.preprocessing.sequence.pad_sequences(data_id_list, seq_length, truncating='pre')  
    labelEncoder = LabelEncoder()
    data_y = labelEncoder.fit_transform(data_label_list)  # 將文字標籤轉爲數字標籤
    data_Y = keras.utils.to_categorical(data_y, num_classes)  # 將數字標籤轉爲one-hot標籤

    while True:
        selected_index = random.sample(list(range(len(data_y))), k=batch_size)   # 按照數據集合的長度隨機抽取batch_size個數據的index
        batch_X = data_X[selected_index]  # 隨機抽取的文本信息(數字化序列)
        batch_Y = data_Y[selected_index]  # 隨機抽取的標籤信息(one-hot編碼)
        yield (batch_X, batch_Y)  
        

 

網絡模型的訓練

現在我們的訓練數據已經準備到位,模型也已經搭建完成,接着我們開始訓練模型:

main.py

from model import text_cnn
from dataGenerator import generatorInfo
from tensorflow import keras

vocab_size = 5000  # 詞彙庫大小
seq_length = 600   # 輸入文本序列長度
embedding_dim = 64  # embedding層輸出詞向量維度
num_classes = 10  # 分類類別
kernel_num=64  # 卷積核數
trianBatchSize = 64  # 訓練時的batch size
evalBatchSize = 200  # 驗證時的batch size
steps_per_epoch = 50000 // trianBatchSize   # 一個epoch對應的訓練步數
epoch = 2  # 訓練的epoch數
logdir = './log'  # tensorbard訓練信息以及train_weights的保存位置
trainFileName = './cnews/cnews.train.txt'  # 訓練文件的路徑
evalFileName = './cnews/cnews.test.txt'  # 驗證文件的路徑

model = text_cnn(seq_length=seq_length,  # 初始化模型
                 vocab_size=vocab_size,
                 embedding_dim=embedding_dim,
                 num_cla=num_classes,
                 kernel_num=kernel_num)

trainGenerator = generatorInfo(trianBatchSize, seq_length, num_classes, trainFileName)  
evalGenerator = generatorInfo(evalBatchSize, seq_length, num_classes, evalFileName)

def lrSchedule(epoch):  # 自定義學習率變化
    lr = keras.backend.get_value(model.optimizer.lr)
    if epoch % 1 == 0 and epoch != 0:
        lr = lr * 0.5
    return lr

log = keras.callbacks.TensorBoard(log_dir=logdir, update_freq='epoch')   # 調用tensorboard
reduceLr = keras.callbacks.LearningRateScheduler(lrSchedule, verbose=1)   # 調用自定義學習率函數

model.fit_generator(generator=trainGenerator,  
                    steps_per_epoch=steps_per_epoch,
                    epochs=epoch,
                    validation_data=evalGenerator,
                    validation_steps=10,
                    callbacks=[log, reduceLr])
model.save_weights(logdir + 'train_weights.h5')        

知識點補充

首先說下我個人理解的文本分類整個完整流程

(1)使用類似jieba的中文分詞庫對整個訓練集進行中文分詞

(2)統計詞彙的出現評率,刪除部分低頻詞

(3)根據禁用詞庫濾除禁用詞

(4)根據生成的詞庫對訓練集進行分詞處理,只保留詞庫中已有的詞彙,詞彙之間用空格隔開

(5)分詞後使用word2vec訓練詞向量模型(該步驟是可選項,可以不做)

(6)搭建文本訓練模型,例如TextCNN(若進行了第五步操作,使用訓練好的詞向量weights初始化embdding層參數)

(7)訓練模型,調參有更高的需求的可以改進模型

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章