[大语言模型-工程实践] 手把手教你-基于BERT模型提取商品标题关键词及优化改进

[大语言模型-工程实践] 手把手教你-基于BERT模型提取商品标题关键词及优化改进


目录


1. 背景介绍

1.1 关键词提取

关键词提取是信息检索和文本挖掘中的一项重要技术,它涉及从文本中识别和提取出最能代表文档内容的词语或短语。如下图所示,对于亚马逊上面的商品标题,在构建底层索引时,通常需要对标题做分词,提取里面核心词,用于构建倒排索引或者用于关键词匹配计算等。关键词提取技术可以通过多种方法实现,包括无监督学习和有监督学习的方法。

在这里插入图片描述

无监督关键词提取方法

无监督方法不依赖于预先标注的数据,而是通过算法自动发现文本中的关键词。这些方法包括:

  1. 基于统计特征的方法:如TF-IDF算法,通过词频(TF)和逆文档频率(IDF)来评估词的重要性。
  2. 基于词图模型的方法:如TextRank算法,构建词与词之间的关系图,并通过网络分析来确定关键词。
  3. 基于主题模型的方法:如LDA模型,通过主题分布来提取关键词。
  4. **基于语言模型的方法: 采用基于BERT, GPT这类大语言模型。
有监督关键词提取方法

有监督方法将关键词提取视为一个分类问题,需要预先标注的训练数据来训练模型。这些方法包括:

  1. 训练分类器:通过机器学习算法,如支持向量机(SVM)或随机森林,来区分关键词和非关键词。
关键词提取实现步骤

传统关键词提取的过程通常包括以下步骤:

  1. 文本预处理:包括去除停用词、标点符号,进行词干提取或词形还原。
  2. 特征提取:根据所选算法提取特征,如TF-IDF值。
  3. 关键词候选生成:根据特征值生成候选关键词列表。
  4. 评估和排序:评估每个候选词的重要性并进行排序。
  5. 选择关键词:选择排名最高的词作为最终的关键词。

2. 基于BERT模型的关键词提取

2.1 算法原理

​​​​在这里插入图片描述
BERT(Bidirectional Encoder Representations from Transformers)是由Google在这篇论文中首次提出的一种预训练深度双向Transformers模型,用于语言理解。

基于BERT模型进行关键词提取的方法主要利用了BERT模型强大的语义理解能力。BERT(Bidirectional Encoder Representations from Transformers)通过在大量文本上进行预训练,学习到了丰富的语言特征,这使得它在处理自然语言时能够考虑到整个文本的上下文信息。
在这里插入图片描述

使用BERT进行关键词提取的基本步骤通常包括:

  1. 文档表示:首先使用BERT模型获取整个文档的嵌入向量,这通常意味着要通过模型获取一个能够代表整个文档的向量表示。

  2. 候选词/短语生成:然后,从文档中提取候选词或短语,并使用相同的模型为这些候选词/短语生成嵌入向量。

  3. 相似度计算:通过计算文档向量与候选词/短语向量之间的相似度(常用的是余弦相似度),来评估候选词/短语与文档内容的匹配程度。

  4. 关键词提取:最后,根据计算出的相似度对候选词/短语进行排序,选择最相似的词/短语作为关键词。

BERT之所以适用于关键词提取,是因为它能够捕获到复杂的语义关系和长距离的依赖,这对于理解文档的主旨和提取关键信息至关重要。BERT的双向训练机制让它能够同时考虑词语的前后文信息,生成的嵌入向量能够很好地表示词语在特定上下文中的含义。

此外,还有一些基于BERT的关键词提取工具和框架,如KeyBERT,它是一个利用BERT嵌入来查找与文档最相似的关键词和关键短语的库。KeyBERT通过简单的余弦相似度度量来实现这一目标,并且易于使用,可以通过pip安装,只需几行代码即可实现关键词提取 。

2.1 工程实践

这类我们展示如何使用PyTorch开发一个基于Bert模型进行商品标题中关键词提取的程序。

首先,我们需要确保系统里面已经安装transformers库,这是由Hugging Face提供的一个非常流行的库,它包含了预训练的BERT模型和其他许多自然语言处理工具。

安装方法如下:

pip instal transformers torch

接着,我们使用Pytorch开发核心代码:

from transformers import BertTokenizer, BertModel
import torch

# 确保你的设备可以运行BERT模型
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 加载预训练的BERT模型和分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')
model.to(device)

def extract_keywords(title, num_keywords=5):
    # 对标题进行分词
    tokens = tokenizer.tokenize(title)
    # 将分词结果转换为BERT模型可以处理的格式
    tokens = ['[CLS]'] + tokens + ['[SEP]']
    token_ids = tokenizer.convert_tokens_to_ids(tokens)
    token_type_ids = [0] * len(token_ids)  # 单句子输入,所有token_type_ids为0
    attention_mask = [1] * len(token_ids)  # 标记每个token是实际的输入

    # 将数据发送到设备
    token_ids = torch.tensor([token_ids]).to(device)
    token_type_ids = torch.tensor([token_type_ids]).to(device)
    attention_mask = torch.tensor([attention_mask]).to(device)

    # 模型预测
    with torch.no_grad():
        outputs = model(input_ids=token_ids, token_type_ids=token_type_ids, attention_mask=attention_mask)

    # 获取每个token的词向量
    embeddings = outputs[0]  # (batch_size, sequence_length, hidden_states)
    embeddings = embeddings.squeeze(0)  # 移除batch_size维度

    # 忽略特殊标记CLS和SEP
    embeddings = embeddings[1:-1]
    tokens = tokens[1:-1]

    # 基于词向量的平均值选择关键词
    keyword_scores = torch.mean(embeddings, dim=1)  # (sequence_length - 2, hidden_states)
    sorted_scores, sorted_indices = torch.sort(keyword_scores, descending=True, dim=0)

    # 获取最高分的词
    keywords_indices = sorted_indices[:num_keywords].cpu().numpy()
    keywords = [tokens[index] for index in keywords_indices]

    return keywords

# 示例标题
title = "2024 New Arrival! Women's Fashion Summer Dress"
keywords = extract_keywords(title, num_keywords=5)
print("Extracted Keywords:", keywords)

上面这个代码,我们直接使用预训练的Bert模型对商品标题进行关键词提取。代码相对简单,主要逻辑:

    1. 加载模型和分词器:从Hugging Face的模型库中加载预训练的BERT模型和分词器。
    1. 定义提取关键词的函数:这个函数接受一个标题和要提取的关键词数量。
    1. 分词和格式化:将标题分词并添加BERT所需的特殊标记([CLS]和[SEP])。
    1. 模型预测:将处理后的输入数据传递给BERT模型,获取每个token的词向量。
    1. 选择关键词:基于词向量的平均值计算每个token的重要性得分,并选择得分最高的词作为关键词。

3 改进优化V1

在上面代码中,我们使用预训练的Bert模型进行关键词提取,对于关键词提取精度要求不是特别高的场景,基本上可以满足需求,然而如果我们需要提取的语料与通用语料库差异较大时,或者我们对于提取的关键词有更高精度的业务要求时,那么我们就需要对模型进行改进优化。

这里假设我们收集了大量的特定领域商品标题数据,那么可以采用对BERT模型进行微调的方式,来进一步提升模型提取的关键词准确性。

简要而言,可以遵循如下几个步骤:

1. 数据准备
  • 数据收集:确保你的商品标题数据是清洗过的,并且每个标题都标注了正确的关键词。
  • 数据标注:如果没有标注,你需要手动或通过众包方式标注关键词。这可能包括商品的主要特征、品牌、型号等。
2. 数据预处理
  • 分词:使用与BERT模型相匹配的分词器对标题进行分词。
  • 构建输入:为BERT模型构建输入,包括input IDs、token type IDs和attention mask。
  • 标签处理:将标注的关键词转换为模型可以理解的格式,例如,可以使用标签索引或one-hot编码。
3. 微调BERT模型
  • 加载预训练模型:加载BERT的预训练权重。
  • 添加自定义层:在BERT模型的基础上添加一个或多个自定义层,以适应关键词提取任务。
  • 损失函数:定义一个损失函数,如交叉熵损失,用于训练模型。
4. 训练模型
  • 设置优化器:选择一个优化器,如Adam,设置学习率和其他超参数。
  • 批处理:将数据分批输入模型进行训练。
  • 反向传播:在每个批次后计算损失,并通过反向传播更新模型权重。
5. 评估和调整
  • 验证集:使用一部分数据作为验证集,以监控模型在训练过程中的表现。
  • 超参数调整:根据验证集的表现调整学习率、批次大小等超参数。
  • 早停:如果验证集上的性能不再提升,可以提前停止训练以避免过拟合。
6. 微调示例代码

套用一句IT工程师们常用的一句话: Talk is cheap. Show me the code!

以下是一个简化的代码示例,展示如何使用PyTorch和transformers库对BERT模型进行微调:

from transformers import BertTokenizer, BertForSequenceClassification, AdamW
from torch.utils.data import Dataset, DataLoader

class KeywordExtractionDataset(Dataset):
    def __init__(self, titles, keywords, tokenizer, max_len=128):
        self.titles = titles
        self.keywords = keywords
        self.tokenizer = tokenizer
        self.max_len = max_len

    def __len__(self):
        return len(self.titles)

    def __getitem__(self, idx):
        title = self.titles[idx]
        keyword = self.keywords[idx]
        inputs = self.tokenizer.encode_plus(
            title,
            None,
            add_special_tokens=True,
            max_length=self.max_len,
            padding='max_length',
            return_token_type_ids=True,
            truncation=True
        )
        inputs['labels'] = 1 if keyword else 0  # Simplified example
        return inputs

# 假设 titles 和 keywords 是已经准备好的数据列表
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertForSequenceClassification.from_pretrained('bert-base-uncased', num_labels=2)

dataset = KeywordExtractionDataset(titles, keywords, tokenizer)
dataloader = DataLoader(dataset, batch_size=16, shuffle=True)

optimizer = AdamW(model.parameters(), lr=2e-5)

for epoch in range(num_epochs):
    model.train()
    for batch in dataloader:
        inputs = {k: v.to(device) for k, v in batch.items() if k != 'labels'}
        inputs['labels'] = batch['labels'].to(device)
        outputs = model(**inputs)
        loss = outputs.loss
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()
7. 模型部署
  • 保存和加载模型:训练完成后,保存模型权重,以便将来使用或进一步微调。
  • 集成到应用:将模型集成到实际的应用或服务中,进行关键词提取。

通过这些步骤,你可以有效地利用商品标题数据对BERT模型进行微调,以提高关键词提取的准确性和相关性。

4. 更进一步优化V2

当然,在上面步骤6中,我们使用收集到的数据对预训练的Bert模型进行微调,实际做得是SFT。在应用过程中,我们可能面临两个实际问题:

    1. 收集的训练数据量有限,无法收集到大量的相关领域数据; 尤其是对训练数据中关键词的标注,通常是非常耗费人力成本,无法获得大量的有标注数据;
    1. 训练的机器资源有限或者是有多种不同的分领域语料数据需要处理。比如需要对不同语种的商品标题数据进行关键词提取,如果是对同一个Bert模型进行统一微调,可能带来整体模型效果不佳,然而逐一进行分语种的微调,在模型训练和部署上消耗又比较大。

那么,在这种情况下,采用基于LoRA的微调方案,可能是比较好的一种选择,不仅训练数据相对可以少些,而且即使做分语言的关键词提取,整体模型参数增加有效,结合MOE等技术,还可以进一步优化公共部分模型参数。关于LoRA的相关介绍,感兴趣的朋友,可以查看笔者之前的博文《人工智能-大语言模型-微调技术-LoRA及背后原理简介》

LoRA(Low-Rank Adaptation)是一种微调预训练模型的技术,它通过在模型权重矩阵中引入低秩结构来进行参数更新,从而减少微调过程中的参数数量。这种方法可以有效地减少计算资源消耗和避免过拟合,同时保持模型性能。

要在PyTorch中结合LoRA进行模型微调,你可以按照以下步骤操作:

1. 定义LoRA模块

首先,你需要定义一个LoRA模块,这个模块将被插入到BERT模型的特定层中。以下是一个简单的LoRA模块实现:

import torch
import torch.nn as nn

class LoRALayer(nn.Module):
    def __init__(self, input_dim, output_dim, rank=4):
        super(LoRALayer, self).__init__()
        self.rank = rank
        self.A = nn.Parameter(torch.randn(output_dim, rank))
        self.B = nn.Parameter(torch.randn(rank, input_dim))

    def forward(self, x):
        return torch.matmul(self.A, torch.matmul(x, self.B).transpose(-1, -2))

2. 集成LoRA到BERT模型

接下来,你需要在BERT模型的适当位置插入LoRA模块。这通常在自注意力和前馈网络的输出部分进行:

from transformers import BertModel, BertConfig

class BertModelWithLoRA(BertModel):
    def __init__(self, config):
        super().__init__(config)
        self.config = config

        self.lora_layers = nn.ModuleDict()
        for i, layer in enumerate(self.encoder.layer):
            self.lora_layers[f"layer_{i}"] = LoRALayer(config.hidden_size, config.hidden_size, rank=4)

    def forward(self, input_ids, attention_mask=None, token_type_ids=None, position_ids=None, head_mask=None, inputs_embeds=None, encoder_hidden_states=None, encoder_attention_mask=None):
        outputs = ()

        for i, layer in enumerate(self.encoder.layer):
            if i in self.lora_layers:
                lora_output = self.lora_layers[f"layer_{i}"](layer.output)
                outputs = layer.output + lora_output  # Residual connection
            else:
                outputs = layer.output

        # Continue with the rest of the BERT model
        return outputs

3. 微调模型

使用上述修改后的模型进行微调。你需要准备数据集、定义损失函数和优化器:

from torch.utils.data import DataLoader, Dataset

class CustomDataset(Dataset):
    def __init__(self, data):
        self.data = data

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        return self.data[idx]

# 准备数据
dataset = CustomDataset(data)  # 假设 data 是准备好的数据集
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)

# 初始化模型和优化器
model = BertModelWithLoRA(BertConfig.from_pretrained('bert-base-uncased'))
optimizer = torch.optim.Adam(model.parameters(), lr=5e-5)

# 微调
model.train()
for epoch in range(num_epochs):
    for batch in dataloader:
        inputs = {k: v.to(device) for k, v in batch.items()}
        outputs = model(**inputs)
        loss = outputs.loss
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()

4. 保存和加载模型

训练完成后,保存模型的权重,并在需要时加载:

model.save_pretrained('path_to_save_model')
model = BertModelWithLoRA.from_pretrained('path_to_save_model')

通过这种方式,你可以有效地使用LoRA技术对BERT模型进行微调,减少参数数量,同时保持或提高模型的性能。这种方法特别适用于资源受限的环境或需要快速部署的场景。

当然,如果你对LoRA算法代码比较熟悉,也可以直接依赖原始的LoRA代码对上面代码进行重写:

  • LoRA
    https://github.com/microsoft/LoRA
git clone https://github.com/microsoft/LoRA.git

在这里插入图片描述

此外,由于LoRA算法现在已经被集成到PEFT库,可以使用Huggingface里面的PEFT库进行重写:

  • PEFT
    https://github.com/huggingface/peft
git clone https://github.com/huggingface/peft.git
pip install peft

在这里插入图片描述


如果您对我的博客内容感兴趣,欢迎三连击(点赞,关注和评论),我将持续为您带来计算机人工智能前沿技术(尤其是AI相关的大语言模型,深度学习,计算机视觉相关方向)最新学术论文及工程实践方面的内容分享,助力您更快更准更系统地了解前沿技术的发展现状。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sp_fyf_2024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值