小参数暴露大问题
修改代码参数的时候出现的问题,可能不仅仅是范围上的小问题,而是整个代码逻辑本来就有很严重的问题。只是在参数范围合适的时候,刚好避开了这些边界而已。所以才会出现有的时候改掉一个小参数,就导致整个代码怎么调都运行不了的情况。
比如在用matlab写KNN模型的时候,为了研究噪点对模型性能的影响,需要按比例(从0到1)生成噪点。如果n一开始就设为800,经过舍弃超出9类位置范围的点之后,实际的数据点大概率少于800,那么在噪点占比很低的情况下,总数据(有效点+噪点)就无法填满整个数据空间,后面再用n=800去训练时,类别Y中可能会自动补零,就无法完成count(Yd(n-i+1),1)的记录(因为matlab不允许数组下标为零),程序会报错。
但在噪声点/训练样本总数x时,若将范围设为0.6到1(即x = [0:0.01:1];),大概率不会报错,因为此时基本上 噪点数>超出9类位置范围的点的数量 ,但逻辑上存在严重漏洞。
因此可以将n尽可能设得大一点(如n=1600),改成:
(注意:这种改法依然不严谨,但暂时没想到更简单有效的方法)
%%%%%%%%%%%%%%%%%%% 数据生成 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
x = [0:0.01:1]; % 表示噪声点数/训练样本总数
xlen = length(x);
y = zeros(xlen);
count = zeros(typeNum,1);
distance = zeros(n,1);
Ym = zeros(m,1); % 将测试样本的预测类别存放在Ym中
errNum = 0; % 类别预测出错的次数
typeNum = 9; % 数据共有typeNum种
k = 10; % 预设的取k个近邻
for index = 1 : xlen % 计算新的训练样本总数
n = 1600; % 样本量大小
X = rand(n,2)*10; % 2n * 2的数据矩阵,每一行表示一个数据点,第一列表示x轴坐标,第二列表示y轴坐标
Y = zeros(n,1); % 类别标签
for i=1:n
if 0<X(i,1) && X(i,1)<3 && 0<X(i,2) && X(i,2)<3 % 根据x和y轴坐标确定分类
Y(i) = 1;
end
if 0<X(i,1) && X(i,1)<3 && 3.5<X(i,2) && X(i,2)<6.5
Y(i) = 2;
end
if 0<X(i,1) && X(i,1)<3 && 7<X(i,2) && X(i,2)<10
Y(i) = 3;
end
if 3.5<X(i,1) && X(i,1)<6.5 && 0<X(i,2) && X(i,2)<3
Y(i) = 4;
end
if 3.5<X(i,1) && X(i,1)<6.5 && 3.5<X(i,2) && X(i,2)<6.5
Y(i) = 5;
end
if 3.5<X(i,1) && X(i,1)<6.5 && 7<X(i,2) && X(i,2)<10
Y(i) = 6;
end
if 7<X(i,1) && X(i,1)<10 && 0<X(i,2) && X(i,2)<3
Y(i) = 7;
end
if 7<X(i,1) && X(i,1)<10 && 3.5<X(i,2) && X(i,2)<6.5
Y(i) = 8;
end
if 7<X(i,1) && X(i,1)<10 && 7<X(i,2) && X(i,2)<10
Y(i) = 9;
end
end
X = X(Y>0,:); % 注意X是在[0,10]*[0,10]范围内均匀生成的,而我们只标出了一部分X,类别之间的白色间隔中的点没有标,因此需要将这些点去掉
Y = Y(Y>0,:); % X(Y>0,:)表示只取X中对应的Y大于0的行,这是因为白色间隔中的点的Y都为0
n = 800;
% X = X(1:n,:);
% Y = Y(1:n,:);
nn = round(n * (1-x(index)));
X(nn+1:n,:) = rand(n-nn,2)*10; % 增加n-nn个噪声点
Y(nn+1:n,:) = ceil( rand(n-nn,1)*9 ); % 噪声点的标签随机选取。rand(n-nn,1)*9表示生产[0,9]的均匀分布,ceil表示上取整,故结果为1,2,...,9
%%%%%%%%%%%%%%%%%%% 测试 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%% 生成测试数据:与训练数据同分布 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
m = 200; % 测试样本量大小
Xt = rand(m,2)*10;
Yt = zeros(m,1);
errNum = 0;
for i=1:m
if 0<Xt(i,1) && Xt(i,1)<3 && 0<Xt(i,2) && Xt(i,2)<3
Yt(i) = 1;
end
if 0<Xt(i,1) && Xt(i,1)<3 && 3.5<Xt(i,2) && Xt(i,2)<6.5
Yt(i) = 2;
end
if 0<Xt(i,1) && Xt(i,1)<3 && 7<Xt(i,2) && Xt(i,2)<10
Yt(i) = 3;
end
if 3.5<Xt(i,1) && Xt(i,1)<6.5 && 0<Xt(i,2) && Xt(i,2)<3
Yt(i) = 4;
end
if 3.5<Xt(i,1) && Xt(i,1)<6.5 && 3.5<Xt(i,2) && Xt(i,2)<6.5
Yt(i) = 5;
end
if 3.5<Xt(i,1) && Xt(i,1)<6.5 && 7<Xt(i,2) && Xt(i,2)<10
Yt(i) = 6;
end
if 7<Xt(i,1) && Xt(i,1)<10 && 0<Xt(i,2) && Xt(i,2)<3
Yt(i) = 7;
end
if 7<Xt(i,1) && Xt(i,1)<10 && 3.5<Xt(i,2) && Xt(i,2)<6.5
Yt(i) = 8;
end
if 7<Xt(i,1) && Xt(i,1)<10 && 7<Xt(i,2) && Xt(i,2)<10
Yt(i) = 9;
end
end
Xt = Xt(Yt>0,:);
Yt = Yt(Yt>0,:);
m = length(Yt);
%%%%%%%%%%%%%%%%%% K-近邻算法:学生实现 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%% 给出模型的预测输出,并与测试数据的真实输出比较,计算错误率 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
for ind = 1 : m
% 计算测试样本到每个训练样本的Lp距离
for i = 1 : n
% distance(i) = abs(X(i,1) - Xt(ind,1)) + abs(X(i,2) - Xt(ind,2)); % 曼哈顿距离
distance(i) = sqrt((X(i,1) - Xt(ind,1)) ^ 2 + (X(i,2) - Xt(ind,2)) ^ 2); % 欧氏距离
% if abs(X(i,1) - Xt(ind,1)) > abs(X(i,2) - Xt(ind,2)) % 切比雪夫距离
% distance(i) = abs(X(i,1) - Xt(ind,1));
% else
% distance(i) = abs(X(i,2) - Xt(ind,2));
% end
end
Yd = Y; % 按距离记录类别
Dtemp = 0;
Ytemp = 0;
% 按距离从大到小排序,类别标记随之改变,只需排出最小的k个值,存在distance和Yd的后k个位置
for i = 1 : k
for j = 1 : (n - i)
if(distance(j) <= distance(j + 1))
Dtemp = distance(j);
distance(j) = distance(j + 1);
distance(j + 1) = Dtemp;
Ytemp = Yd(j);
Yd(j) = Yd(j + 1);
Yd(j + 1) = Ytemp;
end
end
end
for i = 1 : typeNum
count(i,1) = 0;
end
% 记录k个近邻中每一类的数量
for i = 1 : k
count(Yd(n - i + 1),1) = count(Yd(n - i + 1),1) + 1;
end
max = 0;
% 寻找频率最高值对应的下标,即为类型号
for i = 1 : typeNum
if max < count(i,1)
Ym(ind,1) = i;
max = count(i,1);
end
end
% 统计预测错误的次数
if Ym(ind) ~= Yt(ind)
errNum = errNum + 1;
end
end
y(index) = errNum / m;
if y(index) ~= 0
fprintf('%d\n',index);
end
end
%%%%%%%%%%%%%%%%%% 噪声比对错误率的影响 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
figure(4)
plot(x, y), xlabel('噪声点数/训练样本总数'), ylabel('错误率'), title('噪声比对错误率的影响'),
grid on, axis equal
set(gca,'XLim',[0,1])
set(gca,'YLim',[0,1])
效果如下:
2. 一定要记得把用于计数的变量在合适的代码块中置零
在写上面那段代码时,某次结果错误(模型错误率>1)的原因是errNum只进行了初始化为零,而忘记在每次循环时重新置零。
以前已经因为这个错了好几次了,一定要注意这一点 ~