原理:簡單比喻為——人以群分,物以類聚。
優點:對於類域的交叉或重疊較多的待分樣本集來說,K-NN較其他方法更合適。
缺點:計算量較大,因為會計算全體已知樣本的距離。
改進方法:
(1)解決計算量大,事先對已知樣本點進行剪輯,去除對分類作用不大的成分。
(2)盡可能將計算壓縮到接近測試樣本領域的小范圍內,避免盲目地與訓練樣本集中的每個樣本進行距離計算。
算法步驟:
(1)初始化距離為最大值,計算未知樣本和每個樣本的距離dist.
(2)得到目前k個最鄰近樣本的最大距離maxdist,若dist<maxdist,則將該訓練樣本作為K-近鄰樣本。
(3)重復上述計算距離的步驟,直到所有未知樣本與所有訓練樣本的距離計算完。
(4)統計k個最近鄰樣本中每個類別出現的次數。
(5)選擇出現頻率最大的類別作為未知樣本的類別。
實例介紹:
根據客戶的16個屬性,為一家銀行建一個分類器,判斷客戶是否願意購買理財產品:

1 %% ———————————機器學習———————————————%% 2 %%%%%%%%%%%%%%%%%%%%%%%1.K-近鄰分類%%%%%%%%%%%%%%%%%%%%%%%%% 3 %16個屬性分別為: 4 %age job marital education default balance housing loan contact 5 %day month duration campaign pdays previous poutcome y(是否願意) 6 load 'E:\數學建模\學習資料\程序_MATLAB數學建模方法與實踐_卓金武等\Cha5\Classification_method_examples\bank.mat';%載入記錄數據 7 names = bank.Properties.VariableNames;%使用數據文件,記錄自變量和因變量的屬性名 8 category = varfun(@iscellstr,bank,'Output','uniform'); %輸出格式為數值格式。為字符串的返回結果為1,為數字的返回結果為0 9 for i = find(category) 10 bank.(names{i}) = categorical(bank.(names{i})); 11 %將bank中的屬性創建分類數組。bank.(names{i}) 的類別是bank.(names{i})經過分類后的唯一值且經過排序 12 end 13 catPred = category(1:end-1); 14 rng('default');%設置隨機數的生成方式,‘default’可以生成相同的隨機數 15 figure(1) 16 gscatter(bank.balance,bank.duration,bank.y,'kk','xo') 17 set(gca,'linewidth',2); 18 X = table2array(varfun(@double,bank(:,1:end-1)));%預測變量 19 Y = bank.y; 20 disp('數據中YES&No的統計結果'); 21 tabulate(Y) %求重復數字的個數使用tabulate,占比率 22 Xnum = [X(:,~catPred) dummyvar(X(:,catPred))]; 23 Ynum = double(Y)-1; 24 25 %%%設置交叉驗證方式 26 cv = cvpartition(height(bank),'holdout',0.40); 27 Xtrain = X(training(cv),:); 28 Ytrain = Y(training(cv),:); 29 XtrainNum = Xnum(training(cv),:); 30 YtrainNum = Ynum(training(cv),:); 31 Xtest = X(test(cv),:); 32 Ytest = Y(test(cv),:); 33 XtestNum = Xnum(test(cv),:); 34 YtestNum = Ynum(test(cv),:); 35 disp('訓練集'); 36 tabulate(Ytrain); 37 disp('測試集'); 38 tabulate(Ytest); 39 40 %%%訓練K-NN分類器 41 knn = ClassificationKNN.fit(Xtrain,Ytrain,'Distance','seuclidean','NumNeighbors',5); 42 %進行預測 43 [Y_knn,Yscore_knn] = knn.predict(Xtest); 44 Yscore_knn = Yscore_knn(:,2); 45 %計算混淆矩陣 46 disp('最近鄰方法分類結果:'); 47 C_knn = confusionmat(Ytest,Y_knn)
2.題中有關函數用法簡介:
【1】varfun函數:
語法:
1)B = varfun(func,A) 分別向表或時間表 A 的每個變量應用函數 func,並在表或時間表 B 中返回值。
函數 func 必須取一個輸入參數,並在每次調用時返回行數相同的數組。輸出參數中的第 i 個變量 B{:,i} 等於 func(A{:,i})。
如果 A 是時間表,而 func 跨多個行組聚合數據,則 varfun 將 A 中每個行組的第一個行時間指定為 B 中對應的行時間。要以表的形式返回 B 而不帶行時間,請將 'OutputFormat' 指定為 'table'。
2)B = varfun(func,A,Name,Value) 通過一個或多個 Name,Value 對組參數指定的其他選項,分別向表或時間表 A 的每個變量應用函數 func。
例如,您可以指定要傳遞給該函數的變量。
參數說明:
1)= varfun(func,A,Name,Value),'OutputFormat' - B 的格式:'table' (默認) | 'timetable' | 'uniform' | 'cell';若想返回數值向量而非表則可輸入B = varfun(func,A,'OutputFormat','uniform')。B = varfun(func,A,'GroupingVariables','Var1')表示將A中“Var1”變量進行分組(統計個數),會有單獨一列進行顯示,再將func函數(同一類別)計算出的值列出。
【2】categorical函數:
函數說明:
categorical
是為一組有限的離散類別(例如 High
、Med
和 Low
)賦值的數據類型。這些類別可以采用您指定的數學排序,例如 High > Med > Low
,但這並非必須。分類數組可用來有效地存儲並方便地處理非數值數據,同時還為數值賦予有意義的名稱。分類數組的常見用法是用來指定構成表的各組行。
語法:
B = categorical(A)
B = categorical(A,valueset)
B = categorical(A,valueset,catnames)
B = categorical(A,___,Name,Value)
轉換數組並按類別選擇數據
創建一個包含氣象站標簽的分類數組。將其添加到溫度讀數表中。然后使用類別按氣象站選擇溫度讀數。
首先,創建包含溫度讀數、日期和氣象站標簽的數組。
Temps = [58; 72; 56; 90; 76];
Dates = {'2017-04-17';'2017-04-18';'2017-04-30';'2017-05-01';'2017-04-27'}; Stations = {'S1';'S2';'S1';'S3';'S2'};
將 Stations
轉換為分類數組。
Stations = categorical(Stations)
Stations = 5x1 categorical array
S1
S2
S1
S3
S2
顯示類別。三個氣象站標簽為類別。
categories(Stations)
ans = 3x1 cell array
{'S1'}
{'S2'}
{'S3'}
創建包含溫度、日期和氣象站標簽的表。
T = table(Temps,Dates,Stations)
T=5x3 table
Temps Dates Stations
_____ ____________ ________
58 '2017-04-17' S1
72 '2017-04-18' S2
56 '2017-04-30' S1
90 '2017-05-01' S3
76 '2017-04-27' S2
顯示從氣象站 S2
獲得的讀數。您可以使用 ==
運算符找出等於 S2
的 Station
的值。然后使用邏輯索引選擇包含氣象站 S2
的數據的表行。
TF = (T.Stations == 'S2');
T(TF,:)
ans=2x3 table
Temps Dates Stations
_____ ____________ ________
72 '2017-04-18' S2
76 '2017-04-27' S2
【3】discretize函數(本題未用到):將數據分組到 bin 或類別中
例如:使用 discretize 函數(而不是 categorical)將 100 個隨機數划分為三個類別。
x = rand(100,1);
y = discretize(x,[0 .25 .75 1],'categorical',{'small','medium','large'});
summary(y)
運行結果是:
small 22
medium 46
large 32
【4】rng隨機數生成設置函數:
rng(seed) 使用非負整數 seed 為隨機數生成函數提供種子,以使 rand、randi 和 randn 生成可預測的數字序列。
rng('shuffle') 根據當前時間為隨機數生成函數提供種子。這樣,rand、randi 和 randn 會在您每次調用 rng 時生成不同的數字序列。
rng(seed, generator) 和 rng('shuffle', generator) 另外指定 rand、randi 和 randn 使用的隨機數生成函數的類型。generator 輸入為以下項之一:
'twister':Mersenne Twister
'simdTwister':面向 SIMD 的快速 Mersenne Twister 算法
'combRecursive':合並的多個遞歸
'multFibonacci':乘法滯后 Fibonacci
'v5uniform':傳統 MATLAB® 5.0 均勻生成函數
'v5normal':傳統 MATLAB 5.0 正常生成函數
'v4':傳統 MATLAB 4.0 生成函數
rng('default') 將 rand、randi 和 randn 使用的隨機數生成函數的設置重置為其默認值。這樣,會生成相同的隨機數,就好像您重新啟動了 MATLAB。默認設置是種子為 0 的 Mersenne Twister。
scurr = rng 返回 rand、randi 和 randn 使用的隨機數生成函數的當前設置。這些設置將在包含字段 'Type'、'Seed' 和 'State' 的結構體 scurr 中返回。
rng(s) 將 rand、randi 和 randn 使用的隨機數生成函數的設置還原回之前用 s = rng 等命令捕獲的值。
sprev = rng(...) 返回 rand、randi 和 randn 使用的隨機數生成函數的以前設置,然后更改這些設置。
【5】tabulate函數;
可以計算函數的個數以及在數組或矩陣中的占比率.
例如;
test = [1 2 3 3 3 5 4 1 2];
tabulate(test);
運行結果:
Value Count Percent
1 2 22.22%
2 2 22.22%
3 3 33.33%
4 1 11.11%
5 1 11.11%
test = [{'a'},{'aaa'},{'a'}];
tabulate(test)
運行結果:
Value Count Percent
a 2 66.67%
aaa 1 33.33%