【神经网络】CNN在Pytorch中的使用


因为研究方向为关系抽取,所以在文本的处理方面,一维卷积方法是很有必要掌握的,简单介绍下加深学习印象。

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维卷积
  • kernel_size(int or tuple) - 卷积核的尺寸,卷积核的大小为(k,),第二个维度是由in_channels来决定的,所以实际上卷积大小为kernel_size*in_channels
  • stride(int or tupleoptional) - 卷积步长
  • padding (int or tupleoptional)- 输入的每一条边补充0的层数
  • dilation(int or tuple, `optional``) – 卷积核元素之间的间距
  • groups(intoptional) – 从输入通道到输出通道的阻塞连接数
  • bias(booloptional) - 如果bias=True,添加偏置

有一位博主解释的很清楚,我附上他的内容,阅读连接我会在下方提供。

1 conv1 = nn.Conv1d(in_channels=256,out_channels=100,kernel_size=2)
2 input = torch.randn(32,35,256)
3 # batch_size x text_len x embedding_size -> batch_size x embedding_size x text_len
4 input = input.permute(0,2,1)
5 out = conv1(input)
6 print(out.size())

这里32为batch_size,35为句子最大长度,256为词向量

再输入一维卷积的时候,需要将32*35*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

 1 class TextCNN(nn.Module):
 2     def __init__(self, config):
 3         super(TextCNN, self).__init__()
 4         self.is_training = True
 5         self.dropout_rate = config.dropout_rate
 6         self.num_class = config.num_class
 7         self.use_element = config.use_element
 8         self.config = config
 9  
10         self.embedding = nn.Embedding(num_embeddings=config.vocab_size, 
11                                 embedding_dim=config.embedding_size)
12         self.convs = nn.ModuleList([
13                 nn.Sequential(nn.Conv1d(in_channels=config.embedding_size, 
14                                         out_channels=config.feature_size, 
15                                         kernel_size=h),
16 #                              nn.BatchNorm1d(num_features=config.feature_size), 
17                               nn.ReLU(),
18                               nn.MaxPool1d(kernel_size=config.max_text_len-h+1))
19                      for h in config.window_sizes
20                     ])
21         self.fc = nn.Linear(in_features=config.feature_size*len(config.window_sizes),
22                             out_features=config.num_class)
23         if os.path.exists(config.embedding_path) and config.is_training and config.is_pretrain:
24             print("Loading pretrain embedding...")
25             self.embedding.weight.data.copy_(torch.from_numpy(np.load(config.embedding_path)))    
26     
27     def forward(self, x):
28         embed_x = self.embedding(x)
29         
30         #print('embed size 1',embed_x.size())  # 32*35*256
31      # batch_size x text_len x embedding_size  -> batch_size x embedding_size x text_len
32         embed_x = embed_x.permute(0, 2, 1)
33         #print('embed size 2',embed_x.size())  # 32*256*35
34         out = [conv(embed_x) for conv in self.convs]  #out[i]:batch_size x feature_size*1
35         #for o in out:
36         #    print('o',o.size())  # 32*100*1
37         out = torch.cat(out, dim=1)  # 对应第二个维度(行)拼接起来,比如说5*2*1,5*3*1的拼接变成5*5*1
38         #print(out.size(1)) # 32*400*1
39         out = out.view(-1, out.size(1)) 
40         #print(out.size())  # 32*400 
41         if not self.use_element:
42             out = F.dropout(input=out, p=self.dropout_rate)
43             out = self.fc(out)
44         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。

我在关系抽取的论文阅读中,作者使用CNN卷积代码如下:

 1 class CNN3(nn.Module):
 2     def __init__(self, config):
 3         super(CNN3, self).__init__()
 4         self.config = config
 5         self.word_emb = nn.Embedding(config.data_word_vec.shape[0], config.data_word_vec.shape[1])
 6         self.word_emb.weight.data.copy_(torch.from_numpy(config.data_word_vec))
 7         self.word_emb.weight.requires_grad = False
 8 
 9         # self.char_emb = nn.Embedding(config.data_char_vec.shape[0], config.data_char_vec.shape[1])
10         # self.char_emb.weight.data.copy_(torch.from_numpy(config.data_char_vec))
11         # char_dim = config.data_char_vec.shape[1]
12         # char_hidden = 100
13         # self.char_cnn = nn.Conv1d(char_dim,  char_hidden, 5)
14 
15         self.coref_embed = nn.Embedding(config.max_length, config.coref_size, padding_idx=0)
16         self.ner_emb = nn.Embedding(7, config.entity_type_size, padding_idx=0)
17         input_size = config.data_word_vec.shape[1] + config.coref_size + config.entity_type_size #+ char_hidden
18         #140维
19         self.out_channels = 200
20         self.in_channels = input_size
21         self.kernel_size = 3
22         self.stride = 1
23         self.padding = int((self.kernel_size - 1) / 2)
24         self.cnn_1 = nn.Conv1d(self.in_channels, self.out_channels, self.kernel_size, self.stride, self.padding)
25         self.cnn_2 = nn.Conv1d(self.out_channels, self.out_channels, self.kernel_size, self.stride, self.padding)
26         self.cnn_3 = nn.Conv1d(self.out_channels, self.out_channels, self.kernel_size, self.stride, self.padding)
27         self.max_pooling = nn.MaxPool1d(self.kernel_size, stride=self.stride, padding=self.padding)
28         self.relu = nn.ReLU()
29         self.dropout = nn.Dropout(config.cnn_drop_prob)
30         self.bili = torch.nn.Bilinear(self.out_channels+config.dis_size, self.out_channels+config.dis_size, config.relation_num)
31         self.dis_embed = nn.Embedding(20, config.dis_size, padding_idx=10)
32 
33     # model(context_idxs, context_pos, context_ner, context_char_idxs, input_lengths, h_mapping, t_mapping, relation_mask, dis_h_2_t, dis_t_2_h)
34     def forward(self, context_idxs, pos, context_ner, context_char_idxs, context_lens, h_mapping, t_mapping, relation_mask, dis_h_2_t, dis_t_2_h):
35         # para_size, char_size, bsz = context_idxs.size(1), context_char_idxs.size(2), context_idxs.size(0)
36         # context_ch = self.char_emb(context_char_idxs.contiguous().view(-1, char_size)).view(bsz * para_size, char_size, -1)
37         # context_ch = self.char_cnn(context_ch.permute(0, 2, 1).contiguous()).max(dim=-1)[0].view(bsz, para_size, -1)
38 
39         # self.word_emb(context_idxs).shape = [40,512,config.data_word_vec.shape[1]]
40         # self.coref_embed(pos) = [40,512,config.coref_size]
41         # self.coref_embed(pos) = [40,512,config.coref_size]
42         sent = torch.cat([self.word_emb(context_idxs), self.coref_embed(pos), self.ner_emb(context_ner)], dim=-1)
43         sent = sent.permute(0, 2, 1)#torch.Size([40, 140, 512])
44 
45         # batch * embedding_size * max_len
46         x = self.cnn_1(sent)  #(b,140,512)->(b,200,512)
47         x = self.max_pooling(x)#(b,200,512)->(b,200,512)
48         x = self.relu(x)
49         x = self.dropout(x)
50 
51         x = self.cnn_2(x)#(b,200,512)->(b,200,512)
52         x = self.max_pooling(x)#(b,200,512)->(b,200,512)
53         x = self.relu(x)
54         x = self.dropout(x)
55 
56         x = self.cnn_3(x)#(b,200,512)->(b,200,512)
57         x = self.max_pooling(x)#(b,200,512)->(b,200,512)
58         x = self.relu(x)
59         x = self.dropout(x)
60         context_output = x.permute(0, 2, 1)     #(b,512,200)  因为每一步都pading=1加了两列,所以最后输出没有区别
61         start_re_output = torch.matmul(h_mapping, context_output)       #(b,1800,512)*(b,512,200) ->(b,1800,200)
62         end_re_output = torch.matmul(t_mapping, context_output)
63         s_rep = torch.cat([start_re_output, self.dis_embed(dis_h_2_t)], dim=-1)   #(b,1800,200+20)
64         t_rep = torch.cat([end_re_output, self.dis_embed(dis_t_2_h)], dim=-1)
65         predict_re = self.bili(s_rep, t_rep)   #(b,1800,97)
66         return predict_re

作者定义了三层CNN,第一层通过增加通道数并使用大小为3的卷积核来提取特征,为了使句子最大长度维度每次经过卷积层不变而设计了padding

并不是直接通过CNN进行关系分类,先通过第一层提取特征,后经过两层CNN(数据shape未变化,功能?),将通过3层CNN得到的context_ouput,此时context_output已经含有了一些文档级信息,然后将h_mapping中含有的头实体mask的信息与ontext_ouput相乘,将t_mapping中含有的尾实体mask的信息与ontext_ouput相乘。

接着将头实体到尾实体的距离特征与start_re_output的特征维度上进行拼接,将尾实体到头实体的距离特征与end_re_output的特征维度上进行拼接

最后将两个数据均送入与定义好的双线性层进行预测,得到预测的结果。

可以看到35-37行代码作者尝试使用Glove训练好的字符的预训练量,可能遇到问题放弃了。因此效果并不好。

Conv2d

 torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)

in_channels
 Number of channels in the input image
out_channels
 Number of channels produced by the convolution
kernel_size
卷积核尺寸
stride
步长,控制cross-correlation的步长,可以设为1个int型数或者一个(int, int)型的tuple。
padding
(补0):控制zero-padding的数目。
dilation
(扩张):控制kernel点(卷积核点)的间距
groups
 (卷积核个数):通常来说,卷积个数唯一,但是对某些情况,可以设置范围在1 —— in_channels中数目的卷积核:
bias
adds a learnable bias to the output.

 实例:

1 import torch
2 x = torch.randn(2,1,7,3)
3 conv = torch.nn.Conv2d(1,8,(2,3))
4 res = conv(x)
5 print(res.shape)    # shape = (2, 8, 6, 1)

输入x:

[ batch_size, channels, height_1, width_1 ]

batch_size  一个batch中样例的个数 2
channels  通道数,也就是当前层的深度 1
height_1 图片的高 7
width_1 图片的宽  3

Conv2d的参数

[ channels, output, height_2, width_2 ] 

channels 通道数,和上面保持一致,也就是当前层的深度 1
output 输出的深度  8
height_2 过滤器filter的高 2
weight_2 过滤器filter的宽  3

输出res

[batch_size,output,height_3,width_3]

batch_size 一个batch中样例的个数,同上 2
output 输出的深度 8
height_3  卷积结果的高度 h1-h2+1 = 7-2+1 = 6
weight_3 卷积结果的宽度 w1-w2+1 = 3-3+1 = 1

 Shape:

  $(N,C_{in},H_{in},W_{in})$

  $(N,C_{out},H_{out},W_{out})$

  $H_{out} = \left\lfloor\frac{H_{in} + 2 \times \text{padding}[0] - \text{dilation}[0] \times (\text{kernel_size}[0] - 1) - 1}{\text{stride}[0]} + 1\right\rfloor$

  $W_{out} = \left\lfloor\frac{W_{in} + 2 \times \text{padding}[1] - \text{dilation}[1] \times (\text{kernel_size}[1] - 1) - 1}{\text{stride}[1]} + 1\right\rfloor$

 

 

参考:

 

pytorch 中nn.MaxPool1d() 和nn.MaxPool2d()对比: https://www.jianshu.com/p/c5b8e02bedbe

pytorch中的nn.Bilinear的计算原理详解: https://blog.csdn.net/nihate/article/details/90480459

pytorch之nn.Conv1d详解:https://blog.csdn.net/sunny_xsc1994/article/details/82969867

pytorch中的matmul: https://blog.csdn.net/yu_1628060739/article/details/102720385

torch.nn.Conv2d()函数详解:https://blog.csdn.net/m0_37586991/article/details/87855342


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM