简介一份基于 CNN-SVM-GA 的图像分类系统设计与实现代码大全 PDF面向计算机视觉初学者、毕业设计或课程项目开发者。文档完整阐述了利用卷积神经网络提取图像特征、支持向量机分类、遗传算法优化 SVM 惩罚因子 c 与核函数半径 g 的整体流程以 CIFAR-10 数据集为例给出从数据预处理、模型构建、PCA 降维到 GA 参数寻优的实验方案并附带带注释的核心 Python 代码便于读者复现和改造。资源包共 1 个 PDF 文件大小 1.06MB内容紧凑适合离线阅读。文档包含系统流程图、CNN 结构图、数据集样例图、理论公式及主要程序代码覆盖卷积层、池化层、全连接层、SVM 超平面求解和遗传算法选择交叉变异等关键知识点。目前已有 248 人学习下载关注度较好。读者可获得完整的系统设计思路、实验条件与参数设置、核心代码注释以及分类结果输出方法既能用于理解深度学习与机器学习结合的分类框架也可作为图像分类系统设计报告的参考素材。1. 这份“代码大全”到底在讲什么CNN-SVM-GA 三阶段图像分类管线速览当看到“基于 CNN-SVM-GA 的图像分类系统”这个标题时熟悉深度学习落地场景的工程师基本都会点头这不是端到端 CNN 分类而是把卷积神经网络CNN、支持向量机SVM和遗传算法GA拆成三段任务的混合管线。CNN 负责从图像里提取特征SVM 负责分类GA 负责给 SVM 搜索 C 和 gamma。标题里的“代码大全”核心就是给出一条从图像目录到分类结果的最小实现路径。如果你手里有几百到几千张图想在毕业设计或小样本工业分类里快速跑出一个可控、可解释的模型这条管线值得投入。接下来按选型、搜参、训练、排错和交付五个环节说透。2. CNN 特征提取选型用 ResNet18 做特征导出比从零训练 VGG 更省事2.1 三个特征提取器怎么选VGG16、ResNet18 与轻量模型的取舍先确认一个关键设计在这个三阶段管线里CNN 的任务不是输出类别而是把一张图转成一个固定长度的特征向量。所以选模型的标准不是 ImageNet 准确率而是三件事特征维度是否适合 SVM 训练、预训练权重是否容易拿、前向推理时间是否撑得起你的图像规模。VGG16 输出的 4096 维特征信息量大但后续 SVM 训练的核矩阵计算量也大两千张图就会明显变慢ResNet18 输出 512 维是几个常见特征提取器里性价比最高的轻量网络输出维度不低但在小数据集上特征分布不稳定GA 寻优时适应度曲线会来回跳。特征提取器输出特征维度CPU 单张推理参考耗时小样本集表现适合场景VGG164096约 120ms特征冗余易过拟合大样本、细粒度识别ResNet18512约 30ms稳定均衡默认首选轻量分类网络如 MobileNetV3960约 15ms方差偏大需调优嵌入式、高吞吐批处理我一般直接选 ResNet18。512 维往 SVM 里送训练时间和内存占用都很友好且预训练权重在 TorchVision 里一行代码就能加载。另一个容易忽略的点是灰度图。如果数据是 X 光片或表面缺陷图图片本身就是单通道不要强行复制成三通道后直接用 RGB 预训练权重那样第一个卷积层的通道统计会偏移。常见做法是复制通道成三份让预训练模型前几层的 BatchNorm 统计量不至于完全失效也可以把第一个卷积层的权重取均值展开成三通道但后者要改模型结构代码量多一截我倾向于复制通道稳定。2.2 去掉分类头用 PyTorch 导出 512 维特征向量的两处细节选型定了之后第一步是改模型结构。ResNet18 的默认结构是卷积层加全局平均池化加全连接头我们需要把最后的全连接层替换成恒等映射让模型输出 512 维的池化特征import torch import torch.nn as nn import torchvision.models as models def get_feature_extractor(): # 使用 ImageNet 预训练权重比随机初始化在中小数据集上收敛快很多 model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) # 去掉全连接分类头保留 avgpool 输出的 512 维特征 model.fc nn.Identity() model.eval() return model # 验证输出维度 model get_feature_extractor() dummy torch.randn(1, 3, 224, 224) out model(dummy) print(out.shape) # torch.Size([1, 512])这段代码里我先说model.fc nn.Identity()为什么干净。有人习惯遍历子模块取出model.fc.in_features再手动截断网络那样做在保存模型时会把分类头的参数也带进 checkpoint白增加内存和反序列化时间。直接替换成恒等映射后导出的特征提取器里根本没有分类头参数部署体积更小。第二个细节是model.eval()必须调用。如果不切到 eval 模式ResNet18 的 BatchNorm 层会用当前 batch 的统计量做归一化同一张图在单张推理和批量推理时输出不一致SVM 训练阶段导出的特征和推理阶段导出的特征分布就对不上。这个坑在 CNN-SVM 管线里比在端到端 CNN 里更致命因为 SVM 完全靠特征分布做决策分布偏移就是边界失效。输入尺寸方面ResNet18 原生支持 224x224。如果图像分辨率很高比如 1024x1024 的遥感图不要整图缩放那会把细粒度纹理压没。常见做法是中心裁剪 224x224 或分批滑窗取特征后做平均池化后者代码量多一些但保留了高分辨率下的纹理信息。2.3 数据增强参数怎么配让特征向量对平移和光照变化更鲁棒CNN 特征提取器固定之后数据增强的作用不再是防止端到端网络过拟合而是让 SVM 侧的特征分布更稳定。同样的物体平移几个像素、亮度变化一点点ResNet18 倒数第二层输出的 512 维向量应当尽量接近SVM 的决策边界才会又稳又准。否则同一类别在特征空间里就是一坨散点RBF 核再强也分不开。我常用的增强配置是from torchvision import transforms train_transform transforms.Compose([ transforms.RandomResizedCrop(224, scale(0.8, 1.0)), transforms.RandomHorizontalFlip(p0.5), transforms.ColorJitter(brightness0.2, contrast0.2, saturation0.2), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])参数说明RandomResizedCrop的 scale 取 0.8 到 1.0是怕裁剪比例过小导致特征提取器看到的物体残缺ColorJitter的三个系数不要超过 0.2预训练模型提取的特征对光照变化原本就敏感系数再大会让同一类别的特征方差变大SVM 的边界就会模糊。有一点必须单独强调测试集和推理阶段的 transform 不能带任何随机增强只能做 Resize 到 224、ToTensor、Normalize 这三步。训练时用增强后的特征测试时用原图特征两侧分布不一致会让 SVM 表现明显下滑。这条管线里很多人翻车不在模型本身就在预处理不对称。注意固定训练 transform 时RandomResizedCrop 的 scale 下限尽量不要低于 0.5否则特征提取器会长期看到被裁掉一大半的物体特征向量的判别性会显著下降。3. 遗传算法搜索 SVM 参数C、gamma 的寻优管线与交叉验证口径3.1 为什么不用网格搜索SVM 参数空间的特点SVM 分类器里最影响决策边界的是惩罚系数 C 和 RBF 核参数 gamma。C 控制误分类惩罚强度C 越大边界对训练样本越苛刻gamma 控制单个样本的影响半径gamma 越大边界越曲折。理论上网格搜索就能解决但实际项目里 C 经常在 0.1 到 1000 之间按指数分布取值gamma 在 0.0001 到 1 之间二维网格遍历几十个点勉强能跑如果再加上核函数选择、类别权重或者 PCA 维度组合数就炸了。遗传算法不保证全局最优但能在同样的时间预算内找到一组在交叉验证集上足够好的参数。我一般把搜索空间固定成这样的范围C2^-4 到 2^8指数采样gamma2^-10 到 2^2指数采样核函数RBF除非类别数特别多否则不换为什么不直接用 sklearn 默认的gamma1/n_features因为它只考虑特征维度没考虑数据分布。在 CNN 提取的 512 维特征上这个默认值通常偏小决策边界太光滑分类准确率上不去。GA 的价值就是在这种指数级跨度很大的参数空间里快速定位。3.2 GA 个体编码、选择、交叉与变异可直接抄的 Python 实现下面给一个完整可跑的遗传算法寻优实现目标不是工程级框架而是让你能在本地直接跑通再按需改。个体编码没有用二进制串而是用长度为 2 的归一化实数向量分别映射到 log2(C) 和 log2(gamma)这样 C 和 gamma 在指数跨度很大的取值空间里能均匀搜索。import numpy as np from sklearn.model_selection import cross_val_score from sklearn.svm import SVC from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler class GeneticSVM: 用遗传算法搜索 SVM 的 C 和 gamma。 个体编码为长度为 2 的实数向量映射到 log2(C) 和 log2(gamma)。 def __init__(self, X, y, pop_size8, generations10, cv5, seed42): self.X, self.y X, y self.pop_size pop_size self.generations generations self.cv cv self.rng np.random.default_rng(seed) def decode(self, ind): # log2 域映射让 C 和 gamma 在指数尺度上搜索 log2_c -4.0 ind[0] * 12.0 # [-4, 8] log2_g -10.0 ind[1] * 12.0 # [-10, 2] return 2.0 ** log2_c, 2.0 ** log2_g def fitness(self, ind): c, gamma self.decode(ind) model make_pipeline( StandardScaler(), SVC(Cfloat(c), gammafloat(gamma), kernelrbf, cache_size500) ) scores cross_val_score(model, self.X, self.y, cvself.cv, scoringf1_macro, n_jobs-1) return float(scores.mean()) def selection(self, population, fitnesses): # 锦标赛选择随机抽 3 个取适应度最高的个体 selected [] for _ in range(self.pop_size): idx self.rng.choice(len(population), size3, replaceFalse) winner idx[np.argmax([fitnesses[i] for i in idx])] selected.append(population[winner].copy()) return selected def crossover(self, parents): # 单点交叉概率 0.8其余个体直接保留 offspring [] for i in range(0, len(parents) - 1, 2): p1, p2 parents[i], parents[i 1] if self.rng.random() 0.8: point self.rng.integers(1, 2) child1 np.concatenate([p1[:point], p2[point:]]) child2 np.concatenate([p2[:point], p1[point:]]) else: child1, child2 p1.copy(), p2.copy() offspring.extend([child1, child2]) if len(parents) % 2 1: offspring.append(parents[-1].copy()) return offspring[:len(parents)] def mutate(self, offspring): # 高斯变异变异幅度随代数衰减 for ind in offspring: for j in range(len(ind)): if self.rng.random() 0.2: sigma 0.3 * (1.0 - self.current_gen / self.generations) 0.05 ind[j] np.clip(ind[j] self.rng.normal(0, sigma), 0.0, 1.0) return offspring def run(self): # 初始化种群均匀随机分布在 [0,1]^2 编码空间 population [self.rng.random(2) for _ in range(self.pop_size)] best_so_far None best_fitness -1.0 for gen in range(self.generations): self.current_gen gen fitnesses [self.fitness(ind) for ind in population] if max(fitnesses) best_fitness: best_idx int(np.argmax(fitnesses)) best_fitness float(fitnesses[best_idx]) best_so_far population[best_idx].copy() print(fgen {gen}: best f1_macro{best_fitness:.4f}) parents self.selection(population, fitnesses) offspring self.crossover(parents) population self.mutate(offspring) c, gamma self.decode(best_so_far) return c, gamma, best_fitness if __name__ __main__: # 模拟 200 个样本、512 维特征、4 类分类 x np.random.rand(200, 512) y np.random.randint(0, 4, size200) ga GeneticSVM(x, y, pop_size8, generations10, seed42) best_c, best_gamma, f1 ga.run() print(fbest C{best_c:.4f}, gamma{best_gamma:.6f}, f1_macro{f1:.4f})这里有几个值得展开的设计点。编码采用归一化实数向量而不是二进制字符串省去编解码开销也避免二进制串在交叉后产生大量无效个体。decode函数里用 log2 域映射是因为 C 和 gamma 真正取值相差几个数量级线性搜索容易把大部分个体浪费在无效区间。锦标赛选择加上单点交叉和高斯变异是最基础的 GA 结构没有额外的杂交池参数调试成本低。变异幅度随代数衰减早期保证全局探索后期做局部微调这个衰减系数是我调出来的经验值数据量大的时候可以适当加大初始变异幅度。参数建议种群大小 8 到 20代数 10 到 30交叉概率 0.7 到 0.9变异概率 0.1 到 0.3。样本量小的时候用小种群多代数样本量大时种群和代数取中等因为每次适应度评估都是一次五折交叉验证代价不小。3.3 交叉验证策略GA 内部用训练集 K 折测试集一票否决GA 在搜索 SVM 参数时如果每次拿全量数据拟合再在测试集上评估那测试集就被污染了最终报告的准确率会虚高上线的表现直接打七折。正确的口径是先把数据划分成 train 和 testtest 从头到尾不参与 GAGA 内部用训练集自身的五折交叉验证评估适应度寻优结束后用最优参数在完整训练集上重新训练再在 test 上评一次。from sklearn.model_selection import train_test_split # 加载前面保存的 npz 特征库 X_feat, y_all data[features], data[labels] X_train, X_test, y_train, y_test train_test_split( X_feat, y_all, test_size0.2, stratifyy_all, random_state42 ) # GA 只看 X_train / y_train ga GeneticSVM(X_train, y_train, pop_size8, generations10, seed42) best_c, best_gamma, _ ga.run() # 用最优参数在完整训练集上重训 final_model make_pipeline( StandardScaler(), SVC(Cbest_c, gammabest_gamma, kernelrbf, cache_size500) ) final_model.fit(X_train, y_train) # 测试集只评估一次 from sklearn.metrics import classification_report print(classification_report(y_test, final_model.predict(X_test)))这段代码里StandardScaler的用法有讲究它必须在 train 上 fit 均值和方差在 test 上只做 transform。make_pipeline在fit时自动在 train 上学习统计量predict 时自动用同一套统计量处理 test所以直接这么写是没有问题的。如果你拆开用裸的StandardScaler最容易犯的错误是把 train 和 test 放在一起标准化那样在遇到类别不平衡时测试集的信息已经通过均值方差泄漏进训练流程第 5 章会专门展开讲。4. 端到端实现从图像目录到 SVM 分类结果的完整代码4.1 阶段一批量提取图像特征并保存为 npz 特征库特征提取是整个管线最耗时的环节所以把它单独做成一个阶段是值得的。常见实现是遍历训练目录下的所有图片用前面定义好的特征提取器输出 512 维特征连同标签一起存成 npz 文件。之后 GA 和 SVM 训练都可以反复读这个文件不需要重新过一遍 CNN。import torch import numpy as np from pathlib import Path from PIL import Image from torchvision import transforms from torch.utils.data import Dataset, DataLoader class ImageFeatureDataset(Dataset): def __init__(self, root_dir, transformNone): self.samples [] self.labels [] self.transform transform for label_idx, folder in enumerate(sorted(Path(root_dir).iterdir())): if not folder.is_dir(): continue for img_path in folder.glob(*.jpg): self.samples.append(str(img_path)) self.labels.append(label_idx) def __len__(self): return len(self.samples) def __getitem__(self, idx): img Image.open(self.samples[idx]).convert(RGB) if self.transform: img self.transform(img) return img, self.labels[idx] def extract_and_save(model, root_dir, out_path, batch_size32): dataset ImageFeatureDataset(root_dir, transformtest_transform) loader DataLoader(dataset, batch_sizebatch_size, num_workers4, shuffleFalse) features, labels [], [] with torch.no_grad(): for imgs, labs in loader: feat model(imgs) features.append(feat.numpy()) labels.append(labs.numpy()) features np.concatenate(features, axis0) labels np.concatenate(labels, axis0) np.savez_compressed(out_path, featuresfeatures, labelslabels) model get_feature_extractor() extract_and_save(model, data/train, train_features.npz) extract_and_save(model, data/test, test_features.npz)这里说明三个实际工程细节。第一特征导出必须在with torch.no_grad()下进行否则 PyTorch 会为每次前向计算累积大量中间变量一万张图能把显存直接打满。第二shuffleFalse不能省虽然特征库本身不依赖顺序但后续排查特征和标签的对应关系时固定顺序能省很多事。第三单张图片损坏会导致整个中断建议在__getitem__里捕获Image.open的异常记录损坏文件名后返回同类别下一张可读图片不要返回零向量零向量进入 SVM 后会成为一个离群点RBF 核的决策边界会明显畸变。4.2 阶段二把 GA 的最优参数回填到 SVM 训练并保存模型GA 搜索结束后拿到最优 C 和 gamma接下来用完整训练集做一次最终训练把模型保存为 joblib 文件方便后续部署和交付。import joblib from sklearn.svm import SVC from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline data np.load(train_features.npz) X_train, y_train data[features], data[labels] # best_c / best_gamma 来自上一章 GeneticSVM.run() 的返回值 final_model make_pipeline( StandardScaler(), SVC(Cbest_c, gammabest_gamma, kernelrbf, probabilityTrue, cache_size500, class_weightbalanced) ) final_model.fit(X_train, y_train) joblib.dump(final_model, cnn_svm_ga_model.joblib) print(f保存完成训练集准确率: {final_model.score(X_train, y_train):.4f})probabilityTrue会启用 Platt 缩放来预测概率这个开关值得权衡它能输出置信度但训练耗时增加 20% 到 40%。如果需求只需要标签建议关掉。class_weightbalanced用来应对类别不平衡医疗图像和工业质检里正类样本往往只有负类的十分之一此时 GA 的适应度函数也必须换成f1_macro否则用准确率做优化目标GA 会收敛到“把所有样本判为多数类”的高分参数上测试集直接崩。4.3 阶段三测试集推理与类别概率输出推理阶段要处理两件事图像预处理与模型输入对齐以及把 SVM 输出转成人类可读的类别名。import numpy as np import joblib from PIL import Image def preprocess(img_path, transform): img Image.open(img_path).convert(RGB) return transform(img).unsqueeze(0) def predict_image(model, img_tensor, class_names): # 注意 predict_proba 返回形状为 (1, n_classes) proba model.predict_proba(img_tensor)[0] pred_idx int(np.argmax(proba)) confidence float(proba[pred_idx]) return class_names[pred_idx], confidence test_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]) ]) model joblib.load(cnn_svm_ga_model.joblib) class_names [cat, dog, bird] # 与训练文件夹排序一致不能乱 img_tensor preprocess(test_img.jpg, test_transform) cls, conf predict_image(model, img_tensor, class_names) print(f预测类别: {cls}, 置信度: {conf:.4f})这段推理代码最重要的一点是 transform 必须与训练阶段完全一致。很多人训练时用RandomResizedCrop推理时也顺手复制了同一套 transform结果同一张图每次预测结果都不同还以为是模型没收敛。推理阶段只能保留Resize不能有任何带随机性的操作。另外predict_proba输出的置信度是经 Platt 缩放的概率估计反映的是 SVM 决策边界的距离置信度不是贝叶斯后验概率。类别不平衡场景里它可能系统性偏大如果业务对置信度阈值有要求建议在部署前额外做一次温度校准或者用 isotonic 回归做校准否则阈值怎么调都别扭。5. 避坑与常见问题排查CNN-SVM-GA 管线最容易翻车的 5 个坑5.1 现象GA 搜出来的参数比 SVM 默认参数还差现象跑完 10 代 GA返回的 C 和 gamma 在测试集上的表现不如 sklearn 默认 RBF 参数。原因最常见的是适应度评估环节出了泄漏比如交叉验证的折划分没有固定随机种子导致每代评估的分数噪声很大GA 在追一个不稳定的目标另一个原因是种群太小、代数太少搜索还没收敛就停了。解决先在GeneticSVM构造时固定seed确认同一份数据跑两次 GA 结果一致再把每代最优适应度打印出来如果曲线一直在小范围抖动把种群加到 15 以上、代数加到 20 以上。更稳的做法是同时跑一个随机搜索做对照随机搜索的结果如果优于 GA说明选择、交叉或变异环节有实现问题逐段检查。5.2 现象SVM 训练内存打满训练时间按小时计算现象进入 GA 寻优后机器内存飙升第一次交叉验证还没跑完就 OOM。原因RBF 核的 SVM 训练要计算一个 n×n 的核矩阵内存占用是 O(n^2)。样本量超过两万时这个矩阵的尺寸就是几十 GB 量级再加上特征维度高标准 SVC 的求解时间会成倍增加。解决优先换成LinearSVC或开线性核图像特征经过 CNN 提取后一般具备较好的线性可分性如果业务场景必须用 RBF先把特征降维到 128 维或 256 维再训练或者从特征库里随机抽样 5000 到 10000 条先做 GA 寻优最终训练时再上全量数据。cache_size 也要调大到 500 以上否则 SVM 求解器会频繁释放和重建缓存训练慢得让人怀疑人生。5.3 现象训练准确率接近 100%测试集只有七成现象SVM 在训练集上的准确率接近满分测试集掉到七成左右两者差距稳定在 20 个点以上。原因典型过拟合。这条管线里常见两个来源一是数据增强不对称训练时用了较强增强测试时用原图特征分布已经错位二是 GA 把 C 和 gamma 推进了过拟合区间C 很大、gamma 很小的时候决策边界对训练样本的噪声极度敏感。解决先检查训练和测试两套 transform确认测试只有 Resize、ToTensor、Normalize再把 GA 的搜索空间收紧C 的上限从 2^8 降到 2^4gamma 的下限从 2^-10 提高到 2^-6强制搜索过程在偏保守的区间里寻找解。最终看交叉验证分数和测试分数的差值稳定在 5 个点以内就说明泛化正常。5.4 现象同一张图在 GPU 服务器和 CPU 笔记本上推理出不同结果现象训练时在 GPU 服务器上导出特征、训练 SVM模型搬到 CPU 笔记本上推理同一张图上结果不一致甚至类别都变了。原因第一是特征导出时忘了model.eval()BatchNorm 在训练模式和推理模式下行为不同第二是浮点累加顺序在不同硬件上不完全一致虽然这个影响通常很小但 SVM 恰好对特征分布敏感时就会放大第三是输入张量没有做同样的to(device)和数据类型转换FP16 和 FP32 的特征差几个小数点SVM 的边界就会动。解决在导出特征和推理两端都强制调model.eval()并把输入转换统一成同一套代码。验证方法很简单取一张图分别在两台机器上导出特征向量算一下欧氏距离小于 1e-4 基本安全超过 1e-2 就有问题。这条经验是我做跨环境部署时踩出来的特征是 SVM 的输入任何端到端 CNN 能容忍的微小数值差在这里都会被放大。5.5 现象npz 特征库重新加载后标签和特征对不上现象特征库保存后再加载训练准确率正常但抽查发现某些样本的预测类别和原始图像名称对不上。原因特征提取时shuffleFalse是对的但训练阶段如果把标签单独用np.argmax或独热编码处理后忘了同步索引特征和标签的顺序就错位了。另一个隐患是数据处理时用多进程读图某些进程失败静默返回了替代样本导致特征向量的数量对不上标签数量。解决在保存 npz 的同时额外保存一份文件名列表filenames.npy顺序和特征一一对应。训练前做一次长度校验len(features) len(labels) len(filenames)对不上就报错终止。上线前随机挑 5 到 10 个文件名人工核对预测结果这一步能兜住九成以上的数据错位问题。6. 把模型真正交付特征库版本管理、推理封装与鲁棒性验证管线跑通之后要让代码在同事或客户手上稳定复现有三件事值得做特征库版本管理、推理封装、鲁棒性验证。特征库版本管理是最容易被跳过、也最值得做的。特征库文件命名不要只写train.npz要把 CNN 版本和特征维度带进来比如train_v1_resnet18_512.npz。原因很简单换掉任何一层 CNN 结构即使参数不变导出的 512 维特征也完全不同SVM 必须重新训练。不标版本的结果就是隔了两周分不清特征库是用哪个模型导的重新训练发现准确率忽高忽低白折腾一整天。推理封装建议用一个类把所有依赖包住避免不同脚本复制粘贴预处理代码导致不一致。CNN 特征提取器、SVM 模型、类别名、transform 四样东西放一起换环境时只需要迁移一个对象。鲁棒性验证是我交付前的固定动作。取一张测试图分别做亮度加 20%、平移 10 像素、旋转 5 度三次扰动观察预测类别是否稳定。SVM 决策边界往往比端到端 CNN 更脆因为特征分布对扰动更敏感。如果出现抖动回去把数据增强参数调大一点重新导出特征库再走一遍 GA 寻优直到扰动下预测结果稳定为止。最后说一个我自己的习惯每次把 GA 的最优参数、交叉验证分数、测试分数连同随机种子记录到一个文本文件里。GA 这类随机算法不固定种子的话你很难判断某个参数的改进是真实的还是噪声。固定种子、记录结果换数据集后才能比较两个版本的改进幅度。这条管线本身不复杂真正让人抓狂的都是细节而这些细节大多能用版本号和种子管理解决。希望这些经验能帮你在做 CNN-SVM-GA 图像分类时少走点弯路。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
MATLAB随机森林回归预测:从决策树集成原理到TreeBagger实战 随机森林大概是机器学习里最不像“黑盒”的集成模型了。在回归预测任务中,它的逻辑非常朴素:训练一批决策树,每棵树各看一部分数据和一部分特征,最后把它们的预测值取平均。就是这样一个简单的“决策树集成模型”,却总… · 2026/9/25 2:46:45
LSTM外汇预测实战指南:从数据构建到避坑 简介:面向外汇量化入门者与机器学习开发者,这份基于LSTM网络的外汇预测模型压缩包完整演示了从M1级汇率数据清洗、特征构造、样本划分到多步预测的建模流程,适合希望用深度学习处理金融时间序列的读者。包内共27个文件,以ipynb教学… · 2026/9/25 2:46:39
NG-ZORRO Alert 辅助性文字(Description)详解:为警告提示补充说明内容 UI组件前端 【免费下载链接】ng-zorro-antd Angular UI Component Library based on Ant Design 项目地址: https://gitcode.com/gh_mirrors/ng/ng-zorro-antd 点击查看 免费下载 nz-alert 是 NG-ZORRO(基于 Ant Design 的 Angular 组件库)中… · 2026/9/25 2:46:39
TypeDoc @mergeModuleWith 标签详解:合并模块文档与多项目文档整合实践 开发工具文档 【免费下载链接】typedoc Documentation generator for TypeScript projects. 项目地址: https://gitcode.com/gh_mirrors/ty/typedoc 点击查看 免费下载 本文基于 TypeDoc 官方文档 site/tags/mergeModuleWith.md,系统讲解 mergeModuleWi… · 2026/9/25 3:19:10
EPubBuilder如何实现『最近文件』功能:IndexedDB存储EPUB二进制的实战指南 EPubBuilder如何实现『最近文件』功能:IndexedDB存储EPUB二进制的实战指南 【免费下载链接】EPubBuilder 一款在线的epub格式书籍编辑器 项目地址: https://gitcode.com/gh_mirrors/ep/EPubBuilder
EPubBuilder 是一款在线的 EPUB 电子书编辑器,它… · 2026/9/25 3:19:10
旧Mac升不上新macOS?OpenCore Legacy Patcher升级操作指南 旧Mac升不上新macOS?OpenCore Legacy Patcher升级操作指南 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher
打开"系统设置",… · 2026/9/25 3:19:10
rkt rm 命令详解:按 UUID 精确删除 Pod 并立即释放全部资源 容器运行时云原生网络 【免费下载链接】rkt [Project ended] rkt is a pod-native container engine for Linux. It is composable, secure, and built on standards. 项目地址: https://gitcode.com/gh_mirrors/rk/rkt 点击查看 免费下载 导读
rkt rm 是 rkt 容器… · 2026/9/25 3:19:10
为什么坚持零运行时依赖?VideoDownloadHelper轻量视频嗅探解析引擎设计哲学 为什么坚持零运行时依赖?VideoDownloadHelper轻量视频嗅探解析引擎设计哲学 【免费下载链接】VideoDownloadHelper Chrome Extension to Help Download Video for Some Video Sites. 项目地址: https://gitcode.com/gh_mirrors/vi/VideoDownloadHelper
Video… · 2026/9/25 3:19:03
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37