Scikit-learn SVM数字识别

5
我希望制作一个程序,用于识别图像中的数字。我遵循scikit learn中的教程。
我可以像下面这样训练和拟合SVM分类器。
首先,我导入库和数据集。
from sklearn import datasets, svm, metrics

digits = datasets.load_digits()
n_samples = len(digits.images)
data = digits.images.reshape((n_samples, -1))

其次,我创建SVM模型并使用数据集进行训练。
classifier = svm.SVC(gamma = 0.001)
classifier.fit(data[:n_samples], digits.target[:n_samples])

然后,我尝试读取自己的图像并使用函数 predict() 来识别数字。

这是我的图像: enter image description here

我将图像重塑为(8, 8),然后将其转换为一维数组。

img = misc.imread("w1.jpg")
img = misc.imresize(img, (8, 8))
img = img[:, :, 0]

最后,当我打印预测时,它返回[1]

predicted = classifier.predict(img.reshape((1,img.shape[0]*img.shape[1] )))
print predicted

无论我使用其他人的图片,它仍然返回 [1]

enter image description here enter image description here

当我打印出数字“9”的“默认”数据集时,它看起来像:enter image description here 我的图像数字“9”:

enter image description here

您可以看到,对于我的图像,非零数相当大。

我不知道为什么。我正在寻求帮助来解决我的问题。谢谢。

7个回答

5
我认为最可能的问题是您的数据类型和数组形状存在问题。
看起来您正在训练np.float64(或者在32位系统上可能是np.float32)类型的numpy数组,每个图像的形状为(64,)
与此同时,您的输入图像在调整大小操作后经过的预测,其类型为uint8,形状为(1, 64)
首先,我建议尝试更改输入图像的形状,因为dtype转换通常可以按预期工作。因此,将此行更改为: predicted = classifier.predict(img.reshape(img.shape[0]*img.shape[1])) 如果这没有解决问题,您也可以尝试使用以下功能重新设置数据类型: img = img.astype(digits.images.dtype)
希望这能有所帮助。通过代理进行调试比真正坐在电脑前要困难得多:)
编辑:根据SciPy文档,训练数据包含从0到16的整数值。您输入图像中的值应缩放以适应相同的间隔。(http://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_digits.html#sklearn.datasets.load_digits

谢谢您的回复。我尝试了您的方法,但它并没有起作用。我发现我的图像数组元素比训练数据集要大得多。我已经更新了问题。 - VICTOR
1
哦,看起来你需要将它们缩放到0-16范围内:http://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_digits.html#sklearn.datasets.load_digits - Dr K

3

1) 您需要创建自己的训练集 - 基于类似于您要进行预测的数据。在scikit-learn中调用datasets.load_digits()会加载MNIST数字数据集的预处理版本,我们不知道它是否与您尝试识别的图像非常不同。

2) 您需要正确设置分类器的参数。在SVC中调用svm.SVC(gamma = 0.001)只是选择了gamma参数的任意值,这可能不是最佳选项。此外,您没有配置C参数-对于SVM来说非常重要。我敢打赌这就是为什么输出始终为“1”的原因之一。

3) 无论您选择哪种模型的最终设置,都需要使用交叉验证方案来确保算法有效地学习。

这背后有很多机器学习理论,但作为一个良好的开端,我真的建议看看SVM - scikit-learn以获取有关sickit-learn中SVC实现如何工作的更深入说明,以及GridSearchCV用于参数设置的简单技术。


2

这只是一个猜测,但是... Sk-Learn的训练集是在白色背景上的黑色数字。而你正在尝试预测黑色背景上的白色数字...

我认为你应该要么使用你的训练集进行训练,要么使用图片的负片进行训练。

希望这可以帮到你!


谢谢您的回复。我已经尝试了黑色背景,白色数字,但它仍然返回1。 - VICTOR

1
我已经使用以下方法解决了这个问题:
  1. 检查属性数量,太多或太少。

  2. 检查灰度值的范围,我将其改为[0,16]。

  3. 检查数据类型,我将其改为uint8。

  4. 检查训练数据数量,是否过少。

希望对您有所帮助。 ^.^

1
如果你看一下: http://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_digits.html#sklearn.datasets.load_digits 你会发现矩阵中的每个点都有一个0-16之间的值。
你可以尝试将图像的值转换为0-16之间。我这样做了,现在针对数字9的预测效果很好,但对于8和6则不行。它不再给出1的预测结果。
from sklearn import datasets, svm, metrics
import cv2
import numpy as np

# Load digit database
digits = datasets.load_digits()
n_samples = len(digits.images)
data = digits.images.reshape((n_samples, -1))

# Train SVM classifier
classifier = svm.SVC(gamma = 0.001)
classifier.fit(data[:n_samples], digits.target[:n_samples])

# Read image "9"
img = cv2.imread("w1.jpg")
img = img[:,:,0];
img = cv2.resize(img, (8, 8))

# Normalize the values in the image to 0-16
minValueInImage = np.min(img)
maxValueInImage = np.max(img)
normaliizeImg = np.floor(np.divide((img - minValueInImage).astype(np.float),(maxValueInImage-minValueInImage).astype(np.float))*16)

# Predict
predicted = classifier.predict(normaliizeImg.reshape((1,normaliizeImg.shape[0]*normaliizeImg.shape[1] )))
print predicted

0

嗨,除了@carrdelling的回答之外,我想补充一点,如果你将图像归一化到相同的值范围,你可以使用相同的训练集。 例如,你可以将数据二值化(如果大于0则为1,否则为0),或者你可以将图像中的最大强度除以最大强度,从而得到一个任意的区间[0;1]。


0

你可能想要从图像中提取与数据集相关的特征,并在其上训练模型。我从 这里 复制了一个例子。

surf = cv2.SURF(400) kp, des = surf.detectAndCompute(img,None)

但是SURF特征可能不是您的数据集和训练任务最有用或相关的特征。您也应该尝试其他特征,例如 HOG 或其他特征。

请记住,您提取的特征越高级,您的模型对未见过的图像就越具有一般性和容错性。但是,您可能会牺牲已知样本和测试用例的准确性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接