AI從入門到放棄:CNN的導火索,用MLP作圖像分類識別?

歡迎你們前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~javascript

做者:鄭善友 騰訊MIG後臺開發工程師java

導語:在沒有CNN以及更先進的神經網絡的時代,樸素的想法是用多層感知機(MLP)作圖片分類的識別;但殘酷的現實是,MLP作這事的效果並不理想。本文經過使用MLP作圖片分類識別的嘗試做爲思路指引,實爲下一篇CNN相關筆記的引子文章。 本文的文檔和代碼,傳送門: github項目地址python

一. 用MLP作圖像分類識別?

  1. 在沒有CNN以及更先進的神經網絡的時代,樸素的想法是用多層感知機(MLP)作圖片分類的識別,沒毛病
  2. 做爲上篇筆記學習的延續,以及下一篇CNN的藥引,使用MLP來作圖片分類識別,實在是個不錯的過分例子。經過這個例子,從思路上引出一系列的問題,我不賣關子,自問自答吧,即:
    • MLP能作圖片分類識別嗎?—> 答案是是能夠的,上一篇咱們是擬合非線性分類函數,這裏是擬合圖像特徵,數學本質沒區別。
    • MLP作這個事情效果如何?—> 我的認知內,只能說通常通常。
    • MLP在這一領域效果通常,是有什麼缺陷嗎? —> 缺陷是有的,下文會詳細說。
    • 有更好的解決方案嗎? —> 那也是必須有的,地球人火星人喵星人都知道有CNN等等更先進的東東;可是在沒有這些東西存在的時代,你發明出來了,那才真是666。

二. 先上車

1. 數據源

  • 數據源固然是圖片,可是是通過數據化處理的圖片,使用的是h5文件。h5文件簡單說就是把數據按索引固化起來,挺簡單的很少說,度度一下 —> h5py入門講解
  • 咱們有3個h5文件,存着不重複的圖片數據,分別是:
    • train_catvnoncat.h5 (用來訓練模型用的,一共有209張,其中有貓也有不是貓的圖片,尺寸64*64像素)
    • test_catvnoncat.h5 (用來測試模型準確度的,一共有50張圖片,,其中有貓也有不是貓的圖片,尺寸64*64像素)
    • my_cat_misu.h5 (用來玩的,我家貓主子的1張照騙,尺寸64*64像素)

2. 數據結構

  • 拿train_catvnoncat.h5舉例,這個文件有2個索引:
    • train_set_x:這是一個數組,由於是209張圖片,因此數組長度是209。數組中的元素是一個 64*64*3 的矩陣。64*64是圖片像素尺寸,3是什麼鬼?別忘了這是彩色圖片,3就是表明RGB這3個顏色通道的值。
    • train_set_y:圖片標籤數組,長度也是209,是209張圖片的標籤(label),對應數組下標的值是1時,表明這張圖片是喵星人,0則表明不是。
    • 同理,test_catvnoncat.h5 中有 test_set_x 和 test_set_y;my_cat_misu.h5 中有 mycat_set_x 和 mycat_set_y

3. 告訴你怎麼製做圖片的h5文件,之後作cnn等模型訓練時,很是有用

  • 以我主子爲例子:
    • 原圖:

  • 本身處理成64*64的圖片,固然你也能夠寫代碼作圖片處理,我懶,交給你實現了:

  • python代碼,用到h5py庫:
def save_imgs_to_h5file(h5_fname, x_label, y_label, img_paths_list, img_label_list):
   # 構造n張圖片的隨機矩陣
   data_imgs = np.random.rand(len(img_paths_list), 64, 64, 3).astype('int')   
   label_imgs = np.random.rand(len(img_paths_list), 1).astype('int')
   
   # plt.imread能夠把圖片以多維數組形式讀出來,而後咱們存成 n*n*3 的矩陣   
   for i in range(len(img_paths_list)):       
       data_imgs[i] = np.array(plt.imread(img_paths_list[i]))   
       label_imgs[i] = np.array(img_label_list[i])    
       
   # 建立h5文件,按照指定的索引label存到文件中,完事了   
   f = h5py.File(h5_fname, 'w')   
   f.create_dataset(x_label, data=data_imgs)   
   f.create_dataset(y_label, data=label_imgs)   
   f.close()    return data_imgs, label_imgs    
   
   #用法   
   # 圖片label爲1表明這是一張喵星人的圖片,0表明不是   
   save_imgs_to_h5file('datasets/my_cat_misu.h5', 'mycat_set_x', 'mycat_set_y', ['misu.jpg'],[1])
複製代碼

4. 看看個人數據源的樣子

  • 用來訓練的圖片集合,209張:

  • 用來校驗模型準確度的圖片集合, 50張

  • 用來玩的,主子照騙,1張:

三. 開車了

1. 如何設計模型:

  • 輸入層: 咱們的圖片是64*64的像素尺寸,那麼算上RGB三個通道的數據,咱們把三維矩陣拉成麪條 64*64*3 = 12288。 也就是咱們輸入層的數據長度是12288。
  • 隱藏層: 使用多層隱藏層,能夠自行多嘗試一下不一樣的結構。這裏我使用3個隱藏層,隱藏層神經元個數分別是20,7,5
  • 輸出層: 咱們的目標就是判斷某張圖片是不是貓而已,因此輸出層1個神經元,輸出機率大於0.5認爲是貓,小於等於0.5認爲不是。

【插播】:有人會想,第一層隱藏層的神經元和輸入層數量一致是否是會好點?理論上會好點,可是這涉及到MLP的一個缺陷,由於全鏈接狀況下,這樣作,第一層的權重w參數就有1228的平方個,約爲1.5個億。若是圖片更大呢?參數會成指數級膨脹,後果盡情想象。git

2. 如何訓練模型

  • 還用說,把209張圖片的數據扔到神經網絡,完成一次迭代,而後訓練1萬次,可自行嘗試迭不一樣代次數觀察效果。

3. 如何衡量模型的準確度

  • 大神吳恩達(Andrew Ng)提到的方法之一,就是劃分不一樣集合,一部分用來訓練,一部分用來驗證模型效果,這樣能夠達到衡量你所訓練的模型的效果如何。因此咱們訓練使用209張圖片,最終使用50張測試模型效果。
  • 爲了好玩,能夠本身用不一樣圖片經過模型去作分類識別。

四. 老規矩,甩代碼

仍是說明一下代碼流程吧:github

  • 代碼使用到的 NeuralNetwork 是我上一篇筆記的代碼,實現了BP神經網絡,import進來直接用便可。
  • 代碼作的事情就是:
    1. 從h5文件加載圖片數據
    2. 把原始圖片顯示出來,同時也保存成圖片文件
    3. 訓練神經網絡模型
    4. 驗證模型準確度
    5. 把識別結果標註到原始圖片上,同時也保存成圖片文件
#coding:utf-8
import h5py
import matplotlib.font_manager as fm
import matplotlib.pyplot as plt
import numpy as np
from NeuralNetwork import *

font = fm.FontProperties(fname='/System/Library/Fonts/STHeiti Light.ttc')

def load_Cat_dataset():
    train_dataset = h5py.File('datasets/train_catvnoncat.h5', "r")
    train_set_x_orig = np.array(train_dataset["train_set_x"][:])
    train_set_y_orig = np.array(train_dataset["train_set_y"][:])

    test_dataset = h5py.File('datasets/test_catvnoncat.h5', "r")
    test_set_x_orig = np.array(test_dataset["test_set_x"][:])
    test_set_y_orig = np.array(test_dataset["test_set_y"][:])

    mycat_dataset = h5py.File('datasets/my_cat_misu.h5', "r")
    mycat_set_x_orig = np.array(mycat_dataset["mycat_set_x"][:])
    mycat_set_y_orig = np.array(mycat_dataset["mycat_set_y"][:])

    classes = np.array(test_dataset["list_classes"][:])

    train_set_y_orig = train_set_y_orig.reshape((1, train_set_y_orig.shape[0]))
    test_set_y_orig = test_set_y_orig.reshape((1, test_set_y_orig.shape[0]))
    mycat_set_y_orig = mycat_set_y_orig.reshape((1, mycat_set_y_orig.shape[0]))

    return train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, mycat_set_x_orig, mycat_set_y_orig,classes


def predict_by_modle(x, y, nn):

    m = x.shape[1]
    p = np.zeros((1,m))

    output, caches = nn.forward_propagation(x)

    for i in range(0, output.shape[1]):
        if output[0,i] > 0.5:
            p[0,i] = 1
        else:
            p[0,i] = 0

    # 預測出來的結果和指望的結果比對,看看準確率多少:
    # 好比100張預測圖片裏有50張貓的圖片,只識別出40張,那麼識別率就是80%
    print(u"識別率: "  + str(np.sum((p == y)/float(m))))
    return np.array(p[0], dtype=np.int), (p==y)[0], np.sum((p == y)/float(m))*100


def save_imgs_to_h5file(h5_fname, x_label, y_label, img_paths_list, img_label_list):
    data_imgs = np.random.rand(len(img_paths_list), 64, 64, 3).astype('int')
    label_imgs = np.random.rand(len(img_paths_list), 1).astype('int')

    for i in range(len(img_paths_list)):
        data_imgs[i] = np.array(plt.imread(img_paths_list[i]))
        label_imgs[i] = np.array(img_label_list[i])

    f = h5py.File(h5_fname, 'w')
    f.create_dataset(x_label, data=data_imgs)
    f.create_dataset(y_label, data=label_imgs)
    f.close()

    return data_imgs, label_imgs

if __name__ == "__main__":
    # 圖片label爲1表明這是一張喵星人的圖片,0表明不是
    #save_imgs_to_h5file('datasets/my_cat_misu.h5', 'mycat_set_x', 'mycat_set_y', ['misu.jpg'],[1])

    train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, mycat_set_x_orig, mycat_set_y_orig, classes = load_Cat_dataset()

    train_x_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0], -1).T
    test_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1).T
    mycat_x_flatten = mycat_set_x_orig.reshape(mycat_set_x_orig.shape[0], -1).T
    train_set_x = train_x_flatten / 255.
    test_set_x = test_x_flatten / 255.
    mycat_set_x = mycat_x_flatten / 255.

    print(u"訓練圖片數量: %d" % len(train_set_x_orig))
    print(u"測試圖片數量: %d" % len(test_set_x_orig))

    plt.figure(figsize=(10, 20))
    plt.subplots_adjust(wspace=0,hspace=0.15)
    for i in range(len(train_set_x_orig)):
        plt.subplot(21,10, i+1)
        plt.imshow(train_set_x_orig[i],interpolation='none',cmap='Reds_r',vmin=0.6,vmax=.9)
        plt.xticks([])
        plt.yticks([])
    plt.savefig("cat_pics_train.png")
    plt.show()

    plt.figure(figsize=(8, 8))
    plt.subplots_adjust(wspace=0, hspace=0.1)
    for i in range(len(test_set_x_orig)):
        ax = plt.subplot(8, 8, i + 1)
        im = ax.imshow(test_set_x_orig[i], interpolation='none', cmap='Reds_r', vmin=0.6, vmax=.9)
        plt.xticks([])
        plt.yticks([])

    plt.savefig("cat_pics_test.png")
    plt.show()

    plt.figure(figsize=(2, 2))
    plt.subplots_adjust(wspace=0, hspace=0)
    for i in range(len(mycat_set_x_orig)):
        ax = plt.subplot(1, 1, i + 1)
        im = ax.imshow(mycat_set_x_orig[i], interpolation='none', cmap='Reds_r', vmin=0.6, vmax=.9)
        plt.xticks([])
        plt.yticks([])

    plt.savefig("cat_pics_my.png")
    plt.show()

    # 用訓練圖片集訓練模型
    layers_dims = [12288, 20, 7, 5, 1]
    nn = NeuralNetwork(layers_dims, True)
    nn.set_xy(train_set_x, train_set_y_orig)
    nn.set_num_iterations(10000)
    nn.set_learning_rate(0.0075)
    nn.training_modle()

    # 結果展現說明:
    # 【識別正確】:
    #   1.原圖是貓,識別爲貓  --> 原圖顯示
    #   2.原圖不是貓,識別爲不是貓 --> 下降顯示亮度

    # 【識別錯誤】:
    #   1.原圖是貓,可是識別爲不是貓 --> 標紅顯示
    #   2.原圖不是貓, 可是識別成貓 --> 標紅顯示

    # 訓練用的圖片走一遍模型,觀察其識別率
    plt.figure(figsize=(10, 20))
    plt.subplots_adjust(wspace=0, hspace=0.15)

    pred_train, true, accuracy = predict_by_modle(train_set_x, train_set_y_orig, nn)

    for i in range(len(train_set_x_orig)):
        ax = plt.subplot(21, 10, i + 1)

        x_data = train_set_x_orig[i]
        if pred_train[i] == 0 and train_set_y_orig[0][i] == 0:
            x_data = x_data/5

        if true[i] == False:
            x_data[:, :, 0] = x_data[:, :, 0] + (255 - x_data[:, :, 0])

        im = plt.imshow(x_data,interpolation='none',cmap='Reds_r',vmin=0.6,vmax=.9)

        plt.xticks([])
        plt.yticks([])

    plt.suptitle(u"Num Of Pictrues: %d\n Accuracy: %.2f%%" % (len(train_set_x_orig), accuracy), y=0.92, fontsize=20)
    plt.savefig("cat_pics_train_predict.png")
    plt.show()

    # 不屬於訓練圖片集合的測試圖片,走一遍模型,觀察其識別率
    plt.figure(figsize=(8, 8))
    plt.subplots_adjust(wspace=0, hspace=0.1)

    pred_test, true, accuracy = predict_by_modle(test_set_x, test_set_y_orig, nn)

    for i in range(len(test_set_x_orig)):
        ax = plt.subplot(8, 8, i + 1)

        x_data = test_set_x_orig[i]
        if pred_test[i] == 0 and test_set_y_orig[0][i] == 0:
            x_data = x_data/5

        if true[i] == False:
            x_data[:, :, 0] = x_data[:, :, 0] + (255 - x_data[:, :, 0])

        im = ax.imshow(x_data, interpolation='none', cmap='Reds_r', vmin=0.6, vmax=.9)

        plt.xticks([])
        plt.yticks([])

        plt.suptitle(u"Num Of Pictrues: %d\n Accuracy: %.2f%%" % (len(mycat_set_x_orig), accuracy), fontsize=20)
    plt.savefig("cat_pics_test_predict.png")
    plt.show()

    # 用我家主子的照騙,走一遍模型,觀察其識別率,由於只有一張圖片,因此識別率要麼 100% 要麼 0%
    plt.figure(figsize=(2, 2.6))
    plt.subplots_adjust(wspace=0, hspace=0.1)

    pred_mycat, true, accuracy = predict_by_modle(mycat_set_x, mycat_set_y_orig, nn)
    for i in range(len(mycat_set_x_orig)):
        ax = plt.subplot(1, 1, i+1)

        x_data = mycat_set_x_orig[i]
        if pred_mycat[i] == 0 and mycat_set_y_orig[0][i] == 0:
            x_data = x_data/5

        if true[i] == False:
            x_data[:, :, 0] = x_data[:, :, 0] + (255 - x_data[:, :, 0])

        im = ax.imshow(x_data, interpolation='none', cmap='Reds_r', vmin=0.6, vmax=.9)

        plt.xticks([])
        plt.yticks([])

        if pred_mycat[i] == 1:
            plt.suptitle(u"我:'我主子是喵星人嗎?'\nA I :'是滴'", fontproperties = font)
        else:
            plt.suptitle(u"我:'我主子是喵星人嗎?'\nA I :'唔系~唔系~'", fontproperties = font)
    plt.savefig("cat_pics_my_predict.png")
    plt.show()
複製代碼

五.結論

1. 神經網絡模型的輸出結果,標註到了圖片上並展現出來,規則是:

結果展現說明:算法

【識別正確】:數組

  1. 原圖是貓,識別爲貓 —> 原圖顯示
  2. 原圖不是貓,識別爲不是貓 —> 下降顯示亮度

【識別錯誤】:微信

  1. 原圖是貓,可是識別爲不是貓 —> 標紅顯示
  2. 原圖不是貓, 可是識別成貓 —> 標紅顯示

圖片標題會顯示Accuracy(準確度),準確度的計算公式是: 識別正確圖片數/圖片總數。網絡

2. 模型訓練完成後,把訓練用的209張圖片用訓練好的模型識別一遍,觀察結果:能夠看到,迭代1w次的模型,識別訓練圖集,準確度是 100% 的:

3. 模型訓練完成後,使用測試圖集用訓練好的模型識別一遍,觀察結果:能夠看到,迭代1w次的模型,識別訓練圖集,準確度只有 78%:

4. 看看模型能不能認出我主子是喵星人,看樣子,它是認出來了:

六.對結果進一步分析,引出一系列問題

  1. 拋出一個問題: 爲何用測試圖集驗證模型,識別率只有78%?在我嘗試過改變神經網絡結構設計,參數調參後,仍然沒法提升識別率,爲何呢?
  2. 不算完全的解答:
    • 也許是我水平有限,調參姿式不對?姿式帥並非萬能的,咱們應該從更深層次的原理進行分析。
    • 有人說,你訓練數據少了,好像有那麼些道理。實際上是能夠給模型輸入更多圖片的特徵是個不錯的辦法,好比旋轉一下,圖片內容放大縮小,挪挪位置等。可是Andrew Ng也說過,過度追求訓練數據收集是一條不歸路。在同等訓練數據集下,有更好的辦法嗎?由此引出下一個問題。
  3. 刨根問底: 想要知道爲何MLP識別度難以作到很高,撇開網絡結構,調參,訓練數據先不談。咱們應該從MLP身上找找茬。搞清楚咱們目標,是提升對圖片進行分類識別,那麼在使用MLP實現這個目標時,它自身是否有缺陷,致使實現這個目標遇到了困難。那麼解決了這些困難,就找到了解決問題的方法。
  4. MLP在作圖片分類識別的缺陷:
    • 神經元是全鏈接的方式構成的神經網絡,全鏈接狀況下,假設圖片是1k*1k像素大小,那麼隱藏層個數和輸入層尺寸一致時,不考慮RGB顏色通道,單通道下,權重w參數個數會是:$(10^3∗10^3)^2=10^{12}$=1千億(沒數錯0的話) 若是圖片再大點,參數膨脹到不可想象,直接致使的負面效果是:
      • 參數過多,計算量龐大
      • 全鏈接狀況下,過深的網絡容易致使梯度消失,模型難以訓練
    • MLP全鏈接的狀況下,沒法作到圖片的形變識別。怎麼理解這個詞呢,拿手寫數字舉個例子,好比寫8,每一個人書寫習慣不同,有的人寫的很正,但有的人寫歪了點,上半部分小,下半部分大,等等。這時候,MLP的缺點就顯現出來了,同一張圖片,旋轉,或者稍微平移形變一下,它沒法識別。你能夠經過增長更多特徵給模型,但這不是本質上的解決該問題的方法,而是對訓練的優化手段。

七. 總結要解決的問題,離下一個坑就不遠了

上面已經列舉了要解決的幾個問題,這裏總結一下:數據結構

  • 咱們要解決參數膨脹帶來的計算量龐大的問題
  • 優化參數量以後,如何在同等訓練數據集不變的狀況下,如何提取更多特徵
  • 在輸入有必定的旋轉平移伸縮時,仍能正確識別

能解決以上問題的,衆所周知,就是CNN以及衆多更先進的神經網絡模型了。本文做爲一篇引子文章,也是CNN的導火索。 代碼在你手中,把第一層隱藏層設計成和輸入層同樣大,即 layers_dims = [12288, 12288, 20, 7, 5, 1]。還只是64*64的小圖片而已,那龜速,我和個人小破筆記本都不能忍啊。這也是爲何大神們發明CNN的緣由之一吧!


問答

TensorFlow:如何進行圖像識別?

相關閱讀

人臉識別技術發展及實用方案設計

圖像分析那些事︱AI來了

圖像識別基本算法之SURF


此文已由做者受權騰訊雲+社區發佈,原文連接:https://cloud.tencent.com/developer/article/1150162?fromSource=waitui

歡迎你們前往騰訊雲+社區或關注雲加社區微信公衆號(QcloudCommunity),第一時間獲取更多海量技術實踐乾貨哦~

相關文章
相關標籤/搜索