用keras优雅的使用bert

本文介绍了如何在keras中优雅地使用BERT模型,包括文本分类和事件主体抽取的应用。通过重写Tokenizer确保序列长度一致,简化数据处理,并展示了BERT在书评分析任务中取得的优异效果。此外,还讨论了BERT在金融领域事件主体抽取问题上的处理方法,将双输入转化为单输入,实现高效训练。
摘要由CSDN通过智能技术生成

1.概述

如果一个比赛或者一个项目可以使用预训练模型,那么好了,bert预训练模型一定是你最好的选择。对于初入深度学习坑的朋友们直接上手tensorflow版的(fine tune)bert可能不太友好,keras会让你发现使用bert预训练模型就像搭乐高积木一样简单。本文主要介绍bert在文本分类和主体抽取中的应用。
在开始之前要先pip安装一下keras版的bert,然后可以下载官方版的预训练模型,根据官方介绍,这份权重是用中文维基百科为语料进行训练的。

2.bert的文本分类

import json
import numpy as np
import pandas as pd
from random import choice
from keras_bert import load_trained_model_from_checkpoint, Tokenizer
import re, os
import codecs


maxlen = 100
config_path = '../chinese_L-12_H-768_A-12/bert_config.json'
checkpoint_path = '../chinese_L-12_H-768_A-12/bert_model.ckpt'
dict_path = '../chinese_L-12_H-768_A-12/vocab.txt'


token_dict = {
   }

with codecs.open(dict_path, 'r', 'utf8') as reader:
    for line in reader:
        token = line.strip()
        token_dict[token] = len(token_dict)


class OurTokenizer(Tokenizer):
    def _tokenize(self, text):
        R = []
        for c in text:
            if c in self._token_dict:
                R.append(c)
            elif self._is_space(c):
                R.append('[unused1]') # space类用未经训练的[unused1]表示
            else:
                R.append('[UNK]') # 剩余的字符是[UNK]
        return R

tokenizer = OurTokenizer(token_dict)

这一部分是引入bert的Tokenizer,并对其进行进行重写。

这里简单解释一下 Tokenizer 的输出结果。首先,默认情况下,分词后句子首位会分别加上 [CLS] 和 [SEP] 标记,其中 [CLS] 位置对应的输出向量是能代表整句的句向量(反正 Bert 是这样设计的),而 [SEP] 则是句间的分隔符,其余部分则是单字输出(对于中文来说)。

本来 Tokenizer 有自己的 _tokenize 方法,我这里重写了这个方法,是要保证 tokenize 之后的结果,跟原来的字符串长度等长(如果算上两个标记,那么就是等长再加 2)。 Tokenizer 自带的 _tokenize 会自动去掉空格,然后有些字符会粘在一块输出,导致 tokenize 之后的列表不等于原来字符串的长度了,这样如果做序列标注的任务会很麻烦。

而为了避免这种麻烦,还是自己重写一遍好了。主要就是用 [unused1] 来表示空格类字符,而其余的不在列表的字符用 [UNK] 表示,其中 [unused*] 这些标记是未经训练的(随即初始化),是 Bert 预留出来用来增量添加词汇的标记,所以我们可以用它们来指代任何新字符。

neg = pd.read_excel('neg.xls', header=None)
pos = pd.read_excel('pos.xls', header=None)

data = []

for d in neg[0]:
    data.append((d, 0))

for d in pos[0]:
    data.append((d, 1))


# 按照9:1的比例划分训练集和验证集
random_order = list(range(len(data)))
np.random.shuffle(random_order)
train_data = [data[j] for i, j in enumerate(random_order) if i % 10 != 0]
valid_data = [data[j] for i, j in enumerate(random_order) if i % 10 == 0]


def seq_padding(X, padding=0):
    L = [len(x) for x in X]
    ML = max(L)
    return np.array([
        np.concatenate([x, [padding] * (ML - len(x))]) if len(x) < ML else x for x in X
    ])


class data_generator:
    def __init__(self, data, batch_size=32):
        self.data = data
        self.batch_size = batch_size
        self.steps = len(self.data) // self.batch_size
        if len(self.data) % self.batch_size != 0:
            self.steps += 1
    def __len__(self):
        return self.steps
    def __iter__(self):
        while True:
            idxs = list(range(len(self.data)))
            np.random.shuffle(idxs)
            X1, X2, Y = [], [], []
            for i in idxs:
                d = self.data[i]
                text = d[0][:maxlen]
                x1, x2 = tokenizer.encode(first=text)
                y = d[1]
                X1.append(x1)
                X2.append(x2)
                Y.append([y])
                if len(X1) == self.batch_size or i == idxs[-1]:
                    X1 = seq_padding(X1)
                    X2 = seq_padding(X2)
                    Y = seq_padding(Y)
                    yield [X1, X2], Y
                    [X1, X2, Y] = [], [], []

这部分就是数据的处理,了解keras的朋友应该都不陌生,划分训练集和测试集以及一种省内存的数据的读入方式。data_generator只是一种为了节约内存的一种方式可以随便写

from keras.layers import *
from keras.models import Model
import keras.backend as K
from keras
  • 7
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值