支持向量机(SVM)入门C#代码实现smo,改进第三版(五,画出来,吴恩达机器学习实践)

在前面基础上,代码增加了通用性,样本点增加到八个,运行效果如下:

c#代码如下:(对比参考前面的代码,前面运行没有show出来)

namespace smo改进3
{
    public partial class Form1 : Form//核函数仍然保持使用简单点积,没有使用高斯核函数,202009161816改进
    {
        public Form1()
        {
            InitializeComponent();
        }
        int kk(int i, int j, List<Point> xx)
        {//xi*xj           
            return xx[i].X * xx[j].X + xx[i].Y * xx[j].Y;
        }
        private double fxx(int temp, double[] a, int[] yi, double b, List<Point> xx)
        {
            // int k11 = 18; int k12 = 21; int k21 = k12; int k13 = 6; int k31 = 6; int k23 = 7; int k32 = 7; int k22 = 25; int k33 = 2;
            //调用  kk(int i, int j, List<Point> xx)

            double fxi = 0;         
                      
          //  fxi = a[0] * yi[0] * kk(0, temp, xx) + a[1] * yi[1] * kk(1, temp, xx) + a[2] * yi[2] * kk(2, temp, xx) + b;
            for (int m = 0; m < xx.Count; m++)
            {
                fxi += a[m] * yi[m] * kk(m, temp, xx);
            }
            fxi = fxi + b;
            return fxi;
        }
        int RandomSelect(int i, int 范围)
        {
            int j = i;
            while (i == j)
            {
                Random rm = new Random();
                j = rm.Next(0, 范围);
            }
            return j;
        }
        List<Point> drawPoint = new List<Point>();
        List<int> 辨别正负样本 = new List<int>();
        private void Form1_Load(object sender, EventArgs e)
        {
            //增加到8个样本点
            List<Point> xx = new List<Point>();
            Point temppt = new Point(30, 30);
            xx.Add(temppt);
            temppt = new Point(40, 30);
            xx.Add(temppt);
            temppt = new Point(10, 10);
            xx.Add(temppt);
            temppt = new Point(10, 0);
            xx.Add(temppt);
            temppt = new Point(20, 0);
            xx.Add(temppt);
            temppt = new Point(0, 0);
            xx.Add(temppt);
            temppt = new Point(60, 20);
            xx.Add(temppt);
            temppt = new Point(60, 10);
            xx.Add(temppt);

            List<int> yi = new List<int>();
            int tempyi = 1;
            yi.Add(tempyi);
            tempyi = 1;
            yi.Add(tempyi);
            tempyi = -1;
            yi.Add(tempyi);
            tempyi = -1;
            yi.Add(tempyi);
            tempyi = -1;
            yi.Add(tempyi);
            tempyi = -1;
            yi.Add(tempyi);
            tempyi = 1;
            yi.Add(tempyi);
            tempyi = 1;
            yi.Add(tempyi);

            double C = 1; //对不在界内的惩罚因子
            double tol = 0.01;//容忍极限值
            int maxPasses = 5; //表示没有改变拉格朗日乘子的最多迭代次数
            List<int> boundAlpha = new List<int>();
            double[] a = new double[xx.Count];//拉格朗日乘子

            //将乘子初始化为0
            for (int i = 0; i < xx.Count; i++)
            {
                a[i] = 0;
            }
            int passes = 0;
            double b = 0;
            double min = 0;
            while (passes < maxPasses)
            {
                //表示改变乘子的次数(基本上是成对改变的)
                int num_changed_alphas = 0;
                for (int i = 0; i < xx.Count; i++)
                {
                    //表示特定阶段由a和b所决定的输出与真实yi的误差
                    //参照公式(7)
                    //double Ei = getE(i);                  
                    double Ei = fxx(i, a, yi.ToArray(), b, xx) - yi[i];
                    if ((yi[i] * Ei < -tol && a[i] < C) ||
                (yi[i] * Ei > tol && a[i] > 0))
                    {
                        int j;
                        if (boundAlpha.Count > 0)
                        {
                            //参照公式(5)
                            int maxK = -1;   //用于保存临时最大索引
                            double maxDeltaE = 0;  //用于保存临时最大差值--->|Ei-Ej|
                            //j = findMax(Ei, boundAlpha);
                            for (int ii = 0; ii < boundAlpha.Count; ii++)
                            {
                                int k = boundAlpha[ii];
                                if (k == i) continue;
                                double Ek = fxx(k, a, yi.ToArray(), b, xx) - yi[k];
                                double deltaE = Math.Abs(Ei - Ek);
                                if (deltaE > maxDeltaE)
                                {
                                    maxK = k;
                                    maxDeltaE = deltaE;
                                }
                            }
                            j = maxK;//0--2
                        }
                        else
                        {
                            //如果边界上没有,就随便选一个j != i的aj
                            j = RandomSelect(i,xx.Count-1);//1--3,与0---2矛盾,这个函数已更正未0--2,已经全部更改为0---2
                        }
                        double Ej = fxx(j, a, yi.ToArray(), b, xx) - yi[j];
                        //保存当前的ai和aj
                        double oldAi = a[i];
                        double oldAj = a[j];

                        /*
                         * 计算乘子的范围U, V
                         * 参考公式(4)
                         */
                        double L, H;
                        if (yi[i] != yi[j])
                        {
                            L = Math.Max(0, a[j] - a[i]);
                            H = Math.Min(C, C - a[i] + a[j]);
                        }
                        else
                        {
                            L = Math.Max(0, a[i] + a[j] - C);
                            H = Math.Min(0, a[i] + a[j]);
                        }
                        /*
                     * 如果eta等于0或者大于0 则表明a最优值应该在L或者U上
                     */
                        double eta = 2 * kk(i, j, xx) - kk(i, i, xx) - kk(j, j, xx);//公式(3)

                        if (eta >= 0)
                            continue;

                        a[j] = a[j] - yi[j] * (Ei - Ej) / eta;//公式(2)
                        if (0 < a[j] && a[j] < C)
                            boundAlpha.Add(j);

                        if (a[j] < L)
                            a[j] = L;
                        else if (a[j] > H)
                            a[j] = H;
                        if (L == H) continue;

                        if (Math.Abs(a[j] - oldAj) < 1e-5)
                            continue;
                        a[i] = a[i] + yi[i] * yi[j] * (oldAj - a[j]);
                        if (0 < a[i] && a[i] < C)
                            boundAlpha.Add(i);
                        /*
                     * 计算b1, b2
                     * 参照公式(6)
                     */
                        double b1 = b - Ei - yi[i] * (a[i] - oldAi) * kk(i, i, xx) - yi[j] * (a[j] - oldAj) * kk(i, j, xx);
                        double b2 = b - Ej - yi[i] * (a[i] - oldAi) * kk(i, j, xx) - yi[j] * (a[j] - oldAj) * kk(j, j, xx);

                        if (0 < a[i] && a[i] < C)
                            b = b1;
                        else if (0 < a[j] && a[j] < C)
                            b = b2;
                        else
                            b = (b1 + b2) / 2;

                        num_changed_alphas = num_changed_alphas + 1;

                        //double min = a[0] * a[0] * yi[0] * yi[0] * kk(0, 0, xx) + a[0] * a[1] * yi[0] * yi[1] * kk(0, 1, xx)
                        //    + a[0] * a[2] * yi[0] * yi[2] * kk(0, 2, xx) + a[1] * a[0] * yi[1] * yi[0] * kk(1, 0, xx)
                        //    + a[1] * a[1] * yi[1] * yi[1] * kk(1, 1, xx) + a[1] * a[2] * yi[1] * yi[2] * kk(1, 2, xx)
                        //        + a[2] * a[0] * yi[2] * yi[0] * kk(2, 0, xx) + a[2] * a[1] * yi[2] * yi[1] * kk(2, 1, xx)
                        //        + a[2] * a[2] * yi[2] * yi[2] * kk(2, 2, xx) - a[0] - a[1] - a[2];
                     
                        for(int mm=0;mm<xx.Count;mm++)
                            for (int nn = 0; nn < xx.Count; nn++)
                            {
                                min += a[mm] * a[nn] * yi[mm] * yi[nn] * kk(mm, nn, xx);
                            }
                        double he = 0;
                        for (int hh = 0; hh < xx.Count; hh++)
                        {
                            he += a[hh];
                        }
                        min = min - he;

                    }
                }
                if (num_changed_alphas == 0)
                {
                    passes++;
                }
                else
                {
                    passes = 0;
                }
                //求w,
                double wx = 0; double wy = 0;
                for (int nn = 0; nn < xx.Count; nn++)
                {
                    wx += a[nn] * yi[nn] * xx[nn].X;//w.x=A

                    wy += a[nn] * yi[nn] * xx[nn].Y;//w.y=B
                }
                //求与x轴y轴的交点
                x0y =(float)( -b / wy);//未考虑0值,公式x=0,y=-b/B
                y0x = (float)(-b / wx);//未考虑0值,公式y=0,x=-b/A
                //获取样本点
                 drawPoint = new List<Point>();
                 辨别正负样本 = new List<int>();
                 for (int mm = 0; mm < xx.Count; mm++)
                 {
                     drawPoint.Add(xx[mm]);
                     辨别正负样本.Add(yi[mm]);
                 }
            }
        }
        float x0y = 0;
        float y0x = 0;
        private void Form1_Paint(object sender, PaintEventArgs e)
        {//画线段(x=0,y=-b/B)----(y=0,x=-b/A)
            Graphics g = e.Graphics;
            if( x0y != 0&& y0x != 0)
            g.DrawLine(new Pen(Color.Red), new PointF(0, x0y), new PointF(y0x, 0));
           //以下画样本点 
            if (drawPoint.Count != 0)
            {
                for (int jj = 0; jj < drawPoint.Count; jj++)
                {
                    if (辨别正负样本[jj] == -1)
                    {
                        g.DrawEllipse(new Pen(Color.Red), new Rectangle(drawPoint[jj].X - 3, drawPoint[jj].Y - 3, 6, 6));
                    }
                    else
                    {
                        g.DrawRectangle(new Pen(Color.Blue), drawPoint[jj].X - 3, drawPoint[jj].Y - 3, 6, 6);
                    }
                }
            }
        }
    }
}

但也出现了如下结果,看来任务没完没了(svm深似海?),还得下功夫

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值