前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >如何微调BERT模型进行文本分类

如何微调BERT模型进行文本分类

作者头像
deephub
发布于 2022-01-21 12:04:53
发布于 2022-01-21 12:04:53
2.7K00
代码可运行
举报
文章被收录于专栏:DeepHub IMBADeepHub IMBA
运行总次数:0
代码可运行

什么是BERT?

BERT(Bidirectional Encoder Representations from Transformers)在各种自然语言处理任务中提供了最前沿的结果在深度学习社区引起了轰动。德夫林等人。2018 年在 Google 使用英文维基百科和 BookCorpus 开发了 BERT,从那时起,类似的架构被修改并用于各种 NLP 应用程序。XL.net 是建立在 BERT 之上的示例之一,它在 20 种不同任务上的表现优于 BERT。在理解基于 BERT 构建的不同模型之前,我们需要更好地了解 Transformer 和注意力模型。

BERT 的基本技术突破是使用双向训练的 Transformer 和注意力模型来执行语言建模。与早期从左到右或双向训练相结合的文本序列的研究相比,BERT 论文的发现表明,双向训练的语言模型可以更好地理解语言上下文。

BERT 使用注意力机制以及学习单词之间上下文关系的Transformer 。Transformer 由两个独立的部分组成 - 编码器和解码器。编码器读取输入文本,解码器为任务生成预测。与顺序读取输入文本的传统定向模型相比,transformer 的编码器一次读取整个单词序列。由于 BERT 的这种特殊结构,它可以用于许多文本分类任务、主题建模、文本摘要和问答。

在本文中,我们将尝试微调用于文本分类的 BERT 模型,使用 IMDB 电影评论数据集检测电影评论的情绪。

BERT 目前有两种可用的变体:

  • BERT Base:12层,12个注意力头,768个隐藏和110M参数
  • BERT Large:24 层,16 个注意力头,1024 隐藏和 340M 参数

以下是 Devlin 等人的 BERT 架构图。

我们已经快速了解了什么是BERT ,下面开始对 BERT 模型进行微调以进行情感分析。我们将使用 IMDB 电影评论数据集来完成这项任务。

微调前准备

首先,我们需要从 Hugging Face 安装Transformer 库。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pip install transformers

现在让我们导入我们在整个实现过程中需要的所有库。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from transformers import BertTokenizer, TFBertForSequenceClassification
from transformers import InputExample, InputFeatures

import numpy as np
import pandas as pd
import tensorflow as tf
import os
import shutil

我们需要导入 BERT 的预训练分词器和序列分类器以及输入模块。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
model = TFBertForSequenceClassification.from_pretrained("bert-base-uncased")
tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")

有很多方法可以对文本序列进行向量化,例如使用词袋 (BoW)、TF-IDF、Keras 的 Tokenizers 等。在这个实现中,我们将使用预训练的“bert-base-uncase”标记器类.

让我们看看分词器是如何工作的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
example = 'This is a blog post on how to do sentiment analysis with BERT'
tokens = tokenizer.tokenize(example)
token_ids = tokenizer.convert_tokens_to_ids(tokens)
print(tokens)
print(token_ids)

--Output--
['this', 'is', 'a', 'blog', 'post', 'on', 'how', 'to', 'do', 'sentiment', 'analysis', 'with', 'bert']
[2023, 2003, 1037, 9927, 2695, 2006, 2129, 2000, 2079, 15792, 4106, 2007, 14324]

由于 BERT 词汇表的大小固定为 30K 个标记,因此词汇表中不存在的词将表示为子词和字符。分词器检查输入的句子并决定是否将每个单词作为一个完整的单词保留,将其拆分为子单词或将其分解为个别字符作为补充。通过分词器总是可以将一个单词表示为其组成字符的集合。

我们将使用预训练的“bert-base-uncased”模型和序列分类器进行微调。为了更好地理解,让我们看看模型是如何构建的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
model.summary()

--Output--
Model: "tf_bert_for_sequence_classification"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
bert (TFBertMainLayer)       multiple                  109482240 
_________________________________________________________________
dropout_37 (Dropout)         multiple                  0         
_________________________________________________________________
classifier (Dense)           multiple                  1538      
=================================================================
Total params: 109,483,778
Trainable params: 109,483,778
Non-trainable params: 0

我们的主要 BERT 模型由一个用于防止过度拟合的 dropout 层和一个用于实现分类任务的密集层组成。

读取数据

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
dataset = pd.read_csv("IMDB Dataset.csv")
dataset.head()

--Output--
                                             review  | sentiment
0 |One of the other reviewers has mentioned that ... | positive
1 |A wonderful little production. <br /><br />The... | positive
2 |I thought this was a wonderful way to spend ti... | positive
3 |Basically there's a family where a little boy ... | negative
4 |Petter Mattei's "Love in the Time of Money" is... | positive

从上面的输出中可以看出,数据集的情绪使用正面和负面标签进行了注释。因此,我们需要将标签更改为数值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def convert2num(value):
    if value=='positive': 
        return 1
    else: 
        return 0
    
df['sentiment']  =  df['sentiment'].apply(convert2num)
train = df[:45000]
test = df[45000:]

数据预处理

使用 BERT 训练模型时,需要完成一些额外的预处理任务。

添加特殊令牌:

[SEP] - 标记句子的结尾

[CLS] - 为了让 BERT 理解我们正在做一个分类,我们在每个句子的开头添加这个标记

[PAD] - 用于填充的特殊标记

[UNK] - 当分词器无法理解句子中表示的单词时,我们将包含此标记而不是单词

引入填充 - 等长传递序列

创建注意力掩码 - 1(真实标记)和 0(填充标记)的数组

微调模型

创建输入序列

使用InputExample函数,我们可以将df转换为适合 BERT 模型的对象。为了做到这一点,我将创建两个函数。一个函数将接受训练和测试数据集作为输入并将每一行转换为 InputExample 对象,另一个函数将标记 InputExample 对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def convert2inputexamples(train, test, review, sentiment): 
  trainexamples = train.apply(lambda x:InputExample(
                         guid=None, text_a = x[review], 
                         label = x[sentiment]), axis = 1)  validexamples = test.apply(lambda x: InputExample(
                         guid=None, text_a = x[review], 
                         label = x[sentiment]), axis = 1)
  
    return trainexamples, validexamplestrainexamples, validexamples = convert2inputexamples(train,  test, 'review',  'sentiment')

从上面的函数可以看出,它将训练和测试数据集作为输入,并将数据集的每一行转换为 InputExamples。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def convertexamples2tf(examples, tokenizer, max_length=128):
    features = []

    for i in tqdm(examples):
        input_dict = tokenizer.encode_plus(
            i.text_a,
            add_special_tokens=True,    # Add 'CLS' and 'SEP'
            max_length=max_length,    # truncates if len(s) > max_length
            return_token_type_ids=True,
            return_attention_mask=True,
            pad_to_max_length=True, # pads to the right by default # CHECK THIS for pad_to_max_length
            truncation=True
        )

        input_ids, token_type_ids, attention_mask = (input_dict["input_ids"],input_dict["token_type_ids"], input_dict['attention_mask'])
        features.append(InputFeatures( input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids, label=i.label) )

    def generate():
        for f in features:
            yield (
                {
                    "input_ids": f.input_ids,
                    "attention_mask": f.attention_mask,
                    "token_type_ids": f.token_type_ids,
                },
                f.label,
            )

    return tf.data.Dataset.from_generator(
        generate,
        ({"input_ids": tf.int32, "attention_mask": tf.int32, "token_type_ids": tf.int32}, tf.int64),
        (
            {
                "input_ids": tf.TensorShape([None]),
                "attention_mask": tf.TensorShape([None]),
                "token_type_ids": tf.TensorShape([None]),
            },
            tf.TensorShape([]),
        ),
    )


DATA_COLUMN = 'review'
LABEL_COLUMN = 'sentiment'

上面的函数将转换后的输入 Example 对象作为输入,它将标记化和重新格式化输入以适合提供给模型。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
train_data = convertexamples2tf(list(trainexamples), tokenizer)
train_data = train_data.shuffle(100).batch(32).repeat(2)

validation_data = convertexamples2tf(list(validexamples), tokenizer)
validation_data = validation_data.batch(32)

上面的代码片已经将转换后的 InputExample 传递给了我们之前创建的函数。执行此过程最多可能需要 2-3 分钟。

现在我们的数据集被处理成输入序列,我们可以使用处理过的数据来提供我们的模型。

训练微调BERT模型

在开始训练模型之前,请确保已启用 GPU 运行时加速。否则,训练模型可能需要一些时间。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
model.compile(optimizer=tf.keras.optimizers.Adam(
              learning_rate=3e-5, epsilon=1e-08, clipnorm=1.0),             loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), metrics=[tf.keras.metrics.SparseCategoricalAccuracy('accuracy')])
              
model.fit(train_data, epochs=2, validation_data=validation_data)

上面的代码使用 Adam 作为优化器使用 Categorical Cross Entropy 作为损失函数,因为我们只有两个标签,而且这个函数可以量化两个概率分布之间的差异,并且使用稀疏分类准确度计算模型的准确度。

训练完成后,我们可以继续预测电影评论的情绪。

预测情绪

我创建了一个包含两个评论的列表,一个是正面的,第二个是负面的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
sentences = ['This was a good movie. I would watch it again',                  'I cannot believe I have wasted time on this movie, it is the worst movie I have ever seen']

在我们将上述句子列表应用到模型中之前,我们需要使用 BERT Tokenizer 对评论进行标记。在对句子列表进行分词后,我们输入模型并运行 softmax 来预测情绪。为了确定预测情绪的极性,我们将使用 argmax 函数将情绪正确分类为“负面”或“正面”标签。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
tokenized_sentences = tokenizer(sentences, max_length=128, padding=True, truncation=True, return_tensors='tf')
outputs = model(tokenized_sentences)                                  
predictions = tf.nn.softmax(outputs[0], axis=-1)
labels = ['Negative','Positive']
label = tf.argmax(predictions, axis=1)
label = label.numpy()
for i in range(len(sentences)):
    print(sentences[i], ": ", labels[label[i]])
    
--Output--
This was a good movie. I would watch it again :  Positive
I cannot believe I have wasted time on this movie, it is the worst movie I have ever seen :  Negative

从上面的预测中可以看出,我们已经成功地微调了基于 Transformer 的预训练 BERT 模型来预测电影评论的情绪。

总结

这就是这篇关于使用 IMDB 电影评论数据集微调预训练 BERT 模型以预测给定评论的情绪的文章的全部内容。如果您对其他微调技术有兴趣,请参考 Hugging Face 的 BERT 文档。

本文源代码:https://gist.github.com/ravindu9701/1a5451fd79f633727ac1c636cb415892#file-bert-sentiment-analysis-ipynb

作者:Ashish Kumar Singh

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-01-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DeepHub IMBA 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Mysql 实现数据库读写分离
Amoeba(变形虫)项目,专注 分布式数据库 proxy 开发。座落与Client、DB Server(s)之间。对客户端透明。具有负载均衡、高可用性、sql过滤、读写分离、可路由相关的query到目标数据库、可并发请求多台数据库合并结果。
误入歧途
2024/05/08
4030
Mysql 实现数据库读写分离
MySQL主从复制+读写分离原理及配置实例
MySQL的主从复制和MySQL的读写分离两者不分家,基于主从复制的架构才可实现数据的读写分离。
小手冰凉
2019/10/12
7810
MySQL主从复制+读写分离原理及配置实例
搭建Amoeba实现MySQL主从数据库读写分离
之前我们有介绍过如何搭建主从,主主,一主多从, 多主一从数据库集群,那么我们今天就来介绍如何通过中间键Amoeba 来实现主从数据库的读写分离, 从而提升数据库的负载性能。
小土豆Yuki
2020/07/31
4.7K0
搭建Amoeba实现MySQL主从数据库读写分离
Amoeba 实现MySQL读写分离
Amoeba是一个以MySQL为底层数据存储,并对应用提供MySQL协议接口的proxy,它集中地响应应用的请求,依据用户事先设置的规则,将SQL请求发送到特定的数据库上执行.基于此可以实现负载均衡、读写分离、高可用性等需求,与MySQL官方的MySQL Proxy相比,作者强调的是amoeba配置的方便.
王 瑞
2022/12/28
7400
Mysql读写分离方案-MySQL Proxy环境部署记录
Mysql的读写分离可以使用MySQL Proxy和Amoeba实现,其实也可以使用MySQL-MMM实现读写分离的自动切换。MySQL Proxy有一项强大功能是实现"读写分离",基本原理是让主数据库处理写方面事务,让从库处理SELECT查询;Amoeba for MySQL是一款优秀的中间件软件,同样可以实现读写分离,负载均衡等功能。下面重点说下Mysql Proxy: MySQL Proxy处于客户端应用程序和MySQL服务器之间,通过截断、改变并转发客户端和后端数据库之间的通信来实现其功能。代理服务
洗尽了浮华
2018/01/23
2K0
Mysql读写分离方案-MySQL Proxy环境部署记录
Mysql读写分离方案-Amoeba环境部署记录
Mysql的读写分离可以使用MySQL Proxy,也可以使用Amoeba。Amoeba(变形虫)项目是一个类似MySQL Proxy的分布式数据库中间代理层软件,是由陈思儒开发的一个开源的java项目。其主要功能包括读写分离,垂直分库,水平分库等,经过测试,发现其功能和稳定性都非常的不错,如果需要构架分布式数据库环境,采用Amoeba是一个不错的方案。目前Amoeba一共包括For aladdin,For MySQL和For Oracle三个版本,以下介绍主要关注For MySQL版本的一个读写分离实现。
洗尽了浮华
2018/01/23
2.1K0
Mysql读写分离方案-Amoeba环境部署记录
mysql一主多从 读写分离_MySQL主从复制原理
  在企业应用中,成熟的业务通常数据量都比较大。单台 mysql 在安全性、高可用性和高并发方面都无法满足实际的需求,实际生产环境中经常会配置多台主从数据库服务器以实现读写分离。
全栈程序员站长
2022/09/22
1.7K0
mysql一主多从 读写分离_MySQL主从复制原理
MySQL 读写分离
一 什么是读写分离 MySQL Proxy最强大的一项功能是实现“读写分离(Read/Write Splitting)”。基本的原理是让主数据库处理事务性查询,而从数据库处理SELECT查询。数据库
李海彬
2018/03/27
5.7K0
MySQL 读写分离
001.Amoeba读写分离部署
Amoeba(变形虫)项目,该开源框架于2008年 开始发布一款 Amoeba forMysql软件。这个软件致力于MySQL的分布式数据库前端代理层,它主要在应用层访问MySQL的时候充当SQL路由功能,专注于分布式数据库代理层(Database Proxy)开发。座落与 Client、DB Server(s)之间,对客户端透明。具有负载均衡、高可用性、SQL 过滤、读写分离、可路由相关的到目标数据库、可并发请求多台数据库合并结果。通过Amoeba你能够完成多数据源的高可用、负载均衡、数据切片的功能,目前Amoeba已在很多企业的生产线上面使用。
木二
2019/07/01
7060
使用mysql-proxy配置mysql读写分离
简介 对于很多大型网站(pv值百万、千万)来说,在所处理的业务中,其中有70%的业务是查询(select)相关的业务操作(新闻网站,插入一条新闻。查询操作),剩下的则是写(insert、update、delete,只要能对MySQL的数据造成更改的操作都叫写操作)操作。在使用负载均衡集群之后,可以很大程度的提升网站的整体性能,但是最终的数据处理的压力还是会落到MySQL数据库上,所有很有必要使用一些技术来提升MySQL的负载能力。(读写分离) 写操作专门交给写服务器处理(一般网站来说写是比较少的 读写比 4
老七Linux
2018/05/09
1.6K0
MySQL读写分离
  当今MySQL使用相当广泛,随着用户的增多以及数据量的增大,高并发随之而来。然而我们有很多办法可以缓解数据库的压力。分布式数据库、负载均衡、读写分离、增加缓存服务器等等。这里我们将采用读写分离技术进展缓解数据库的压力。
那一叶随风
2018/08/22
2.5K0
MySQL读写分离
MySQL主从+Atlas 实现读写分离
Atlas是由 Qihoo 360公司Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项目。它在MySQL官方推出的MySQL-Proxy 0.8.2版本的基础上,修改了大量bug,添加了很多功能特性。目前该项目在360公司内部得到了广泛应用,很多MySQL业务已经接入了Atlas平台,每天承载的读写请求数达几十亿条。同时,有超过50家公司在生产环境中部署了Atlas,超过800人已加入了我们的开发者交流群,并且这些数字还在不断增加。
全栈程序员站长
2021/06/10
9340
MySQL主从+Atlas 实现读写分离
CentOS 7.4下MySQL+Amoeba实现主从同步读写分离
CentOS 7.4环境下MySQL+Amoeba实现主从同步读写分离的详细过程。
星哥玩云
2022/08/16
6960
CentOS 7.4下MySQL+Amoeba实现主从同步读写分离
MySQL主从复制 mysql-proxy实现读写分离
docker安装步骤 https://docs.docker.com/install/linux/docker-ce/centos/#install-docker-ce-1
星哥玩云
2022/08/17
6420
MySQL主从复制 mysql-proxy实现读写分离
关于数据库读写分离
1、what 读写分离 读写分离,基本的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。
全栈程序员站长
2022/07/22
7610
关于数据库读写分离
听说Mysql你很豪横?-------------MySQL5.7主从复制!读写分离!
读写分离就是只在主服务器上写,只在从服务器上读 主数据库处理事务性査询,而从数据库处理 select査询 数据库复制被用来把事务性査询导致的变更同步到集群中的从数据库
不吃小白菜
2020/09/03
7930
听说Mysql你很豪横?-------------MySQL5.7主从复制!读写分离!
CentOS 7.2下MySQL读写分离配置
场景描述: 数据库Master主服务器:192.168.206.100 数据库Slave从服务器:192.168.206.200 MySQL-Proxy调度服务器:192.168.206.210
星哥玩云
2022/08/17
4840
mysql 读写分离_详解MySQL读写分离
MySQL的主从复制和读写分离两者有着紧密的联系,首先要部署主从复制,只有主从复制完成了才能在此基础上进行数据的读写分离。
全栈程序员站长
2022/08/11
7.6K0
mysql 读写分离_详解MySQL读写分离
MySQL数据库:读写分离
读写分离解决的是,数据库的写操作,影响了查询的效率,适用于读远大于写的场景。读写分离的实现基础是主从复制,主数据库利用主从复制将自身数据的改变同步到从数据库集群中,然后主数据库负责处理写操作(当然也可以执行读操作),从数据库负责处理读操作,不能执行写操作。并可以根据压力情况,部署多个从数据库提高读操作的速度,减少主数据库的压力,提高系统总体的性能。
全栈程序员站长
2022/06/29
2.1K0
MySQL数据库:读写分离
MySQL读写分离(ProxySQL)
读写分离就是用户在发送请求时,请求经过中间件,中间件将请求中的读和写操作分辨出来将读请求发送给后端的从服务器,将写请求发送给后端的主服务器,再又主服务器通过主从复制将数据复制给其他从服务器
Java帮帮
2019/05/17
6.3K0
MySQL读写分离(ProxySQL)
相关推荐
Mysql 实现数据库读写分离
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验