赞
踩
如果一个比赛或者一个项目可以使用预训练模型,那么好了,bert预训练模型一定是你最好的选择。对于初入深度学习坑的朋友们直接上手tensorflow版的(fine tune)bert可能不太友好,keras会让你发现使用bert预训练模型就像搭乐高积木一样简单。本文主要介绍bert在文本分类和主体抽取中的应用。
在开始之前要先pip安装一下keras版的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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。