编程笔记——易错点1

小参数暴露大问题

修改代码参数的时候出现的问题,可能不仅仅是范围上的小问题,而是整个代码逻辑本来就有很严重的问题。只是在参数范围合适的时候,刚好避开了这些边界而已。所以才会出现有的时候改掉一个小参数,就导致整个代码怎么调都运行不了的情况。
比如在用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只进行了初始化为零,而忘记在每次循环时重新置零。
以前已经因为这个错了好几次了,一定要注意这一点 ~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值