1. 程式人生 > 其它 >NLP文字分類學習筆記4.1:基於RCNN的文字分類

NLP文字分類學習筆記4.1:基於RCNN的文字分類

迴圈卷積神經網路RCNN

1、CNN與RNN缺點

  • CNN通過視窗獲取特徵,視窗尺寸不合適就會捕獲不到好特徵,視窗也不能太大,這樣就捕獲不到全域性的特徵,所以它類似於傳統的N-gram
  • RNN使用最後的輸出作為特徵,使得序列後的詞會比前面的詞更加重要,從而影響捕獲準確的特徵

2、CNN與RNN優點

  • CNN使用池化,能夠捕獲重要的特徵
  • RNN處理序列有優勢,能夠捕獲全域性特徵

所以Recurrent Convolutional Neural Networks for Text Classification這篇論文將兩者優點結合起來,提出下圖模型RCNN

  • 圖中虛線圈出部分,實際上是一個雙向迴圈網路(之後用雙向LSTM實現,儘管論文中並不是,但也類似)
  • 之後將所有時刻的輸出和輸入的詞向量拼接起來(即圖中的\(y^{(2)}_3\)\(y^{(2)}_4\)等,圖中並未表示完整),論文中拼接的公式為,其中\(c_l(w_i)和c_r(w_i)\)為雙向LSTM的兩個輸出,\(e(w_i)\)為詞向量
\[x_i=[c_l(w_i);e(w_i);c_r(w_i)] \]
  • 然後經過啟用函式tanh(圖中未畫出,實現時採用relu)
  • 之後對每一維進行最大池化,組成新的特徵向量
  • 最後連線全連線層實現分類

pytorch實現基於RCNN的文字分類

對於10分類任務,在測試集分類準確率為87.27%,關於網路結構程式碼如下,更多程式碼詳細介紹見

NLP文字分類學習筆記0

import json
import pickle
import torch
import torch.nn.functional as F
import torch.nn as nn
import numpy as np


class Config(object):

    def __init__(self, embedding_pre):
        self.embedding_path = 'data/embedding.npz'
        self.embedding_model_path = "mymodel/word2vec.model"

        self.train_path = 'data/train.df'  # 訓練集
        self.dev_path = 'data/valid.df'  # 驗證集
        self.test_path = 'data/test.df'  # 測試集

        self.class_path = 'data/class.json'  # 類別名單
        self.vocab_path = 'data/vocab.pkl'  # 詞表
        self.save_path ='mymodel/rcnn.pth'        # 模型訓練結果
        self.embedding_pretrained = torch.tensor(np.load(self.embedding_path, allow_pickle=True)["embeddings"].astype(
            'float32')) if embedding_pre == True else None  # 預訓練詞向量
        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')  # 裝置

        self.dropout = 0.5  # 隨機失活
        self.num_classes = len(json.load(open(self.class_path, encoding='utf-8')))  # 類別數
        self.n_vocab = 0  # 詞表大小,在執行時賦值
        self.epochs = 10  # epoch數
        self.batch_size = 128  # mini-batch大小
        self.maxlen = 32  # 每句話處理成的長度(短填長切)
        self.learning_rate = 1e-3  # 學習率
        self.embed_size = self.embedding_pretrained.size(1) \
            if self.embedding_pretrained is not None else 200  # 字向量維度
        self.hidden_size = 128                                          # lstm隱藏層
        self.num_layers = 1                                             # lstm層數

class Model(nn.Module):
    def __init__(self, config):
        super(Model, self).__init__()
        if config.embedding_pretrained is not None:
            self.embedding = nn.Embedding.from_pretrained(config.embedding_pretrained, freeze=False)
        else:
            vocab = pickle.load(open(config.vocab_path, 'rb'))
            config.n_vocab=len(vocab.dict)
            self.embedding = nn.Embedding(config.n_vocab, config.embed_size, padding_idx=config.n_vocab - 1)
        self.lstm = nn.LSTM(config.embed_size, config.hidden_size, config.num_layers,
                            bidirectional=True, batch_first=True, dropout=config.dropout)
        self.maxpool = nn.MaxPool1d(config.maxlen)
        self.fc = nn.Linear(config.hidden_size * 2 + config.embed_size, config.num_classes)

    def forward(self, x):
        embed = self.embedding(x)
        out, _ = self.lstm(embed)
        out = torch.cat((embed, out), 2)
        out = F.relu(out)
        out = out.permute(0, 2, 1)
        out = self.maxpool(out).squeeze()
        out = self.fc(out)
        return out