记初次调试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的嵌入]...]
心有远方 何惧苟且