NLP文字分類學習筆記4.1:基於RCNN的文字分類
阿新 • • 發佈:2022-04-08
迴圈卷積神經網路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)\)為詞向量
- 然後經過啟用函式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