訂閱
糾錯
加入自媒體

基于卷積神經網絡的圖像分類

現(xiàn)在是學習卷積神經網絡及其在圖像分類中的應用了。

什么是卷積?

卷積運算是使用具有恒定大小的“窗口”移動圖像,并將圖像像素與卷積窗口相乘以獲得輸出圖像的過程。讓我們看看下面的例子:

我們看到一個9x9圖像和一個3x3卷積濾波器,其恒定權重為3 0 3 2 0 2 1 0 1,以及卷積運算的計算。嘗試使用如下所示的濾波器遍歷圖像,并更好地了解輸出圖像的這些像素是如何通過卷積計算的。

窗口、濾波器、核、掩碼是提到“卷積濾波器”的不同方式,我們也將在本文中使用這些術語。

填充

填充是在輸入圖像邊框上添加額外像素的過程,主要是為了保持輸出圖像的大小與輸入圖像的大小相同。最常見的填充技術是添加零(稱為零填充)。

跨步

步幅是我們使用卷積窗口遍歷圖像時每次迭代的步長。在下面的例子中,我們實際上看到步幅是1,所以我們將窗口移動了1。現(xiàn)在讓我們看另一個例子,更好地理解水平步幅=1,垂直步幅=2:

因此,填充大小、步幅大小、濾波器大小和輸入大小會影響輸出圖像的大小,根據這些不同的參數(shù),輸出圖像大小的公式如下:

到目前為止,我們只研究了一個應用于輸入圖像的卷積運算,現(xiàn)在讓我們看看什么是卷積神經網絡,以及我們如何訓練它們。

卷積神經網絡(CNN)

如果將層作為卷積窗口,窗口中的每個像素實際上是一個權重(而不是我們在前一篇文章中學習的全連接的神經網絡),那么這是一個卷積神經網絡。

我們的目標是訓練模型,以在最后以最小的成本更新這些權重。因此,與前面的例子相反,我們的卷積濾波器中沒有任何常量值,我們應該讓模型為它們找到最佳值。

所以一個簡單的CNN是一些卷積運算的序列,如下所示:

基于CNN的圖像分類

但是如何利用CNN實現(xiàn)圖像分類呢?

圖像分類的唯一區(qū)別是,我們現(xiàn)在處理的是圖像,而不是房價、房間號等結構化數(shù)據。

每個卷積運算都會參與提取圖像特征,例如耳朵、腳、狗的嘴等。隨著卷積層的加深,該特征提取步驟會更深入,而在第一層,我們只獲得圖像的一些邊緣。

因此,卷積層負責提取重要的特征,最后,為了有一個完整的圖像分類模型,我們只需要一些全連接的輸出節(jié)點,根據它們的權重來決定圖像的正確類別!

我們假設狗有一個分類問題。在這種情況下,在訓練結束時,一些輸出節(jié)點將代表狗類特征和一些貓類特征。

如果通過這些卷積層的輸入圖像在激活函數(shù)結束時狗類提供了更高的值,那么他將分類為狗。否則分類為貓。讓我們可視化一下這個過程:

CNN+全連接的神經網絡創(chuàng)建了一個圖像分類模型!

在討論用于圖像分類的常見CNN體系結構之前,讓我們先來看一些更復雜、更真實的CNN示例:

當我們談論CNN層時,我們并不是只討論一層中的一個卷積核;實際上,多個卷積核可以創(chuàng)建一個卷積層。所以我們把所有這些卷積濾波器一個接一個地應用到我們的輸入圖像上,然后我們傳遞到下一個卷積層。1個卷積層中卷積核的數(shù)量有3個,與圖像的“通道大小”相同。

我們學習了如何在應用卷積運算后計算輸出圖像大小,現(xiàn)在你應該知道卷積層的通道大小直接是輸出通道大小,因為我們應用1個卷積運算來獲得1個輸出圖像。因此,如果CNN層內部有5個卷積核,我們在這一層的末尾得到5個輸出核。

如果我們使用RGB圖像,我們有3個通道,而灰度圖像有1個通道。在這種情況下,我們應用1個卷積核3次,以獲得輸出通道。因此,輸出圖像的通道大小不會改變,但卷積層的參數(shù)總數(shù)會改變。1個CNN層的參數(shù)總數(shù)計算如下:

因此,對于我們前面的例子,我們可以說n=64 m=64 l=1 k=32。

池化

這是圖像分類中使用的另一個重要術語。這是一種用于減少CNN模型參數(shù)的方法。

逐次減少參數(shù)的數(shù)量,并且只從特征映射中選擇最重要的特征(來自每個卷積層的輸出映射。正如我們之前所說,更深的卷積層具有更具體的特征)是很重要的。有兩種常見的池類型:

最大池

其基本思想是再次使用窗口,此時不使用任何權重,在遍歷特征圖時,選擇最大像素值作為輸出。

我們使用相同的公式來計算帶有卷積運算的最大池的輸出映射大小,正如我前面提到的。重要的一點是,由于其目的是減少參數(shù)大小,因此給定padding size=0和stride size=池內核的大小是一種合理且常用的方法,就像我們在本例中所做的那樣。

平均池

我們不是根據最大像素計算輸出,而是根據池內核中像素的平均值計算輸出。

通用卷積神經網絡結構

ImageNet大規(guī)模視覺識別挑戰(zhàn)賽(ILSVRC)多年來一直是一項非常受歡迎的比賽。我們將考察不同年份該競賽的一些獲勝者。這些是圖像分類任務中最常見的架構,與其他模型相比,它們具有更高的性能。

ImageNet是一個數(shù)據集,有1281167張訓練圖像、50000張驗證圖像和100000張1000個類的測試圖像。

驗證數(shù)據集:除了用于任何模型的訓練步驟的訓練數(shù)據集和訓練步驟完成后用于測試模型的測試圖像,以計算模型精度性能外,它是模型以前沒有見過的數(shù)據,即在訓練階段不參與反向傳播權重更新階段,但用于測試,以便真實地跟蹤訓練階段的進度。

· AlexNet (2012)

該模型由8層組成,5層卷積,3層全連接。

用于RGB輸入圖像(3通道)

包含6000萬個參數(shù)

ImageNet測試數(shù)據集的最終誤差為15.3%

該模型首次使用了ReLU激活函數(shù)。除了最后一個完全連接的層具有Softmax激活功能外,ReLu用作整個模型的激活功能

使用0.5%的Dropout。

使用動量為0.9且批量為128的隨機梯度下降法

使用標準差=0.01的零平均高斯分布初始化權重

偏置以恒定值1初始化。

學習率初始化為0.01,“權重衰減正則化”應用為0.0005

在上圖中,我們看到了AlexNet體系結構,我們有1000個完全連接層的節(jié)點,因為ImageNet數(shù)據集有1000個類。

在這個架構中,我們還遇到了一些我以前沒有提到的術語:

動量梯度下降:這是對梯度下降計算的優(yōu)化,我們將之前梯度下降的導數(shù)添加到我們的梯度下降計算中。我們用動量超參數(shù)乘以這個附加部分,對于這個架構,動量超參數(shù)是0.9。

高斯分布的權重初始化:在開始訓練模型之前,有不同的方法初始化我們的權重。例如,將每個權重設為0是一種方法,但這是一種糟糕的方法!與此相反,根據高斯分布初始化所有權重是一種常用的方法。我們只需要選擇分布的平均值和標準差,我們的權重將在這個分布范圍內。

權重衰減優(yōu)化:在本文中使用SGD(隨機梯度下降)的情況下,L2正則化也是如此!

· VGG16(2014)

VGG架構是一個16層模型,具有13個卷積和3個全連接。

它有1.38億個參數(shù)

測試數(shù)據集有7.5%的錯誤

與AlexNet相反,每個卷積層中的所有核都使用相同的大小。這是3x3的核,步幅=1,填充=1。最大池為2x2,步長=2

與AlexNet類似,ReLu用于隱藏層,Softmax用于輸出層。動量為0.9的SGD,衰減參數(shù)為0.00005的權重衰減正則化,初始學習率為0.01,使用高斯分布的權重初始化。

作為AlexNet之間的一個小差異,VGG16體系結構使用批量大小=256,將偏差初始化為0,而不是1,并且具有224x224x3的輸入圖像大小。

有一個新版本名為VGG19,共有19層。

· ResNet(2015)

該架構的名稱來自Residual Blocks,其中Residual Blocks是“輸入”和“卷積和激活函數(shù)后的輸出”的組合。

有不同版本的ResNet具有不同數(shù)量的層,如Resnet18、Resnet34、Resnet50、Resnet101和Resnet152。在下圖中,我們在左側看到一個“正!钡18層架構,在右側看到ResNet版本。

紅線表示它們具有相同的尺寸,因此可以直接組合,而藍線表示尺寸不同,因此應使用零填充或在使用1x1卷積內核使用兼容的填充調整大小。

ResNet152實現(xiàn)了測試數(shù)據集的%3.57錯誤,它有58M個參數(shù)。當我們將參數(shù)大小和小誤差與以前的架構進行比較時,效果很好,對嗎?

除了可訓練參數(shù)的數(shù)量外,浮點運算(每秒浮點運算)也是一個重要因素。讓我們比較一下我們迄今為止研究的模型:

如果你想研究更多類型的卷積神經網絡,建議你搜索Inception、SeNet(2017年ILSVRC獲獎者)和MobileNet。

現(xiàn)在是我們將VGG16與Python和Tensorflow結合使用來應用圖像分類的時候了!

VGG架構實現(xiàn)

# import necessary layers  

from tensorflow.keras.layers import Input, Conv2D , Dropout, MaxPool2D, Flatten, Dense

from tensorflow.keras import Model

from tensorflow.keras.preprocessing.image import ImageDataGenerator

from tensorflow.keras.regularizers import l2

import tensorflow as tf
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint

import os

import matplotlib.pyplot as plt

import sys

from tensorflow.keras.callbacks import CSVLogger

MODEL_FNAME = "trained_model.h5"

base_dir = "dataset"

tmp_model_name = "tmp.h5"

INPUT_SIZE = 224

BATCH_SIZE = 16

physical_devices = tf.config.list_physical_devices()

print("DEVICES : ", physical_devices)

print('Using:')

print(' u2022 Python version:',sys.version)

print(' u2022 TensorFlow version:', tf.__version__)

print(' u2022 tf.keras version:', tf.keras.__version__)

print(' u2022 Running on GPU' if tf.test.is_gpu_available() else ' u2022 GPU device not found. Running on CPU')

count = 0

previous_acc = 0

if not os.path.exists(MODEL_FNAME):
 

 """ Create VGG Model"""

  # input

  input = Input(shape =(INPUT_SIZE,INPUT_SIZE,3))
   

  weight_initializer = tf.keras.initializers.RandomNormal(mean=0.0, stddev=0.01, seed=None)

  bias_initializer=tf.keras.initializers.Zeros()
   

  # 1st Conv Block
 

   x = Conv2D (filters =64, kernel_size =3, padding ='same', activation='relu',kernel_initializer=weight_initializer,kernel_regularizer=l2(0.00005),bias_initializer=bias_initializer)(input)

   x = Conv2D (filters =64, kernel_size =3, padding ='same', activation='relu',kernel_initializer=weight_initializer,kernel_regularizer=l2(0.00005),bias_initializer=bias_initializer)(x)

   x = MaxPool2D(pool_size =2, strides =2, padding ='same')(x)
   

  # 2nd Conv Block
   

   x = Conv2D (filters =128, kernel_size =3, padding ='same', activation='relu',kernel_initializer=weight_initializer,kernel_regularizer=l2(0.00005),bias_initializer=bias_initializer)(x)

   x = Conv2D (filters =128, kernel_size =3, padding ='same', activation='relu',kernel_initializer=weight_initializer,kernel_regularizer=l2(0.00005),bias_initializer=bias_initializer)(x)

   x = MaxPool2D(pool_size =2, strides =2, padding ='same')(x)
   

  # 3rd Conv block
   

   x = Conv2D (filters =256, kernel_size =3, padding ='same', activation='relu',kernel_initializer=weight_initializer,kernel_regularizer=l2(0.00005),bias_initializer=bias_initializer)(x)

   x = Conv2D (filters =256, kernel_size =3, padding ='same', activation='relu',kernel_initializer=weight_initializer,kernel_regularizer=l2(0.00005),bias_initializer=bias_initializer)(x)

   x = Conv2D (filters =256, kernel_size =3, padding ='same', activation='relu',kernel_initializer=weight_initializer,kernel_regularizer=l2(0.00005),bias_initializer=bias_initializer)(x)

   x = MaxPool2D(pool_size =2, strides =2, padding ='same')(x)
   

  # 4th Conv block
   

   x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu',kernel_initializer=weight_initializer,kernel_regularizer=l2(0.00005),bias_initializer=bias_initializer)(x)

   x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu',kernel_initializer=weight_initializer,kernel_regularizer=l2(0.00005),bias_initializer=bias_initializer)(x)

   x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu',kernel_initializer=weight_initializer,kernel_regularizer=l2(0.00005),bias_initializer=bias_initializer)(x)

   x = MaxPool2D(pool_size =2, strides =2, padding ='same')(x)
   
          # 5th Conv block
   

   x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu',kernel_initializer=weight_initializer,kernel_regularizer=l2(0.00005),bias_initializer=bias_initializer)(x)

   x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu',kernel_initializer=weight_initializer,kernel_regularizer=l2(0.00005),bias_initializer=bias_initializer)(x)

   x = Conv2D (filters =512, kernel_size =3, padding ='same', activation='relu',kernel_initializer=weight_initializer,kernel_regularizer=l2(0.00005),bias_initializer=bias_initializer)(x)

   x = MaxPool2D(pool_size =2, strides =2, padding ='same')(x)
   

   # Fully connected layers
   

   x = Flatten()(x)

   x = Dropout(0.5)(x)

   x = Dense(units = 4096, activation ='relu', kernel_initializer=weight_initializer,kernel_regularizer=l2(0.00005),bias_initializer=bias_initializer)(x)
          x = Dropout(0.5)(x)

   x = Dense(units = 4096, activation ='relu', kernel_initializer=weight_initializer,kernel_regularizer=l2(0.00005),bias_initializer=bias_initializer)(x)

  output = Dense(units = 2, activation ='softmax')(x)
   

  # creating the model
   

  model = Model (inputs=input, outputs =output)
   

  m = model

  m.save(tmp_model_name)

  del m

  tf.keras.backend.clear_session()
   

  model.summary()

 """ Prepare the Dataset for Training"""
   

  train_dir = os.path.join(base_dir, 'train')

  val_dir = os.path.join(base_dir, 'validation')

   

  train_batches = ImageDataGenerator(rescale = 1 / 255.).flow_from_directory(train_dir,
                                                        target_size=(INPUT_SIZE,INPUT_SIZE),
                                                        shuffle=True,
                                                        seed=42,
                                                        batch_size=BATCH_SIZE)

val_batches = ImageDataGenerator(rescale = 1 / 255.).flow_from_directory(val_dir,
                                                        target_size=(INPUT_SIZE,INPUT_SIZE),
                                                        shuffle=True,
                                                        seed=42,
                                                        batch_size=BATCH_SIZE)
   

  """ Train """
   

  class CustomLearningRateScheduler(tf.keras.callbacks.Callback):

      def __init__(self, schedule):

          super(CustomLearningRateScheduler, self).__init__()

          self.schedule = schedule
       

      def on_epoch_end(self, epoch, logs=None):

          if not hasattr(self.model.optimizer, "lr"):

              raise ValueError('Optimizer must have a "lr" attribute.')

          # Get the current learning rate from model's optimizer.

          lr = float(tf.keras.backend.get_value(self.model.optimizer.learning_rate))

          # Call schedule function to get the scheduled learning rate.

          # keys = list(logs.keys())

          # print("keys",keys)

          val_acc = logs.get("val_binary_accuracy")

          scheduled_lr = self.schedule(lr, val_acc)

          # Set the value back to the optimizer before this epoch starts

          tf.keras.backend.set_value(self.model.optimizer.lr, scheduled_lr)
         

  def learning_rate_scheduler(lr, val_acc):

      global count

      global previous_acc
     

      if val_acc == previous_acc:

        #  print("acc ", val_acc, "previous acc ", previous_acc)

          count += 1

      else:

          count = 0
       

      if count >= 5:

          print("acc is the same for 10 epoch, learnin rate decreased by /10")

          count = 0

          lr /= 10

          print("new learning rate:", lr)
           

      previous_acc = val_acc  

      return lr
   

  #compile the model by determining loss function Binary Cross Entropy, optimizer as SGD

  model.compile(optimizer=tf.keras.optimizers.SGD(lr=0.0000001, momentum=0.9),

                loss=tf.keras.losses.BinaryCrossentropy(),

                metrics=[tf.keras.metrics.BinaryAccuracy()],

                sample_weight_mode=[None])
   

  early_stopping = EarlyStopping(monitor='val_loss', patience=10)
       

  checkpointer = ModelCheckpoint(filepath=MODEL_FNAME, verbose=1, save_best_only=True)
   

  csv_logger = CSVLogger('log.csv', append=True, separator=' ')
   

  history=model.fit(train_batches,

      validation_data = val_batches,

      epochs = 100,

      verbose = 1,

      shuffle = True,

      callbacks = [checkpointer,early_stopping,CustomLearningRateScheduler(learning_rate_scheduler),csv_logger])
   

  """ Plot the train and validation Loss """
   

  plt.plot(history.history['loss'])

  plt.plot(history.history['val_loss'])

  plt.title('model loss')

  plt.ylabel('loss')

  plt.xlabel('epoch')

  plt.legend(['train', 'validation'], loc='upper left')

  plt.show()
   

  """ Plot the train and validation Accuracy """
 

  plt.plot(history.history['binary_accuracy'])

  plt.plot(history.history['val_binary_accuracy'])

  plt.title('model accuracy')

  plt.ylabel('accuracy')

  plt.xlabel('epoch')

  plt.legend(['train', 'validation'], loc='upper left')

  plt.show()
 

  print("End of Training")
   

else:
   

  """ Test """
   

  test_dir = os.path.join(base_dir, 'test')
   

  test_batches = ImageDataGenerator(rescale = 1 / 255.).flow_from_directory(test_dir,
                                                      target_size=(INPUT_SIZE,INPUT_SIZE),
                                                      class_mode='categorical',
                                                      shuffle=False,
                                                      seed=42,
                                                      batch_size=1)

  model = tf.keras.models.load_model(MODEL_FNAME)

  model.summary()
   

  # Evaluate on test data

  scores = model.evaluate(test_batches)

  print("metric names",model.metrics_names)
 

  print(model.metrics_names[0], scores[0])

  print(model.metrics_names[1], scores[1])
       

tf.keras.backend.clear_session()

這是使用Python和Tensorflow實現(xiàn)的VGG16。讓我們稍微檢查一下代碼

· 首先,我們檢查我們的TensorFlow Cuda Cudnn安裝是否正常,以及TensorFlow是否可以找到我們的GPU。因為如果不是這樣的話,那就意味著有一個包沖突或一個錯誤,我們應該解決,因為使用CPU進行模型訓練太慢了。

· 然后,我們使用Conv2D函數(shù)為卷積層創(chuàng)建VGG16模型,MaxPool2D函數(shù)為最大池層,展平函數(shù)為CNN輸出一個能夠傳遞到全連接層的展平輸入,Dense函數(shù)為全連接層,Dropout函數(shù)為最后一個全連接層之間添加Dropout優(yōu)化。可以看到,我們應該在使用相關層函數(shù)的同時,將權重初始化、偏差初始化、l2正則化器1x1添加到層中。請注意,正態(tài)分布是高斯分布的同義詞,所以當看到weight_initializer = tf.keras.initializers.RandomNormal(mean=0.0, stddev=0.01, seed=None),不要困惑

· 將用兩個類來測試這個模型,而不是用1000個類來測試巨大的ImageNet數(shù)據集,所以我將輸出層從1000個節(jié)點更改為2個節(jié)點

· 使用ImageGenerator函數(shù)和flow_from_directory,我們將數(shù)據集準備為能夠使用TensorFlow模型的向量。我們這里也給出了批量大小。(由于我的電腦內存不足,我可以給出16個,而不是論文中提到的256個。shuffle參數(shù)意味著數(shù)據集將在每個epoch前被打亂

· model.compile()函數(shù)是訓練模型之前的最后一部分。我們決定使用哪個優(yōu)化器(帶動量的SGD)、哪個損失函數(shù)(因為在我們的例子中我們有兩個類),以及在訓練期間計算性能的指標(二進制精度)。

· 使用model.fit()訓練我們的模型。添加了一些選項,比如“EarlyStoping”和“ModelCheckPoint”回調。如果驗證損失在10個epoch內沒有增加,則第一個epoch停止訓練,如果驗證損失比前一epoch好,則模型檢查點不僅在最后保存我們的模型,而且在訓練期間保存我們的模型。

· CustomLearningRateScheduler是我手動實現(xiàn)的學習速率調度器,用于應用“如果驗證準確率停止提高,我們將通過除以10來更新學習速率”

· 在訓練結束時,我將驗證和訓練數(shù)據集的準確性和損失圖可視化。

· 最后一部分是使用測試數(shù)據集測試模型。我們檢查是否有任何名為“trained_model.h5”的訓練模型,如果沒有,我們訓練一個模型,如果有,我們使用這個模型來測試性能

想解釋一下我在解釋代碼時使用的一些術語:

· 驗證集準確率:驗證集準確率是一種衡量模型在驗證數(shù)據集上表現(xiàn)的指標。它只是檢查在驗證數(shù)據集中有多少圖像預測為真。我們也會對訓練數(shù)據集進行同樣的檢查。但這只是為了我們監(jiān)控模型,只有訓練損失用于梯度下降計算

· 迭代-epoch:1迭代是為一批中的所有圖像提供模型的過程。Epoch是指為訓練數(shù)據集中的所有圖像提供模型的過程。例如,如果我們有100張圖片,batch=10,那么1個epoch將有10次迭代。

· 展平:這只不過是重塑CNN輸出,使其具有1D輸入,這是從卷積層傳遞到全連接層的唯一方法。

· 現(xiàn)在讓我們檢查一下結果

即使通過監(jiān)控驗證準確度來改變學習率,結果也不是很好,驗證準確度也沒有提高。但是為什么它不能像VGG16論文中那樣直接工作呢?

1. 在最初的論文中,我們討論了374個epoch的1000個輸出類和1500000個圖像。不幸的是,使用這個數(shù)據集并訓練1000個類的模型需要幾天的時間,所以正如我之前所說,我使用了兩個類的數(shù)據集,并將輸出層更改為有兩個輸出節(jié)點。

1. 模型體系結構和數(shù)據集都需要不同的優(yōu)化,因此一個運行良好的模型可能不適用于另一個數(shù)據集。

3. 微調超參數(shù)以改進模型與首先了解如何構建模型一樣重要。你可能已經注意到我們需要微調多少超參數(shù)

· 權重和偏差初始化

· 損失函數(shù)選擇

· 初始學習率選擇

· 優(yōu)化器選擇(梯度下降法)

· Dropout與否的使用

· 數(shù)據擴充(如果你沒有足夠的圖像,或者它們太相似,你希望獲得相同圖像的不同版本)

等等…

但在你對如何優(yōu)化這么多變量感到悲觀之前,想提兩點。

1.遷移學習

這是一種非常重要的方法,我們使用預先訓練好的模型,用我們自己的數(shù)據集對其進行訓練。在我們的例子中,我們不會只使用VGG16體系結構,而是一個已經使用VGG16體系結構和ImageNet數(shù)據集訓練過的模型,我們將使用比ImageNet小得多的數(shù)據集對其進行重新訓練。

這種方法為我們提供了一個并非未知的起點——一些隨機權重。我們的數(shù)據集可能包含不同的對象,但不同對象之間有一些基本的共同特征,比如邊、圓形,為什么不使用它們,而不是從頭開始呢?我們將看到這種方法如何減少耗時并提高精度性能。

2.當查看我們的輸出時,發(fā)現(xiàn)問題不是因為模型停止學習,而是因為它沒有開始學習!精度從0.5開始,沒有改變!這個具體的結果給了我們一個非常明確的信息:我們應該降低學習率,讓模型開始學習。請記住,學習率是學習的步長,每次迭代的梯度下降計算會對權重產生多大影響。如果這個速率太大,步驟太大,我們無法控制權重更新,因此模型無法學習任何東西。

以下代碼僅包括遷移學習的更改,其中采用了帶有預訓練權重Tensorflow內置模型和較低學習率(0.001)的模型

# -*- coding: utf-8 -*-

"""

Created on Wed Dec 29 20:56:04 2021

@author: aktas

"""

# import necessary layers  

from tensorflow.keras.preprocessing.image import ImageDataGenerator

from tensorflow.keras.regularizers import l2

import tensorflow as tf

from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint

import os

import matplotlib.pyplot as plt

import sys

from tensorflow.keras.callbacks import CSVLogger

from tensorflow.keras.applications import vgg16, imagenet_utils

from tensorflow.keras.layers import Dense, Flatten

from tensorflow.keras.models import Model

MODEL_FNAME = "pretrained_model.h5"

tmp_model_name = "tmp.h5"

base_dir = "dataset"

INPUT_SIZE = 224

BATCH_SIZE = 16

physical_devices = tf.config.list_physical_devices()

print("DEVICES : ", physical_devices)

print('Using:')

print(' u2022 Python version:',sys.version)

print(' u2022 TensorFlow version:', tf.__version__)

print(' u2022 tf.keras version:', tf.keras.__version__)

print(' u2022 Running on GPU' if tf.test.is_gpu_available() else '

u2022 GPU device not found. Running on CPU')

count = 0

previous_acc = 0

if not os.path.exists(MODEL_FNAME):
   

  base_model = vgg16.VGG16(weights='imagenet', include_top=False, input_shape=(INPUT_SIZE,INPUT_SIZE,3))
   

  m = base_model

  m.save(tmp_model_name)

  del m

  tf.keras.backend.clear_session()
   

  print("Number of layers in the base model: ", len(base_model.layers))

  base_model.trainable = False

  last_output = base_model.output

  x = Flatten()(last_output)

  x = Dense(2, activation='softmax')(x)

  model = Model(inputs=[base_model.input], outputs=[x])
   

  model.summary()
   

  """ Prepare the Dataset for Training"""
   
   

  train_dir = os.path.join(base_dir, 'train')
   val_dir = os.path.join(base_dir, 'validation')

   

  train_batches = ImageDataGenerator(rescale = 1 / 255.).flow_from_directory(train_dir,
                                                        target_size=(INPUT_SIZE,INPUT_SIZE),
                                                        shuffle=True,
                                                        seed=42,
                                                        batch_size=BATCH_SIZE)

  val_batches = ImageDataGenerator(rescale = 1 / 255.).flow_from_directory(val_dir,
                                                        target_size=(INPUT_SIZE,INPUT_SIZE),
                                                        shuffle=True,
                                                        seed=42,
                                                        batch_size=BATCH_SIZE)
   

  """ Train """
   

  class CustomLearningRateScheduler(tf.keras.callbacks.Callback):

      def __init__(self, schedule):

          super(CustomLearningRateScheduler, self).__init__()

          self.schedule = schedule
       

      def on_epoch_end(self, epoch, logs=None):

          if not hasattr(self.model.optimizer, "lr"):

              raise ValueError('Optimizer must have a "lr" attribute.')

          # Get the current learning rate from model's optimizer.

          lr = float(tf.keras.backend.get_value(self.model.optimizer.learning_rate))

          # Call schedule function to get the scheduled learning rate.

          # keys = list(logs.keys())

          # print("keys",keys)

          val_acc = logs.get("val_binary_accuracy")

          scheduled_lr = self.schedule(lr, val_acc)

          # Set the value back to the optimizer before this epoch starts

          tf.keras.backend.set_value(self.model.optimizer.lr, scheduled_lr)
         

  def learning_rate_scheduler(lr, val_acc):

      global count

      global previous_acc
     

      if val_acc <= previous_acc:

        #  print("acc ", val_acc, "previous acc ", previous_acc)

          count += 1

      else:

          previous_acc = val_acc  

          count = 0
       

      if count >= 5:

          print("acc is the same for 10 epoch, learnin rate decreased by /10")

          count = 0

          lr /= 10

          print("new learning rate:", lr)
           
     

      return lr
   

  #compile the model by determining loss function Binary Cross Entropy, optimizer as SGD

  model.compile(optimizer=tf.keras.optimizers.SGD(lr=0.001, momentum=0.9),
                 loss=tf.keras.losses.BinaryCrossentropy(),
                 metrics=[tf.keras.metrics.BinaryAccuracy()],
                 sample_weight_mode=[None])
   

  early_stopping = EarlyStopping(monitor='val_loss', patience=10)
       

  checkpointer = ModelCheckpoint(filepath=MODEL_FNAME, verbose=1, save_best_only=True)
   

  csv_logger = CSVLogger('log.csv', append=True, separator=' ')
   

  history=model.fit(train_batches,

      validation_data = val_batches,

      epochs = 100,

      verbose = 1,

      shuffle = True,

      callbacks = [checkpointer,early_stopping,CustomLearningRateScheduler(learning_rate_scheduler),csv_logger])
   

  """ Plot the train and validation Loss """
   

  plt.plot(history.history['loss'])

  plt.plot(history.history['val_loss'])

  plt.title('model loss')

  plt.ylabel('loss')

  plt.xlabel('epoch')

  plt.legend(['train', 'validation'], loc='upper left')

  plt.show()
   

  """ Plot the train and validation Accuracy """
 

  plt.plot(history.history['binary_accuracy'])

  plt.plot(history.history['val_binary_accuracy'])

  plt.title('model accuracy')

  plt.ylabel('accuracy')

  plt.xlabel('epoch')

  plt.legend(['train', 'validation'], loc='upper left')

  plt.show()
 

  print("End of Training")
   

else:
   

  """ Test """
   

  test_dir = os.path.join(base_dir, 'test')
   

  test_batches = ImageDataGenerator(rescale = 1 / 255.).flow_from_directory(test_dir,
                                                      target_size=(INPUT_SIZE,INPUT_SIZE),
                                                      class_mode='categorical',
                                                      shuffle=False,
                                                      seed=42,
                                                      batch_size=1)

  model = tf.keras.models.load_model(MODEL_FNAME)

  model.summary()
   

  # Evaluate on test data

  scores = model.evaluate(test_batches)

  print("metric names",model.metrics_names)
 

  print(model.metrics_names[0], scores[0])

  print(model.metrics_names[1], scores[1])
       

tf.keras.backend.clear_session()

讓我們檢查一下結果:

性能有了很大的提高,對吧?!我們看到,驗證精度不再被卡住,直到0.97,而訓練精度達到1.00。

一切似乎都很好!

想和大家分享我做的一些實驗及其結果:

現(xiàn)在,我們可以做以下分析:

如果我們有一個不兼容的學習率,遷移學習本身仍然是不夠的。

如果我們將base_model_trainable變量設置為False,這意味著我們不訓練我們所使用的模型,我們只訓練我們添加的最后一個全連接層。(VGG有1000個輸出,所以我使用include_top=False不使用最后的全連接層,并且我在這個預訓練模型的末尾添加了一個自己的全連接層,正如我們在代碼中看到的那樣)

從零開始改變了vgg_from_scratch實現(xiàn)的學習率,但validation 準確率仍然停留在0.5上,所以僅僅優(yōu)化學習率本身是不夠的。(至少如果你沒有時間在微調后從頭開始訓練模型。)

可以通過此鏈接訪問使用的數(shù)據集,請不要忘記將其與下面的代碼放在同一文件夾中(當然,訓練完成后,.h和日志文件會出現(xiàn)????):

https://drive.google.com/drive/folders/1vt8HiybDroEMCvpdGQJx2T50Co4nZNYe?usp=sharing

使用Anaconda和Python=3.7、Tensorflow=2.2.0、Cuda=10.1、Cudnn=7.6.5來使用GPU訓練和測試我的模型。如果你不熟悉這些術語,這里有一個快速教程:

下載Anaconda(請為你的計算機選擇正確的系統(tǒng))

打開Anaconda terminal,并使用conda create-n im_class python=3.7 Anaconda命令創(chuàng)建環(huán)境。環(huán)境可能是Anaconda最重要的屬性,它允許你為不同的項目單獨工作。你可以將任何包添加到你的環(huán)境中,如果你需要另一個包的另一個版本,你可以簡單地創(chuàng)建另一個環(huán)境,以避免另一個項目崩潰,等等。

使用conda activate im_class命令進入你的環(huán)境以添加更多包(如果你忘記了這一步,你基本上不會對你的環(huán)境進行更改,而是在所有anaconda空間中進行更改)

使用pip install Tensorflow GPU==2.2.0安裝具有GPU功能的Tensorflow

使用conda Install cudatoolkit=10.1命令安裝CUDA和Cudnn

現(xiàn)在,你已經準備好測試上述代碼了!

請注意,軟件包版本之間的沖突是一個巨大的問題。這將幫助你在構建環(huán)境時花費更少的時間。

你已經完成了卷積神經網絡圖像分類教程。你可以嘗試從頭開始構建任何模型(甚至可能是你自己的模型)對其進行微調,針對不同的體系結構應用遷移學習,等等。

參考引用

image.png


       原文標題 : 基于卷積神經網絡的圖像分類

聲明: 本文由入駐維科號的作者撰寫,觀點僅代表作者本人,不代表OFweek立場。如有侵權或其他問題,請聯(lián)系舉報。

發(fā)表評論

0條評論,0人參與

請輸入評論內容...

請輸入評論/評論長度6~500個字

您提交的評論過于頻繁,請輸入驗證碼繼續(xù)

暫無評論

暫無評論

人工智能 獵頭職位 更多
掃碼關注公眾號
OFweek人工智能網
獲取更多精彩內容
文章糾錯
x
*文字標題:
*糾錯內容:
聯(lián)系郵箱:
*驗 證 碼:

粵公網安備 44030502002758號