使用了周志华老师写的机器学习中的神经网络的公式,具体公式可以看《机器学习》--周志华第103页,用的时标准BP算法
具体代码在下面,注释明确
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace mssbyi
{
//Random Random = new Random();
class BpDeep
{
Random Random = new Random();
//构造函数,构造bp网络,n为学习率,MST为最大学习次数
public BpDeep(List<int> net_size, double n, int MST, double min_error)
{
//模型参数传入
inputSize = net_size[0];
numLaters = net_size.Count;
netSize = net_size;
outputSize = net_size.Last();
N = n;
MaxStudyTimes = MST;
//设置输入层
layers.Add(new layer(netSize[0], netSize[1], n));
//初始化隐藏层
for (int i = 1; i < numLaters - 1; i++)
{
layers.Add(new layer(netSize[i], netSize[i + 1], n));
}
//初始化输出层
layers.Add(new layer(netSize.Last(), 0, n));//后期需要设置分类数量!!!!!!!!!!!
error = 100;//初使可以用很大的值
minError = min_error;
}
//正向传播
public void forward(List<double> input)
{
layers[0].Y = input;
for (int i = 1; i < numLaters; i++)
{
layers[i].forward(layers[i - 1]);
}
}
//反向传播
public void backward(List<double> output_real)
{
//计算误差
//计算误差和输出层神经元梯度项
List<double> g = new List<double>();
for (int i = 0; i < outputSize; i++)
{
g.Add((layers.Last().Y[i] * (1 - layers.Last().Y[i]) * (output_real[i] - layers.Last().Y[i])));
error += 0.5 * Math.Pow((output_real[i] - layers.Last().Y[i]), 2);
}
//设置输出层的阈值
List<double> db_last = new List<double>();
for (int i = 0; i < layers[numLaters - 1].numNeuron; i++)
{
db_last.Add(-1 * N * g[i]);
//db_last[i] = -1 * N * g[i];
}
layers[numLaters - 1].setDB = db_last;
//计算最后一个隐藏层的神经元梯度项
List<double> e = new List<double>();
for (int i = 0; i < layers[numLaters - 2].numNeuron; i++)
{
double sum = 0;
for (int j = 0; j < outputSize; j++)
{
sum += layers[numLaters - 2].getW[i][j] * g[j];
}
e.Add(layers[numLaters - 2].Y[i] * (1 - layers[numLaters - 2].Y[i]) * sum);
//e[i] = layers[numLaters - 2].Y[i] * (1 - layers[numLaters - 2].Y[i]) * sum;
}
layers[numLaters - 2].setE = e;
//设置最后一个隐藏层参数改正量
List<List<double>> dw = new List<List<double>>();
List<double> db_lastsecond = new List<double>();
for (int i = 0; i < layers[numLaters - 2].numNeuron; i++)
{
db_lastsecond.Add(-1 * N * e[i]);
//db_last[i] = -1 * N * e[i];
dw.Add(new List<double>());
for (int j = 0; j < layers[numLaters - 1].numNeuron; j++)
{
//w[i].Add(new double());
//dw[i] = new List<double>();
dw[i].Add(N * g[j] * layers[numLaters - 2].Y[i]);
//dw[i][j] = N * g[j] * layers[numLaters - 2].Y[i];
}
layers[numLaters - 2].setDW = dw;
}
layers[numLaters - 2].setDB = db_lastsecond;
//从倒数第二个隐藏层开始反向传播
for (int i = numLaters - 3; i >= 0; i--)
{
layers[i].backward(layers[i + 1]);
}
for(int i=numLaters-1;i>=0;i--)
{
layers[i].refreshW();
layers[i].refreshB();
}
}
//设置训练集和测试集,l为训练集的比例
public void setTrainAndTest(List<List<double>> input, List<List<double>> output, double l)
{
//将数据随机打乱
List<List<double>> newInput = new List<List<double>>();
List<List<double>> newOutput = new List<List<double>>();
for (int i = 0; i < input.Count; i++)
{
int j = Random.Next(newInput.Count + 1);
newInput.Insert(j, input[i]);
newOutput.Insert(j, output[i]);
}
//将数据集分为训练集和测试集
int num_train = (int)(input.Count * l);
int num_test = input.Count - num_train;
trainInput = newInput; //(List<List<double>>)newInput.Take(num_train);
trainOutput = newOutput;// (List<List<double>>)newOutput.Take(num_train);
//layers.Last().numNeuron = trainOutput.Count;
//layers.Add(new layer(trainOutput.Count, 0));
// testInput = (List<List<double>>)newInput.Skip(num_train);
// testOutput = (List<List<double>>)newOutput.Skip(num_train);
}
//训练
public double trainning()
{
int sum = 0;
while (error > minError && sum < MaxStudyTimes)
{
error = 0;
for (int i = 0; i < trainInput.Count; i++)
{
forward(trainInput[i]);
backward(trainOutput[i]);
}
error = error / outputSize;
sum++;
}
return error;
}
//预测
public int predict(List<double> input)
{
forward(input);
List<double> output = layers.Last().Y;
int key = -1;
double max = -1;
for (int i = 0; i < output.Count; i++)
{
if (output[i] > max)
{
max = output[i];
key = i;
}
}
return key;
}
private int inputSize;//输入层神经元数
private int numLaters;//网络总层数
private List<int> netSize = new List<int>(); //各层神经元数量
private int outputSize;//输出层神经元数量
private double N;//学习率
public double error;//误差
private double minError;//最小误差
private int MaxStudyTimes;
private List<layer> layers = new List<layer>();
public List<List<double>> trainInput = new List<List<double>>();
public List<List<double>> trainOutput = new List<List<double>>();
List<List<double>> testInput = new List<List<double>>();
List<List<double>> testOutput = new List<List<double>>();
};
class layer
{
Random Random = new Random();
//生成高斯分布的随机数
public double Rand(double u, double d)
{
double u1, u2, z, x;
//Random ram = new Random();
if (d <= 0)
{
return u;
}
u1 = Random.NextDouble();
u2 = Random.NextDouble();
z = Math.Sqrt(-2 * Math.Log(u1)) * Math.Sin(2 * Math.PI * u2);
x = u + d * z;
return x;
}
//构造函数
public layer(int num_neuron = 0, int last_nn = 0, double n=0.1)
{
numNeuron = num_neuron;
//初始化权值和阈值
db = new List<double>(numNeuron);
// dw.Add(numNeuron);
for (int i = 0; i < numNeuron; i++)
{
dw.Add(new List<double>());
b.Add(Rand(0, 0.3));
//b[i] = Rand(0, 0.3);
w.Add(new List<double>());
y.Add(new double());
db.Add(new double());
e.Add(last_nn);
for (int j = 0; j < last_nn; j++)
{
w[i].Add(Rand(0, 0.3));
dw[i].Add(new double());
//w[i][j] = Rand(0, 0.3);
}
}
N = n;
}
//激发函数
public double sigmoid(double x)
{
return 1 / (1 + Math.Exp(-x));
}
//正向传播,需要知道前一层的信息
public void forward(layer layer)
{
//List<List<double>> wl = new List<List<double>>(layer.w);
// List<double> x = new List<double>(layer.y);
for (int i = 0; i < numNeuron; i++)
{
for (int j = 0; j < layer.numNeuron; j++)
{
y[i] = y[i] + layer.y[j] * layer.w[j][i];
}
y[i] = sigmoid(y[i] + b[i]);
}
}
//误差逆传播,需要知道后一层的信息
public void backward(layer layer)
{
//计算这一层的神经元梯度项
for (int i = 0; i < numNeuron; i++)
{
double sum = 0;
for (int j = 0; j < layer.numNeuron; j++)
{
sum += w[i][j] * layer.e[j];
}
// e.Add(y[i] * (1 - y[i]) * sum);
e[i] = y[i] * (1 - y[i]) * sum;
}
//计算这一层的参数改正数
for (int i = 0; i < numNeuron; i++)
{
//db.Add(-1 * N * e[i]);
db[i] = -1 * N * e[i];
// dw.Add(new List<double>());
for (int j = 0; j < layer.numNeuron; j++)
{
//dw[i].Add(N * layer.e[j] * y[i]);
dw[i][j] = N * layer.e[j] * y[i];
}
}
}
//设置输出(用于训练时设置输入层)
public List<double> Y
{
set { y = value; }
get { return y; }
}
public List<List<double>> getW
{
get { return w; }
}
public List<List<double>> setDW
{
set { dw = value; }
}
public List<double> setDB
{
set { db = value; }
}
public List<double> setE
{
set { e = value; }
}
//刷新
public void refreshW()
{
for (int i = 0; i < numNeuron; i++)
{
for (int j = 0; j < w[i].Count; j++)
{
w[i][j] = w[i][j] + dw[i][j];
}
}
}
public void refreshB()
{
for (int i = 0; i < numNeuron; i++)
{
b[i] = b[i] + db[i];
}
}
//神经元数量
public int numNeuron = 0;
//权值
List<List<double>> w = new List<List<double>>();
List<List<double>> dw = new List<List<double>>();
//阈值
List<double> b = new List<double>();
List<double> db;// = new List<double>();
//输出
List<double> y = new List<double>();
//学习率
public double N;
//神经元梯度项
List<double> e = new List<double>();
};
}
下面有一个示例的使用方法
private void bP神经网络ToolStripMenuItem_Click(object sender, EventArgs e)
{
List<int> net_size = new List<int>();
net_size.Add(bands);
for(int i=0;i<5;i++)
{
net_size.Add(25);
}
net_size.Add(form6.ybdata.Count);
BpDeep net = new BpDeep(net_size, 0.1, 10000,0.01);
//将样本信息转为训练集和测试集形式,并输入
List<List<double>> input = new List<List<double>>();
List<List<double>> output = new List<List<double>>();
int num = 0;
double temp;
double realMax=0;
for(int i=0;i<pixelData.Count;i++)
{
if (pixelData[i].Max() > realMax)
{
realMax = pixelData[i].Max();
}
}
for(int i=0;i<form6.ybdata.Count;i++)//i表示类别
{
for(int j=0;j<form6.ybdata[i].Count;j++)//j表示该类别数量
{
input.Add(new List<double>());
output.Add(new List<double>());
//input.Add((List<double>)(form6.ybdata[i][j]));
for(int k=0;k<form6.ybdata[i][j].Count;k++)
{
temp = form6.ybdata[i][j][k];/// realMax;
input[num].Add(temp);
}
for(int k=0;k<form6.ybdata.Count;k++)
{
output[num].Add(0);
}
output[num][i] = 1;
num++;
}
}
net.setTrainAndTest(input, output,0.9);
//训练
double error=net.trainning();
//预测
List<double> predict_input = new List<double>();
for (int xx = 0; xx < width; xx++)
{
for (int yy = 0; yy < height; yy++)
{
for (int b = 0; b < bands; b++)
{
temp = pixelData[b][yy * width + xx];/// realMax;
predict_input.Add(temp);
}
bitmap.SetPixel(xx, yy, Class1.sll[net.predict(predict_input)].color);
predict_input.Clear();
}
}
pictureBox1.Image = bitmap;
//net.predict()
}
}
这个网络还存在一些问题。。。