学习笔记:深度学习——基于pytorch的bert应用实践(代码片段)

新四石路打卤面 新四石路打卤面     2022-12-04     403

关键词:

学习时间:2022.04.26~2022.04.30

文章目录

7. 基于PyTorch的BERT应用实践

本节着重于将BERT模型应用到具体的实践当中,因此未来有很多可以优化的地方,比如自己重写dataset和dataloader方法,这样对于pytorch应该能有更好地、更灵活的运用。

7.1 工具选取

使用PyTorch框架应用Bert,一般是去Hugging Face (一个社区,有很多人会把训练好了的模型放在上面,可以支持PyTorch和Tenserflow)上,把Bert模型下载下来,然后再应用。

在应用具体模型之前,需要先安装一下Hugging Face提供的transformers库,因为它上面的模型都是基于这个transformers库来编写的,即使下载了模型也要通过这个库来使用。(这个是必要的)

pip install transformers

此外,数据处理部分,如果不想自己重写dataset和dataloader方法的话,Hugging Face也提供了datasets库,可以通过pip install datasets来进行安装和使用。(这个是可选的)

这两个库的使用方法,可以直接去官网查看使用文档,我觉得还是相对来说比较详细了(见TransformersDatasets)。

此外,自己这次使用的数据集也是来自Kaggle:Quora Insincere Questions Classification | Kaggle

7.2 文本预处理

这一部分主要参考了Kaggle上大神的代码,然后汇总了一下装进一个函数里了。以下简单举例:

  1. 将一些表情符号、html网址、email ids、urls全都清除:
def clean_data(data):
    punct_tag = re.compile(r'[^\\w\\s]')
    data = punct_tag.sub(r'', data)
    html_tag = re.compile(r'<.*?>')
    data = html_tag.sub(r'', data)
    url_clean = re.compile(r"https://\\S+|www\\.\\S+")
    data = url_clean.sub(r'', data)
    emoji_clean = re.compile("["
                             u"\\U0001F600-\\U0001F64F"  # emoticons
                             u"\\U0001F300-\\U0001F5FF"  # symbols & pictographs
                             u"\\U0001F680-\\U0001F6FF"  # transport & map symbols
                             u"\\U0001F1E0-\\U0001F1FF"  # flags (iOS)
                             u"\\U00002702-\\U000027B0"
                             u"\\U000024C2-\\U0001F251"
                             "]+", flags=re.UNICODE)
    data = emoji_clean.sub(r'', data)
    url_clean = re.compile(r"https://\\S+|www\\.\\S+")
    data = url_clean.sub(r'', data)
    return data
  1. 清除所有格:
def strip_possessives(text):
    text = text.replace("'s", '')
    text = text.replace('’s', '')
    text = text.replace("\\'s", '')
    text = text.replace("\\’s", '')
    return text
  1. 将数字替换成##:
def clean_numbers(x):
    x = re.sub("[0-9]5,", '#####', x)
    x = re.sub("[0-9]4", '####', x)
    x = re.sub("[0-9]3", '###', x)
    x = re.sub("[0-9]2", '##', x)
    return x

……

最后通过一个函数全部调用:

def texts_preprogress(df):
    # 应用之前所有的预处理步骤
    df = df.apply(lambda x: clean_data(x))
    df = df.apply(lambda x: expand_contractions(x))
    df = df.apply(lambda x: replace_typical_misspell(x))
    df = df.apply(lambda x: strip_possessives(x))
    df = df.apply(lambda x: replace_multi_exclamation_mark(x))
    df = df.apply(lambda x: clean_text(x))
    df = df.apply(lambda x: change_stopwords(x))
    df = df.apply(lambda x: clean_numbers(x))
    return df

7.3 使用BERT模型

整个使用分为5部分:数据输入及应用预处理、提取词向量、构建网络模型(嵌入BERT)、参数准备和模型训练。

个人本次使用的是bert-base-uncased模型。

7.3.1 数据输入及应用预处理

# 确定设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)
# 导入文件并进行文本预处理
train_df = pd.read_csv('train.csv', nrows=540)
train_df['question_text'] = texts_preprogress(train_df['question_text'])
eval_df = pd.read_csv('train.csv', nrows=675)[540:675]
eval_df['question_text'] = texts_preprogress(eval_df['question_text'])

7.3.2 提取词向量

⭐这里值得特别特别特别提一下的是:tokenizer函数中的padding参数。

padding=True等价于padding='max_length',只对于句子对任务起作用,会一样自动补全到batch中的最长长度;但是!!!对于单句的任务,两者并不等价!!!这也是我一开始设置了padding=True,然后指定max_length=72,最后输出的句子却依然长短不一,然后输不进网络的原因。

所以对于单句任务,一定要先指定padding='max_length',然后再设置max_length= ,这样才会真正补全。

# 导入分词器
tokenizer = AutoTokenizer.from_pretrained('D:/Py-project/models/huggingface/bert-base-uncased/')


# 定义token函数
def tokenize_function(examples):
    return tokenizer(examples['question_text'], padding='max_length', max_length=72, truncation=True)
    # padding='max_length'填充批处理中较短的序列以匹配最长的序列(太坑了!!!!);truncation=True将序列截断为模型接受的最大长度


# 定义处理标签及id列的函数
def batch_label(df):
    df = df.drop('qid', axis=1)
    dataset = Dataset.from_pandas(df)
    for k in dataset.column_names:
        if k == 'target':
            dataset = dataset.rename_column(k, 'labels')
    inputs = dataset.map(tokenize_function, batched=True)  # 分词并输出词向量;batched=True开启批次输入
    inputs.set_format(type='torch')  # 词向量转为tensor
    inputs = inputs.remove_columns('question_text')  # 删除文本列,因为模型不接受原始文本作为输入
    dataloader = DataLoader(inputs, shuffle=True, batch_size=128)  # 创建成DataLoader
    return dataloader


train_dl = batch_label(train_df)
eval_dl = batch_label(eval_df)

7.3.3 网络建模

其实Bert也有直接提供一个训练好的bertclassification模型,但只能直接调用,不太好嵌入到网络,所以这里只用了bert模型,然后嵌入到了网络里面。

class my_bert(nn.Module):
    def __init__(self):
        super(my_bert, self).__init__()

        # Bert模型需要嵌入到网络中
        self.bert = BertModel.from_pretrained("D:/Py-project/models/huggingface/bert-base-uncased")
        # 将Bert模型的参数设置为可以更新
        for param in self.bert.parameters():
            param.requires_grad = True
        self.linear = torch.nn.Linear(768, 2)
        self.dropout = torch.nn.Dropout(0.5)

    def forward(self, x):

        input_ids = x['input_ids'].to(device)
        token_type_ids = x['token_type_ids'].to(device)
        attention_mask = x['attention_mask'].to(device)

        output = self.bert(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
        output = output['pooler_output']  # bert的输出有4个,这个任务仅需要1个
        output = self.linear(output)
        output = self.dropout(output)
        output = torch.sigmoid(output)
        return output

7.3.4 参数准备

# 设置随机数种子,保证结果可复现
seed = 42
if device == 'cuda':
    torch.cuda.manual_seed(seed)
else:
    torch.manual_seed(seed)

# 实例化模型
model = my_bert()
model.to(device)

# 设置参数
lr = 2e-5
epoch = 2
show_step = 1
optimizer = optim.AdamW(model.parameters(), lr=lr)
criterion = nn.CrossEntropyLoss()

7.3.5 模型训练

这里只涉及到一个小问题:如果数据集太大了,基本都需要转成dataloader后再分批输入网络进行训练和验证。

for i in range(epoch):
    model.train()
    losses = []
    accuracy = []
    start_time = time.time()

    for batch in tqdm(train_dl, desc=f'第i+1/epoch次迭代进度', ncols=100):
        pred = model(batch)  # 正向传播
        label = batch['labels'].to(device)
        loss = criterion(pred, label)  # 计算损失函数

        # 存入准确率和loss
        losses.append(loss.item())
        pred_labels = torch.argmax(pred, dim=1)
        acc = torch.sum(pred_labels == label).item() / len(pred_labels)
        accuracy.append(acc)

        optimizer.zero_grad()  # 优化器的梯度清零
        loss.backward()  # 反向传播
        optimizer.step()  # 参数更新

    # 测试集评估
    if i % show_step == 0:  # 控制输出间隔
        model.eval()
        ev_losses = []
        ev_acc = []
        with torch.no_grad():
            for batch in eval_dl:
                ev_pred = model(batch)
                ev_label = batch['labels'].to(device)
                ev_loss = criterion(ev_pred, ev_label)

                # 存入准确率和loss
                ev_losses.append(ev_loss.item())
                pred_labels = torch.argmax(ev_pred, dim=1)
                acc = torch.sum(pred_labels == ev_label).item() / len(pred_labels)
                ev_acc.append(acc)

        elapsed_time = time.time() - start_time
        print("\\nEpoch: /: ".format(i+1, epoch),
              "Accuracy: :.6f; ".format(np.mean(accuracy)),
              "Val Accuracy: :.6f; ".format(np.mean(ev_acc)),
              "Loss: :.6f; ".format(np.mean(losses)),
              "Val Loss: :.6f; ".format(np.mean(ev_losses)),
              'Time: :.2fs'.format(elapsed_time))

以上就是本人的bert第一次尝试的大部分内容了,之后应该会去试试ALBERT和RoBERTa,然后也会尝试自己重写下datasets和dataloader。未来继续加油!

「深度学习一遍过」必修18:基于pytorch的语义分割模型实现(代码片段)

...型基础上实现膨胀卷积 1自定义5层普通卷积 模型结构 pytorch代码 fromtor 查看详情

深度学习-pytorch框架实战系列

深度学习-PyTorch框架实战系列PyTorch是一个开源的Python机器学习库,基于Torch,用于自然语言处理等应用程序。2017年1月,由Facebook人工智能研究院(FAIR)基于Torch推出了PyTorch。它是一个基于Python的可续计算包,提供两个高级功能... 查看详情

「深度学习一遍过」必修17:基于pytorch细粒度分类实战(代码片段)

本专栏用于记录关于深度学习的笔记,不光方便自己复习与查阅,同时也希望能给您解决一些关于深度学习的相关问题,并提供一些微不足道的人工神经网络模型设计思路。专栏地址:「深度学习一遍过」必修篇... 查看详情

对比学习:《深度学习之pytorch》《pytorch深度学习实战》+代码

PyTorch是一个基于Python的深度学习平台,该平台简单易用上手快,从计算机视觉、自然语言处理再到强化学习,PyTorch的功能强大,支持PyTorch的工具包有用于自然语言处理的AllenNLP,用于概率图模型的Pyro,扩展了PyTorch的功能。通... 查看详情

百分点认知智能实验室出品:深度迁移学习十八问

编者按深度迁移学习是基于深度神经网络的迁移学习方法,BERT通过预训练模型达到深度迁移学习的效果,自从2018年底BERT横空出世以来,就以势不可挡的姿态横扫了众多榜单,甚至在阅读理解任务SQuAD中超越人类水平。BERT在公检... 查看详情

「深度学习一遍过」必修14:基于pytorch研究深度可分离卷积与正常卷积的性能差异(代码片段)

本专栏用于记录关于深度学习的笔记,不光方便自己复习与查阅,同时也希望能给您解决一些关于深度学习的相关问题,并提供一些微不足道的人工神经网络模型设计思路。专栏地址:「深度学习一遍过」必修篇... 查看详情

pytorch学习笔记3.深度学习基础(代码片段)

...多分类22.全连接层23.激活函数与GPU加速24.测试根据龙良曲Pytorch学习视频整理,视频链接:【计算机-AI】PyTorch学这个就够了!(好课推荐)深度学习与PyTorch入门实战——主讲人龙 查看详情

《深度学习笔记》——pytorch调整学习率(代码片段)

1定义调整学习率函数defadjust_learning_rate(optimizer,epoch,lr):"""SetsthelearningratetotheinitialLRdecayedby10every2epochs"""#optimizer表示优化器对象lr*=(0.1**(epoch//2))forparam_groupinop 查看详情

李沐《动手学深度学习》第二版pytorch笔记1环境搭建(代码片段)

李沐《动手学深度学习》第二版pytorch笔记1搭建环境文章目录李沐《动手学深度学习》第二版pytorch笔记1搭建环境此时尚有耐心虚拟环境搭建创建虚拟环境查看已有的虚拟环境激活虚拟环境安装深度学习框架和d2l软件包安装d2l安... 查看详情

《pytorch深度学习实践》介绍

...bsp;     本文为bilibili刘二大人所讲的《PyTorch深度学习实践》学习笔记介绍。       笔记融入了自己对课程和代码的理解,希望对大家有所帮助,由于本人知识尚浅,如果笔记中有任何... 查看详情

基于深度学习的语义分割初探fcn以及pytorch代码实现(代码片段)

基于深度学习的语义分割初探FCN以及pytorch代码实现FCN论文论文地址:https://arxiv.org/abs/1411.4038FCN是基于深度学习方法的第一篇关于语义分割的开山之作,虽然这篇文章的分割结果现在看起来并不是目前最好的,但其意... 查看详情

pytorch深度学习项目实战100例——基于transformer实现twitter文本隐喻二分类|第43例

前言大家好,我是阿光。本专栏整理了《PyTorch深度学习项目实战100例》,内包含了各种不同的深度学习项目,包含项目原理以及源码,每一个项目实例都附带有完整的代码+数据集。正在更新中~✨ 查看详情

pytorch学习笔记基础知识

PyTorch深度学习:60分钟入门(Translation) 查看详情

pytorch深度学习项目实战100例——基于lenet5实现交通标志分类任务|第50例

前言大家好,我是阿光。本专栏整理了《PyTorch深度学习项目实战100例》,内包含了各种不同的深度学习项目,包含项目原理以及源码,每一个项目实例都附带有完整的代码+数据集。正在更新中~✨ 查看详情

pytorch深度学习实践入门01(代码片段)

文章目录基于PyTorch的两层神经网络一、基于numpy的两层神经网络实现:二、基于PyTorch的两层神经网络实现:三、使用nn库实现两层神经网络四、自定义nnModules实现两层神经网络总结基于PyTorch的两层神经网络提示:在... 查看详情

pytorch深度学习项目实战100例——基于alexnet实现宠物小精灵(宝可梦)分类任务|第49例

前言大家好,我是阿光。本专栏整理了《PyTorch深度学习项目实战100例》,内包含了各种不同的深度学习项目,包含项目原理以及源码,每一个项目实例都附带有完整的代码+数据集。正在更新中~✨ 查看详情

pytorch深度学习——逻辑斯蒂回归(分类问题)(b站刘二大人p6学习笔记)(代码片段)

目录1什么是分类问题?2为什么要使用逻辑斯蒂回归?3SigmidFounction(逻辑斯蒂回归函数)3.1模型的改变3.2损失函数的改变(BCELoss) 3.3代码的改变1什么是分类问题?    分类问题,与之前学习的... 查看详情

小白学习pytorch教程十五bert:通过pytorch来创建一个文本分类的bert模型(代码片段)

@Author:Runsen2018年,谷歌发表了一篇题为《Pre-trainingofdeepbidirectionalTransformersforLanguageUnderstanding》的论文。在本文中,介绍了一种称为BERT(带转换器Transformers的双向编码Encoder器表示)的语言模型,该模型在问答、自然语言推理、... 查看详情