一、第一个图像分类器

机器学习算法例如K-NN、SVM等,即便是卷积神经网络也需要使数据集中的数据有固定的向量大小,对于图像来说就是要求图像的尺寸一致。

有许多改变图像大小的方法,从保持图像适应比的高级方法 到 压缩\拉伸图像的简单方法。应用那种方法因人而因、因地制宜、根据应用场景而变化。

我们选择的使简单的改变图像大小,不考虑适应比(长宽比)的方法,目录结构如下:


----pyimgsearch
	----__init__.py
	----datasets
		----__init__.py
        ----simpledatasetloader.py
    ----preprocessing
    	----__init__.py
    	----simplepreprocessor.py

打开simplepreprocessor.py文件写入如下代码

import cv2


class SimplePreprocessor:
    def __init__(self, width, height, inter=cv2.INTER_LINEAR):
        self.width = width
        self.height = height
        self.inter = inter

    def preprocess(self, image):

        return cv2.resize(image, (self.height, self.width), interpolation=self.inter)

打开simpledatasetloader.py写入如下代码:

import numpy as np
import cv2
import os


class SimpleDatasetLoader:
    def __init__(self, preprocessor=None):
        self.preprocessor = preprocessor

        if self.preprocessor is None:
            self.preprocessor = []

    def load(self, imagePaths, verbose=-1):
        data = []
        labels = []

        for (i, imagePath) in enumerate(imagePaths):
            image = cv2.imread(imagePath)
            label = imagePath.split(os.path.sep)[-2]

            if self.preprocessor is not None:
                for p in self.preprocessor:
                    image = p.preprocess(image)
                    data.append(image)
                    labels.append(label)

            if verbose > 0 and i > 0 and (i+1) % verbose == 0:
                print("[INFO] processed{}/{}".format(i+1, len(imagePath)))

        return np.array(data), np.array(labels)

我们设计的datasetloader能够轻松在我们的每一张图片上应用多个预处理器,但这也只适用于数据集中的数据能够一次性的装入内存。

简单的分类器:k-NN

k-NN分类器使目前为止最简单的机器学习和图像分类算法。实际是,它过于简单了,并没有真正的去学习任何东西,而是直接依靠两个特征向量之间的距离来进行分类。

告诉我你的邻居是谁,我就能知道你是谁

k-NN算法的前提假设是具有相同视觉内容的物体在n维空间中离得很近。

为了表示特征向量直接的距离,我们首选要选择一个距离度量公式,常见的有欧几里得距离和曼哈顿距离:

欧几里得距离公式:
图像分类 计算acc代码_sed
曼哈顿距离公式:
图像分类 计算acc代码_神经网络_02

新建 knn.py文件写入如下代码:

from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from pyimagesearch.preprocessing.simplepreprocessor import SimplePreprocessor
from pyimagesearch.datasets.simpledatasetsloader import SimpleDatasetLoader
from imutils import paths

dataset = r"E:\PycharmProjects\DLstudy\data\animal"

print("[INFO] lading dataset...")
imagePaths = list(paths.list_images(dataset))

sp = SimplePreprocessor(32, 32)
sdl = SimpleDatasetLoader(preprocessor=[sp])
(data, labels) = sdl.load(imagePaths, verbose=500)
data = data.reshape((data.shape[0], 3072))

print("[INFO] features matrix:{:.1f}MB".format(data.nbytes / (1024 * 1000.0)))

le = LabelEncoder()
labels = le.fit_transform(labels)

(trainX, testX, trainY, testY) = train_test_split(data, labels, test_size=0.25, random_state=42)

print("[INFO] evaluating k-NN classfier...")
model = KNeighborsClassifier(n_neighbors=1, n_jobs=-1)
model.fit(trainX, trainY)
print(classification_report(testY, model.predict(testX), target_names=le.classes_))

使用的数据集Animal-10

十种动物:狗,猫,马,大象,蝴蝶,鸡,牛,羊,蜘蛛,松鼠

每个种类数量不等,格式为.jpeg

我们只取其中三个种类来进行实验:狗、大象和马

运行结果:

E:\DLstudy\Scripts\python.exe E:/PycharmProjects/DLstudy/run/knn.py
[INFO] lading dataset...
[INFO] processed 500/8932
[INFO] processed 1000/8932
[INFO] processed 1500/8932
[INFO] processed 2000/8932
[INFO] processed 2500/8932
[INFO] processed 3000/8932
[INFO] processed 3500/8932
[INFO] processed 4000/8932
[INFO] processed 4500/8932
[INFO] processed 5000/8932
[INFO] processed 5500/8932
[INFO] processed 6000/8932
[INFO] processed 6500/8932
[INFO] processed 7000/8932
[INFO] processed 7500/8932
[INFO] processed 8000/8932
[INFO] processed 8500/8932
[INFO] features matrix:26.8MB
[INFO] evaluating k-NN classfier...
              precision    recall  f1-score   support

         dog       0.69      0.73      0.71      1201
    elephant       0.32      0.45      0.38       347
       horse       0.56      0.39      0.46       685

    accuracy                           0.58      2233
   macro avg       0.52      0.52      0.51      2233
weighted avg       0.59      0.58      0.58      2233

libpng warning: iCCP: known incorrect sRGB profile

Process finished with exit code 0

显然,这个结果虽然不够理想,但作为最简单的分类器,我们还要什么自行车呢?我们仅仅使用它作为一个入门的例子进行演示说明。

k-NN的优势与缺陷:

其优势在于理解和实施起来极度的简单,甚至都不用花时间去训练,因为我们需要做的就是将所有的训练样本点存储起来,然后每次预测时候计算预测样本与训练样本之间的距离即可,然后根据与预测样本最近的k个训练样本的类别来确定预测样本的类别。

但是,我们为其简洁性所付出的代价便是分类的时间,预测一个新的测试样本点需要与每个训练样本进行比较,在数据集规模大的情况下计算资源耗费极高。

ANN(Approximate Nearest Neighbour)算法可以克服这种时间开销,但是需要我们用预测的准确性来与空间复杂度作交换,因为我们进行了“Approximate" ,

总结:

k-NN算法更加适合与低维度的特征空间(图像不是)。高维度特征空间中的距离不直观。

k-NN算法没有真正的进行 ”学习“

那为什么还要学习k-NN算法?

因为其简单易理解,最重要的是,它为我们提供了一个基准,我们可以用它来和其他算法进行比较,诸如神经网络和卷积神经网络。