分享内容
- 新闻评论分析任务训练流程加载自定义数据集处理超长文本的训练问题扩展词汇表并匹配模型修改模型配置信息评论分析任务概述
评论分析是情感分析的一个应用场景,旨在通过文本分类技术识别评论的情感倾向。本次将详细介绍如何更换数据集和模型来实现这一任务。
训练流程
1.1 数据集更换
在自然语言处理(NLP)任务中,数据集通常包含文本和对应的标签。文章评论分析的数据集可以采用CSV格式,其中一列存储评论文本,另一列存储情感标签(如正面或负面)。
加载CSV格式的数据
我们可以使用datasets
库来加载本地的CSV文件。以下是一个示例代码:
CustomData.pyfrom torch.utils.data import Dataset # 从torch库中导入Dataset类,用于创建自定义数据集类from datasets import load_dataset # 从datasets库中导入loaddataset函数,用于加载数据集class <span class="hljs-title class">CustomDataset(Dataset): # 定义一个名为MyDataset的类,继承自Dataset类 def init(self,split): # 定义类的初始化方法,接收一个参数split,表示数据集的分割类型 #使用load_dataset函数加载csv文件,路径为"./data/{split}.csv",并指定数据集分割为"train" self.dataset = load_dataset(path="csv",datafiles=f"./data/{split}.csv",split="train") def <span class="hljs-title function">len(self): # 定义一个方法用于返回数据集的长度 return len(self.dataset) # 返回加载的数据集的长度,即数据集中的样本数量 def getitem(self, item): # 定义一个方法用于获取数据集中的某个样本 text = self.dataset[item]["text"] # 获取数据集中第item个样本的"text"字段 label = self.dataset[item]["label"] # 获取数据集中第item个样本的"label"字段 return text,label # 返回获取的文本和标签if name == 'main': # 判断是否在主程序中运行 dataset = CustomDataset("test") # 创建MyDataset类的实例,传入"test"作为split参数 for data in dataset: # 遍历数据集中的每个样本 print(data) # 打印每个样本的内容
代码解析
load_dataset
:用于从磁盘加载数据集,支持多种格式(如CSV、JSON等)。len
:返回数据集的大小,便于在训练中使用。getitem
:根据索引返回单个数据条目(文本和标签),符合PyTorch的Dataset
类标准。注意事项
- 数据集路径应根据实际情况调整,如
data/Article/{split}.csv
。确保CSV文件中的列名与代码中的字段匹配(如"text"
和"label"
)。1.2 预训练模型更换
文章评论分析任务可以通过微调预训练的BERT模型来完成。可以选择不同的中文预训练模型,如bert-base-chinese
或hfl/chinese-roberta-wwm-ext
。
代码示例
net.pyfrom transformers import BertModel, BertConfig # 导入Bert模型和Bert配置类import torch # 导入PyTorch库DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 设置设备为CUDA(如果可用),否则为CPUpretrained = BertModel.from_pretrained(r"bert-base-chinese").to(DEVICE)pretrained.embeddings.position_embeddings = torch.nn.Embedding(1500,768).to(DEVICE)print(pretrained)config = pretrained.configconfiguration = BertConfig.from_pretrained(r"bert-base-chinese") # 从预训练模型加载配置configuration.max_position_embeddings = 1500 # 设置最大位置嵌入为1500print(configuration) # 打印配置信息初始化模型pretrained = BertModel(configuration).to(DEVICE) # 使用配置初始化Bert模型并移动到指定设备print(pretrained.embeddings.position_embeddings) # 打印模型的位置嵌入print(pretrained) # 打印整个模型定义下游任务模型(将主干网络所提取的特征分类)class Model(torch.nn.Module): # 定义一个PyTorch模型 def init(self): super().init() # 调用父类构造函数 self.fc = torch.nn.Linear(768, 8) # 定义一个全连接层,输入768维,输出8维 # def forward(self,input_ids,attention_mask,token_type_ids): # # with torch.no_grad(): # # out = pretrained(input_ids=input_ids,attention_mask=attention_mask,token_type_ids=token_type_ids) # out = pretrained(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids) # out = self.fc(out.last_hiddenstate[:,0]) # out = out.softmax(dim=1) # return out # 调整模型的前向计算,让embeddings部分参与到模型的训练过程(修改了embeddings) def <span class="hljs-title function">forward(self, input_ids, attention_mask, token_type_ids): # 定义模型的前向传播 # 让embeddings参与训练 embeddings_output = pretrained.embeddings(input_ids=input_ids) # 获取嵌入层的输出 attention_mask = attention_mask.to(torch.float) # 将注意力掩码转换为浮点类型 # 将数据形状转换为[N,1,1,sequence_length]使其匹配attention层的输入形状 attention_mask = attention_mask.unsqueeze(1).unsqueeze(2) # 增加两个维度 attention_mask = attention_mask.to(embeddings_output.dtype) # 将注意力掩码的数据类型与嵌入层输出对齐 # 冻结encoder和pooler,使用torch.no_grade()节省显存 with torch.no_grad(): # 在不计算梯度的情况下执行 encoder_output = pretrained.encoder(embeddings_output, attention_mask=attention_mask) # 获取编码器的输出 out = self.fc(encoder_output.last_hidden_state[:, 0]) # 使用全连接层处理编码器的最后一个隐藏状态 return out # 返回输出
代码解析
BertModel.from_pretrained()
:加载Hugging Face提供的预训练BERT模型。with torch.no_grad()
:冻结BERT模型的权重,只训练下游任务的全连接层。self.fc
:定义一个全连接层,用于将BERT的输出映射到分类结果。last_hidden_state[:, 0]
:提取BERT的[CLS]标记对应的向量,通常用于分类任务的最终输出。注意事项
- 如果任务是二分类(如情感分析),请将
self.fc = torch.nn.Linear(768, 2)
进行调整。1.3 训练与测试
训练是微调模型的关键步骤。我们使用DataLoader
来批量加载数据,并使用AdamW
作为优化器来进行模型参数的优化。
训练代码
trainer.pyimport torch # 导入PyTorch库from CustomData import CustomDataset # 导入自定义数据集类from torch.utils.data import DataLoader # 导入PyTorch的数据加载器from net import Model # 导入自定义的模型类from transformers import AdamW, BertTokenizer # 导入AdamW优化器和Bert分词器DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 设置设备为CUDA(如果可用),否则为CPUEPOCH = 1 # 设置训练的总轮数token = BertTokenizer.from_pretrained(r"bert-base-chinese") # 加载预训练的Bert分词器def collate_fn(data): sentes = [i[0] for i in data] # 提取数据集中的句子 label = [i[1] for i in data] # 提取数据集中的标签 # print(sentes) # 编码句子 data = token.batch_encode_plus(batch_text_or_text_pairs=sentes, # 使用BertTokenizer对句子进行批量编码,类似于将句子转换为模型可以理解的数字形式 truncation=True, # 启用截断功能,确保每个句子不会超过指定的最大长度 padding="max_length", # 使用最大长度进行填充,确保所有句子长度一致 max_length=1500, # 设置最大长度为1500,超过的部分会被截断,未达到的部分会被填充 return_tensors="pt", # 返回PyTorch张量格式的数据,方便后续在模型中使用 return_length=True) # 返回每个句子的实际长度,便于后续处理 input_ids = data["input_ids"] # 获取输入ID,这些ID是词汇表中每个词的唯一标识符 attention_mask = data["attention_mask"] # 获取注意力掩码,用于指示哪些词是填充的,哪些是实际内容 token_type_ids = data["token_type_ids"] # 获取token类型ID,用于区分句子对中的不同句子 labels = torch.LongTensor(label) # 将标签转换为LongTensor格式,便于在PyTorch中进行计算 # print(input_ids,attention_mask,token_type_ids) return input_ids, attention_mask, token_type_ids, labels # 返回处理后的数据,包括输入ID、注意力掩码、token类型ID和标签创建数据集train_dataset = CustomDataset("train") # 创建训练数据集,类似于准备好一组用于训练的样本val_dataset = CustomDataset("validation") # 创建验证数据集,用于在训练过程中评估模型性能创建数据加载器train_laoder = DataLoader(dataset=train_dataset, # 使用DataLoader为训练数据集创建数据加载器,便于批量处理数据 batch_size=1, # 设置每个批次的大小为1,即每次只处理一个样本 shuffle=True, # 启用随机打乱功能,确保每次训练时数据顺序不同,增加模型的泛化能力 drop_last=True, # 如果最后一个批次的数据量不足,则丢弃该批次,确保每个批次的数据量一致 collate_fn=collate_fn) # 使用自定义的collate_fn函数来处理每个批次的数据if name == 'main': # 开始训练 print(DEVICE) # 打印使用的设备,告诉用户当前使用的是CPU还是GPU model = Model().to(DEVICE) # 初始化模型并将其移动到指定设备上,确保计算在合适的硬件上进行 optimizer = AdamW(model.parameters(), lr=5e-4) # 使用AdamW优化器,设置学习率为5e-4,优化器用于更新模型参数 loss_func = torch.nn.CrossEntropyLoss() # 使用交叉熵损失函数,常用于分类问题中计算预测值与真实值之间的差异 model.train() # 设置模型为训练模式,启用dropout等训练时特有的功能 for epoch in range(EPOCH): # 训练EPOCH轮,EPOCH表示完整遍历一次训练数据集 sum_val_acc = 0 # 初始化验证准确率的累加器 sum_val_loss = 0 # 初始化验证损失的累加器 # 训练 for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(train_laoder): # 遍历训练数据集 # print(input_ids) input_ids, attention_mask, token_type_ids, labels = input_ids.to(DEVICE), attention_mask.to( DEVICE), token_type_ids.to(DEVICE), labels.to(DEVICE) # 将数据移动到指定设备上 out = model(input_ids, attention_mask, token_type_ids) # 前向传播,计算模型的输出 loss = loss_func(out, labels) # 计算损失,衡量模型输出与真实标签之间的差异 optimizer.zero_grad() # 清空优化器的梯度,防止梯度累积 loss.backward() # 反向传播,计算梯度 optimizer.step() # 更新模型参数,根据计算出的梯度调整参数值 if i % 5 == 0: # 每5个批次打印一次信息,便于观察训练过程 out = out.argmax(dim=1) # 获取预测结果,选择概率最大的类别 acc = (out == labels).sum().item() / len(labels) # 计算准确率,预测正确的样本数除以总样本数 print(epoch, i, loss.item(), acc) # 打印当前轮数、批次、损失和准确率 torch.save(model.state_dict(), f"model/{epoch}-bert.pth") # 保存模型参数到文件,便于后续加载和使用 print(epoch, "参数保存成功!") # 打印成功信息,告知用户模型参数已保存
代码解析
DataLoader
:用于批量加载数据,每次加载batch_size=100
的样本并随机打乱数据。
BertTokenizer
:用于将文本转换为模型可处理的token。
AdamW
:适合BERT等大型语言模型的优化器,结合了Adam和权重衰减,防止过拟合。
变量说明
在自然语言处理(NLP)任务中,input_ids
、attention_mask
和 token_type_ids
是使用预训练的BERT模型进行文本处理时常用的输入格式。让我们逐一解释这些变量的作用,并通过一个简单的例子来说明。
input_ids
:
- 作用:
input_ids
是文本经过分词器(如BertTokenizer)处理后得到的词汇表索引。每个单词或子词被映射到一个唯一的整数ID。例子: 假设我们有一个句子 "我爱编程"。分词器可能会将其分成 ["我", "爱", "编", "程"],并将每个词映射到相应的ID,比如 [101, 1001, 2001, 3001]。attention_mask
:
- 作用:
attention_mask
用于指示模型在处理输入时应该关注哪些词。值为1的地方表示模型应该关注,值为0的地方表示模型应该忽略(通常是填充的部分)。例子: 如果句子 "我爱编程" 被填充到最大长度5,可能会变成 ["我", "爱", "编", "程", "[PAD]"],对应的 attention_mask
为 [1, 1, 1, 1, 0]。token_type_ids
:
- 作用:
token_type_ids
用于区分不同的句子对(如句子A和句子B)在输入中。对于单个句子输入,通常全为0。例子: 在句子对任务中,比如句子A是 "我爱编程",句子B是 "编程很有趣",token_type_ids
可能是 [0, 0, 0, 0, 1, 1, 1, 1],表示前四个词属于句子A,后四个词属于句子B。labels
:
- 作用:
labels
是目标标签,用于监督学习中的损失计算。通常是一个整数,表示类别。例子: 如果我们在做情感分析,句子 "我爱编程" 的标签可能是1(表示正面情感)。这些变量共同作用,使得模型能够有效地理解和处理输入文本。通过这种结构化的输入,BERT模型可以更好地捕捉文本中的语义信息。
注意事项
max_length=500
用于限制文本长度,防止输入过长导致内存占用过大。2. 资讯评论数据集介绍
资讯评论是NLP中的经典任务,通常需要对评论文本进行分类,将其归入不同的类别。
2.1 加载Hugging Face的资讯评论数据集
我们可以使用Hugging Face的datasets
库来加载资讯评论数据集,如THUCNews
。这是一个中文资讯评论数据集,适合用于文本分类任务。
2.2 加载自定义CSV数据集
如果有自定义的资讯评论数据集,可以将其保存为CSV文件,并通过datasets
库加载。
代码示例
数据集地址:huggingface.co/datasets/se…
data.pyfrom datasets import load_dataset加载Hugging Face上的THUCNews数据集data = load_dataset(path="seamew/THUCNewsText", split="train")print(data)遍历数据集查看样本for i in data: print(i)data = load_dataset(path="csv", data_files="data/train.csv", split="train")
注意事项
- 确保CSV文件的格式正确,列名要与代码中的字段匹配,如
"text"
和"label"
。22
3.处理超长文本的训练问题
BERT模型的最大输入长度为512个token,但在实际应用中,文本长度可能会超过该限制,导致信息丢失或无法输入模型。
3.1 文本截断
对于长度超过最大输入长度的文本,可以通过截断的方式保留前512个token。
3.2 增加max_position_embeddings
如果文本长度远远超过512个token,可以通过调整BERT模型的配置来增加max_position_embeddings
,使模型支持更长的输入。
代码示例
from transformers import BertConfig, BertModel# 修改max_position_embeddings参数configuration = BertConfig.from_pretrained("yechen/bert-large-chinese")configuration.max_position_embeddings = 1500 # 增加最大输入长度model = BertModel(configuration)
注意事项
- 增加
max_position_embeddings
需要更多的计算资源,输入的长度越大,计算量和内存需求越高。4.扩展词汇表并匹配模型
在实际应用中,数据集中可能会出现未包含在预训练模型词汇表中的新词汇。这时我们需要扩展模型的vocab
。
4.1 添加新词汇到词汇表
通过BertTokenizer
的add_tokens
方法添加新词汇:
token = BertTokenizer.from_pretrained("yechen/bert-large-chinese")token.add_tokens(["新词汇1", "新词汇2"])
4.2 调整模型的嵌入层
添加新词汇后,需要调整模型的嵌入层,以便模型能够处理新的词汇。
代码示例
model.resize_token_embeddings(len(token))
注意事项
add_tokens
:用于向现有的词汇表中添加新词汇。resize_token_embeddings
:调整模型的嵌入层大小,以匹配扩展后的词汇表。5.修改模型配置信息
模型配置(如max_position_embeddings
和hidden_size
)可以根据任务需求进行调整,特别是在处理不同长度的文本或需要调整模型容量时。
5.1 修改模型配置
通过BertConfig
可以定制化模型配置。
代码示例
from transformers import BertConfig, BertModel# 自定义BERT配置configuration = BertConfig.from_pretrained("yechen/bert-large-chinese")configuration.max_position_embeddings = 1500 # 修改最大输入长度configuration.hidden_size = 1024 # 修改隐藏层大小# 使用自定义配置初始化模型model = BertModel(configuration)
注意事项
- 修改配置时要确保与实际任务的需求匹配,同时要考虑计算资源的限制。