-
2019-10-25 17:48:34
目录
一、Bert 预训练模型准备
中文预训练模型下载 当Bert遇上Keras:这可能是Bert最简单的打开姿势 keras-bert
不同模型的性能对比如下(可根据自己的数据选择合适的模型,模型越大需要训练的时间越长)
模型 开发集 测试集 BERT 83.1 (82.7) / 89.9 (89.6) 82.2 (81.6) / 89.2 (88.8) ERNIE 73.2 (73.0) / 83.9 (83.8) 71.9 (71.4) / 82.5 (82.3) BERT-wwm 84.3 (83.4) / 90.5 (90.2) 82.8 (81.8) / 89.7 (89.0) BERT-wwm-ext 85.0 (84.5) / 91.2 (90.9) 83.6 (83.0) / 90.4 (89.9) RoBERTa-wwm-ext 86.6 (85.9) / 92.5 (92.2) 85.6 (85.2) / 92.0 (91.7) RoBERTa-wwm-ext-large 89.6 (89.1) / 94.8 (94.4) 89.6 (88.9) / 94.5 (94.1) 二、Bert 模型文本分类
1、数据准备
使用的仍是用户评论情感极性判别的数据
训练集:data_train.csv ,样本数为82025,情感极性标签(0:负面、1:中性、2:正面)
测试集:data_test.csv ,样本数为35157
评论数据主要包括:食品餐饮类,旅游住宿类,金融服务类,医疗服务类,物流快递类;部分数据如下:
2、代码实现
import pandas as pd import codecs, gc import numpy as np from sklearn.model_selection import KFold from keras_bert import load_trained_model_from_checkpoint, Tokenizer from keras.metrics import top_k_categorical_accuracy from keras.layers import * from keras.callbacks import * from keras.models import Model import keras.backend as K from keras.optimizers import Adam from keras.utils import to_categorical #读取训练集和测试集 train_df=pd.read_csv('data/data_train.csv', sep='\t', names=['id', 'type', 'contents', 'labels']).astype(str) test_df=pd.read_csv('data/data_test.csv', sep='\t', names=['id', 'type', 'contents']).astype(str) maxlen = 100 #设置序列长度为120,要保证序列长度不超过512 #预训练好的模型 config_path = 'chinese_roberta_wwm_large_ext_L-24_H-1024_A-16/bert_config.json' checkpoint_path = 'chinese_roberta_wwm_large_ext_L-24_H-1024_A-16/bert_model.ckpt' dict_path = 'chinese_roberta_wwm_large_ext_L-24_H-1024_A-16/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) #重写tokenizer 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]') # 用[unused1]来表示空格类字符 else: R.append('[UNK]') # 不在列表的字符用[UNK]表示 return R tokenizer = OurTokenizer(token_dict) #让每条文本的长度相同,用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 ]) #data_generator只是一种为了节约内存的数据方式 class data_generator: def __init__(self, data, batch_size=32, shuffle=True): self.data = data self.batch_size = batch_size self.shuffle = shuffle 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))) if self.shuffle: 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[:, 0, :] [X1, X2, Y] = [], [], [] #计算top-k正确率,当预测值的前k个值中存在目标类别即认为预测正确 def acc_top2(y_true, y_pred): return top_k_categorical_accuracy(y_true, y_pred, k=2) #bert模型设置 def build_bert(nclass): bert_model = load_trained_model_from_checkpoint(config_path, checkpoint_path, seq_len=None) #加载预训练模型 for l in bert_model.layers: l.trainable = True x1_in = Input(shape=(None,)) x2_in = Input(shape=(None,)) x = bert_model([x1_in, x2_in]) x = Lambda(lambda x: x[:, 0])(x) # 取出[CLS]对应的向量用来做分类 p = Dense(nclass, activation='softmax')(x) model = Model([x1_in, x2_in], p) model.compile(loss='categorical_crossentropy', optimizer=Adam(1e-5), #用足够小的学习率 metrics=['accuracy', acc_top2]) print(model.summary()) return model #训练数据、测试数据和标签转化为模型输入格式 DATA_LIST = [] for data_row in train_df.iloc[:].itertuples(): DATA_LIST.append((data_row.contents, to_categorical(data_row.labels, 3))) DATA_LIST = np.array(DATA_LIST) DATA_LIST_TEST = [] for data_row in test_df.iloc[:].itertuples(): DATA_LIST_TEST.append((data_row.contents, to_categorical(0, 3))) DATA_LIST_TEST = np.array(DATA_LIST_TEST) #交叉验证训练和测试模型 def run_cv(nfold, data, data_labels, data_test): kf = KFold(n_splits=nfold, shuffle=True, random_state=520).split(data) train_model_pred = np.zeros((len(data), 3)) test_model_pred = np.zeros((len(data_test), 3)) for i, (train_fold, test_fold) in enumerate(kf): X_train, X_valid, = data[train_fold, :], data[test_fold, :] model = build_bert(3) early_stopping = EarlyStopping(monitor='val_acc', patience=3) #早停法,防止过拟合 plateau = ReduceLROnPlateau(monitor="val_acc", verbose=1, mode='max', factor=0.5, patience=2) #当评价指标不在提升时,减少学习率 checkpoint = ModelCheckpoint('./bert_dump/' + str(i) + '.hdf5', monitor='val_acc',verbose=2, save_best_only=True, mode='max', save_weights_only=True) #保存最好的模型 train_D = data_generator(X_train, shuffle=True) valid_D = data_generator(X_valid, shuffle=True) test_D = data_generator(data_test, shuffle=False) #模型训练 model.fit_generator( train_D.__iter__(), steps_per_epoch=len(train_D), epochs=5, validation_data=valid_D.__iter__(), validation_steps=len(valid_D), callbacks=[early_stopping, plateau, checkpoint], ) # model.load_weights('./bert_dump/' + str(i) + '.hdf5') # return model train_model_pred[test_fold, :] = model.predict_generator(valid_D.__iter__(), steps=len(valid_D), verbose=1) test_model_pred += model.predict_generator(test_D.__iter__(), steps=len(test_D), verbose=1) del model gc.collect() #清理内存 K.clear_session() #clear_session就是清除一个session # break return train_model_pred, test_model_pred #n折交叉验证 train_model_pred, test_model_pred = run_cv(2, DATA_LIST, None, DATA_LIST_TEST) test_pred = [np.argmax(x) for x in test_model_pred] #将测试集预测结果写入文件 output=pd.DataFrame({'id':test_df.id,'sentiment':test_pred}) output.to_csv('data/results.csv', index=None)
3、分类过程与结果
在服务器上跑了两天,终于完成了……
最终提交结果F1-score达到了94.90%,比使用的其他模型效果都好。
直接看排名结果,一下子上升到了第一,哈哈哈
Bert文本分类(keras-bert实现)源代码及数据集资源下载:
项目实战-Bert文本分类(keras-bert实现)源代码及数据集.zip-自然语言处理文档类资源-CSDN下载
本人博文NLP学习内容目录:
一、NLP基础学习
二、NLP项目实战
交流学习资料共享欢迎入群:955817470(群一),801295159(群二)
更多相关内容 -
BERT文本分类数据
2021-01-11 21:27:54BERT文本分类代码对应的数据 -
自然语言处理动手学Bert文本分类
2021-07-21 14:28:50自然语言处理动手学Bert文本分类,本套课程基于Pytorch最新1.4版本来实现利用Bert实现中文文本分类任务,延续动手学系列课程风格,全程手敲代码,跟着杨博一行一行代码撸起来。 -
bert文本分类 代码+数据
2022-08-06 09:32:19bert文本分类 代码+数据bert文本分类 代码+数据bert文本分类 代码+数据 -
项目实战-Bert文本分类(keras-bert实现)源代码及数据集.zip
2022-04-09 10:26:081、内容概要:本资源主要基于bert(keras)实现文本分类,适用于初学者学习文本分类使用。 2、数据集为电商真实商品评论数据,主要包括训练集data_train,测试集data_test ,经过预处理的训练集clean_data_train和... -
PyTorch Bert文本分类
2022-03-31 23:39:16最近pytorch大火,而目前很少有博客完整的给出pytorch-bert的应用代码,本文从最简单的中文文本分类入手,一步一步的给出每段代码~ (代码简单清晰,读者有兴趣可上手实践) 首先安装pytorch-bert库, 即:pip ...改文章转载于作者:weixin_40001805
仅供学习参考!!!
之前用bert一直都是根据keras-bert封装库操作的,操作非常简便(可参考苏剑林大佬博客当Bert遇上Keras:这可能是Bert最简单的打开姿势),这次想要来尝试一下基于pytorch的bert实践。最近pytorch大火,而目前很少有博客完整的给出pytorch-bert的应用代码,本文从最简单的中文文本分类入手,一步一步的给出每段代码~ (代码简单清晰,读者有兴趣可上手实践)
- 首先安装pytorch-bert库, 即:pip install pytorch_pretrained_bert;
- 然后下载预训练模型权重,这里下载的是 chinese_roberta_wwm_ext_pytorch
,下载链接为中文BERT-wwm系列模型 (这里可选择多种模型); - 数据集选择的THUCNews,整理出18w条数据,10类新闻文本的中文分类问题(10分类),每类新闻数据量相等,为1.8w条,数据集来自train.txt(只选择了网址里的train.txt),
数据集的具体格式如下。
下面进入代码阶段。(训练环境为Google Colab)
1.导入必要的库
# coding: UTF-8 import torch import time import torch.nn as nn import torch.nn.functional as F from pytorch_pretrained_bert import BertModel, BertTokenizer, BertConfig, BertAdam import pandas as pd import numpy as np from tqdm import tqdm from torch.utils.data import * path = "data/" bert_path = "chinese_roberta_wwm_ext_pytorch/" tokenizer = BertTokenizer(vocab_file=bert_path + "vocab.txt") # 初始化分词器
2.预处理数据集
input_ids = [] # input char ids input_types = [] # segment ids input_masks = [] # attention mask label = [] # 标签 pad_size = 32 # 也称为 max_len (前期统计分析,文本长度最大值为38,取32即可覆盖99%) with open(path + "train.txt", encoding='utf-8') as f: for i, l in tqdm(enumerate(f)): x1, y = l.strip().split('t') x1 = tokenizer.tokenize(x1) tokens = ["[CLS]"] + x1 + ["[SEP]"] # 得到input_id, seg_id, att_mask ids = tokenizer.convert_tokens_to_ids(tokens) types = [0] *(len(ids)) masks = [1] * len(ids) # 短则补齐,长则切断 if len(ids) < pad_size: types = types + [1] * (pad_size - len(ids)) # mask部分 segment置为1 masks = masks + [0] * (pad_size - len(ids)) ids = ids + [0] * (pad_size - len(ids)) else: types = types[:pad_size] masks = masks[:pad_size] ids = ids[:pad_size] input_ids.append(ids) input_types.append(types) input_masks.append(masks) # print(len(ids), len(masks), len(types)) assert len(ids) == len(masks) == len(types) == pad_size label.append([int(y)])
输出:180000it [00:26, 6728.85it/s] (26秒,速度较快)
3.切分训练集和测试集
# 随机打乱索引 random_order = list(range(len(input_ids))) np.random.seed(2020) # 固定种子 np.random.shuffle(random_order) print(random_order[:10]) # 4:1 划分训练集和测试集 input_ids_train = np.array([input_ids[i] for i in random_order[:int(len(input_ids)*0.8)]]) input_types_train = np.array([input_types[i] for i in random_order[:int(len(input_ids)*0.8)]]) input_masks_train = np.array([input_masks[i] for i in random_order[:int(len(input_ids)*0.8)]]) y_train = np.array([label[i] for i in random_order[:int(len(input_ids) * 0.8)]]) print(input_ids_train.shape, input_types_train.shape, input_masks_train.shape, y_train.shape) input_ids_test = np.array([input_ids[i] for i in random_order[int(len(input_ids)*0.8):]]) input_types_test = np.array([input_types[i] for i in random_order[int(len(input_ids)*0.8):]]) input_masks_test = np.array([input_masks[i] for i in random_order[int(len(input_ids)*0.8):]]) y_test = np.array([label[i] for i in random_order[int(len(input_ids) * 0.8):]]) print(input_ids_test.shape, input_types_test.shape, input_masks_test.shape, y_test.shape)
得到结果
4.加载到高效的DataLoader
BATCH_SIZE = 16 train_data = TensorDataset(torch.LongTensor(input_ids_train), torch.LongTensor(input_types_train), torch.LongTensor(input_masks_train), torch.LongTensor(y_train)) train_sampler = RandomSampler(train_data) train_loader = DataLoader(train_data, sampler=train_sampler, batch_size=BATCH_SIZE) test_data = TensorDataset(torch.LongTensor(input_ids_test), torch.LongTensor(input_types_test), torch.LongTensor(input_masks_test), torch.LongTensor(y_test)) test_sampler = SequentialSampler(test_data) test_loader = DataLoader(test_data, sampler=test_sampler, batch_size=BATCH_SIZE)
5.定义bert模型
class Model(nn.Module): def __init__(self): super(Model, self).__init__() self.bert = BertModel.from_pretrained(bert_path) # /bert_pretrain/ for param in self.bert.parameters(): param.requires_grad = True # 每个参数都要 求梯度 self.fc = nn.Linear(768, 10) # 768 -> 2 def forward(self, x): context = x[0] # 输入的句子 (ids, seq_len, mask) types = x[1] mask = x[2] # 对padding部分进行mask,和句子相同size,padding部分用0表示,如:[1, 1, 1, 1, 0, 0] _, pooled = self.bert(context, token_type_ids=types, attention_mask=mask, output_all_encoded_layers=False) # 控制是否输出所有encoder层的结果 out = self.fc(pooled) # 得到10分类 return out
可以发现,bert模型的定义由于高效简易的封装库存在,使得定义模型较为容易,如果想要在bert之后加入cnn/rnn等层,可在这里定义。
6.实例化bert模型
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = Model().to(DEVICE) print(model)
得到结果
bert模型结构,未完整输出,可根据这个输出学习bert的内部结构
7.定义优化器
param_optimizer = list(model.named_parameters()) # 模型参数名字列表 no_decay = ['bias', 'LayerNorm.bias', 'LayerNorm.weight'] optimizer_grouped_parameters = [ {'params': [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)], 'weight_decay': 0.01}, {'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay)], 'weight_decay': 0.0}] NUM_EPOCHS = 3 optimizer = BertAdam(optimizer_grouped_parameters, lr=2e-5, warmup=0.05, t_total=len(train_loader) * NUM_EPOCHS ) # optimizer = torch.optim.Adam(model.parameters(), lr=2e-5) # 简单起见,可用这一行代码完事
8.定义训练函数和测试函数
def train(model, device, train_loader, optimizer, epoch): # 训练模型 model.train() best_acc = 0.0 for batch_idx, (x1,x2,x3, y) in enumerate(train_loader): start_time = time.time() x1,x2,x3, y = x1.to(device), x2.to(device), x3.to(device), y.to(device) y_pred = model([x1, x2, x3]) # 得到预测结果 model.zero_grad() # 梯度清零 loss = F.cross_entropy(y_pred, y.squeeze()) # 得到loss loss.backward() optimizer.step() if(batch_idx + 1) % 100 == 0: # 打印loss print('Train Epoch: {} [{}/{} ({:.2f}%)]tLoss: {:.6f}'.format(epoch, (batch_idx+1) * len(x1), len(train_loader.dataset), 100. * batch_idx / len(train_loader), loss.item())) # 记得为loss.item() def test(model, device, test_loader): # 测试模型, 得到测试集评估结果 model.eval() test_loss = 0.0 acc = 0 for batch_idx, (x1,x2,x3, y) in enumerate(test_loader): x1,x2,x3, y = x1.to(device), x2.to(device), x3.to(device), y.to(device) with torch.no_grad(): y_ = model([x1,x2,x3]) test_loss += F.cross_entropy(y_, y.squeeze()) pred = y_.max(-1, keepdim=True)[1] # .max(): 2输出,分别为最大值和最大值的index acc += pred.eq(y.view_as(pred)).sum().item() # 记得加item() test_loss /= len(test_loader) print('nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.2f}%)'.format( test_loss, acc, len(test_loader.dataset), 100. * acc / len(test_loader.dataset))) return acc / len(test_loader.dataset)
9.开始训练和测试
best_acc = 0.0 PATH = 'roberta_model.pth' # 定义模型保存路径 for epoch in range(1, NUM_EPOCHS+1): # 3个epoch train(model, DEVICE, train_loader, optimizer, epoch) acc = test(model, DEVICE, test_loader) if best_acc < acc: best_acc = acc torch.save(model.state_dict(), PATH) # 保存最优模型 print("acc is: {:.4f}, best acc is {:.4f}n".format(acc, best_acc))
输出:(训练时间较长,这里只训练了一个epoch,测试集得到0.9407的accuracy)
10.加载最优模型进行测试
model.load_state_dict(torch.load("roberta_model.pth")) acc = test(model, DEVICE, test_loader) # 如果打比赛的话,下面代码也可参考 """ # 测试集提交 PATH = "roberta_model.pth" model.load_state_dict(torch.load(PATH)) def test_for_submit(model, device, test_loader): # 测试模型 model.eval() preds = [] for batch_idx, (x1,x2,x3) in tqdm(enumerate(test_loader)): x1,x2,x3 = x1.to(device), x2.to(device), x3.to(device) with torch.no_grad(): y_ = model([x1,x2,x3]) pred = y_.max(-1, keepdim=True)[1].squeeze().cpu().tolist() # .max() 2输出,分别为最大值和最大值的index preds.extend(pred) return preds preds = test_for_submit(model, DEVICE, test_loader) """
得到结果
经过以上10步,即可建立起较为完整的pytorch-bert文本分类体系,代码也较为简单易懂,对读者有帮助记得点个赞呀~
完结-
-
Bert文本分类(基于keras-bert实现训练,保存,加载,预测单个文本).zip
2022-05-07 11:30:42Bert文本分类(基于keras-bert实现训练,保存,加载,预测单个文本).zip 大学生课程设计 基于python的课程设计 自己大二写的课程设计 -
Bert文本分类实战(附代码讲解)
2022-03-23 10:52:46BERT全称是Bidirectional Encoder Representations from Transformers,是google最新提出的NLP预训练方法,在大型文本语料库(如维基百科)上训练通用的“语言理解”模型,然后将该模型用于我们关心的下游NLP任务...目录
一、Bert简介
BERT全称是Bidirectional Encoder Representations from Transformers,是google最新提出的NLP预训练方法,在大型文本语料库(如维基百科)上训练通用的“语言理解”模型,然后将该模型用于我们关心的下游NLP任务(如分类、阅读理解)。 BERT优于以前的方法,因为它是用于预训练NLP的第一个**无监督,深度双向**系统,从名字我们能看出该模型两个核心特质:依赖于Transformer以及双向,同时它也是木偶动画《芝麻街》里面的角色,它还有个兄弟EMLo。长右边这样:
关于Bert这个模型的神奇之处我就不在这里多说了,它直接颠覆了人们对Pretrained model的理解。尽管Bert模型有多得骇人听闻的参数,但是我们可以直接借助迁移学习的想法使用已经预训练好的模型参数,并根据自己的实际任务进行fine-tuning。复旦大学的一篇论文《How to Fine-Tune BERT for Text Classification》(下载地址:https://arxiv.org/pdf/1905.05583.pdf)给出了一些调节Bert参数的建议,作者针对文本分类任务的BERT微调方法,给出了微调模式的一般解决方案。最后,提出的解决方案在8个广泛研究的文本分类数据集上获取了最新的结果,具体实现步骤如下:
(1)进一步在开放域预训练BERT;
(2)采用多任务方式可选择性地微调BERT;
(3)在目标任务上微调BERT。同时研究了fine-tuning技术对Bert在长文本任务、隐藏层选择、隐藏层学习率、知识遗忘、少样本学习问题上的影响。
1. 微调策略:不同网络层包含不同的特征信息,哪一层更有助于目标任务?这是一个考虑的方向;
2. 进一步预训练:在目标域进一步得到预训练模型;
3. 多任务微调:多任务可以挖掘共享信息,同时对所有任务进行微调是否,使用多任务策略对结果有帮助。
1.1 Transformer模型
为了描述得更清楚,还是先把transformer的模型结构列在下面(来自论文《Attention Is All You Need》):
1.2 Bert模型
而Bert则是多个Transformer的双向叠加,中间每一个蓝色的圈圈都是一个transformer(来自论文《Pre-training of Deep Bidirectional Transformers for Language Understanding》)。
二、BERT的发展历程
2.1 One-Hot 编码
One-Hot编码,又称为一位有效编码,主要是采用N位状态寄存器来对N个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候只有一位有效。
One-Hot编码是分类变量作为二进制向量的表示。这首先要求将分类值映射到整数值。然后,每个整数值被表示为二进制向量,除了整数的索引标记为1之外,其他都是零值。
2.1.1 无法计算词相似度
例如One-Hot编码如下:
我们 = (0,1,0,0,0,0) 爬山 = (0,0,1,0,0,0)
运动 = (1,0,0,0,0,0) 昨天 = (0,0,0,1,0,0)
Euclidean distance(欧式距离)
d(我们,爬山)=√2
d(我们,运动)=√2
d(运动,爬山)=√2
d(运动,昨天)=√2
Cosine similarity(余弦相似度)
sim(我们,爬山)=0
sim(我们,运动)=0
sim(运动,爬山)=0
sim(运动,昨天)=0
结论:One-hot 无法表达单词间的相似度
2.1.2 Sparsity(稀疏性)
向量大小大于等于词典大小
比如我们要对 “hello world” 进行one-hot编码,怎么做呢?
1.确定要编码的对象--hello world,
2.确定分类变量--h e l l o 空格 w o r l d,共27种类别(26个小写字母 + 空格,);
3.以上问题就相当于,有11个样本,每个样本有27个特征,将其转化为二进制向量表示,
这里有一个前提,特征排列的顺序不同,对应的二进制向量亦不同(比如我把空格放在第一列和a放第一列,one-hot编码结果肯定是不同的)
因此我们必须要事先约定特征排列的顺序:
1. 27种特征首先进行整数编码:a--0,b--1,c--2,......,z--25,空格--26
2. 27种特征按照整数编码的大小从前往后排列
得到的one-hot编码如下:
再比如:我们要对["中国", "美国", "日本"]进行one-hot编码,
怎么做呢?
1.确定要编码的对象--["中国", "美国", "日本", "美国"],
2.确定分类变量--中国 美国 日本,共3种类别;
3.以上问题就相当于,有3个样本,每个样本有3个特征,将其转化为二进制向量表示。
我们首先进行特征的整数编码:中国--0,美国--1,日本--2,并将特征按照从小到大排列
得到one-hot编码如下:
["中国", "美国", "日本", "美国"] ---> [[1,0,0], [0,1,0], [0,0,1], [0,1,0]]
2.2 Word2vec
基于One-Hot编码存在的稀疏性和高维度性缺点,2013年,Google开源了一款用于词向量计算的工具——word2vec,引起了工业界和学术界的关注。首先,word2vec可以在百万数量级的词典和上亿的数据集上进行高效地训练;其次,该工具得到的训练结果——词向量(word embedding),可以很好地度量词与词之间的相似性。随着深度学习(Deep Learning)在自然语言处理中应用的普及,很多人误以为word2vec是一种深度学习算法。其实word2vec算法的背后是一个浅层神经网络。另外需要强调的一点是,word2vec是一个计算word vector的开源工具。当我们在说word2vec算法或模型的时候,其实指的是其背后用于计算word vector的CBoW模型和Skip-gram模型,下面我用图的方式直观描述word2vec的优越性。
From One-hot Respresentation to Distributed Respresentation
维度灾难、无法保留词序信息、语义鸿沟 词向量长度自定义(100、200、300)
2.3 BERT的诞生
BERT 的思想其实很大程度上来源于 CBOW 模型,如果从准确率上说改进的话,BERT 利用更深的模型,以及海量的语料,得到的 embedding 表示,来做下游任务时的准确率是要比 word2vec 高不少的。实际上,这也离不开模型的“加码”以及数据的“巨大加码”。再从方法的意义角度来说,BERT 的重要意义在于给大量的 NLP 任务提供了一个泛化能力很强的预训练模型,而仅仅使用 word2vec 产生的词向量表示,不仅能够完成的任务比 BERT 少了很多,而且很多时候直接利用 word2vec 产生的词向量表示给下游任务提供信息,下游任务的表现不一定会很好,甚至会比较差。
三、BERT 的训练过程
BERT的核心过程
1. 从句子中随机选取15%去除,作为模型预测目标,例如:
Input: the man went to the [MASK1] . he bought a [MASK2] of milk. Labels: [MASK1] = store; [MASK2] = gallon
2. 为了学习句子之间的关系。会从数据集抽取两个句子,其中第二句是第一句的下一句的概率是 50%
Sentence A: the man went to the store . Sentence B: he bought a gallon of milk . Label: IsNextSentence
Sentence A: the man went to the store . Sentence B: penguins are flightless . Label: NotNextSentence
3.最后再将经过处理的句子传入大型 Transformer 模型,并通过两个损失函数同时学习上面两个目标就能完成训练。
在论文原文中,作者提出了两个预训练任务:Masked LM 和 Next Sentence Prediction,我们下面来做简单讲解。
3.1 Masked LM(Language Model)
Masked LM 的任务描述为:给定一句话,随机抹去这句话中的一个或几个词,要求根据剩余词汇预测被抹去的几个词分别是什么,如下图所示。
BERT 模型的这个预训练过程其实就是在模仿我们学语言的过程,思想来源于「完形填空」的任务。具体来说,文章作者在一句话中随机选择 15% 的词汇用于预测。对于在原句中被抹去的词汇, 80% 情况下采用一个特殊符号 [MASK] 替换, 10% 情况下采用一个任意词替换,剩余 10% 情况下保持原词汇不变。
80%的概率替换成[MASK],比如 my dog is hairy → my dog is [MASK]
10%的概率替换成随机的一个词,比如 my dog is hairy → my dog is apple
10%的概率替换成它本身,比如 my dog is hairy → my dog is hairy
这么做的主要原因是:在后续微调任务中语句中并不会出现 [MASK] 标记,而且这么做的另一个好处是:预测一个词汇时,模型并不知道输入对应位置的词汇是否为正确的词汇( 10% 概率),这就迫使模型更多地依赖于上下文信息去预测词汇,并且赋予了模型一定的纠错能力。上述提到了这样做的一个缺点,其实这样做还有另外一个缺点,就是每批次数据中只有 15% 的标记被预测,这意味着模型可能需要更多的预训练步骤来收敛。
3.2 Next Sentence Prediction
Next Sentence Prediction 的任务描述为:给定一篇文章中的两句话,判断第二句话在文本中是否紧跟在第一句话之后,如下图所示。
这个类似于「段落重排序」的任务,即:将一篇文章的各段打乱,让我们通过重新排序把原文还原出来,这其实需要我们对全文大意有充分、准确的理解。
Next Sentence Prediction 任务实际上就是段落重排序的简化版:只考虑两句话,判断是否是一篇文章中的前后句。在实际预训练过程中,文章作者从文本语料库中随机选择 50% 正确语句对和 50% 错误语句对进行训练,与 Masked LM 任务相结合,让模型能够更准确地刻画语句乃至篇章层面的语义信息。
BERT 模型通过对 Masked LM 任务和 Next Sentence Prediction 任务进行联合训练,使模型输出的每个字 / 词的向量表示都能尽可能全面、准确地刻画输入文本(单句或语句对)的整体信息,为后续的微调任务提供更好的模型参数初始值。
四、BERT的用途
尽管从直观上来看,Bert可能类似于Word2vec,返回单词们的稠密表示,但是根据不同的任务Bert可以有许多不同的用法。
4.1 文本分类
尽管从直观上来看,Bert可能类似于Word2vec,返回单词们的稠密表示,但是根据不同的任务Bert可以有许多不同的用法。
4.2 单词分类
每个单词都对应一个class。最典型的就是序列标注任务。
4.3 判断两个句子之间的关系
输入两个句子,output一个[CLS]的分类结果。
4.4 QA(问答系统)
问答。需要答案落在输入语料里面。输入是文档以及问题,输出是答案Start的token的id(s)和结束的id(e)。
比如,问题“什么导致的降水?”在文中的答案是第17个单词gravity(重力),所以s=17,e=17。Bert如何实现这个任务呢?首先,还是学习到文档中每个单词的embedding,对应图中浅黄色的条条;然后再学一个橘黄色的条条,这个维度与单词的embedding是一毛一样的,因此这俩张量之间可以做一个dot product,这个类似于注意力。得到的结果通过softmax对每一个向量都返回一个概率,概率最高的token位置就是s。同理,蓝色的条条表示e。若e<s,那么此题无解。
五、BERT文本分类实战
下面是代码部分,我们分别用Fine-tune和非Fine-tune模式下看一下分类效果
5.1 环境搭建
pyotroch>=0.4.1
Python 3.7
条件允许可以使用GPU训练,我CPU、GPU分别都试了一下
GPU服务器:3090Ti 20G
5.2 模型下载
1. 下载bert:
下载地址:GitHub - google-research/bert: TensorFlow code and pre-trained models for BERT
2. 下载bert预训练模型:
pytorch_model.bin
bert_config.json
vocab.txt下载地址: https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-chinese.tar.gz
5.3 数据准备
将你的语料分成4个文件,分别为train.csv,test.csv,dev.csv,class.csv
链接:https://pan.baidu.com/s/19QlxsVKNziRKvNismH4wvA
提取码:cx44具体操作:
我的语料来自于情感分析比赛的,是判断新闻标题情感积极消极还是中性,首先使用pandas对语料进行处理,最终处理成“content+label”的格式。如图所示:
5.4 整体代码架构
5.5 代码分解
5.5.1 bert.py
# coding: UTF-8 import torch import torch.nn as nn # from pytorch_pretrained_bert import BertModel, BertTokenizer from pytorch_pretrained import BertModel, BertTokenizer class Config(object): """配置参数""" def __init__(self, dataset): self.model_name = 'bert' self.train_path = dataset + '/data/train.txt' # 训练集 self.dev_path = dataset + '/data/dev.txt' # 验证集 self.test_path = dataset + '/data/test.txt' # 测试集 self.class_list = [x.strip() for x in open( dataset + '/data/class.txt').readlines()] # 类别名单 self.save_path = dataset + '/saved_dict/' + self.model_name + '.ckpt' # 模型训练结果 self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') # 设备 self.require_improvement = 1000 # 若超过1000batch效果还没提升,则提前结束训练 self.num_classes = len(self.class_list) # 类别数 self.num_epochs = 3 # epoch数 self.batch_size = 128 # mini-batch大小 self.pad_size = 32 # 每句话处理成的长度(短填长切) self.learning_rate = 1e-5 # 学习率 self.bert_path = './bert_pretrain' # bert预训练模型位置 self.tokenizer = BertTokenizer.from_pretrained(self.bert_path) # bert切分词 self.hidden_size = 768 # bert隐藏层个数 class Model(nn.Module): def __init__(self, config): super(Model, self).__init__() self.bert = BertModel.from_pretrained(config.bert_path) for param in self.bert.parameters(): param.requires_grad = True self.fc = nn.Linear(config.hidden_size, config.num_classes) def forward(self, x): context = x[0] # 输入的句子 mask = x[2] # 对padding部分进行mask,和句子一个size,padding部分用0表示,如:[1, 1, 1, 1, 0, 0] _, pooled = self.bert(context, attention_mask=mask, output_all_encoded_layers=False) out = self.fc(pooled) return out
5.5.2 main.py
# coding: UTF-8 import time import torch import numpy as np from train_eval import train, init_network from importlib import import_module import argparse from utils import build_dataset, build_iterator, get_time_dif parser = argparse.ArgumentParser(description='Bert-Chinese-Text-Classification') parser.add_argument('--model', type=str, default='bert', help='choose a model') args = parser.parse_args() if __name__ == '__main__': dataset = 'THUCNews' # 数据集 model_name = args.model # bert x = import_module('models.' + model_name) config = x.Config(dataset) np.random.seed(1) torch.manual_seed(1) torch.cuda.manual_seed_all(4) torch.backends.cudnn.deterministic = True # 保证每次结果一样 start_time = time.time() print("Loading data...") train_data, dev_data, test_data = build_dataset(config) train_iter = build_iterator(train_data, config) dev_iter = build_iterator(dev_data, config) test_iter = build_iterator(test_data, config) time_dif = get_time_dif(start_time) print("Time usage:", time_dif) # train model = x.Model(config).to(config.device) train(config, model, train_iter, dev_iter, test_iter)
5.5.3 train.py
# coding: UTF-8 import numpy as np import torch import torch.nn as nn import torch.nn.functional as F from sklearn import metrics import time from utils import get_time_dif from pytorch_pretrained.optimization import BertAdam # 权重初始化,默认xavier def init_network(model, method='xavier', exclude='embedding', seed=123): for name, w in model.named_parameters(): if exclude not in name: if len(w.size()) < 2: continue if 'weight' in name: if method == 'xavier': nn.init.xavier_normal_(w) elif method == 'kaiming': nn.init.kaiming_normal_(w) else: nn.init.normal_(w) elif 'bias' in name: nn.init.constant_(w, 0) else: pass def train(config, model, train_iter, dev_iter, test_iter): start_time = time.time() model.train() param_optimizer = list(model.named_parameters()) no_decay = ['bias', 'LayerNorm.bias', 'LayerNorm.weight'] optimizer_grouped_parameters = [ {'params': [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)], 'weight_decay': 0.01}, {'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay)], 'weight_decay': 0.0}] # optimizer = torch.optim.Adam(model.parameters(), lr=config.learning_rate) optimizer = BertAdam(optimizer_grouped_parameters, lr=config.learning_rate, warmup=0.05, t_total=len(train_iter) * config.num_epochs) total_batch = 0 # 记录进行到多少batch dev_best_loss = float('inf') last_improve = 0 # 记录上次验证集loss下降的batch数 flag = False # 记录是否很久没有效果提升 model.train() for epoch in range(config.num_epochs): print('Epoch [{}/{}]'.format(epoch + 1, config.num_epochs)) for i, (trains, labels) in enumerate(train_iter): outputs = model(trains) model.zero_grad() loss = F.cross_entropy(outputs, labels) loss.backward() optimizer.step() if total_batch % 100 == 0: # 每多少轮输出在训练集和验证集上的效果 true = labels.data.cpu() predic = torch.max(outputs.data, 1)[1].cpu() train_acc = metrics.accuracy_score(true, predic) dev_acc, dev_loss = evaluate(config, model, dev_iter) if dev_loss < dev_best_loss: dev_best_loss = dev_loss torch.save(model.state_dict(), config.save_path) improve = '*' last_improve = total_batch else: improve = '' time_dif = get_time_dif(start_time) msg = 'Iter: {0:>6}, Train Loss: {1:>5.2}, Train Acc: {2:>6.2%}, Val Loss: {3:>5.2}, Val Acc: {4:>6.2%}, Time: {5} {6}' print(msg.format(total_batch, loss.item(), train_acc, dev_loss, dev_acc, time_dif, improve)) model.train() total_batch += 1 if total_batch - last_improve > config.require_improvement: # 验证集loss超过1000batch没下降,结束训练 print("No optimization for a long time, auto-stopping...") flag = True break if flag: break test(config, model, test_iter) def test(config, model, test_iter): # test model.load_state_dict(torch.load(config.save_path)) model.eval() start_time = time.time() test_acc, test_loss, test_report, test_confusion = evaluate(config, model, test_iter, test=True) msg = 'Test Loss: {0:>5.2}, Test Acc: {1:>6.2%}' print(msg.format(test_loss, test_acc)) print("Precision, Recall and F1-Score...") print(test_report) print("Confusion Matrix...") print(test_confusion) time_dif = get_time_dif(start_time) print("Time usage:", time_dif) def evaluate(config, model, data_iter, test=False): model.eval() loss_total = 0 predict_all = np.array([], dtype=int) labels_all = np.array([], dtype=int) with torch.no_grad(): for texts, labels in data_iter: outputs = model(texts) loss = F.cross_entropy(outputs, labels) loss_total += loss labels = labels.data.cpu().numpy() predic = torch.max(outputs.data, 1)[1].cpu().numpy() labels_all = np.append(labels_all, labels) predict_all = np.append(predict_all, predic) acc = metrics.accuracy_score(labels_all, predict_all) if test: report = metrics.classification_report(labels_all, predict_all, target_names=config.class_list, digits=4) confusion = metrics.confusion_matrix(labels_all, predict_all) return acc, loss_total / len(data_iter), report, confusion return acc, loss_total / len(data_iter)
5.5.4 utils.py
# coding: UTF-8 import torch from tqdm import tqdm import time from datetime import timedelta PAD, CLS = '[PAD]', '[CLS]' # padding符号, bert中综合信息符号 def build_dataset(config): def load_dataset(path, pad_size=32): contents = [] with open(path, 'r', encoding='UTF-8') as f: for line in tqdm(f): lin = line.strip() if not lin: continue content, label = lin.split('\t') token = config.tokenizer.tokenize(content) token = [CLS] + token seq_len = len(token) mask = [] token_ids = config.tokenizer.convert_tokens_to_ids(token) if pad_size: if len(token) < pad_size: mask = [1] * len(token_ids) + [0] * (pad_size - len(token)) token_ids += ([0] * (pad_size - len(token))) else: mask = [1] * pad_size token_ids = token_ids[:pad_size] seq_len = pad_size contents.append((token_ids, int(label), seq_len, mask)) return contents train = load_dataset(config.train_path, config.pad_size) dev = load_dataset(config.dev_path, config.pad_size) test = load_dataset(config.test_path, config.pad_size) return train, dev, test class DatasetIterater(object): def __init__(self, batches, batch_size, device): self.batch_size = batch_size self.batches = batches self.n_batches = len(batches) // batch_size self.residue = False # 记录batch数量是否为整数 if len(batches) % self.n_batches != 0: self.residue = True self.index = 0 self.device = device def _to_tensor(self, datas): x = torch.LongTensor([_[0] for _ in datas]).to(self.device) y = torch.LongTensor([_[1] for _ in datas]).to(self.device) # pad前的长度(超过pad_size的设为pad_size) seq_len = torch.LongTensor([_[2] for _ in datas]).to(self.device) mask = torch.LongTensor([_[3] for _ in datas]).to(self.device) return (x, seq_len, mask), y def __next__(self): if self.residue and self.index == self.n_batches: batches = self.batches[self.index * self.batch_size: len(self.batches)] self.index += 1 batches = self._to_tensor(batches) return batches elif self.index >= self.n_batches: self.index = 0 raise StopIteration else: batches = self.batches[self.index * self.batch_size: (self.index + 1) * self.batch_size] self.index += 1 batches = self._to_tensor(batches) return batches def __iter__(self): return self def __len__(self): if self.residue: return self.n_batches + 1 else: return self.n_batches def build_iterator(dataset, config): iter = DatasetIterater(dataset, config.batch_size, config.device) return iter def get_time_dif(start_time): """获取已使用时间""" end_time = time.time() time_dif = end_time - start_time return timedelta(seconds=int(round(time_dif)))
详细代码和数据集均放在我Github上,大家可以去下载:https://github.com/LePetitPrinceWh/Bert-Pytorch-TextClassification.git
5.5.5 结果分析
从运行结果可知,Bert文本分类的效果还是比较好的。
当我们把Bert.py文件的这一行代码将True改为False,就是模型训练变为非Fin Tune情况下
param.requires_grad = False #非Fine Tune的情况
提示:
给定预训练模型(Pre_trained model),基于模型进行微调(Fine Tune)。相对于从头开始训练(Training a model from scatch),微调为你省去大量计算资源和计算时间,提高了计算效率,甚至提高准确率。
我们发现在非 Fine Tune 的情况下,准确率要降低很多。后面我会在Bert结合CNN、RNN等其他模型,并对比实验结果。
参考文档
1. BERT模型的详细介绍_IT之一小佬的博客-CSDN博客_bert模型
2. 《How to Fine-Tune BERT for Text Classification》-阅读心得 - 今夜无风 - 博客园
3. BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding_Hi-Cloud的博客-CSDN博客
4. BERT解析及文本分类应用 - xlturing - 博客园
5. 基于BERT做中文文本分类(情感分析)_我开心呀的博客-CSDN博客_bert中文文本分类
7. Bert原理_五月的echo的博客-CSDN博客_bert分类原理
8. 《https://arxiv.org/pdf/1706.03762.pdfAttention is all you need》《https://arxiv.org/pdf/1706.03762.pdf
9. 《https://arxiv.org/pdf/1810.04805.pdfBERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》《https://arxiv.org/pdf/1810.04805.pdf
-
bert 文本分类实战
2021-12-05 10:33:50前言: ... 本次模型为监督学习模型,根据已有标签的文本数据集,对bert模型进行训练。使用训练好的模型对句子进行预测,输出得到句子的类别。本质上属于多分类问题。 大致流程为,数据集预处...前言:
由于课题需要,学习自然语言处理(NLP),于是在网上找了找文章和代码进行学习,在此记录,课题代码就不展示了,使用网上的代码和大家分享。思想和代码大部分参考苏神,在此感谢。
任务目标:
希望bert模型解决的问题: 输入:一段话; 输出:这段话属于的类别。
任务实现原理:
本次模型为监督学习模型,根据已有标签的文本数据集,对bert模型进行训练。使用训练好的模型对句子进行预测,输出得到句子的类别。本质上属于多分类问题。
大致流程为,数据集预处理;划分数据集();对数据集加工处理(文本数据编码成符合bert输入的向量);构建模型(bert模型导入与使用); 将数据送入模型进行训练和预测。
模型总体结构:
具体代码:
数据集预处理:
将数据集读入,并打乱数据的排列顺序。
mainPath = 'bert多文本分类//' rc = pd.read_csv(mainPath + 'data/tnews/toutiao_news_dataset.txt', delimiter="_!_", names=['labels', 'text'], header=None, encoding='utf-8') rc = shuffle(rc) #打乱顺序
划分数据集
将数据集划分为训练集和测试集(验证集)
# 构建全部所需数据集 data_list = [] for d in rc.iloc[:].itertuples(): #itertuples(): 将DataFrame迭代为元祖。 data_list.append((d.text, d.labels)) # 取一部分数据做训练和验证 train_data = data_list[0:20000] valid_data = data_list[20000:22000]
数据加工处理:
修改原有的字典:
修改原因:苏神解读,本来 Tokenizer 有自己的 _tokenize 方法,我这里重写了这个方法,是要保证 tokenize 之后的结果,跟原来的字符串长度等长(如果算上两个标记,那么就是等长再加 2)。 Tokenizer 自带的 _tokenize 会自动去掉空格,然后有些字符会粘在一块输出,导致 tokenize 之后的列表不等于原来字符串的长度了,这样如果做序列标注的任务会很麻烦。主要就是用 [unused1] 来表示空格类字符,而其余的不在列表的字符用 [UNK] 表示,其中 [unused*] 这些标记是未经训练的(随即初始化),是 Bert 预留出来用来增量添加词汇的标记,所以我们可以用它们来指代任何新字符。
#vocabPath里存储了大量的词语,每个词语对应的着一个编号 例如10640 posts # 将词表中的词编号转换为字典 # 字典的形式为 '仑': 796, #得到最原始的字典 tokenDict = {} with codecs.open(vocabPath, 'r', encoding='utf-8') as reader: for line in reader: token = line.strip() # 去除首尾空格 tokenDict[token] = len(tokenDict) #原始的字典存在着瑕疵,在原始的字典上需要根据自己的数据集,创造自己的字典 # 重写tokenizer class OurTokenizer(Tokenizer): def _tokenize(self, content): reList = [] for t in content: if t in self._token_dict: reList.append(t) elif self._is_space(t): # 用[unused1]来表示空格类字符 reList.append('[unused1]') else: # 不在列表的字符用[UNK]表示 reList.append('[UNK]') return reList #使用新的字典 tokenizer = OurTokenizer(tokenDict)
文本数据根据字典编码成符合bert输入的向量,逐批生成数据([X1,X2],Y),从而可以丢到模型中训练。
def seqPadding(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, shuffle=True): #构造函数,使用时执行 self.data = data self.batch_size = batch_size self.shuffle = shuffle 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))) #数据元组下标 if self.shuffle: 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) # encode方法可以一步到位地生成对应模型的输入。 y = d[1] X1.append(x1) ## x1 是字对应的索引 # x2 是句子对应的索引 X2.append(x2) Y.append([y]) if len(X1) == self.batch_size or i == idxs[-1]: X1 = seqPadding(X1) #如果等于batchsize或者最后一个值后面补充0 X2 = seqPadding(X2) Y = seqPadding(Y) yield [X1, X2], Y [X1, X2, Y] = [], [], []
构建模型和训练:
加载bert模型,并对bert模型的输出进行调整,使bert模型能够完成我们的任务目标。
# 设置预训练bert模型的路径 configPath = mainPath + 'chinese_roberta_wwm_ext_L-12_H-768_A-12/bert_config.json' ckpPath = mainPath + 'chinese_roberta_wwm_ext_L-12_H-768_A-12/bert_model.ckpt' vocabPath = mainPath + 'chinese_roberta_wwm_ext_L-12_H-768_A-12/vocab.txt' # bert模型设置 bert_model = load_trained_model_from_checkpoint(configPath, ckpPath, seq_len=None) # 加载预训练模型 for l in bert_model.layers: l.trainable = True x1_in = Input(shape=(None,)) x2_in = Input(shape=(None,)) x = bert_model([x1_in, x2_in]) # 取出[CLS]对应的向量用来做分类 x = Lambda(lambda x: x[:, 0])(x) p = Dense(15, activation='softmax')(x) model = Model([x1_in, x2_in], p) model.compile(loss='sparse_categorical_crossentropy', optimizer=Adam(1e-5), metrics=['accuracy']) model.summary() train_D = data_generator(train_data) valid_D = data_generator(valid_data) model.fit_generator(train_D.__iter__(), steps_per_epoch=len(train_D), epochs=5, validation_data=valid_D.__iter__(), validation_steps=len(valid_D))
模型预测:
#测试的数据集 str1 = "上港主场1-2负于国安,遭遇联赛两连败,上港到底输在哪?" str2 = "普京总统会见了拜登总统" str3 = "这3辆10万出头小钢炮,随便改改轻松秒奔驰,第一辆还是限量款" predict_D = data_generator([(str1, 0), (str2, 3), (str3, 10)], shuffle=False) #获取总的标签类别 #array(['体育', '军事', '农业', '国际', '娱乐', '房产', '教育', '文化', '旅游', '民生故事', '汽车','电竞游戏', '科技', '证券股票', '财经'], dtype=object) output_label2id_file = os.path.join(mainPath, "model/keras_class/label2id.pkl") if os.path.exists(output_label2id_file): with open(output_label2id_file, 'rb') as w: labes = pickle.load(w) #加载保存的模型 from keras_bert import get_custom_objects custom_objects = get_custom_objects() model = load_model(mainPath + 'model/keras_class/tnews.h5', custom_objects=custom_objects) #使用生成器获取测试的数据 tmpData = predict_D.__iter__() #预测 preds = model.predict_generator(tmpData, steps=len(predict_D), verbose=1) # 求每行最大值得下标,其中,axis=1表示按行计算 index_maxs = np.argmax(preds, axis=1) result = [(x, labes[x]) for x in index_maxs] print(result)
输出preds,index_maxs, result
完整代码
import pickle from keras_bert import load_trained_model_from_checkpoint, Tokenizer from keras.layers import * from keras.models import Model from keras.optimizers import Adam from sklearn.preprocessing import LabelEncoder from sklearn.utils import shuffle from keras.utils.vis_utils import plot_model import codecs, gc import keras.backend as K import os import pandas as pd import numpy as np # 文件主路径定义 mainPath = '你的目录/keras_bert文本分类实例/' # 从文件中读取数据,获取训练集和验证集 rc = pd.read_csv(mainPath + 'data/tnews/toutiao_news_dataset.txt', delimiter="_!_", names=['labels', 'text'], header=None, encoding='utf-8') #delimiter rc = shuffle(rc) # shuffle数据,打乱 # 把类别转换为数字 # 一共15个类别:"教育","科技","军事","旅游","国际","证券股票","农业","电竞游戏", # "民生故事","文化","娱乐","体育","财经","房产","汽车" class_le = LabelEncoder() rc.iloc[:, 0] = class_le.fit_transform(rc.iloc[:, 0].values) # 保存标签文件 output_label2id_file = os.path.join(mainPath, "model/keras_class/label2id.pkl") if not os.path.exists(output_label2id_file): with open(output_label2id_file, 'wb') as w: pickle.dump(class_le.classes_, w) # 构建全部所需数据集 data_list = [] for d in rc.iloc[:].itertuples(): data_list.append((d.text, d.labels)) # 取一部分数据做训练和验证 train_data = data_list[0:20000] valid_data = data_list[20000:22000] maxlen = 100 # 设置序列长度为100,要保证序列长度不超过512 # 设置预训练模型 configPath = mainPath + 'chinese_roberta_wwm_ext_L-12_H-768_A-12/bert_config.json' ckpPath = mainPath + 'chinese_roberta_wwm_ext_L-12_H-768_A-12/bert_model.ckpt' vocabPath = mainPath + 'chinese_roberta_wwm_ext_L-12_H-768_A-12/vocab.txt' # 将词表中的词编号转换为字典 tokenDict = {} with codecs.open(vocabPath, 'r', encoding='utf-8') as reader: for line in reader: token = line.strip() tokenDict[token] = len(tokenDict) # 重写tokenizer class OurTokenizer(Tokenizer): def _tokenize(self, content): reList = [] for t in content: if t in self._token_dict: reList.append(t) elif self._is_space(t): # 用[unused1]来表示空格类字符 reList.append('[unused1]') else: # 不在列表的字符用[UNK]表示 reList.append('[UNK]') return reList tokenizer = OurTokenizer(tokenDict) def seqPadding(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, shuffle=True): self.data = data self.batch_size = batch_size self.shuffle = shuffle 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))) if self.shuffle: 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 = seqPadding(X1) X2 = seqPadding(X2) Y = seqPadding(Y) yield [X1, X2], Y [X1, X2, Y] = [], [], [] # bert模型设置 bert_model = load_trained_model_from_checkpoint(configPath, ckpPath, seq_len=None) # 加载预训练模型 for l in bert_model.layers: l.trainable = True x1_in = Input(shape=(None,)) x2_in = Input(shape=(None,)) x = bert_model([x1_in, x2_in]) # 取出[CLS]对应的向量用来做分类 x = Lambda(lambda x: x[:, 0])(x) p = Dense(15, activation='softmax')(x) model = Model([x1_in, x2_in], p) model.compile(loss='sparse_categorical_crossentropy', optimizer=Adam(1e-5), metrics=['accuracy']) model.summary() train_D = data_generator(train_data) valid_D = data_generator(valid_data) model.fit_generator(train_D.__iter__(), steps_per_epoch=len(train_D), epochs=5, validation_data=valid_D.__iter__(), validation_steps=len(valid_D)) model.save(mainPath + 'model/keras_class/tnews.h5', True, True) # 保存模型结构图 plot_model(model, to_file='model/keras_class/tnews.png', show_shapes=True)
参考链接
https://blog.csdn.net/qq_39290990/article/details/121672141 -
pytorch-bert文本分类
2021-06-18 14:50:26基于bert预训练模型和pytorch深度学习框架实现文本分类 -
BERT文本分类,代码超基础、超详细解析
2022-04-10 19:26:28使用bert预训练模型实现文本分类,超基础,解析超详细 -
BERT详解:bert文本分类怎么做的
2021-11-30 14:05:06BERT详解:bert文本分类怎么做的 -
Transformer课程 第7课Gavin BERT文本分类-数据预处理
2021-12-06 12:09:31Transformer课程 第7课Gavin BERT文本分类-数据预处理 Comment Length Distribution 为了决定这个数据集的截断策略,让我们首先看看评论长度的分布。为此,我们的第一步是标记训练集中的所有评论。 标记所有评论,... -
自然语言处理动手学Bert文本分类视频教程
2021-12-24 16:42:12自然语言处理动手学Bert文本分类视频教程,本套课程基于Pytorch最新1.4版本来实现利用Bert实现中文文本分类任务,延续动手学系列课程风格,全程手敲代码,跟着杨博一行一行代码撸起来。 -
Bert_Classifier:bert文本分类,albert,keras_bert,bert4keras,kashgari,fastbert,flask + uwsgi + ...
2021-05-11 15:13:541、run_cnews_classifier.py 原生bert实现的文本分类 原文链接: 2、run_tnews_classifier.py 基于keras_bert实现的文本分类 原文链接: 3、run_lcqmc_similarity.py 基于bert4keras实现的文本相似度计算 原文链接:... -
『NLP学习笔记』BERT文本分类实战
2021-12-20 22:05:50Bert模型是Google在2018年10月发布的语言表示模型,Bert在NLP领域横扫了11项任务的最优结果,可以说是现今最近NLP中最重要的突破。Bert模型的全称是Bidirectional Encoder Representations from Transformers,是... -
Python-谷歌BERT文本分类教程
2019-08-10 06:43:46谷歌BERT文本分类教程 -
bert文本分类
2021-10-22 16:21:25# bert文本分类baseline模型 import os import numpy as np import pandas as pd import torch import torch.nn as nn import torch.utils.data as Data import torch.optim as optim import transformers from ... -
人工智能NLP自然语言之基础篇文本分类pytorch-transformers实现BERT文本分类bert
2021-12-07 11:08:29中文文本分类数据集 数据来源: 今日头条客户端 数据格式: 6554695793956094477_!_110_!_news_military_!_「欧洲第一陆军」法兰西帝国的欧陆霸权_!_查理八世,布列塔尼,卡佩王朝,佛兰德斯,法国 6554855520291783175_... -
bert 文本分类
2020-11-23 16:21:57github项目 pytorch https://gitee.com/jfdwd/Bert-Chinese-Text-Classification-Pytorch -
自然语言处理(NLP): 12 BERT文本分类
2020-04-08 23:03:57BERT介绍 BERT 论文阅读 来自论文《https://arxiv.org/pdf/1810.04805.pdf》 BERT说:“我要用 transformer 的 encoders” Ernie不屑道:“呵呵,你不能像Bi-Lstm一样考虑文章” BERT自信回答道:“我们会用masks” ... -
BERT文本分类使用指南
2018-12-21 16:19:27本文档介绍了如何使用BERT实现多类别文本分类任务,适合稍微了解BERT和文本分类的同学参考。 (一) 下载 首先,在github上clone谷歌的BERT项目,或者直接下载。项目地址 然后,下载中文预训练模型,地址 (二) ... -
BERT文本分类实践Keras
2019-05-28 22:38:42项目中,BERT文本二分类或者多分类,总感觉效果难言理想的样子。使用了keras-bert包。 概述: 问题一: tf版bert分类似乎太重,训练、预测很不方便,要定义类什么的,很难看。 问题二: 使用bert分类,如果文本太... -
广告行业中那些趣事系列16:NLPer一定要知道的BERT文本分类优化策略及原理
2020-08-02 08:25:10本篇一共7100个字摘要:本篇主要分享了项目实践中的BERT文本分类优化策略和原理。首先是背景介绍及优化方向,其中优化方向主要分成从数据层面优化和模型层面优化;然后通过实验的方式重点分析... -
全网最详细的bert Bert文本分类教程 数据+完整代码 可直接运行
2022-08-06 09:35:09全网最详细的bert Bert文本分类教程 数据+完整代码 可直接运行 -
基于huggingface/transforms(pytorch)框架实现Bert文本分类
2020-08-26 15:40:34基于huggingface/transforms-PyTorch框架实现Bert文本分类背景项目结构安装依赖包数据与预训练模型数据预训练模型代码部分 背景 作者在使用bert_keras实现bert文本分类模型后发现训练时并不能使用GPU加速训练,因此... -
Transformers预训练bert文本分类DEMO
2021-02-26 16:15:39简单应用Transformers的预训练模型做文本分类 import os import logging import numpy as np import transformers from datasets import Dataset from sklearn import metrics from sklearn.model_selection import ... -
NLP-文本分类:Bert文本分类(fine-tuning)【一分类(MSELoss)、多分类(CrossEntropyLoss)、多标签分类...
2022-02-24 12:28:38本文介绍如何利用BERT fine-tuning一个文本情感分类模型。 二、Bert源码(BertForSequenceClassification) 源码位置:\transformers\models\bert\modeling_bert.py @add_start_docstrings( """ Bert Model ... -
python实现Bert文本分类
2019-11-15 11:53:19"""The main BERT model and related functions.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function import collections import copy import ...