詳解文本分類之多通道CNN的理論與實踐

導讀git

最近在梳理文本分類的各個神經網絡算法,特意一個來總結下。接下來將要一個文章一個文章的講解各個算法的理論與實踐。目錄暫定爲:github

  1. 多通道卷積神經網絡(multi_channel_CNN)算法

  2. 深度卷積神經網絡(deep_CNN)網絡

  3. 基於字符的卷積神經網絡(Char_CNN)app

  4. 循環與卷積神經網絡並用網絡(LSTM_CNN)dom

  5. 樹狀循環神經網絡(Tree-LSTM)ide

  6. Transformer(目前經常使用於NMT)函數

  7. etc..spa

以後的之後再補充。今天咱們先講第一個,多通道卷積神經網絡。orm


先前知識補充

先說點基礎的,咱們最剛開始的分類其實就是embedding層以後直接通過線性層進行降維,將其映射到分類上,圖爲:


圖片


而後由於參數太多,計算太慢,因此產生了pooling池化層,取指定維度的一個參數表明整個維度,從而大大下降了計算量,並且效果還不錯。圖爲:


圖片


以後又有人想到沒有充分的利用到句子的上下詞語的關係,因此就講圖像算法的CNN運用到了NLP上,這個就至關於NLP裏的n-gram(unigram,bigram,trigram...)同樣,尋找相鄰詞語組合造成的特徵。圖爲:


圖片

有了上面的基礎,咱們引出multi_channel_CNN就容易多了。


multi_channel_CNN

多通道,就是CNN中的一次性卷積要處理的多少組數據。好比圖像中,若是是隻有灰度值的圖像就只有一個通道,若是是彩色圖片的話,就會RGB三個圖像(也就是三個通道)。那麼NLP中怎麼利用這個多通道特徵呢?有人就想了NLP中不就一個句子長度 * embed維度組成的一個二維輸入嗎?是這樣的,剛開始咱們用的都是單通道的。


可是有人就提出了這樣的想法:

初始化兩個不一樣的embedding,將句子用兩個embedding表示出來,這樣就能夠有兩個通道了。


時間確實是這樣的,可是咱們經常使用的是一個是隨機初始化的embedding,另外一個是使用預訓練embedding(w2v or GloVe ...)。圖爲:


圖片



實踐

這個其實和圖像是想的差很少了。(pytorch)


class Multi_Channel_CNN 初始化:

 
 

def __init__(self, opts, vocab, label_vocab):
       super(Multi_Channel_CNN, self).__init__()

       random.seed(opts.seed)
       torch.manual_seed(opts.seed)
       torch.cuda.manual_seed(opts.seed)

       self.embed_dim = opts.embed_size
       self.word_num = vocab.m_size
       self.pre_embed_path = opts.pre_embed_path
       self.string2id = vocab.string2id
       self.embed_uniform_init = opts.embed_uniform_init
       self.stride = opts.stride
       self.kernel_size = opts.kernel_size
       self.kernel_num = opts.kernel_num
       self.label_num = label_vocab.m_size
       self.embed_dropout = opts.embed_dropout
       self.fc_dropout = opts.fc_dropout

       self.embeddings = nn.Embedding(self.word_num, self.embed_dim)
       self.embeddings_static = nn.Embedding(self.word_num, self.embed_dim)

       if opts.pre_embed_path != '':
           embedding = Embedding.load_predtrained_emb_zero(self.pre_embed_path, self.string2id)
           self.embeddings_static.weight.data.copy_(embedding)
       else:
           nn.init.uniform_(self.embeddings_static.weight.data, -self.embed_uniform_init, self.embed_uniform_init)

       nn.init.uniform_(self.embeddings.weight.data, -self.embed_uniform_init, self.embed_uniform_init)

       # 2 convs
       self.convs = nn.ModuleList(
           [nn.Conv2d(2self.embed_dim, (K, self.embed_dim), stride=self.stride, padding=(K // 2, 0)) for K in self.kernel_size])

       in_fea = len(self.kernel_size)*self.kernel_num
       self.linear1 = nn.Linear(in_fea, in_fea // 2)
       self.linear2 = nn.Linear(in_fea // 2, self.label_num)
       self.embed_dropout = nn.Dropout(self.embed_dropout)
       self.fc_dropout = nn.Dropout(self.fc_dropout)

這個部分主要將輸入的通道數1改成2便可。


數據流通部分:

 
 

def forward(self, input):
       static_embed = self.embeddings_static(input)  # torch.Size([64, 39, 100])
       embed = self.embeddings(input)  # torch.Size([64, 39, 100])

       x = torch.stack([static_embed, embed], 1)  # torch.Size([64, 2, 39, 100])

       out = self.embed_dropout(x)

       l = []
       for conv in self.convs:
           l.append(F.relu(conv(out)).squeeze(3))  # torch.Size([64, 100, 39])

       out = l
       l = []
       for i in out:
           l.append(F.max_pool1d(i, kernel_size=i.size(2)).squeeze(2))  # torch.Size([64, 100])

       out = torch.cat(l, 1)  # torch.Size([64, 300])

       out = self.fc_dropout(out)

       out = self.linear1(out)
       out = self.linear2(F.relu(out))

       return out


這裏主要就是一個stack函數的應用,將兩個embedding放到一個新的維度裏。


數據對比


圖片


能夠明顯看出多通道優勢仍是很突出的。


github地址:

https://github.com/zenRRan/Sentiment-Analysis/blob/master/models/multi_channel_CNN.py

歡迎fork,有問題你們儘管指出!


PS:上述圖片均來自於導師張梅山,唐都鈺的《Deep Learning in Natural Language Processing》的情感分析篇。


IELTS a bit


rival n. 競爭對手

        v. 與...相匹敵

roar n/v. 咆哮,吼叫

robust adj. 強勁的;富有活力的

slippery adj. 滑的;光滑的



圖片

相關文章
相關標籤/搜索