# 如何利用 C# 实现神经网络的感知器模型？

https://en.wikipedia.org/wiki/Perceptron

1. 实现激活函数 `IActivationFunction`

``````public class ThresholdFunction : IActivationFunction
{
public double Function(double x)
{
return (x >= 0) ? 1 : 0;
}

public double Derivative(double x)
{
return 0;
}

public double Derivative2(double y)
{
return 0;
}
}
``````

``````public class SignFunction : IActivationFunction
{
public double Function(double x)
{
return x >= 0 ? 1 : -1;
}

public double Derivative(double x)
{
return 0;
}

public double Derivative2(double y)
{
return 0;
}
}
``````

2. 继承抽象神经元类 `Neuron`

``````public class ActivationNeuron : Neuron
{
// 阈值
public double Threshold { get; set; } = 0.0;

// 激活函数
public IActivationFunction ActivationFunction { get; set; }

// 构造函数
public ActivationNeuron(int inputs, IActivationFunction function)
: base(inputs)
{
ActivationFunction = function;
}

// 初始化权值阈值
public override void Randomize()
{
base.Randomize();
Threshold = Rand.NextDouble()*(RandRange.Length) + RandRange.Min;
}

// 计算神经元的输出
public override double Compute(double[] input)
{
if (input.Length != InputsCount)
throw new ArgumentException("输入向量的长度错误。");

double sum = 0.0;
for (int i = 0; i < Weights.Length; i++)
{
sum += Weights[i]*input[i];
}

sum += Threshold;
double output = ActivationFunction.Function(sum);
Output = output;
return output;
}
}
``````

3. 继承抽象神经网络层类 `Layer`

``````public class ActivationLayer : Layer
{
public ActivationLayer(int neuronsCount, int inputsCount, IActivationFunction function)
: base(neuronsCount, inputsCount)
{
for (int i = 0; i < Neurons.Length; i++)
Neurons[i] = new ActivationNeuron(inputsCount, function);
}

public void SetActivationFunction(IActivationFunction function)
{
for (int i = 0; i < Neurons.Length; i++)
{
((ActivationNeuron)Neurons[i]).ActivationFunction = function;
}
}
}
``````

4. 继承抽象的神经网络类 `Network`

``````public class ActivationNetwork : Network
{
public ActivationNetwork(IActivationFunction function, int inputsCount, params int[] neuronsCount)
: base(inputsCount, neuronsCount.Length)
{
// neuronsCount 指定神经网络每层中的神经元数量。
for (int i = 0; i < Layers.Length; i++)
{
Layers[i] = new ActivationLayer(
neuronsCount[i],
(i == 0) ? inputsCount : neuronsCount[i - 1],
function);
}
}

public void SetActivationFunction(IActivationFunction function)
{
for (int i = 0; i < Layers.Length; i++)
{
((ActivationLayer)Layers[i]).SetActivationFunction(function);
}
}
}
``````

``````ActivationNetwork network = new ActivationNetwork(
new SigmoidFunction(), // sigmoid 激活函数
3, // 3个输入
new int[] {4, 1} //两层 中间层4个神经元，输出层1个神经元
);
``````

5. 实现感知器学习规则。

``````public class PerceptronLearning : ISupervisedLearning
{
// 神经网络
// 学习率
private double _learningRate = 0.1;

// 学习率, [0, 1].
public double LearningRate
{
get { return _learningRate; }
set
{
_learningRate = Math.Max(0.0, Math.Min(1.0, value));
}
}

public PerceptronLearning(ActivationNetwork network)
{
if (network.Layers.Length != 1)
{
throw new ArgumentException("无效的神经网络，它应该只有一层。");
}

_network = network;
}
// 单个训练样本
public double Run(double[] input, double[] output)
{
double[] networkOutput = _network.Compute(input);
Layer layer = _network.Layers[0];
double error = 0.0;

for (int j = 0; j < layer.Neurons.Length; j++)
{
double e = output[j] - networkOutput[j];
if (e != 0)
{
ActivationNeuron perceptron = layer.Neurons[j] as ActivationNeuron;

if (perceptron == null)
throw new Exception("神经元为null。");

for (int i = 0; i < perceptron.Weights.Length; i++)
{
perceptron.Weights[i] += _learningRate * e * input[i];
}
perceptron.Threshold += _learningRate * e;

error += Math.Abs(e);
}
}

return error;
}

// 所有训练样本
public double RunEpoch(double[][] input, double[][] output)
{
double error = 0.0;
for (int i = 0, n = input.Length; i < n; i++)
{
error += Run(input[i], output[i]);
}
return error;
}
}
``````

6. 感知器模型的应用

``````double[][] inputs = new double[4][];
double[][] outputs = new double[4][];

//(0,0);(0,1);(1,0)
inputs[0] = new double[] {0, 0};
inputs[1] = new double[] {0, 1};
inputs[2] = new double[] {1, 0};

outputs[0] = new double[] {0};
outputs[1] = new double[] {0};
outputs[2] = new double[] {0};

//(1,1)
inputs[3] = new double[] {1, 1};
outputs[3] = new double[] {1};

ActivationNetwork network = new ActivationNetwork(
new ThresholdFunction(), 2, 1);

PerceptronLearning teacher = new PerceptronLearning(network);
teacher.LearningRate = 0.1;

int iteration = 1;
while (true)
{
double error = teacher.RunEpoch(inputs, outputs);
Console.WriteLine(@"迭代次数:{0},总体误差:{1}", iteration, error);

if (error == 0)
break;
iteration++;
}

Console.WriteLine();

ActivationNeuron neuron = network.Layers[0].Neurons[0] as ActivationNeuron;

Console.WriteLine(@"Weight 1:{0}", neuron.Weights[0].ToString("F3"));
Console.WriteLine(@"Weight 2:{0}", neuron.Weights[1].ToString("F3"));
Console.WriteLine(@"Threshold:{0}", neuron.Threshold.ToString("F3"));
``````

and

``````double[][] inputs = new double[4][];
double[][] outputs = new double[4][];

//(0,0)
inputs[0] = new double[] {0, 0};
outputs[0] = new double[] {0};

//(1,1);(0,1);(1,0)
inputs[1] = new double[] {0, 1};
inputs[2] = new double[] {1, 0};
inputs[3] = new double[] {1, 1};

outputs[1] = new double[] {1};
outputs[2] = new double[] {1};
outputs[3] = new double[] {1};

ActivationNetwork network = new ActivationNetwork(
new ThresholdFunction(), 2, 1);

PerceptronLearning teacher = new PerceptronLearning(network);
teacher.LearningRate = 0.1;

int iteration = 1;
while (true)
{
double error = teacher.RunEpoch(inputs, outputs);
Console.WriteLine(@"迭代次数:{0},总体误差:{1}", iteration, error);

if (error == 0)
break;
iteration++;
}

Console.WriteLine();
ActivationNeuron neuron = network.Layers[0].Neurons[0] as ActivationNeuron;

Console.WriteLine(@"Weight 1:{0}", neuron.Weights[0].ToString("F3"));
Console.WriteLine(@"Weight 2:{0}", neuron.Weights[1].ToString("F3"));
Console.WriteLine(@"Threshold:{0}", neuron.Threshold.ToString("F3"));
``````

or

``````第一类：(0.1,0.1);(0.2,0.3);(0.3,0.4);(0.1,0.3);(0.2,0.5)

``````

``````double[][] inputs = new double[15][];
double[][] outputs = new double[15][];

//(0.1,0.1);(0.2,0.3);(0.3,0.4);(0.1,0.3);(0.2,0.5)
inputs[0] = new double[] {0.1, 0.1};
inputs[1] = new double[] {0.2, 0.3};
inputs[2] = new double[] {0.3, 0.4};
inputs[3] = new double[] {0.1, 0.3};
inputs[4] = new double[] {0.2, 0.5};

outputs[0] = new double[] {1, 0, 0};
outputs[1] = new double[] {1, 0, 0};
outputs[2] = new double[] {1, 0, 0};
outputs[3] = new double[] {1, 0, 0};
outputs[4] = new double[] {1, 0, 0};

//(0.1,1.0);(0.2,1.1);(0.3,0.9);(0.4,0.8);(0.2,0.9)
inputs[5] = new double[] {0.1, 1.0};
inputs[6] = new double[] {0.2, 1.1};
inputs[7] = new double[] {0.3, 0.9};
inputs[8] = new double[] {0.4, 0.8};
inputs[9] = new double[] {0.2, 0.9};

outputs[5] = new double[] {0, 1, 0};
outputs[6] = new double[] {0, 1, 0};
outputs[7] = new double[] {0, 1, 0};
outputs[8] = new double[] {0, 1, 0};
outputs[9] = new double[] {0, 1, 0};

//(1.0,0.4);(0.9,0.5);(0.8,0.6);(0.9,0.4);(1.0,0.5)
inputs[10] = new double[] {1.0, 0.4};
inputs[11] = new double[] {0.9, 0.5};
inputs[12] = new double[] {0.8, 0.6};
inputs[13] = new double[] {0.9, 0.4};
inputs[14] = new double[] {1.0, 0.5};

outputs[10] = new double[] {0, 0, 1};
outputs[11] = new double[] {0, 0, 1};
outputs[12] = new double[] {0, 0, 1};
outputs[13] = new double[] {0, 0, 1};
outputs[14] = new double[] {0, 0, 1};
``````

``````ActivationNetwork network = new ActivationNetwork(
new ThresholdFunction(), 2, 3);

PerceptronLearning teacher = new PerceptronLearning(network);
teacher.LearningRate = 0.1;

int iteration = 1;

while (true)
{
double error = teacher.RunEpoch(inputs, outputs);
Console.WriteLine(@"迭代次数:{0},总体误差:{1}", iteration, error);

if (error == 0)
break;
iteration++;
}
``````

``````ActivationLayer layer = network.Layers[0] as ActivationLayer;
for (int i = 0; i < 3; i++)
{
Console.WriteLine(@"神经元:{0}", i + 1);
Console.WriteLine(@"Weight 1:{0}", layer.Neurons[i].Weights[0]);
Console.WriteLine(@"Weight 2:{0}", layer.Neurons[i].Weights[1]);
Console.WriteLine(@"Threshold:{0}",
((ActivationNeuron) layer.Neurons[i]).Threshold);
}
``````

• 序言：七十年代末，一起剥皮案震惊了整个滨河市，随后出现的几起案子，更是在滨河造成了极大的恐慌，老刑警刘岩，带你破解...
沈念sama阅读 157,924评论 4 360
• 序言：滨河连续发生了三起死亡事件，死亡现场离奇诡异，居然都是意外死亡，警方通过查阅死者的电脑和手机，发现死者居然都...
沈念sama阅读 66,902评论 1 290
• 文/潘晓璐 我一进店门，熙熙楼的掌柜王于贵愁眉苦脸地迎上来，“玉大人，你说我怎么就摊上这事。” “怎么了？”我有些...
开封第一讲书人阅读 107,716评论 0 239
• 文/不坏的土叔 我叫张陵，是天一观的道长。 经常有香客问我，道长，这世上最难降的妖魔是什么？ 我笑而不...
开封第一讲书人阅读 43,783评论 0 203
• 正文 为了忘掉前任，我火速办了婚礼，结果婚礼上，老公的妹妹穿的比我还像新娘。我一直安慰自己，他们只是感情好，可当我...
茶点故事阅读 52,166评论 3 286
• 文/花漫 我一把揭开白布。 她就那样静静地躺着，像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上，一...
开封第一讲书人阅读 40,510评论 1 216
• 那天，我揣着相机与录音，去河边找鬼。 笑死，一个胖子当着我的面吹牛，可吹牛的内容都是我干的。 我是一名探鬼主播，决...
沈念sama阅读 31,784评论 2 311
• 文/苍兰香墨 我猛地睁开眼，长吁一口气：“原来是场噩梦啊……” “哼！你这毒妇竟也来了？” 一声冷哼从身侧响起，我...
开封第一讲书人阅读 30,476评论 0 196
• 序言：老挝万荣一对情侣失踪，失踪者是张志新（化名）和其女友刘颖，没想到半个月后，有当地人在树林里发现了一具尸体，经...
沈念sama阅读 34,196评论 1 241
• 正文 独居荒郊野岭守林人离奇死亡，尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
茶点故事阅读 30,459评论 2 243
• 正文 我和宋清朗相恋三年，在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
茶点故事阅读 31,978评论 1 258
• 序言：一个原本活蹦乱跳的男人离奇死亡，死状恐怖，灵堂内的尸体忽然破棺而出，到底是诈尸还是另有隐情，我是刑警宁泽，带...
沈念sama阅读 28,321评论 2 252
• 正文 年R本政府宣布，位于F岛的核电站，受9级特大地震影响，放射性物质发生泄漏。R本人自食恶果不足惜，却给世界环境...
茶点故事阅读 32,964评论 3 235
• 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹，春花似锦、人声如沸。这庄子的主人今日做“春日...
开封第一讲书人阅读 26,046评论 0 8
• 文/苍兰香墨 我抬头看了看天上的太阳。三九已至，却和暖如春，着一层夹袄步出监牢的瞬间，已是汗流浃背。 一阵脚步声响...
开封第一讲书人阅读 26,803评论 0 193
• 我被黑心中介骗来泰国打工， 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留，地道东北人。 一个月前我还...
沈念sama阅读 35,530评论 2 271
• 正文 我出身青楼，却偏偏与公主长得像，于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子，可洞房花烛夜当晚...
茶点故事阅读 35,420评论 2 265