TensorFlow.NET機器學習入門【4】采用神經網絡處理分類問題


 上一篇文章我們介紹了通過神經網絡來處理一個非線性回歸的問題,這次我們將采用神經網絡來處理一個多元分類的問題。

這次我們解決這樣一個問題:輸入一個人的身高和體重的數據,程序判斷出這個人的身材狀況,一共三個類別:偏瘦、正常、偏胖。

處理流程如下:

1、收集數據

2、構建神經網絡

3、訓練網絡

4、保存和消費模型

 

 詳細步驟如下:

1、收集數據

對於一個復雜的業務數據,在實際應用時應該是通過收集取得數據,本文的重點不在數據收集,所以我們將制造一批標准數據來進行學習。

關於人體的胖瘦問題,有一個BMI算法,即:BMI=weight / (height * height),當BMI小於18時,認為偏瘦,當BMI大於28時,認為偏胖,18到28之間,認為正常。

首先隨機生成身高和體重的數據,然后計算BMI值,並對結果進行標記,其中,偏瘦標記為0,正常標記為1,偏胖標記為2 。代碼如下:

        /// <summary>
        /// 加載訓練數據
        /// </summary>
        /// <param name="total_size"></param>    
        private (NDArray, NDArray) PrepareData(int total_size)
        {
            float[,] arrx = new float[total_size, num_features];
            int[] arry = new int[total_size];

            for (int i = 0; i < total_size; i++)
            {
                float weight = (float)random.Next(30, 100) / 100;
                float height = (float)random.Next(140, 190) / 100;
                float bmi = (weight * 100) / (height * height);

                arrx[i, 0] = weight;
                arrx[i, 1] = height;

                switch (bmi)
                {
                    case var x when x < 18.0f:
                        arry[i] = 0;
                        break;

                    case var x when x >= 18.0f && x <= 28.0f:
                        arry[i] = 1;
                        break;

                    case var x when x > 28.0f:
                        arry[i] = 2;
                        break;
                }
            }

 

2、構建神經網絡

相對於簡單的非線性模型,本次的網絡結構會稍微復雜一些:

       // 網絡參數
       int num_features = 2; // data features
       int num_classes = 3; // total output 

 

        /// <summary>
        /// 構建網絡模型
        /// </summary>     
        private Model BuildModel()
        {
            // 網絡參數          
            int n_hidden_1 = 64; // 1st layer number of neurons.     
            int n_hidden_2 = 64; // 2nd layer number of neurons.           

            var model = keras.Sequential(new List<ILayer>
            {
                keras.layers.InputLayer(num_features),
                keras.layers.Dense(n_hidden_1, activation:keras.activations.Relu),
                keras.layers.Dense(n_hidden_2, activation:keras.activations.Relu),
                keras.layers.Dense(num_classes, activation:keras.activations.Softmax)
            });

            return model;
        }

首先,本次包含兩層神經網絡,激活函數均采用RELU,輸出層激活函數采用Softmax函數。

和上一篇文章中的網絡結構相比看上去復雜很多,但其本質實際上差別不大,只是多了一個Softmax函數。 

請注意觀察3個Output節點,如果只是看其中一個節點的話,它實際上就是一個普通的非線性模型。

由於1、2、3三個節點的數據之和不一定等於1,Softmax函數的目的就是要使得最終輸出的三個數字之和為1,這樣數字本身就可以表示概率了。其計算方法也非常簡單:

  

最后我們看一下這個網絡的摘要信息:

第一層網絡的訓練參數數量:(2+1)*64=192

第二層網絡的訓練參數數量:(64+1)*64=4160

輸出層網絡的訓練參數數量:(64+1)*3=195

 

3、訓練網絡

            (NDArray train_x, NDArray train_y) = PrepareData(1000);
            model.compile(optimizer: keras.optimizers.Adam(0.001f),
                          loss: keras.losses.SparseCategoricalCrossentropy(),
                          metrics: new[] { "accuracy" });
            model.fit(train_x, train_y, batch_size: 128, epochs: 300);

這里注意一點:損失函數采用稀疏分類交叉熵(SparseCategoricalCrossentropy)方法,對於分類任務,大部分時候都是采用分類交叉熵方法作為損失函數。

下面為二值交叉熵的實現公式:

可以不用看公式,簡單理解交叉熵的含義:就是如果標記值為1時預測值接近1 或 標記值為0時預測值接近0 則損失函數的值就會比較小。

比如標記值為[1,0,0],預測值為[0.99,0.01,0],則損失比較小,反之,如果預測值為[0.1,0.1,0.8],則損失比較大。

下面時一個二值交叉熵的實現方法:

        private Tensor BinaryCrossentropy(Tensor x, Tensor y)
        {
            var shape = tf.reduce_prod(tf.shape(x));
            var count = tf.cast(shape, TF_DataType.TF_FLOAT);
            x = tf.clip_by_value(x, 1e-6f, 1.0f - 1e-6f);
            var z = y * tf.log(x) + (1 - y) * tf.log(1 - x);
            var result = -1.0f / count * tf.reduce_sum(z);
            return result;
        }

稀疏分類交叉熵和二值交叉熵的區別在於:二值交叉熵需要對標記結果進行獨熱編碼(one-hot),而稀疏分類交叉熵則不需要。

前面提到,我們對分類結果進行標記,其中,偏瘦標記為0,正常標記為1,偏胖標記為2;而采用二值交叉熵進行計算時,偏瘦標記為[1,0,0],正常標記為[0,1,0],偏胖標記為[0,0,1] 。

 

4、保存和消費模型

訓練完成后,我們通過消費這個模型來檢查模型的准確性。

        /// <summary>
        /// 消費模型
        /// </summary>      
        private void test(Model model)
        {
            int test_size = 20;
            for (int i = 0; i < test_size; i++)
            {
                float weight = (float)random.Next(40, 90) / 100;
                float height = (float)random.Next(145, 185) / 100;
                float bmi = (weight * 100) / (height * height);

                var test_x = np.array(new float[1, 2] { { weight, height } });
                var pred_y = model.Apply(test_x);

                Console.WriteLine($"{i}:weight={(float)weight} \theight={height} \tBMI={bmi:0.0} \tPred:{pred_y[0].numpy()}");
            }
        }

下面為測試結果:

 

  隨便看兩條數據:當BMI為30.5時,預測結果為[0,0.0016,0.9983];當BMI為12.5時,預測結果為:[1,0,0],可見結果還是准確的。

 

全部代碼如下:

    /// <summary>
    /// 通過神經網絡來實現多元分類
    /// </summary>
    public class NN_MultipleClassification_BMI
    {
        private readonly Random random = new Random(1);

        // 網絡參數
        int num_features = 2; // data features       
        int num_classes = 3;  // total output .

        public void Run()
        {
            var model = BuildModel();
            model.summary();          

            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();

            (NDArray train_x, NDArray train_y) = PrepareData(1000);
            model.compile(optimizer: keras.optimizers.Adam(0.001f),
              loss: keras.losses.SparseCategoricalCrossentropy(),
              metrics: new[] { "accuracy" });
            model.fit(train_x, train_y, batch_size: 128, epochs: 300);

            test(model);
        }

        /// <summary>
        /// 構建網絡模型
        /// </summary>     
        private Model BuildModel()
        {
            // 網絡參數          
            int n_hidden_1 = 64; // 1st layer number of neurons.     
            int n_hidden_2 = 64; // 2nd layer number of neurons.           

            var model = keras.Sequential(new List<ILayer>
            {
                keras.layers.InputLayer(num_features),
                keras.layers.Dense(n_hidden_1, activation:keras.activations.Relu),
                keras.layers.Dense(n_hidden_2, activation:keras.activations.Relu),
                keras.layers.Dense(num_classes, activation:keras.activations.Softmax)
            });

            return model;
        }

        /// <summary>
        /// 加載訓練數據
        /// </summary>
        /// <param name="total_size"></param>    
        private (NDArray, NDArray) PrepareData(int total_size)
        {
            float[,] arrx = new float[total_size, num_features];
            int[] arry = new int[total_size];

            for (int i = 0; i < total_size; i++)
            {
                float weight = (float)random.Next(30, 100) / 100;
                float height = (float)random.Next(140, 190) / 100;
                float bmi = (weight * 100) / (height * height);

                arrx[i, 0] = weight;
                arrx[i, 1] = height;

                switch (bmi)
                {
                    case var x when x < 18.0f:
                        arry[i] = 0;
                        break;

                    case var x when x >= 18.0f && x <= 28.0f:
                        arry[i] = 1;
                        break;

                    case var x when x > 28.0f:
                        arry[i] = 2;
                        break;
                }
            }

            return (np.array(arrx), np.array(arry));
        }

        /// <summary>
        /// 消費模型
        /// </summary>      
        private void test(Model model)
        {
            int test_size = 20;
            for (int i = 0; i < test_size; i++)
            {
                float weight = (float)random.Next(40, 90) / 100;
                float height = (float)random.Next(145, 185) / 100;
                float bmi = (weight * 100) / (height * height);

                var test_x = np.array(new float[1, 2] { { weight, height } });
                var pred_y = model.Apply(test_x);

                Console.WriteLine($"{i}:weight={(float)weight} \theight={height} \tBMI={bmi:0.0} \tPred:{pred_y[0].numpy()}");
            }
        }
    }
View Code

 

【相關資源】

 源碼:Git: https://gitee.com/seabluescn/tf_not.git

項目名稱:NN_MultipleClassification_BMI

目錄:查看TensorFlow.NET機器學習入門系列目錄


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM