在前面基础上,代码增加了通用性,样本点增加到八个,运行效果如下:
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深似海?),还得下功夫: