您好,登錄后才能下訂單哦!
前言:
最近在研究機器學習,過程中的心得體會會記錄到blog里,文章與代碼均為原創。會不定期龜速更新,注意這不是正式的教程,因為本人也是初學者,但是估計C#版本的代碼能幫到一些剛入門的同學去理解復雜的公式。
------------------------ 我是分割線 ------------------------
k近鄰(k-Nearest Neighbor,KNN)算法,應該是機器學習里最基礎的算法,其核心思想是:給定一個未知分類的樣本,如果與它最相似的k個已知樣本中的多數屬于某一個分類,那么這個未知樣本也屬于這個分類。
所謂相似,是指兩個樣本之間的歐氏距離小,其計算公式為:
其中Xi為樣本X的第i個特征。
k近鄰算法的優點在于實現簡單,缺點在于時間和空間復雜度高。
上C#版代碼,這里取k=1,即只根據最相近的一個點確定分類:
首先是DataVector,包含N維數據和分類標簽,用于表示一個樣本。
using System; namespace MachineLearning { /// <summary> /// 數據向量 /// </summary> /// <typeparam name="T"></typeparam> public class DataVector<T> { /// <summary> /// N維數據 /// </summary> public T[] Data { get; private set; } /// <summary> /// 分類標簽 /// </summary> public string Label { get; set; } /// <summary> /// 構造 /// </summary> /// <param name="dimension">數據維度</param> public DataVector(int dimension) { Data = new T[dimension]; } public int Dimension { get { return this.Data.Length; } } } }
然后是核心算法:
using System; using System.Collections.Generic; namespace MachineLearning { /// <summary> /// k近鄰法 /// </summary> public class NearestNeighbour { private int m_K; private List<DataVector<double>> m_TrainingSet; public NearestNeighbour(int k = 1) { m_K = k; } /// <summary> /// 訓練 /// </summary> /// <param name="trainingSet"></param> public void Train(List<DataVector<double>> trainingSet) { m_TrainingSet = trainingSet; } /// <summary> /// 分類 /// </summary> /// <param name="vector"></param> /// <returns></returns> public string Classify(DataVector<double> vector) { //K=1時可簡化處理提高效率 if(m_K == 1) { double minDist = double.PositiveInfinity; int targetIndex = -1; for(int i = 0;i < m_TrainingSet.Count;i++) { //計算距離 double distance = ComputeDistance(vector, m_TrainingSet[i], minDist); //找最小值 if(distance < minDist) { minDist = distance; targetIndex = i; } } return m_TrainingSet[targetIndex].Label; } else { var dict = new SortedDictionary<double, string>(); for(int i = 0;i < m_TrainingSet.Count;i++) { //計算距離并記錄 double distance = ComputeDistance(vector, m_TrainingSet[i]); dict[distance] = m_TrainingSet[i].Label; } //找最多的Label var labels = new List<string>(); int count = 0; foreach(var label in dict.Values) { labels.Add(label); if(++count > m_K - 1) break; } return GetMajorLabel(labels); } } /// <summary> /// 計算距離 /// </summary> /// <param name="v1"></param> /// <param name="v2"></param> /// <param name="minValue"></param> /// <returns></returns> private double ComputeDistance(DataVector<double> v1, DataVector<double> v2, double minValue = double.PositiveInfinity) { double distance = 0.0; minValue = minValue * minValue; for(int i = 0;i < v1.Data.Length;++i) { double diff = v1.Data[i] - v2.Data[i]; distance += diff * diff; //如果當前累加的距離已經大于給定的最小值,不用繼續計算了 if(distance > minValue) return double.PositiveInfinity; } return Math.Sqrt(distance); } /// <summary> /// 取多數 /// </summary> /// <param name="dataSet"></param> /// <returns></returns> private string GetMajorLabel(List<string> labels) { var dict = new Dictionary<string, int>(); foreach(var item in labels) { if(!dict.ContainsKey(item)) dict[item] = 0; dict[item]++; } string label = string.Empty; int count = -1; foreach(var key in dict.Keys) { if(dict[key] > count) { label = key; count = dict[key]; } } return label; } } }
需要注意的是,計算距離時,數量級大的維度會對距離影響大,因此大多數情況下,不能直接計算,要對原始數據做歸一化,并根據重要性進行加權。歸一化可以使用公式:value = (old-min)/(max-min),其中old是原始值,max是所有數據的最大值,min是所有數據的最小值。這樣計算得到的value將落在0至1的區間上。
這個算法太簡單,暫時不上測試代碼了,有時間再補吧。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。