Распознавание рукописных цифр с помощью алгоритма KNN

искусственный интеллект Идентификация изображения
Распознавание рукописных цифр с помощью алгоритма KNN

«Это 22-й день моего участия в ноябрьском испытании обновлений, ознакомьтесь с подробностями события:Вызов последнего обновления 2021 г."

предисловие

K-ближайший сосед (k-nearest neighbours, KNN) — один из простейших алгоритмов обучения с учителем,KNNможет использоваться для задач классификации и регрессии, вOpenCVмогут быть реализованы и обученыKNNАлгоритм кластеризации. В этой статье мы узнаем, как использоватьKNNКлассификатор выполняет распознавание рукописных цифр, и мы начнем с базовой программы и усовершенствуем ее, чтобы повысить ее производительность.

Введение в набор данных рукописных цифр MNIST

Чтобы обеспечить полноту, начиная с обучающих данных, используемых алгоритмом, обучающие данные состоят изMNISTсоставленные из рукописных цифр,MNISTНабор данных предоставлен Национальным институтом стандартов и технологий и состоит из цифр, написанных 250 разными людьми, а обучающий набор содержит60000изображений тестовый набор содержит10000изображений, каждое изображение имеет свою метку, а размер изображения28*28. Многие библиотеки машинного обучения обеспечивают загрузкуMNISTИспользуемый здесь метод набора данныхkerasбиблиотека для загрузки:

# 导入 keras 库
import keras
# 加载数据
(train_dataset, train_labels), (test_dataset, test_labels) = keras.datasets.mnist.load_data()
train_labels = np.array(train_labels, dtype=np.int32)
# 打印数据集形状
print(train_dataset.shape, test_dataset.shape)
# 图像预览
for i in range(40):
    plt.subplot(4, 10, i+1)
    plt.imshow(train_dataset[i], cmap='gray')
    plt.title(train_labels[i], fontsize=10)
    plt.axis('off')
plt.show()

MNIST数据集预览

Распознавание рукописных цифр с помощью алгоритма KNN

После загрузки набора данных мы пытаемся использоватьKNNКлассификатор распознает цифры, в исходном методе мы сначала используем необработанные значения пикселей в качестве признаков, поэтому размер дескриптора изображения составляет28 × 28 = 784.

первое использованиеkerasЗагрузите все цифровые изображения, чтобы понять весь процесс обучения данных, мы разделим загруженный набор данных обучения на训练数据集 + 测试数据集, доля каждой части50%:

# 加载数据集
(train_dataset, train_labels), (test_dataset, test_labels) = keras.datasets.mnist.load_data()
train_labels = np.array(train_labels, dtype=np.int32)
# 将原始图像作为描述符
def raw_pixels(img):
    return img.flatten()
# 数据打散
shuffle = np.random.permutation(len(train_dataset))
train_dataset, train_labels = train_dataset[shuffle], train_labels[shuffle]
# 计算每个图像的描述符,这里特征描述符是原始像素
raw_descriptors = []
for img in train_dataset:
    raw_descriptors.append(np.float32(raw_pixels(img)))
raw_descriptors = np.squeeze(raw_descriptors)
# 将数据拆分为训练和测试数据(各占 50%)
# 因此,使用 30000 个数字来训练分类器,30000 位数字来测试训练后的分类器
partition = int(0.5 * len(raw_descriptors))
raw_descriptors_train, raw_descriptors_test = np.split(raw_descriptors, [partition])
labels_train, labels_test = np.split(train_labels, [partition])

Теперь мы можем использоватьknn.train()метод обученияKNNмоделировать и использоватьget_accuracy()функция для проверки:

# 训练 KNN 模型
knn = cv2.ml.KNearest_create()
knn.train(raw_descriptors_train, cv2.ml.ROW_SAMPLE, labels_train)
# 测试 kNN 模型
k = 5
ret, result, neighbours, dist = knn.findNearest(raw_descriptors_test, k)
# 根据真实值和预测值计算准确率
def get_accuracy(predictions, labels):
    acc = (np.squeeze(predictions) == labels).mean()
    return acc * 100
acc = get_accuracy(result, labels_test)
print("Accuracy: {}".format(acc))

Мы видим, что при К = 5,KNNМодель может достигать точности 96,48%, но мы все еще можем улучшить ее для повышения производительности.

Влияние параметра К на точность распознавания рукописных цифр

Мы уже знаем, что в алгоритме KNN важным параметром, влияющим на производительность алгоритма, является K, поэтому мы можем сначала попробовать разные значения K, чтобы увидеть, как он влияет на точность распознавания рукописных цифр. Чтобы сравнить точность модели для разных значений K, нам сначала нужно создать словарь для хранения точности при тестировании разных значений K:

from collections import defaultdict
results = defaultdict(list)

Далее вычисляемknn.findNearest()метод, изменяя параметр K и сохраняя результат в словаре:

# K 取值范围为 (1, 9)
for k in range(1, 10):
    ret, result, neighbours, dist = knn.findNearest(raw_descriptors_test, k)
    acc = get_accuracy(result, labels_test)
    print(" {}".format("%.2f" % acc))
    results['50'].append(acc)

Наконец, постройте результат:

ax = plt.subplot(1, 1, 1)
ax.set_xlim(0, 10)
dim = np.arange(1, 10)
for key in results:
    ax.plot(dim, results[key], linestyle='--', marker='o', label="50%")
    
plt.legend(loc='upper left', title="% training")
plt.title('Accuracy of the K-NN model varying k')
plt.xlabel("number of k")
plt.ylabel("accuracy")
plt.show()

Результат работы программы показан на следующем рисунке:

KNN识别手写数字

Как показано на рисунке выше, точность, получаемая при изменении параметра K, также отличается, поэтому в приложении параметр K можно настроить для получения наилучшей производительности.

Влияние объема обучающих данных на точность распознавания рукописных цифр

В машинном обучении обучение классификатора на большем количестве данных обычно повышает производительность модели, поскольку классификатор может лучше изучить структуру признаков. существуетKNNВ классификаторе увеличение количества тренировок также увеличивает вероятность нахождения правильного совпадения для тестовых данных в пространстве признаков.

Затем мы изменяем = процент изображений, используемых для обучения и тестирования модели, чтобы наблюдать влияние количества обучающих данных на точность распознавания рукописных цифр:

# 划分训练数据集和测试数据集
split_values = np.arange(0.1, 1, 0.1)
# 存储结果准确率
results = defaultdict(list)
# 创建模型
knn = cv2.ml.KNearest_create()
# 不同训练数据量对识别手写数字精确度的影响
for split_value in split_values:
    # 将数据集划分为训练和测试数据集
    partition = int(split_value * len(raw_descriptors))
    raw_descriptors_train, raw_descriptors_test = np.split(raw_descriptors, [partition])
    labels_train, labels_test = np.split(train_labels, [partition])
    # 训练 KNN 模型
    print('Training KNN model - raw pixels as features')
    knn.train(raw_descriptors_train, cv2.ml.ROW_SAMPLE, labels_train)
    # 同时对于每种划分测试不同 K 值影响
    for k in range(1, 10):
        ret, result, neighbours, dist = knn.findNearest(raw_descriptors_test, k)
        acc = get_accuracy(result, labels_test)
        print("{}".format("%.2f" % acc))
        results[int(split_value * 100)].append(acc)

Процент изображений цифр для обучения алгоритма составляет 10 %, 20 %, ..., 90 %, процент цифр для проверки алгоритма составляет 90 %, 80 %, ..., 10 %, и, наконец, постройте график результат:

ax = plt.subplot(1, 1, 1)
ax.set_xlim(0, 10)
dim = np.arange(1, 10)
for key in results:
    ax.plot(dim, results[key], linestyle='--', marker='o', label=str(key) + "%")

plt.legend(loc='upper left', title="% training")
plt.title('Accuracy of the KNN model varying both k and the percentage of images to train/test')
plt.xlabel("number of k")
plt.ylabel("accuracy")
plt.show()

训练数据量对识别手写数字精确度的影响

Как видно из приведенного выше графика, по мере увеличения количества обучающих изображений растет и точность. Поэтому, когда позволяют условия, производительность модели можно улучшить, увеличив количество обучающих данных.

Хотя видно, что показатель точности достиг более 97%, но мы не можем останавливаться на достигнутом.

Влияние предварительной обработки на точность распознавания рукописных цифр

В приведенных выше примерах мы использовали необработанные значения пикселей в качестве признаков для обучения классификатора. В машинном обучении обычно можно выполнить некоторую предварительную обработку входных данных перед обучением классификатора, чтобы повысить эффективность обучения классификатора, поэтому затем мы применяем предварительную обработку, чтобы увидеть ее влияние на точность распознавания рукописных цифр. функция предварительной обработкиdesew()следующее:

def deskew(img):
    m = cv2.moments(img)
    if abs(m['mu02']) < 1e-2:
        return img.copy()
    skew = m['mu11'] / m['mu02']
    M = np.float32([[1, skew, -0.5 * SIZE_IMAGE * skew], [0, 1, 0]])
    img = cv2.warpAffine(img, M, (SIZE_IMAGE, SIZE_IMAGE), flags=cv2.WARP_INVERSE_MAP | cv2.INTER_LINEAR)

    return img

desew()Функция выравнивает число, используя его второй момент. Более конкретно, его можно определить по соотношению двух центральных моментов(mu11/mu02)Вычисляет меру асимметрии. Вычисленная асимметрия используется для вычисления аффинного преобразования, устраняющего асимметрию чисел. Затем сравните эффекты изображения до и после предварительной обработки:

for i in range(10):
    plt.subplot(2, 10, i+1)
    plt.imshow(train_dataset[i], cmap='gray')
    plt.title(train_labels[i], fontsize=10)
    plt.axis('off')
    plt.subplot(2, 10, i+11)
    plt.imshow(deskew(train_dataset[i]), cmap='gray')
    plt.axis('off')
plt.show()

В первой строке следующего рисунка показано исходное цифровое изображение, а во второй строке — предварительно обработанное цифровое изображение:

预处理前后图像对比

Применяя эту предварительную обработку, точность распознавания повышается, и кривая точности показана на следующем рисунке:

预处理对识别手写数字精确度的影响

Видно, что точность предварительно обработанного классификатора может быть даже близка к 98%, учитывая, что мы используем только простойKNNМодель, эффект уже очень хороший, но мы можем еще улучшить производительность модели.

Улучшите точность алгоритма KNN, используя расширенные дескрипторы в качестве признаков изображения

В приведенных выше примерах мы использовали необработанные значения пикселей в качестве дескрипторов функций. В машинном обучении общий подход заключается в использовании дескрипторов более высокого уровня, за которыми следует гистограмма ориентированных градиентов (Histogram of Oriented Gradients, HOG) в качестве признаков изображения для повышения точности алгоритма KNN. Дескриптор функции — это представление изображения, которое упрощает изображение за счет извлечения полезной информации, описывающей основные функции, такие как форма, цвет или текстура. Как правило, дескрипторы функций преобразуют изображения в длинуnсобственный вектор ,HOGявляется популярным дескриптором функций для компьютерного зрения. Следующее определениеget_hog()приобретение функцииHOGДескриптор:

(train_dataset, train_labels), (test_dataset, test_labels) = keras.datasets.mnist.load_data()
SIZE_IMAGE = train_dataset.shape[1]
train_labels = np.array(train_labels, dtype=np.int32)
def get_hog():
    hog = cv2.HOGDescriptor((SIZE_IMAGE, SIZE_IMAGE), (8, 8), (4, 4), (8, 8), 9, 1, -1, 0, 0.2, 1, 64, True)
    print("hog descriptor size: {}".format(hog.getDescriptorSize()))
    return hog

затем используйтеHOGОсобенности обученияKNNМодель

hog = get_hog()

hog_descriptors = []
for img in train_dataset:
    hog_descriptors.append(hog.compute(deskew(img)))
hog_descriptors = np.squeeze(hog_descriptors)

Точность обученной модели показана на следующем рисунке:

使用高级描述符作为图像特征提高 KNN 算法准确率

Из процесса улучшения, описанного выше, видно, что хороший подход при написании модели машинного обучения состоит в том, чтобы начать с базовой базовой модели для проблемы, а затем итеративно улучшать, добавляя лучшую предварительную обработку, более продвинутые дескрипторы функций или другое машинное обучение. техники Модель. Наконец, если позволяют условия, можно собрать больше данных для обучения и тестирования модели.