ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CNN-KELM图像分类:CNN特征提取结合核极限学习机实现快速准确预测

CNN-KELM图像分类:CNN特征提取结合核极限学习机实现快速准确预测 简介一套基于PyTorch的CNN-KELM图像分类预测完整工程面向有一定深度学习基础、希望将卷积神经网络与极限学习机ELM结合开展图像识别实验的研究者与学生。项目以CIFAR-10数据集为载体在CNN提取视觉特征后通过KELM核极限学习机完成分类决策并包含标签平滑与校正处理相比传统Softmax分类器提供了另一种可复现的建模思路。压缩包共43个文件涵盖Python源码23个py、PyCharm工程配置7个xml、CIFAR-10数据批次文件及模型可视化脚本等整体约162MB目录按数据、模型、损失函数、ELM分类器等模块划分便于直接定位训练、测试、特征提取与标签修正代码。目前已有363人学习下载适合需要完整源码参考、希望快速跑通实验并对比CNN-Softmax与CNN-KELM效果的读者。1. CNN-KELM图像分类CNN提特征、KELM做分类的组合为什么值得用做图像分类最常遇到的时间黑洞是数据整理好了模型一个个试训练一轮成小时地烧。CNN-KELM这个组合我用了好几个项目用CNN卷积神经网络把图像转成特征向量再用核极限学习机KELM做分类预测训练时间从小时级压到秒级。在标注样本几百到几千张、类别几十个以内的小规模场景里它比端到端CNN更容易调出稳定可用的结果也比纯手工特征省心得多。这篇笔记就是照着Python完整源码和数据记录的一条落地路径环境怎么搭、数据怎么组织、特征怎么提、KELM怎么写、参数怎么设、坑在哪里。适合正在做图像分类预测、手里有数据集但不想被GPU训练拖死的从业者。2. 组合原理与选型边界CNN为什么取特征、KELM为什么能快速分类2.1 从CNN卷积神经网络到特征提取把图像变成一条可分类的向量CNN卷积神经网络出现在图像分类场景里大家最熟悉的就是“卷积池化堆叠最后接全连接层输出softmax”的端到端结构。这种结构在数据量足够大的时候确实好用但在小数据集上很容易过拟合而且全连接层的参数动辄上千万每次训练都要把所有层的梯度更新一遍时间成本很高。反过来看CNN前面的卷积层和池化层在特征提取这件事上非常可靠。边缘、纹理、局部形状这些低级特征在浅层被提取出来语义信息在深层逐步抽象到了全局平均池化那一步每张图会被压缩成一个定长向量。这个向量就是图像在CNN视角下的“指纹”拿去做分类预测时完全可以不经过全连接层的训练。因此把CNN冻结成特征提取器去掉全连接层只保留卷积池化部分再把这个特征向量交给一个浅层分类器是一个很自然的拆分。分类器可以选SVM、逻辑回归、KELM。这类“深度学习cnn提特征浅层分类器”的方案在实际工程里出现频率很高因为它在硬件要求、调试速度和小样本稳定性之间找到了一个不错的平衡。特征向量的维度取决于选什么网络。ResNet50的全局平均池化输出是2048维VGG16的Flatten层输出是4096维MobileNet是1024维EfficientNet-B0是1280维。维度低一点KELM计算更快维度高一点信息更充分实操中我一般优先试ResNet50处理不了再换轻量网络。2.2 KELM的核极限学习机原理为什么不需要反向传播也能拟合KELM全称Kernel Extreme Learning Machine核极限学习机是ELM的核函数版本。先讲ELM随机初始化输入层到隐层的权重和偏置隐层激活后得到特征矩阵H输出权重β用最小二乘一次性解出来。整个过程没有反向传播、没有学习率、没有迭代训练速度极快。ELM的局限也很明显随机映射带来的波动性很大同样的数据换一个随机种子隐层输出H就变了结果可能差好几个百分点。KELM的做法是把随机映射替换成核映射让输出权重β的解写成 β H^T (H H^T C I)^{-1} T其中H H^T这个n×n矩阵可以直接用核函数替代定义Ω_ij K(x_i, x_j)于是不需要构造随机隐层只需要算核矩阵。核函数选RBF时K(x_i, x_j) exp(-gamma * ||x_i - x_j||^2)。C是正则化系数作用在单位矩阵上用来控制对噪声的拟合程度。预测阶段的公式是f(x) K(x, X_train) · (Ω C I)^{-1} · T其中K(x, X_train)是新样本和所有训练样本的核函数值行向量。理解了这套矩阵运算其实KELM的代码实现就只是“算核矩阵、解一个线性方程组、做一次矩阵乘”没有黑匣子调参也主要集中在gamma和C两个参数上这点比端到端CNN的黑匣子式调参要直观得多。2.3 选型边界CNN-KELM和端到端CNN、SVM、KNN怎么选项目选型不能只看新概念得按数据规模、时间成本和调参空间来算账。CNN-KELM最适合的区间是样本量几百到几千类别数几十个以内你需要在一两天内把baseline跑出来并且之后能持续快速调参。和端到端CNN比CNN-KELM不用训练CNN主体只训练一个分类器训练时间和资源消耗都低一个量级小样本上过拟合风险也更小。坏处是上限受限于预训练特征的表达能力如果预训练数据和你的任务域差异很大比如都是医学影像或者遥感图特征质量可能不够这时端到端微调反而更好。和SVM比两者都要算核矩阵但SVM依靠SMO迭代优化样本量增大后训练时间明显上升KELM是直接解线性方程组多分类时一次性完成不用像SVM那样做多个二分类器组合。和KNN比KNN没有显式训练过程但预测时要逐个算距离高维特征下距离度量不稳定KELM有显式的β矩阵预测更快、更稳。方案训练时间小样本表现主要调参项适合规模端到端CNN小时级弱易过拟合学习率、网络结构、增强策略万级样本CNN特征KELM秒级到分钟级强gamma、C几百到上万样本CNN特征SVM分钟级强C、核类型千级到万级CNN特征KNN无需训练一般K值、距离度量千级以下2.4 一个自测顺序先跑通线性KELM再换RBF核看增益实操时我建议不要一上来就上RBF核。先用线性核KELM跑通整个流程得到一个准确率基准。线性核只有一个C参数要调实现简单、计算快还能顺手验证特征文件、标签对齐这些基础环节是否正确。如果线性核的准确率已经能接受RBF核就不一定需要因为RBF核引入的gamma参数如果没有足够数据支撑反而容易过拟合。如果线性核效果不行再换RBF核网格搜索gamma和C。这个“从简到繁”的自测顺序能避免同时踩多个坑先确认Pipeline没写错再谈模型能力。我在项目里遇到过几次“RBF核准确率比线性核还低”的情况最后查下来都是特征没标准化或者标签没对齐根本不是核函数的问题。3. 跑通前的基础工作Python环境、数据集组织与特征预提取3.1 Python环境与依赖最小依赖组合与版本思路这个方案不需要GPUCPU就能完整跑通因为重计算只在预训练CNN的前向推理阶段普通笔记本十几分钟也能完成几千张图的特征提取。我一般用Python 3.8以上核心依赖是torch、numpy、scikit-learn、Pillow。如果从官网打包安装带了CUDA的torchCPU机器上会白白占用几个G磁盘建议直接装CPU版本。对很多python入门阶段遇到的问题比如pip install慢、版本冲突常见做法是建一个虚拟环境再装能省掉后面大半的玄学问题。版本不需要追求最新。torch 2.x和1.x都能跑numpy用1.21以上就行scikit-learn 1.0以上就够。如果项目里已经有一个老环境先检查torch能不能import能就不折腾新环境。完整源码里一般把特征提取和KELM分成两个脚本中间用.npy文件交换数据这个结构的好处是只要特征文件一致KELM脚本在任何机器上都能复现结果。提示如果机器上没装过Python先在官网装3.8以上版本安装时勾选Add to PATH装完在命令行执行python --version确认。新手最常见的卡点不是代码问题而是命令行找不到python命令。3.2 数据集目录组织与train/val/test划分图像分类最通用的数据组织是ImageFolder风格根目录下每个类别一个文件夹文件夹名就是类别名。pytorch、keras、torchvision都支持直接读这种结构不用写额外的解析器。我的习惯是根目录下分train和test两个大目录train里面再按类别分文件夹test同理验证集从train里切这样能保证测试集完全没参与过训练。给出一个划分脚本把散图标签文件转成ImageFolder结构import os import random import shutil from collections import defaultdict random.seed(42) src raw_data dst dataset train_ratio 0.8 class_to_files defaultdict(list) for class_name in os.listdir(src): class_dir os.path.join(src, class_name) if not os.path.isdir(class_dir): continue for fname in os.listdir(class_dir): if fname.lower().endswith((.jpg, .jpeg, .png)): class_to_files[class_name].append(os.path.join(class_dir, fname)) for class_name, paths in class_to_files.items(): random.shuffle(paths) n_train int(len(paths) * train_ratio) for split_name, split_paths in [(train, paths[:n_train]), (test, paths[n_train:])]: out_dir os.path.join(dst, split_name, class_name) os.makedirs(out_dir, exist_okTrue) for p in split_paths: shutil.copy(p, os.path.join(out_dir, os.path.basename(p))) print(train类别数:, len(class_to_files), train总样本数:, sum(int(len(v) * train_ratio) for v in class_to_files.values()))这段代码的逻辑先按类别收集所有图片路径再对每个类别内部做随机打乱按比例切出train和test最后拷贝到目标目录。注意一定按类别划分不能全量乱序后直接切否则很容易出现测试集里缺某个类别的情况。如果你的类别样本特别少比如每类不满20张建议把划分比例改成0.7并用分层抽样或者干脆跳过固定的test集依赖交叉验证。3.3 用预训练CNN批量提取特征并保存为.npy特征提取是整个方案的关键中间环节质量直接决定KELM的天花板。我一般用torchvision的ResNet50预训练权重把最后一层全连接替换成恒等映射这样forward的输出就是2048维的全局平均池化特征。用ImageNet预训练权重是为了在中小数据集上借到迁移学习的力比自己从零训练CNN稳定得多。import torch import numpy as np from torchvision import models, transforms from PIL import Image import os device torch.device(cuda if torch.cuda.is_available() else cpu) model models.resnet50(weightsmodels.ResNet50_Weights.IMAGENET1K_V2) model.fc torch.nn.Identity() model model.to(device).eval() transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) def extract_features_from_dir(root_dir, out_path): feats, labels [], [] label_to_id {} with torch.no_grad(): for label_idx, class_name in enumerate(sorted(os.listdir(root_dir))): label_to_id[class_name] label_idx class_dir os.path.join(root_dir, class_name) for fname in os.listdir(class_dir): path os.path.join(class_dir, fname) try: img Image.open(path).convert(RGB) except Exception as e: print(跳过坏图:, path, e) continue x transform(img).unsqueeze(0).to(device) feat model(x).cpu().numpy().flatten() feats.append(feat) labels.append(label_idx) X np.array(feats) y np.array(labels) np.savez(out_path, XX, yy, classesnp.array(sorted(label_to_id.keys()))) print(已保存, out_path, 特征维度:, X.shape) extract_features_from_dir(dataset/train, train_features.npz) extract_features_from_dir(dataset/test, test_features.npz)逻辑说明extract_features_from_dir按类别名排序后逐个读图统一resize到224x224、转tensor、做ImageNet的RGB标准化然后过一遍ResNet50得到2048维向量。这里加了一个try/except图片损坏时跳过而不是让整个脚本崩掉标签依然按类别顺序对齐。最后用np.savez一起保存特征矩阵、标签和类别名后续KELM脚本只需要加载这一个文件。参数说明Resize((224, 224))是ResNet系列的标准输入尺寸换EfficientNet要改成各自预训练要求的尺寸。Normalize的mean和std是ImageNet统计值不能随意替换否则预训练权重相当于白加载。灰度图经过convert(RGB)会自动复制成三通道PIL和torchvision的行为都能对齐。3.4 特征文件的质量检查先看分布再看维度特征提取完别急着进KELM先做一次质量检查。我最常做的就是打印X.shape、y的类别分布和每类的样本数再按类别算特征均值看类间均值向量的差异。如果某类样本数只有个位数说明这一类的代表性和区分度都有风险KELM就算拟合了也是玄学。还可以顺手做一个快速SVM或逻辑回归打一下baseline。如果连线性SVM都跑不出85%以上说明CNN特征本身在这个任务上不够好换KELM也不会有质的提升。这个前置检查能帮你区分问题是出在特征侧还是分类器侧避免把所有希望押在调gamma上。4. KELM核心实现与参数调节从.fit()到预测一条完整链路4.1 KELM的numpy实现RBF核矩阵与正则化解特征提取到位后分类器部分就是纯numpy的工作。最小可用的KernelELM实现大约40行核心是三个方法算核矩阵、fit解线性方程组、predict做矩阵乘。代码我贴一版可以直接加进自己项目的import numpy as np class KernelELM: def __init__(self, gamma0.01, C1.0, kernelrbf): self.gamma gamma self.C C self.kernel kernel def _kernel_fn(self, A, B): A: (n1, d), B: (n2, d) - 核矩阵 (n1, n2) if self.kernel rbf: sq_dists (np.sum(A * A, axis1, keepdimsTrue) np.sum(B * B, axis1, keepdimsTrue).T - 2 * A B.T) sq_dists np.clip(sq_dists, 0, None) return np.exp(-self.gamma * sq_dists) elif self.kernel linear: return A B.T else: raise ValueError(unsupported kernel) def fit(self, X_train, y_train): self.X_train np.asarray(X_train, dtypenp.float64) classes np.unique(y_train) self.classes_ classes T (y_train[:, None] classes[None, :]).astype(np.float64) T T * 2 - 1 Omega self._kernel_fn(self.X_train, self.X_train) self.beta np.linalg.solve(Omega np.eye(X_train.shape[0]) / self.C, T) def predict(self, X_test): X_test np.asarray(X_test, dtypenp.float64) K self._kernel_fn(X_test, self.X_train) scores K self.beta return self.classes_[np.argmax(scores, axis1)]逻辑说明fit方法先把标签转成one-hot矩阵T再映射到1/-1范围这是ELM论文里的常见表示。Omega是训练数据两两之间的核矩阵n×n加上np.eye(n)/C完成L2正则化C越大正则化越弱。用np.linalg.solve解线性方程组而不是直接求逆数值上稳定很多样本量几千时速度也足够快。predict方法对每个测试样本都算一行与所有训练样本的核值乘beta取最大值对应的类别。参数说明gamma控制RBF核的宽度。gamma太小核函数几乎恒为1模型退化成均值预测gamma太大每个样本只影响自己周围极小的范围容易把噪声也学进去。C控制正则化强度C越小越平滑。初次搜索时用对数网格比如gamma取[0.001, 0.01, 0.1, 1]C取[1, 10, 100]后面再细化峰值附近区间。4.2 训练与评估网格搜索gamma和CKELM训练极快所以网格搜索是完全可行的操作这比端到端CNN那种“一组参数等一晚”的模式舒服得多。我用StratifiedKFold做交叉验证再配合StandardScaler做特征标准化选参过程基本在一分钟内完成。from sklearn.model_selection import StratifiedKFold from sklearn.preprocessing import StandardScaler from sklearn.metrics import accuracy_score data np.load(train_features.npz) X, y data[X], data[y] scaler StandardScaler() X_scaled scaler.fit_transform(X) best_acc, best_params 0.0, {} for gamma in [0.001, 0.01, 0.1, 1.0]: for C in [1.0, 10.0, 100.0]: fold_accs [] skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) for train_idx, val_idx in skf.split(X_scaled, y): model KernelELM(gammagamma, CC) model.fit(X_scaled[train_idx], y[train_idx]) pred model.predict(X_scaled[val_idx]) fold_accs.append(accuracy_score(y[val_idx], pred)) mean_acc np.mean(fold_accs) if mean_acc best_acc: best_acc, best_params mean_acc, {gamma: gamma, C: C} print(fgamma{gamma}, C{C}, acc{mean_acc:.4f}) print(best acc:, f{best_acc:.4f}, params:, best_params)逻辑说明外层两层循环遍历gamma和C内层StratifiedKFold把训练数据切成5折每一折轮流当验证集。scaler的fit_transform是在切折之前对整个训练集做的这里的风险是信息泄漏更严格的做法是先切折再在训练折上fit scaler。不过CNN特征都是同一分布出来的实践里先整体标准化的影响较小如果你在意更严谨的实验就把scaler放进fold循环里。按这个结果选参后用全量训练集重新fit一次最终模型。注意交叉验证这里选的是“参数组合”不是“哪一折的模型”所以拿到最优参数后一定要在全量数据上重新训练才算完成选参到建模的闭环。4.3 单张图片的CNN-KELM预测管线从文件到类别训练结束后的部署阶段要把CNN特征提取、scaler和KELM串成一条完整的预测管线。老项目里这三步都是分开的脚本部署时最容易漏的就是把scaler忘了或者换transform参数所以我习惯把整个管线封装成一个函数。def build_pipeline(): data_train np.load(train_features.npz) scaler StandardScaler() X_train scaler.fit_transform(data_train[X].astype(np.float64)) model KernelELM(gammabest_params[gamma], Cbest_params[C]) model.fit(X_train, data_train[y]) return scaler, model, data_train[classes] def predict_one_image(path, transform, cnn_model, scaler, kelm, label_names): img Image.open(path).convert(RGB) x transform(img).unsqueeze(0).to(device) with torch.no_grad(): feat cnn_model(x).cpu().numpy().flatten().reshape(1, -1) feat_scaled scaler.transform(feat) label_idx kelm.predict(feat_scaled)[0] return label_names[label_idx] scaler, kelm, label_names build_pipeline() pred predict_one_image(dataset/test/sample.jpg, transform, model, scaler, kelm, label_names) print(预测类别:, pred)逻辑说明build_pipeline里重新fit了scaler和KELM参数来自网格搜索的best_params标签名来自特征文件里保存的classes字段。predict_one_image里图片先做和特征提取时完全相同的预处理再过CNN取特征scaler转换后进KELM。这里最关键的是一致性预测时用的transform、scaler参数必须和训练时完全一致不然特征分布对不上结果自然翻车。单张预测的速度瓶颈在CNN前向推理KELM部分几乎可以忽略CPU上单张几十毫秒到两百毫秒完全能满足小规模部署。如果后续要并发预测很多张可以把CNN的batch size调大一次forward出多张特征再统一交给KELM。4.4 预测结果转成业务指标准确率之外还要看什么对图像分类预测项目来说只报一个准确率在老板那里过关没问题但上线前还是要多算几项指标。二分类看precision、recall、F1多分类看宏平均F1和每类召回率。KELM的predict_proba输出的是softmax归一化后的分数虽然它不影响类别排序但可以用这个分数做置信度过滤把低于阈值的样本交给人工复核。from sklearn.metrics import classification_report probs kelm.predict_proba(X_test_scaled) pred kelm.predict(X_test_scaled) report classification_report(y_test, pred, target_nameslabel_names) print(report)classification_report会打印每个类别的precision、recall、F1以及宏平均结果。我习惯把置信度最低的20个样本单独抽出来看如果它们都是模糊样本或者标注错误说明模型本身没问题如果置信度低的样本集中在某个类别就要回看那个类别的清理数据和特征质量。5. CNN-KELM踩坑与常见问题五条让我翻过车的排查记录5.1 图片尺寸不一致导致特征维度对不上现象特征提取脚本跑完没报错一进KELM就报维度不一致或者准确率掉到离谱水平。原因Resize((224, 224))理论上会把所有图统一尺寸但实际项目中总有几个意外。RGBA四通道图在某些PIL版本里读出来是4通道ToTensor后变成4通道输入ResNet50第一层卷积就报错更隐蔽的是Exif旋转手机拍的图在PIL里可能宽高互换缩放到224x224后内容变形但不报错。还有一种常见情况是数据集里混入了.txt、.db之类的杂文件Image.open读不出来直接中断了脚本。解决遍历时按扩展名过滤jpg、jpeg、png之外的不要读图统一用convert(RGB)再包一层try/except坏图跳过并打印路径。我自己的习惯是提取完特征后立即打印X.shape和len(labels)两者不一致就说明中途有样本被吞掉立刻回头排查。5.2 没做特征归一化KELM准确率莫名低现象代码逻辑没毛病gamma和C调了一圈准确率还是六十多怎么调都上不去。原因RBF核算的是欧氏距离的负指数如果特征向量里某些维度数值远大于其他维度距离几乎完全由那几个大数值维度决定其他有用的信息被淹没。深度学习cnn提出来的特征虽然经过BatchNorm等操作但也不是天然标准化的尤其是不同图片之间明暗差异大时特征数值范围能差好几个数量级。解决在KELM训练前用StandardScaler标准化所有特征之后测试和预测都复用同一个scaler对象。这一步的收益很多时候是跨越式的曾经有个项目准确率从72%直接拉到90%我们一开始还以为是核函数选错了其实只是没做标准化。5.3 类别不平衡让KELM偏向多数类现象整体准确率不低但少数类的召回率接近0强行调高gamma后多数类反而开始错乱。原因KELM的最小二乘公式里所有样本的误差是等权求和的多数类样本数量大对总损失的贡献自然更大解出来的β也就更偏向拟合多数类。这正是svm和cnn原理里都讨论过的类别不平衡问题但在CNN-KELM组合里问题会在CNN特征这一侧被放大因为在类别不均衡时预训练CNN提取的特征对少数类的区分度本身就偏低。解决先把重采样做在前少数类过采样或多数类降采样跑到平衡再进KELM。或者改一层KELM的C把每个样本的正则化权重改成按类别数量倒数加权相当于给少数类更大的拟合自由度。我一般先试多数类降采样因为实现简单而且不会引入重复样本。5.4 核矩阵内存爆炸样本量一大就OOM现象训练数据从几千涨到一两万脚本直接MemoryError或者电脑风扇狂转、卡到不可操作。原因KELM的训练需要n×n核矩阵n是训练样本数。20000样本的核矩阵占3.2GB内存float64这在很多开发机上已经是极限了预测时还要再算一遍测试样本和训练样本之间的核矩阵内存压力更大。这是KELM这类核方法的通病靠硬件堆内存不是可持续的方案。解决如果业务数据确实上万要么用线性核把核矩阵退化成XX.T可以配合PCA把特征维度降下来要么把特征用MiniBatchKMeans先聚成聚类中心再做KELM。在几千样本的范围内RBF核KELM依然是最省心的选择超过一万就老实换方案。5.5 结果不可复现随机种子与PyTorch的坑现象同一个脚本两次运行准确率差1%到5%有时感觉模型看运气出结果。原因三个来源最容易忽略。PyTorch的数据加载有随机打乱且默认不固定种子torchvision预训练模型里的dropout和BatchNorm在训练/推断状态下的行为不同sklearn的StratifiedKFold每次随机切分也不同。KELM本身是确定性的所以复现问题通常出在前置环节。解决脚本开头固定random.seed和np.random.seedPyTorch额外设置torch.manual_seed(42)如果用了DataLoader还要设置generator。更省心的做法是特征提取和KELM分开成两个脚本特征只提取一次并落盘KELM脚本每次读的是同一个.npy文件这样分类器部分的复现基本就锁死了。5.6 KELM效果就是上不去特征侧才是真凶现象交叉验证结果一直在75%到85%之间波动换了gamma和C的组合也没突破模型像撞上了一堵墙。原因KELM作为分类器再优化也只是在CNN特征空间里找一个线性可分面它解决不了特征本身区分度不足的问题。常见原因包括图片预处理没有对齐比如训练用灰度、测试用彩色、预训练模型和任务域差异太大、部分类别在特征空间里本来就重叠严重。解决先用线性SVM跑同一个特征文件如果SVM的上限和KELM差不多说明瓶颈在特征侧而不是分类器侧。替代方向有三个换更强的预训练特征提取器比如从ResNet50换到ResNet101或ViT做数据增强后重提特征让特征对尺度、翻转更鲁棒做特征拼接把CNN特征和手工特征颜色直方图、HOG拼接后再进KELM。6. 验证与进阶用混淆矩阵、特征可视化和对比实验确认模型真的能用6.1 五折交叉验证与混淆矩阵不能只盯准确率KELM训练快交叉验证的代价几乎可以忽略所以每次调参我都至少跑五折。除了总准确率更关键的是看混淆矩阵哪两个类别互相混往往决定了下一步是补数据还是换特征。我用sklearn的confusion_matrix画出来后重点关注对角线以外的热点如果猫和狗互混说明CNN特征在这个任务上的区分度不够如果从不互混的类别也开始混就要怀疑预处理流程是不是有样本被处理坏了。6.2 TSNE特征可视化先看特征是否线性可分一个几十行的排查技巧是把训练特征扔进TSNE降到二维按类别着色画一张散点图。类内聚合、类间分开KELM的准确率大概率不会差如果图上所有类别糊成一团调gamma和C等于白费力气问题在特征侧。这时候我的处理顺序是换更强预训练模型、做数据增强、重新提特征直到TSNE图上类簇清晰。6.3 进阶技巧从KELM到在线更新如果业务上每天有增量样本KELM的全量重算就不合适了。退化成线性KELM后输出权重β有一组等价的最小二乘递推公式只需要维护一个协方差矩阵和一个交叉项新样本到来时做一次秩1更新就能更新模型不用重新算核矩阵。这个过程大概40行代码适合数据持续增长但不想引入复杂框架的团队。我的习惯是先跑TSNE确认特征没有先天问题再跑交叉验证选参最后看混淆矩阵决定要不要折腾特征侧。这套流程下来CNN-KELM在几百到几千张的小样本图像分类场景里基本能做到一个下午出可用模型。希望帮到你。本文还有配套的精品资源点击获取
返回列表