Convolutional Neural Networks for Sentence Classification阅读梗概

该博客介绍了将预训练的word2vec词向量与卷积神经网络(CNN)结合,应用于句子级别的分类任务。作者通过简单CNN模型展示了即使静态词向量也能取得不错的效果,同时探讨了允许词向量学习以提升性能的可能性。模型结构包括多卷积核和最大池化层,以及应用了l2正则化和dropout防止过拟合。虽然最终F1值未达论文水平,但作者提供了实现代码以展示模型架构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开端

本人有幸加入哈工大赛尔NLP实验室。慢慢学习与NLP有关的知识。这篇论文是要求阅读项目中的一篇。本文也不算翻译,只是总结每一部分的重点。我希望能够通过这种写博客的方式巩固自己的知识,也和大家一起分享。

综述

这篇论文介绍的是在word2vec上预训练词向量使用卷积神经网络完成句子级别的分类任务。简单的CNN使用静态的词向量可以达到多个好结果在多任务上。如果进一步允许学习词向量能够获得更好的表现。

介绍

介绍部分介绍了深度学习模型在计算机视觉和语音识别获得了很好的成果。许多深度学习方面的研究都使用了从语言模型获得的词向量表示。
词向量把单词从稀疏的独热码向量投影到一个低维的紧密表示。在这个表示中相似的词向量的距离相近。
卷积神经网络使用卷积滤波器获得局部特征。一开始使用在计算机视觉领域,逐渐在NLP领域获取了成果。
在目前的工作中,我们训练一个CNN一层卷积在词向量上。一开始我们的词向量静止。尽管模型很简单,还是取得了不错的成果。

模型

模型架构
xi∈Rk表示第i个单词由一个k维向量表示。长度为n的句子表示为
x1:n = x1 ⊕ x2 ⊕ . . . ⊕ xn ⊕表示向量的连接。
xi:i+j 表示对向量i…i+j连接
ci = f(w · xi:i+h−1 + b)表示了一个特征。其实就是把i…i+h-1的向量首尾连接,与w做向量乘法,得到一个值。这样做其是和卷积等价的。h是窗口大小。w∈R(h*k)是一个卷积核。
对每个窗口计算特征,把他们连到一起c = [c1, c2, . . . , cn−h+1]得到一个特征图。c ∈ Rn−h+1
然后使用最大池化cˆ = max{c},为了捕获最明显的特征。
上面过程是使用一个的卷积核。模型中使用多个卷积核,来获取多个特征图。这些来自多卷积核的特征最后连接到一起放入softmax分类器中。
模型还实验了多输入,意思是一个向量静态一个向量动态然后两个结果相加。

正则化

在这个论文中,正则化使用了l2正则化和dropout层。
dropout用来阻止过拟合。就是让神经元以一定概率失活。
就是在倒数第二层中训练中
z = [ˆc1, . . . , cˆm]
不使用y = w · z + b
而是y = w · (z ◦ r) + b,
r是一个向量取值从0,1按伯努利分布随机采样按dropout_rate也就是论文中的p为等于1的概率。
然后测试的时候,按照p来缩放向量而不dropout。

训练结果

训练结果我就不详细写了。
几个数据集

最后附上一份自己的代码

没有用训练好的词向量,就是学习架构用的,最后F1值也没有达到论文水平,72左右。

import torch
import torch.nn as nn
import torch.nn.functional as F

class MyModel(nn.Module):

    def __init__(self, embeddings, n_features, n_classes, batch_size, dropout_prob=0.5):
        super().__init__()
        self.embeddings = embeddings
        self.n_features = n_features
        self.n_classes = n_classes
        self.dropout_prob = dropout_prob
        self.batch_size = batch_size
        self.embed_size = embeddings.shape[1]
        self.my_embeddings = nn.Embedding(embeddings.shape[0], self.embed_size)
        #[batch_size,seq_len]输入 每一列代表一句
        #[batch_size,seq_len,embedding_size]输出,每列变多维度
        self.my_embeddings.weight = nn.Parameter(torch.tensor(embeddings))
        self.fc1_1 = nn.Conv1d(in_channels=self.embed_size,out_channels=100,kernel_size = 1)
        #[batch_size,embedding_size,seq_len]输入,每列变多维度,一维卷积是在最后维度上扫
        #3*[batch_size,100,seq_len-3+1]
        self.fc1_2 = nn.Conv1d(in_channels=self.embed_size, out_channels=100, kernel_size=4)
        self.fc1_3 = nn.Conv1d(in_channels=self.embed_size, out_channels=100, kernel_size=5)
        self.fc1_1_2 = nn.ReLU()
        self.fc1_2_2 = nn.ReLU()
        self.fc1_3_2 = nn.ReLU()
        #做池化 [batch_size,100,seq_len-3+1]输入
        #[batch_size,300,1]输出
        #线性层,每列变多维度
        self.fc3_1 = nn.Linear(in_features = 300, out_features =100)
        self.fc3_2 = nn.ReLU()
        self.fc3_3 = nn.Dropout(0.5)
        self.fc4_1 = nn.Linear(in_features=100, out_features=50)
        self.fc4_2 = nn.ReLU()
        self.fc4_3 = nn.Dropout(0.5)
        self.fc5= nn.Linear(in_features=50,out_features=2)
        self.softmax = nn.Softmax(dim=1)
    def forward(self,x):
        x = x.long()
        x = self.my_embeddings(x)
        x = x.permute([0,2,1]).to(torch.float32)
        x1 = self.fc1_1(x)
        x2 = self.fc1_2(x)
        x3 = self.fc1_3(x)
        x1 = F.relu(self.fc1_1_2(F.max_pool1d(x1, kernel_size = x1.shape[2])))
        x2 = F.relu(self.fc1_2_2(F.max_pool1d(x2, kernel_size=x2.shape[2])))
        x3 = F.relu(self.fc1_3_2(F.max_pool1d(x3, kernel_size=x3.shape[2])))
        x = torch.cat((x3, x2, x1),1).squeeze(2)
        x = self.fc3_1(x)
        x = self.fc3_2(x)
        x = self.fc3_3(x)
        x = self.fc4_1(x)
        x = self.fc4_2(x)
        x = self.fc4_3(x)
        x = self.fc5(x)
        return self.softmax(x)
##训练过程
from torch.utils.data.dataloader import DataLoader
import model
import dataget
import torch.optim as optim
from sklearn.metrics import f1_score, confusion_matrix, accuracy_score, classification_report, precision_recall_fscore_support
import matplotlib.pyplot as plt
mydataset = dataget.MyDataSet()
mymodel = model.MyModel(batch_size=64,dropout_prob=0.5,embeddings=mydataset.word2vec,n_classes=2,n_features=mydataset.maxlen)
mydatasetloader = DataLoader(dataset=mydataset,batch_size=64)
mytestdatasetloader = DataLoader(dataset=dataget.MytestSet(mydataset),batch_size=64)
loss_function = torch.nn.CrossEntropyLoss()
optimzer = optim.Adam(mymodel.parameters(),lr=0.0001)
history_loss = list()
for epoches in range(100):
    tr_loss = 0
    nb_tr_steps = 0
    train_logits_list = []
    train_labels_list = []

    tv_loss = 0
    nb_tv_steps = 0
    valid_logits_list = []
    valid_labels_list = []
    mymodel.train()
    for data,mask,labels in tqdm(mydatasetloader):

        predict = mymodel(data)
        labels = labels.long()
        print(labels.shape)
        print(predict.shape)
        #print(predict)
        loss = loss_function(predict, labels)
        loss.backward()
        predict = predict.view(-1, 2).detach().cpu().numpy()
        labels = labels.view(-1).to('cpu').numpy()

        train_logits_list += [int(x) for x in np.argmax(predict, axis=1)]
        train_labels_list += [int(x) for x in labels]
        tr_loss = tr_loss+loss.item()
        nb_tr_steps = nb_tr_steps +1
        optimzer.step()
        optimzer.zero_grad()
    train_loss = tr_loss
    train_accuracy = metrics.accuracy_score(train_labels_list, train_logits_list)
    train_w_f1 = metrics.f1_score(train_labels_list, train_logits_list, average='weighted')

    mymodel.eval()
    for data,mask,labels in tqdm(mytestdatasetloader):
        predict = mymodel(data)
        labels = labels.long()
        loss = loss_function(predict, labels)
        tv_loss = tv_loss+loss.item()
        nb_tv_steps = nb_tv_steps +1
        predict = predict.view(-1, 2).detach().cpu().numpy()
        labels = labels.view(-1).to('cpu').numpy()
        valid_logits_list += [int(x) for x in np.argmax(predict, axis=1)]
        valid_labels_list += [int(x) for x in labels]
    valid_loss = tv_loss
    valid_accuracy = metrics.accuracy_score(valid_labels_list, valid_logits_list)
    valid_w_f1 = metrics.f1_score(valid_labels_list, valid_logits_list, average='weighted')
    history_loss.append(valid_loss)
    print('\nEpoch %d, train_loss=%.5f, train_acc=%.2f, train_w_f1=%.2f,valid_loss=%.5f, valid_acc=%.2f, valid_w_f1=%.2f'
          %(epoches, train_loss, train_accuracy * 100, train_w_f1 * 100,valid_loss, valid_accuracy * 100, valid_w_f1 * 100))







fig=plt.figure(num=1,figsize=(4,4))
plt.subplot(111)
plt.plot(np.arange(0,len(history_loss)),history_loss)
plt.show()
##数据处理
import random

import chardet
import torch
import pickle
import numpy as np
from torch.utils.data.dataset import Dataset
from PIL import Image
import numpy as np
import torch

from tqdm import tqdm


def get_word2id(data_paths):
    print('加载数据集')
    wordid = {'PAD': '0'}
    for path in data_paths:
      with open(path,encoding="Windows-1252") as f:
        for line in tqdm(f):
            words = line.strip().split()
            for word in words:
                if word not in wordid.keys():
                    wordid[word] = len(wordid)
    print(wordid)
    return wordid


def get_word2vec(word2id):
    word2vec = np.array(np.random.uniform(-1., 1., [len(word2id) + 1, 50]))
    #apple = ge.models.KeyedVectors.load_word2vec_format("GoogleNews-vectors-negative300.bin.gz", binary=True)
    return word2vec


def get_corpus(word2id):
    print('loading corpus...')
    contents = list()
    labels = list()
    masks = list()
    maxlen = 0
    with open('E:\\python_project\\dataset\\rt-polaritydata\\rt-polarity.neg', encoding="Windows-1252") as f:
        for line in tqdm(f):
            words = line.strip().split()
            if not words: continue
            content = [word2id[word] for word in words]
            if len(content) >maxlen:maxlen = len(content)
            label = 0
            contents.append(content)
            labels.append(label)
    with open('E:\\python_project\\dataset\\rt-polaritydata\\rt-polarity.pos', encoding="Windows-1252") as f:
        for line in tqdm(f):
            words = line.strip().split()
            if not words: continue
            content = [word2id[word] for word in words]
            if len(content) > maxlen: maxlen = len(content)
            label = 1
            contents.append(content)
            labels.append(label)
    for index,line in enumerate(contents):
        addline = [0 for i in range(maxlen - len(line))]
        mask = [1 for i in range(len(line))]
        line = line + addline
        mask  = mask + addline
        masks.append(mask)
        contents[index] = line
    cc = list(zip(contents,labels))
    random.shuffle(cc)
    contents[:], labels[:] = zip(*cc)
    return contents,masks,labels,maxlen


class MyDataSet(Dataset):
    def __init__(self):
        datapath = ['E:\\python_project\\dataset\\rt-polaritydata\\rt-polarity.pos',
                    'E:\\python_project\\dataset\\rt-polaritydata\\rt-polarity.neg']
        self.word2id = get_word2id(data_paths=datapath)
        self.corpus = get_corpus(self.word2id)
        self.word2vec = get_word2vec(self.word2id)
        self.maxlen = self.corpus[3]
    def __len__(self):
        return 8000

    def __getitem__(self, index):
        '''
        :param index:
        :return: 编码,掩码,标签
        '''
        return np.array(self.corpus[0][index]), np.array(self.corpus[1][index]),np.array(self.corpus[2][index])

class MytestSet(Dataset):
    def __init__(self,MyDataSet):
        self.mydataset = MyDataSet
    def __len__(self):
        return 2050

    def __getitem__(self, index):
        '''
        :param index:
        :return: 编码,掩码,标签
        '''
        return np.array(self.mydataset.corpus[0][index+8000]), np.array(self.mydataset.corpus[1][index+8000]), np.array(self.mydataset.corpus[2][index+8000])
### 回答1: 卷积神经网络 (Convolutional Neural Networks, CNN) 是一种常用于文本分类的深度学习模型。它通过卷积和池化层来提取文本中的特征,并使用全连接层来进行分类。 CNN 的一个优点是能够处理变长的输入,并且不需要对文本进行预处理。 ### 回答2: 卷积神经网络是一种深度学习方法,用于对文本进行分类。在训练过程中,这种网络可以自动学习输入数据的特征表示。卷积神经网络中的卷积层可以识别输入中的局部模式,这些局部模式组合起来形成更高级别的特征,最终帮助分类器确定类别。对于文本分类问题,卷积神经网络的输入是文本的词嵌入向量,可以从先验知识中自动学习特征。 在一些文本分类任务中,卷积神经网络已经取得了很好的表现。文本分类任务通常被分为两种类型:二元分类和多分类。二元分类任务是指将数据分为两类,例如垃圾邮件和非垃圾邮件。多类分类任务是指将数据分为多类,例如新闻分类。在这两种任务中,卷积神经网络都能够进行有效的分类。 对于二元分类任务,卷积神经网络可以使用一个输出节点,并使用 sigmoid 激活函数将输入映射到 0 到 1 之间的概率。对于多分类任务,卷积神经网络可以使用多个输出节点,每个节点对应一个类别,并使用 softmax 激活函数将输入映射到 0 到 1 之间,并且所有输出节点的和为 1。 要训练卷积神经网络进行文本分类,需要对模型进行三个主要的训练步骤。首先,需要构建词嵌入矩阵,该矩阵将文本中的每个词都映射到一个向量。然后,需要将文本数据转换为卷积神经网络所需的格式。最后,需要对模型进行训练,并根据测试数据进行评估。 总之,卷积神经网络已经被证明是一种强大的工具,可以用于文本分类等任务。在处理文本数据时,卷积神经网络可以自动学习输入数据的特征表示,并使用这些特征来确定文本的类别。 ### 回答3: 卷积神经网络(CNN)是一种深度学习模型,它在图像识别、计算机视觉和自然语言处理中表现出色。最近几年,CNN 在句子分类中也获得了很大的成功。 CNN 句子分类模型的输入是一个序列,输出是类别标签。与传统的 RNN 模型不同之处在于,CNN 可以使每个神经元只能捕获一个固定大小的区域的特征,从而加快模型的训练和降低了模型的复杂度。 CNN 句子分类模型的基本架构包括词嵌入层、卷积层、池化层和全连接层。词嵌入层将输入的文本转化为向量表示。卷积层通过滑动窗口对输入的序列进行卷积操作,提取出局部特征。池化层在每个滑动窗口上提取出一个最大值或平均值,进一步降低维度。最后,全连接层将提取出的特征传递到输出层进行分类。 CNN 句子分类模型的优点在于它可以处理不定长的文本序列,并在仅有少量特征的情况下表现出色。但是,CNN 模型的缺点在于不善于处理长期依赖关系,例如情感分析中的Irony识别。为了解决这个问题,可以引入 RNN 或 Transformer 等模型。 总的来说,CNN 模型为句子分类问题提供了一个简单有效的解决方案。在实践中,需要根据具体的任务选择合适的模型结构和参数设置,才能取得最佳效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值