图像分类之:经典机器学习 Battle 深度学习

  • A+
所属分类:头条
摘要

这样我就可以安心回家吃蛋糕了.用机器来做图片分类简单来讲就是给机器输入一张图片,机器会输出这幅图片里面的内容.机器学习…

广告也精彩

前段时间,有个朋友和我提到,自己最近正打算用机器来判别图片中的场景是古镇还是园林,所以我这一期特地写了一篇文章,来描述图像的分类算法。由于最近工作略忙,所以文章断断续续写了好久,终于在自己生日前夕完成,希望可以有所帮助,这样我就可以安心回家吃蛋糕了。


用机器来做图片分类简单来讲就是给机器输入一张图片,机器会输出这幅图片里面的内容。机器学习中提供了很多对数据分类的算法,像KNN(最近邻)AdaboostNaive Bayes(朴素贝叶斯)SVM(支持向量机)ANN(人工神经网络)等以及最近几年兴起的CNN(卷积神经网络)


在这篇文章的实作部分,我会挑出KNN、SVM、ANN来实现,使用的是使用经典的scikit-learn(sklearn)库,CNN则使用Keras来实现。


编程的环境需求:

  • 系统:windows / linux

  • 解释器:python 3.6

  • 依赖库:numpy、opencv-python 3、tensorflow、keras、scikit-learn


数据集选择:


由于这几天无法穿越到苏州去采集大量园林和古镇的图片,而且本文还会去实验多分类的情况,但是既然同是图片的分类问题,我决定去网上搜集一些和风景相关的数据集。

突然有一天,在逛GitHub的时候,它就这样出现了,

在我的世界里

带给我惊喜

情不自已

为此,我给作者写了一份信:

作者在我晚上吃饭的时候给了回复:

这。。。。。。。

看来这条路没走顺,不如去看看一些知名的数据集里有没有自己需要的东西吧,不过有承诺在先,我还是会标注上数据集作者的GitHub地址:(https://github.com/yuweiming70/Landscape-Dataset),毕竟他送了我这么多精美壁纸图像分类之:经典机器学习  Battle  深度学习


牛津大学的17 Category Flower Dataset

(http://www.robots.ox.ac.uk/~vgg/data/flowers/17/index.html)很漂亮,看起来就是我在寻找的数据集(没错,我判断一个东西是不是自己需要的标准就是漂不漂亮图像分类之:经典机器学习  Battle  深度学习这个数据集总共17种花,每种花有80张图片,整个数据集有1360张图片,为了既达到实验的目的又不在训练上耗费太多的时间,我在同一种算法上选取了前两种花和前四种花做对比实验:


由于SVM和ANN的原理会占用太多的篇幅,并且这篇文章的主要目的是为了讲解代码实现,所以这里只介绍下机器学习中最简单的KNN分类器:


KNN是数据挖掘分类技术中最简单的方法之一,k最近邻,从名字大致就可以看出它的含义,就是找出K个离自己最近(相似)的数据,在这K个找到的数据中,看看那个类别最多,那么就认为自己是属于哪一个类别。

关于相似度度量的方法有很多,常见的有欧氏距离、曼哈顿距离、切比雪夫距离(切比雪夫兄跨界实在太多)、汉明距离、余弦相似度(夹角余弦值)等等。


KNN用到的是:欧氏距离(L2):


在数据(向量)只有二维的情况下,两个数据之间的欧氏距离就是两个点在二维坐标系下的直线距离,用初中一年级的数学公式就可以算出来:

当数据推广到多维的时候,两个数据之间的欧式距离就变成了:

图像分类之:经典机器学习  Battle  深度学习

距离越小表示两个向量相似度越大。

KNN有着实现方法简单、无需训练的优点,但是由于每次分类都要计算和所有数据之间的相似度,所以当数据维度很大或者数据数量很大的时候,计算会很耗时。




实验(KNN、SVM、ANN):

现在我要使用sklearn中的KNeighborsClassifier( KNN )、SVC( SVM )、MLPClassifier( 多层感知机分类器:ANN ),来实现这三个算法,由于sklearn中的算法模型高度统一化,所以三个程序可以写在同一个例子中,只是在创建分类器模型的时候略有不同:

引入KNeighborsClassifier、SVC、MLPClassifier模块:

from sklearn.neighbors import  KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.neural_network import MLPClassifier

引入训练样本分割函数:

from sklearn.model_selection import train_test_split

引入numpy 和 opencv:

import cv2
import numpy as np

读取图像函数,返回图像列表和标签列表:

IMAGE_SIZE = 100

def
 resize_without_deformation(image, size = (IMAGE_SIZE, IMAGE_SIZE)):

    height, width, _ = image.shape
    longest_edge = max(height, width)
    top, bottom, left, right = 0000
    if height < longest_edge:
        height_diff = longest_edge - height
        top = int(height_diff / 2)
        bottom = height_diff - top
    elif width < longest_edge:
        width_diff = longest_edge - width
        left = int(width_diff / 2)
        right = width_diff - left

    image_with_border = cv2.copyMakeBorder(image, top , bottom, left, right, cv2.BORDER_CONSTANT, value=[000])

    resized_image = cv2.resize(image_with_border, size)
    return resized_image

def read_image(size = None):
    data_x, data_y = [], []
    #for i in range(1, 1361):
    for i in range(1241):
        try:
            im = cv2.imread('17flowers/image_%s.jpg' % str(i).zfill(4))

            if size is None:
                size = (IMAGE_SIZE, IMAGE_SIZE)
            im = resize_without_deformation(im, size)
            im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
            data_x.append(np.asarray(im, dtype = np.int8))
            data_y.append(str(int((i-1)/80.0)))
        except IOError as e:
            print(e)
        except:
            print('Unknown Error!')

    return data_x, data_y

raw_images, raw_labels = read_image(size = (IMAGE_SIZE, IMAGE_SIZE))
raw_images, raw_labels = np.asarray(raw_images, dtype = np.float32), np.asarray(raw_labels, dtype = np.int32)

由于这三种分类器只接受一维向量的输入,所以将图片拍扁:

raw_images = raw_images.reshape((-1, IMAGE_SIZE * IMAGE_SIZE))

分割训练集和测试集(训练集:测试集 = 8 : 2):

train_images, test_images, train_labels, test_labels = train_test_split(raw_images, raw_labels,
                                                                        test_size = 0.2)

图片数据归一化:

train_images /= 255.0
test_images /= 255.0

创建分类器模型:

classifier_model = KNeighborsClassifier(n_neighbors = 7)

'''
classifier_model = SVC(C = 1.0,
                       kernel = 'rbf',
                       max_iter = 10000,
                       class_weight = 'balanced')
'''


'''
classifier_model = MLPClassifier(hidden_layer_sizes=(20, 100, 70), activation = 'relu',
                                 solver = 'sgd', batch_size = 5,
                                 learning_rate_init = 0.001, max_iter = 1000,
                                 alpha=1e-4, tol=1e-4, 
                                 random_state=1, shuffle = True,
                                 momentum = 0.8)
'''

训练:

classifier_model.fit(train_images, train_labels)

计算准确率:

accuracy = classifier_model.score(test_images, test_labels)
print('Accuracy: %s' % str(accuracy))

实验了几次,计算准确率平均值大致得到:

  • KNN:     73.8%

  • SVM:     78.5%

  • ANN:     78.9%


上面只是2分类的情况,现在取4种花来训练,得到准确率:

  • KNN:     40.1%

  • SVM:     40.6%

  • ANN:     41.5%


并且ANN在训练集上的正确率表现为100%, 很明显,已经过拟合,即模型已经呈现了记忆效应。


实验(CNN):


现在来试下卷积神经网络(由于上篇文章已经讲解过卷积神经网络的构建过程,这篇文章就不再赘述):


引入相关模块:

import  keras
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dense, Dropout, Flatten
from keras.optimizers import SGD
from keras.utils import np_utils
from sklearn.model_selection import train_test_split
import cv2
import numpy as np

读取图像函数,返回图像列表和标签列表:

IMAGE_SIZE = 100

def resize_without_deformation(image, size = (IMAGE_SIZE, IMAGE_SIZE)):
    height, width, _ = image.shape
    longest_edge = max(height, width)
    top, bottom, left, right = 0000
    if height < longest_edge:
        height_diff = longest_edge - height
        top = int(height_diff / 2)
        bottom = height_diff - top
    elif width < longest_edge:
        width_diff = longest_edge - width
        left = int(width_diff / 2)
        right = width_diff - left

    image_with_border = cv2.copyMakeBorder(image, top , bottom, left, right, cv2.BORDER_CONSTANT, value=[000])

    resized_image = cv2.resize(image_with_border, size)

    return resized_image

def read_image(size = None):
    data_x, data_y = [], []
    #for i in range(1, 1361):
    for i in range(1161):
        try:
            im = cv2.imread('17flowers/image_%s.jpg' % str(i).zfill(4))
            #im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
            if size is None:
                size = (IMAGE_SIZE, IMAGE_SIZE)
            im = resize_without_deformation(im, size)
            data_x.append(np.asarray(im, dtype = np.int8))
            data_y.append(str(int((i-1)/80.0)))
        except IOError as e:
            print(e)
        except:
            print('Unknown Error!')

    return data_x, data_y

raw_images, raw_labels = read_image(size = (IMAGE_SIZE, IMAGE_SIZE))
raw_images, raw_labels = np.asarray(raw_images, dtype = np.float32), np.asarray(raw_labels, dtype = np.int32)

One-Hot编码:

ont_hot_labels = np_utils.to_categorical(raw_labels)

分割训练集和测试集(训练集:测试集 = 8 : 2):

train_images, test_images, train_labels, test_labels = train_test_split(raw_images, ont_hot_labels,
                                                                        test_size = 0.2)

图片数据归一化:

train_images /= 255.0
test_images /= 255.0

构建CNN:

采用了VGG19结构的CNN:


图像分类之:经典机器学习  Battle  深度学习

深度很深,训练时间很长,特别耗内存和处理器(训练的时候记得在电脑下面垫冰块):

image_classification_model = keras.Sequential()

image_classification_model.add(Conv2D(64,(3,3),strides=(1,1),input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3),padding='same',activation='relu',kernel_initializer='uniform'))
image_classification_model.add(Conv2D(64,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
image_classification_model.add(MaxPooling2D(pool_size=(2,2)))
image_classification_model.add(Conv2D(128,(3,2),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
image_classification_model.add(Conv2D(128,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
image_classification_model.add(MaxPooling2D(pool_size=(2,2)))
image_classification_model.add(Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
image_classification_model.add(Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
image_classification_model.add(Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
image_classification_model.add(MaxPooling2D(pool_size=(2,2)))
image_classification_model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
image_classification_model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
image_classification_model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
image_classification_model.add(MaxPooling2D(pool_size=(2,2)))
image_classification_model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
image_classification_model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
image_classification_model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
image_classification_model.add(MaxPooling2D(pool_size=(2,2)))
image_classification_model.add(Flatten())
image_classification_model.add(Dense(4096,activation='relu'))
image_classification_model.add(Dropout(0.5))
image_classification_model.add(Dense(4096,activation='relu'))
image_classification_model.add(Dropout(0.5))
image_classification_model.add(Dense(len(ont_hot_labels[0]),activation='softmax'))

image_classification_model.summary()

如果想要快速训练,也可以使用下面的简化模型,效果也还不错:

image_classification_model = keras.Sequential()

image_classification_model.add(Conv2D(3233, border_mode='valid',
                                  subsample = (11),
                                  dim_ordering = 'tf',
                                  input_shape = (IMAGE_SIZE, IMAGE_SIZE, 3),
                                  activation='relu'))

image_classification_model.add(Conv2D(3233,border_mode='valid',
                                  subsample = (11),
                                  dim_ordering = 'tf',
                                  activation = 'relu'))

image_classification_model.add(MaxPooling2D(pool_size=(22)))
image_classification_model.add(Dropout(0.25))

image_classification_model.add(Conv2D(6433, border_mode='valid',
                                  subsample = (11),
                                  dim_ordering = 'tf',
                                  activation = 'relu'))

image_classification_model.add(Conv2D(6433, border_mode='valid',
                                  subsample = (11),
                                  dim_ordering = 'tf',
                                  activation = 'relu'))

image_classification_model.add(MaxPooling2D(pool_size=(22)))
image_classification_model.add(Dropout(0.25))

image_classification_model.add(Flatten())
image_classification_model.add(Dense(512, activation = 'relu'))
image_classification_model.add(Dropout(0.5))
image_classification_model.add(Dense(len(ont_hot_labels[0]), activation = 'sigmoid'))

image_classification_model.summary()

设置SGD优化器并编译模型:

learning_rate = 0.01
decay = 1e-6
momentum = 0.9
nesterov = True
sgd_optimizer = SGD(lr = learning_rate, decay = decay,
                momentum = momentum, nesterov = nesterov)
image_classification_model.compile(loss = 'categorical_crossentropy',
                               optimizer = sgd_optimizer,
                               metrics = ['accuracy'])

训练,这里只训练30次:

batch_size = 20
epochs = 30
image_classification_model.fit(train_images, train_labels,
                           epochs = epochs,
                           batch_size = batch_size,
                           shuffle = True,
                           validation_data = (test_images, test_labels))

看看这个模型在测试集上的表现:

score = image_classification_model.evaluate(test_images, test_labels, verbose=0)
print("%s: %.2f%%" % (image_classification_model.metrics_names[1], score[1] * 100))

准确率96.88%


再试下四种花的情况,在测试集上正确率为 70%,在训练集上正确率为 99.6%,虽然也过拟合,但是比三种经典分类器效果要好很多。


这种感觉就好像CNN把经典机器学习分类器的脸按在地上疯狂的摩擦,不放润滑油的那种。


不过反思一下,如果在进SVM、KNN、ANN之前,可以做一些特征提取,效果应该会更好一些。毕竟CNN训练和运行起来实在是太耗硬件了。

  • 微信
  • 扫一扫
  • weinxin
  • 微信公众号
  • 扫一扫
  • weinxin
广告也精彩
七分袖连衣裙
多功能扳手
唐人街探案 大朋vr一体机M2 Pro 头戴式VR眼镜  虚拟现实电影视频 1万+部影视 百款游戏
懒人鞋
广告也精彩

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: