You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Pillow展示MNIST数据方法及LeNet-5手写数字识别精度问题排查

嘿,这个问题我之前帮不少开发者解决过——你的LeNet-5在MNIST上能跑98%准确率,但碰自己拍的手写数字就拉胯,核心原因就是训练数据和测试数据的分布完全不一样(行业里叫“分布偏移”),咱们一步步来排查和解决:

1. 先检查灰度图的像素模式是否匹配MNIST

MNIST的图片是黑底白字:背景像素值是0(纯黑),数字是255(纯白)。但你用手机拍的照片大概率是白底黑字,这相当于把输入完全反转了,模型肯定认不出来!

你可以在灰度转换后加一步像素反转:

from PIL import Image, ImageOps

# 你的原有代码
im = Image.open(path)
im_resized = im.resize((28, 28))  # 注意LeNet-5输入是28x28,这里要明确size
gr = ImageOps.grayscale(im_resized)

# 新增:反转像素,匹配MNIST的黑底白字
gr = ImageOps.invert(gr)

另外,记得和训练时的归一化逻辑保持一致——比如MNIST训练时通常会把像素值缩到[0,1],那你也要做同样的处理:

import numpy as np
img_array = np.array(gr) / 255.0
# 如果训练时用了减均值/除标准差,也要对应上

2. 预处理流程要和训练时完全对齐

除了像素反转,这些细节也很关键:

  • 数字要居中:MNIST的数字都是居中的,你拍的照片可能数字偏左/偏右,试试先裁剪出数字的区域,再resize到28x28:
    可以用PIL的getbbox()方法找到数字的边界框,然后裁剪:
    bbox = gr.getbbox()
    if bbox:
        gr_cropped = gr.crop(bbox)
        gr_resized = gr_cropped.resize((28, 28))
    
  • 降噪处理:拍摄的照片可能有噪点,用高斯模糊轻量处理下:
    from PIL import ImageFilter
    gr = gr.filter(ImageFilter.GaussianBlur(radius=0.5))
    
  • 二值化阈值:把灰度图转成纯黑纯白的二值图,过滤掉浅灰色的背景干扰:
    threshold = 127  # 可以根据你的照片亮度调整,比如调到150或100
    img_binary = gr.point(lambda x: 0 if x < threshold else 255)
    

3. 给模型做少量微调,适配你的数据

LeNet-5的容量很小,对陌生数据的泛化能力弱。你可以用自己拍的10-20张手写数字图片(每个数字拍几张)做微调

  • 冻结模型的前几层(卷积层),只训练最后的全连接层
  • 用很小的学习率(比如1e-4)训练几个epoch,让模型快速适应你的手写风格

4. 拍摄时的小技巧

  • 用纯白背景,避免复杂纹理
  • 光线均匀,不要有阴影
  • 数字尽量写得和MNIST里的风格接近(粗细、大小)

如果还有后续问题,比如微调的具体代码、阈值怎么调更合适,随时补充细节就行~

内容的提问来源于stack exchange,提问作者Abhisek

火山引擎 最新活动