《神經網絡與機器學習》學習筆記——第1章 感知器


這是本人學習《神經網絡與機器學習》時查找各種資料的總結的筆記,為了讓自己的印象更深刻並且分享些經驗出來,最后有C#與MATLAB的代碼實現。

如果有不對的地方歡迎指點。

這里需要有一定的數學基礎,如:線性代數,微積分,矩陣,概率,統計等。

 

目錄:

1.概念

2.感知器學習

3.誤差修正學習算法

4.代碼實現(C#與MATLAB)

 

《神經網絡與機器學習》學習筆記——第1章 感知器

 

一、概念 

      感知器是生物神經細胞的簡單抽象,神經細胞結構大致可分為:樹突、突觸、細胞體及軸突。
      單個神經細胞可被視為一種只有兩種狀態的機器——激動時為‘是’,而未激動時為‘否’。神經細胞的狀態取決於從其它的神經細胞收到的輸入信號量,及突觸的強度(抑制或加強)。當信號量總和超過了某個閾值時,細胞體就會激動,產生電脈沖。電脈沖沿着軸突並通過突觸傳遞到其它神經元。為了模擬神經細胞行為,與之對應的感知機基礎概念被提出,如權值(突觸)偏置(閾值)激活函數(細胞體)

  

 1.感知器是使用特征向量來表示的前饋式人工神經網絡,它是一種二元分類器,把矩陣上的輸入(實數值向量)映射到輸出值。由於輸入直接經過權重關系轉換為輸出,所以感知機可以被視為最簡單形式的前饋式人工神經網絡。它是建立在一個非線性神經元上,即神經元的 McCulloch-Pitts 模型。

 2.神經元模型是一個線性組合器和隨后的硬限幅器(激活函數)組合(如下圖1.1)。

                                      圖1.1

感知器的輸入值記為:權值記為:偏置值記為:b。

輸入值:表示將要分類的事物的特性的組合,對應每個神經細胞接收到外部輸入的刺激信號,即:初始特征

權值:表示該模型的分類變化參數,也是該模型在學習過程中需要計算出的參數

偏置值(Bias Units):它是一個不依賴於任何輸入值的常數。可以認為是激活函數的偏移量,或者給神經元一個基礎活躍等級。從空間上看(如:圖1.2),該值的作用僅僅是把決策邊界(圖1.2)從原點移開。

誘導局部域:每一個輸入值與其對應的權值相乘,然后將這些乘積的結果相加得到的結果,即:v 的值(PS:這概念是本人在看《神經網絡與機器學習》一書的譯本中看到的)。

公式1.1

 

硬限幅器(激活函數):使用誘導局部域作為輸入參數,當輸入為正時,感知器輸出 +1,反之則輸出 -1

公式1.2

 

3.感知器的目的是把輸入值表示的點確的分為   或者   兩類。

 

   感知器的分類的規則是:

 

   ①感知器的輸出值 +1 就將表示的點分配給   類。

 

   ②感知器的輸出值 y  -1 就將表示的點分配給   類。

 

 4.決策邊界。

根據前面兩點,可得出:

①當時, 屬於  類。

②當時, 屬於  類。

 所以在圖1.2中,由一個超平面分開的兩個區域,則決策邊界超平面定義是:
公式1.3 

 

該圖中決策邊界是條直線,位於邊界線上面的兩個綠點分入,位於邊界線下面的兩個紅點分入

                         

                             圖1.2

 

5.感知器只能對線性可分的兩個類進行分類。

                                                              圖1.3

二、感知器學習

 

首先,這里引用了一張在帖吧上看到的一張圖片:

                                      圖1.4

     我們看到上圖的汽車的時候,會記憶它的一些基本特征(比如:有4個輪子,車身灰白色等等),如果沒有人告訴我們“車”的概念,我們雖然記下了它的大概基本特征,但卻並不知道它是什么。同樣,如果有人告訴我們“車”是什么,但我們卻從來沒有看見過車,同樣對“車”一無所知。只有我們看到過車並記憶了它的一些基本特征,並且有人告訴我們,這就是“車”,當我們再次看到相似的事物的時候,我們自然就會判斷,這就是“車”。

感知器也一樣,給出待分類的幾個點()就如同我們人只見過“車”但卻沒有“車”這一概念時一樣並不知道這就是“車”這一種類,感知器是無法知道它們是屬於類還是類。

 

這時,我們需要讓感知器正確的識別出是屬於類還是類的過程,就是感知器學習(訓練)的過程,而在學習(訓練)過程中需要使用的已分類好的一組數據,就是訓練樣本,當感知器完成了學習(訓練)之后,就能正確的識別與分類了。

------------------------------------------------------------------------------------------------

  示例1:

 

------------------------------------------------------------------------------------------------

三、誤差修正學習算法 

      誤差修正學習算法是通過多次迭代的方式來調整感知器的權值,使感知器的輸出信號 的值逐漸逼近給定訓練樣本的輸出值。

     在上面的示例1中,使用誤差修正學習算法即可找到一組合適的權值()以完成訓練

設:

訓練樣本中,屬於類的樣本所組成的子集,

訓練樣本中,屬於類的樣本所組成的子集,

的並集是整個訓練樣本集n 是訓練樣本個數(迭代次數),給定訓練樣本集來訓練感知器,訓練過程中對權值向量  的調整使兩個類線性可分

即:

也就是說,存在一個權值  具有以下性質:

公式1.4

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

補充說明:

示例1中,訓練樣本是坐標軸上的一個,因此該點的初始特征就是坐標,因此,輸入值就有兩個,所以對應的權值也有兩個,所以使用矩陣的形式來表示公式1.1,則是:

所以,感知器的訓練問題就是找到一組權值向量  滿足公式1.4中的兩個不等式。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

使感知器的權值自適應的算法現在可以用以下公式來描述

1.假設訓練集合的第 n 個成員 x(n) 根據算法中的第 n 次迭代的權值向量 w(n) 能正確分類,那么感知器的權值向量按下述規則不做修改: 

公式1.5

 
2.否則,感知器的權值向量根據以下規則進行修改:
 
公式1.6
 
這里的學習率控制着第 n 次迭代中作用於權值向量的調節,即:控制權值修改的幅度,這里的學習率只要是正數,具體值並不重要。 
 
3.引入一個期望值,定義為: 
 
 
 
激活函數使用sgn(·)來表示,並根據公式1.2公式1.4得到: 
 
 
公式1.6變成:
 
 公式1.7
 
 其中,表示期望輸出值與實際輸出值的誤差,η 是學習率,取值范圍是。 
 

四、代碼實現 

1.C# 實現
 
為了方便開發,首先,是矩陣類的實現:
 
Matrix.cs
    /// <summary>
    /// 矩陣。
    /// </summary>
    public class Matrix
    {
        #region 字段

        /// <summary>
        /// 矩陣數據。
        /// </summary>
        private readonly double[][] _data;

        #endregion 字段

        #region 初始化

        /// <summary>
        /// 初始化。
        /// </summary>
        /// <param name="row">行=列。</param>
        public Matrix(int row) : this(row, row)
        {
        }

        /// <summary>
        /// 初始化。
        /// </summary>
        /// <param name="row">行。</param>
        /// <param name="column">列。</param>
        public Matrix(int row, int column)
        {
            Row = row;
            Column = column;
            _data = new double[row][];

            for (var index = 0; index < row; index++)
            {
                _data[index] = new double[column];
            }
        }

        /// <summary>
        /// 初始化。
        /// </summary>
        /// <param name="row">行。</param>
        /// <param name="column">列。</param>
        /// <param name="value">初始值。</param>
        public Matrix(int row, int column, double value) : this(row, column, index => value)
        {
        }

        /// <summary>
        /// 初始化。
        /// </summary>
        /// <param name="row">行。</param>
        /// <param name="column">列。</param>
        /// <param name="func">初始值計算方法。</param>
        public Matrix(int row, int column, Func<int, double> func)
        {
            Row = row;
            Column = column;
            _data = new double[row][];

            for (var index = 0; index < row; index++)
            {
                _data[index] = new double[column];

                if (func != null)
                {
                    for (var loop = 0; loop < column; loop++)
                    {
                        _data[index][loop] = func(loop);
                    }
                }
            }
        }

        /// <summary>
        /// 初始化。
        /// </summary>
        /// <param name="matrix">矩陣。</param>
        public Matrix(double[][] matrix)
        {
            if (matrix == null || matrix.Length <= 0)
                throw new ArgumentNullException(nameof(matrix));

            Row = matrix.Length;
            Column = matrix[0].Length;
            _data = matrix;
        }

        /// <summary>
        /// 復制矩陣。
        /// </summary>
        /// <param name="matrix">矩陣。</param>
        public Matrix(Matrix matrix)
        {
            var row = matrix.Row;
            var column = matrix.Column;
            Row = row;
            Column = column;
            _data = new double[row][];

            for (var rowNumber = 0; rowNumber < row; rowNumber++)
            {
                _data[rowNumber] = new double[column];
                for (var columnNumber = 0; columnNumber < column; columnNumber++)
                {
                    this[rowNumber, columnNumber] = matrix[rowNumber, columnNumber];
                }
            }
        }

        /// <summary>
        /// 生成均勻分布在(0-1)之間偽隨機數的矩陣。
        /// </summary>
        /// <param name="row">行。</param>
        /// <param name="column">列。</param>
        /// <returns>矩陣。</returns>
        public static Matrix CreateRandMatrix(int row, int column)
        {
            return new Matrix(row, column, index => RandomToolit.GetRandom().NextDouble());
        }

        #endregion 初始化

        #region 屬性

        /// <summary>
        /// 返中行數。
        /// </summary>
        public int Row { get; }

        /// <summary>
        /// 返回列數。
        /// </summary>
        public int Column { get; }

        public double this[int row, int column]
        {
            get
            {
                return _data[row][column];
            }
            set
            {
                _data[row][column] = value;
            }
        }

        #endregion 屬性

        #region 操作符

        /// <summary>
        /// 矩陣加法 binary addition。
        /// </summary>
        /// <param name="lhs"></param>
        /// <param name="rhs"></param>
        /// <returns></returns>
        public static Matrix operator +(Matrix lhs, Matrix rhs)
        {
            if (lhs.Row != rhs.Row)
            {
                throw new Exception("相加的兩個矩陣的行數不等");
            }

            if (lhs.Column != rhs.Column)
            {
                throw new Exception("相加的兩個矩陣的列數不等");
            }

            var row = lhs.Row;
            var column = lhs.Column;
            var ret = new Matrix(row, column);

            for (var index = 0; index < row; index++)
            {
                for (var loop = 0; loop < column; loop++)
                {
                    var value = lhs[index, loop] + rhs[index, loop];

                    ret[index, loop] = value;
                }
            }

            return ret;
        }

        /// <summary>
        /// 矩陣減法 binary subtraction。
        /// </summary>
        /// <param name="lhs"></param>
        /// <param name="rhs"></param>
        /// <returns></returns>
        public static Matrix operator -(Matrix lhs, Matrix rhs)
        {
            if (lhs.Row != rhs.Row)
            {
                throw new Exception("相減的兩個矩陣的行數不等");
            }
            if (lhs.Column != rhs.Column)
            {
                throw new Exception("相減的兩個矩陣的列數不等");
            }

            var row = lhs.Row;
            var column = lhs.Column;
            var ret = new Matrix(row, column);

            for (var index = 0; index < row; index++)
            {
                for (var loop = 0; loop < column; loop++)
                {
                    var value = lhs[index, loop] - rhs[index, loop];
                    ret[index, loop] = value;
                }
            }

            return ret;
        }

        /// <summary>
        /// 矩陣乘法(乘積)。
        /// </summary>
        /// <remarks>
        /// 當矩陣A的列數等於矩陣B的行數時,A與B可以相乘。
        /// 1.矩陣C的行數等於矩陣A的行數,C的列數等於B的列數。
        /// 2.乘積C的第m行第n列的元素等於矩陣A的第m行的元素與矩陣B的第n列對應元素乘積之和。
        /// </remarks>
        /// <param name="lhs"></param>
        /// <param name="rhs"></param>
        /// <returns></returns>
        public static Matrix operator *(Matrix lhs, Matrix rhs)
        {
            if (lhs.Column != rhs.Row)
            {
                throw new Exception("相乘的兩個矩陣的行列數不匹配");
            }

            var ret = new Matrix(lhs.Row, rhs.Column);

            for (var index = 0; index < lhs.Row; index++)
            {
                for (var loop = 0; loop < rhs.Column; loop++)
                {
                    double temp = 0;

                    for (var k = 0; k < lhs.Column; k++)
                    {
                        temp += lhs[index, k] * rhs[k, loop];
                    }

                    ret[index, loop] = temp;
                }
            }

            return ret;
        }

        /// <summary>
        /// 矩陣除法 binary division。
        /// </summary>
        /// <param name="lhs"></param>
        /// <param name="rhs"></param>
        /// <returns></returns>
        public static Matrix operator /(Matrix lhs, Matrix rhs)
        {
            return lhs * rhs.Inverse();
        }

        /// <summary>
        /// 單目加 unary addition。
        /// </summary>
        /// <param name="sourceMatrix"></param>
        /// <returns></returns>
        public static Matrix operator +(Matrix sourceMatrix)
        {
            var returnMatrix = new Matrix(sourceMatrix);

            for (var rowNumber = 0; rowNumber < returnMatrix.Row; rowNumber++)
            {
                for (var columnNumber = 0; columnNumber < returnMatrix.Column; columnNumber++)
                {
                    returnMatrix[rowNumber, columnNumber] = +returnMatrix[rowNumber, columnNumber];
                }
            }

            return returnMatrix;
        }

        /// <summary>
        /// 單目減 unary subtraction。
        /// </summary>
        /// <param name="sourceMatrix"></param>
        /// <returns></returns>
        public static Matrix operator -(Matrix sourceMatrix)
        {
            var returnMatrix = new Matrix(sourceMatrix);

            for (var rowNumber = 0; rowNumber < returnMatrix.Row; rowNumber++)
            {
                for (var columnNumber = 0; columnNumber < returnMatrix.Column; columnNumber++)
                {
                    returnMatrix[rowNumber, columnNumber] = -returnMatrix[rowNumber, columnNumber];
                }
            }

            return returnMatrix;
        }

        /// <summary>
        /// 矩陣加法。
        /// </summary>
        /// <param name="m"></param>
        /// <param name="d"></param>
        /// <returns></returns>
        public static Matrix operator +(Matrix m, double d)
        {
            var ret = new Matrix(m);
            for (var rowNumber = 0; rowNumber < ret.Row; rowNumber++)
                for (var columnNumber = 0; columnNumber < ret.Column; columnNumber++)
                    ret[rowNumber, columnNumber] += d;

            return ret;
        }

        /// <summary>
        /// 矩陣加法。
        /// </summary>
        /// <param name="d"></param>
        /// <param name="m"></param>
        /// <returns></returns>
        public static Matrix operator +(double d, Matrix m)
        {
            var ret = new Matrix(m);
            for (var i = 0; i < ret.Row; i++)
                for (var j = 0; j < ret.Column; j++)
                    ret[i, j] += d;

            return ret;
        }

        /// <summary>
        /// 矩陣減法。
        /// </summary>
        /// <param name="sourceMatrix"></param>
        /// <param name="d"></param>
        /// <returns></returns>
        public static Matrix operator -(Matrix sourceMatrix, double d)
        {
            var targetMatrix = new Matrix(sourceMatrix);
            for (var rowNumber = 0; rowNumber < targetMatrix.Row; rowNumber++)
                for (var columnNumber = 0; columnNumber < targetMatrix.Column; columnNumber++)
                    targetMatrix[rowNumber, columnNumber] -= d;

            return targetMatrix;
        }

        /// <summary>
        /// 矩陣減法。
        /// </summary>
        /// <param name="d"></param>
        /// <param name="sourceMatrix"></param>
        /// <returns></returns>
        public static Matrix operator -(double d, Matrix sourceMatrix)
        {
            var targetMatrix = new Matrix(sourceMatrix);
            for (var rowNumber = 0; rowNumber < targetMatrix.Row; rowNumber++)
                for (var columnNumber = 0; columnNumber < targetMatrix.Column; columnNumber++)
                    targetMatrix[rowNumber, columnNumber] = d - targetMatrix[rowNumber, columnNumber];

            return targetMatrix;
        }

        /// <summary>
        /// 數乘 number multiple。
        /// </summary>
        /// <param name="m"></param>
        /// <param name="d"></param>
        /// <returns></returns>
        public static Matrix operator *(Matrix m, double d)
        {
            var ret = new Matrix(m);
            for (var rowNumber = 0; rowNumber < ret.Row; rowNumber++)
                for (var columnNumber = 0; columnNumber < ret.Column; columnNumber++)
                    ret[rowNumber, columnNumber] *= d;

            return ret;
        }

        /// <summary>
        /// 數乘 number multiple。
        /// </summary>
        /// <param name="d"></param>
        /// <param name="m"></param>
        /// <returns></returns>
        public static Matrix operator *(double d, Matrix m)
        {
            var ret = new Matrix(m);
            for (var i = 0; i < ret.Row; i++)
                for (var j = 0; j < ret.Column; j++)
                    ret[i, j] *= d;

            return ret;
        }

        /// <summary>
        /// 數除 number division。
        /// </summary>
        /// <param name="sourceMatrix"></param>
        /// <param name="d"></param>
        /// <returns></returns>
        public static Matrix operator /(Matrix sourceMatrix, double d)
        {
            var targetMatrix = new Matrix(sourceMatrix);
            for (var rowNumber = 0; rowNumber < targetMatrix.Row; rowNumber++)
                for (var columnNumber = 0; columnNumber < targetMatrix.Column; columnNumber++)
                    targetMatrix[rowNumber, columnNumber] /= d;

            return targetMatrix;
        }

        /// <summary>
        /// 數除 number division。
        /// </summary>
        /// <param name="d"></param>
        /// <param name="sourceMatrix"></param>
        /// <returns></returns>
        public static Matrix operator /(double d, Matrix sourceMatrix)
        {
            var targetMatrix = new Matrix(sourceMatrix);
            for (var rowNumber = 0; rowNumber < targetMatrix.Row; rowNumber++)
                for (var columnNumber = 0; columnNumber < targetMatrix.Column; columnNumber++)
                    targetMatrix[rowNumber, columnNumber] = d / targetMatrix[rowNumber, columnNumber];

            return targetMatrix;
        }

        #endregion 操作符

        #region 矩陣乘法(點積)

        /// <summary>
        /// 矩陣乘法(點積)。
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        public Matrix DotMultiple(Matrix target)
        {
            if (target == null)
                throw new ArgumentNullException(nameof(target));

            if (target.Row != Row || target.Column != Column)
                throw new ArgumentException("矩陣維度不一致");

            var ret = new Matrix(target);
            for (var rowNumber = 0; rowNumber < ret.Row; rowNumber++)
                for (var columnNumber = 0; columnNumber < ret.Column; columnNumber++)
                    ret[rowNumber, columnNumber] = this[rowNumber, columnNumber] * target[rowNumber, columnNumber];

            return ret;
        }

        #endregion 矩陣乘法(點積)

        #region 初等變換

        /// <summary>
        /// 初等變換 primary change。
        /// </summary>
        /// <remarks>
        /// 對調兩行:ri←→rj。
        /// </remarks>
        /// <param name="row"></param>
        /// <param name="column"></param>
        /// <returns></returns>
        public Matrix Exchange(int row, int column)
        {
            for (var index = 0; index < Column; index++)
            {
                var temp = this[row, index];
                this[row, index] = this[column, index];
                this[column, index] = temp;
            }

            return this;
        }

        /// <summary>
        /// 初等變換,第 index 行乘以 mul。
        /// </summary>
        /// <param name="index">行號。</param>
        /// <param name="mul"></param>
        /// <returns></returns>
        private Matrix Multiple(int index, double mul)
        {
            for (var loop = 0; loop < Column; loop++)
            {
                this[index, loop] *= mul;
            }

            return this;
        }

        /// <summary>
        /// 初等變換,第 src 行乘以 mul 加到第 index 行。
        /// </summary>
        /// <param name="index"></param>
        /// <param name="src"></param>
        /// <param name="mul"></param>
        /// <returns></returns>
        private Matrix MultipleAdd(int index, int src, double mul)
        {
            for (var loop = 0; loop < Column; loop++)
            {
                this[index, loop] += this[src, loop] * mul;
            }

            return this;
        }

        #endregion 初等變換

        #region unit matrix:設為單位陣

        /// <summary>
        /// unit matrix:設為單位陣。
        /// </summary>
        public void SetUnit()
        {
            for (int i = 0; i < _data.GetLength(0); i++)
                for (int j = 0; j < _data.GetLength(1); j++)
                    this[i, j] = ((i == j) ? 1 : 0);
        }

        #endregion unit matrix:設為單位陣

        #region 逆陣 inversion:使用矩陣的初等變換,列主元素消去法

        /// <summary>
        /// 逆陣 inversion:使用矩陣的初等變換,列主元素消去法。
        /// </summary>
        /// <returns></returns>
        public Matrix Inverse()
        {
            if (Row != Column)
            {
                throw new Exception("求逆的矩陣不是方陣");
            }

            var tmp = new Matrix(this);

            //單位陣
            var ret = new Matrix(Row);
            ret.SetUnit();

            double dMul;

            for (int i = 0; i < Row; i++)
            {
                var maxIndex = tmp.Pivot(i);

                if (tmp[maxIndex, i] == 0)
                {
                    throw new Exception("求逆的矩陣的行列式的值等於0,");
                }

                //下三角陣中此列的最大值不在當前行,交換
                if (maxIndex != i)
                {
                    tmp.Exchange(i, maxIndex);
                    ret.Exchange(i, maxIndex);
                }

                ret.Multiple(i, 1 / tmp[i, i]);
                tmp.Multiple(i, 1 / tmp[i, i]);

                for (int j = i + 1; j < Row; j++)
                {
                    dMul = -tmp[j, i] / tmp[i, i];
                    tmp.MultipleAdd(j, i, dMul);
                }
            }

            for (int i = Row - 1; i > 0; i--)
            {
                for (int j = i - 1; j >= 0; j--)
                {
                    dMul = -tmp[j, i] / tmp[i, i];
                    tmp.MultipleAdd(j, i, dMul);
                    ret.MultipleAdd(j, i, dMul);
                }
            }

            return ret;
        }

        #endregion 逆陣 inversion:使用矩陣的初等變換,列主元素消去法

        #region 轉置 transpose

        /// <summary>
        /// 轉置 transpose。
        /// </summary>
        /// <returns>矩陣。</returns>
        public Matrix Transpose()
        {
            var ret = new Matrix(Column, Row);

            for (var index = 0; index < Row; index++)
            {
                for (var loop = 0; loop < Column; loop++)
                {
                    ret[loop, index] = this[index, loop];
                }
            }

            return ret;
        }

        #endregion 轉置 transpose

        #region 復制和平鋪矩陣

        /// <summary>
        /// 復制和平鋪矩陣。
        /// </summary>
        /// <remarks>
        /// repmat = Replicate Matrix。
        /// 示例:
        /// B = A.Replicate(m,n),就是創建一個矩陣 B,B 中復制了共 m * n 個 A 矩陣,因此 B 矩陣的大小為 [A.Row * m, A.Col * m]。
        /// 即:A 是一個 5 行 3 列的矩陣,則 B 為 5 * m 行 3 * n 列的矩陣。
        /// </remarks>
        /// <param name="row">行數。</param>
        /// <param name="column">列數。</param>
        /// <returns>矩陣。</returns>
        public Matrix Repmat(int row = 1, int column = 1)
        {
            if (row <= 0)
                throw new ArgumentException("矩陣行數必須大於 0");

            if (column <= 0)
                throw new ArgumentException("矩陣行數必須大於 0");

            var matrix = new Matrix(Row * row, Column * column);

            for (var index = 0; index < matrix.Row; index++)
            {
                for (var loop = 0; loop < matrix.Column; loop++)
                {
                    matrix[index, loop] = this[index % Row, loop % Column];
                }
            }

            return matrix;
        }

        #endregion 復制和平鋪矩陣

        #region 調整矩陣的行數、列數和維數

        /// <summary>
        /// 調整矩陣的行數、列數和維數。
        /// </summary>
        /// <param name="row">新矩陣的行數。</param>
        /// <param name="column">新矩陣的列數。</param>
        /// <returns>矩陣。</returns>
        public Matrix Reshape(int row, int column)
        {
            return Reshape(this, row, column);
        }

        /// <summary>
        /// 調整矩陣的行數、列數和維數。
        /// </summary>
        /// <param name="source">源矩陣。</param>
        /// <param name="row">新矩陣的行數。</param>
        /// <param name="column">新矩陣的列數。</param>
        /// <returns>矩陣。</returns>
        public static Matrix Reshape(Matrix source, int row, int column)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));

            if (source.Row * source.Column != row * column)
                throw new ArgumentException("元素的個數(row * column)必須是源矩陣一致");

            var data = new Matrix(row, column);
            var newColumnNumber = 0;
            var newRowNumber = 0;

            for (var sourceColumnNumber = 0; sourceColumnNumber < source.Column; sourceColumnNumber++)
            {
                for (var sourceRowNumber = 0; sourceRowNumber < source.Row; sourceRowNumber++)
                {
                    data[newRowNumber, newColumnNumber] = source[sourceRowNumber, sourceColumnNumber];

                    newRowNumber++;

                    if (newRowNumber >= row)
                    {
                        newRowNumber = 0;
                        newColumnNumber++;
                    }
                }
            }

            return data;
        }

        #endregion 調整矩陣的行數、列數和維數

        #region 返回列主元素的行號

        /// <summary>
        /// 返回列主元素的行號。
        /// </summary>
        /// <remarks>
        /// 在行號 [row, Col) 范圍內查找第 row 列中絕對值最大的元素,返回所在行號。
        /// </remarks>
        /// <param name="row">開始查找的行號。</param>
        /// <returns>行號。</returns>
        private int Pivot(int row)
        {
            int index = row;

            for (int i = row + 1; i < Row; i++)
            {
                if (this[i, row] > this[index, row])
                    index = i;
            }

            return index;
        }

        #endregion 返回列主元素的行號

        #region 獲得指定行的數據

        /// <summary>
        /// 獲得指定行的數據。
        /// </summary>
        /// <param name="rowNumber">行號。</param>
        /// <returns>行數據。</returns>
        public double[] GetRow(int rowNumber)
        {
            if (rowNumber < 0 || rowNumber >= Row)
                throw new ArgumentException("行號不正確", nameof(rowNumber));

            return _data[rowNumber];
        }

        #endregion 獲得指定行的數據

        #region 獲得指定列的數據

        /// <summary>
        /// 獲得指定列的數據。
        /// </summary>
        /// <param name="columnNumber">列號。</param>
        /// <returns>列數據。</returns>
        public double[] GetColumn(int columnNumber)
        {
            if (columnNumber < 0 || columnNumber >= Column)
                throw new ArgumentException("列號不正確", nameof(columnNumber));

            var column = new double[Row];

            for (var rowNumber = 0; rowNumber < Row; rowNumber++)
            {
                column[rowNumber] = _data[rowNumber][columnNumber];
            }

            return column;
        }

        #endregion 獲得指定列的數據

        #region 轉換字符串

        /// <summary>
        /// 轉換字符串。
        /// </summary>
        /// <returns>矩陣字符串。</returns>
        public override string ToString()
        {
            var maxLength = 0;

            for (var rowNumber = 0; rowNumber < Row; rowNumber++)
            {
                for (var columnNumber = 0; columnNumber < Column; columnNumber++)
                {
                    var length = this[rowNumber, columnNumber].ToString(CultureInfo.InvariantCulture).Length;
                    if (length > maxLength)
                    {
                        maxLength = length;
                    }
                }
            }

            maxLength += 3;

            var printBuilder = new StringBuilder();

            for (var rowNumber = 0; rowNumber < Row; rowNumber++)
            {
                if (rowNumber > 0)
                {
                    printBuilder.AppendLine();
                }

                for (var columnNumber = 0; columnNumber < Column; columnNumber++)
                {
                    printBuilder.Append(this[rowNumber, columnNumber].ToString(CultureInfo.InvariantCulture).PadRight(maxLength));
                }
            }

            return printBuilder.ToString();
        }

        #endregion 轉換字符串
    }
View Code

 

MathExtensions.cs

    /// <summary>
    /// 擴展出矩陣相關的數學函數。
    /// </summary>
    public static class MathExtensions
    {
        /// <summary>
        /// 矩陣求和。
        /// </summary>
        /// <param name="source">矩陣。</param>
        /// <returns>和。</returns>
        public static double Sum(this Matrix source)
        {
            double sum = 0;
            for (var rowNumber = 0; rowNumber < source.Row; rowNumber++)
                for (var columnNumber = 0; columnNumber < source.Column; columnNumber++)
                    sum += source[rowNumber, columnNumber];

            return sum;
        }

        /// <summary>
        /// 矩陣列求和。
        /// </summary>
        /// <param name="source">矩陣。</param>
        /// <returns>和。</returns>
        public static Matrix SumColumn(this Matrix source)
        {
            var ret = new Matrix(1, source.Column);
            for (var rowNumber = 0; rowNumber < source.Row; rowNumber++)
                for (var columnNumber = 0; columnNumber < source.Column; columnNumber++)
                    ret[0, columnNumber] += source[rowNumber, columnNumber];

            return ret;
        }

        /// <summary>
        /// 矩陣行求和。
        /// </summary>
        /// <param name="source">矩陣。</param>
        /// <returns>和。</returns>
        public static Matrix SumRow(this Matrix source)
        {
            var ret = new Matrix(source.Row, 1);
            for (var rowNumber = 0; rowNumber < source.Row; rowNumber++)
                for (var columnNumber = 0; columnNumber < source.Column; columnNumber++)
                    ret[rowNumber, 0] += source[rowNumber, columnNumber];

            return ret;
        }

        /// <summary>
        /// 對矩陣中的每個元素都進行 Math.Log(x) 運算。
        /// </summary>
        /// <remarks>
        /// 返回指定數字的自然對數(底為 e)。
        /// </remarks>
        /// <param name="source">矩陣。</param>
        /// <returns>結果。</returns>
        public static Matrix Log(this Matrix source)
        {
            return source.Function(Math.Log);
        }

        /// <summary>
        /// 對矩陣中的每個元素都進行 Math.Exp(x) 運算。
        /// </summary>
        /// <remarks>
        /// 即: e 的指定次冪。
        /// </remarks>
        /// <param name="source">矩陣。</param>
        /// <param name="digits">返回的小數數字(即:該值是 4 的時候,表示保留 4 位小數)。</param>
        /// <returns>結果。</returns>
        public static Matrix Exp(this Matrix source, int digits = 0)
        {
            if (digits > 0)
            {
                return source.Function(item => Math.Round(Math.Exp(item), digits));
            }

            return source.Function(Math.Exp);
        }

        /// <summary>
        /// 對矩陣中的每個元素都進行 Math.Pow(y) 運算。
        /// </summary>
        /// <remarks>
        /// 即: 指定數字的指定次冪。
        /// </remarks>
        /// <param name="source">矩陣。</param>
        /// <param name="y">指定次冪。</param>
        /// <param name="digits">返回的小數數字(即:該值是 4 的時候,表示保留 4 位小數)。</param>
        /// <returns>結果。</returns>
        public static Matrix Pow(this Matrix source, int y, int digits = 0)
        {
            if (digits > 0)
            {
                return source.Function(x => Math.Round(Math.Pow(x, y)));
            }

            return source.Function(x => Math.Pow(x, y));
        }

        /// <summary>
        /// 將雙精度浮點值按指定的小數位數舍入。
        /// </summary>
        /// <param name="source">矩陣。</param>
        /// <param name="digits">返回值中的小數數字。</param>
        /// <returns>最接近 value 的 digits 位小數的數字矩陣。</returns>
        public static Matrix Round(this Matrix source, int digits)
        {
            return source.Function(item => Math.Round(item, digits));
        }

        /// <summary>
        /// 對矩陣中的每個元素進行計算。
        /// </summary>
        /// <param name="source">矩陣。</param>
        /// <param name="func">計算方法。</param>
        /// <returns>結果。</returns>
        public static Matrix Function(this Matrix source, Func<double, double> func)
        {
            var ret = new Matrix(source.Row, source.Column);
            for (var rowNumber = 0; rowNumber < source.Row; rowNumber++)
                for (var columnNumber = 0; columnNumber < source.Column; columnNumber++)
                    ret[rowNumber, columnNumber] = func(source[rowNumber, columnNumber]);

            return ret;
        }
    }
View Code

感知器的實現:

注意,在初始化權值(權重)的時候,可以是任意的一個初始值。

Perceptron.cs

    /// <summary>
    /// 感知器。
    /// </summary>
    public class Perceptron
    {
        /// <summary>
        /// 輸入參數個數。
        /// </summary>
        private readonly int _inputCount;

        /// <summary>
        /// 權重矩陣。
        /// </summary>
        private Matrix _hiddenWeights;

        /// <summary>
        /// 初始化感知器。
        /// </summary>
        /// <param name="inputCount">輸入參數個數。</param>
        public Perceptron(int inputCount)
        {
            _inputCount = inputCount;

            //生成輸入參數對應的權重。
            _hiddenWeights = new Matrix(inputCount, 1, index => -2);
        }

        /// <summary>
        /// 學習率η。
        /// </summary>
        public double Eta => 1;

        /// <summary>
        /// 訓練。
        /// </summary>
        /// <param name="sample">樣本數據。</param>
        /// <param name="classType">分類。</param>
        public void Epoch(double[] sample, int classType)
        {
            if (sample == null || sample.Length <= 0 || sample.Length != _inputCount)
                throw new ArgumentException("樣本數據不正確");

            var matrixSample = new Matrix(1, sample.Length, index => sample[index]);

            //輸出值 y。
            var y = Hander(matrixSample);

            //權重修正。
            //w(n + 1) = w(n) + η[d(n) - y(n)]x(n)
            _hiddenWeights = _hiddenWeights + (Eta * (classType - y) * matrixSample).Transpose();
        }

        #region 計算

        /// <summary>
        /// 真實計算結果。
        /// </summary>
        /// <param name="properties">輸入參數。</param>
        /// <returns>結果。</returns>
        public double Compute(double[] properties)
        {
            if (properties.Length != _inputCount)
                throw new ArgumentException("輸入參數個數不正確");

            var matrixProperties = new Matrix(1, properties.Length, index => properties[index]);

            return Hander(matrixProperties);
        }

        #endregion 計算

        #region 神經網絡處理

        public double Hander(Matrix properties)
        {
            //誘導局部域 v,_hiddenWeights 矩陣是 1 列,matrixSample 矩陣是 1 行。
            //v = x1 * w1+ x2 * w2+···+xm * wm
            var v = (_hiddenWeights * properties + 1).Sum();

            //輸出值 y。
            var y = 1.0;

            if (v <= 0)
            {
                y = -1.0;
            }

            return y;
        }

        #endregion 神經網絡處理
    }
View Code

 

使用示例:

    public class PerceptronTest
    {
        public static void Execute()
        {
            //R1 類的訓練樣本。
            var sample1 = new Matrix(new[] { new[] { 1.0, 2 }, new[] { 2.0, 3 }, new[] { 3.0, 4 } });
            //R2 類的訓練樣本。
            var sample2 = new Matrix(new[] { new[] { -1.0, -2 }, new[] { -2.0, -3 }, new[] { -3.0, -4 } });

            //定義感知器。
            var perceptron = new Perceptron(2);

            //開始訓練。
            for (var rowNumber = 0; rowNumber < sample1.Row; rowNumber++)
            {
                //這里的輸入值是點的坐標,輸出值根據公式,當為 R1 類的時候,輸出的期望值是 1。
                perceptron.Epoch(sample1.GetRow(rowNumber), 1);
            }

            for (var rowNumber = 0; rowNumber < sample2.Row; rowNumber++)
            {
                //這里的輸入值是點的坐標,輸出值根據公式,當為 R2 類的時候,輸出的期望值是 -1。
                perceptron.Epoch(sample2.GetRow(rowNumber), -1);
            }

            /*
            1.當權值不在改變的時候,就表示訓練已完成。
            2.當訓練完成后,就可以輸入任意一個點,自動判斷出它是屬於哪一類了。
            */
            var output = perceptron.Compute(new[] { 3.0, 2 });
            Console.WriteLine($"點 (3, 2) 是屬於 {(output > 0 ? "R1" : "R2")} 類。");
            output = perceptron.Compute(new[] { -1.0, -3 });
            Console.WriteLine($"點 (-1, -3) 是屬於 {(output > 0 ? "R1" : "R2")} 類。");
        }
    }
View Code

輸出結果:

 

2.MATLAB 實現

訓練函數 Epoch.m

function [output, weights] = Epoch(weights, inputs, target_output, eta)
v = sum(sum(weights * inputs + 1));
y = 1.0;
if v <= 0
    y = -1.0;
end
weights = weights + (eta * (target_output - y) * inputs)';
output = y;
end

 

計算函數 Compute.m

function [ output ] = Compute(inputs, weights)
v = sum(sum(weights * inputs + 1));
y = 1.0;
if v <= 0
    y = -1.0;
end
output = y;
end

使用示例 Execute.m

%% Example Title
% 初始化
sample1 = [1,2;2,3;3,4]
sample2 = [-1,-2;-2,-3;-3,-4]
eta = 1
R1 = 1
R2 = -1
weights = [-2;-2]
%% Section 1 Title
% 訓練
for index = 1:3
    input_sample1 = sample1(index,:);
    input_sample2 = sample2(index,:);
    [output, weights] = Epoch(weights, input_sample1, R1, eta);
    [output, weights] = Epoch(weights, input_sample2, R2, eta);
end
%% Section 2 Title
% 執行計算
a=[1,3];
output = Compute(a, weights)

輸出結果:

R1

 R2

 

 

 參考資料:

《神經網絡與機器學習》第三版譯本

機器學習入門——淺談神經網絡

 UFLDL教程

 

 


免責聲明!

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



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