
简介这份数据集面向计算机视觉入门者、深度学习教学者以及需要轻量级图像分类素材的开发者提供星形、圆形、正方形、三角形四类基本几何形状的标注图像可用于图像分类、卷积神经网络训练、数据增强与模型评估等实验场景。压缩包内共14973个文件以14970个png图片为主每张图片统一为200×200像素另附3个py脚本便于批量读取、预处理或统计类别分布整体包体约21.57MB体量轻便适合快速下载与本地调试。目前已有293人学习下载可作为课程作业、入门练手或算法对比的稳定数据来源。借助该数据集读者能够直接搭建四分类基线模型观察不同网络结构在简单形状上的收敛差异也可通过脚本扩展旋转、缩放等增强策略为后续迁移到更复杂图像任务积累经验。1. 四种基本形状数据集从玩具级样本到产线级分类器的第一道坎很多做视觉分类的朋友第一个跑通的模型不是 MNIST 就是猫狗大战但真到了要自己造数据、自己定标签、自己控质量的时候往往是从「星形、圆形、正方形、三角形」这种四种基本形状的图片数据集开始的。它看起来像个玩具但恰恰是检验一个人会不会做数据工程的最小闭环图像怎么生成、类别怎么平衡、尺寸怎么统一、噪声怎么加、训练集验证集怎么切、模型学到的到底是形状还是背景。我见过太多人拿网上下载的几十张图直接丢进 CNN结果准确率 99%换一批手绘的图立刻掉到 60% 以下这就是数据集没做扎实的典型翻车现场。这篇笔记就围绕这个数据集把从零构造、清洗、增强到训练验证的完整路径讲清楚适合想自己造数据集但不知道从哪下手的人也适合已经有一堆图但模型泛化很差、想回头查数据问题的从业者。2. 四种基本形状数据集到底长什么样类别定义、图像规格与生成逻辑2.1 类别边界不是「看起来像」就行星形、圆形、正方形、三角形这四个类听起来毫无歧义但真正开始标注或生成的时候第一个要拍板的是边界规则。圆形和正方形在低分辨率下会互相混淆尤其是 28×28 这种尺寸一个圆角矩形到底算圆还是算方我的做法是生成阶段就用参数化绘图不依赖手绘或截图从源头保证每个类的几何定义是严格的。圆形用matplotlib.patches.Circle正方形用Rectangle且宽高比锁定 1:1三角形用等边三角形星形用五角星且外接圆半径固定。这样每个类的数学定义清晰后续做消融实验时也能精确控制变量。另一个容易忽略的点是旋转不变性。如果你希望模型学到的是「形状」而不是「朝向」那训练集里每个类都要覆盖足够多的旋转角度。我一般按 15 度步长生成 0 到 345 度共 24 个角度每个角度再叠加少量平移和缩放。这样四个类各 24 张基础图再通过增强扩到每类 500 到 1000 张总量控制在 2000 到 4000 张之间对个人显卡足够友好。2.2 图像规格尺寸、通道、背景与噪声尺寸方面如果只是做教学演示或快速验证28×28 灰度图足够但如果要迁移到真实场景比如交通标志识别、工业零件分拣建议直接上 64×64 或 128×128 的 RGB。我的习惯是先生成 256×256 的高清底图再按需降采样这样一份数据可以反复用在不同分辨率的实验里不用重新生成。背景不能全是纯白。纯白背景会让模型走捷径学到「背景是白的所以是形状」这种无效特征。我通常准备三套背景纯色随机从调色板里抽、高斯噪声、以及少量真实纹理截图比如纸张、桌面。比例大概是 5:3:2保证模型必须关注前景轮廓。噪声方面加高斯噪声和椒盐噪声各占一部分标准差控制在 0.05 到 0.15 之间太大会把细窄的星形尖角直接抹掉。2.3 用代码生成第一批样本下面这段代码是我常用的生成脚本骨架依赖matplotlib、numpy、Pillow不依赖任何深度学习框架纯 CPU 就能跑。import numpy as np import matplotlib.pyplot as plt from matplotlib.patches import Circle, Rectangle, RegularPolygon, Polygon from PIL import Image import os, random def draw_shape(shape_type, size256, angle0, bg_typesolid): fig, ax plt.subplots(figsize(size/100, size/100), dpi100) ax.set_xlim(0, 1) ax.set_ylim(0, 1) ax.axis(off) fig.subplots_adjust(left0, right1, top1, bottom0) # 背景策略 if bg_type solid: bg_color np.random.rand(3) * 0.3 0.7 # 浅色随机 fig.patch.set_facecolor(bg_color) elif bg_type noise: noise np.random.normal(0.9, 0.08, (size, size, 3)).clip(0, 1) ax.imshow(noise, extent[0, 1, 0, 1]) cx, cy, r 0.5, 0.5, 0.3 color np.random.rand(3) * 0.5 # 深色前景 if shape_type circle: patch Circle((cx, cy), r, colorcolor) elif shape_type square: patch Rectangle((cx - r*0.9, cy - r*0.9), r*1.8, r*1.8, colorcolor) elif shape_type triangle: patch RegularPolygon((cx, cy), 3, radiusr*1.2, orientationnp.deg2rad(angle), colorcolor) elif shape_type star: # 五角星外顶点与内顶点交替 pts [] for i in range(10): rad r*1.2 if i % 2 0 else r*0.5 theta np.deg2rad(angle i * 36 - 90) pts.append((cx rad*np.cos(theta), cy rad*np.sin(theta))) patch Polygon(pts, closedTrue, colorcolor) ax.add_patch(patch) fig.canvas.draw() buf np.frombuffer(fig.canvas.tostring_rgb(), dtypenp.uint8) img buf.reshape(fig.canvas.get_width_height()[::-1] (3,)) plt.close(fig) return Image.fromarray(img) # 批量生成示例 os.makedirs(shapes_raw, exist_okTrue) for shape in [circle, square, triangle, star]: for i in range(50): ang random.choice(range(0, 360, 15)) bg random.choice([solid, noise]) img draw_shape(shape, size256, angleang, bg_typebg) img img.resize((64, 64), Image.LANCZOS) img.save(fshapes_raw/{shape}_{i:03d}.png)这段代码的逻辑是每个形状用 matplotlib 的 patch 对象绘制保证几何精确背景分纯色和噪声两种前景颜色随机但偏深避免和背景对比度过低角度按 15 度步长随机抽覆盖旋转变化最后统一缩放到 64×64。参数上size控制底图分辨率r控制形状大小bg_type切换背景策略。跑完一遍大概几十秒四个类各 50 张够做第一轮 sanity check。2.4 类别平衡与划分策略生成完之后四个类的数量必须严格一致不能圆形 800 张、星形 300 张否则模型会偏向多数类。划分训练集、验证集、测试集时我一般按 7:1.5:1.5 切并且保证每个集合里四个类的比例相同。更重要的是如果同一张底图做了多次增强这些增强版本必须落在同一个集合里不能一张图的旋转版进训练集、原图进测试集否则测试准确率会虚高。常见做法是按「原始底图 ID」分组后再切分而不是按增强后的图片随机切。3. 从原始图片到训练张量清洗、增强与 DataLoader 落地3.1 清洗去重、去模糊、去背景泄漏生成的数据虽然可控但增强之后容易产生近似重复样本。我一般用感知哈希做一次去重汉明距离小于 5 的视为重复只保留一张。另外要检查有没有「背景泄漏」比如某类形状恰好总是配某种背景模型就会学背景而不是形状。做法是统计每个类在不同背景类型下的分布如果某一类 90% 都是纯色背景就要补噪声背景的样本。模糊检测用拉普拉斯方差阈值设在 100 左右对 64×64 灰度图低于这个值的直接丢弃。这一步能过滤掉缩放过程中过度平滑的样本。3.2 增强策略几何增强为主颜色增强为辅形状分类的核心是几何特征所以增强要以旋转、平移、缩放、轻微透视为主颜色抖动只做辅助。具体参数旋转 ±180 度其实全覆盖了、平移 ±10%、缩放 0.85 到 1.15、剪切 ±5 度。颜色方面亮度 ±15%、对比度 ±10% 就够了饱和度不要动太多否则星形和三角形的颜色差异会被抹平。下面是一个基于torchvision的增强管道示例from torchvision import transforms train_tf transforms.Compose([ transforms.RandomAffine(degrees180, translate(0.1, 0.1), scale(0.85, 1.15), shear5), transforms.ColorJitter(brightness0.15, contrast0.1), transforms.ToTensor(), transforms.Normalize(mean[0.5]*3, std[0.5]*3) ]) val_tf transforms.Compose([ transforms.ToTensor(), transforms.Normalize(mean[0.5]*3, std[0.5]*3) ])注意验证集和测试集不做随机增强只做归一化。归一化的均值和标准差用训练集统计出来不要直接套 ImageNet 的因为你的数据分布和自然图像差别很大。我一般会先跑一遍训练集算每个通道的均值和标准差再填进去。3.3 用 Dataset 和 DataLoader 串起来自定义 Dataset 的时候关键是把「图片路径」和「标签」的映射关系写清楚并且保证__getitem__返回的是张量和整数标签。下面是一个最小实现from torch.utils.data import Dataset, DataLoader from PIL import Image import os class ShapeDataset(Dataset): def __init__(self, root_dir, transformNone): self.samples [] self.transform transform self.class_to_idx {circle: 0, square: 1, triangle: 2, star: 3} for fname in os.listdir(root_dir): if fname.endswith(.png): cls fname.split(_)[0] self.samples.append((os.path.join(root_dir, fname), self.class_to_idx[cls])) def __len__(self): return len(self.samples) def __getitem__(self, idx): path, label self.samples[idx] img Image.open(path).convert(RGB) if self.transform: img self.transform(img) return img, label train_ds ShapeDataset(shapes_train, transformtrain_tf) train_loader DataLoader(train_ds, batch_size32, shuffleTrue, num_workers2)这里batch_size设 32 是个人显卡上的稳妥值显存够可以上 64。num_workers在 Windows 上有时会出问题设 0 最稳Linux 上设 2 到 4。shuffleTrue只在训练集开验证集和测试集必须关掉否则评估结果不可复现。3.4 训练前的 sanity check在正式训练之前我一定会做两件事第一从 DataLoader 里取一个 batch把图片和标签打印出来肉眼确认图片没反色、没裁切错、标签没对错第二用随机初始化的模型跑一遍看 loss 是不是在 1.38 左右四分类的随机猜测水平如果一开始 loss 就接近 0说明数据泄漏了。这两步花不了五分钟但能省掉后面几个小时的无效训练。4. 四种形状分类的避坑与排查那些让准确率虚高的隐形陷阱4.1 现象训练准确率 99%测试准确率 60%原因最常见的是增强版本跨集合泄漏。同一张底图的旋转版进了训练集原图进了测试集模型相当于在「背题」。解决按底图 ID 分组切分确保同一底图的所有增强版本只出现在一个集合里。4.2 现象模型对星形和三角形混淆严重原因星形的尖角和三角形的顶点在低分辨率下都表现为「尖刺」如果训练集里星形的内凹部分不够明显模型很难区分。解决生成星形时把内外半径比从 0.5 调到 0.4让内凹更明显同时增加星形和三角形的样本量或者在损失函数里给这两类更高的权重。4.3 现象验证集 loss 震荡准确率忽高忽低原因背景噪声太强或者归一化参数不对。如果背景噪声的标准差超过 0.15形状轮廓会被淹没。解决把噪声标准差降到 0.08 到 0.12重新统计训练集均值和标准差替换归一化参数。4.4 现象换一批手绘形状后准确率暴跌原因生成数据太「干净」模型没见过手绘的不规则边缘。解决在训练集里混入 10% 到 20% 的手绘或真实拍摄样本或者用弹性形变增强模拟手绘抖动。弹性形变的alpha参数设 30 到 50sigma设 4 到 6效果比较自然。4.5 现象GPU 利用率低训练速度慢原因num_workers设太小或者图片从磁盘读取太慢。解决把数据集转成LMDB或WebDataset格式减少小文件读取开销num_workers按 CPU 核数的一半设置pin_memoryTrue也能提速。5. 进阶技巧用混淆矩阵和 t-SNE 反查数据质量问题训练完一个四分类模型准确率只是一个数字真正能告诉你数据哪里有问题的是混淆矩阵和特征可视化。我习惯在验证集上跑一遍混淆矩阵如果发现星形被大量预测成三角形那就回头检查星形的内凹比例和三角形顶角角度是不是太接近。另一个手段是把倒数第二层的特征拿出来做 t-SNE如果四个类的簇边界模糊说明特征空间里形状的区分度不够要么加数据要么改网络结构。下面这段代码用来画混淆矩阵和 t-SNEimport torch import numpy as np from sklearn.metrics import confusion_matrix from sklearn.manifold import TSNE import matplotlib.pyplot as plt import seaborn as sns def evaluate(model, loader, device): model.eval() all_preds, all_labels, all_feats [], [], [] with torch.no_grad(): for imgs, labels in loader: imgs imgs.to(device) feats model.features(imgs) # 假设模型有 features 方法 logits model.classifier(feats) preds logits.argmax(dim1).cpu().numpy() all_preds.extend(preds) all_labels.extend(labels.numpy()) all_feats.append(feats.cpu().numpy()) return np.array(all_labels), np.array(all_preds), np.concatenate(all_feats) labels, preds, feats evaluate(model, val_loader, device) cm confusion_matrix(labels, preds) sns.heatmap(cm, annotTrue, fmtd, xticklabels[circle,square,triangle,star], yticklabels[circle,square,triangle,star]) plt.savefig(confusion_matrix.png) tsne TSNE(n_components2, perplexity30, random_state42) feats_2d tsne.fit_transform(feats) plt.figure() for i, name in enumerate([circle,square,triangle,star]): idx labels i plt.scatter(feats_2d[idx, 0], feats_2d[idx, 1], labelname, alpha0.6) plt.legend() plt.savefig(tsne.png)混淆矩阵看的是「谁被错认成谁」t-SNE 看的是「类间距离够不够大」。如果 t-SNE 图上星形和三角形混在一起而混淆矩阵也显示这两类互相错那就基本确定是数据层面的问题不是模型容量不够。这时候加网络深度没用得回去改生成参数或者补样本。还有一个我踩过的坑一开始图省事把四个类的图片全部混在一个文件夹里用文件名前缀区分标签。后来文件一多前缀写错了一个导致几百张星形被标成三角形模型怎么训都上不去。从那以后我养成了一个习惯每个类一个子文件夹文件夹名就是标签生成脚本里写死路径绝不靠文件名解析。这个习惯看起来笨但省了太多后悔药。希望帮到你。本文还有配套的精品资源点击获取