目录

一、项目流程

1.1 加载预训练模型Bert

1.2 数据预处理

1.3 模型定义

1.4 模型训练

1.5 模型预测

二、总结


BERT 是谷歌于 2018 年提出的基于 Transformer 编码器结构的预训练语言模型。本文基于 BERT 架构,完成从预处理、微调训练到效果评估的全流程实验,经过多轮迭代优化,模型在情感分析任务上精确率达到 98.7%,相比 LSTM 模型精确率提高108%

github项目地址:zhanghong203/bert_based_emotion_analysis at master

LSTM项目地址:zhanghong203/emotion_analyse_lm: 基于LSTM的情感分析模型


一、项目流程

实验配置:

        显卡: NVIDIA GeForce RTX 3050 LAPTOP GP 

        模型:google-bert/bert-base-chinese · Hugging Face

        数据:评论情感分析数据集

1.1 加载预训练模型Bert

Bert模型 通过官方链接或者代码方式下载:

from transformers import AutoTokenizer, AutoModelForMaskedLM

tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")

model = AutoModelForMaskedLM.from_pretrained("bert-base-chinese")

默认huggingface安装目录 ~/.cache/huggingface/hub,可以通过配置环境变量修改

完成Bert模型加载后,通过from_pretrained方法可以从huggingface缓存中读取,同时也可以把模型放置在项目目录下,通过读取绝对路径方式加载。

关于from_pretrained()方法,详细介绍参考链接

from_pretrained 做了啥_from pretrained-CSDN博客


1.2 数据预处理

该过程主要完成数据清洗、对接Bert输入的任务。数据清洗过滤无用列,去除不符合的行。

# 过滤数据
	dataset = dataset.remove_columns(['cat'])
	dataset = dataset.filter(lambda x: x['review'] is not None)
	dataset = dataset.cast_column('label', ClassLabel(names=['negative', 'positive']))

Bert一般接收参数:(input_ids, token_type_ids,attention_mask,Optional [labels])

	def batch_encode(batch):
		inputs = tokenizer(batch['review'], truncation=True, padding='max_length', max_length=config.MAX_LENGTH)
		inputs['labels'] = batch['label']
		return inputs

	dataset_dict = dataset_dict.map(batch_encode, batched=True, remove_columns=['review', 'label'])

调用tokenizer()获取前三个字段,为了保证批处理,需要做填充和截断操作。针对带句子分类任务头的Bert还需要labels字段。在后续过程中,为了方便解构,提前将label字段转化成labels,后续添加任务头,不需要再做预处理。

tokenizer方法参考链接


1.3 模型定义

首先,明确设计的任务是对评论进行情感分析,属于二分类任务。通过Bert前向传播,即通过12层隐藏层,只需要将最后一层last_hidden_state(batch_size, sequence_length, hidden_size)(Bert模型第一个token蕴含的向量信息是整个句子信息)接一个线性层。默认hidden_size是768,线性层输出维度设为1。

	def forward(self, input_ids, attention_mask, token_type_ids):
		output = self.bert(input_ids, attention_mask, token_type_ids)
		last_hidden_state = output.last_hidden_state
		cls_hidden_state = last_hidden_state[:, 0, :]
		output = self.linear(cls_hidden_state).squeeze(-1)
		return output

Bert输出结构参考链接


1.4 模型训练

对于不带任务头的Bert,传入参数不需要携带labels,并且在进行训练时,需要再多一步计算损失;然而针对句子分类任务的Bert,输出包含loss字段。

# 含任务头
model = AutoModelForSequenceClassification.from_pretrained('google-bert/bert-base-chinese').to(device)

训练代码如下:

def train_one_epoch(model, dataloader, loss_fn, optimizer, device):
	total_loss = 0
	model.train()
	for batch in tqdm(dataloader, desc='训练'):
		inputs = {k: v.to(device) for k, v in batch.items()}
		labels = inputs.pop('labels').to(dtype=torch.float)
		outputs = model(**inputs)
		loss = loss_fn(outputs, labels)

		loss.backward()
		optimizer.step()
		optimizer.zero_grad()

		total_loss += loss.item()

	return total_loss / len(dataloader)

def train():
	# 1.设备
	device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
	print(device)
	# 2.数据
	dataloader = get_loader()
	# 3.分词器
	tokenizer = AutoTokenizer.from_pretrained('google-bert/bert-base-chinese')
	# 4.模型
	model = ReviewAnalyzeModel().to(device)
	# 5.损失函数
	loss_fn = torch.nn.BCEWithLogitsLoss()
	# 6.优化器
	optimizer = torch.optim.Adam(model.parameters(), lr=config.LEARNING_RATE)
	# train
	best_loss = float('inf')
	for epoch in range(config.EPOCHS):
		print(f'Epoch: {epoch + 1}')
		loss = train_one_epoch(model, dataloader, loss_fn, optimizer, device)
		print(f'Loss: {loss:.4f}')
		# 保存模型
		if loss < best_loss:
			best_loss = loss
			torch.save(model.state_dict(), config.MODELS_DIR / 'best.pt')
			print("保存模型")

BertForSequenceClassification模型介绍


1.5 模型预测

1.3小节中的模型,是通过一个线性层得到一个结果,通过sigmoid映射到[0,1];含任务头的Bert模型,输出包含一个logit字段((batch_size, config.num_labels))在预测阶段,logit的形状是(1, 2),可以使用argmax完成标签映射。

# 含任务头
def predict_batch(model, inputs):
	model.eval()
	with torch.no_grad():
		output = model(**inputs)
		logits = output.logits
	# (batch_size, config.num_labels)
	result = torch.argmax(logits, dim=1)
	return result.tolist()

-------------------------------------------
# 不含任务头
def predict_batch(model, inputs):
	model.eval()
	with torch.no_grad():
		output = model(**inputs)
		# output.shape (batch_size)
	batch_result = torch.sigmoid(output)
	return batch_result.tolist()

二、总结

本文基于Bert完成从数据预处理到模型预测的全流程,在项目过程中免不了Bert文档的查看,笔者在每个小节都添加了相关API,便于读者理解。此外,下载Bert以及训练有一些小问题没有列举出来,比如访问huggingface超时、GPU显存利用率过高训练效率很低等问题。上述内容如果有错误的地方,希望大佬们可以指正。我一直在学习的路上,您的帮助使我收获更大!觉得对您有帮助的话,还请点赞支持!我也会不断更新文章!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐