【Bug已解决】How to fine tune BERT on unlabeled data? 解决方案
【Bug已解决】How to fine tune BERT on unlabeled data? 解决方案问题标签BERT、无标签数据、微调、半监督学习、自监督学习、领域适应适用版本Transformers 4.x、PyTorch 1.8难度等级⭐⭐⭐⭐较高一、问题描述在实际业务场景中标注数据往往稀缺且昂贵而无标签数据则大量存在。很多开发者面临的问题是如何利用大量无标签数据来微调 BERT 模型提升模型在下游任务上的表现这是一个经典的半监督学习/自监督学习问题。BERT 本身就是通过自监督方式MLM NSP在无标签数据上预训练的我们可以在下游任务中继续利用无标签数据来进一步提升模型性能。核心挑战挑战说明无标签数据无法直接用于监督学习需要设计自监督或半监督策略领域差异通用 BERT 可能不了解领域术语计算资源限制大规模无标签数据处理耗时何时停止自监督训练容易过拟合二、错误复现场景直接用无标签数据做监督训练必然失败import torch from transformers import BertTokenizer, BertForSequenceClassification # 加载预训练 BERT tokenizer BertTokenizer.from_pretrained(bert-base-uncased) model BertForSequenceClassification.from_pretrained(bert-base-uncased, num_labels2) # 模拟无标签数据 unlabeled_texts [ The new machine learning model achieves state-of-the-art results., Quantum computing may revolutionize cryptography., The stock market experienced significant volatility today., # ... 大量无标签文本 ] # ❌ 错误无标签数据没有标签无法计算 CrossEntropyLoss texts unlabeled_texts # labels ??? # 没有标签 # 以下代码会失败因为没有标签 # loss model(input_ids, attention_mask, labelslabels)问题无标签数据没有标签无法直接使用监督学习的损失函数。三、根因分析3.1 为什么需要利用无标签数据领域适应通用 BERT 在法律、医学、金融等垂直领域表现不佳需要领域数据继续预训练。数据效率少量标注 大量无标签 仅用少量标注。语言/风格适应社交媒体文本、口语化表达与标准文本差异大。3.2 可行的策略概览策略原理适用场景继续预训练Domain-Adaptive Pretraining, DAPT在无标签数据上继续 MLM 训练领域适应任务自适应预训练TAPT在任务相关无标签数据上 MLM提升特定任务性能伪标签Pseudo-Labeling用模型预测作为伪标签半监督学习一致性正则化Consistency Regularization对输入做扰动要求输出一致半监督学习对比学习Contrastive Learning拉近相似样本推远不同样本表示学习四、解决方案方案一领域自适应预训练DAPT—— 最推荐在无标签数据上继续 BERT 的 Masked Language ModelMLM训练让模型适应领域语言。import torch import torch.nn as nn from transformers import BertTokenizer, BertForMaskedLM, BertConfig from torch.utils.data import Dataset, DataLoader import random import numpy as np # MLM 数据集 class MLMDataset(Dataset): Masked Language Model 数据集 def __init__(self, texts, tokenizer, max_length128, mlm_probability0.15): self.texts texts self.tokenizer tokenizer self.max_length max_length self.mlm_probability mlm_probability def __len__(self): return len(self.texts) def __getitem__(self, idx): text self.texts[idx] encoding self.tokenizer( text, truncationTrue, max_lengthself.max_length, paddingmax_length, return_tensorspt, ) input_ids encoding[input_ids].squeeze(0) attention_mask encoding[attention_mask].squeeze(0) # 创建 MLM 标签 labels input_ids.clone() # 随机 mask 15% 的 token probability_matrix torch.full(input_ids.shape, self.mlm_probability) special_tokens_mask [ self.tokenizer.get_special_tokens_mask(val.tolist(), already_has_special_tokensTrue) for val in input_ids ] special_tokens_mask torch.tensor(special_tokens_mask, dtypetorch.bool) probability_matrix.masked_fill_(special_tokens_mask, value0.0) masked_indices torch.bernoulli(probability_matrix).bool() labels[~masked_indices] -100 # 只计算 masked 位置的 loss # 80% 替换为 [MASK] indices_replaced torch.bernoulli(torch.full(input_ids.shape, 0.8)).bool() masked_indices input_ids[indices_replaced] self.tokenizer.convert_tokens_to_ids(self.tokenizer.mask_token) # 10% 替换为随机 token indices_random torch.bernoulli(torch.full(input_ids.shape, 0.5)).bool() masked_indices ~indices_replaced random_words torch.randint(len(self.tokenizer), input_ids.shape, dtypetorch.long) input_ids[indices_random] random_words[indices_random] # 10% 保持不变 return { input_ids: input_ids, attention_mask: attention_mask, labels: labels, } # DAPT 训练 def domain_adaptive_pretraining(texts, model_namebert-base-uncased, epochs3, batch_size16, lr5e-5): 领域自适应预训练 device torch.device(cuda if torch.cuda.is_available() else cpu) print(f使用设备: {device}) # 加载 tokenizer 和模型 tokenizer BertTokenizer.from_pretrained(model_name) model BertForMaskedLM.from_pretrained(model_name) model.to(device) # 创建数据集 dataset MLMDataset(texts, tokenizer, max_length128) dataloader DataLoader(dataset, batch_sizebatch_size, shuffleTrue) # 优化器 optimizer torch.optim.AdamW(model.parameters(), lrlr, weight_decay0.01) # 学习率调度器 num_training_steps epochs * len(dataloader) scheduler torch.optim.lr_scheduler.LinearLR( optimizer, start_factor0.1, total_itersnum_training_steps // 10 ) print(f无标签数据量: {len(texts)}) print(f训练步数: {num_training_steps}) # 训练循环 for epoch in range(epochs): model.train() total_loss 0 for batch_idx, batch in enumerate(dataloader): input_ids batch[input_ids].to(device) attention_mask batch[attention_mask].to(device) labels batch[labels].to(device) optimizer.zero_grad() outputs model(input_idsinput_ids, attention_maskattention_mask, labelslabels) loss outputs.loss loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() scheduler.step() total_loss loss.item() if batch_idx % 50 0: print(fEpoch [{epoch1}/{epochs}] Batch [{batch_idx}/{len(dataloader)}] fLoss: {loss.item():.4f}) avg_loss total_loss / len(dataloader) print(fEpoch [{epoch1}/{epochs}] Average Loss: {avg_loss:.4f}) return model, tokenizer # 运行示例 # 模拟领域无标签数据 unlabeled_texts [ The patient presented with acute myocardial infarction and was admitted to the ICU., Clinical trials show that the new drug reduces mortality by 30% in high-risk patients., The radiologist identified a mass in the left lung during routine screening., Laboratory results indicate elevated levels of troponin and creatine kinase., The surgical team performed a coronary artery bypass graft successfully., Post-operative care includes monitoring for signs of infection and blood clots., The epidemiological study tracked 5000 participants over a ten-year period., Genetic testing revealed a mutation in the BRCA1 gene associated with breast cancer., The emergency department handled a surge of trauma cases during the holiday weekend., Telemedicine platforms have expanded access to specialist consultations in rural areas., ] * 50 # 复制以增加数据量 # 运行 DAPT # model, tokenizer domain_adaptive_pretraining(unlabeled_texts, epochs3)方案二伪标签Pseudo-Labeling先用少量标注数据训练初始模型然后用该模型对无标签数据预测将高置信度预测作为伪标签。import torch import torch.nn as nn from transformers import BertTokenizer, BertForSequenceClassification from torch.utils.data import Dataset, DataLoader, TensorDataset import numpy as np class PseudoLabelTrainer: 伪标签训练器 def __init__(self, model_namebert-base-uncased, num_labels2, confidence_threshold0.9, deviceauto): self.model_name model_name self.num_labels num_labels self.confidence_threshold confidence_threshold if device auto: self.device torch.device(cuda if torch.cuda.is_available() else cpu) else: self.device torch.device(device) self.tokenizer BertTokenizer.from_pretrained(model_name) self.model BertForSequenceClassification.from_pretrained( model_name, num_labelsnum_labels ).to(self.device) def train_on_labeled(self, labeled_texts, labeled_labels, epochs5, batch_size16, lr2e-5): 在有标签数据上训练 # 编码 encodings self.tokenizer( labeled_texts, truncationTrue, max_length128, paddingTrue, return_tensorspt ) dataset TensorDataset( encodings[input_ids], encodings[attention_mask], torch.tensor(labeled_labels) ) dataloader DataLoader(dataset, batch_sizebatch_size, shuffleTrue) optimizer torch.optim.AdamW(self.model.parameters(), lrlr) criterion nn.CrossEntropyLoss() self.model.train() for epoch in range(epochs): total_loss 0 correct 0 total 0 for batch in dataloader: input_ids, attention_mask, labels [b.to(self.device) for b in batch] optimizer.zero_grad() outputs self.model(input_idsinput_ids, attention_maskattention_mask) loss criterion(outputs.logits, labels) loss.backward() optimizer.step() total_loss loss.item() _, predicted outputs.logits.max(1) total labels.size(0) correct predicted.eq(labels).sum().item() print(f[有标签训练] Epoch [{epoch1}/{epochs}] fLoss: {total_loss/len(dataloader):.4f}, fAcc: {100.*correct/total:.2f}%) def generate_pseudo_labels(self, unlabeled_texts, batch_size32): 为无标签数据生成伪标签 encodings self.tokenizer( unlabeled_texts, truncationTrue, max_length128, paddingTrue, return_tensorspt ) dataset TensorDataset(encodings[input_ids], encodings[attention_mask]) dataloader DataLoader(dataset, batch_sizebatch_size) self.model.eval() all_probs [] all_preds [] with torch.no_grad(): for batch in dataloader: input_ids, attention_mask [b.to(self.device) for b in batch] outputs self.model(input_idsinput_ids, attention_maskattention_mask) probs torch.softmax(outputs.logits, dim-1) confidence, preds probs.max(dim-1) all_probs.extend(confidence.cpu().tolist()) all_preds.extend(preds.cpu().tolist()) # 筛选高置信度样本 pseudo_labeled [] pseudo_labels [] for i, (text, pred, conf) in enumerate(zip(unlabeled_texts, all_preds, all_probs)): if conf self.confidence_threshold: pseudo_labeled.append(text) pseudo_labels.append(pred) print(f伪标签生成: {len(pseudo_labeled)}/{len(unlabeled_texts)} f({100.*len(pseudo_labeled)/len(unlabeled_texts):.1f}%) 样本置信度 {self.confidence_threshold}) return pseudo_labeled, pseudo_labels def train_with_pseudo_labels(self, labeled_texts, labeled_labels, unlabeled_texts, epochs5, batch_size16, lr2e-5): 结合有标签和伪标签数据训练 # 生成伪标签 pseudo_texts, pseudo_labels self.generate_pseudo_labels(unlabeled_texts) # 合并数据 all_texts labeled_texts pseudo_texts all_labels labeled_labels pseudo_labels # 编码 encodings self.tokenizer( all_texts, truncationTrue, max_length128, paddingTrue, return_tensorspt ) dataset TensorDataset( encodings[input_ids], encodings[attention_mask], torch.tensor(all_labels) ) dataloader DataLoader(dataset, batch_sizebatch_size, shuffleTrue) optimizer torch.optim.AdamW(self.model.parameters(), lrlr) criterion nn.CrossEntropyLoss() self.model.train() for epoch in range(epochs): total_loss 0 correct 0 total 0 for batch in dataloader: input_ids, attention_mask, labels [b.to(self.device) for b in batch] optimizer.zero_grad() outputs self.model(input_idsinput_ids, attention_maskattention_mask) loss criterion(outputs.logits, labels) loss.backward() optimizer.step() total_loss loss.item() _, predicted outputs.logits.max(1) total labels.size(0) correct predicted.eq(labels).sum().item() print(f[伪标签训练] Epoch [{epoch1}/{epochs}] fLoss: {total_loss/len(dataloader):.4f}, fAcc: {100.*correct/total:.2f}%) # 使用示例 # trainer PseudoLabelTrainer(confidence_threshold0.9) # trainer.train_on_labeled(labeled_texts, labeled_labels, epochs5) # trainer.train_with_pseudo_labels(labeled_texts, labeled_labels, unlabeled_texts, epochs5)方案三一致性正则化对同一输入施加不同扰动要求模型输出保持一致。import torch import torch.nn as nn from transformers import BertTokenizer, BertForSequenceClassification import random class ConsistencyRegularizationTrainer: 一致性正则化训练器 def __init__(self, model_namebert-base-uncased, num_labels2): self.device torch.device(cuda if torch.cuda.is_available() else cpu) self.tokenizer BertTokenizer.from_pretrained(model_name) self.model BertForSequenceClassification.from_pretrained( model_name, num_labelsnum_labels ).to(self.device) def augment_text(self, text, aug_prob0.1): 文本数据增强 words text.split() augmented [] for word in words: if random.random() aug_prob: # 随机删除 continue elif random.random() aug_prob: # 随机替换为 [MASK] augmented.append([MASK]) else: augmented.append(word) # 随机打乱 if random.random() 0.1 and len(augmented) 2: idx random.randint(0, len(augmented) - 2) augmented[idx], augmented[idx 1] augmented[idx 1], augmented[idx] return .join(augmented) if augmented else text def train_step(self, labeled_batch, unlabeled_batch, optimizer, lambda_consistency1.0, lambda_supervised1.0): 一步训练 self.model.train() optimizer.zero_grad() # 有标签数据的监督损失 labeled_input_ids labeled_batch[input_ids].to(self.device) labeled_attention_mask labeled_batch[attention_mask].to(self.device) labeled_labels labeled_batch[labels].to(self.device) labeled_outputs self.model( input_idslabeled_input_ids, attention_masklabeled_attention_mask ) supervised_loss nn.CrossEntropyLoss()( labeled_outputs.logits, labeled_labels ) # 无标签数据的一致性损失 unlabeled_input_ids unlabeled_batch[input_ids].to(self.device) unlabeled_attention_mask unlabeled_batch[attention_mask].to(self.device) # 原始预测 with torch.no_grad(): original_outputs self.model( input_idsunlabeled_input_ids, attention_maskunlabeled_attention_mask ) original_probs torch.softmax(original_outputs.logits, dim-1) # 增强后预测 augmented_input_ids unlabeled_batch[augmented_input_ids].to(self.device) augmented_attention_mask unlabeled_batch[augmented_attention_mask].to(self.device) augmented_outputs self.model( input_idsaugmented_input_ids, attention_maskaugmented_attention_mask ) augmented_probs torch.softmax(augmented_outputs.logits, dim-1) # KL 散度作为一致性损失 consistency_loss nn.KLDivLoss(reductionbatchmean)( torch.log(augmented_probs 1e-8), original_probs.detach() ) # 总损失 total_loss (lambda_supervised * supervised_loss lambda_consistency * consistency_loss) total_loss.backward() optimizer.step() return { supervised_loss: supervised_loss.item(), consistency_loss: consistency_loss.item(), total_loss: total_loss.item(), }五、完整修复代码import torch import torch.nn as nn from transformers import ( BertTokenizer, BertForMaskedLM, BertForSequenceClassification, get_linear_schedule_with_warmup ) from torch.utils.data import Dataset, DataLoader, TensorDataset import numpy as np import random import os # 完整 DAPT 下游微调流程 class BertUnlabeledFineTuner: BERT 无标签数据微调完整流程 def __init__(self, model_namebert-base-uncased, num_labels2, max_length128, deviceauto): self.model_name model_name self.num_labels num_labels self.max_length max_length if device auto: self.device torch.device(cuda if torch.cuda.is_available() else cpu) else: self.device torch.device(device) self.tokenizer BertTokenizer.from_pretrained(model_name) print(f设备: {self.device}) def step1_domain_adaptive_pretraining(self, unlabeled_texts, epochs3, batch_size16, lr5e-5): 步骤 1: 领域自适应预训练 (DAPT) print(\n * 60) print(步骤 1: 领域自适应预训练 (DAPT)) print( * 60) # 加载 MLM 模型 model BertForMaskedLM.from_pretrained(self.model_name).to(self.device) # 创建 MLM 数据集 dataset self._create_mlm_dataset(unlabeled_texts) dataloader DataLoader(dataset, batch_sizebatch_size, shuffleTrue) # 优化器 optimizer torch.optim.AdamW(model.parameters(), lrlr, weight_decay0.01) total_steps epochs * len(dataloader) scheduler get_linear_schedule_with_warmup( optimizer, num_warmup_stepstotal_steps // 10, num_training_stepstotal_steps ) # 训练 for epoch in range(epochs): model.train() total_loss 0 for batch in dataloader: input_ids batch[input_ids].to(self.device) attention_mask batch[attention_mask].to(self.device) labels batch[labels].to(self.device) optimizer.zero_grad() outputs model(input_idsinput_ids, attention_maskattention_mask, labelslabels) loss outputs.loss loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step() scheduler.step() total_loss loss.item() print(f Epoch [{epoch1}/{epochs}] Loss: {total_loss/len(dataloader):.4f}) # 保存领域适应后的模型 self.dapt_model_path ./dapt_bert model.save_pretrained(self.dapt_model_path) self.tokenizer.save_pretrained(self.dapt_model_path) print(f 领域适应模型已保存到: {self.dapt_model_path}) return model def step2_supervised_finetuning(self, labeled_texts, labeled_labels, epochs5, batch_size16, lr2e-5): 步骤 2: 在有标签数据上微调 print(\n * 60) print(步骤 2: 有标签数据监督微调) print( * 60) # 加载 DAPT 后的模型如果有否则用原始 BERT model_path getattr(self, dapt_model_path, self.model_name) model BertForSequenceClassification.from_pretrained( model_path, num_labelsself.num_labels ).to(self.device) # 编码有标签数据 encodings self.tokenizer( labeled_texts, truncationTrue, max_lengthself.max_length, paddingTrue, return_tensorspt ) dataset TensorDataset( encodings[input_ids], encodings[attention_mask], torch.tensor(labeled_labels) ) dataloader DataLoader(dataset, batch_sizebatch_size, shuffleTrue) optimizer torch.optim.AdamW(model.parameters(), lrlr, weight_decay0.01) total_steps epochs * len(dataloader) scheduler get_linear_schedule_with_warmup( optimizer, num_warmup_stepstotal_steps // 10, num_training_stepstotal_steps ) criterion nn.CrossEntropyLoss() for epoch in range(epochs): model.train() total_loss 0 correct 0 total 0 for batch in dataloader: input_ids, attention_mask, labels [b.to(self.device) for b in batch] optimizer.zero_grad() outputs model(input_idsinput_ids, attention_maskattention_mask) loss criterion(outputs.logits, labels) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step() scheduler.step() total_loss loss.item() _, predicted outputs.logits.max(1) total labels.size(0) correct predicted.eq(labels).sum().item() print(f Epoch [{epoch1}/{epochs}] Loss: {total_loss/len(dataloader):.4f}, fAcc: {100.*correct/total:.2f}%) self.classification_model model return model def step3_pseudo_label_training(self, labeled_texts, labeled_labels, unlabeled_texts, confidence_threshold0.9, epochs3, batch_size16, lr1e-5): 步骤 3: 伪标签训练 print(\n * 60) print(步骤 3: 伪标签训练) print( * 60) model self.classification_model # 生成伪标签 model.eval() pseudo_texts [] pseudo_labels [] with torch.no_grad(): for text in unlabeled_texts: encoding self.tokenizer( text, truncationTrue, max_lengthself.max_length, paddingTrue, return_tensorspt ) input_ids encoding[input_ids].to(self.device) attention_mask encoding[attention_mask].to(self.device) output model(input_idsinput_ids, attention_maskattention_mask) probs torch.softmax(output.logits, dim-1) confidence, pred probs.max(dim-1) if confidence.item() confidence_threshold: pseudo_texts.append(text) pseudo_labels.append(pred.item()) print(f 伪标签样本数: {len(pseudo_texts)}/{len(unlabeled_texts)}) if len(pseudo_texts) 0: print( 无高置信度伪标签跳过此步骤) return model # 合并数据 all_texts labeled_texts pseudo_texts all_labels labeled_labels pseudo_labels # 训练 encodings self.tokenizer( all_texts, truncationTrue, max_lengthself.max_length, paddingTrue, return_tensorspt ) dataset TensorDataset( encodings[input_ids], encodings[attention_mask], torch.tensor(all_labels) ) dataloader DataLoader(dataset, batch_sizebatch_size, shuffleTrue) optimizer torch.optim.AdamW(model.parameters(), lrlr, weight_decay0.01) criterion nn.CrossEntropyLoss() for epoch in range(epochs): model.train() total_loss 0 correct 0 total 0 for batch in dataloader: input_ids, attention_mask, labels [b.to(self.device) for b in batch] optimizer.zero_grad() outputs model(input_idsinput_ids, attention_maskattention_mask) loss criterion(outputs.logits, labels) loss.backward() optimizer.step() total_loss loss.item() _, predicted outputs.logits.max(1) total labels.size(0) correct predicted.eq(labels).sum().item() print(f Epoch [{epoch1}/{epochs}] Loss: {total_loss/len(dataloader):.4f}, fAcc: {100.*correct/total:.2f}%) return model def _create_mlm_dataset(self, texts, mlm_probability0.15): 创建 MLM 数据集 encoded self.tokenizer( texts, truncationTrue, max_lengthself.max_length, paddingmax_length, return_tensorspt ) input_ids encoded[input_ids] attention_mask encoded[attention_mask] labels input_ids.clone() # Mask probability_matrix torch.full(input_ids.shape, mlm_probability) special_tokens_mask [ self.tokenizer.get_special_tokens_mask(ids.tolist(), already_has_special_tokensTrue) for ids in input_ids ] special_tokens_mask torch.tensor(special_tokens_mask, dtypetorch.bool) probability_matrix.masked_fill_(special_tokens_mask, value0.0) masked_indices torch.bernoulli(probability_matrix).bool() labels[~masked_indices] -100 # 80% [MASK] indices_replaced torch.bernoulli(torch.full(input_ids.shape, 0.8)).bool() masked_indices input_ids[indices_replaced] self.tokenizer.mask_token_id # 10% random indices_random (torch.bernoulli(torch.full(input_ids.shape, 0.5)).bool() masked_indices ~indices_replaced) random_tokens torch.randint(len(self.tokenizer), input_ids.shape, dtypetorch.long) input_ids[indices_random] random_tokens[indices_random] return TensorDataset(input_ids, attention_mask, labels) def evaluate(self, test_texts, test_labels, batch_size32): 评估模型 model self.classification_model model.eval() encodings self.tokenizer( test_texts, truncationTrue, max_lengthself.max_length, paddingTrue, return_tensorspt ) dataset TensorDataset( encodings[input_ids], encodings[attention_mask], torch.tensor(test_labels) ) dataloader DataLoader(dataset, batch_sizebatch_size) correct 0 total 0 with torch.no_grad(): for batch in dataloader: input_ids, attention_mask, labels [b.to(self.device) for b in batch] outputs model(input_idsinput_ids, attention_maskattention_mask) _, predicted outputs.logits.max(1) total labels.size(0) correct predicted.eq(labels).sum().item() accuracy 100. * correct / total print(f\n测试准确率: {accuracy:.2f}%) return accuracy # 运行完整流程 def run_complete_pipeline(): 运行完整的无标签数据微调流程 # 模拟数据 np.random.seed(42) # 无标签数据大量 unlabeled_texts [ fSample domain text number {i} with various technical terms. for i in range(200) ] # 有标签数据少量 labeled_texts [fLabeled sample {i} for classification. for i in range(50)] labeled_labels [i % 2 for i in range(50)] # 测试数据 test_texts [fTest sample {i}. for i in range(20)] test_labels [i % 2 for i in range(20)] # 初始化 finetuner BertUnlabeledFineTuner( model_namebert-base-uncased, num_labels2, max_length64, # 演示用小长度 ) # 步骤 1: DAPT在无标签数据上继续预训练 # finetuner.step1_domain_adaptive_pretraining(unlabeled_texts, epochs2) # 步骤 2: 有标签微调 # finetuner.step2_supervised_finetuning(labeled_texts, labeled_labels, epochs3) # 步骤 3: 伪标签训练 # finetuner.step3_pseudo_label_training( # labeled_texts, labeled_labels, unlabeled_texts, # confidence_threshold0.9, epochs2 # ) # 评估 # finetuner.evaluate(test_texts, test_labels) print(\n完整流程:) print(1. DAPT: 在无标签数据上继续 MLM 预训练) print(2. 监督微调: 在有标签数据上训练分类器) print(3. 伪标签: 用模型预测高置信度样本作为伪标签再次训练) print(4. 评估: 在测试集上评估最终模型) if __name__ __main__: run_complete_pipeline()六、常见陷阱与注意事项陷阱 1DAPT 训练过拟合# ❌ 在小数据集上训练太多 epoch # finetuner.step1_domain_adaptive_pretraining(unlabeled_texts, epochs50) # 过多 # ✓ 通常 2-5 个 epoch 足够 # finetuner.step1_domain_adaptive_pretraining(unlabeled_texts, epochs3)陷阱 2伪标签置信度阈值过低# ❌ 阈值过低引入大量错误伪标签 # trainer PseudoLabelTrainer(confidence_threshold0.5) # ✓ 使用高阈值0.9只保留高置信度预测 # trainer PseudoLabelTrainer(confidence_threshold0.95)陷阱 3忘记保存 DAPT 模型# ❌ DAPT 训练后直接加载原始 BERT 做分类 # model BertForSequenceClassification.from_pretrained(bert-base-uncased) # ✓ 使用 DAPT 后的模型 # model BertForSequenceClassification.from_pretrained(./dapt_bert)陷阱 4MLM 的 mask 策略不正确# ❌ mask 了特殊 token[CLS], [SEP], [PAD] # masked_indices torch.bernoulli(probability_matrix).bool() # 没有排除特殊 token # ✓ 排除特殊 token # special_tokens_mask get_special_tokens_mask(...) # probability_matrix.masked_fill_(special_tokens_mask, value0.0)陷阱 5学习率设置不当# DAPT 和监督微调需要不同的学习率 # DAPT: 5e-5较大因为只是继续预训练 # 监督微调: 2e-5较小避免破坏预训练知识 # 伪标签训练: 1e-5更小因为已有基础七、总结策略适用场景优点缺点DAPT领域差异大效果最好计算成本高TAPT任务特定提升针对性强需要任务相关数据伪标签有少量标注简单易行可能引入噪声一致性正则化半监督学习鲁棒性好实现复杂推荐流程1. 收集领域无标签数据 2. DAPT: 在无标签数据上继续 MLM 预训练2-5 epochs 3. 监督微调: 在有标签数据上训练分类器3-5 epochs 4. 伪标签: 生成高置信度伪标签阈值 ≥ 0.9 5. 混合训练: 有标签 伪标签数据联合训练 6. 评估: 在独立测试集上评估核心原则无标签数据的价值在于让模型理解领域语言而非直接提供监督信号。DAPT 是利用无标签数据最有效的方式伪标签是锦上添花的补充手段。通过合理利用无标签数据可以在标注数据有限的情况下显著提升 BERT 模型的性能这在实际业务场景中具有极高的实用价值。

相关新闻

OSPF区域外路由计算与FA转发地址机制深度解析

OSPF区域外路由计算与FA转发地址机制深度解析

1. 从一个"路由表里看不到下一跳"的怪现象说起做网络这行的朋友,估计都遇到过这种让人挠头的场景:明明区域边界路由器已经把外部路由引入进来了,show ip route也能看到这条路由,但下一跳那一栏显示的却是一个"看起…

2026/10/17 8:50:26 阅读更多 →
【Bug已解决】How to use groups parameter in PyTorch conv2d function 解决方案

【Bug已解决】How to use groups parameter in PyTorch conv2d function 解决方案

【Bug已解决】How to use groups parameter in PyTorch conv2d function 解决方案问题标签:PyTorch、Conv2d、groups参数、分组卷积、深度可分离卷积适用版本:PyTorch 1.0 ~ 2.x难度等级:⭐⭐⭐(中等)一、问题描述 在 …

2026/10/17 8:50:26 阅读更多 →
2026 AIGC平台实战测评:从文本、图像到视频的降本增效选型指南

2026 AIGC平台实战测评:从文本、图像到视频的降本增效选型指南

2026年聊AIGC平台测评,这个标题最近在我后台被问炸了——不少朋友发的其实是“降”,大概是输入法把“聊”吞了。不过我倒是觉得“降”这个字误打误撞点到了要害:眼下的AI工具测评,真正该聊的就是“降本增效”。市面上的AIGC平台已…

2026/10/17 8:50:26 阅读更多 →

最新新闻

不完全信息Epsilon纳什均衡在航天器末端追逃博弈的Matlab实现

不完全信息Epsilon纳什均衡在航天器末端追逃博弈的Matlab实现

作为研究航天器近距离任务的开发者,我们组在复现不完全信息Epsilon纳什均衡的航天器末端追逃博弈策略(Matlab代码实现)这篇论文时,最大的感受是:以前觉得博弈论在航天里就是理论推推公式,真正要把“对方参数…

2026/10/17 9:27:38 阅读更多 →
企业Agent项目真实ROI怎么算?从成本结构到收益模型的完整拆解

企业Agent项目真实ROI怎么算?从成本结构到收益模型的完整拆解

这年头,谁家汇报材料里没放两个Agent流程Demo,都不好意思讲数字化。可真正把Agent放进生产流程,让它自己处理工单、回客户消息、跑财务核对的团队,远没有PPT里那么多;能把账算清楚的,更是少数中的少数。我说…

2026/10/17 9:27:38 阅读更多 →
Premiere Pro新手防崩溃指南:5个关键操作安全阀

Premiere Pro新手防崩溃指南:5个关键操作安全阀

1. 项目概述:这不是剪辑软件说明书,而是一份“PR上手防崩溃指南”“PR简单上手笔记”——光看这标题,你可能以为是某位刚考完Adobe认证的学员随手记下的快捷键清单,或者某培训机构发在群里的PDF速成课大纲。但在我带过二十多个零基…

2026/10/17 9:27:38 阅读更多 →
open-code-review:轻量级开源代码评审范式解析

open-code-review:轻量级开源代码评审范式解析

1. “open-code-review”不是工具名,而是一类新型代码评审范式的代号你可能在 GitHub Trending、Hacker News 或某次技术分享里见过这个词——它不像eslint或pre-commit那样有明确的安装命令,也不在 npm 或 PyPI 上搜得到可直接pip install open-code-re…

2026/10/17 9:27:38 阅读更多 →
电商图片工作流画布:节点式自动化生产与批量处理实战

电商图片工作流画布:节点式自动化生产与批量处理实战

1. 电商图片制作的痛点与工作流画布的破局思路做电商这行的朋友应该都有体会,图片制作这件事看起来简单,实际上是个无底洞。一个SKU从拍摄到上架,中间要经历主图、详情页、场景图、白底图、尺寸标注图、卖点图、对比图、包装图等至少七八种图…

2026/10/17 9:27:38 阅读更多 →
macOS上用Docker部署nginx+php:从零搭建容器化开发环境

macOS上用Docker部署nginx+php:从零搭建容器化开发环境

你在macOS上装过nginx和php吗?如果装过,大概率体会过那种“装起来半小时、调起来一晚上”的滋味。今天这篇我就带你完整走一遍macOS上安装Docker,然后在Docker里部署一套nginxphp环境的过程,整个过程不追求什么高大上的编排技巧&a…

2026/10/17 9:26:37 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →