# 自定义数据集类 class MolecularDataset(Dataset): def __init__(self, csv_path: str, tokenizer: AutoTokenizer, max_seq_len: int = 512): self.df = pd.read_csv(csv_path) self.tokenizer = tokenizer self.max_seq_len = max_seq_len # 预处理质谱数据 spectra_data = preprocess_spectra(self.df) self.spec_encoded = encode_spectra(spectra_data, P, dimn) def __len__(self): return len(self.df) def __getitem__(self, idx) -> dict: # 分子式向量 formula = self.df.iloc[idx]['Molecular Formula'] formula_vec = formula_to_dense(formula) # 质谱矩阵 spec_matrix = self.spec_encoded[idx] # SELFIES编码 selfies_str = self.df.iloc[idx]['SELFIES'] encoding = self.tokenizer( selfies_str, add_special_tokens=True, padding='max_length', truncation=True, return_attention_mask=True, max_length=self.max_seq_len, return_tensors='pt' ) return { 'formula_vec': formula_vec, 'spec_matrix': spec_matrix, 'input_ids': encoding['input_ids'].squeeze(0), 'attention_mask': encoding['attention_mask'].squeeze(0) }我要进行的任务是大模型的监督微调,输入是formula_vec和spec_matrix,输入到添加的encoder作为初始状态生成selfies字符串,这个时候需要input_ids吗,那label又不用设立吗

时间: 2025-07-24 07:19:54 AIGC 浏览: 28
<think>我们正在构建一个用于分子式生成的自定义数据集类。根据你的描述,任务是大模型的监督微调,输入是formula_vec和spec_matrix,它们将被输入到添加的encoder中,然后生成SELFIES字符串。 在监督微调中,我们通常需要输入和对应的标签(目标输出)。在序列生成任务中,输入是条件信息(这里是formula_vec和spec_matrix),输出是SELFIES字符串。因此,在训练时,我们需要将SELFIES字符串作为目标(即标签)。 在数据集中,我们通过tokenizer对SELFIES字符串进行编码,得到input_ids和attention_mask。在训练时,我们通常将input_ids作为输入,并希望模型能够预测下一个token。因此,标签(label)应该是input_ids向右移动一位(即下一个token),并且通常我们会将最后一个token的标签忽略(或者用padding token的ignore_index来屏蔽)。 但是,在标准的自回归语言模型训练中,我们通常将同一个序列同时作为输入和输出,只是输出向右移动一位。因此,在数据集中,我们返回的'input_ids'可以作为输入,而标签则是将同一个'input_ids'序列向右移动一位,并设置最后一个token的标签为忽略(或者使用一个特殊的损失忽略索引)。 然而,在你的描述中,你提到输入是formula_vec和spec_matrix,它们通过一个encoder处理,然后作为初始状态来生成SELFIES字符串。这意味着模型的结构可能是encoder-decoder(类似于T5)或者只有decoder但带有条件encoder(类似于一些条件生成模型)。在训练时,decoder部分需要输入SELFIES序列(作为输入)并预测下一个token(作为标签)。 因此,在数据集中,我们仍然需要提供SELFIES的tokenized序列(即input_ids)作为decoder的输入,同时提供formula_vec和spec_matrix作为encoder的输入。标签则是SELFIES序列的偏移版本。 但是,请注意:在标准的transformers库中,对于encoder-decoder模型(如T5),我们只需要提供`decoder_input_ids`和`labels`。实际上,`labels`就是偏移后的`decoder_input_ids`(即第一个token是<SOS>,而标签的第一个token是SELFIES序列的第一个token)。不过,在训练时,transformers库会自动将`labels`的右侧移动一位来构建decoder输入(即`decoder_input_ids`),并计算损失。具体来说,模型在训练时,会使用`labels`作为decoder的输入(通过向右移动,并在前面加上一个起始token)?实际上,这取决于模型的具体实现。 为了简化,通常我们直接提供`input_ids`(对于encoder)和`labels`(对于decoder)。在自定义数据集时,我们可以这样处理: 1. 对于encoder的输入:formula_vec和spec_matrix(可能通过一个自定义的encoder网络)。 2. 对于decoder的输入:我们需要提供SELFIES的tokenized序列,但是作为标签,我们使用同一个序列,但是要设置起始和结束标记,并注意在计算损失时忽略padding部分。 然而,在你的代码中,目前只返回了'input_ids'和'attention_mask',这通常用于decoder的输入(在只有decoder的模型中)或者用于encoder(在encoder-decoder模型中,但这里显然不是)。但是,根据你的任务描述,你有一个encoder(处理formula_vec和spec_matrix)和一个decoder(生成SELFIES)。因此,我们需要在数据集中提供用于decoder的输入和标签。 在transformers库中,对于encoder-decoder模型,我们通常提供: - encoder_inputs: 这里就是formula_vec和spec_matrix(可能需要合并或分别处理) - decoder_input_ids: SELFIES序列(包含起始标记,但不包含结束标记?或者包含完整序列?) - labels: 与decoder_input_ids相同长度的序列,但是向右移动一位(即下一个token),并且通常将第一个token设置为起始标记,最后一个token之后是结束标记,但注意在labels中,结束标记之后的位置应该被忽略(用-100表示)。 但实际上,在训练时,transformers的模型(如T5)会自己处理:我们只需要提供`labels`,而`decoder_input_ids`可以由模型内部通过将`labels`向右移动并添加起始标记来生成。 因此,常见的做法是:我们只提供`labels`,而不提供`decoder_input_ids`,模型内部会自动构造。但是,这取决于你使用的模型。如果你使用的是标准的Seq2Seq模型,那么你只需要在数据集中返回`labels`(即目标序列的token ids,并且用-100来屏蔽不需要计算损失的位置)。 但是,你的模型结构可能是自定义的,因此我们需要明确: 假设你使用的是transformers库的EncoderDecoderModel或者AutoModelForSeq2SeqLM,那么你在训练时只需要提供: - input_ids: 对于encoder,这里可能不适用,因为你的encoder输入是formula_vec和spec_matrix,而不是文本。所以你可能需要自定义encoder,因此这个input_ids可能不会被使用。 - 你的自定义encoder输入:formula_vec和spec_matrix。 - labels: 目标序列(SELFIES的token ids),并且将padding部分设置为-100(以便损失函数忽略)。 因此,在数据集中,我们应该返回: - formula_vec: 分子式的向量表示 - spec_matrix: 质谱矩阵 - labels: 目标序列(即SELFIES的token ids),但是注意,在计算损失时,我们需要忽略padding部分。所以,我们将padding部分的标签设置为-100。 然而,在当前的tokenizer调用中,我们返回的是'input_ids'和'attention_mask',其中'input_ids'就是SELFIES的tokenized序列(包括特殊标记,如[CLS]或<EOS>等,具体取决于tokenizer)。但是,在生成任务中,这个'input_ids'实际上就是我们的目标序列(即labels)。所以,我们可以将'input_ids'作为labels返回,但是需要将padding部分的标签设置为-100。 修改建议: 在__getitem__方法中,我们不再返回'input_ids'和'attention_mask'作为输入(因为decoder的输入将由模型内部构建),而是返回: - formula_vec 和 spec_matrix 作为encoder的输入 - labels: 将当前编码得到的input_ids复制,但是将padding部分的标签设置为-100(注意:tokenizer在padding时默认使用0作为pad token,但具体取决于tokenizer) 具体步骤: 1. 使用tokenizer对SELFIES字符串进行编码,得到input_ids和attention_mask。 2. 将input_ids作为labels,但是将attention_mask为0的位置(即padding位置)的标签设置为-100(因为损失函数会忽略这些位置)。 因此,我们可以这样修改: 在返回的字典中,将'input_ids'去掉,改为返回'labels',这个'labels'是根据input_ids修改得到的。 但是,注意:在decoder中,通常需要知道序列的起始(如<bos>)和结束(<eos>),这些在tokenizer中已经添加。所以,我们直接使用tokenizer编码的结果,然后将其作为labels,并修改padding部分。 修改后的__getitem__可能如下: ```python def __getitem__(self, idx) -> dict: # 分子式向量 formula = self.df.iloc[idx]['Molecular Formula'] formula_vec = formula_to_dense(formula) # 质谱矩阵 spec_matrix = self.spec_encoded[idx] # SELFIES编码 selfies_str = self.df.iloc[idx]['SELFIES'] encoding = self.tokenizer( selfies_str, add_special_tokens=True, padding='max_length', truncation=True, return_attention_mask=True, max_length=self.max_seq_len, return_tensors='pt' ) input_ids = encoding['input_ids'].squeeze(0) attention_mask = encoding['attention_mask'].squeeze(0) # 创建labels,将padding部分的token id设置为-100(忽略索引) labels = input_ids.clone() # 将attention_mask为0的位置(即padding位置)的标签设置为-100 labels[labels == self.tokenizer.pad_token_id] = -100 # 注意:这里我们通常将pad token的标签设为-100,但前提是pad_token_id已经被正确设置 # 但是,上面的做法并不完全正确,因为padding部分在input_ids中已经是pad_token_id,但我们需要根据attention_mask来设置?或者直接根据pad_token_id设置? # 更常见的做法是:我们直接根据pad_token_id来设置,但注意,有些tokenizer可能没有设置pad_token_id,或者使用其他方式。 # 另一种做法:使用attention_mask,将attention_mask为0的位置设为-100 # 这里我们使用attention_mask来设置 labels = input_ids.clone() labels[attention_mask == 0] = -100 # 注意:有些模型要求起始标记(如<bos>)在labels中应该保留,但不需要预测,所以通常将第一个token(即<bos>)的标签设为-100?实际上,不需要。 # 在标准的语言模型中,我们预测的是下一个token,所以对于序列中的每个位置i,我们使用位置i之前的token来预测位置i的token。 # 因此,整个序列的标签就是原始序列向右移动一位(即最后一个token被忽略,而第一个token的标签是序列的第二个token)。但是,在transformers库中,如果你提供labels,模型内部会处理位移。 # 所以,我们这里直接返回整个序列作为labels(包括起始和结束标记),并将padding部分设为-100。 return { 'formula_vec': formula_vec, 'spec_matrix': spec_matrix, 'labels': labels, # 注意:这里我们返回的是labels,而不是input_ids # 注意:我们可能不需要返回attention_mask,因为模型在计算decoder的损失时已经不需要attention_mask(因为labels中已经用-100屏蔽了padding部分) # 但是,如果你的decoder在训练时需要attention_mask(例如,用于防止关注到padding部分),那么你可能还需要返回decoder的attention_mask。 # 然而,在标准的transformers的Seq2Seq模型中,训练时使用的是labels,而decoder的attention_mask可以由labels不等于-100的位置来生成(或者由传入的attention_mask生成)。所以,为了安全,我们可以返回decoder_attention_mask。 'decoder_attention_mask': attention_mask } ``` 但是,这里有一个问题:你的模型结构是怎样的?如果是一个自定义的encoder-decoder模型,那么你可能需要分别提供encoder的输入(formula_vec和spec_matrix)和decoder的输入(这里我们提供了labels,但实际上模型在训练时会自己构建decoder_input_ids,即去掉最后一个token,并在前面加上起始标记?)。但是,在transformers库中,我们通常只提供`labels`,模型内部会处理。 因此,为了与transformers库的Seq2Seq训练保持一致,我们只返回: - 自定义的encoder输入:formula_vec和spec_matrix - labels:处理后的目标序列(padding部分用-100填充) 然后,在模型的前向传播中,我们需要: - 将formula_vec和spec_matrix输入到自定义的encoder中,得到encoder的输出(隐藏状态)。 - 将encoder的输出传递给decoder,并使用labels计算损失。 在transformers库中,如果你使用EncoderDecoderModel,那么你可以这样调用: outputs = model(input_ids=None, inputs_embeds=encoder_inputs, labels=labels, ...) 但是,由于你的encoder输入不是文本,所以你可能需要自定义一个encoder,然后使用transformers的decoder(如GPT2LMHeadModel)来构建一个自定义的模型。 因此,在数据集中,我们返回的字典中应该包含: - 'formula_vec' 和 'spec_matrix':作为encoder的输入 - 'labels':用于计算损失的目标序列(已经处理了padding部分) 另外,注意:在训练时,decoder需要知道输入序列的长度(即非padding部分),但是我们在labels中已经用-100屏蔽了padding部分,所以模型在计算损失时会自动忽略。同时,decoder的注意力掩码(decoder_attention_mask)可以告诉模型哪些位置是padding,以避免在自注意力中关注到这些位置。因此,我们可能还需要返回'decoder_attention_mask',它是我们之前得到的attention_mask(值为1和0)。 所以,最终返回的字典包含四个键:'formula_vec', 'spec_matrix', 'labels', 'decoder_attention_mask' 但是,请注意:在标准的transformers的Seq2Seq模型中,我们通常不返回'decoder_attention_mask',因为模型可以根据labels自动生成(即labels不等于-100的位置)。但是,为了清晰和避免混淆,我们可以返回。 因此,修改后的__getitem__如下: ```python def __getitem__(self, idx) -> dict: formula = self.df.iloc[idx]['Molecular Formula'] formula_vec = formula_to_dense(formula) spec_matrix = self.spec_encoded[idx] selfies_str = self.df.iloc[idx]['SELFIES'] encoding = self.tokenizer( selfies_str, add_special_tokens=True, padding='max_length', truncation=True, return_attention_mask=True, max_length=self.max_seq_len, return_tensors='pt' ) input_ids = encoding['input_ids'].squeeze(0) attention_mask = encoding['attention_mask'].squeeze(0) # 创建labels:将padding部分的token id设置为-100 labels = input_ids.clone() # 使用attention_mask来设置padding部分为-100 labels[attention_mask == 0] = -100 return { 'formula_vec': formula_vec, 'spec_matrix': spec_matrix, 'labels': labels, 'decoder_attention_mask': attention_mask } ``` 注意:这里我们假设formula_vec和spec_matrix已经是处理好的张量,可以直接输入到模型中的encoder部分。 另外,在训练时,我们可能会将这些数据组合成一个批次。因此,你需要确保在数据加载器中使用自定义的collate函数,或者确保这些返回的值可以直接被批次化(即它们都是张量,并且形状一致)。 总结:在监督微调中,我们需要标签(labels)来训练模型。在序列生成任务中,标签就是目标序列(这里就是SELFIES字符串的token序列),但是需要将padding部分设置为忽略索引(-100)。 现在,你的数据集类将返回formula_vec、spec_matrix作为条件输入,labels和decoder_attention_mask用于decoder的训练。 注意:在模型的前向传播中,你需要将formula_vec和spec_matrix输入到自定义的encoder中,然后将encoder的输出作为decoder的hidden_states输入(在transformers库中,这通常通过`encoder_outputs`参数传递)。具体的模型实现需要根据你选择的框架来定。 最后,关于你最初的问题:需要input_ids吗?在decoder的输入中,我们不需要显式地提供decoder_input_ids,因为模型在训练时会根据labels自动构建(将labels向右移动,并在前面加上一个起始标记)。所以,在数据集中,我们不需要返回decoder_input_ids,只需要返回labels。 但是,如果你的模型不是使用transformers库的标准模型,而是自己实现的,那么你可能需要自己构建decoder_input_ids(即去掉labels的最后一个token,并在前面加上起始标记)。不过,transformers库的模型会自动处理,所以如果你使用标准模型,就不需要。 因此,我建议使用上述修改后的数据集类。 注意:在测试时,我们不需要labels,所以你可能需要创建一个不同的测试数据集类,或者通过设置一个标志来控制是否返回labels。 由于你只问了数据集类,所以以上是针对你的MolecularDataset类的修改。 另外,请注意preprocess_spectra和encode_spectra函数,以及formula_to_dense函数,这些函数需要你自行实现,因为它们没有在代码中给出。 现在,我们按照要求,给出修改后的完整代码(假设你已经实现了这些函数):</think># 分子式生成任务的数据集类设计 在你的分子式生成任务中,输入是分子式向量(`formula_vec`)和质谱矩阵(`spec_matrix`),目标是生成SELFIES字符串。在监督微调任务中,你需要提供输入特征和对应的标签。以下是修改后的数据集类实现: ```python import pandas as pd import torch from torch.utils.data import Dataset from transformers import AutoTokenizer class MolecularDataset(Dataset): def __init__(self, csv_path: str, tokenizer: AutoTokenizer, max_seq_len: int = 512): """ 初始化分子数据集 参数: csv_path: CSV文件路径 tokenizer: 用于SELFIES编码的分词器 max_seq_len: 最大序列长度 """ self.df = pd.read_csv(csv_path) self.tokenizer = tokenizer self.max_seq_len = max_seq_len self.pad_token_id = tokenizer.pad_token_id # 预处理质谱数据 (假设这些函数已实现) spectra_data = preprocess_spectra(self.df) self.spec_encoded = encode_spectra(spectra_data, P, dimn) def __len__(self): return len(self.df) def __getitem__(self, idx) -> dict: # 分子式向量 formula = self.df.iloc[idx]['Molecular Formula'] formula_vec = formula_to_dense(formula) # 假设已实现 # 质谱矩阵 spec_matrix = self.spec_encoded[idx] # SELFIES编码 selfies_str = self.df.iloc[idx]['SELFIES'] encoding = self.tokenizer( selfies_str, add_special_tokens=True, padding='max_length', truncation=True, max_length=self.max_seq_len, return_tensors='pt' ) input_ids = encoding['input_ids'].squeeze(0) attention_mask = encoding['attention_mask'].squeeze(0) # 创建标签 - 将输入向右移动一位 labels = input_ids.clone() # 将padding部分的标签设为-100(损失函数会忽略) labels[labels == self.pad_token_id] = -100 return { 'formula_vec': formula_vec, # 分子式特征向量 'spec_matrix': spec_matrix, # 质谱特征矩阵 'input_ids': input_ids, # 用于decoder输入的token IDs 'attention_mask': attention_mask, # 注意力掩码 'labels': labels # 训练标签(右移后的token IDs) } ``` ## 关键设计解释 ### 1. 是否需要input_ids? **需要**。在decoder-only架构中: - `input_ids`作为decoder的输入序列 - 模型会根据输入序列和encoder状态生成下一个token - 在训练时,模型接收当前token预测下一个token ### 2. 标签(label)如何设置? - **标签是输入序列向右移动一位**(shifted right) - 将padding部分的标签设为`-100`(PyTorch的CrossEntropyLoss会忽略这些位置) - 这样模型学习的是给定前t个token,预测第t+1个token ### 3. 模型训练流程 ```mermaid graph LR A[formula_vec] --> B[Encoder] C[spec_matrix] --> B B --> D[Encoder输出状态] D --> E[Decoder] F[input_ids] --> E E --> G[预测labels] ``` ### 4. 数据处理流程 1. 从CSV加载分子数据 2. 提取分子式并转换为特征向量 3. 预处理质谱数据并编码为矩阵 4. 将SELFIES字符串分词为token IDs 5. 创建右移的标签序列 6. 返回包含所有特征的字典 ## 模型训练示例代码 ```python from transformers import AutoModelForCausalLM, Trainer, TrainingArguments # 初始化模型 (使用GPT-2架构示例) model = AutoModelForCausalLM.from_pretrained("gpt2") # 修改输入层以接受额外的encoder输入 model.resize_token_embeddings(len(tokenizer)) # 调整token嵌入大小 # 自定义前向传播 def custom_forward(formula_vec, spec_matrix, input_ids, attention_mask, labels): # 1. 将分子特征输入encoder (需自定义encoder网络) encoder_output = my_custom_encoder(formula_vec, spec_matrix) # 2. 将encoder输出与decoder输入结合 # (这里需要根据模型架构设计融合方式) combined_input = combine_features(encoder_output, input_ids) # 3. 通过decoder生成预测 outputs = model.decoder( inputs_embeds=combined_input, attention_mask=attention_mask, labels=labels ) return outputs # 训练参数 training_args = TrainingArguments( output_dir='./results', num_train_epochs=3, per_device_train_batch_size=4, logging_dir='./logs', ) # 创建Trainer trainer = Trainer( model=model, args=training_args, train_dataset=dataset, compute_metrics=None ) # 开始训练 trainer.train() ``` ## 关键注意事项 1. **Encoder设计**:需要自定义网络处理`formula_vec`和`spec_matrix` 2. **特征融合**:需要设计如何将encoder输出与decoder输入结合 3. **损失计算**:使用交叉熵损失,忽略标签为-100的位置 4. **推理模式**:生成时使用encoder输出作为初始状态,从<bos>开始自回归生成
阅读全文

相关推荐

import pandas as pd import numpy as np import torch from transformers import BertTokenizer, BertForSequenceClassification, Trainer, TrainingArguments from sklearn.model_selection import train_test_split from torch.utils.data import Dataset, DataLoader # 自定义数据集类 class CommentDataset(Dataset): def __init__(self, texts, labels, weights): self.texts = texts self.labels = labels self.weights = weights self.tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') self.max_len = 128 def __len__(self): return len(self.texts) def __getitem__(self, idx): text = str(self.texts[idx]) encoding = self.tokenizer.encode_plus( text, add_special_tokens=True, max_length=self.max_len, padding='max_length', truncation=True, return_attention_mask=True, return_tensors='pt', ) return { 'input_ids': encoding['input_ids'].flatten(), 'attention_mask': encoding['attention_mask'].flatten(), 'labels': torch.tensor(self.labels[idx], dtype=torch.long), 'weights': torch.tensor(self.weights[idx], dtype=torch.float) } # 数据预处理函数 def preprocess_data(file_path): # 读取数据 df = pd.read_csv(file_path) # 清洗数据:去除无评分的记录 df = df.dropna(subset=['RATING']) # 创建标签映射 df['label'] = df['RATING'].apply(lambda x: 2 if x >=4 else 1 if x ==3 else 0) # 创建样本权重(votes + 1) df['weight'] = df['VOTES'] + 1 # 划分训练集和测试集 train_df, test_df = train_test_split( df, test_size=0.2, stratify=df['label'], random_state=42 ) return train_df, test_df # 自定义训练器(支持样本权重) class WeightedTrainer(Trainer): def compute_loss(self, model, inputs, return_outputs=False): labels = inputs.get("labels") weights = inputs.get("weights") outputs = model(**inputs) logits = outputs.get('logits') loss_fct = torch.nn.CrossEntropyLoss(reduction='none') loss = loss_fct(logits.view(-1, self.model.config.num_labels), labels.view(-1)) weighted_loss = torch.mean(loss * weights) return (weighted_loss, outputs) if return_outputs else weighted_loss # 主程序 def main(): # 数据预处理 train_df, test_df = preprocess_data('comments.csv') # 创建数据集 train_dataset = CommentDataset( texts=train_df['CONTENT'].values, labels=train_df['label'].values, weights=train_df['weight'].values ) test_dataset = CommentDataset( texts=test_df['CONTENT'].values, labels=test_df['label'].values, weights=test_df['weight'].values ) # 初始化模型 model = BertForSequenceClassification.from_pretrained( 'bert-base-uncased', num_labels=3, problem_type="single_label_classification" ) # 训练参数配置 training_args = TrainingArguments( output_dir='./results', num_train_epochs=3, per_device_train_batch_size=16, per_device_eval_batch_size=16, warmup_steps=500, weight_decay=0.01, logging_dir='./logs', fp16=True, # 启用混合精度训练 evaluation_strategy='epoch', save_strategy='epoch', load_best_model_at_end=True ) # 初始化训练器 trainer = WeightedTrainer( model=model, args=training_args, train_dataset=train_dataset, eval_dataset=test_dataset, compute_metrics=lambda p: { 'accuracy': (np.argmax(p.predictions, axis=1) == p.label_ids).mean() } ) # 训练模型 trainer.train() # 保存最终模型 model.save_pretrained('./sentiment_model') trainer.tokenizer.save_pretrained('./sentiment_model') if __name__ == '__main__': main() 请给出基于此代码改良后的完整代码

import torch import pandas as pd import numpy as np from transformers import BertTokenizer, BertModel from torch.optim import Adam from tqdm import tqdm from torch import nn from torch.utils.data import Dataset, DataLoader from sklearn.model_selection import train_test_split import matplotlib.pyplot as plt # 设置随机种子保证可复现性 SEED = 42 torch.manual_seed(SEED) np.random.seed(SEED) torch.backends.cudnn.deterministic = True # 检查GPU是否可用 DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") print(f"使用设备: {DEVICE}") # 1. 数据准备 def load_and_prepare_data(train_path, test_path): # 加载数据集 train_df = pd.read_csv(train_path) test_df = pd.read_csv(test_path) # 确保数据有需要的列 assert {'url', 'website_text', 'category'}.issubset(train_df.columns) # 创建类别标签映射 categories = train_df['category'].unique() label_map = {cat: idx for idx, cat in enumerate(sorted(categories))} num_classes = len(label_map) print(f"数据集详情:") print(f"- 训练样本数: {len(train_df)}") print(f"- 测试样本数: {len(test_df)}") print(f"- 类别数量: {num_classes}") print(f"- 类别映射: {label_map}") # 分割验证集 train_df, val_df = train_test_split( train_df, test_size=0.1, stratify=train_df['category'], # 按类别分层抽样 random_state=SEED ) return train_df, val_df, test_df, label_map, num_classes # 2. 数据处理类 class WebsiteDataset(Dataset): def __init__(self, df, tokenizer, label_map, max_length=512): """ 参数: df: 包含网站数据的DataFrame tokenizer: BERT分词器 label_map: 类别到数字的映射字典 max_length: 文本最大长度 """ self.texts = df['website_text'].tolist() self.labels = [label_map[label] for label in df['category']] self.tokenizer = tokenizer self.max_length = max_length def __len__(self): return len(self.labels) def __getitem__(self, idx): text = str(self.texts[idx]) label = self.labels[idx] # 分词和编码 encoding = self.tokenizer.encode_plus( text, add_special_tokens=True, max_length=self.max_length, padding='max_length', truncation=True, return_attention_mask=True, return_tensors='pt' ) return { 'input_ids': encoding['input_ids'].flatten(), 'attention_mask': encoding['attention_mask'].flatten(), 'label': torch.tensor(label, dtype=torch.long) } # 3. 模型架构 class WebsiteClassifier(nn.Module): def __init__(self, bert_model, num_classes, dropout=0.3): super().__init__() self.bert = bert_model self.dropout = nn.Dropout(dropout) self.classifier = nn.Linear(self.bert.config.hidden_size, num_classes) def forward(self, input_ids, attention_mask): outputs = self.bert( input_ids=input_ids, attention_mask=attention_mask ) pooled_output = outputs.pooler_output pooled_output = self.dropout(pooled_output) return self.classifier(pooled_output) # 4. 训练辅助函数 def create_dataloaders(train_df, val_df, test_df, tokenizer, label_map, batch_size=16): train_dataset = WebsiteDataset(train_df, tokenizer, label_map) val_dataset = WebsiteDataset(val_df, tokenizer, label_map) test_dataset = WebsiteDataset(test_df, tokenizer, label_map) train_loader = DataLoader( train_dataset, batch_size=batch_size, shuffle=True ) val_loader = DataLoader( val_dataset, batch_size=batch_size ) test_loader = DataLoader( test_dataset, batch_size=batch_size ) return train_loader, val_loader, test_loader def train_epoch(model, dataloader, criterion, optimizer, device): model.train() total_loss, total_acc = 0, 0 for batch in tqdm(dataloader, desc="训练批次"): input_ids = batch['input_ids'].to(device) attention_mask = batch['attention_mask'].to(device) labels = batch['label'].to(device) optimizer.zero_grad() outputs = model(input_ids, attention_mask) loss = criterion(outputs, labels) acc = (outputs.argmax(dim=1) == labels).sum().item() loss.backward() optimizer.step() total_loss += loss.item() total_acc += acc return total_loss / len(dataloader.dataset), total_acc / len(dataloader.dataset) def evaluate(model, dataloader, criterion, device): model.eval() total_loss, total_acc = 0, 0 with torch.no_grad(): for batch in tqdm(dataloader, desc="评估批次"): input_ids = batch['input_ids'].to(device) attention_mask = batch['attention_mask'].to(device) labels = batch['label'].to(device) outputs = model(input_ids, attention_mask) loss = criterion(outputs, labels) acc = (outputs.argmax(dim=1) == labels).sum().item() total_loss += loss.item() total_acc += acc return total_loss / len(dataloader.dataset), total_acc / len(dataloader.dataset) def plot_training_history(train_losses, val_losses, train_accs, val_accs): plt.figure(figsize=(15, 5)) # 损失曲线 plt.subplot(1, 2, 1) plt.plot(train_losses, label='训练损失') plt.plot(val_losses, label='验证损失') plt.title('训练和验证损失') plt.xlabel('Epoch') plt.ylabel('Loss') plt.legend() # 准确率曲线 plt.subplot(1, 2, 2) plt.plot(train_accs, label='训练准确率') plt.plot(val_accs, label='验证准确率') plt.title('训练和验证准确率') plt.xlabel('Epoch') plt.ylabel('Accuracy') plt.legend() plt.tight_layout() plt.savefig('training_history.png') plt.show() def save_model(model, tokenizer, label_map, path="website_classifier.pth"): torch.save({ 'model_state_dict': model.state_dict(), 'label_map': label_map, 'tokenizer_config': tokenizer.init_kwargs }, path) print(f"模型已保存至: {path}") # 5. 主训练函数 def train_website_classifier(): # 加载和准备数据 train_path = "train_dataset.csv" test_path = "test_dataset.csv" train_df, val_df, test_df, label_map, num_classes = load_and_prepare_data(train_path, test_path) # 初始化分词器和模型 model_dir = "./model/bert-base-multilingual-cased" tokenizer = BertTokenizer.from_pretrained(model_dir) bert_model = BertModel.from_pretrained(model_dir) model = WebsiteClassifier(bert_model, num_classes=num_classes) model.to(DEVICE) # 创建数据加载器 batch_size = 16 train_loader, val_loader, test_loader = create_dataloaders( train_df, val_df, test_df, tokenizer, label_map, batch_size ) # 设置优化器和损失函数 optimizer = Adam(model.parameters(), lr=3e-5) # BERT推荐的微调学习率 criterion = nn.CrossEntropyLoss() # 训练参数 epochs = 5 best_acc = 0 history = { 'train_loss': [], 'val_loss': [], 'train_acc': [], 'val_acc': [] } print(f"\n开始训练...\n{'='*40}") for epoch in range(epochs): print(f"\nEpoch {epoch+1}/{epochs}") print('-'*40) # 训练 train_loss, train_acc = train_epoch( model, train_loader, criterion, optimizer, DEVICE ) history['train_loss'].append(train_loss) history['train_acc'].append(train_acc) # 验证 val_loss, val_acc = evaluate( model, val_loader, criterion, DEVICE ) history['val_loss'].append(val_loss) history['val_acc'].append(val_acc) print(f"\n训练结果: 损失={train_loss:.4f}, 准确率={train_acc:.4f}") print(f"验证结果: 损失={val_loss:.4f}, 准确率={val_acc:.4f}") # 保存最佳模型 if val_acc > best_acc: best_acc = val_acc save_model(model, tokenizer, label_map, f"best_model_epoch{epoch+1}.pth") # 可视化训练过程 plot_training_history( history['train_loss'], history['val_loss'], history['train_acc'], history['val_acc'] ) # 在测试集上评估最终模型 print("\n测试最终模型...") test_loss, test_acc = evaluate(model, test_loader, criterion, DEVICE) print(f"测试集性能: 损失={test_loss:.4f}, 准确率={test_acc:.4f}") # 保存最终模型 save_model(model, tokenizer, label_map) return model, tokenizer, label_map # 6. 运行训练 if __name__ == "__main__": trained_model, tokenizer, label_map = train_website_classifier() print("训练完成!") 这是原代码 import torch import pandas as pd import numpy as np from sklearn.metrics import classification_report, confusion_matrix import seaborn as sns import matplotlib.pyplot as plt from tqdm import tqdm from torch import nn from torch.utils.data import Dataset, DataLoader from transformers import BertTokenizer, BertModel # 关键修复:确保WebsiteDataset返回'website_text' class WebsiteDataset(Dataset): def __init__(self, df, tokenizer, label_map, max_length=512): self.texts = df['website_text'].tolist() self.labels = [label_map[label] for label in df['category']] self.tokenizer = tokenizer self.max_length = max_length def __len__(self): return len(self.labels) def __getitem__(self, idx): text = str(self.texts[idx]) label = self.labels[idx] encoding = self.tokenizer.encode_plus( text, add_special_tokens=True, max_length=self.max_length, padding='max_length', truncation=True, return_attention_mask=True, return_tensors='pt' ) # 确保包含website_text字段 return { 'input_ids': encoding['input_ids'].flatten(), 'attention_mask': encoding['attention_mask'].flatten(), 'label': torch.tensor(label, dtype=torch.long), 'website_text': text # 修复:添加website_text字段 } class WebsiteClassifier(nn.Module): def __init__(self, bert_model, num_classes, dropout=0.3): super().__init__() self.bert = bert_model self.dropout = nn.Dropout(dropout) self.classifier = nn.Linear(self.bert.config.hidden_size, num_classes) def forward(self, input_ids, attention_mask): outputs = self.bert( input_ids=input_ids, attention_mask=attention_mask ) pooled_output = outputs.pooler_output pooled_output = self.dropout(pooled_output) return self.classifier(pooled_output) # 设备配置 DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") def load_model(model_path, bert_model_dir="./model/bert-base-multilingual-cased"): """加载保存的模型""" checkpoint = torch.load(model_path, map_location=DEVICE) tokenizer = BertTokenizer.from_pretrained(bert_model_dir) bert_model = BertModel.from_pretrained(bert_model_dir) num_classes = len(checkpoint['label_map']) model = WebsiteClassifier(bert_model, num_classes) model.load_state_dict(checkpoint['model_state_dict']) model.to(DEVICE) model.eval() return model, tokenizer, checkpoint['label_map'] def evaluate_model(model, test_loader): """在测试集上评估模型性能""" true_labels = [] pred_labels = [] all_probs = [] texts = [] with torch.no_grad(): for batch in tqdm(test_loader, desc="测试批次"): input_ids = batch['input_ids'].to(DEVICE) attention_mask = batch['attention_mask'].to(DEVICE) labels = batch['label'].cpu().numpy() outputs = model(input_ids, attention_mask) probs = torch.nn.functional.softmax(outputs, dim=1).cpu().numpy() preds = outputs.argmax(dim=1).cpu().numpy() true_labels.extend(labels) pred_labels.extend(preds) all_probs.extend(probs) texts.extend(batch['website_text']) # 直接从batch获取 return true_labels, pred_labels, all_probs, texts def generate_classification_report(true_labels, pred_labels, label_map): """生成分类报告和混淆矩阵""" inv_label_map = {v: k for k, v in label_map.items()} class_names = [inv_label_map[i] for i in range(len(label_map))] report = classification_report( true_labels, pred_labels, target_names=class_names, output_dict=True, zero_division=0 ) report_df = pd.DataFrame(report).transpose() cm = confusion_matrix(true_labels, pred_labels) cm_df = pd.DataFrame(cm, index=class_names, columns=class_names) return report_df, cm_df def plot_confusion_matrix(cm_df, title='混淆矩阵'): """绘制混淆矩阵热力图""" plt.figure(figsize=(12, 10)) sns.heatmap(cm_df, annot=True, fmt='d', cmap='Blues', cbar=False) plt.title(title) plt.ylabel('真实标签') plt.xlabel('预测标签') plt.xticks(rotation=45) plt.yticks(rotation=0) plt.tight_layout() plt.savefig('confusion_matrix.png') plt.close() # 避免阻塞运行 def save_detailed_results(df, filename="detailed_results.csv"): """保存详细预测结果""" df.to_csv(filename, index=False, encoding='utf-8-sig') print(f"详细预测结果已保存至: {filename}") def main(): # 配置参数 MODEL_PATH = "website_classifier.pth" TEST_CSV = "test_dataset.csv" BATCH_SIZE = 16 print(f"使用设备: {DEVICE}") print("加载训练好的模型...") model, tokenizer, label_map = load_model(MODEL_PATH) print(f"模型加载完成,类别数: {len(label_map)}") print("\n准备测试数据...") test_df = pd.read_csv(TEST_CSV) test_dataset = WebsiteDataset(test_df, tokenizer, label_map) test_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False) print("\n运行模型预测...") true_labels, pred_labels, all_probs, texts = evaluate_model(model, test_loader) print("\n生成分类报告...") report_df, cm_df = generate_classification_report(true_labels, pred_labels, label_map) print("\n================ 分类报告 ================") print(report_df) print("\n================ 混淆矩阵 ================") print(cm_df) plot_confusion_matrix(cm_df) print("\n保存详细预测结果...") inv_label_map = {v: k for k, v in label_map.items()} results_df = pd.DataFrame({ 'text': texts, 'true_label': [inv_label_map[l] for l in true_labels], 'predicted_label': [inv_label_map[l] for l in pred_labels], 'confidence': [max(probs) for probs in all_probs] }) # 添加每个类别的概率 for i in range(len(label_map)): class_name = inv_label_map[i] results_df[f'prob_{class_name}'] = [prob[i] for prob in all_probs] save_detailed_results(results_df) # 打印关键指标 accuracy = report_df.loc['accuracy', 'f1-score'] macro_f1 = report_df.loc['macro avg', 'f1-score'] weighted_f1 = report_df.loc['weighted avg', 'f1-score'] print("\n================ 关键指标 ================") print(f"测试集样本数: {len(test_df)}") print(f"整体准确率: {accuracy:.4f}") print(f"宏平均F1分数: {macro_f1:.4f}") print(f"加权平均F1分数: {weighted_f1:.4f}") if __name__ == "__main__": main() 这是验证测试集的代码,为什么第二个代码再使用模型预测时速度慢于训练时的代码

import json import torch from typing import Dict, List from torch.utils.data import Dataset import transformers from peft import LoraConfig, TaskType, get_peft_model from torch.utils.data import DataLoader, SequentialSampler from transformers import Trainer, TrainingArguments from lora_plus import LoraPlusTrainer from torch.utils.data import RandomSampler from swanlab.integration.transformers import SwanLabCallback import swanlab import numpy as np import pandas as pd import re from typing import Dict, List import torch from tqdm import tqdm from transformers import PreTrainedTokenizer from transformers import AutoTokenizer import torch.nn as nn swanlab.init("Finetune-Llama3.2-with-Encoder") swanlab_callback = SwanLabCallback( project="Finetune-Llama3.2-with-Encoder", experiment_name="Finetune-Llama3.2-with-Encoder" ) # 常量定义 CHEM_FORMULA_SIZE = "([A-Z][a-z]*)([0-9]*)" VALID_ELEMENTS = ["C", "N", "P", "O", "S", "Si", "I", "H", "Cl", "F", "Br", "B", "Se", "Fe", "Co", "As", "K", "Na"] ELEMENT_VECTORS = np.eye(len(VALID_ELEMENTS)) element_to_position = dict(zip(VALID_ELEMENTS, ELEMENT_VECTORS)) # 化学式转密集向量 def formula_to_dense(chem_formula: str) -> np.ndarray: total_onehot = [] for (chem_symbol, num) in re.findall(CHEM_FORMULA_SIZE, chem_formula): num = 1 if num == "" else int(num) one_hot = element_to_position[chem_symbol].reshape(1, -1) one_hot_repeats = np.repeat(one_hot, repeats=num, axis=0) total_onehot.append(one_hot_repeats) if len(total_onehot) == 0: dense_vec = np.zeros(len(VALID_ELEMENTS)) else: dense_vec = np.vstack(total_onehot).sum(0) return dense_vec # 正弦嵌入 def sine_embed(v, max_count=256): num_freqs = int(np.ceil(np.log2(max_count))) freqs = 0.5 ** torch.arange(num_freqs, dtype=torch.float32) * np.pi v_tensor = torch.tensor(v, dtype=torch.float32)[:, None] embedded = torch.sin(v_tensor * freqs[None, :]) return torch.abs(embedded).numpy() def positional_encoding(max_position, d_model, min_freq=1e-6): position = np.arange(max_position) freqs = min_freq **(2 * (np.arange(d_model) // 2) / d_model) pos_enc = position.reshape(-1, 1) * freqs.reshape(1, -1) pos_enc[:, ::2] = np.cos(pos_enc[:, ::2]) pos_enc[:, 1::2] = np.sin(pos_enc[:, 1::2]) return pos_enc # 生成位置编码 P = positional_encoding(2000000, 256, min_freq=1e2) # 转换为PyTorch张量以便后续使用 P = torch.tensor(P, dtype=torch.float32) dimn = 255 # 质谱数据编码(修复后) def encoding(rag_tensor, P, dimn): to_pad = [] for sample in rag_tensor: # 直接使用列表(因为sample[0]和sample[1]是Python列表) all_dim = [sample[0]] # 移除.tolist(),因为本身就是列表 # 处理位置编码(sample[1]是列表,直接遍历) pos_enc = [P[int(i)-1] for i in sample[1]] for dim_idx in range(dimn): dim_vals = [i[dim_idx].item() for i in pos_enc] all_dim.append(dim_vals) to_pad.append(all_dim) # 使用PyTorch进行序列填充 padded = [] for i in to_pad: # 转换为张量 tensor = torch.tensor(i, dtype=torch.float32) # 计算需要填充的长度 pad_length = max(0, 501 - tensor.size(1)) # 进行后向填充 padded_tensor = torch.nn.functional.pad(tensor, (0, pad_length), mode='constant', value=0) # 如果长度超过501,则截断 if padded_tensor.size(1) > 501: padded_tensor = padded_tensor[:, :501] padded.append(padded_tensor) # 堆叠并交换轴 to_pad = torch.stack(padded) to_pad = to_pad.permute(0, 2, 1) # 相当于numpy的swapaxes(to_pad, 1, -1) return to_pad # 质谱数据预处理(PyTorch实现) def prepro_specs_train(df): df = df.reset_index(drop=True) valid = [] mz_intensity = df['Spectrum'].to_list() def process_line(line): pairs = line.split() mz_list = [] intensity_list = [] for pair in pairs: mz, intensity = pair.split(':') mz_list.append(float(mz)) intensity_list.append(float(intensity)) return mz_list, intensity_list for idx, intensities in tqdm(enumerate(mz_intensity)): mz_list, intensity_list = process_line(intensities) # 添加总精确质量和0强度值 mz_list.append(float(df.at[idx, 'Total Exact Mass'])) intensity_list.append(0.0) # 四舍五入处理 round_mz_list = [round(float(mz), 2) for mz in mz_list] round_intensity_list = [round(float(intensity), 2) for intensity in intensity_list] valid.append([round_mz_list, round_intensity_list]) return valid # 返回列表的列表 # 自定义数据集类 class CSVDataset(torch.utils.data.Dataset): def __init__(self, csv_path, tokenizer: PreTrainedTokenizer, max_selfies_len=512): self.df = pd.read_csv(csv_path) self.tokenizer = tokenizer self.max_selfies_len = max_selfies_len # 预处理质谱数据 spec_df = self.df[['Total Exact Mass', 'Spectrum']].copy() self.rag_tensor = prepro_specs_train(spec_df) self.spec_encoded = encoding(self.rag_tensor, P, dimn) def __len__(self): return len(self.df) def __getitem__(self, idx) -> Dict[str, torch.Tensor]: # 1. 处理分子式 formula = self.df.iloc[idx]['Molecular Formula'] formula_vec = formula_to_dense(formula) # 形状: (18,) # 2. 处理质谱数据 spec_matrix = self.spec_encoded[idx] # 形状: (501, 257) # 3. 处理SELFIES - 添加attention_mask selfies_str = self.df.iloc[idx]['SELFIES'] # 编码时同时获取input_ids和attention_mask encoding_result = self.tokenizer.encode_plus( selfies_str, add_special_tokens=True, # 添加[CLS]和[SEP] max_length=self.max_selfies_len, padding='max_length', truncation=True, return_attention_mask=True, return_tensors='pt' ) input_ids = encoding_result['input_ids'].squeeze(0) attention_mask = encoding_result['attention_mask'].squeeze(0) return { 'formula_vec': torch.tensor(formula_vec, dtype=torch.float32), 'spec_matrix': spec_matrix, # 已为tensor,无需重复转换 'selfies_ids': input_ids, 'attention_mask': attention_mask } # 初始化tokenizer tokenizer = AutoTokenizer.from_pretrained('/root/workspace/checkpoint-2500') # 创建数据集 dataset = CSVDataset('/root/workspace/SELFIES-SFT.csv', tokenizer) data_collator = transformers.DataCollatorForSeq2Seq( tokenizer=tokenizer) # 定义带额外Encoder的自定义模型 class LlamaWithEncoder(nn.Module): def __init__(self, base_model, encoder1_dim=18, encoder2_dim=256, hidden_dim=512): super().__init__() self.base_model = base_model # 第一个Transformer Encoder encoder1_layer = nn.TransformerEncoderLayer( d_model=encoder1_dim, nhead=3, dim_feedforward=hidden_dim, batch_first=True ) self.encoder1 = nn.TransformerEncoder(encoder1_layer, num_layers=2) # 第二个Transformer Encoder encoder2_layer = nn.TransformerEncoderLayer( d_model=encoder2_dim, nhead=8, dim_feedforward=hidden_dim, batch_first=True ) self.encoder2 = nn.TransformerEncoder(encoder2_layer, num_layers=2) # 投影层 self.proj1 = nn.Linear(encoder1_dim, base_model.config.hidden_size) self.proj2 = nn.Linear(encoder2_dim, base_model.config.hidden_size) # 融合层 self.fusion = nn.Linear(2 * base_model.config.hidden_size, base_model.config.hidden_size) def prepare_inputs_for_generation(self, input_ids, past_key_values=None, **kwargs): return self.base_model.prepare_inputs_for_generation( input_ids, past_key_values=past_key_values, **kwargs ) def forward( self, input_ids=None, attention_mask=None, encoder1_inputs=None, encoder2_inputs=None, labels=None, past_key_values=None, output_attentions=None, output_hidden_states=None, return_dict=None, **kwargs ): # 处理编码器输入 enc1_out = self.encoder1(encoder1_inputs) enc1_out = enc1_out.mean(dim=1) enc1_proj = self.proj1(enc1_out) enc2_out = self.encoder2(encoder2_inputs) enc2_out = enc2_out.mean(dim=1) enc2_proj = self.proj2(enc2_out) # 融合编码器输出 fused = self.fusion(torch.cat([enc1_proj, enc2_proj], dim=1)) fused = fused.unsqueeze(1) # 获取嵌入层输出 embeddings = self.base_model.get_input_embeddings()(input_ids) # 将融合结果与第一个token的嵌入结合 if embeddings.size(1) > 0: embeddings[:, 0, :] = (embeddings[:, 0, :] + fused[:, 0, :]) / 2 # 使用修改后的嵌入调用基础模型 return self.base_model( inputs_embeds=embeddings, attention_mask=attention_mask, labels=labels, past_key_values=past_key_values, output_attentions=output_attentions, output_hidden_states=output_hidden_states, return_dict=return_dict, **kwargs ) # 加载预训练模型 base_model = transformers.AutoModelForCausalLM.from_pretrained( "/root/workspace/checkpoint-2500", trust_remote_code=True, torch_dtype=torch.bfloat16, ) model = LlamaWithEncoder(base_model) lora_config = LoraConfig( r=8, lora_alpha=16, target_modules="all-linear", # 目标注意力层 lora_dropout=0.0, bias="none", task_type="CAUSAL_LM" ) model = get_peft_model(model, lora_config) model.print_trainable_parameters() # 输出示例:0.3% 参数可训练 training_args = TrainingArguments( output_dir="./llama3.2-SELFIES-SFT", per_device_train_batch_size=16, gradient_accumulation_steps=16, num_train_epochs=10, learning_rate=5.0e-05, optim="adamw_torch", logging_steps=10, bf16=True, save_strategy="steps", lr_scheduler_type='cosine', max_grad_norm=1.0, save_steps=2000, warmup_steps=0 ) class CustomTrainer(LoraPlusTrainer): def get_train_dataloader(self) -> DataLoader: """ Returns the training dataloader using a random sampler to shuffle the dataset. """ return DataLoader( self.train_dataset, batch_size=self.args.train_batch_size, shuffle=True, collate_fn=self.data_collator, drop_last=False, ) # 使用修改后的 CustomTrainer lp_trainer = CustomTrainer( model, training_args, train_dataset=dataset, tokenizer=tokenizer, data_collator=data_collator, callbacks=[swanlab_callback], ) lp_trainer.train() lp_trainer.save_model(output_dir='./llama3.2-SELFIES-SFT') 修改代码,确保添加的Encoder可以顺利进行lora微调

import pandas as pd import torch from sklearn.model_selection import train_test_split from sklearn.preprocessing import LabelEncoder from torch.utils.data import Dataset, DataLoader from torch.utils.tensorboard import SummaryWriter from transformers import BertTokenizer, BertForSequenceClassification, AdamW # 训练模型 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') # 加载数据 data = pd.read_csv('simplifyweibo_5_moods.csv') # 获取text和label texts = data['text'].tolist() labels = data['label'].tolist() # 将本文标签转换为数值标签 label_encoder = LabelEncoder() labels = label_encoder.fit_transform(labels) # 划分训练集和测试集 train_texts, val_texts, train_labels, val_labels = train_test_split(texts, labels, test_size=0.2, random_state=42) # 加载BERT的分词器 tokenizer = BertTokenizer.from_pretrained('./bert_localpath/') # 对文本进行编码 train_encodings = tokenizer(train_texts, truncation=True, padding=True, max_length=128) val_encodings = tokenizer(val_texts, truncation=True, padding=True, max_length=128) # 创建PyTorch数据集 class WeiboDataset(Dataset): def __init__(self, encodings, labels): self.encodings = encodings self.labels = labels def __getitem__(self, idx): item = {key: torch.tensor(val[idx]) for key, val in self.encodings.items()} item['labels'] = torch.tensor(self.labels[idx]) return item def __len__(self): return len(self.labels) train_dataset = WeiboDataset(train_encodings, train_labels) val_dataset = WeiboDataset(val_encodings, val_labels) # 加载BERT模型,设置输出维度为类别数 num_classes = len(label_encoder.classes_) model = BertForSequenceClassification.from_pretrained('./bert_localpath', num_labels=num_classes).to(device) # 创建DataLoader train_dataloader = DataLoader(train_dataset, batch_size=16, shuffle=True) val_dataloader = DataLoader(val_dataset, batch_size=16, shuffle=True) # 定义优化器 optimizer = AdamW(model.parameters(), lr=2e-5) # 创建TensorBoard的SummmaryWriter writer = SummaryWriter('./logs') epochs = 3 for epoch in r

最新推荐

recommend-type

【微信小程序源码】小程序官方Demo.zip

资源说明: 1:本资料仅用作交流学习参考,请切勿用于商业用途。 2:一套精品实用微信小程序源码资源,无论是入门练手还是项目复用都超实用,省去重复开发时间,让开发少走弯路! 更多精品资源请访问 https://blog.csdn.net/ashyyyy/article/details/146464041
recommend-type

体育赛事摘要数据集构建与自然语言处理技术应用_基于人工标注的大规模体育赛事评论文本与新闻文本摘要数据集SGSum_提供高质量训练集验证集测试集用于学术研究支持文本摘要模型开发与评估.zip

体育赛事摘要数据集构建与自然语言处理技术应用_基于人工标注的大规模体育赛事评论文本与新闻文本摘要数据集SGSum_提供高质量训练集验证集测试集用于学术研究支持文本摘要模型开发与评估.zip
recommend-type

【微信小程序源码】医疗床位查询小程序.zip

资源说明: 1:本资料仅用作交流学习参考,请切勿用于商业用途。 2:一套精品实用微信小程序源码资源,无论是入门练手还是项目复用都超实用,省去重复开发时间,让开发少走弯路! 更多精品资源请访问 https://blog.csdn.net/ashyyyy/article/details/146464041
recommend-type

bedrock-core-7.0.4.jar中文-英文对照文档.zip

1、压缩文件中包含: 中文-英文对照文档、jar包下载地址、Maven依赖、Gradle依赖、源代码下载地址。 2、使用方法: 解压最外层zip,再解压其中的zip包,双击 【index.html】 文件,即可用浏览器打开、进行查看。 3、特殊说明: (1)本文档为人性化翻译,精心制作,请放心使用; (2)只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; (3)不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 4、温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件。 5、本文件关键字: jar中文-英文对照文档.zip,java,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,中文API文档,手册,开发手册,使用手册,参考手册。
recommend-type

【微信小程序源码】艺术.zip

资源说明: 1:本资料仅用作交流学习参考,请切勿用于商业用途。 2:一套精品实用微信小程序源码资源,无论是入门练手还是项目复用都超实用,省去重复开发时间,让开发少走弯路! 更多精品资源请访问 https://blog.csdn.net/ashyyyy/article/details/146464041
recommend-type

Docker化部署TS3AudioBot教程与实践

### 标题知识点 #### TS3AudioBot_docker - **Dockerfile的用途与组成**:Dockerfile是一个文本文件,包含了所有构建Docker镜像的命令。开发者可以通过编辑Dockerfile来指定Docker镜像创建时所需的所有指令,包括基础镜像、运行时指令、环境变量、软件安装、文件复制等。TS3AudioBot_docker表明这个Dockerfile与TS3AudioBot项目相关,TS3AudioBot可能是一个用于TeamSpeak 3服务器的音频机器人,用于播放音频或与服务器上的用户进行交互。 - **Docker构建过程**:在描述中,有两种方式来获取TS3AudioBot的Docker镜像。一种是从Dockerhub上直接运行预构建的镜像,另一种是自行构建Docker镜像。自建过程会使用到docker build命令,而从Dockerhub运行则会用到docker run命令。 ### 描述知识点 #### Docker命令的使用 - **docker run**:这个命令用于运行一个Docker容器。其参数说明如下: - `--name tsbot`:为运行的容器指定一个名称,这里命名为tsbot。 - `--restart=always`:设置容器重启策略,这里是总是重启,确保容器在失败后自动重启。 - `-it`:这是一对参数,-i 表示交互式操作,-t 分配一个伪终端。 - `-d`:表示后台运行容器。 - `-v /home/tsBot/data:/data`:将宿主机的/home/tsBot/data目录挂载到容器内的/data目录上,以便持久化存储数据。 - `rofl256/tsaudiobot` 或 `tsaudiobot`:指定Docker镜像名称。前者可能是从DockerHub上获取的带有用户名命名空间的镜像,后者是本地构建或已重命名的镜像。 #### Docker构建流程 - **构建镜像**:使用docker build命令可以将Dockerfile中的指令转化为一个Docker镜像。`docker build . -t tsaudiobot`表示从当前目录中读取Dockerfile,并创建一个名为tsaudiobot的镜像。构建过程中,Docker会按顺序执行Dockerfile中的指令,比如FROM、RUN、COPY等,最终形成一个包含所有依赖和配置的应用镜像。 ### 标签知识点 #### Dockerfile - **Dockerfile的概念**:Dockerfile是一个包含创建Docker镜像所有命令的文本文件。它被Docker程序读取,用于自动构建Docker镜像。Dockerfile中的指令通常包括安装软件、设置环境变量、复制文件等。 - **Dockerfile中的命令**:一些常用的Dockerfile命令包括: - FROM:指定基础镜像。 - RUN:执行命令。 - COPY:将文件或目录复制到镜像中。 - ADD:类似于COPY,但是 ADD 支持从URL下载文件以及解压 tar 文件。 - ENV:设置环境变量。 - EXPOSE:声明端口。 - VOLUME:创建挂载点。 - CMD:容器启动时要运行的命令。 - ENTRYPOINT:配置容器启动时的执行命令。 ### 压缩包子文件的文件名称列表知识点 #### 文件命名 - **TS3AudioBot_docker-main**:此文件名表明了这是一个主要的代码库或Dockerfile的存放位置。在开发中,通常main分支代表当前的主版本或正在积极开发的分支。因此TS3AudioBot_docker-main可能表示这是在Dev分支上开发的Dockerfile的主要代码版本。主分支一般比较稳定,并作为新的特性开发的基础。 ### 综合知识点 - **Docker在DevOps中的角色**:Docker作为一种轻量级的容器化技术,在DevOps领域扮演重要角色。它可以快速部署、一致的运行环境、便于测试和迁移应用。通过Dockerfile的编写和docker build命令,开发者可以构建可移植的容器镜像,通过docker run命令则可以快速启动容器实例。 - **TS3AudioBot与TeamSpeak**:TS3AudioBot很可能是一个针对TeamSpeak 3服务器的自动化解决方案。TeamSpeak是一个语音通讯软件,广泛用于线上游戏团队进行沟通。一个音频机器人可以提供自动化的消息通知、音频流控制等功能,提高游戏社区的交流效率。 - **Docker镜像的版本管理**:镜像的命名通常包括用户名、项目名和标签。如rofl256/tsaudiobot中rofl256可能是一个用户名,tsaudiobot可能是项目名称,这样的命名规则有利于镜像的共享和管理。 在介绍以上知识点后,我们理解了标题、描述、标签及文件名称列表中所含的知识点。这些知识点涵盖了Dockerfile的构建、Docker镜像的使用、TS3AudioBot功能以及版本控制等多个方面,为IT专业人员在使用和开发Docker相关项目时提供了必要的信息。
recommend-type

零售销售数据的探索性分析与DeepAR模型预测

### 零售销售数据的探索性分析与DeepAR模型预测 #### 1. 探索性数据分析 在拥有45家商店的情况下,我们选择了第20号商店,来分析其不同部门在三年间的销售表现。借助DeepAR算法,我们可以了解不同部门商品的销售情况。 在SageMaker中,通过生命周期配置(Lifecycle Configurations),我们可以在笔记本实例启动前自定义安装Python包,避免在执行笔记本前手动跟踪所需的包。为了探索零售销售数据,我们需要安装最新版本(0.9.0)的seaborn库。具体操作步骤如下: 1. 在SageMaker的Notebook下,点击Lifecycle Config
recommend-type

llm agent平台

<think>好的,我现在需要帮用户寻找与LLM代理平台解决方案相关的信息。首先,我得明确用户的需求。用户提到了“LLM agent platform solutions”,也就是大型语言模型代理平台的解决方案。这可能涉及到如何构建、部署或优化基于LLM的代理系统。接下来,我应该回忆之前用户提供的引用内容,看看有没有相关的信息可以利用。 根据引用[1],提到构建LLM应用程序的步骤分解,可能涉及到代理平台的设计。引用[2]讨论了评估LLM的挑战,包括可重复性和开源模型的解决方案,这可能影响代理平台的稳定性和选择。引用[3]则提到大模型相关的岗位和面试题,可能涉及实际应用中的技术问题。 接下
recommend-type

Docker实现OAuth2代理:安全的HTTPS解决方案

### 知识点详细说明: #### Dockerfile基础 Dockerfile是一种文本文件,它包含了用户创建Docker镜像所需的命令和参数。Docker通过读取Dockerfile中的指令自动构建镜像。Dockerfile通常包含了如下载基础镜像、安装软件包、执行脚本等指令。 #### Dockerfile中的常用指令 1. **FROM**: 指定基础镜像,所有的Dockerfile都必须以FROM开始。 2. **RUN**: 在构建过程中执行命令,如安装软件。 3. **CMD**: 设置容器启动时运行的命令,可以被docker run命令后面的参数覆盖。 4. **EXPOSE**: 告诉Docker容器在运行时监听指定的网络端口。 5. **ENV**: 设置环境变量。 6. **ADD**: 将本地文件复制到容器中,如果是tar归档文件会自动解压。 7. **ENTRYPOINT**: 设置容器启动时的默认命令,不会被docker run命令覆盖。 8. **VOLUME**: 创建一个挂载点以挂载外部存储,如磁盘或网络文件系统。 #### OAuth 2.0 Proxy OAuth 2.0 Proxy 是一个轻量级的认证代理,用于在应用程序前提供OAuth认证功能。它主要通过HTTP重定向和回调机制,实现对下游服务的安全访问控制,支持多种身份提供商(IdP),如Google, GitHub等。 #### HTTPS和SSL/TLS HTTPS(HTTP Secure)是HTTP的安全版本,它通过SSL/TLS协议加密客户端和服务器之间的通信。使用HTTPS可以保护数据的机密性和完整性,防止数据在传输过程中被窃取或篡改。SSL(Secure Sockets Layer)和TLS(Transport Layer Security)是用来在互联网上进行通信时加密数据的安全协议。 #### Docker容器与HTTPS 为了在使用Docker容器时启用HTTPS,需要在容器内配置SSL/TLS证书,并确保使用443端口。这通常涉及到配置Nginx或Apache等Web服务器,并将其作为反向代理运行在Docker容器内。 #### 临时分叉(Fork) 在开源领域,“分叉”指的是一种特殊的复制项目的行为,通常是为了对原项目进行修改或增强功能。分叉的项目可以独立于原项目发展,并可选择是否合并回原项目。在本文的语境下,“临时分叉”可能指的是为了实现特定功能(如HTTPS支持)而在现有Docker-oauth2-proxy项目基础上创建的分支版本。 #### 实现步骤 要实现HTTPS支持的docker-oauth2-proxy,可能需要进行以下步骤: 1. **准备SSL/TLS证书**:可以使用Let's Encrypt免费获取证书或自行生成。 2. **配置Nginx/Apache服务器**:在Dockerfile中添加配置,以使用SSL证书和代理设置。 3. **修改OAuth2 Proxy设置**:调整OAuth2 Proxy配置以使用HTTPS连接。 4. **分叉Docker-oauth2-proxy项目**:创建项目的分支副本,以便进行修改。 5. **编辑Dockerfile**:在分叉的项目中编写或修改Dockerfile,包括下载基础镜像、设置环境变量、添加SSL证书、配置Nginx/Apache和OAuth2 Proxy等步骤。 6. **构建和测试新镜像**:使用Docker构建镜像,并在安全环境中进行测试,确保HTTPS配置正确,并且OAuth2 Proxy功能正常工作。 7. **部署到生产环境**:在确认无误后,将配置好的镜像部署到生产环境中。 #### 压缩包子文件的文件名称列表 - **docker-oauth2-proxy-master**: 这可能是指在GitHub等代码托管平台上,docker-oauth2-proxy项目的主分支或主仓库。名称列表中的“master”暗示了该文件夹包含的是主分支的代码。 总结来说,要实现一个支持HTTPS的docker-oauth2-proxy,开发者需要进行一系列的配置和编码工作,包括使用Dockerfile来构建自定义的Docker镜像,配置SSL/TLS证书,分叉并修改现有的开源项目代码。通过这些步骤,可以确保OAuth2 Proxy能够安全地处理HTTPS请求,并为下游服务提供安全认证功能。
recommend-type

利用AmazonSageMaker进行图像分类:从理论到实践

# 利用 Amazon SageMaker 进行图像分类:从理论到实践 ## 1. 主题建模与图像分类概述 在数据科学领域,从大量非结构化数据中提取信息和主题至关重要。像 SageMaker 的神经主题模型(NTM)这类主题建模技术,提供了线性和非线性学习方法,能帮助我们深入挖掘数据中的潜在主题。它通过特定的架构和内部机制工作,还支持分布式训练,将数据集分割成多个块进行并行处理。训练完成后,我们可以将模型部署为端点并进行推理,例如解读安然邮件中的主题。 图像分类在过去五年中一直是热门研究领域,因为它能解决众多行业的实际业务问题,如自动驾驶汽车行业就高度依赖图像分类和目标检测模型的准确性。A