Day 27:迁移学习与微调 — 站在巨人的肩膀上

今日目标 :掌握迁移学习的两种范式、冻结策略选择、用预训练模型在10分钟内训练一个高精度分类器

预计阅读 :10分钟 | 动手操作:40分钟


一、迁移学习:90%的AI项目都在用

markdown 复制代码
从零训练一个ResNet-50(ImageNet):
  - 需要120万张图片
  - 需要8张V100训练2-3天
  - 需要大量调参经验

迁移学习:
  - 下载预训练权重(1分钟)
  - 在你自己的几百张图微调(10分钟)
  - 就能达到不错的准确率

这就是站在巨人肩膀上的力量!
python 复制代码
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset
from torchvision import datasets, transforms, models
import numpy as np
from collections import OrderedDict

torch.manual_seed(42)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

二、核心知识点

2.1 两种范式:特征提取 vs 微调

python 复制代码
"""
迁移学习的两大范式:

┌────────────────┬─────────────────────┬─────────────────────┐
│                │  特征提取            │  微调               │
│                │  (Feature Extraction)│  (Fine-tuning)      │
├────────────────┼─────────────────────┼─────────────────────┤
│ 怎么做          │ 冻结backbone,       │ 解冻backbone,       │
│                │ 只训练分类头          │ 全部参数参与训练      │
│ 适用场景        │ 数据少(<1000张)    │ 数据多(>1000张)    │
│                │ 和预训练任务相似      │ 和预训练任务有差异    │
│ 训练速度        │ 快(只训练几层)     │ 慢(训练全部参数)   │
│ 过拟合风险      │ 低                   │ 中等                │
│ 准确率上限      │ 中等                 │ 高                  │
└────────────────┴─────────────────────┴─────────────────────┘

端侧AI推荐:
  1. 先做特征提取(快,baseline)
  2. 如果准确率不够,再做微调(提升上限)
  3. 数据量决定解冻多少层
"""

2.2 冻结层的基础操作

python 复制代码
def freeze_unfreeze_demo():
    """演示冻结和解冻层的各种操作"""
    model = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)
    
    # ===== 方法1:冻结所有层 =====
    for param in model.parameters():
        param.requires_grad = False
    
    # 只训练最后的全连接层
    model.fc = nn.Linear(512, 10)  # 新的分类头,默认requires_grad=True
    
    trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
    total = sum(p.numel() for p in model.parameters())
    print(f"特征提取模式: {trainable:,}/{total:,} 参数可训练 ({trainable/total*100:.1f}%)")
    
    # ===== 方法2:只解冻最后几层 =====
    model2 = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)
    
    # 先全部冻结
    for param in model2.parameters():
        param.requires_grad = False
    
    # 解冻layer4和fc
    for param in model2.layer4.parameters():
        param.requires_grad = True
    model2.fc = nn.Linear(512, 10)
    
    trainable = sum(p.numel() for p in model2.parameters() if p.requires_grad)
    total = sum(p.numel() for p in model2.parameters())
    print(f"部分解冻模式: {trainable:,}/{total:,} 参数可训练 ({trainable/total*100:.1f}%)")
    
    # ===== 方法3:全部解冻(完整微调)=====
    model3 = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)
    model3.fc = nn.Linear(512, 10)
    
    trainable = sum(p.numel() for p in model3.parameters() if p.requires_grad)
    total = sum(p.numel() for p in model3.parameters())
    print(f"完整微调模式: {trainable:,}/{total:,} 参数可训练 ({trainable/total*100:.1f}%)")

freeze_unfreeze_demo()

2.3 冻结策略:解冻多少层?

python 复制代码
def demonstrate_freeze_strategies():
    """
    三种冻结策略,根据数据量选择
    
    数据量和冻结策略的关系:
    ┌──────────────────┬──────────────────────┬──────────────────┐
    │   数据量          │   推荐策略            │   解冻范围        │
    ├──────────────────┼──────────────────────┼──────────────────┤
    │ < 100张          │ 特征提取              │ 仅fc层           │
    │ 100-1000张       │ 浅层冻结              │ layer4 + fc      │
    │ 1000-10000张     │ 逐层解冻 + 差分lr     │ layer3/4 + fc    │
    │ > 10000张        │ 完整微调              │ 全部             │
    └──────────────────┴──────────────────────┴──────────────────┘
    """
    
    model = models.resnet50(weights=models.ResNet50_Weights.DEFAULT)
    
    # 策略1:特征提取(最少数据)
    def freeze_all_except_fc(model, num_classes):
        for param in model.parameters():
            param.requires_grad = False
        model.fc = nn.Linear(model.fc.in_features, num_classes)
        return model
    
    # 策略2:逐层解冻(中等数据)
    def gradual_unfreeze(model, num_classes, unfreeze_from=4):
        """从第unfreeze_from层开始解冻"""
        for param in model.parameters():
            param.requires_grad = False
        
        # ResNet的层级结构
        layer_groups = [
            ['conv1', 'bn1'],           # 0
            ['layer1'],                  # 1
            ['layer2'],                  # 2
            ['layer3'],                  # 3
            ['layer4'],                  # 4
        ]
        
        for group in layer_groups[unfreeze_from:]:
            for name in group:
                for param in getattr(model, name).parameters():
                    param.requires_grad = True
        
        model.fc = nn.Linear(model.fc.in_features, num_classes)
        return model
    
    # 策略3:差分学习率(较多数据)
    def discriminative_lr(model, num_classes, lr_mult=0.1):
        """
        不同层用不同的学习率
        浅层(通用特征)→ 小学习率
        深层(任务相关)→ 大学习率
        """
        for param in model.parameters():
            param.requires_grad = True
        model.fc = nn.Linear(model.fc.in_features, num_classes)
        
        # 分组设置不同学习率
        param_groups = [
            {'params': model.conv1.parameters(), 'lr': 1e-4 * lr_mult},
            {'params': model.bn1.parameters(), 'lr': 1e-4 * lr_mult},
            {'params': model.layer1.parameters(), 'lr': 1e-4 * lr_mult},
            {'params': model.layer2.parameters(), 'lr': 1e-4 * lr_mult * 2},
            {'params': model.layer3.parameters(), 'lr': 1e-4 * lr_mult * 5},
            {'params': model.layer4.parameters(), 'lr': 1e-4},
            {'params': model.fc.parameters(), 'lr': 1e-3},
        ]
        
        return model, param_groups
    
    print("冻结策略选择:")
    print("  < 100张:  冻结全部,只训练fc")
    print("  100-1k张: 解冻layer4 + fc")
    print("  1k-10k张: 逐层解冻 + 差分学习率")
    print("  > 10k张:  全部解冻,完整微调")

demonstrate_freeze_strategies()

2.4 预训练模型加载

python 复制代码
def load_pretrained_models():
    """演示各种预训练模型加载方式"""
    
    # ===== 1. torchvision自带模型(最常用)=====
    # ResNet系列
    resnet18 = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)
    resnet50 = models.resnet50(weights=models.ResNet50_Weights.DEFAULT)
    
    # EfficientNet
    efficientnet = models.efficientnet_b0(weights=models.EfficientNet_B0_Weights.DEFAULT)
    
    # MobileNet(端侧AI常用)
    mobilenet_v3_small = models.mobilenet_v3_small(weights=models.MobileNet_V3_Small_Weights.DEFAULT)
    mobilenet_v3_large = models.mobilenet_v3_large(weights=models.MobileNet_V3_Large_Weights.DEFAULT)
    
    # Vision Transformer
    vit = models.vit_b_16(weights=models.ViT_B_16_Weights.DEFAULT)
    
    # ===== 2. 加载本地权重文件 =====
    # model = models.resnet50()
    # model.load_state_dict(torch.load('path/to/weights.pth'))
    
    # ===== 3. 只加载部分权重(不匹配的层跳过)=====
    # pretrained_dict = torch.load('weights.pth')
    # model_dict = model.state_dict()
    # pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict}
    # model_dict.update(pretrained_dict)
    # model.load_state_dict(model_dict)
    
    # ===== 4. 加载不同输入尺寸的模型 =====
    # 原始ImageNet: 224×224
    # 你的数据: 64×64
    # 只需要改第一层卷积的stride!
    # model = models.resnet18(weights=None)
    # model.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)
    # model.maxpool = nn.Identity()
    # 然后加载预训练权重(conv1部分跳过)
    
    models_info = {
        'ResNet-18': (sum(p.numel() for p in resnet18.parameters()), 'CNN'),
        'ResNet-50': (sum(p.numel() for p in resnet50.parameters()), 'CNN'),
        'EfficientNet-B0': (sum(p.numel() for p in efficientnet.parameters()), 'CNN'),
        'MobileNetV3-Small': (sum(p.numel() for p in mobilenet_v3_small.parameters()), '端侧CNN'),
        'MobileNetV3-Large': (sum(p.numel() for p in mobilenet_v3_large.parameters()), '端侧CNN'),
        'ViT-B/16': (sum(p.numel() for p in vit.parameters()), 'Transformer'),
    }
    
    print("常用预训练模型:")
    for name, (params, arch_type) in models_info.items():
        print(f"  {name:<20s}: {params:>12,} 参数 ({arch_type})")

load_pretrained_models()

三、动手实践

3.1 实战:完整迁移学习Pipeline

python 复制代码
class TransferLearningPipeline:
    """
    完整的迁移学习Pipeline
    
    支持三种模式:
    1. feature_extraction: 冻结backbone,只训练分类头
    2. partial_finetune: 解冻最后几层
    3. full_finetune: 训练全部参数
    """
    def __init__(self, model_name='resnet50', num_classes=10, 
                 mode='feature_extraction', device='cuda'):
        self.device = torch.device(device if torch.cuda.is_available() else 'cpu')
        self.mode = mode
        self.num_classes = num_classes
        self.model = self._build_model(model_name)
        self.best_acc = 0
    
    def _build_model(self, model_name):
        """构建模型并设置训练模式"""
        # 加载预训练模型
        model = models.resnet50(weights=models.ResNet50_Weights.DEFAULT)
        
        # 获取预训练时的预处理参数
        self.preprocess_mean = [0.485, 0.456, 0.406]
        self.preprocess_std = [0.229, 0.224, 0.225]
        
        if self.mode == 'feature_extraction':
            # 冻结所有层
            for param in model.parameters():
                param.requires_grad = False
            model.fc = nn.Linear(model.fc.in_features, self.num_classes)
            
        elif self.mode == 'partial_finetune':
            # 冻结前3个stage
            for param in model.parameters():
                param.requires_grad = False
            for param in model.layer4.parameters():
                param.requires_grad = True
            model.fc = nn.Linear(model.fc.in_features, self.num_classes)
            
        elif self.mode == 'full_finetune':
            model.fc = nn.Linear(model.fc.in_features, self.num_classes)
        
        return model.to(self.device)
    
    def get_optimizer(self, lr=0.001):
        """根据模式选择优化器"""
        if self.mode == 'feature_extraction':
            # 只训练fc,可以用大学习率
            return optim.AdamW(self.model.fc.parameters(), lr=lr, weight_decay=1e-4)
        
        elif self.mode == 'partial_finetune':
            # 差分学习率
            params = [
                {'params': self.model.layer4.parameters(), 'lr': lr * 0.1},
                {'params': self.model.fc.parameters(), 'lr': lr},
            ]
            return optim.AdamW(params, weight_decay=1e-4)
        
        else:  # full_finetune
            # 差分学习率:backbone小lr,fc大lr
            backbone_params = []
            fc_params = []
            for name, param in self.model.named_parameters():
                if 'fc' in name:
                    fc_params.append(param)
                else:
                    backbone_params.append(param)
            
            params = [
                {'params': backbone_params, 'lr': lr * 0.1},
                {'params': fc_params, 'lr': lr},
            ]
            return optim.AdamW(params, weight_decay=1e-4)
    
    def train_one_epoch(self, loader, optimizer, criterion):
        self.model.train()
        total_loss, correct, total = 0, 0, 0
        
        for x, y in loader:
            x, y = x.to(self.device), y.to(self.device)
            optimizer.zero_grad()
            loss = criterion(self.model(x), y)
            loss.backward()
            optimizer.step()
            
            total_loss += loss.item() * x.size(0)
            correct += (self.model(x).argmax(1) == y).sum().item()
            total += y.size(0)
        
        return total_loss / total, correct / total
    
    @torch.no_grad()
    def evaluate(self, loader, criterion):
        self.model.eval()
        total_loss, correct, total = 0, 0, 0
        
        for x, y in loader:
            x, y = x.to(self.device), y.to(self.device)
            output = self.model(x)
            loss = criterion(output, y)
            total_loss += loss.item() * x.size(0)
            correct += output.argmax(1).eq(y).sum().item()
            total += y.size(0)
        
        return total_loss / total, correct / total
    
    def fit(self, train_loader, val_loader, epochs=20):
        optimizer = self.get_optimizer()
        scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=epochs)
        criterion = nn.CrossEntropyLoss()
        
        # 打印训练信息
        trainable = sum(p.numel() for p in self.model.parameters() if p.requires_grad)
        total = sum(p.numel() for p in self.model.parameters())
        print(f"模式: {self.mode}")
        print(f"可训练参数: {trainable:,}/{total:,} ({trainable/total*100:.1f}%)")
        print(f"{'Epoch':<6s} {'Train Loss':<12s} {'Train Acc':<10s} {'Val Loss':<12s} {'Val Acc':<10s}")
        print('-' * 55)
        
        for epoch in range(epochs):
            train_loss, train_acc = self.train_one_epoch(train_loader, optimizer, criterion)
            val_loss, val_acc = self.evaluate(val_loader, criterion)
            scheduler.step()
            
            if val_acc > self.best_acc:
                self.best_acc = val_acc
                torch.save(self.model.state_dict(), 'best_transfer_model.pth')
            
            if epoch % 5 == 0:
                print(f"{epoch+1:<6d} {train_loss:<12.4f} {train_acc:<10.2%} "
                      f"{val_loss:<12.4f} {val_acc:<10.2%}")
        
        print(f"\n最佳准确率: {self.best_acc:.2%}")
        self.model.load_state_dict(torch.load('best_transfer_model.pth'))
        return self.model


# 演示:在CIFAR-10上迁移学习
def demo_transfer_learning():
    """对比三种迁移学习模式"""
    # 数据准备
    train_transform = transforms.Compose([
        transforms.Resize(224),  # ImageNet预训练输入是224×224
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
    ])
    
    val_transform = transforms.Compose([
        transforms.Resize(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
    ])
    
    train_dataset = datasets.CIFAR10('./data', train=True, download=True, transform=train_transform)
    val_dataset = datasets.CIFAR10('./data', train=False, transform=val_transform)
    
    # 只用少量数据模拟真实场景
    indices = np.random.choice(len(train_dataset), 2000, replace=False)
    train_subset = torch.utils.data.Subset(train_dataset, indices)
    
    train_loader = DataLoader(train_subset, batch_size=64, shuffle=True)
    val_loader = DataLoader(val_dataset, batch_size=64, shuffle=False)
    
    modes = ['feature_extraction', 'partial_finetune', 'full_finetune']
    
    for mode in modes:
        print(f"\n{'='*50}")
        print(f"模式: {mode}")
        print(f"{'='*50}")
        pipeline = TransferLearningPipeline(
            model_name='resnet50', num_classes=10, mode=mode, device='cuda'
        )
        pipeline.fit(train_loader, val_loader, epochs=20)

# 运行(可选,需要GPU)
# demo_transfer_learning()

3.2 实战:自定义数据集迁移学习

python 复制代码
def custom_dataset_transfer():
    """
    模板:在自己的数据集上做迁移学习
    
    假设你的数据是这样的:
    data/
    ├── train/
    │   ├── class_0/
    │   │   ├── img001.jpg
    │   │   └── ...
    │   ├── class_1/
    │   └── ...
    └── val/
        ├── class_0/
        └── ...
    """
    # 1. 数据加载
    data_dir = './data/my_dataset'
    train_transform = transforms.Compose([
        transforms.Resize(256),
        transforms.RandomCrop(224),
        transforms.RandomHorizontalFlip(),
        transforms.ColorJitter(0.1, 0.1, 0.1, 0.05),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
    ])
    
    val_transform = transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
    ])
    
    # 2. 加载预训练模型
    model = models.resnet50(weights=models.ResNet50_Weights.DEFAULT)
    
    # 3. 获取数据集的类别数
    # num_classes = len(os.listdir(data_dir + '/train'))
    num_classes = 10  # 示例
    
    # 4. 替换分类头
    model.fc = nn.Sequential(
        nn.Linear(model.fc.in_features, 512),
        nn.ReLU(),
        nn.Dropout(0.3),
        nn.Linear(512, num_classes),
    )
    
    # 5. 冻结策略
    print("自定义数据集迁移学习模板:")
    print("  1. 数据路径: ImageFolder格式")
    print("  2. 预处理: Resize → CenterCrop → Normalize (ImageNet统计量)")
    print("  3. 分类头: 替换fc层,可以加Dropout")
    print("  4. 训练: 差分学习率 + AdamW + CosineAnnealing")
    print("  5. 保存: 只保存state_dict,不保存整个模型")
    
    return model

custom_dataset_transfer()

3.3 实战:端侧模型迁移学习

python 复制代码
def edge_transfer_learning():
    """
    端侧AI的迁移学习策略
    
    与服务器端的不同:
    1. 选轻量模型:MobileNetV3/EfficientNet-lite/ShuffleNet
    2. 考虑量化:训练时做QAT(量化感知训练)
    3. 输入尺寸:不一定是224×224,端侧更小
    4. 推理速度优先:准确率差不多就行
    """
    # 端侧推荐模型
    edge_models = {
        'MobileNetV3-Small': models.mobilenet_v3_small,
        'MobileNetV3-Large': models.mobilenet_v3_large,
        'EfficientNet-B0': models.efficientnet_b0,
        'ShuffleNetV2': models.shufflenet_v2_x1_0,
    }
    
    print("端侧AI迁移学习推荐:")
    for name, model_fn in edge_models.items():
        model = model_fn(weights='DEFAULT')
        params = sum(p.numel() for p in model.parameters())
        print(f"  {name:<20s}: {params:>10,} 参数")
    
    print("\n端侧迁移学习要点:")
    print("  1. 选MobileNet/EfficientNet,不要用ResNet-50")
    print("  2. 输入尺寸可以缩小到160×160或128×128")
    print("  3. 如果有数据,完整微调效果更好")
    print("  4. 训练后考虑量化,进一步减小模型")
    print("  5. 用ONNX导出,方便端侧部署")

edge_transfer_learning()

四、迁移学习黄金法则

python 复制代码
"""
迁移学习黄金法则:

1. 数据量决定策略
   - 数据越少,冻结越多
   - 数据越多,解冻越多

2. 相似度决定学习率
   - 和预训练任务越相似,backbone学习率越小
   - 和预训练任务差异越大,backbone学习率越大

3. 分类头学习率是backbone的10倍
   - backbone: lr=1e-4
   - fc: lr=1e-3

4. 先用小学习率,看loss趋势
   - 如果loss下降慢 → 提高学习率
   - 如果loss震荡 → 降低学习率

5. 模型保存只保存state_dict
   - 不要保存整个模型(依赖代码结构)
   - 保存时带上类别数和预处理参数

6. 端侧AI的特殊考虑
   - 优先选轻量backbone
   - 考虑量化部署
   - 输入尺寸可以缩小
"""

五、常见坑点

坑1:忘记用预训练模型的预处理

python 复制代码
# ❌ 错误:用了预训练模型,但数据没做ImageNet归一化
# preprocess = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])

# ✅ 正确:必须用和预训练时相同的归一化
# preprocess = transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])

坑2:换了分类头但忘记初始化

python 复制代码
# ✅ 好的做法:显式初始化新添加的层
model.fc = nn.Linear(512, 10)
nn.init.kaiming_normal_(model.fc.weight)
nn.init.zeros_(model.fc.bias)
# PyTorch的Linear默认就是Kaiming初始化,但显式写更安全

坑3:冻结所有层后分类头没设requires_grad

python 复制代码
# ❌ 错误顺序
for param in model.parameters():
    param.requires_grad = False
model.fc = nn.Linear(512, 10)  # 新层默认requires_grad=True ✓

# 但如果是先改fc再冻结:
model.fc = nn.Linear(512, 10)
for param in model.parameters():
    param.requires_grad = False  # ❌ fc也被冻结了!

坑4:小数据集上完整微调

python 复制代码
# 如果你只有200张图,完整微调ResNet-50 → 严重过拟合
# 200张图 → 特征提取模式(只训练fc)
# 2000张图 → 可以解冻layer4
# 20000张图 → 完整微调

六、今日作业

  1. 特征提取练习:用ResNet-18做特征提取,在CIFAR-10上训练最后一个fc层,记录准确率
  2. 冻结策略对比:对比"仅训练fc" vs "解冻layer4+fc" vs "全部微调",记录准确率和训练时间
  3. 端侧思考:你的端侧AI项目适合用哪个预训练模型?为什么?
  4. 打卡 :评论区发你的对比结果,格式:"Day 27/100 打卡:迁移学习已掌握!"

今日小结

复制代码
今天你学会了:
✅ 迁移学习两大范式:特征提取 vs 微调
✅ 冻结/解冻层的基础操作
✅ 三种冻结策略:全部冻结/逐层解冻/差分学习率
✅ 数据量决定策略(<100张→100-1k→1k-10k→>10k)
✅ 常用预训练模型加载(ResNet/EfficientNet/MobileNet/ViT)
✅ 完整TransferLearningPipeline
✅ 自定义数据集迁移学习模板
✅ 端侧AI迁移学习策略
✅ 6条黄金法则 + 4个经典坑点

明日预告

Day 28:目标检测入门 --- 两阶段vs单阶段

R-CNN系列、YOLO系列、SSD、Anchor机制、NMS、mAP


🔥 关注我,每天解锁一个端侧AI技能!

微信公众号:xxx | 小红书:xxx | CSDN:xxx

评论区打卡,一起坚持100天!


附:小红书图文版

封面标题建议:迁移学习 | 10分钟训练高精度模型 🎯

P1 --- 封面

标题:迁移学习与微调

副标题:特征提取 / 微调 / 冻结策略

关键词:迁移学习 / Fine-tuning / 预训练

P2 --- 两大范式

特征提取:冻结backbone,只训练分类头

→ 数据少,速度快,过拟合少

微调:解冻backbone,全部训练

→ 数据多,准确率高,上限高

P3 --- 冻结策略选择

<100张: 只训练fc

100-1k张: 解冻layer4

1k-10k张: 逐层解冻+差分lr

10k张: 完整微调

P4 --- 差分学习率

backbone: lr=1e-4 (小)

fc: lr=1e-3 (大)

浅层更小,深层更大

通用特征→小lr,任务相关→大lr

P5 --- 端侧AI迁移学习

选MobileNet/EfficientNet,不用ResNet-50

输入可以缩小到160×160

训练后考虑量化

ONNX导出方便部署

P6 --- 今日作业

冻结策略对比实验

评论区打卡 Day 27/100

标签:#迁移学习 #FineTuning #预训练模型 #深度学习


CSDN发布提示:CSDN版本建议在两大范式部分放对比图,在冻结策略部分放ResNet层级结构图,在差分学习率部分放不同层学习率分配的示意图。

相关推荐
happyprince1 小时前
03-深刻观-CodeX哲学与升华(源码)
算法·ai编程
数模竞赛Paid answer2 小时前
2025年中青杯数学建模A题康养城市建设求解全过程论文及程序
算法·数学建模·数据分析·中青杯
网安蟹佬霸2 小时前
密码学安全实战:从加密原理到哈希破解的完整攻防指南
网络·算法·安全·web安全·开源·密码学·哈希算法
yangmu32032 小时前
深度解析:短视频是如何通过“算法+神经机制”劫持用户时间的?
算法
LuminousCPP3 小时前
栈和队列专题(四):LeetCode 232. 用栈实现队列|双栈分工 + 按需迁移 + 摊还 O(1)
c语言·数据结构·笔记·算法·leetcode
tudousisi2223 小时前
01背包8.21
数据结构·算法
ZJU_统一阿萨姆3 小时前
【算子开发】Reduction算子完全指南
人工智能·算法·语言模型
鹿角片ljp12 小时前
LeetCode 46. 全排列|吃透回溯
算法·leetcode·职场和发展
鼎艺创新科技12 小时前
不依赖 UE/Unity:我们如何从零搭建一套国产三维 GIS 渲染引擎
人工智能·算法·unity·游戏引擎·三维电子沙盘