C#中的深度學習(四):使用Keras.NET識別硬幣

碼農譯站發表於2020-12-24

在本文中,我們將研究一個卷積神經網路來解決硬幣識別問題,並且我們將在Keras.NET中實現一個卷積神經網路。

在這裡,我們將介紹卷積神經網路(CNN),並提出一個CNN的架構,我們將訓練它來識別硬幣。

什麼是CNN?正如我們在本系列的前一篇文章中提到的,CNN是一類經常用於影像分類任務的神經網路(NN),比如物體和人臉識別。在CNN中,並非每個節點都連線到下一層的所有節點。這種部分連通性有助於防止在完全連線的網路神經網路中出現的過擬合問題,並且加速了神經網路的收斂速度。

圍繞CNN的核心概念是一種被稱為卷積的數學運算,卷積在數字訊號處理領域非常常見。卷積被定義為兩個函式的乘積,產生的第三個函式表示前兩個函式之間的重疊量。

在物體識別中,卷積操作允許我們檢測影像中的不同特徵,如垂直和水平的邊緣,紋理和曲線。這就是為什麼任何CNN的第一層都是卷積層。

CNN中另一個常見的層是池化層。池化用於減少影像表示的大小,這意味著減少引數的數量,並最終減少計算量。最常見的池化型別是最大池化,它在每個位置上從匹配的單元格組中獲取最大值。最後,根據所獲得的最大值建立新的影像。

另一個與卷積相關的概念是填充。填充保證了卷積過程將均勻地發生在整個影像,包括邊界畫素。這個保證是由一個零畫素的邊框支援的,該邊框在縮小後的影像周圍新增(在池化之後),以便可以以相同的次數訪問影像的所有畫素。

最常見的CNN架構通常從卷積層開始,接著是啟用層,然後是池化層,最後是傳統的全連線網路,比如多層NN。這種型別的模型是層次化的,稱為順序模型。為什麼以一個完全連線的網路結束?為了學習轉換後影像(經過卷積和池化)中的特徵的非線性組合。

下面是我們將在CNN中實現的架構:

  • Conv2D層- 32個過濾器,過濾器大小為3
  • 啟用層使用ReLU函式
  • Conv2D層- 32個過濾器,過濾器大小為3
  • 啟用層使用ReLU函式
  • MaxPooling2D層-應用(2,2)池視窗
  • DropOut圖層,25% -通過隨機刪除前一層的一些值來防止過擬合(設定為0);也就是稀釋法
  • Conv2D層- 64個過濾器,過濾器大小為3
  • 啟用層使用ReLU函式
  • Conv2D圖層- 64個過濾器,過濾器大小為3,步幅為3
  • 啟用層使用ReLU函式
  • MaxPooling2D層-應用(2,2)池視窗
  • DropOut層,25%
  • Flatten層-轉換資料,以在下一層中使用
  • Dense 層——表示具有512個節點的傳統神經網路的全連線。
  • 啟用層使用ReLU函式
  • DropOut層,在50%
  • Dense層,與節點數量匹配的類的數量
  • Softmax層

該體系結構遵循了一種用於物體識別的CNN體系結構模式;層引數通過實驗進行了微調。

我們經過的引數微調過程的結果部分儲存在Settings類中,我們在這裡展示:

public class Settings
{
        public const int ImgWidth = 64;
        public const int ImgHeight = 64;
        public const int MaxValue = 255;
        public const int MinValue = 0;
        public const int Channels = 3;
        public const int BatchSize = 12;
        public const int Epochs = 10;
        public const int FullyConnectedNodes = 512;
        public const string LossFunction = "categorical_crossentropy";
        public const string Accuracy = "accuracy";
        public const string ActivationFunction = "relu";
        public const string PaddingMode = "same";
        public static StringOrInstance Optimizer = new RMSprop(lr: Lr, decay: Decay);
        private const float Lr = 0.0001f;
        private const float Decay = 1e-6f;
}

我們現在有了CNN的體系結構。接下來,我們將研究使用Keras.NET實現的用於硬幣識別的CNN。

首先,讓我們從Nuget包管理器下載Keras.NET包。我們可以在Tools > Nuget package manager中找到Nuget包管理器。Keras.NET依賴於包Numpy.NET和pythonnet_netstandard。如果沒有安裝它們,讓我們繼續安裝它們。

需要指出的是,Keras.NET 需要在你的作業系統中安裝Python 2.7-3.7版本。它還需要安裝Python庫Numpy和TensorFlow。在本例中,我們使用的是64位的Python 3.7。

如果在執行本文中的程式碼時遇到任何問題,請在控制檯應用程式中主方法的開始執行時嘗試執行以下程式碼一次。這段程式碼將設定所需的環境變數,以便找到所有dll:

private static void SetupPyEnv()
{
     string envPythonHome = @"C:\Users\arnal\AppData\Local\Programs\Python\Python37\";
     string envPythonLib = envPythonHome + "Lib\\;" + envPythonHome + @"Lib\site-packages\";
     Environment.SetEnvironmentVariable("PYTHONHOME", envPythonHome, EnvironmentVariableTarget.Process);
     Environment.SetEnvironmentVariable("PATH", envPythonHome + ";" + envPythonLib + ";" + Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Machine), EnvironmentVariableTarget.Process);
     Environment.SetEnvironmentVariable("PYTHONPATH", envPythonLib, EnvironmentVariableTarget.User);
     PythonEngine.PythonHome = envPythonHome;
     PythonEngine.PythonPath = Environment.GetEnvironmentVariable("PYTHONPATH");
}

現在我們將看到使用Keras.NET建立我們的硬幣識別CNN是多麼簡單和透明。下面的類顯示了包含模型的所有邏輯的Cnn類。

public class Cnn
    {
        private DataSet _dataset;
        private Sequential _model;

        public Cnn(DataSet dataset)
        {
            _dataset = dataset;
            _model = new Sequential();
        }

        public void Train()
        {
            // Build CNN model
            _model.Add(new Conv2D(32, kernel_size: (3, 3).ToTuple(),
                                 padding: Settings.PaddingMode,
                                 input_shape: new Shape(Settings.ImgWidth, Settings.ImgHeight, Settings.Channels)));
            _model.Add(new Activation(Settings.ActivationFunction));
            _model.Add(new Conv2D(32, (3, 3).ToTuple()));
            _model.Add(new Activation(Settings.ActivationFunction));
            _model.Add(new MaxPooling2D(pool_size: (2, 2).ToTuple()));
            _model.Add(new Dropout(0.25));

            _model.Add(new Conv2D(64, kernel_size: (3, 3).ToTuple(),
                                padding: Settings.PaddingMode));
            _model.Add(new Activation(Settings.ActivationFunction));
            _model.Add(new Conv2D(64, (3, 3).ToTuple()));
            _model.Add(new Activation(Settings.ActivationFunction));
            _model.Add(new MaxPooling2D(pool_size: (2, 2).ToTuple()));
            _model.Add(new Dropout(0.25));

            _model.Add(new Flatten());
            _model.Add(new Dense(Settings.FullyConnectedNodes));
            _model.Add(new Activation(Settings.ActivationFunction));
            _model.Add(new Dropout(0.5));
            _model.Add(new Dense(_dataset.NumberClasses));
            _model.Add(new Softmax());
            
            _model.Compile(loss: Settings.LossFunction,
              optimizer: Settings.Optimizer, 
              metrics: new string[] { Settings.Accuracy });
            
            _model.Fit(_dataset.TrainX, _dataset.TrainY,
                          batch_size: Settings.BatchSize,
                          epochs: Settings.Epochs,
                          validation_data: new NDarray[] { _dataset.ValidationX, _dataset.ValidationY });

            var score = _model.Evaluate(_dataset.ValidationX, _dataset.ValidationY, verbose: 0);
            Console.WriteLine("Test loss:" + score[0]);
            Console.WriteLine("Test accuracy:" + score[1]);
        }

        public NDarray Predict(string imgPath)
        {
            NDarray x = Utils.Normalize(imgPath);
            x = x.reshape(1, x.shape[0], x.shape[1], x.shape[2]);
            return _model.Predict(x);
        }
}

如我們所見,我們首先有一個建構函式,用於接收資料集(在本系列的第二篇文章中匯入和處理),並建立Sequential類的新例項儲存在私有變數_model中。Sequential是什麼?這是一個空模型,它給了我們疊加層次的可能性,而這正是我們所需要的。

然後,在Train方法中,我們首先按照上一篇文章中介紹的架構建立我們的層堆疊,然後編譯模型並呼叫fit方法開始訓練。使用的損失函式是categorical_crossentropy。什麼是損失函式?它是我們用來優化學習過程的函式,也就是說,我們要麼最小化它,要麼最大化它。負責最小化損失函式的是優化器——一種通過改變網路的權重和學習率來最小化損失的演算法。

最後,利用驗證資料集對模型進行評估。另一個方法是Predict,顧名思義,它預測新輸入資料的標籤。訓練結束後,應呼叫此方法。開始訓練階段就像執行以下程式碼一樣簡單:

var cnn = new Cnn(dataSet);
cnn.Train();

讓我們來看看在這個系列中我們正在經歷的硬幣識別問題的訓練中所獲得的結果:

我們可以看到,在訓練過程中,我們能夠達到100%的準確率。在prediction方法中,它的輸出將是一個NDarray,它包含了物體或影像屬於CNN某個類的概率。

那麼,什麼樣的架構需要GPU而不是CPU呢?例如,AlexNet體系結構包括5個卷積層和3個完全連線的層,以及池化和啟用層。這種型別的深度CNN由於其複雜性,在GPU上表現得更好。一般的規則是,你新增的層越多,權重的計算就會越複雜。

在瞭解瞭如何編寫自己的CNN之後,我們將進入預訓練模型的領域。下一篇文章將詳細介紹這一點!

歡迎關注我的公眾號,如果你有喜歡的外文技術文章,可以通過公眾號留言推薦給我。

原文連結:https://www.codeproject.com/Articles/5284228/Deep-Learning-in-Csharp-Coin-Recognition-in-Keras

 

相關文章