[译]重塑神经网络
By robot-v1.0
本文链接 https://www.kyfws.com/ai/reinventing-neural-networks-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 13 分钟阅读 - 6213 个词 阅读量 0重塑神经网络(译文)
原文地址:https://www.codeproject.com/Articles/1220276/ReInventing-Neural-Networks
原文作者:Byte-Master-101
译文由本站 robot-v1.0 翻译
前言
Neural Networks can do a lot of amazing things, and you can understand how you can make one from the ground up. You can actually be surprised how easy it is to develop one from scratch!
神经网络可以做很多令人惊奇的事情,您可以理解如何从头开始制作一个.您实际上可能会感到惊讶,从头开始开发一个是如此容易!
全系列:(The Full Series:)
- 第1部分(Part 1) :我们创造整体(: We create the whole)
NeuralNetwork
从头开始上课.(class from scratch.) - 第2部分(Part 2) :我们在Unity中创建一个环境,以便测试该环境中的神经网络.(: We create an envirnment in Unity in order to test the neural network within that environment.)
- 第三部分(Part 3) :通过在代码中添加一种新型的变异,我们对已经创建的神经网络进行了重大改进.(: We make a great improvement to the neural network already created by adding a new type of mutation to the code.)
介绍(Introduction)
在您开始之前,我想让您知道我要切换到YouTube:(Before you start, I want you to know that I’m switching to YouTube:)
在过去的二十年中,机器学习已成为信息技术的主流之一,因此,机器学习已成为我们生活中相当重要的一部分,尽管通常是隐藏的.随着可用数据量的不断增长,有充分的理由相信,智能数据分析将作为技术进步的必要组成部分变得更加普及.最近,不仅神经网络已经接管了"机器学习"工作,而且我还注意到缺乏教程来解释如何从头开始实现神经网络,所以我认为我应该做一个!(Over the past two decades, Machine Learning has become one of the mainstays of information technology and with that, a rather central, albeit usually hidden, part of our life. With the ever increasing amounts of data becoming available, there is good reason to believe that smart data analysis will become even more pervasive as a necessary ingredient for technological progress. Recently, not only Neural Networks have been taking over the “Machine Learning” gig, but I also noticed there was a lack of tutorials that explain how you can implement a Neural Network from scratch, so I thought I should make one!)
背景(Background)
在本文中,您将了解神经网络的核心基础知识,如何在纯C#中实现一个并使用遗传突变对其进行训练.在阅读本文之前,您需要了解C#编程知识和面向对象编程的基础知识.请注意,神经网络将通过无监督学习/变异来学习.本文将不会介绍监督学习/反向流行.但是,这是我的首要任务之一.(In this article, you’re going to understand the core fundamentals of Neural Networks, how you can implement one in pure C# and train it using genetic mutation. You need to know basing C# programming knowledge and basic knowledge of Object Oriented Programming before going through this article. Note that the Neural Network is going to learn through unsupervised learning/mutation. Supervised Learning/Backpopagation is not going to be introduced in this article. This is, however, one of my top priorities.)
了解神经网络(Understanding Neural Networks)
如果您不知道神经网络如何工作,我建议您注意(If you have no idea how neural networks work, I suggest you watch) 这个视频(this video) 由制成(made by) 3蓝色1棕色(3Blue1Brown) :(:)
我没有制作该视频,并且我认为我将永远无法以更直观的方式来解释神经网络.请注意,在视频中,引入了Sigmoid激活功能,但我将改用"泄漏ReLU",因为它训练速度更快,并且不会引入ReLU所遇到的相同问题(死神经元).此外,视频中解释的偏差将始终表示为一个值为1的神经元.(I did not make that video, and I don’t think I’ll ever be able to explain Neural Networks in a more intuitive way. Note that, in the video, the Sigmoid activation function was introduced, but I’m going to use “Leaky ReLU” instead because it is faster to train and does not introduce the same issues the ReLU has (Dead Neurons). Furthermore, the bias explained in the video is going to be represented as a neuron that has a value of 1 all the time.)
您还可以观看本文开头早些时候发布的视频.我做了那个,它使用Leaky ReLU作为激活功能.(You can also watch the video posted earlier at the beginning of the article. I made that one, and It’s using Leaky ReLU as an activation function.)
使用代码(Using the Code)
假设我们要创建一个像这样的神经网络:(Let’s assume we want to make a neural network like this one:)
将网络分成多个部分是一个很好的做法.像这样:(It would be a good practice to split the network into a group of sections. Like this:)
这样,神经网络就是一系列神经部分.在里面(This way, a neural network is just an array of NeuralSections. In the)**神经网络(NeuralNetwork.cs)**脚本,我们应该首先导入所需的名称空间:(script, we should first import the needed namespaces:)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
然后,我们声明(Then, let’s declare the) NeuralNetwork
类:(class:)
public class NeuralNetwork
{
...
}
的(The) NeuralNetwork
课程应以以下内容开头:(class should start with:)
public UInt32[] Topology // Returns the topology in the form of an array
{
get
{
UInt32[] Result = new UInt32[TheTopology.Count];
TheTopology.CopyTo(Result, 0);
return Result;
}
}
ReadOnlyCollection<UInt32> TheTopology; // Contains the topology of the NeuralNetwork
NeuralSection[] Sections; // Contains the all the sections of the NeuralNetwork
Random TheRandomizer; // It is the Random instance used to mutate the NeuralNetwork
然后,构造函数应确保输入有效,并进行初始化(Then, the constructor should ensure that the inputs are valid, initialize) TheRandomizer
,设置网络的拓扑,初始化网络的所有部分并构造以下每个部分:(, set the topology of the network, initialize all the sections of the network and construct each of these sections:)
/// <summary>
/// Initiates a NeuralNetwork from a Topology and a Seed.
/// </summary>
/// <param name="Topology">The Topology of the Neural Network.</param>
/// <param name="Seed">The Seed of the Neural Network.
/// Set to 'null' to use a Timed Seed.</param>
public NeuralNetwork (UInt32[] Topology, Int32? Seed = 0)
{
// Validation Checks
if (Topology.Length < 2)
throw new ArgumentException("A Neural Network cannot contain less than 2 Layers.",
"Topology");
for (int i = 0; i < Topology.Length; i++)
{
if(Topology[i] < 1)
throw new ArgumentException("A single layer of neurons must contain,
at least, one neuron.", "Topology");
}
// Initialize Randomizer
if (Seed.HasValue)
TheRandomizer = new Random(Seed.Value);
else
TheRandomizer = new Random();
// Set Topology
TheTopology = new List<uint>(Topology).AsReadOnly();
// Initialize Sections
Sections = new NeuralSection[TheTopology.Count - 1];
// Set the Sections
for (int i = 0; i < Sections.Length; i++)
{
Sections[i] = new NeuralSection(TheTopology[i], TheTopology[i + 1], TheRandomizer);
}
}
另一个可以克隆的构造函数(Another constructor that can clone) NeuralNetwork
为了使后代训练成为可能,应该存在:(s should be present in order to make training of offsprings possible:)
/// <summary>
/// Initiates an independent Deep-Copy of the Neural Network provided.
/// </summary>
/// <param name="Main">The Neural Network that should be cloned.</param>
public NeuralNetwork (NeuralNetwork Main)
{
// Initialize Randomizer
TheRandomizer = new Random(Main.TheRandomizer.Next());
// Set Topology
TheTopology = Main.TheTopology;
// Initialize Sections
Sections = new NeuralSection[TheTopology.Count - 1];
// Set the Sections
for (int i = 0; i < Sections.Length; i++)
{
Sections[i] = new NeuralSection (Main.Sections[i]);
}
}
然后,有(Then, there is the) FeedForward
功能.它接受输入数组,确保其有效,将其传递给所有节,然后返回最后一节的输出:(function. It takes the input array, makes sure it is valid, passes it through all the sections and returns the output of the final section:)
/// <summary>
/// Feed Input through the NeuralNetwork and Get the Output.
/// </summary>
/// <param name="Input">The values to set the Input Neurons.</param>
/// <returns>The values in the output neurons after propagation.</returns>
public double[] FeedForward(double[] Input)
{
// Validation Checks
if (Input == null)
throw new ArgumentException("The input array cannot be set to null.", "Input");
else if (Input.Length != TheTopology[0])
throw new ArgumentException
("The input array's length does not match the number of neurons
in the input layer.", "Input");
double[] Output = Input;
// Feed values through all sections
for (int i = 0; i < Sections.Length; i++)
{
Output = Sections[i].FeedForward(Output);
}
return Output;
}
现在,我们需要使开发人员能够更改(Now, we need to give the developer the ability to mutate the) NeuralNetwork
s.的(s. The) Mutate
函数只是独立地改变每个部分:(function just mutates each section independently:)
/// <summary>
/// Mutate the NeuralNetwork.
/// </summary>
/// <param name="MutationProbablity">The probability that a weight is going to be mutated.
/// (Ranges 0-1)</param>
/// <param name="MutationAmount">
/// The maximum amount a mutated weight would change.</param>
public void Mutate (double MutationProbablity = 0.3, double MutationAmount = 2.0)
{
// Mutate each section
for (int i = 0; i < Sections.Length; i++)
{
Sections[i].Mutate(MutationProbablity, MutationAmount);
}
}
这就是(This is how the) NeuralNetwork
该类现在应该看起来像:(class should look like right now:)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
public class NeuralNetwork
{
public UInt32[] Topology // Returns the topology in the form of an array
{
get
{
UInt32[] Result = new UInt32[TheTopology.Count];
TheTopology.CopyTo(Result, 0);
return Result;
}
}
ReadOnlyCollection<UInt32> TheTopology; // Contains the topology of the NeuralNetwork
NeuralSection[] Sections; // Contains the all the sections of the NeuralNetwork
Random TheRandomizer; // It is the Random instance used to mutate the NeuralNetwork
/// <summary>
/// Initiates a NeuralNetwork from a Topology and a Seed.
/// </summary>
/// <param name="Topology">The Topology of the Neural Network.</param>
/// <param name="Seed">The Seed of the Neural Network.
/// Set to 'null' to use a Timed Seed.</param>
public NeuralNetwork (UInt32[] Topology, Int32? Seed = 0)
{
// Validation Checks
if (Topology.Length < 2)
throw new ArgumentException("A Neural Network cannot contain less than 2 Layers.",
"Topology");
for (int i = 0; i < Topology.Length; i++)
{
if(Topology[i] < 1)
throw new ArgumentException
("A single layer of neurons must contain, at least, one neuron.", "Topology");
}
// Initialize Randomizer
if (Seed.HasValue)
TheRandomizer = new Random(Seed.Value);
else
TheRandomizer = new Random();
// Set Topology
TheTopology = new List<uint>(Topology).AsReadOnly();
// Initialize Sections
Sections = new NeuralSection[TheTopology.Count - 1];
// Set the Sections
for (int i = 0; i < Sections.Length; i++)
{
Sections[i] = new NeuralSection(TheTopology[i], TheTopology[i + 1], TheRandomizer);
}
}
/// <summary>
/// Initiates an independent Deep-Copy of the Neural Network provided.
/// </summary>
/// <param name="Main">The Neural Network that should be cloned.</param>
public NeuralNetwork (NeuralNetwork Main)
{
// Initialize Randomizer
TheRandomizer = new Random(Main.TheRandomizer.Next());
// Set Topology
TheTopology = Main.TheTopology;
// Initialize Sections
Sections = new NeuralSection[TheTopology.Count - 1];
// Set the Sections
for (int i = 0; i < Sections.Length; i++)
{
Sections[i] = new NeuralSection (Main.Sections[i]);
}
}
/// <summary>
/// Feed Input through the NeuralNetwork and Get the Output.
/// </summary>
/// <param name="Input">The values to set the Input Neurons.</param>
/// <returns>The values in the output neurons after propagation.</returns>
public double[] FeedForward(double[] Input)
{
// Validation Checks
if (Input == null)
throw new ArgumentException("The input array cannot be set to null.", "Input");
else if (Input.Length != TheTopology[0])
throw new ArgumentException
("The input array's length does not match the number of neurons in the input layer.",
"Input");
double[] Output = Input;
// Feed values through all sections
for (int i = 0; i < Sections.Length; i++)
{
Output = Sections[i].FeedForward(Output);
}
return Output;
}
/// <summary>
/// Mutate the NeuralNetwork.
/// </summary>
/// <param name="MutationProbablity">
/// The probability that a weight is going to be mutated. (Ranges 0-1)</param>
/// <param name="MutationAmount">The maximum amount a mutated weight would change.
/// </param>
public void Mutate (double MutationProbablity = 0.3, double MutationAmount = 2.0)
{
// Mutate each section
for (int i = 0; i < Sections.Length; i++)
{
Sections[i].Mutate(MutationProbablity, MutationAmount);
}
}
}
现在,我们已经实施了(Now that we have implemented the) NeuralNetwork
课,是时候实施(class, it’s time to implement the) NeuralSection
类:(class:)
public class NeuralNetwork
{
...
private class NeuralSection
{
...
}
...
}
每(Each) NeuralSection
应该包含这些全局变量:(should contain those global variables:)
private double[][] Weights; // Contains all the weights of the section where [i][j]
// represents the weight from neuron i in the input layer
// and neuron j in the output layer
private Random TheRandomizer; // Contains a reference to the
// Random instance of the NeuralNetwork
的(The) NeuralSection
类还应包含2个构造函数:(class should also contain 2 constructors:)
/// <summary>
/// Initiate a NeuralSection from a topology and a seed.
/// </summary>
/// <param name="InputCount">The number of input neurons in the section.</param>
/// <param name="OutputCount">The number of output neurons in the section.</param>
/// <param name="Randomizer">The Ransom instance of the NeuralNetwork.</param>
public NeuralSection(UInt32 InputCount, UInt32 OutputCount, Random Randomizer)
{
// Validation Checks
if (InputCount == 0)
throw new ArgumentException
("You cannot create a Neural Layer with no input neurons.", "InputCount");
else if (OutputCount == 0)
throw new ArgumentException
("You cannot create a Neural Layer with no output neurons.", "OutputCount");
else if (Randomizer == null)
throw new ArgumentException("The randomizer cannot be set to null.", "Randomizer");
// Set Randomizer
TheRandomizer = Randomizer;
// Initialize the Weights array
Weights = new double[InputCount + 1][]; // +1 for the Bias Neuron
for (int i = 0; i < Weights.Length; i++)
Weights[i] = new double[OutputCount];
// Set random weights
for (int i = 0; i < Weights.Length; i++)
for (int j = 0; j < Weights[i].Length; j++)
Weights[i][j] = TheRandomizer.NextDouble() - 0.5f;
}
/// <summary>
/// Initiates an independent Deep-Copy of the NeuralSection provided.
/// </summary>
/// <param name="Main">The NeuralSection that should be cloned.</param>
public NeuralSection(NeuralSection Main)
{
// Set Randomizer
TheRandomizer = Main.TheRandomizer;
// Initialize Weights
Weights = new double[Main.Weights.Length][];
for (int i = 0; i < Weights.Length; i++)
Weights[i] = new double[Main.Weights[0].Length];
// Set Weights
for (int i = 0; i < Weights.Length; i++)
{
for (int j = 0; j < Weights[i].Length; j++)
{
Weights[i][j] = Main.Weights[i][j];
}
}
}
现在来了(Now comes the) FeedForward
完成所有传播魔术的功能:(function that does all the propagation magic:)
/// <summary>
/// Feed input through the NeuralSection and get the output.
/// </summary>
/// <param name="Input">The values to set the input neurons.</param>
/// <returns>The values in the output neurons after propagation.</returns>
public double[] FeedForward(double[] Input)
{
// Validation Checks
if (Input == null)
throw new ArgumentException("The input array cannot be set to null.", "Input");
else if (Input.Length != Weights.Length - 1)
throw new ArgumentException("The input array's length
does not match the number of neurons in the input layer.", "Input");
// Initialize Output Array
double[] Output = new double[Weights[0].Length];
// Calculate Value
for (int i = 0; i < Weights.Length; i++)
{
for (int j = 0; j < Weights[i].Length; j++)
{
if (i == Weights.Length - 1) // If is Bias Neuron
Output[j] += Weights[i][j]; // Then, the value of the neuron is equal to one
else
Output[j] += Weights[i][j] * Input[i];
}
}
// Apply Activation Function
for (int i = 0; i < Output.Length; i++)
Output[i] = ReLU(Output[i]);
// Return Output
return Output;
}
正如我们在(As we have done in the) NeuralNetwork
上课,应该有一个(class, there should be a) Mutate
在功能(function in the) NeuralSection
上课:(class too:)
/// <summary>
/// Mutate the NeuralSection.
/// </summary>
/// <param name="MutationProbablity">The probability that
/// a weight is going to be mutated. (Ranges 0-1)</param>
/// <param name="MutationAmount">The maximum amount a Mutated Weight would change.
/// </param>
public void Mutate (double MutationProbablity, double MutationAmount)
{
for (int i = 0; i < Weights.Length; i++)
{
for (int j = 0; j < Weights[i].Length; j++)
{
if (TheRandomizer.NextDouble() < MutationProbablity)
Weights[i][j] = TheRandomizer.NextDouble() *
(MutationAmount * 2) - MutationAmount;
}
}
}
最后,我们需要将ReLU激活功能添加到(Finally, we need to add our ReLU activation function to the) NeuralSection
类:(class:)
/// <summary>
/// Puts a double through the activation function ReLU.
/// </summary>
/// <param name="x">The value to put through the function.</param>
/// <returns>x after it is put through ReLU.</returns>
private double ReLU(double x)
{
if (x >= 0)
return x;
else
return x / 20;
}
这样,脚本应该最终看起来像这样:(This way, the script should end up looking like this:)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
public class NeuralNetwork
{
public UInt32[] Topology // Returns the topology in the form of an array
{
get
{
UInt32[] Result = new UInt32[TheTopology.Count];
TheTopology.CopyTo(Result, 0);
return Result;
}
}
ReadOnlyCollection<UInt32> TheTopology; // Contains the topology of the NeuralNetwork
NeuralSection[] Sections; // Contains the all the sections of the NeuralNetwork
Random TheRandomizer; // It is the Random instance used to mutate the NeuralNetwork
private class NeuralSection
{
private double[][] Weights; // Contains all the weights of the section
// where [i][j] represents the weight from neuron i in the
// input layer and neuron j in the output layer
private Random TheRandomizer; // Contains a reference to the Random instance
// of the NeuralNetwork
/// <summary>
/// Initiate a NeuralSection from a topology and a seed.
/// </summary>
/// <param name="InputCount">The number of input neurons in the section.</param>
/// <param name="OutputCount">The number of output neurons in the section.</param>
/// <param name="Randomizer">The Ransom instance of the NeuralNetwork.</param>
public NeuralSection(UInt32 InputCount, UInt32 OutputCount, Random Randomizer)
{
// Validation Checks
if (InputCount == 0)
throw new ArgumentException
("You cannot create a Neural Layer with no input neurons.", "InputCount");
else if (OutputCount == 0)
throw new ArgumentException
("You cannot create a Neural Layer with no output neurons.", "OutputCount");
else if (Randomizer == null)
throw new ArgumentException("The randomizer cannot be set to null.", "Randomizer");
// Set Randomizer
TheRandomizer = Randomizer;
// Initialize the Weights array
Weights = new double[InputCount + 1][]; // +1 for the Bias Neuron
for (int i = 0; i < Weights.Length; i++)
Weights[i] = new double[OutputCount];
// Set random weights
for (int i = 0; i < Weights.Length; i++)
for (int j = 0; j < Weights[i].Length; j++)
Weights[i][j] = TheRandomizer.NextDouble() - 0.5f;
}
/// <summary>
/// Initiates an independent Deep-Copy of the NeuralSection provided.
/// </summary>
/// <param name="Main">The NeuralSection that should be cloned.</param>
public NeuralSection(NeuralSection Main)
{
// Set Randomizer
TheRandomizer = Main.TheRandomizer;
// Initialize Weights
Weights = new double[Main.Weights.Length][];
for (int i = 0; i < Weights.Length; i++)
Weights[i] = new double[Main.Weights[0].Length];
// Set Weights
for (int i = 0; i < Weights.Length; i++)
{
for (int j = 0; j < Weights[i].Length; j++)
{
Weights[i][j] = Main.Weights[i][j];
}
}
}
/// <summary>
/// Feed input through the NeuralSection and get the output.
/// </summary>
/// <param name="Input">The values to set the input neurons.</param>
/// <returns>The values in the output neurons after propagation.</returns>
public double[] FeedForward(double[] Input)
{
// Validation Checks
if (Input == null)
throw new ArgumentException("The input array cannot be set to null.", "Input");
else if (Input.Length != Weights.Length - 1)
throw new ArgumentException("The input array's length
does not match the number of neurons in the input layer.", "Input");
// Initialize Output Array
double[] Output = new double[Weights[0].Length];
// Calculate Value
for (int i = 0; i < Weights.Length; i++)
{
for (int j = 0; j < Weights[i].Length; j++)
{
if (i == Weights.Length - 1) // If is Bias Neuron
Output[j] += Weights[i][j]; // Then, the value of the neuron is equal to one
else
Output[j] += Weights[i][j] * Input[i];
}
}
// Apply Activation Function
for (int i = 0; i < Output.Length; i++)
Output[i] = ReLU(Output[i]);
// Return Output
return Output;
}
/// <summary>
/// Mutate the NeuralSection.
/// </summary>
/// <param name="MutationProbablity">The probability
/// that a weight is going to be mutated. (Ranges 0-1)</param>
/// <param name="MutationAmount">The maximum amount a Mutated Weight would change.
/// </param>
public void Mutate (double MutationProbablity, double MutationAmount)
{
for (int i = 0; i < Weights.Length; i++)
{
for (int j = 0; j < Weights[i].Length; j++)
{
if (TheRandomizer.NextDouble() < MutationProbablity)
Weights[i][j] = TheRandomizer.NextDouble() *
(MutationAmount * 2) - MutationAmount;
}
}
}
/// <summary>
/// Puts a double through the activation function ReLU.
/// </summary>
/// <param name="x">The value to put through the function.</param>
/// <returns>x after it is put through ReLU.</returns>
private double ReLU(double x)
{
if (x >= 0)
return x;
else
return x / 20;
}
}
/// <summary>
/// Initiates a NeuralNetwork from a Topology and a Seed.
/// </summary>
/// <param name="Topology">The Topology of the Neural Network.</param>
/// <param name="Seed">The Seed of the Neural Network.
/// Set to 'null' to use a Timed Seed.</param>
public NeuralNetwork (UInt32[] Topology, Int32? Seed = 0)
{
// Validation Checks
if (Topology.Length < 2)
throw new ArgumentException
("A Neural Network cannot contain less than 2 Layers.", "Topology");
for (int i = 0; i < Topology.Length; i++)
{
if(Topology[i] < 1)
throw new ArgumentException
("A single layer of neurons must contain, at least, one neuron.", "Topology");
}
// Initialize Randomizer
if (Seed.HasValue)
TheRandomizer = new Random(Seed.Value);
else
TheRandomizer = new Random();
// Set Topology
TheTopology = new List<uint>(Topology).AsReadOnly();
// Initialize Sections
Sections = new NeuralSection[TheTopology.Count - 1];
// Set the Sections
for (int i = 0; i < Sections.Length; i++)
{
Sections[i] = new NeuralSection(TheTopology[i], TheTopology[i + 1], TheRandomizer);
}
}
/// <summary>
/// Initiates an independent Deep-Copy of the Neural Network provided.
/// </summary>
/// <param name="Main">The Neural Network that should be cloned.</param>
public NeuralNetwork (NeuralNetwork Main)
{
// Initialize Randomizer
TheRandomizer = new Random(Main.TheRandomizer.Next());
// Set Topology
TheTopology = Main.TheTopology;
// Initialize Sections
Sections = new NeuralSection[TheTopology.Count - 1];
// Set the Sections
for (int i = 0; i < Sections.Length; i++)
{
Sections[i] = new NeuralSection (Main.Sections[i]);
}
}
/// <summary>
/// Feed Input through the NeuralNetwork and Get the Output.
/// </summary>
/// <param name="Input">The values to set the Input Neurons.</param>
/// <returns>The values in the output neurons after propagation.</returns>
public double[] FeedForward(double[] Input)
{
// Validation Checks
if (Input == null)
throw new ArgumentException("The input array cannot be set to null.", "Input");
else if (Input.Length != TheTopology[0])
throw new ArgumentException("The input array's length
does not match the number of neurons in the input layer.", "Input");
double[] Output = Input;
// Feed values through all sections
for (int i = 0; i < Sections.Length; i++)
{
Output = Sections[i].FeedForward(Output);
}
return Output;
}
/// <summary>
/// Mutate the NeuralNetwork.
/// </summary>
/// <param name="MutationProbablity">The probability
/// that a weight is going to be mutated. (Ranges 0-1)</param>
/// <param name="MutationAmount">The maximum amount a mutated weight would change.
/// </param>
public void Mutate (double MutationProbablity = 0.3, double MutationAmount = 2.0)
{
// Mutate each section
for (int i = 0; i < Sections.Length; i++)
{
Sections[i].Mutate(MutationProbablity, MutationAmount);
}
}
}
现在我们已经准备好实现,现在我们必须尝试一些简单的方法. XOR函数将用作概念证明.如果您不知道什么是XOR函数,则应达到以下要求:(Now that we have our implementation ready, we have to try it on something simple. The XOR function will do as a proof of concept. If you don’t know what an XOR function is, here is what you should expect:)
因为一张图片值一千个单词,所以这里是训练过程的工作方式:(Because a picture is worth a thousand words, here is how the training process should work:)
如果将流程图转换为代码,则应该以以下内容结束:(If we turn that flow chart into code, this is what we should end up with:)
using System;
namespace NeuralXOR
{
class Program
{
static void Main(string[] args)
{
int Iteration = 0; // Current Training Iteration
NeuralNetwork BestNetwork = new NeuralNetwork
(new uint[] { 2, 2, 1 }); // The best network currently made
double BestCost = double.MaxValue; // The cost that the best network achieved
double[] BestNetworkResults = new double[4]; // The results that the best network calculated
double[][] Inputs = new double[][] // This represents the possible inputs
// or the training dataset
{
new double[] { 0, 0 },
new double[] { 0, 1 },
new double[] { 1, 0 },
new double[] { 1, 1 }
};
double[] ExpectedOutputs = new double[] { 0, 1, 1, 0 }; // This represents
// the expected outputs from the optimum NeuralNetwork
while (true) // Keep Training forever
{
NeuralNetwork MutatedNetwork = new NeuralNetwork(BestNetwork); // Clone the current
// best network
MutatedNetwork.Mutate(); // Mutate the clone
double MutatedNetworkCost = 0;
double[] CurrentNetworkResults = new double[4]; // The results that the mutated
// network calculated
// Calculate the cost of the mutated network
for (int i = 0; i < Inputs.Length; i++)
{
double[] Result = MutatedNetwork.FeedForward(Inputs[i]);
MutatedNetworkCost += Math.Abs(Result[0] - ExpectedOutputs[i]);
CurrentNetworkResults[i] = Result[0];
}
// Does the mutated network perform better than the last one
if (MutatedNetworkCost < BestCost)
{
BestNetwork = MutatedNetwork;
BestCost = MutatedNetworkCost;
BestNetworkResults = CurrentNetworkResults;
}
// Print only each 20000 iteration in order to speed up the training process
if (Iteration % 20000 == 0)
{
Console.Clear(); // Clear the current console text
for (int i = 0; i < BestNetworkResults.Length; i++) // Print the best truth table
{
Console.WriteLine(Inputs[i][0] + "," +
Inputs[i][1] + " | " + BestNetworkResults[i].ToString("N17"));
}
Console.WriteLine("Cost: " + BestCost); // Print the best cost
Console.WriteLine("Iteration: " + Iteration); // Print the current Iteration
}
// An iteration is done
Iteration++;
}
}
}
}
运行它,您将得到:(Run it, and you’ll get that:)
兴趣点(Points of Interest)
当我发现只有20,000次迭代可以将XOR函数的成本降低到0.03以下时,我感到非常惊讶.我确实希望它花费更多时间,但是只是不需要更多时间.如果有人有任何疑问或想谈论任何事情,您可以在Skype上给我发消息(mokhtar.mohammed.red@gmail.com)或发送电子邮件(mokhtar.mohammed.red@gmail.com).等待未来的文章介绍如何使用此实现使汽车学习如何通过强化学习在Unity中自行驾驶所有汽车.我正在做类似的事情(I was really surprised when I found out that only 20,000 iterations could reduce the cost of the XOR function to under 0.03. I did expect it to take some more time, but it just didn’t need more time. If anybody has any questions or just wants to talk about anything, you can message me on Skype (mokhtar.mohammed.red@gmail.com) or send an email (mokhtar.mohammed.red@gmail.com). Wait for a future article that explains how you can use this implementation to make cars learn how to drive all by themselves in Unity using reinforcement learning. I’m working on something similar to) 这是Samuel Arzt制作的(this one made by Samuel Arzt) :(:)
2017年12月11日更新:(Update on December 11th 2017:)
我完成了第二篇文章,目前正在等待提交.您可以看一下演示视频.它看起来有些令人毛骨悚然,但是…(I finished the second article and it is currently waiting for submission. You can have a look at the demo video. It looks a bit creepy, but… Here you go:)
2017年12月11日的另一个更新:(Another Update on December 11th 2017:)
看起来像(Looks like) 第2部分(Part 2) 提交,伙计们.玩得开心!还有…别想了,我们已经完成了.我当前的目标是实现3个Crossover运算符,以使演化效率更高,并为开发人员提供更多的多样性.之后,以反向传播为目标.(is submitted, guys. Have fun! And… Never think for a bit that we’re done here. My current target is to implement 3 Crossover operators to make evolution a bit more efficient and offer the developer more diversity. After that, Backpropagation is the target.)
2018年2月20日更新:(Update on February 20th 2018:)
第三部分(Part 3) 启动并运行!它显示了对第1部分和第2部分中讨论的系统的实质性改进.请告诉我您的想法!(is up and running! It shows a substantial improvement over the system discussed in Parts 1 and 2. Tell me what you think!)
历史(History)
- 版本1.0:主要实现(2017年12月8日)(Version 1.0: Main implementation (December 8th 2017))
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C# Linux Windows VS2013 新闻 翻译