1. 程式人生 > 實用技巧 >記初次除錯CNN做文字向量表示

記初次除錯CNN做文字向量表示

心得:
卷積操作在影象處理的領域應用廣泛,影象做卷積處理有一個天然的好處,即:每個畫素點的位置與相鄰位置相對固定,也就是說,除了邊緣,每一個節點都有相同數量的相鄰節點。所以卷積操作相對容易。

但是,隨著文字表示的發展,用向量來表示文字的思想應用的越來越廣泛深入。textCNN是我理解的卷積在文字表示學習的開山之作,我們知道,影象領域的卷積操作,可以通過不同的卷積核收取一副或一組影象(向量)中的多個特徵,那麼類比來說,文字上的卷積操作,同樣也是向量的卷積操作,一樣可以提取文字向量的特徵。

textCNN把文字描述成:詞嵌入的序列,卷積操作不再左右滑動,而是隻上下滑動,每次卷積操作都涉及到了上下幾行(與卷積核的尺寸有關)特徵也就是單詞,所以我們說卷積操作可以提取到文字的上下文資訊,可以更好的表示文字資訊。

操作:
torch框架作為流行的深度智慧框架,配置簡單,操作更加適合大眾思維。因此嘗試使用CNN做文字的向量表示:

(1) textCNN class
這個是搭建的建議CNN框架,包含基礎的conv層、ReLU、MeanPooling層。程式碼如下

  import sys
  
  sys.path.append('/home/student/xxx/project/Branch1/')
  # sys.path.append('E:/for_study/pythonMLSpace/Branch1/')
  from torch import nn as nn
  import torch
  import math
  
  
  class bTextCNN(nn.Module):
      def __init__(self, param):
          super(bTextCNN, self).__init__()
          ci = 1  # RGB的通道數 文字的話相當於灰度圖只一個通道
          kernel_num = param['kernel_num']  # 卷積核數量,輸出向量維度
          kernel_size = param['kernel_size']  # 卷積核尺寸
          vocab_size = param['vocab_size']  # 文字長度n,word-level
          embed_dim = param['embed_dim']  # 輸入詞嵌入的維度
          dropout = param['dropout']  # dropout比率
          padding = param['padding']
          # class_num = param['class_num']  # 分類數量
          self.param = param
  
          # 兩層卷積
          # self.conv1 = nn.Conv2d(in_channels=ci, out_channels=kernel_num, kernel_size=(kernel_size[0], embed_dim))
          # self.relu1 = nn.ReLU(True)
          # self.avgpool1 = nn.AvgPool2d(kernel_size=16)
  
  
  
          layer1 = nn.Sequential()
          layer1.add_module('CONV1', nn.Conv2d(in_channels=ci, out_channels=kernel_num, kernel_size=(kernel_size[0], embed_dim), padding=padding))
          layer1.add_module('RELU1', nn.ReLU(True))
          layer1.add_module('POOL1', nn.AvgPool2d(kernel_size=vocab_size))
          self.layer1 = layer1
  
          layer2 = nn.Sequential()
          layer2.add_module('CONV2', nn.Conv2d(in_channels=ci, out_channels=kernel_num, kernel_size=(kernel_size[0], kernel_num)))
          layer2.add_module('RELU2', nn.ReLU(True))
          layer2.add_module('POOL2', nn.AvgPool2d(kernel_size=vocab_size))
          self.layer2 = layer2
  
          dropoutlayer = nn.Sequential()
          dropoutlayer.add_module('DROPOUT', nn.Dropout(dropout))
          self.dropoutlayer = dropoutlayer
  
          # self.fc1 = nn.Linear(len(kernel_size) * kernel_num, class_num)  # 全連線層
  
      def forward(self, x):
          x = x.unsqueeze(0)  # 增加一個維度,使之適應CNN
          x = x.unsqueeze(0)  # 增加一個維度,使之適應CNN
          # print('input尺寸:' + str(x.size()))
          # out = self.conv1(x)
          # print('conv後尺寸:' + str(out.size()))
          # out = self.relu1(out)
          # print('relu後尺寸:' + str(out.size()))
          # out = self.avgpool1(out)
          # print('pooling後尺寸:' + str(out.size()))
          out = self.layer1(x)
          # out = self.layer2(out)
          out = self.dropoutlayer(out)
          return out
  
      def init_weight(self):
          for m in self.modules():
              if isinstance(m, nn.Conv2d):
                  n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                  m.weight.data.normal_(0, math.sqrt(2. / n))
                  if m.bias is not None:
                      m.bias.data.zero_()
              elif isinstance(m, nn.BatchNorm2d):
                  m.weight.data.fill_(1)
                  m.bias.data.zero_()
              elif isinstance(m, nn.Linear):
                  m.weight.data.normal_(0, 0.01)
                  m.bias.data.zero_()

(2) CNN結構設計好之後,需要考慮卷積等的引數:
param如下:

  textCNN_params = {
      "vocab_siz"': 1000,  # 文字長度,word-level
      "embed_dim": 100,  # 詞向量維度
      "kernel_num": 128,  # 卷積核數量
      "kernel_size": [3],  # 卷積核尺寸
      "dropout": 0.2,  # dropout比例
      "padding": 1    # 新增邊緣
  }

引數的vocab_size其實不需要設計,但是padding引數,如果補設為1,執行到小於卷積核尺寸的詞向量序列時,會報錯,報錯資訊如下:

  RuntimeError: cuDNN error: CUDNN_STATUS_BAD_PARAM

(3) 傳入的詞嵌入序列:

  curr_word_vec1 = torch.from_numpy(np.array(question_vec_secqence[0:textCNN_params['vocab_size']]))

curr_word_vec1 的結構是二維陣列:[[w1的嵌入], [w2的嵌入], [w3的嵌入]...]