Bert+CNN文本分类(含代码实现)

目录

一、什么是CNN?

CNN的特点

二、 CNN模型构造过程

2.1 Convolution(卷积)

2.2 Max Pooling(池化)

2.3 Flatten

三、TextCNN

3.1 什么是TextCNN

3.2 TextCNN与Image-CNN对比​

四、CNN模型分解

4.1 输入层

4.2 卷积层

4.3 池化层 

4.4 全连接层

五、数据集及BERT+CNN维度分析

5.1 参数及维度介绍

5.2 数据集介绍

5.3 预训练模型

5.4 Bert模型参数分析

六、BERT特征提取

6.1 生成BERT Embedding

6.2 微调(Fine tuning)

七、Bert+CNN文本分类代码实现

7.1 代码下载地址

7.2 过程分析

7.3 维度分析

7.3.1 参数定义

7.3.2 卷积+池化

7.3.3 整体维度转换图 


一、什么是CNN?

        将神经网络应用于大图像时,输入可能有上百万个维度,如果输入层和隐含层进行“全连接”,需要训练的参数将会非常多。如果构建一个“部分联通”网络,每个隐含单元仅仅只能连接输入单元的一部分,参数数量会显著下降。卷积神经网络就是基于这个原理而构建的。这其中的思想就是,降维或者说是特征选择,通过前面的卷积层或者池化层将重要的特征选取出来,然后全连接进行分类。特征是最重要的。 

CNN的特点

(1)特定特征位移不变性

(2)特征缩放不变性

二、 CNN模型构造过程

2.1 Convolution(卷积)

Filter 先初始化,然后在训练过程中学习更新新的参数

 (1)当stride=1且Filter如图所示时

 

(2)Feature Map

 如图,一张图有RGB三个通道,则Filter也有三个通道

 

                                 

(1)获取关键特征(减少参数)

(2)共享权重(再次减少参数)

2.2 Max Pooling(池化)

(1)Filter结果

(2)取各个单元最大值 

  

2.3 Flatten

向量转换为如图所示可以进入全连接层的向量模式

三、TextCNN

3.1 什么是TextCNN

上图可理解为: 

通过不同的size(如上图2、3、4等),上图红色代表 size=2,黄色代表 size=3。两个size=2,两个size=3的不同kernel,4个单词得到4个不同长度的向量,经过polling得到4个向量的拼接,经过CNN的Flatten,得到一个可以输入到全连接层,结果通过一个softmax等线性函数,得到一个概率,实现分类任务的输出。下图可以更直观的表达具体实现思路

3.2 TextCNN与Image-CNN对比

        可以看到,每次在卷积的时候,都是整行整行的进行的。这好比是n-gram模型,如果每两行conv一次,那么就是2-gram,要知道,google最多也不过使用了5-gram模型,因为这种模型计算量非常大,但是如果在CNN中进行类似的操作,计算量反而减小了。在第二层的卷积层中,我们可以看到,每次得到的Feature Map的行数,是和输入的sentence的长度相关的。但是,在池化层,采用了1-max pooling的技术,从而将每个Feature Map的维度全部下降为1,因此,pooling结束之后,得到的向量的维度,就是卷积层Feature Map的数量。这样也便解决了输入长度的问题。但是,接下来无法再进行conv操作了,而且,在这个应用里,也不会出现像LeNet-5那样的Feature Map的组合输出的现象,因为只有一层卷积层。那么这里问题就来了,这样单层的结构到底效果如何?能不能扩展成多层的结构呢?能不能使用Feature Map的组合策略呢?单从Yoon的paper来看,结果是不错的,可是这样的结构一定适合其他应用问题吗?如果我们在conv操作的时候,不整行整行的卷积,那么这样就无法用n-gram去解释了,但是这样的效果如何呢?其实也有人做过了,是Nal的Paper,参考文献[4]. 个人不推荐这种部分卷积的做法。

作者:MiracleJQ
链接:https://www.jianshu.com/p/1267072ee8f8
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

四、CNN模型分解

4.1 输入层

        这里的输入层显示有两个channel,其实我们可以看作是一个,因为后文中说到这两个channel分别是static和non-static,即使用的词向量是否随着训练发生变化。non-static就是词向量随着模型训练变化(Fine tune),这样的好处是词向量可以根据数据集做适当调整,但是CS224d课程里也说过当数据集较小时不推荐此操作,否则容易产生过拟合现象。static就是直接使用word2vec训练好的词向量即可。此外,由图可知,输入层是将一个句子所有单词(padding)的词向量进行拼接成一个矩阵,每一行代表一个词。每个句子固定20个词,如果不够的补padding。 

4.2 卷积层

        卷积层,不做过多解释。每个卷积核的大小为filter_size*embedding_size。filter_size代表卷积核纵向上包含单词个数,即认为相邻几个词之间有词序关系,代码里使用的是[3,4,5]。embedding_size就是词向量的维数。每个卷积核计算完成之后我们就得到了1个列向量,代表着该卷积核从句子中提取出来的特征。有多少和卷积核就能提取出多少种特征,即图中在纵深方向上channel的数量。

卷积层:

1. 由不同窗口大小的Filter构成;

2. Filter个数自己定义,超参数;

3. 同一个Filter参数共享,极大减少参数个数;

4. 因为参数共享,所以一个Filter只能识别同一类特征;

5. 一个Filter就是一类特征识别区;

6. 窗口大小其实就是识别n-gram信息。 

4.3 池化层 

        池化层。文中提到pooling操作就是将卷积得到的列向量的最大值提取出来。这样pooling操作之后我们会获得一个num_filters维的行向量,即将每个卷积核的最大值连接起来。这样做还有一个好处就是,如果我们之前没有对句子进行padding操作,那么句子的长度是不同的,卷积之后得到的列向量维度也是不同的,可以通过pooling来消除句子之间长度不同的差异。 

池化层:

1. 一般是对每个Filter向量取max值;

2. max的意思是:这个特征跟出现位置无关,不论出现在哪里,都取最大值;

3. 有多少个Filter决定了池化层的维度;

4. 变长的输入变成了定长,无论输入多长,都在此统一维度。

4.4 全连接层

        全连接层,为了将pooling层输出的向量转化为我们想要的预测结果,加上一个softmax层即可。针对电影评价的分类任务,就是将其转化为正面、负面两个结果。文中还提到了过拟合的问题,因为实验中所使用的数据集相对较小,很容易就会发生过拟合现象,在实验过程中也会发现当迭代3000多轮的时候准确率就会接近1。所以这里引如dropout来减少过拟合现象。此外还可以考虑L2正则化等方法实现防止过拟合的功能。

全连接层:

1. 三层神经网络的隐藏层到输出层的映射;

2. Dropout防止过拟合。 

五、数据集及BERT+CNN维度分析

5.1 参数及维度介绍

  • BERT-Large, Uncased (Whole Word Masking): 24-layer, 1024-hidden, 16-heads, 340M parameters
  • BERT-Large, Cased (Whole Word Masking): 24-layer, 1024-hidden, 16-heads, 340M parameters
  • BERT-Base, Uncased: 12-layer, 768-hidden, 12-heads, 110M parameters
  • BERT-Large, Uncased: 24-layer, 1024-hidden, 16-heads, 340M parameters
  • BERT-Base, Cased: 12-layer, 768-hidden, 12-heads , 110M parameters
  • BERT-Large, Cased: 24-layer, 1024-hidden, 16-heads, 340M parameters
  • BERT-Base, Multilingual Cased (New, recommended): 104 languages, 12-layer, 768-hidden, 12-heads, 110M parameters
  • BERT-Base, Multilingual Uncased (Orig, not recommended) (Not recommended, use Multilingual Cased instead): 102 languages, 12-layer, 768-hidden, 12-heads, 110M parameters
  • BERT-Base, Chinese: Chinese Simplified and Traditional, 12-layer, 768-hidden, 12-heads, 110M parameters
  • 我们用的是BERT-Base-Chinese:12-layer,768-hidden,12-heads

    其中layer表示的是encoder的层数,hidden表示的是隐藏层的大小(也就是最后的前馈网络中的神经元个数,等同于特征输出维度)。

    BERT预训练数据集来源:BERT预培训程序大体上遵循现有的关于语言模型的预训练。对于预训练的语料库,BERT使用BooksCorpus(8亿字)(Zhu et al, 2015)和英语维基百科(2,500M字)。对于维基百科,只提取文本段落而忽略了列表、表格和标题。这一点至关重要使用文档级的语料库而非洗牌的句子级语料库,如Billion字基准(Chelba等人,2013),以便于提取长的连续序列。 

    除此之外,谷歌还提供了BERT-uncased与BERT-cased格式,分别对应是否包含大小写。一般来说,BERT-uncased(仅包含小写)比较常用,因为大部分场景下,单词是否大小写对任务的影响并不大。但是在部分特定场景,例如命名体识别(NER),则BERT-cased是更合适的。

    在应用BERT预训练模型时,实际上就是迁移学习,所以用法就是2个:

    1. 特征提取(feature extraction)
    2. 微调(fine-tune)

    5.2 数据集介绍

    我从THUCNews中抽取了20万条新闻标题,已上传至github,文本长度在20到30之间。一共10个类别,每类2万条。数据以字为单位输入模型。

    类别:财经、房产、股票、教育、科技、社会、时政、体育、游戏、娱乐。

    数据集划分:

    数据集 数据量
    训练集 18万
    验证集 1万
    测试集 1万

    更换自己的数据集 

  • 按照我数据集的格式来格式化你的中文数据集。
  • 5.3 预训练模型

    bert模型放在 bert_pretain目录下,ERNIE模型放在ERNIE_pretrain目录下,每个目录下都是三个文件:

  • pytorch_model.bin
  • bert_config.json
  • vocab.txt
  • 预训练模型下载地址:
    bert_Chinese: 模型 https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-chinese.tar.gz
    词表 https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-chinese-vocab.txt
    来自这里
    备用:模型的网盘地址:bert-base-chinese.tar.gz_免费高速下载|百度网盘-分享无限制

    ERNIE_Chinese: http://image.nghuyong.top/ERNIE.zip
    来自这里
    备用:网盘地址:ERNIE.zip_免费高速下载|百度网盘-分享无限制

    解压后,按照上面说的放在对应目录下,文件名称确认无误即可。

    5.4 Bert模型参数分析

    输入

    Inputs
    shape
    introduction
    input_ids
    [batch_size, sequence_length]
    一个形状为[batch_size, sequence_length]的Torch.LongTensor。词汇表中的词标索引
    token_type_ids
    [batch_size, sequence_length]
    一个可选的touch.LongTensor,其形状为[batch_size, sequence_length],token
                类型的索引选择在[0, 1]。类型0对应于 "句子A",类型1对应于 "句子B "标记(更多细节见BERT论文)。
    attention_mask
    [batch_size, sequence_length]
    一个可选的Torch.LongTensor,其形状为[batch_size, sequence_length],索引为在[0, 1]中选择。这是一个掩码,如果输入序列长度小于当前批次的最大长度。当一个批次有不同长度的句子时,我们通常使用这个掩码。
    output_all_encoded_layers
    布尔值,控制`encoded_layers`的输出内容。默认值:`True'。

    输出:元组(encoded_layers, pooled_output)

    encoded_layers:由`output_all_encoded_layers`参数控制。 

    output_all_encoded_layers=True , 在每个关注块的末尾输出一个完整的编码隐藏状态序列的列表。每个注意块的末尾(即BERT-base为12个完整序列,BERT-large为24个),每个编码的隐藏状态是一个大小为[batch_size, sequence_length, hidden_size]的Torch.FloatTensor。

    output_all_encoded_layers=False:只输出对应于最后一个形状为[batch_size]的注意力块的全部隐藏状态序列。形状为[batch_size, sequence_length, hidden_size]的最后一个注意块的全部隐藏状态序列。

    pooled_output:一个大小为[batch_size, hidden_size]的Torch.FloatTensor,它是一个在与第一个字符相关的隐藏状态上预训练的分类器的输出。在与输入的第一个字符相关的隐藏状态上预训练的分类器的输出。输入(‘CLS’)的隐性状态之上进行预训练的分类器的输出,用于训练 "下一句话 "任务(见BERT的论文)。

    六、BERT特征提取

    特征提取非常简单,直接将单词序列输入到已经预训练好的BERT中,得到的输出即为单词以及句子的特征。 

    举个例子,以情感分析任务为例,假设有条句子”I love Beijing” ,它的情感为正面情感。在对这个句子通过BERT做特征提取时,首先使用WordPiece对它进行分词,得到单词列表:

    Tokens = [ I, love, Beijing]

    然后加上特殊token [CLS] 与 [SEP](它们的作用已在前面的章节进行过介绍,在此不再赘述):

    Tokens = [ [CLS], I, love, Beijing, [SEP] ]

    接下来,为了使得训练集中所有句子的长度保持一致,我们会指定一个“最大长度” max_length。对于长度小于此max_length的句子,对它进行补全;而对于超过了此max_length的句子,对它进行裁剪。假设我们这里指定了max_length=7,则对上面的句子进行补全时,使用特殊token [PAD],将句子长度补全到7。如:

    Tokens = [ [CLS], I, love, Beijing, [SEP], [PAD], [PAD] ]

    在使用了[PAD] 作为填充后,还需要一个指示标志,用于表示 [PAD] 仅是用于填充,不代表任何意义。这里用到了一个称为attention mask的列表来表示,它的长度等同于max_length。对于每条句子,如果对应位置的单词为[PAD],则attention mask此位置的元素取值为0,反之为1。例如,对于这个例子,attention mask的值为:

    attention_mask = [ 1, 1, 1, 1, 1, 0, 0 ]

    最后,由于模型无法直接识别单词,仅能识别数字,所以还需要将单词映射为数字。我们首先会对整个单词库做一个词典,每个单词都有对应的1个序号(此序号为不重复的数字)。在这个例子中,假设我们已经构建了一个字典,则对应的这些单词的列表为:

    token_ids = [101, 200, 303, 408, 102, 0, 0]

    其中 101 即对应的是 [CLS] 的序号,200对应的即是单词“I”的序号,依此类推。

    在准备好以上数据后,即可将 token_ids 与 attention_mask 输入到预训练好的BERT模型中,便得到了每个单词的embedding表示。

    6.1 生成BERT Embedding

    前面我们介绍了BERT特征提取,下面通过代码实现此功能。 

    通过 from_pretrained() 方法可以下载指定的预训练好的模型以及分词器,这里我们使用的是bert-base-uncased。前面对bert-based 有过介绍,它包含12个堆叠的encoder,输出的embedding维度为768。

    下面从一个例子来看输入数据的处理。 

    (1)先对句子做分词,然后加上特殊token [CLS] 与 [SEP]。假设我们指定的max_length 为7,则继续补上 [PAD]:

    sentence = 'I love Beijing'
    
    tokens = tokenizer.tokenize(sentence)
    print(tokens)
    
    tokens = ['[CLS]'] + tokens + ['[SEP]']
    print(tokens)
    
    tokens = tokens + ['[PAD]'] * 2
    print(tokens)
    
    ['i', 'love', 'beijing']
    ['[CLS]', 'i', 'love', 'beijing', '[SEP]']
    ['[CLS]', 'i', 'love', 'beijing', '[SEP]', '[PAD]', '[PAD]']

    (2)根据tokens构造attention_mask:

    attention_mask = [ 1 if t != '[PAD]' else 0 for t in tokens]
    print(attention_mask)
    
    [1, 1, 1, 1, 1, 0, 0]

    (3)将所有tokens 转为 token id:

    token_ids = tokenizer.convert_tokens_to_ids(tokens)
    print(token_ids)
    
    [101, 1045, 2293, 7211, 102, 0, 0]

    (4)将token_ids 与 attention_mask 转为tensor:

    token_ids = torch.tensor(token_ids)
    token_ids = torch.reshape(token_ids, [1, -1])
    
    attention_mask = torch.tensor(attention_mask)
    attention_mask = torch.reshape(attention_mask, [1, -1])

    (5)在这些步骤后,我们下一步即可将它们输入到预训练的模型中,得到embedding:

    output = model(token_ids, attention_mask = attention_mask)
    print(output[0].shape, output[1].shape)
    
    (1, 7, 768) (1, 768)

    前面讲过输出为数组(encoded_layers, pooled_output)

    encoded_layers的shape为 [batch_size, sequence_length, hidden_size]

    pooled_output的shape为 [batch_size, hidden_size]

    1. BERT模型最后一层的输出。由于输入有7个tokens,所以对应有7个token的Embedding。其对应的维度为(batch_size, sequence_length, hidden_size)
    2. 输出层中第1个token(这里也就是对应的[CLS])的Embedding,并且已被一个线性层 + Tanh激活层处理。线性层的权重由预训练中得到。其对应的维度为(batch_size, hidden_size)

    (6)是否需要其他隐藏层输出 

    上面介绍了如何获取BERT最后一层的输出表示,要获取每层的表示也非常简单,仅需要添加参数 output_hidden_states=True 即可,例如

    output = model(token_ids, attention_mask = attention_mask, output_hidden_states=True)

    不过这里有一点需要讨论的是:是否需要中间隐藏层的输出?还是仅使用最后一层的输出就足够了?

    假设输入层为h0,第一个编码器层为h1,第二个编码器层为h2,以此类推,最后一个编码器层为h12(下图为引用的图,在此文举例中Paris应改为Beijing):

    img  

    对于此问题,BERT的研究人员做了进一步研究。在命名体识别任务中,研究人员除了使用BERT最后一层的输出作为提取的特征外,还尝试使用了其他层的输出(例如通过拼接的方式进行组合),并得到以下F1分数:

    img

    从这个结果可以看到,在使用最后4层(h9到h12层的拼接)的输出时,能得到比仅使用h12的输出更高的F1分数96.1。所以仅使用BERT最后一层的输出并非在所有场景下都是最好的选择,有时候也需要尝试使用其他层的输出,观察是否能得到更好的效果。

    6.2 微调(Fine tuning)

    在从预训练的BERT中抽取嵌入表示中,我们知道在提取句子的嵌入后,我们将R [CLS]提供给分类器并训练分类器执行分类。类似地,在微调期间,我们也将R [CLS] 喂给一个分类器(前馈网络+softmax),该分类器会返回句子是属于某一类别的概率。 

    不同点在于当我们微调预训练的BERT模型时,我们在更新分类器的参数时也会更新BERT的参数。但当我们使用预训练的BERT模型作为特征抽取器时,我们只更新分类器的参数,而不更新预训练的BERT模型。

    在微调期间,我们可以通过以下两个方式调整BERT模型的参数:

  • 随着分类器一起更新预训练的BERT模型的参数
  • 仅更新分类器的参数而不更新预训练BERT模型的。此时,这种方式类似将预训练的BERT模型作为特征抽取器的情况。
  • 七、Bert+CNN文本分类代码实现

    7.1 代码下载地址

    链接:https://pan.baidu.com/s/1yjsHg8c7i1739CFkIFBnnw 
    提取码:omti

    7.2 过程分析

    数据处理格式

    (1)train.txt文件

    (2)转换为token

    <class 'list'>: ['[CLS]', '中', '华', '女', '子', '学', '院', ':', '本', '科', '层', '次', '仅', '1', '专', '业', '招', '男', '生']

    (3)vocab.txt([CLS]对应id为1)

    [PAD]
    [CLS]
    [SEP]
    [MASK]
    ,
    的
    、
    一
    人
    有
    是
    在
    中
    为
    和
    了
    不
    年
    学
    大
    国
    生
    以
    .
    .
    .

    (4)token_ids

    根据train.txt内容与vocab.txt的字符对应的id进行转换,content有三部分组成,分别是[token_id]、seq_lens、lab,[mask],如下图所示

    token_ids(pad_size = 32,短于32的可以用0来代替)

    <class 'list'>: [1, 12, 403, 291, 85, 18, 245, 74, 89, 147, 522, 218, 674, 208, 206, 26, 931, 654, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

    (5)mask (有内容的部分用1mask掉)

    <class 'list'>: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

    7.3 维度分析

    Bert字向量嵌入 

        def forward(self, x):
            # x [ids, seq_len, mask]
            context = x[0] #对应输入的句子 shape[128,32]
            mask = x[2] #对padding部分进行mask shape[128,32]
            encoder_out, pooled = self.bert(context, attention_mask = mask, output_all_encoded_layers = False) #shape [128,768]
            out = encoder_out.unsqueeze(1)
            out = torch.cat([self.conv_and_pool(out, conv)for conv in self.convs], 1)
            out = self.droptout(out)
            out = self.fc(out)

    卷积和池化函数定义 

        def conv_and_pool(self, x, conv):
            x = conv(x)
            x = F.relu(x)
            x = x.squeeze(3)
            size = x.size(2)  
            x = F.max_pool1d(x, size)
            x = x.squeeze(2)
            return x

    下面我们来逐行分析

    7.3.1 参数定义

            # epoch数
            self.num_epochs = 3
            # batch_size
            self.batch_size = 128
            # 序列长度
            self.pad_size = 32
            # 学习率
            self.learning_rate = 1e-5
            # Bert的隐藏层数量
            self.hidden_size = 768
            # 卷积核尺寸
            self.filter_sizes = (2,3,4)
            # 卷积核数量
            self.num_filters = 256
            # droptout
            self.dropout = 0.5

    7.3.2 卷积+池化

    (1)encoder_out 维度 torch,Size([128,32,768])   (batch_size, pad_size, hidden_size)

    (2)out 维度 torch,Size([128,1,32,768])  

    卷积操作 conv2d 需要一个四维的输入数据,对应的维度分别是批处理大小,宽度,高度和通道数。在我们嵌入层得到的数据中不包含通道数,所以我们需要手动添加它,所以最终的数据维度是 [batch_size, 1, sequence_length, embedding_size, ] 。 

    (3) 卷积核尺寸 filter_sizes = (2,3,4) 则(2)中增加的维度依次为卷积核2,3,4

    第一次卷积 Conv2d(1, 256, kernel_size=(2, 768), stride=(1, 1))

    第二次卷积 Conv2d(1, 256, kernel_size=(3, 768), stride=(1, 1))

    第三次卷积 Conv2d(1, 256, kernel_size=(4, 768), stride=(1, 1)) 

    7.3.3 整体维度转换图 

    经过BERT作为embeding,然后通过3个不同尺寸卷积核和和256个kernel。因为每个卷积核经过卷积操作之后产生的张量是不同维度的,所有我们需要为每一个卷积核创建一层网络,最后再把这些卷积之后的觉果合并成一个大的特征向量。最终经过dropout和FC层实现一个结果的10分类(类别数量num_classes=10,batch_szie=128)

    Dropout 也许是最流行的方法来正则化卷积神经网络。Dropout 的思想非常简单,就是按照一定的概率来“禁用”一些神经元的发放。这种方法可以防止神经元共同适应一个特征,而迫使它们单独学习有用的特征。神经元激活的概率,我们从参数 dropout_keep_prob 中得到。我们在训练阶段将其设置为 0.5,在测试阶段将其设置为 1.0(即所有神经元都被激活)。 

    模型结果

    Test Loss: 0.36, Test Acc:88.73%
    Precision, Recall and F1-Score
                   precision    recall  f1-score   support
    
          finance     0.8750    0.8540    0.8644      1000
           realty     0.9001    0.9190    0.9095      1000
           stocks     0.8287    0.8080    0.8182      1000
        education     0.9302    0.9330    0.9316      1000
          science     0.8581    0.7860    0.8205      1000
          society     0.8778    0.9050    0.8912      1000
         politics     0.8385    0.8720    0.8549      1000
           sports     0.9688    0.9620    0.9654      1000
             game     0.9073    0.9200    0.9136      1000
    entertainment     0.8865    0.9140    0.9000      1000
    
         accuracy                         0.8873     10000
        macro avg     0.8871    0.8873    0.8869     10000
     weighted avg     0.8871    0.8873    0.8869     10000
    
    Confusion Maxtrix
    [[854  20  71   4   9   7  20   2   4   9]
     [ 14 919  14   6   4  16  11   3   3  10]
     [ 67  27 808   2  43   3  34   5   7   4]
     [  2   1   4 933   4  17  16   1   6  16]
     [  9  13  46  11 786  23  28   3  52  29]
     [  2  15   3  20   9 905  29   0   6  11]
     [ 15  12  17  11  12  34 872   3   2  22]
     [  3   3   2   1   2   4  11 962   1  11]
     [  4   6   8   5  38   5   7   2 920   5]
     [  6   5   2  10   9  17  12  12  13 914]]
    

    参考文档

    1. NLP与深度学习(六)BERT模型的使用 – ZacksTang – 博客园 (cnblogs.com)

    2. 【实战篇】是时候彻底弄懂BERT模型了(收藏)_愤怒的可乐的博客-CSDN博客_bert模型实战 

    3. CNN文本分类 – 知乎 (zhihu.com) 

    4.  Kalchbrenner N, Grefenstette E, Blunsom P. A Convolutional Neural Network for Modelling Sentences[J]. Eprint Arxiv, 2014, 1.

    5. CNN在NLP中的应用 – 简书 (jianshu.com)

    来源:迪迦瓦特曼

    物联沃分享整理
    物联沃-IOTWORD物联网 » Bert+CNN文本分类(含代码实现)

    发表评论