MHuiG

「看庭前花开花落,望天上云卷云舒」

# BERT 预训练模型及其应用案例

MHuiG

字数：6.7k 字

时长：25 分钟

预训练模型最开始是在图像领域提出的，获得了良好的效果，近几年才被广泛应用到自然语言处理各项任务中。

- (1) 2003 年 Bengio 提出神经网络语言模型 NNLM，从此统一了 NLP 的特征形式 ——Embedding；

- (2) 2013 年 Mikolov 提出词向量 Word2vec，延续 NNLM 又引入了大规模预训练（Pretrain）的思路；

- (3) 2017 年 Vaswani 提出 Transformer 模型，实现用一个模型处理多种 NLP 任务。

- (4) 基于 Transformer 架构，2018 年底开始出现一大批预训练语言模型 (3 个预训练代表性模型 BERT [2018]、XLNet [2019] 和 MPNet [2020])，刷新众多 NLP 任务，形成新的里程碑事件。

预训练模型的应用通常分为两步:

- 第一步：在计算性能满足的情况下用某个较大的数据集训练出一个较好的模型。

- 第二步：根据不同的任务，改造预训练模型，用新任务的数据集在预训练模型上进行微调。

预训练模型的好处是训练代价较小，配合下游任务可以实现更快的收敛速度，并且能够有效地提高模型性能，尤其是对一些训练数据比较稀缺的任务。换句话说，预训练方法可以认为是让模型基于一个更好的初始状态进行学习，从而能够达到更好的性能。

要讲自然语言的预训练，得先从图像领域的预训练说起。

## 图像领域的预训练

设计好网络结构以后，对于图像来说一般是 CNN 的多层叠加网络结构，可以先用某个训练集合比如训练集合 A 或者训练集合 B 对这个网络进行预先训练，在 A 任务上或者 B 任务上学会网络参数，然后存起来以备后用。假设我们面临第三个任务 C，网络结构采取相同的网络结构，在比较浅的几层 CNN 结构，网络参数初始化的时候可以加载 A 任务或者 B 任务学习好的参数，其它 CNN 高层参数仍然随机初始化。

之后我们用 C 任务的训练数据来训练网络，此时有两种做法，一种是浅层加载的参数在训练 C 任务过程中不动，这种方法被称为 “Frozen”;

另外一种是底层网络参数尽管被初始化了，在 C 任务训练过程中仍然随着训练的进程不断改变，这种一般叫 “Fine-Tuning”，顾名思义，就是更好地把参数进行调整使得更适应当前的 C 任务。

对于层级的 CNN 结构来说，不同层级的神经元学习到了不同类型的图像特征，由底向上特征形成层级结构。

如果我们手头是个人脸识别任务，训练好网络后，把每层神经元学习到的特征可视化肉眼看一看每层学到了啥特征，你会看到最底层的神经元学到的是线段等特征，图示的第二个隐层学到的是人脸五官的轮廓，第三层学到的是人脸的轮廓，通过三步形成了特征的层级结构，越是底层的特征越是所有不论什么领域的图像都会具备的比如边角线弧线等底层基础特征，越往上抽取出的特征越与手头任务相关。

正因为此，所以预训练好的网络参数，尤其是底层的网络参数抽取出特征跟具体任务越无关，越具备任务的通用性，所以这是为何一般用底层预训练好的参数初始化新任务网络参数的原因。

而高层特征跟任务关联较大，实际可以不用使用，或者采用 Fine-tuning 用新数据集合清洗掉高层无关的特征抽取器。

一般我们用 ImageNet 来做网络的预训练，主要有两点，一方面 ImageNet 是图像领域里有超多事先标注好训练数据的数据集合，分量足是个很大的优势，量越大训练出的参数越靠谱；另外一方面因为 ImageNet 有 1000 类，类别多，算是通用的图像数据，跟领域没太大关系，所以通用性好。

## Word Embedding

现有的机器学习方法往往无法直接处理文本数据，因此需要找到合适的方法，将文本数据转换为数值型数据，由此引出了 Word Embedding 的概念，Word Embedding 算法携带了语义信息且维度经过压缩便于运算。

### 语言模型

为了能够量化地衡量哪个句子更像一句人话，可以设计如上图所示函数，核心函数 P 的思想是根据句子里面前面的一系列前导单词预测后面单词的概率大小。

### 神经网络语言模型

NNLM 是从语言模型出发 (即计算概率角度)，构建神经网络针对目标函数对模型进行最优化，训练的起点是使用神经网络去搭建语言模型实现词的预测任务，并且在优化过程后模型的副产品就是词向量。

### Word2Vec

2013 年最火的用语言模型做 Word Embedding 的工具是 Word2Vec，后来又出了 Glove。

Word2Vec 有两种训练方法，一种叫 CBOW，核心思想是从一个句子里面把一个词抠掉，用这个词的上文和下文去预测被抠掉的这个词；

第二种叫做 Skip-gram，和 CBOW 正好反过来，输入某个单词，要求网络预测它的上下文单词。

使用 Word2Vec 或者 Glove，通过做语言模型任务，就可以获得每个单词的 Word Embedding。

### Word Embedding 的使用

我们有个 NLP 的下游任务，比如 QA，就是问答问题，所谓问答问题，指的是给定一个问题 X，给定另外一个句子 Y, 要判断句子 Y 是否是问题 X 的正确答案。

句子中每个单词以 Onehot 形式作为输入，然后乘以 Word Embedding 矩阵 Q，就直接取出单词对应的 Word Embedding。

使用 Word Embedding 等价于把 Onehot 层到 embedding 层的网络用预训练好的参数矩阵 Q 初始化。

这跟前面讲的图像领域的低层预训练过程其实是一样的，区别无非 Word Embedding 只能初始化第一层网络参数，再高层的参数就无能为力了。

下游 NLP 任务在使用 Word Embedding 的时候也类似图像有两种做法，一种是 Frozen，就是 Word Embedding 那层网络参数固定不动；另外一种是 Fine-Tuning，就是使用新的训练集合训练，在训练过程中，更新 Word Embedding 这层参数。

### Word Embedding 的问题

是多义词问题。多义词是自然语言中经常出现的现象，也是语言灵活性和高效性的一种体现。

多义词对 Word Embedding 来说有什么负面影响？如上图所示，比如多义词 Bank，有两个常用含义，但是 Word Embedding 在对 bank 这个单词进行编码的时候，是区分不开这两个含义的，因为它们尽管上下文环境中出现的单词不同，但是在用语言模型训练的时候，不论什么上下文的句子经过 word2vec，都是预测相同的单词 bank，而同一个单词占的是同一行的参数空间，这导致两种不同的上下文信息都会编码到相同的 word embedding 空间里去。所以 word embedding 无法区分多义词的不同语义，这就是它的一个比较严重的问题。

## 从 Word Embedding 到 ELMO

ELMO 是 “Embedding from Language Models” 的简称。在此之前的 Word Embedding 本质上是个静态的方式，所谓静态指的是训练好之后每个单词的表达就固定住了，以后使用的时候，不论新句子上下文单词是什么，这个单词的 Word Embedding 不会跟着上下文场景的变化而改变。

ELMO 的本质思想是：事先用语言模型学好一个单词的 Word Embedding，此时多义词无法区分，实际使用 Word Embedding 的时候，单词已经具备了特定的上下文了，这个时候可以根据上下文单词的语义去调整单词的 Word Embedding 表示，这样经过调整后的 Word Embedding 更能表达在这个上下文中的具体含义，自然也就解决了多义词的问题了。所以 ELMO 本身的思路是根据当前上下文对 Word Embedding 动态调整。

ELMO 采用了典型的两阶段过程，第一个阶段是利用语言模型进行预训练；第二个阶段是在做下游任务时，从预训练网络中提取对应单词的网络各层的 Word Embedding 作为新特征补充到下游任务中。

使用这个网络结构利用大量语料做语言模型任务就能预先训练好这个网络，如果训练好这个网络后，输入一个新句子 ，句子中每个单词都能得到对应的三个 Embedding: 最底层是单词的 Word Embedding，往上走是第一层双向 LSTM 中对应单词位置的 Embedding，这层编码单词的句法信息更多一些；再往上走是第二层 LSTM 中对应单词位置的 Embedding，这层编码单词的语义信息更多一些。也就是说，ELMO 的预训练过程不仅仅学会单词的 Word Embedding，还学会了一个双层双向的 LSTM 网络结构，而这两者后面都有用。

上图展示了下游任务的使用过程，比如我们的下游任务仍然是 QA 问题，此时对于问句 X，我们可以先将句子 X 作为预训练好的 ELMO 网络的输入，这样句子 X 中每个单词在 ELMO 网络中都能获得对应的三个 Embedding，之后给予这三个 Embedding 中的每一个 Embedding 一个权重 a，这个权重可以学习得来，根据各自权重累加求和，将三个 Embedding 整合成一个。然后将整合后的这个 Embedding 作为 X 句在自己任务的那个网络结构中对应单词的输入，以此作为补充的新特征给下游任务使用。对于上图所示下游任务 QA 中的回答句子 Y 来说也是如此处理。因为 ELMO 给下游提供的是每个单词的特征形式，所以这一类预训练的方法被称为 “Feature-based Pre-Training”。

## 从 Word Embedding 到 GPT

GPT 是 “Generative Pre-Training” 的简称，从名字看其含义是指的生成式的预训练。GPT 也采用两阶段过程，第一个阶段是利用语言模型进行预训练，第二阶段通过 Fine-tuning 的模式解决下游任务。

### Transformer

Transformer 是个叠加的 “自注意力机制（Self Attention）” 构成的深度网络，是目前 NLP 里最强的特征提取器。

Transformer 是一种基于 encoder-decoder 结构的模型。在机器翻译任务上的表现超过了 RNN，CNN，只用 encoder-decoder 和 attention 机制就能达到很好的效果，最大的优点是可以高效地并行化。

### 自注意力机制模型

人类视觉通过快速扫描全局图像，获得需要重点关注的目标区域，也就是一般所说的注意力焦点，而后对这一区域投入更多注意力资源，以获取更多所需要关注目标的细节信息，而抑制其他无用信息。

这是人类利用有限的注意力资源从大量信息中快速筛选出高价值信息的手段.

深度学习中的注意力机制从本质上讲和人类的选择性视觉注意力机制类似，核心目标也是从众多信息中选择出对当前任务目标更关键的信息。

Attention 在同一个英语句子内单词间产生的联系。

Self Attention 可以捕获同一个句子中单词之间的一些句法特征（比如图展示的有一定距离的短语结构）或者语义特征（比如图展示的 its 的指代对象 Law）。

很明显，引入 Self Attention 后会更容易捕获句子中长距离的相互依赖的特征，因为如果是 RNN 或者 LSTM，需要依次序序列计算，对于远距离的相互依赖的特征，要经过若干时间步步骤的信息累积才能将两者联系起来，而距离越远，有效捕获的可能性越小。

SelfAttention 在计算过程中会直接将句子中任意两个单词的联系通过一个计算步骤直接联系起来，所以远距离依赖特征之间的距离被极大缩短，有利于有效地利用这些特征。除此外，SelfAttention 对于增加计算的并行性也有直接帮助作用。这是为何 Self Attention 逐渐被广泛使用的主要原因。

### GPT 如何使用

把任务的网络结构改造成和 GPT 的网络结构是一样的。然后，在做下游任务的时候，利用第一步预训练好的参数初始化 GPT 的网络结构，对网络参数进行 Fine-tuning，使得这个网络更适合解决手头的问题。

## 从 GPT 和 ELMO 及 word2Vec 到 Bert

Bert 采用和 GPT 完全相同的两阶段模型，首先是语言模型预训练；其次是使用 Fine-Tuning 模式解决下游任务。和 GPT 的最主要不同在于在预训练阶段采用了类似 ELMO 的双向语言模型，当然另外一点是语言模型的数据规模要比 GPT 大。

BERT 本质上是一个自编码（Auto Encoder）语言模型，为了能见多识广，BERT 使用 3 亿多词语训练，采用 12 层双向 Transformer 架构。注意，BERT 只使用了 Transformer 的编码器部分，可以理解为 BERT 旨在学习庞大文本的内部语义信息。

具体训练目标之一，是被称为掩码语言模型的 MLM。即输入一句话，给其中 15% 的字打上 “mask” 标记，经过 Embedding 输入和 12 层 Transformer 深度理解，来预测 “mask” 标记的地方原本是哪个字。

    input:   欲把西[mask]比西子，淡[mask]浓抹总相宜
    output:  欲把西[湖]比西子，淡[妆]浓抹总相宜

例如我们输入 “欲把西 [mask] 比西子，淡 [mask] 浓抹总相宜” 给 BERT，它需要根据没有被 “mask” 的上下文，预测出掩盖的地方是 “湖” 和 “妆”。

MLM 任务的灵感来自于人类做完形填空。挖去文章中的某些片段，需要通过上下文理解来猜测这些被掩盖位置原先的内容。

训练目标之二，是预测输入的两句话之间是否为上下文（NSP）的二分类问题。继续输入 “欲把西 [湖] 比西子，淡 [妆] 浓抹总相宜”，BERT 将预测这两句话的组合是否合理（这个例子是 “yes”）。（随后的研究者对预训练模型探索中证明，NSP 任务过于简单，对语言模型的训练作用并不是很大）

通过这两个任务和大规模语料训练，BERT 语言模型可以很好学习到文本之间的蕴含的关系。

## NLP 的四大任务

绝大部分 NLP 问题可以归入上图所示的四类任务中：

一类是序列标注，这是最典型的 NLP 任务，比如中文分词，词性标注，命名实体识别，语义角色标注等都可以归入这一类问题，它的特点是句子中每个单词要求模型根据上下文都要给出一个分类类别。

第二类是分类任务，比如我们常见的文本分类，情感计算等都可以归入这一类。它的特点是不管文章有多长，总体给出一个分类类别即可。

第三类任务是句子关系判断，比如 Entailment，QA，语义改写，自然语言推理等任务都是这个模式，它的特点是给定两个句子，模型判断出两个句子是否具备某种语义关系；

第四类是生成式任务，比如机器翻译，文本摘要，写诗造句，看图说话等都属于这一类。它的特点是输入文本内容后，需要自主生成另外一段文字。

根据任务选择不同的预训练数据初始化 encoder 和 decoder 即可。这是相当直观的一种改造方法。当然，也可以更简单一点，比如直接在单个 Transformer 结构上加装隐层产生输出也是可以的。不论如何，从这里可以看出，NLP 四大类任务都可以比较方便地改造成 Bert 能够接受的方式。这其实是 Bert 的非常大的优点，这意味着它几乎可以做任何 NLP 的下游任务，具备普适性，这是很强的。

## BERT 的应用案例

### 下载 bert 预训练模型

Google - BERT 源码 https://github.com/google-research/bert 下载预训练模型。

我这里选择中文的 BERT，下载解压后的目录如下：

### 安装 bert-as-service

顾名思义，将 BERT 模型直接封装成一个服务，堪称上手最快的 BERT 工具。作者是肖涵博士。

使用 pip 安装：

    pip install bert-serving-server # server
    pip install bert-serving-client # client, independent of `bert-serving-server

开启 BERT service

    bert-serving-start -model_dir E:\nlp\chinese_L-12_H-768_A-12

使用客户端获取句子编码

### 案例一 查找最相近的句子

根据 bert 获取句子向量，并计算出句子之间的余弦相似度，找出最相似的句子。

    # 导入bert客户端
    from bert_serving.client import BertClient
    import numpy as np

    class SimilarModel:
        def __init__(self):
            self.bert_client = BertClient()

        def close_bert(self):
            self.bert_client .close()

        def get_sentence_vec(self,sentence):
            '''
            根据bert获取句子向量
            :param sentence:
            :return:
            '''
            return self.bert_client .encode([sentence])[0]

        def cos_similar(self,sen_a_vec, sen_b_vec):
            '''
            计算两个句子的余弦相似度
            :param sen_a_vec:
            :param sen_b_vec:
            :return:
            '''
            vector_a = np.mat(sen_a_vec)
            vector_b = np.mat(sen_b_vec)
            num = float(vector_a * vector_b.T)
            denom = np.linalg.norm(vector_a) * np.linalg.norm(vector_b)
            cos = num / denom
            return cos

    if __name__=='__main__':
        # 从候选集condinates 中选出与sentence_a 最相近的句子
        condinates = ['为什么天空是蔚蓝色的','太空为什么是黑的？','天空怎么是蓝色的','明天去爬山如何']
        sentence_a = '天空为什么是蓝色的'
        bert_client = SimilarModel()
        max_cos_similar = 0
        most_similar_sentence = ''
        for sentence_b in condinates:
            sentence_a_vec = bert_client.get_sentence_vec(sentence_a)
            sentence_b_vec = bert_client.get_sentence_vec(sentence_b)
            cos_similar = bert_client.cos_similar(sentence_a_vec,sentence_b_vec)
            if cos_similar > max_cos_similar:
                max_cos_similar = cos_similar
                most_similar_sentence = sentence_b

        print('最相似的句子：',most_similar_sentence)
        bert_client .close_bert()
        # 为什么天空是蔚蓝色的

### 案例二 简单模糊搜索

将问题编码为向量：

    from bert_serving.client import BertClient
    doc_vecs = bc.encode(questions)

最后，我们准备接收新查询并针对现有问题执行简单的 “模糊” 搜索。为此，每次出现新查询时，我们都将其编码为向量，并使用来计算其点积 doc_vecs。将结果递减排序；并返回前 k 个类似的问题，如下所示：

    while True:
        query = input('your question: ')
        query_vec = bc.encode([query])[0]
        # compute normalized dot product as score
        score = np.sum(query_vec * doc_vecs, axis=1) / np.linalg.norm(doc_vecs, axis=1)
        topk_idx = np.argsort(score)[::-1][:topk]
        print('top %d questions similar to "%s"' % (topk, query))
        for idx in topk_idx:
            print('> %s\t%s' % ('%.1f' % score[idx], questions[idx]))

现在运行代码并键入查询，查看此搜索引擎如何处理模糊匹配：

### 案例三 法条推荐

#### 介绍

根据刑事法律文书中的案情描述和事实部分，预测本案涉及的相关法条；

#### 数据说明

所使用的数据集是来自 “中国裁判文书网” 公开的刑事法律文书，其中每份数据由法律文书中的案情描述和事实部分组成，同时也包括每个案件所涉及的法条、被告人被判的罪名和刑期长短等要素。

数据集共包括 268 万刑法法律文书，共涉及 202 条罪名，183 条法条，刑期长短包括 0-25 年、无期、死刑。

数据利用 json 格式储存，每一行为一条数据，每条数据均为一个字典。

fact: 事实描述

meta: 标注信息，标注信息中包括:

criminals: 被告 (数据中均只含一个被告)

punish_of_money: 罚款 (单位：元)

accusation: 罪名

relevant_articles: 相关法条

term_of_imprisonment: 刑期

刑期格式 (单位：月)

death_penalty: 是否死刑

life_imprisonment: 是否无期

imprisonment: 有期徒刑刑期

这里是简单的一条数据展示:

    {
       "fact": "2015年11月5日上午，被告人胡某在平湖市乍浦镇的嘉兴市多凌金牛制衣有限公司车间内，与被害人孙某因工作琐事发生口角，后被告人胡某用木制坐垫打伤被害人孙某左腹部。经平湖公安司法鉴定中心鉴定：孙某的左腹部损伤已达重伤二级。",
       "meta":
       {
           "relevant_articles": [234],
           "accusation": ["故意伤害"],
           "criminals": ["胡某"],
           "term_of_imprisonment":
           {
              "death_penalty": false,
              "imprisonment": 12,
              "life_imprisonment": false
           }
       }
    }

#### 实现流程

进入 OpenCLaP 下载刑事文书 BERT，并运行 bert-as-service 服务。

##### 创建一个连接到 BertServer 的 BertClient

    from bert_serving.client import ConcurrentBertClient
    bc = ConcurrentBertClient()

##### 获取编码向量和标签

    def get_encodes(x):
        # x 是 batch_size 大小的行 ，每行都是一个json对象
        samples = [json.loads(l) for l in x] # 一个 batch_size 大小的连续样本
        text = [s['fact'][:50] + s['fact'][-50:] for s in samples] # 获取案情描述和事实部分文字
        features = bc.encode(text) # 使用bert将字符串列表编码为向量列表
        # 随机选择一个标签
        labels = [[str(random.choice(s['meta']['relevant_articles']))] for s in samples]
        return features, labels

##### 构建 TensorFlow DNN 模型的分类器

    estimator = DNNClassifier(
        hidden_units=[512], # 每层隐藏单元的 Iterable 数.所有层都完全连接.
        feature_columns=[tf.feature_column.numeric_column('feature', shape=(768,))], # 包含模型使用的所有特征列的iterable.集合中的所有项目都应该是从 _FeatureColumn 派生的类的实例.
        n_classes=len(laws), # 标签类的数量.默认为 2,即二进制分类,必须大于1.
        config=run_config, # RunConfig 对象配置运行时设置
        label_vocabulary=laws_str, # 字符串列表,表示可能的标签值.如果给定,标签必须是字符串类型,并且 label_vocabulary 具有任何值.如果没有给出,这意味着标签已经被编码为整数或者在[0,1]内浮动, n_classes=2 ；并且被编码为{0,1,...,n_classes-1}中的整数值,n_classes> 2.如果没有提供词汇表并且标签是字符串,也会出现错误.
        optimizer=tf.train.AdamOptimizer(),# 优化函数
        dropout=0.1 # 当不是 None 时,我们将放弃给定坐标的概率.
        )

##### 训练和评估

    # 输入函数
    input_fn = lambda fp: (tf.data.TextLineDataset(fp) # TextLineDataset接口提供了一种方法从数据文件中读取。只需要提供文件名（1个或者多个）。这个接口会自动构造一个dataset，类中保存的元素：文中一行，就是一个元素，是string类型的tenser。
                           # map将分别对Dataset的每个元素执行一个函数，而apply将立即对整个Dataset执行一个函数
                           .apply(tf.contrib.data.shuffle_and_repeat(buffer_size=10000)) # repeat重复和shuffle重排 tf.data.Dataset.repeat 转换会将输入数据重复有限（或无限）次；每次数据重复通常称为一个周期。tf.data.Dataset.shuffle 转换会随机化数据集样本的顺序。
                           # 将此数据集的连续元素合并为批。
                           .batch(batch_size)
                           # tf.py_func()接收的是tensor，然后将其转化为numpy array送入我们自定义的get_encodes函数，最后再将get_encodes函数输出的numpy array转化为tensor返回
                           .map(lambda x: tf.py_func(get_encodes, [x], [tf.float32, tf.string], name='bert_client'),
                                num_parallel_calls=num_parallel_calls)
                           .map(lambda x, y: ({'feature': x}, y))
                           .prefetch(20)) # 创建一个从该数据集中预提取元素的Dataset 大多数数据集输入管道应以调用结束prefetch。这允许在处理当前元素时准备以后的元素。这通常会提高延迟和吞吐量，但以使用额外的内存存储预取元素为代价。
    # TrainSpec确定训练的输入数据以及持续时间
    train_spec = TrainSpec(input_fn=lambda: input_fn(train_fp))
    # EvalSpec结合了训练模型的计算和输出的详细信息.计算由计算指标组成,用以判断训练模型的性能.输出将训练好的模型写入外部存储.
    eval_spec = EvalSpec(input_fn=lambda: input_fn(eval_fp), throttle_secs=0) # 第一次评估发生在throttle_secs秒后
    # 训练和评估
    train_and_evaluate(estimator, train_spec, eval_spec)

##### 运行 tensorboard 可视化训练过程

    tensorboard --logdir=law-model

### 案例四 互联网新闻情感分析

#### 介绍

对新闻情绪进行分类，0 代表正面情绪、1 代表中性情绪、2 代表负面情绪。

#### 数据说明

Field | Type | Description
--- | --- | ---
id | String | 新闻 ID News ID
text | String | 新闻正文内容 Content of news text
label | String | 新闻情感标签 Emotional label in news

#### 实现流程

##### 加载数据集

    def load_dataset(filepath):
        dataset_list = []
        f = open(filepath, 'r', encoding='utf-8')
        r = csv.reader(f)
        for item in r:
            if r.line_num == 1:
                continue
            dataset_list.append(item)

        # 空元素补0
        for item in dataset_list:
            if item[1].strip() == '':
                item[1] = '0'

        return dataset_list

##### 网络类，全连接层

    class Net(nn.Module):
        # in_dim=768, out_dim=3
        def __init__(self, in_dim, out_dim):
            super(Net, self).__init__()

            self.linear1 = nn.Linear(in_dim, 500)
            self.linear2 = nn.Linear(500, 400)
            self.linear3 = nn.Linear(400, 300)
            self.linear4 = nn.Linear(300, 200)
            self.linear5 = nn.Linear(200, out_dim)

        def forward(self, x):
            x = self.linear1(x)
            x = self.linear2(x)
            x = self.linear3(x)
            x = self.linear4(x)
            x = self.linear5(x)
            return x

##### 计算每个 batch 的准确率

    def  batch_accuracy(pre, label):
        pre = pre.argmax(dim=1)
        correct = torch.eq(pre, label).sum().float().item()
        accuracy = correct / float(len(label))

        return accuracy

##### 训练

    for epoch in range(EPOCHS):
        step = -1
        for text, label in train_loader:
            # tuple转list
            text = list(text)
            label = list(label)
            label = list(map(int, label))
            # 使用中文bert，生成句向量
            sen_vec = bertclient.encode(text)
            sen_vec = torch.tensor(sen_vec)
            label = torch.LongTensor(label)
            label = label.cuda()
            # 输入到网络中，反向传播
            pre = net(sen_vec).cuda()
            loss = criterion(pre, label)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            # 更新loss曲线，并计算准确率
            step = step + 1
            flag = flag + 1
            if step % 100 == 0:
                acc = batch_accuracy(pre, label)
                print('epoch:{} | batch:{} | acc:{} | loss:{}'.format(epoch, step, acc, loss.item()))

##### 测试

    net.load_state_dict(torch.load('net.pt'))

    test_result = []
    for item in test_dataset:

        sen_vec = bertclient.encode([item[1]])
        sen_vec = torch.tensor(sen_vec)

        with torch.no_grad():
            pre = net(sen_vec).cuda()
            pre = pre.argmax(dim=1)
            pre = pre.item()
            test_result.append([item[0], pre])

            # 写入csv文件
            df = pd.DataFrame(test_result)
            df.to_csv('test_result.csv',index=False, header=['id', 'label'])

##### 训练可视化

##### 控制台输出

## 参考文献

从 Word Embedding 到 Bert 模型 — 自然语言处理中的预训练技术发展史

Transformer

深度学习中的注意力模型

谷歌的 bert

肖涵博士的 bert-as-service

Serving-Google-BERT-in-Production-using-Tensorflow-and-ZeroMQ

CAIL2018 数据集

刑事文书 BERT:

tensorboard 使用详解

Visdom 可视化工具

互联网新闻情感分析

NLP NLP

NLP

NLP

发布于：2020年11月5日

更新于：2020年11月5日

互联网进化！

简介《互联网进化论》书中提出” 互联网的未来功能和结构将于人类大脑高度相似，也将具备互联网虚拟感觉，虚拟运动，虚拟中枢，虚拟运动神经系统”, 并绘制了一幅互联网虚拟大脑结构图. 云计算是互联...

尝试使用 GPU 加速计算

大规模训练，gpu 和 cpu 速度差别很大。 概述GPU CPU (Central Processing Unit) 即中央处理器。 GPU (Graphics Processing Un...

留言区

本文目录
Are you Robot?