一、题目
通过修改提供的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的库函数链接
http://www.runoob.com/cprogramming/c-function-fscanf.html
真的是小白菜又菜,唉~