人工智慧考試——k近鄰演算法對鳶尾花(iris)資料集進行分析
一、題目
通過修改提供的k_nn.c檔案,讀取鳶尾花的資料集,其中iris_training_set.txt和iris_test_set.txt分別為訓練集和測試集,兩個資料集中最後一列為類別標籤,其餘列為表示花瓣和萼片長度和寬度的輸入特徵。通過計算測試集中的每個輸入行和訓練集中所有行的相似性並通過投票來確定測試集中當前行的類別,即預測該行的輸出類別值Y1。最後,根據測試集中所有的原始類別Y0和預測的類別,計算整體的分類準確率並輸出。K近鄰演算法中的k值取3和5分別計算分類預測結果。
二、思路
讀取訓練集和測試集的樣本,把資料匯出來(這裡我把它們分別放到了sample_t結構體陣列 samples[MAX_SAMPLES]和fv[MAX_SAMPLES]中,前者儲存訓練集後者儲存測試集)然後計算每個特徵向量距離,把結果存到距離矩陣中。最後計算計算選擇投票數最多的返回
三、程式碼
感覺寫的有些繁,希望各位精簡!stringToclass函式實現由種類字串轉換為種類編號,為了驗證後面對於測試集結果是否準確。其實也可以直接判斷兩個字串相等啦,readfile讀檔案,設定了訓練集還是測試集,前者標誌位為0
#include <stdio.h> #include <stdlib.h> #include <String.h> #include "maths.h" #define MAX_LINE 1024 #define MAX_FEATURES 4 #define MAX_CLASSES 3 typedef struct { float features[MAX_FEATURES]; int class; } sample_t; #define MAX_SAMPLES 120 #define Test_SAMPLES 30 sample_t samples[MAX_SAMPLES]; sample_t fv[MAX_SAMPLES]; char *names[3] = { "Iris-setosa", "Iris-versicolor", "Iris-virginica" }; int count_votes(double *dist, int k) { int i, list[MAX_SAMPLES]; int votes[MAX_CLASSES]; int sorted; int max, class; for (i = 0; i < MAX_SAMPLES; i++) list[i] = samples[i].class; /* Sort the list in ascending order of distance */ sorted = 0; while (!sorted) { sorted = 1; for (i = 0; i < MAX_SAMPLES - 1; i++) { if (dist[i] > dist[i + 1]) { int temp = list[i]; list[i] = list[i + 1]; list[i + 1] = temp; double tdist = dist[i]; dist[i] = dist[i + 1]; dist[i + 1] = tdist; sorted = 0; } } } /* Count the votes */ for (i = 0; i < MAX_CLASSES; i++) votes[i] = 0; /* Add the vote to the particular class */ for (i = 0; i < k; i++) { votes[list[i]]++; } /* Count the votes and return the largest class */ max = votes[0]; class = 0; for (i = 1; i < MAX_CLASSES; i++) { if (votes[i] > max) { max = votes[i]; class = i; } } return class; } double calc_distance(float *feature_vector, int example) { double distance = 0.0; int i; for (i = 0; i < MAX_FEATURES; i++) { distance += sqr((samples[example].features[i] - feature_vector[i])); } return sqrt(distance); } //字串轉換成種類號 int stringToclass(char *name) { if (strcmp(name, "Iris-setosa") == 0) return 0; if (strcmp(name, "Iris-versicolor") == 0) return 1; if (strcmp(name, "Iris-virginica") == 0) return 2; } //讀檔案函式,區分訓練集和測試集 int readfile(char *filename,int count,int whichset) //whichset表示是訓練集還是測試集,訓練集用0表示,測試集非0 { float a[4]; char name[50]; //讀檔案 char buf[MAX_LINE]; /*緩衝區*/ FILE *fp; /*檔案指標*/ if ((fp = fopen(filename, "r")) == NULL) { perror("fail to read"); //exit(1); return 0; } for (int i = 0; i < count; i++) { if (whichset == 0) { fscanf(fp, "%f %f %f %f %s", &samples[i].features[0], &samples[i].features[1], &samples[i].features[2],&samples[i].features[3],name); samples[i].class = stringToclass(name); //printf("%f %f %f %f %d\n", samples[i].features[0], samples[i].features[1], samples[i].features[2], samples[i].features[3], samples[i].class); //printf("%d\n", samples[i].class); } else { fscanf(fp, "%f %f %f %f %s", &fv[i].features[0], &fv[i].features[1], &fv[i].features[2], &fv[i].features[3],name); fv[i].class = stringToclass(name); //printf("%f %f %f %f %d\n", fv[i].features[0], fv[i].features[1], fv[i].features[2], fv[i].features[3], fv[i].class, samples[i].class); //printf("%d\n", fv[i].class); } } return 1; } int main(void) { int sum = 0; if (readfile("C:\\Users\\15721\\Desktop\\大三計算機課程PPt\\人工智慧\\工程\\機器學習\\機器學習演算法\\iris_training_set.txt", MAX_SAMPLES,0)==0) exit(1); else printf("讀取訓練集檔案成功!\n"); if (readfile("C:\\Users\\15721\\Desktop\\大三計算機課程PPt\\人工智慧\\工程\\機器學習\\機器學習演算法\\iris_test_set.txt", 30,1) == 0) exit(1); else printf("讀取測試集檔案成功!\n"); int i, class = 0; double distance[MAX_SAMPLES]; int k = 3; for (int j = 0; j < Test_SAMPLES; j++) { //float fv[] = { 5.5, 2.6, 4.4, 1.2 }; /* Walk through each example vector */ for (int i = 0; i < MAX_SAMPLES; i++) { distance[i] = calc_distance(fv[j].features, i); //distance[i] = calc_distance(fv, i); } /* Count, Sort and Return Winning Class */ class = count_votes(distance, k); printf("Class is %s \n", names[class]); if (class == fv[j].class) sum = sum + 1; } printf("正確率是 %.2f%% \n", (sum / 30.0)*100); system("pause"); return 0; }
四、除錯的bug
考試過程中遇到的bug(有些簡單低階,原諒我這個zz)。考試中上來就連結器錯誤,真的很慌!!
(1)VS連結器錯誤(LINK ERROR)
描述:debug時出現報聯結器錯誤,打不開編譯執行的exe檔案
原因:連結器錯誤出現的原因多種多樣,此次錯誤的原因極大可能時上一次編譯執行的exe檔案沒有完全關閉,雖然不顯示,但依然有它的程序
解決方法:徹底殺死執行的exe檔案的程序或者採用比較暴力的手段,直接關vs重新開啟工程
【注】: 原來exe程序之所以沒有被殺死,是因為下面的bug導致沒有完全終止除錯
(2)變數周圍棧被破環(Stack around the variale... was corruoted)
描述:debugging過程中報此錯誤,程式中斷
原因:低階錯誤!鳶尾花(iris)的類別為字串型別,我給的空間太小,破環了此變數周圍的棧空間
解決方法:vs下斷點除錯,找到錯誤點
(3)報斷言緩衝區不為空
描述:錯誤來源於我寫的讀檔案的函式
原因:將檔案的花的型別那一列字串讀到了一個int型變數裡,導致野指標
解決方法: debug找野指標,修改引數int型變數為字串型別
五、總結
整體考試沒有太大難度,但是C語言真的很久不用了,一寫一個bug。。。檔案讀取操作只記得fgets,一讀一個字串,慢慢分(突然想到python...)真心累!! 下面放一個fscanf的庫函式連結
真的是小白菜又菜,唉~