這是本人學習《神經網絡與機器學習》時查找各種資料的總結的筆記,為了讓自己的印象更深刻並且分享些經驗出來,最后有C#與MATLAB的代碼實現。
如果有不對的地方歡迎指點。
這里需要有一定的數學基礎,如:線性代數,微積分,矩陣,概率,統計等。
目錄:
一、概念
感知器是生物神經細胞的簡單抽象,神經細胞結構大致可分為:樹突、突觸、細胞體及軸突。
單個神經細胞可被視為一種只有兩種狀態的機器——激動時為‘是’,而未激動時為‘否’。神經細胞的狀態取決於從其它的神經細胞收到的輸入信號量,及突觸的強度(抑制或加強)。當信號量總和超過了某個閾值時,細胞體就會激動,產生電脈沖。電脈沖沿着軸突並通過突觸傳遞到其它神經元。為了模擬神經細胞行為,與之對應的感知機基礎概念被提出,如權值(突觸)、偏置(閾值)及激活函數(細胞體)。
1.感知器是使用特征向量來表示的前饋式人工神經網絡,它是一種二元分類器,把矩陣上的輸入(實數值向量)映射到輸出值。由於輸入直接經過權重關系轉換為輸出,所以感知機可以被視為最簡單形式的前饋式人工神經網絡。它是建立在一個非線性神經元上,即神經元的 McCulloch-Pitts 模型。
2.神經元模型是一個線性組合器和隨后的硬限幅器(激活函數)組合(如下圖1.1)。
圖1.1
感知器的輸入值記為:,權值記為:
,偏置值記為:b。
輸入值:表示將要分類的事物的特性的組合,對應每個神經細胞接收到外部輸入的刺激信號,即:初始特征。
權值:表示該模型的分類變化參數,也是該模型在學習過程中需要計算出的參數。
偏置值(Bias Units):它是一個不依賴於任何輸入值的常數。可以認為是激活函數的偏移量,或者給神經元一個基礎活躍等級。從空間上看(如:圖1.2),該值的作用僅僅是把決策邊界(圖1.2)從原點移開。
誘導局部域:每一個輸入值與其對應的權值相乘,然后將這些乘積的結果相加得到的結果,即:v 的值(PS:這概念是本人在看《神經網絡與機器學習》一書的譯本中看到的)。
公式1.1
硬限幅器(激活函數):使用誘導局部域作為輸入參數,當輸入為正時,感知器輸出 +1,反之則輸出 -1。
公式1.2
3.感知器的目的是把輸入值所表示的點正確的分為
或者
兩類。
感知器的分類的規則是:
①感知器的輸出值 y 是 +1 就將表示的點分配給
類。
②感知器的輸出值 y 是 -1 就將表示的點分配給
類。
4.決策邊界。
根據前面兩點,可得出:
①當時,
屬於
類。
②當時,
屬於
類。




該圖中決策邊界是條直線,位於邊界線上面的兩個綠點分入,位於邊界線下面的兩個紅點分入
。
圖1.2
5.感知器只能對線性可分的兩個類進行分類。
圖1.3
二、感知器學習
首先,這里引用了一張在帖吧上看到的一張圖片:
圖1.4
我們看到上圖的汽車的時候,會記憶它的一些基本特征(比如:有4個輪子,車身灰白色等等),如果沒有人告訴我們“車”的概念,我們雖然記下了它的大概基本特征,但卻並不知道它是什么。同樣,如果有人告訴我們“車”是什么,但我們卻從來沒有看見過車,同樣對“車”一無所知。只有我們看到過車並記憶了它的一些基本特征,並且有人告訴我們,這就是“車”,當我們再次看到相似的事物的時候,我們自然就會判斷,這就是“車”。
感知器也一樣,給出待分類的幾個點(),就如同我們人只見過“車”但卻沒有“車”這一概念時一樣並不知道這就是“車”這一種類,感知器是無法知道它們是屬於
類還是
類。
這時,我們需要讓感知器正確的識別出是屬於類還是
類的過程,就是感知器學習(訓練)的過程,而在學習(訓練)過程中需要使用的已分類好的一組數據,就是訓練樣本,當感知器完成了學習(訓練)之后,就能正確的識別與分類了。
------------------------------------------------------------------------------------------------
示例1:
------------------------------------------------------------------------------------------------
三、誤差修正學習算法
誤差修正學習算法是通過多次迭代的方式來調整感知器的權值,使感知器的輸出信號 y 的值逐漸逼近給定訓練樣本的輸出值。
在上面的示例1中,使用誤差修正學習算法即可找到一組合適的權值(和
)以完成訓練。
設:
是訓練樣本
中,屬於
類的樣本所組成的子集,
是訓練樣本
中,屬於
類的樣本所組成的子集,
和
的並集是整個訓練樣本集
,n 是訓練樣本個數(迭代次數),給定訓練樣本集
和
來訓練感知器,訓練過程中對權值向量
的調整使兩個類
和
線性可分,
即:
也就是說,存在一個權值 具有以下性質:
公式1.4
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
補充說明:
在示例1中,訓練樣本是坐標軸上的一個點,因此該點的初始特征就是坐標
,因此,輸入值就有兩個,所以對應的權值
也有兩個,所以使用矩陣的形式來表示公式1.1,則是:
。
所以,感知器的訓練問題就是找到一組權值向量 滿足公式1.4中的兩個不等式。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
使感知器的權值自適應的算法現在可以用以下公式來描述:
1.假設訓練集合的第 n 個成員 x(n)
根據算法中的第
n 次迭代的權值向量 w(n)
能正確分類,那么感知器的權值向量按下述規則不做修改:
公式1.5



















/// <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 轉換字符串 }
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; } }
感知器的實現:
注意,在初始化權值(權重)的時候,可以是任意的一個初始值。
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 神經網絡處理 }
使用示例:

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")} 類。"); } }
輸出結果:
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
參考資料:
《神經網絡與機器學習》第三版譯本