pytorch之nn.Conv1d詳解
之前學習pytorch用於文字分類的時候,用到了一維卷積,花了點時間瞭解其中的原理,看網上也沒有詳細解釋的部落格,所以就記錄一下。
Conv1d
class torch.nn.Conv1d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
- in_channels(
int
) – 輸入訊號的通道。在文字分類中,即為詞向量的維度 - out_channels(
int
) – 卷積產生的通道。有多少個out_channels,就需要多少個1維卷積 - kerner_size(
int
tuple
) - 卷積核的尺寸,卷積核的大小為(k,),第二個維度是由in_channels來決定的,所以實際上卷積大小為kerner_size*in_channels - stride(
int
ortuple
,optional
) - 卷積步長 - padding (
int
ortuple
,optional
)- 輸入的每一條邊補充0的層數 - dilation(
int
ortuple
, `optional``) – 卷積核元素之間的間距 - groups(
int
,optional
) – 從輸入通道到輸出通道的阻塞連線數 - bias(
bool
,optional
) - 如果bias=True
舉個例子:
conv1 = nn.Conv1d(in_channels=256,out_channels=100,kernel_size=2)
input = torch.randn(32,35,256)
# batch_size x text_len x embedding_size -> batch_size x embedding_size x text_len
input = input.permute(0,2,1)
out = conv1(input)
print(out.size())
這裡32為batch_size,35為句子最大長度,256為詞向量
再輸入一維卷積的時候,需要將32*25*256變換為32*256*35,因為一維卷積是在最後維度上掃的,最後out的大小即為:32*100*(35-2+1)=32*100*34
附上一張圖,可以很直觀的理解一維卷積是如何用的:
圖中輸入的詞向量維度為5,輸入大小為7*5,一維卷積和的大小為2、3、4,每個都有兩個,總共6個特徵。
對於k=4,見圖中紅色的大矩陣,卷積核大小為4*5,步長為1。這裡是針對輸入從上到下掃一遍,輸出的向量大小為((7-4)/1+1)*1=4*1,最後經過一個卷積核大小為4的max_pooling,變成1個值。最後獲得6個值,進行拼接,在經過一個全連線層,輸出2個類別的概率。
附上一個程式碼來詳解:
其中,embedding_size=256, feature_size=100, window_sizes=[3,4,5,6], max_text_len=35
class TextCNN(nn.Module):
def __init__(self, config):
super(TextCNN, self).__init__()
self.is_training = True
self.dropout_rate = config.dropout_rate
self.num_class = config.num_class
self.use_element = config.use_element
self.config = config
self.embedding = nn.Embedding(num_embeddings=config.vocab_size,
embedding_dim=config.embedding_size)
self.convs = nn.ModuleList([
nn.Sequential(nn.Conv1d(in_channels=config.embedding_size,
out_channels=config.feature_size,
kernel_size=h),
# nn.BatchNorm1d(num_features=config.feature_size),
nn.ReLU(),
nn.MaxPool1d(kernel_size=config.max_text_len-h+1))
for h in config.window_sizes
])
self.fc = nn.Linear(in_features=config.feature_size*len(config.window_sizes),
out_features=config.num_class)
if os.path.exists(config.embedding_path) and config.is_training and config.is_pretrain:
print("Loading pretrain embedding...")
self.embedding.weight.data.copy_(torch.from_numpy(np.load(config.embedding_path)))
def forward(self, x):
embed_x = self.embedding(x)
#print('embed size 1',embed_x.size()) # 32*35*256
# batch_size x text_len x embedding_size -> batch_size x embedding_size x text_len
embed_x = embed_x.permute(0, 2, 1)
#print('embed size 2',embed_x.size()) # 32*256*35
out = [conv(embed_x) for conv in self.convs] #out[i]:batch_size x feature_size*1
#for o in out:
# print('o',o.size()) # 32*100*1
out = torch.cat(out, dim=1) # 對應第二個維度(行)拼接起來,比如說5*2*1,5*3*1的拼接變成5*5*1
#print(out.size(1)) # 32*400*1
out = out.view(-1, out.size(1))
#print(out.size()) # 32*400
if not self.use_element:
out = F.dropout(input=out, p=self.dropout_rate)
out = self.fc(out)
return out
embed_x一開始大小為32*35*256,32為batch_size。經過permute,變為32*256*35,輸入到自定義的網路後,out中的每一個元素,大小為32*100*1,共有4個元素。在dim=1維度上進行拼接後,變為32*400*1,在經過view,變為32*400,最後通過400*num_class大小的全連線矩陣,變為32*2。