《手写数字识别器》(八)特征优化

在先前的训练阶段,我们发现算法模型在训练集上达到了1.0的准确率,然而,其在测试集上的表现却不尽如人意,这暗示着模型可能出现了过拟合现象。

我们的算法模型是直接依据每个数字图像的像素数据进行学习的。因此,当遇到新的数字图像时,只要像素的位置或值稍有变动,就可能导致预测结果的不准确。

例如,在训练过程中,模型可能只是机械地学习了2+3=5。这意味着,当你输入3+2或4+3时,模型可能无法给出正确的计算结果。这表明模型并未充分掌握通用的计算规则,而只是学到了一些特殊的、不重要的知识,甚至是噪声。

出现此问题,可能是由于算法模型的学习能力存在局限,或者我们提供给算法进行学习的特征质量不高。在神经网络出现之前,SVM 的学习能力还是相当不错的。所以,我们可以认为更为关键的因素是我们传递给算法模型的图像特征质量不高。为了解决这一问题,我们应该致力于让算法模型学习更具泛化性和鲁棒性的特征。

1. HOG 特征使用

HOG(Histogram of Oriented Gradients)就是一种能够提取更具泛化和鲁棒性的图像特征提取算法。它能够有效地描述图像中的物体轮廓和表象,从而有助于检测和识别物体。如下图所示(HOG算法能够提取出图像物体的轮廓信息):

下面是 HOG 特征的使用举例:

import cv2
import numpy as np
from skimage.feature import hog
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')


def show_image(image, title, index):
    plt.subplot(1, 2, index)
    plt.imshow(image, cmap='gray')
    plt.title(title)
    plt.axis('off')


def test():

    image = cv2.imread('demo.jpg')
    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # hog_vector 特征向量用于算法训练
    # hog_image 用于可视化 HOG 特征
    hog_vector, hog_image = hog(image,
                                orientations=9,
                                pixels_per_cell=(20, 20),
                                cells_per_block=(3, 3),
                                visualize=True)

    print(hog_vector.shape, hog_image.shape)
    print(np.min(hog_image), np.max(hog_image))

    show_image(image, 'origin', 1)
    show_image(hog_image, 'hog', 2)
    plt.show()


if __name__ == '__main__':
    test()

在使用 HOG 算法时,涉及到 orientations、pixels_per_cell、cells_per_block 参数,这几个参数在算法计算过程中,有什么作用?

2. HOG 算法初探

HOG 算法为了能够提取到输入图像中物体的轮廓信息,会将输入图像分割成一个一个网格,每个网格叫做一个 Cell,参数 pixels_per_cell 来决定每一个 Cell 的大小。

接着,分别计算每个 Cell 内的物体轮廓、纹理信息,并用 orientations 维度的向量表示该轮廓信息,该值一般默认都是 9。该 9 维的向量中,就包含了提取图像轮廓 纹理特征的变化方向和强度信息。

然后,再以 Block 为单位进行轮廓信息的归一化,Block 的大小由 cells_per_block 参数指定。这一步可以减少光照等因素的影响,从而更好的表示图像的特征。

最后,每一次滑动 Block 窗口,会得到 36 维的向量(以上图为例)。收集每一个 Block 的特征组成 1D 的向量作为该图像的 HOG 特征。

原来使用图像像素值作为特征时,输入的每一张图像就会包含 560×400 维度的特征。使用 HOG 特征之后,就可以使用  24 * 36 = 864 维度的特征来表征图像。并且该特征在实际进行物体检测时,泛化和鲁棒性都非常不错。

3. 应用 HOG 算法

创建 Estimator2.py 文件,内容如下:

import pickle
from sklearn.svm import SVC
import cv2
import glob
import os
import numpy as np
from skimage import feature
from skimage import transform
from skimage import io


class Estimator2:

    def __init__(self):
        estimator_path = 'model/estimator.pth'
        if os.path.exists(estimator_path):
            self.estimator = pickle.load(open(estimator_path, 'rb'))

    # 使用 HOG 特征
    def extract_feature(self, image):
        # image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
        image = feature.hog(image,
                            orientations=9,
                            pixels_per_cell=(100, 100),
                            cells_per_block=(3, 3),
                            visualize=False)
        return image


    def load_data(self, data_type='train'):
        image_fnames = glob.glob(f'data/{data_type}/[0-9]-[0-9]*.png')
        images, labels = [], []
        for fname in image_fnames:
            # 读取图像数据
            image = cv2.imread(fname)

            # 提取图像特征
            image = self.extract_feature(image)
            images.append(image)

            # 解析标签
            fname = os.path.basename(fname)
            label = int(fname.split('-')[0])
            labels.append(label)

        images = np.array(images)
        labels = np.array(labels)

        return images, labels

    def train(self):
        images, labels = self.load_data(data_type='train')
        estimator = SVC()
        estimator.fit(images, labels)
        train_acc = estimator.score(images, labels)

        images, labels = self.load_data(data_type='test')
        test_acc = estimator.score(images, labels)
        pickle.dump(estimator, open('model/estimator.pth', 'wb'))
        self.estimator = estimator

        return train_acc, test_acc

    def predict(self):
        image = cv2.imread('data/train/temp.png')
        image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        image = self.image_center(image)
        image = self.extract_feature(image)
        label = self.estimator.predict([image])

        return label[0]


if __name__ == '__main__':

    model = Estimator2()
    model.train()




未经允许不得转载:一亩三分地 » 《手写数字识别器》(八)特征优化
评论 (1)

1 + 2 =

  1. avatar
    烬湾04-14 14:05回复

    如果是使用MNIST数据集的话,使用这个效果会好些:
    def CreateHOGDescriptor(self):
    # 定义要使用的参数
    winSize = (28, 28)
    blockSize = (14, 14)
    blockStride = (7, 7)
    cellSize = (7, 7)
    nbins = 9
    derivAperture = 1
    winSigma = -1.
    histogramNormType = 0
    L2HysThreshold = 0.2
    gammaCorrection = 1
    nlevels = 64
    signedGradient = True

    # 创建 HOG 描述符对象
    hog = cv2.HOGDescriptor(winSize, blockSize, blockStride, cellSize, nbins, derivAperture,
    winSigma, histogramNormType, L2HysThreshold, gammaCorrection, nlevels, signedGradient)
    return hog

    def extract_feature(self, image):
    # 创建 HOG 描述符对象
    hog = self.CreateHOGDescriptor()

    # 使用 HOG 描述符对象提取特征
    hog_feature = hog.compute(image, winStride=(0, 0), padding=(0, 0), locations=[])

    return hog_feature