【Bug已解决】How to fine tune BERT on unlabeled data? 解决方案

发布时间:2026/10/17 8:50:06

【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 模型的性能这在实际业务场景中具有极高的实用价值。
延伸阅读

更多相关文章

2026/10/17 8:50:06

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

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

2026/10/17 8:45:06

XblGameSaveTask.exe是什么?别乱下载!系统组件识别与修复全攻略

先说结论:XblGameSaveTask.exe 不是一款需要单独下载的第三方软件,它是 Windows 系统里游戏存档同步服务的一部分。前段时间有朋友拿着一张截图来找我,说启动器提示找不到 XblGameSaveTask.exe,网上搜了一圈全是“高速下载”链接&…

2026/10/17 8:45:06

wuauclt.exe丢失怎么办?安全修复Windows更新组件指南

很多人在系统更新遇到问题、或者不小心删除了系统文件之后,都会跑去搜索引擎找"wuauclt.exe下载"。我当初第一次遇到这个需求时,也是在各种下载站里翻了个遍,结果不是捆绑安装包就是来路不明的压缩包,根本不敢用。这个文…

2026/10/17 13:55:29

数学建模竞赛备赛全攻略:从模型库到论文交付的完整知识地图

简介:面向全国大学生数学建模竞赛选手与培训者的系统性学习包,内容涵盖数模培训讲义、历年赛题解读、优秀获奖论文及可直接运行的MATLAB源代码。资料从概率统计、优化方法等基础理论入手,结合蒙特卡洛模拟、神经网络、微分方程建模等常见方法…

2026/10/17 13:55:29

LeetCode 61 旋转链表:思路演进、代码实现与高频踩坑点

在很多算法训练手册里,链表题往往被归为“入门容易,精通难”的那一类。链表本身不复杂,无非是节点结构和指针操作,但一旦涉及边界条件、指针移动步数、循环与取模,许多人就开始头大。LeetCode 61题“旋转链表”恰好就是…

2026/10/17 13:55:29

基于Scrapy与Django的二手房数据采集、可视化与房价预测系统

1. 项目拆解与整体方案设计 1.1 我为什么选择做这个项目 这两年数据分析岗位的竞争越来越激烈,简历上光写“我会Python”“我熟悉SQL”已经没什么说服力了。我当时的想法是:要做就做一个 有真实数据来源、有完整业务闭环、能展示工程能力 的项目。房地…

2026/10/17 13:55:29

Kafka Producer Sender线程深度解析:核心机制与调优实战

1. Kafka Sender线程的核心定位与设计哲学1.1 为什么Sender线程是Kafka Producer的“心脏”很多刚接触Kafka Producer的同学,写代码时就是调一个producer.send(record),然后要么不管,要么调一下get()同步等结果。代码能跑通,但一旦…

2026/10/17 13:50:29

虚拟存储器课后题精讲:地址转换、缺页处理与页面置换算法实战

1. 虚拟存储器到底在解决什么问题1.1 从一道课后题说起:为什么需要虚拟存储器2022版操作系统教材里关于虚拟存储器的课后题,我印象最深的是那道“某进程访问逻辑地址0x0000,页表项中有效位为0,会发生什么”的题目。很多同学第一反…

2026/10/16 5:33:43

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/16 4:03:48

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/15 13:42:16

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/17 0:04:32

PlanarSLAM:面向曼哈顿世界的点线面联合SLAM系统

简介:本资源是面向机器人视觉与SLAM研究者的C开源RGB-D SLAM系统实现,专为结构化室内场景(如办公室、走廊)设计,通过融合点、线、平面三类几何特征并引入“曼哈顿世界”假设,显著提升位姿跟踪鲁棒性与稠密地…

2026/10/17 0:04:32

TDH Workflow调度实战:从DAG依赖驱动到任务编排与故障排查

1. 从一个调度脚本说起:TDH Workflow到底在解决什么问题第一次接触TDH的Workflow,是在一个数据清洗项目里。当时团队每天要跑几十个ETL任务,任务之间有严格的先后依赖——A表清洗完才能跑B表的关联,B表跑完才能触发C表的聚合。最开…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑