混合模型:监督 + 无监督学习(机器学习与深度学习)

原理速览

  • 什么是混合模型:在同一个学习框架中同时利用有标签数据(监督)无标签数据(无监督),提升模型在标签稀少时的泛化能力。
  • 核心动机:现实场景中标注数据昂贵,未标注数据大量存在,仅靠监督学习容易过拟合小样本;仅用无监督又难以完成分类/回归等目标任务。
  • 主要范式
    • 半监督学习(Semi‑supervised Learning):用少量标签 + 大量无标签数据共同训练,如自训练、协同训练、标签传播、一致性正则化。
    • 自监督学习(Self‑supervised Learning):从无标签数据中构造伪标签任务,学得通用表示后再微调,如对比学习、掩码预测。
    • 深度聚类 + 分类联合:将聚类损失与分类损失组合,让特征既可分又能形成有意义的簇。
  • 典型算法(传统机器学习):Label Propagation、Label Spreading、Self‑training with classifiers。
  • 典型结构(深度学习):自编码器 + 分类头联合训练、Π‑Model、Mean Teacher、MixMatch 等。
  • 应用场景:医学图像分割(少量标注+大量未标注)、文本分类(少样本+大规模无标签语料)、异常检测与开集识别等。

案例一:半监督分类 — Label Spreading 与自训练(传统机器学习)

任务:使用鸢尾花数据集,随机隐藏大部分标签,利用 Label Spreading(标签传播)和 Self‑training(自训练)两种混合策略提升分类准确率。

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.semi_supervised import LabelSpreading
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

# -------------------- 1. 加载数据 --------------------
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 划分训练/测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 标准化特征(很多半监督方法对尺度敏感)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

# -------------------- 2. 模拟少量有标签 + 大量无标签 --------------------
rng = np.random.RandomState(42)
# 随机选取 80% 的训练样本标签隐藏(设为 -1)
unlabeled_mask = rng.rand(len(y_train)) < 0.8
y_train_mixed = np.copy(y_train)
y_train_mixed[unlabeled_mask] = -1   # -1 表示无标签

print(f"已标注样本数:{np.sum(~unlabeled_mask)},未标注样本数:{np.sum(unlabeled_mask)}")

# -------------------- 3. 方法一:Label Spreading(标签传播)--------------------
lp_model = LabelSpreading(kernel='rbf', gamma=20, alpha=0.8)
lp_model.fit(X_train, y_train_mixed)                     # 输入包含 -1 的标签
y_pred_lp = lp_model.predict(X_test)
acc_lp = accuracy_score(y_test, y_pred_lp)

# 打印传播后的伪标签覆盖率
propagated_labels = lp_model.transduction_
print(f"Label Spreading 准确率:{acc_lp:.4f}")

# -------------------- 4. 方法二:自训练(Self‑training)--------------------
# 使用逻辑回归作为基分类器,通过迭代伪标签实现半监督
base_clf = LogisticRegression(multi_class='ovr', max_iter=200)

# 先用已标注数据训练初始模型
labeled_idx = ~unlabeled_mask
X_labeled = X_train[labeled_idx]
y_labeled = y_train[labeled_idx]
base_clf.fit(X_labeled, y_labeled)

# 自训练迭代
n_iter = 10
threshold = 0.9   # 置信度阈值,高于此值才赋予伪标签
y_train_self = np.copy(y_train_mixed)

for iteration in range(n_iter):
    # 找出当前无标签样本
    unlabeled_idx = (y_train_self == -1)
    if np.sum(unlabeled_idx) == 0:
        break
    
    # 预测无标签样本的概率
    proba = base_clf.predict_proba(X_train[unlabeled_idx])
    max_proba = np.max(proba, axis=1)
    pseudo_labels = np.argmax(proba, axis=1)
    
    # 选择高置信度样本赋予伪标签
    confident_idx = max_proba > threshold
    if np.sum(confident_idx) == 0:
        break
    
    # 更新标签
    unlabeled_indices = np.where(unlabeled_idx)[0]
    confident_indices = unlabeled_indices[confident_idx]
    y_train_self[confident_indices] = pseudo_labels[confident_idx]
    
    # 用扩展后的训练集重新训练
    current_labeled = (y_train_self != -1)
    base_clf.fit(X_train[current_labeled], y_train_self[current_labeled])

y_pred_self = base_clf.predict(X_test)
acc_self = accuracy_score(y_test, y_pred_self)

# 仅用已标注样本训练的监督基线
baseline_clf = LogisticRegression(multi_class='ovr', max_iter=200)
baseline_clf.fit(X_labeled, y_labeled)
y_pred_base = baseline_clf.predict(X_test)
acc_base = accuracy_score(y_test, y_pred_base)

# -------------------- 5. 结果比较 --------------------
print("\n====== 准确率比较 ======")
print(f"仅用少量标注(监督基线):{acc_base:.4f}")
print(f"Label Spreading 半监督:{acc_lp:.4f}")
print(f"自训练(Self‑training):{acc_self:.4f}")

# 可视化
methods = ['Supervised (few labels)', 'Label Spreading', 'Self-training']
accs = [acc_base, acc_lp, acc_self]
plt.bar(methods, accs, color=['gray', 'skyblue', 'lightgreen'])
plt.ylim(0, 1)
plt.ylabel('Accuracy')
plt.title('Semi-supervised Learning Comparison')
for i, v in enumerate(accs):
    plt.text(i, v + 0.01, f'{v:.3f}', ha='center')
plt.show()

案例二:自编码器 + 分类器联合训练(深度学习) 任务:在 MNIST 数据集上,只用少量标签,结合重建损失(无监督)与分类损失(监督)共同训练,学习更好的表示。

python
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torch.utils.data import DataLoader, Subset
from torchvision import datasets, transforms
import numpy as np
import matplotlib.pyplot as plt

# -------------------- 1. 数据准备 --------------------
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])

train_dataset = datasets.MNIST('./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST('./data', train=False, transform=transform)

# 模拟少量有标签 + 大量无标签:从训练集中随机取 500 个标签保留,其余标签设置为 -1
n_total = len(train_dataset)
n_labeled = 500
indices = np.random.permutation(n_total)
labeled_idx = indices[:n_labeled]
unlabeled_idx = indices[n_labeled:]

# 我们将所有训练数据都参与无监督重建,但只有 labeled_idx 参与分类损失
# 在训练时需区分哪些样本有标签
# 采用自定义数据集标记是否有标签
train_targets = train_dataset.targets.clone()
train_targets[unlabeled_idx] = -1       # -1 表示无标签

# 构建带有目标掩码的数据集
from torch.utils.data import TensorDataset
train_data = TensorDataset(train_dataset.data.float().unsqueeze(1) / 255.0, train_targets)

# 使用所有数据训练(但监督损失仅计算有标签样本)
train_loader = DataLoader(train_data, batch_size=256, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=256, shuffle=False)

# -------------------- 2. 定义混合模型:卷积自编码器 + 分类头 --------------------
class SemiAutoEncoder(nn.Module):
    def __init__(self, latent_dim=64, num_classes=10):
        super().__init__()
        # 编码器(卷积 -> 全连接)
        self.encoder = nn.Sequential(
            nn.Conv2d(1, 16, 3, stride=2, padding=1),  # 28 -> 14
            nn.ReLU(),
            nn.Conv2d(16, 32, 3, stride=2, padding=1), # 14 -> 7
            nn.ReLU(),
            nn.Flatten(),
            nn.Linear(32 * 7 * 7, latent_dim),
            nn.ReLU()
        )
        # 解码器(全连接 + 转置卷积)
        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, 32 * 7 * 7),
            nn.ReLU(),
            nn.Unflatten(1, (32, 7, 7)),
            nn.ConvTranspose2d(32, 16, 3, stride=2, padding=1, output_padding=1), # 7 -> 14
            nn.ReLU(),
            nn.ConvTranspose2d(16, 1, 3, stride=2, padding=1, output_padding=1),  # 14 -> 28
            nn.Sigmoid()
        )
        # 分类头(从编码器输出分类)
        self.classifier = nn.Linear(latent_dim, num_classes)
    
    def forward(self, x):
        # x: (batch, 1, 28, 28)
        latent = self.encoder(x)
        recon = self.decoder(latent)
        logits = self.classifier(latent)
        return recon, logits

model = SemiAutoEncoder(latent_dim=64, num_classes=10)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)

# -------------------- 3. 训练:组合重建损失与分类损失 --------------------
recon_criterion = nn.MSELoss()            # 重建损失
class_criterion = nn.CrossEntropyLoss()   # 分类损失
optimizer = optim.Adam(model.parameters(), lr=0.001)

n_epochs = 20
lambda_recon = 1.0    # 重建损失权重
lambda_class = 1.0    # 分类损失权重(仅对有标签样本计算)

for epoch in range(n_epochs):
    model.train()
    total_recon_loss = 0.0
    total_class_loss = 0.0
    total_correct = 0
    n_labeled_total = 0
    
    for images, labels in train_loader:
        images = images.to(device)
        labels = labels.to(device)
        
        # 判断哪些样本有标签 (label != -1)
        mask_labeled = (labels != -1)
        
        # 前向传播
        recon, logits = model(images)
        
        # 重建损失(无监督,所有样本)
        loss_recon = recon_criterion(recon, images)
        
        # 分类损失(仅对有标签样本计算)
        loss_class = 0.0
        if mask_labeled.sum() > 0:
            # 提取有标签样本的 logits 和真实标签
            logits_labeled = logits[mask_labeled]
            labels_labeled = labels[mask_labeled]
            loss_class = class_criterion(logits_labeled, labels_labeled)
            
            # 记录准确率(仅统计有标签样本)
            _, preds = torch.max(logits_labeled, 1)
            total_correct += (preds == labels_labeled).sum().item()
            n_labeled_total += mask_labeled.sum().item()
        
        loss = lambda_recon * loss_recon + lambda_class * loss_class
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        total_recon_loss += loss_recon.item()
        if isinstance(loss_class, torch.Tensor):
            total_class_loss += loss_class.item()
    
    # 评估测试集准确率
    model.eval()
    correct_test = 0
    with torch.no_grad():
        for images, labels in test_loader:
            images = images.to(device)
            labels = labels.to(device)
            _, logits = model(images)
            _, preds = torch.max(logits, 1)
            correct_test += (preds == labels).sum().item()
    test_acc = correct_test / len(test_dataset)
    
    # 训练集上有标签样本的准确率
    train_acc = total_correct / n_labeled_total if n_labeled_total > 0 else 0
    print(f"Epoch {epoch+1:2d} | Recon Loss: {total_recon_loss:.4f} | Class Loss: {total_class_loss:.4f} | "
          f"Train Acc: {train_acc:.4f} | Test Acc: {test_acc:.4f}")

# -------------------- 4. 可视化重建效果 --------------------
model.eval()
with torch.no_grad():
    # 取一批测试图像
    test_iter = iter(test_loader)
    images, _ = next(test_iter)
    images = images[:8].to(device)
    recon, _ = model(images)
    
    # 绘制原始与重建对比
    fig, axes = plt.subplots(2, 8, figsize=(12, 3))
    for i in range(8):
        axes[0, i].imshow(images[i].cpu().squeeze(), cmap='gray')
        axes[0, i].axis('off')
        axes[1, i].imshow(recon[i].cpu().squeeze(), cmap='gray')
        axes[1, i].axis('off')
    axes[0, 0].set_title('Original')
    axes[1, 0].set_title('Reconstructed')
    plt.show()

小结

半监督学习在标签稀疏时能有效利用无标签数据分布,传统机器学习中的标签传播和自训练是简单实用的基线。

深度混合模型通过联合优化重建损失(无监督)与分类损失(监督),可以学到鲁棒的低维表示,提升小样本分类性能。

实践中可扩展至更复杂的任务,如一致性正则化(MixMatch)、深度聚类联合训练等。

机器学习实现少样本苹果分类:原理与实战


原理

  • 少样本学习(Few‑shot Learning):模型仅通过每类极少量样本(如 1 或 5 张图片)就能学会识别新类别。
  • N‑way K‑shot 任务定义:从 N 个类别中各选 K 个标注样本组成支持集(Support Set),查询集(Query Set)用于评估模型在该任务上的泛化能力。
  • 核心挑战:样本稀少导致严重过拟合,普通监督学习直接训练不可行。
  • 主流方法
    • 度量学习(Metric Learning):学习一个嵌入空间,使同类样本距离近、异类距离远,用最近邻完成分类。
      • 孪生网络(Siamese Network):判断两个样本是否属于同一类。
      • 原型网络(Prototypical Network):计算每个类的嵌入中心(原型),查询样本归入最近原型。
    • 元学习(Meta‑learning):在大量不同的少样本任务上训练模型,让模型“学会如何快速学习”。
    • 迁移学习 + 数据增强:利用预训练模型提取强特征,结合几何/色彩增强扩充支持集。
  • 在苹果分类中的意义:农业场景下标注大量苹果品种图片成本高、周期长;利用少样本学习可在少量人工标注后快速部署分选模型。

案例一:基于特征提取与 SVM/KNN 的少样本苹果分类(传统机器学习)

**任务**:模拟 4 种苹果品种(如富士、嘎啦、金帅、红将军)的特征数据,抽取极少量样本进行训练,用 SVM 和 KNN 进行分类,观察少样本下的表现。

python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score

# -------------------- 1. 生成模拟苹果特征数据 --------------------
# 模拟 4 个品种,每个品种 200 个样本,每个样本有 10 个特征(如颜色、直径、硬度、甜度等)
np.random.seed(42)
n_features = 10
n_classes = 4
samples_per_class = 200

# 为每个类生成有区分度的特征(不同均值、相同协方差)
X_list, y_list = [], []
for i in range(n_classes):
    mean = np.random.randn(n_features) * 2 + i * 2   # 不同类的中心偏移
    cov = np.eye(n_features) * 0.5                    # 各向同性协方差
    X_class = np.random.multivariate_normal(mean, cov, samples_per_class)
    X_list.append(X_class)
    y_list.append(np.full(samples_per_class, i))
X = np.vstack(X_list)
y = np.hstack(y_list)

# 划分全局训练/测试集(传统监督学习的基准)
X_train_full, X_test_full, y_train_full, y_test_full = train_test_split(
    X, y, test_size=0.3, random_state=42)

# -------------------- 2. 模拟少样本设置(N-way K-shot)--------------------
# 从训练集中为每个类别随机抽取 K 个样本作为支持集,其余训练样本可作查询集
def create_few_shot_dataset(X_train, y_train, n_way=4, k_shot=5, random_state=42):
    rng = np.random.RandomState(random_state)
    support_idx = []
    for cls in range(n_way):
        cls_idx = np.where(y_train == cls)[0]
        selected = rng.choice(cls_idx, k_shot, replace=False)
        support_idx.append(selected)
    support_idx = np.concatenate(support_idx)
    # 查询集:训练集中除支持集外的所有样本
    query_idx = np.setdiff1d(np.arange(len(y_train)), support_idx)
    return support_idx, query_idx

# 不同 K 值下的分类准确率对比
k_shots = [1, 3, 5, 10]
results_svm = []
results_knn = []

scaler = StandardScaler()
X_train_full_scaled = scaler.fit_transform(X_train_full)
X_test_scaled = scaler.transform(X_test_full)

for k in k_shots:
    support_idx, query_idx = create_few_shot_dataset(X_train_full_scaled, y_train_full, n_way=4, k_shot=k)
    X_support = X_train_full_scaled[support_idx]
    y_support = y_train_full[support_idx]
    
    # SVM 分类器(RBF 核)
    svm = SVC(kernel='rbf', C=1.0, gamma='scale')
    svm.fit(X_support, y_support)
    y_pred_svm = svm.predict(X_test_scaled)
    acc_svm = accuracy_score(y_test_full, y_pred_svm)
    results_svm.append(acc_svm)
    
    # KNN 分类器(k=1 或 3,当支持集很小时 k 不宜过大)
    knn_k = min(3, k)
    knn = KNeighborsClassifier(n_neighbors=knn_k)
    knn.fit(X_support, y_support)
    y_pred_knn = knn.predict(X_test_scaled)
    acc_knn = accuracy_score(y_test_full, y_pred_knn)
    results_knn.append(acc_knn)
    
    print(f"K-shot = {k:2d} | SVM Accuracy: {acc_svm:.4f} | KNN Accuracy: {acc_knn:.4f}")

# 全量数据监督学习基线
svm_full = SVC(kernel='rbf', C=1.0, gamma='scale')
svm_full.fit(X_train_full_scaled, y_train_full)
acc_full = accuracy_score(y_test_full, svm_full.predict(X_test_scaled))
print(f"\n全量监督 SVM 准确率: {acc_full:.4f}")

# -------------------- 3. 可视化结果 --------------------
plt.figure(figsize=(8,5))
plt.plot(k_shots, results_svm, 'o-', label='SVM (few-shot)')
plt.plot(k_shots, results_knn, 's-', label='KNN (few-shot)')
plt.axhline(y=acc_full, color='r', linestyle='--', label='Full data SVM')
plt.xlabel('K (samples per class)')
plt.ylabel('Test Accuracy')
plt.title('Few-shot Apple Classification Performance')
plt.legend()
plt.grid(True)
plt.show()

案例二:原型网络(Prototypical Network)实现少样本苹果分类(深度学习度量学习) 任务:构建一个简单的嵌入网络,在模拟的苹果特征数据上训练原型网络,使其能够通过计算类原型快速适应新类别,演示 episode 训练机制。

python
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# -------------------- 1. 生成多类苹果特征数据(用于元学习)--------------------
# 为体现元学习,生成更多类别(如 8 类苹果),部分用于训练阶段的任务采样,部分用于测试
np.random.seed(42)
n_features = 10
n_total_classes = 8
samples_per_class = 200

X_all, y_all = [], []
for i in range(n_total_classes):
    mean = np.random.randn(n_features) * 2 + i * 2
    cov = np.eye(n_features) * 0.5
    X_class = np.random.multivariate_normal(mean, cov, samples_per_class)
    X_all.append(X_class)
    y_all.append(np.full(samples_per_class, i))
X_all = np.vstack(X_all)
y_all = np.hstack(y_all)

# 标准化特征
scaler = StandardScaler()
X_all = scaler.fit_transform(X_all)

# 划分训练类别和测试类别(模拟新苹果品种)
train_classes = [0,1,2,3,4]      # 5 个基类用于训练
test_classes = [5,6,7]           # 3 个新类用于评估少样本泛化

# 构建训练/测试样本索引
train_idx = np.isin(y_all, train_classes)
test_idx = np.isin(y_all, test_classes)
X_train, y_train = X_all[train_idx], y_all[train_idx]
X_test, y_test = X_all[test_idx], y_all[test_idx]

# -------------------- 2. 定义嵌入网络(小型 MLP)--------------------
class EmbeddingNet(nn.Module):
    def __init__(self, input_dim=10, hidden_dim=64, output_dim=32):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, output_dim)
        )
    
    def forward(self, x):
        return self.net(x)

# -------------------- 3. 原型网络核心:计算原型并分类 --------------------
def compute_prototypes(support_embeddings, support_labels, n_way):
    # support_embeddings: (n_way * k_shot, embed_dim)
    # 按类别求平均得到原型
    prototypes = []
    for cls in range(n_way):
        mask = (support_labels == cls)
        proto = support_embeddings[mask].mean(dim=0)  # 平均原型
        prototypes.append(proto)
    return torch.stack(prototypes)  # (n_way, embed_dim)

def prototypical_loss(prototypes, query_embeddings, query_labels, n_way):
    # 计算查询样本到每个原型的欧氏距离,距离越近,概率越高
    dists = torch.cdist(query_embeddings, prototypes)  # (n_query, n_way)
    log_p = -dists  # 距离的负值作为 logits
    loss = nn.CrossEntropyLoss()(log_p, query_labels)
    return loss, log_p

# -------------------- 4. Episode 采样与训练 --------------------
def sample_episode(X, y, n_way=3, k_shot=5, n_query=15):
    """
    从数据中随机抽取 n_way 个类别,每类选 k_shot 个支持样本和 n_query 个查询样本
    """
    classes = np.random.choice(np.unique(y), n_way, replace=False)
    support_X, support_y = [], []
    query_X, query_y = [], []
    for i, cls in enumerate(classes):
        cls_idx = np.where(y == cls)[0]
        selected = np.random.choice(cls_idx, k_shot + n_query, replace=False)
        support_X.append(X[selected[:k_shot]])
        support_y.append(np.full(k_shot, i))
        query_X.append(X[selected[k_shot:]])
        query_y.append(np.full(n_query, i))
    support_X = torch.FloatTensor(np.vstack(support_X))
    support_y = torch.LongTensor(np.hstack(support_y))
    query_X = torch.FloatTensor(np.vstack(query_X))
    query_y = torch.LongTensor(np.hstack(query_y))
    return support_X, support_y, query_X, query_y

# 初始化模型和优化器
embed_net = EmbeddingNet(input_dim=10, hidden_dim=64, output_dim=32)
optimizer = optim.Adam(embed_net.parameters(), lr=0.001)

n_epochs = 500
n_episodes_per_epoch = 10
n_way = 3
k_shot = 5

loss_history = []
for epoch in range(n_epochs):
    epoch_loss = 0.0
    for _ in range(n_episodes_per_epoch):
        support_X, support_y, query_X, query_y = sample_episode(
            X_train, y_train, n_way=n_way, k_shot=k_shot, n_query=15)
        
        # 嵌入
        support_emb = embed_net(support_X)
        query_emb = embed_net(query_X)
        
        # 计算原型与损失
        prototypes = compute_prototypes(support_emb, support_y, n_way)
        loss, _ = prototypical_loss(prototypes, query_emb, query_y, n_way)
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        epoch_loss += loss.item()
    loss_history.append(epoch_loss / n_episodes_per_epoch)
    if (epoch+1) % 50 == 0:
        print(f"Epoch {epoch+1:3d}, Avg Loss: {loss_history[-1]:.4f}")

# -------------------- 5. 在新类别上评估少样本分类准确率 --------------------
def evaluate_few_shot(embed_net, X_test, y_test, n_way=3, k_shot=5, n_query=15, n_tasks=100):
    embed_net.eval()
    acc_list = []
    with torch.no_grad():
        for _ in range(n_tasks):
            support_X, support_y, query_X, query_y = sample_episode(
                X_test, y_test, n_way=n_way, k_shot=k_shot, n_query=n_query)
            support_emb = embed_net(support_X)
            query_emb = embed_net(query_X)
            prototypes = compute_prototypes(support_emb, support_y, n_way)
            _, logits = prototypical_loss(prototypes, query_emb, query_y, n_way)
            preds = torch.argmax(logits, dim=1)
            acc = (preds == query_y).float().mean().item()
            acc_list.append(acc)
    return np.mean(acc_list), np.std(acc_list)

test_acc, test_std = evaluate_few_shot(embed_net, X_test, y_test, n_way=3, k_shot=5)
print(f"\n新苹果品种 3-way 5-shot 分类准确率: {test_acc:.4f} ± {test_std:.4f}")

# 绘制训练损失曲线
plt.figure(figsize=(6,4))
plt.plot(loss_history)
plt.xlabel('Epoch')
plt.ylabel('Episode Loss')
plt.title('Prototypical Network Training')
plt.grid(True)
plt.show()

小结

传统机器学习 + 少样本:通过特征提取(如预训练 CNN)配合 SVM/KNN,在少量标注下也能获得可接受的分类效果,但性能受限。

原型网络(Prototypical Networks):学习一个度量空间,用类原型高效实现少样本分类,尤其适合品种间差异较小的苹果分类任务。

实际应用中可将本案例的模拟特征替换为从真实苹果图像(如 Fruit‑360 数据集)中提取的深度特征,直接复用训练流程。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐