將文本當作一維圖像,從而能夠用一維卷積神經網絡來捕捉鄰近詞之間的關聯。數組
與⼆維卷積層⼀樣,⼀維卷積層使⽤⼀維的互相關運算。在⼀維互相關運算中,卷積窗⼝從輸⼊數組的最左⽅開始,按從左往右的順序,依次在輸⼊數組上滑動。當卷積窗⼝滑動到某⼀位置時,窗⼝中的輸⼊⼦數組與核數組按元素相乘並求和,獲得輸出數組中相應位置的元素。網絡
1 def corr1d(X, K): 2 w = K.shape[0] 3 Y = torch.zeros((X.shape[0] - w + 1)) 4 for i in range(Y.shape[0]): 5 Y[i] = (X[i: i + w] * K).sum() 6 return Y 7 8 #測試 9 X, K = torch.tensor([0, 1, 2, 3, 4, 5, 6]), torch.tensor([1, 2]) 10 corr1d(X, K) #output: tensor([ 2., 5., 8., 11., 14., 17.])
多輸⼊通道的⼀維互相關運算也與多輸⼊通道的⼆維互相關運算相似:在每一個通道上,將核與相應的輸⼊作⼀維互相關運算,並將通道之間的結果相加獲得輸出結果。app
1 def corr1d_multi_in(X, K): 2 # 首先沿着X和K的第0維(通道維)遍歷並計算一維互相關結果,而後將全部結果堆疊起來沿第0維累加 3 return torch.stack([corr1d(x, k) for x, k in zip(X, K)]).sum(dim=0) 4 5 # 測試 6 X = torch.tensor([[0,1,2,3,4,5,6], 7 [1,2,3,4,5,6,7], 8 [2,3,4,5,6,7,8]]) 9 K = torch.tensor([[1,2],[3, 4], [-1,-3]]) 10 corr1d_multi_in(X, K) # output: tensor([ 2., 8., 14., 20., 26., 32.])
textCNN中使⽤的時序最⼤池化(max-over-time pooling)層實際上對應⼀維全局最⼤池化層:假設輸⼊包含多個通道,各通道由不一樣時間步上的數值組成,各通道的輸出即該通道全部時間步中最⼤的數值。所以,時序最⼤池化層的輸⼊在各個通道上的時間步數能夠不一樣。因爲時序最⼤池化的主要⽬的是抓取時序中最᯿要的特徵,它一般能使模型不受⼈爲添加字符的影響。ide
1 class GlobalMaxPool1d(nn.Module): 2 def __init__(self): 3 super(GlobalMaxPool1d, self).__init__() 4 def forward(self, x): 5 # x shape: (batch_size, channel, seq_len) 6 return F.max_pool1d(x, kernel_size=x.shape[2]) # shape:(batch_size, channel, 1)
textCNN模型主要使⽤了⼀維卷積層和時序最⼤池化層。假設輸⼊的⽂本序列由 個詞組成,每一個詞⽤維的詞向量表示。那麼輸⼊樣本的寬爲 ,⾼爲1,輸⼊通道數爲 。 textCNN的計算主要分爲如下⼏步。
1. 定義多個⼀維卷積核,並使⽤這些卷積覈對輸⼊分別作卷積計算。寬度不一樣的卷積核可能會捕捉到不一樣個數的相鄰詞的相關性。
2. 對輸出的全部通道分別作時序最⼤池化,再將這些通道的池化輸出值連結爲向量。
3. 經過全鏈接層將連結後的向量變換爲有關各種別的輸出。這⼀步可使⽤丟棄層應對過擬合。函數
1 class TextCNN(nn.Module): 2 def __init__(self, vocab, embed_size, kernel_sizes, num_channels): 3 super(TextCNN, self).__init__() 4 self.embedding = nn.Embedding(len(vocab), embed_size) 5 # 不參與訓練的嵌入層 6 self.constant_embedding = nn.Embedding(len(vocab), embed_size) 7 self.dropout = nn.Dropout(0.5) 8 self.decoder = nn.Linear(sum(num_channels), 2) 9 # 時序最大池化層沒有權重,因此可共用一個實例 10 self.pool = GlobalMaxPool1d() 11 self.convs = nn.ModuleList() # 建立多個一維卷積層 12 for c, k in zip(num_channels, kernel_sizes): 13 self.convs.append(nn.Conv1d(in_channels = 2 * embed_size, 14 out_channels = c, 15 kernel_size = k)) 16 17 def forward(self, inputs): 18 # 將兩個形狀是(批量大小,詞數,詞向量維度)的嵌入層的輸出按詞向量鏈接 19 embeddings = torch.cat(( 20 self.embedding(inputs), 21 self.constant_embedding(inputs)), dim=2) # (batch_size, seq_len, 2*embed_size) 22 # 根據Conv1d要求的輸入格式,將詞向量維,即一維卷積層的通道維變換到前一維 23 embeddings = embeddings.permute(0, 2, 1) 24 # 對於每一個一維卷積層,在時序最大池化後會獲得一個形狀爲(批量大小,通道大小,1) 25 # 的Tensor.使用flatten函數去掉最後一維,而後在通道維上鍊接 26 encoding = torch.cat([self.pool(F.relu(conv(embedding))).squeeze(-1) for conv in self.convs], dim=1) 27 # 應用丟棄法後使用全鏈接層獲得輸出 28 outputs = self.decoder(self.dropout(encoding)) 29 return outputs 30 31 embed_size, kernel_sizes, nums_channels = 100, [3, 4, 5], [100, 100, 100] 32 net = TextCNN(vocab, embed_size, kernel_sizes, nums_channels)
OK,記錄一下模型,以上內容都來自《動手學深度學習》這本書。學習