Проверка Encog XOR результатов в Excel
Я работаю, чтобы проверить основные результаты нейронной сети и до сих пор не смог. Я делаю прямую xor проблему в encog и экспортирую окончательные веса и вычисленный результат.
Для доказательства у меня есть лист Excel, в который я ввожу веса, тогда I1*W1+I2*W2 | I1*W3+I2*W4 до скрытого слоя, затем активация сигмоида для каждого, затем H1*W5+H2*W6, затем снова сигмоид для выхода.
Так что никакого смещения, просто базовые 2x2x1, но выходные значения, которые я получаю, когда я подключаю весы, не близки к ожидаемым выходным значениям, которые я получаю с помощью encog.
У меня есть 8 выходных наборов из Encog для тестирования, но пока я не прихожу к тем же выводам. Любая помощь будет оценена.
Ниже приведен пример вывода, если это поможет. Спасибо Израиль
Веса на выходе
61.11812639080170, -70.09419692460420, 2.58264325902522, 2.59015713019213, 1.16050691499417, 1.16295830927117
Выходные значения
0,01111771776254, 0,96929877340644, 0,96926035361899, 0,04443376315742
В Excel вот что я использую для сигмоидальной функции: =1/(1+EXP(-1*(C3))), не знаю, поможет ли больше, так как это просто сложение и умножение вне сигмоида.
Вот Form1.cs:
using Encog.Engine.Network.Activation;
using Encog.ML.Data.Basic;
using Encog.Neural.Networks;
using Encog.Neural.Networks.Layers;
using Encog.Neural.Networks.Training.Propagation.Resilient;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Encog_Visual
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
double[][] XOR_Input =
{
new[] {0.0,0.0},
new[] {1.0,0.0},
new[] {0.0,1.0},
new[] {1.0,1.0}
};
double[][] XOR_Ideal =
{
new[] {0.0},
new[] {1.0},
new[] {1.0},
new[] {0.0}
};
var trainingSet = new BasicMLDataSet(XOR_Input, XOR_Ideal);
BasicNetwork network = CreateNetwork();
var train = new ResilientPropagation(network, trainingSet);
int epoch = 0;
do
{
train.Iteration();
epoch++;
string result0 = String.Format("Iteration No :{0}, Error: {1}", epoch, train.Error);
textBox1.AppendText(result0 + Environment.NewLine);
} while (train.Error > 0.001);
foreach (var item in trainingSet)
{
var output = network.Compute(item.Input);
string result1 = String.Format("Input : {0}, {1} Ideal : {2} Actual : {3}", item.Input[0], item.Input[1], item.Ideal[0], output[0]);
textBox1.AppendText(result1 + Environment.NewLine + network.DumpWeights() + Environment.NewLine);
}
}
private void Form1_Load(object sender, EventArgs e)
{
}
private static BasicNetwork CreateNetwork()
{
var network = new BasicNetwork();
network.AddLayer(new BasicLayer(null, false, 2));
network.AddLayer(new BasicLayer(new ActivationSigmoid(), false, 2));
network.AddLayer(new BasicLayer(new ActivationSigmoid(), false, 1));
network.Structure.FinalizeStructure();
network.Reset();
return network;
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
}
}
2 ответа
Я наткнулся на это, и попал сюда, и я думаю, что есть ответ, который будет подробным:
Да, действительно, требуется время, чтобы выяснить, к какому слою относится каждый вес, учитывая, что слои кажутся пронумерованными по разным критериям в Encog Framework.
Например, если вы напишите некоторый код, подобный следующему, где networkName
это просто то, что вы хотите назвать своим (например: "XOR_one"). Тогда вы можете вызвать эту функцию из вашего main public Form1()
после цикла обучения по сети, добавив одну строку: saveNetwork("XOR_one");
а потом...
public DirectoryInfo dataDirRoot;
public FileInfo dataFileRoot;
public StreamWriter fileWriteSW;
public bool saveNetwork(string networkName)
{
try
{
// File data initialized
if (dataDirRoot == null) dataDirRoot = new DirectoryInfo(Application.StartupPath + "\\Data");
if (!dataDirRoot.Exists) dataDirRoot.Create();
dataFileRoot = new FileInfo(dataDirRoot + "\\" + networkName + ".weights.txt");
fileWriteSW = new StreamWriter(dataFileRoot.FullName, false, Encoding.Default);
// (A) Write down weights from left to right layers, meaning input first and output last.
// ...within each layer, weights are ordered up-down, always, in all three methods.
for (int j = 0; j < network.LayerCount-1; j++)
{
for (int l = 0; l < network.GetLayerNeuronCount(j + 1); l++)
{
for (int k = 0; k < network.GetLayerTotalNeuronCount(j); k++)
{
fileWriteSW.Write(network.GetWeight(j, k, l).ToString("r") + ", ");
}
fileWriteSW.Write("\r\n");
}
}
fileWriteSW.Write("\r\n\r\n");
// (B) Write down weights from left to right layers, output first, input last
double[] auxDouble = new double[network.EncodedArrayLength()];
network.EncodeToArray(auxDouble);
for (int j = 0; j < network.EncodedArrayLength(); j++)
{
fileWriteSW.Write(auxDouble[j] + "\r\n");
}
fileWriteSW.Flush();
fileWriteSW.Close();
// (C) Write down network structure
// ...you will find that "weights" in the same order as with "DumpWeights()"
dataFileRoot = new FileInfo(dataDirRoot + networkName + ".encog.txt");
Encog.Persist.EncogDirectoryPersistence.SaveObject(dataFileRoot, network);
}
catch (Exception e)
{
MessageBox.Show("Error: " + e.Message);
return false;
}
return true;
}
Важно: действительно сложно обучить сеть XOR без смещения скрытого слоя, поэтому результаты, которые я показываю, имеют на два веса больше, чем ваш пример. Это может быть достигнуто путем изменения одной строки в вашем коде:
... network.AddLayer(new BasicLayer(null, false, 2));
в network.AddLayer(new BasicLayer(null, true, 2));
... чтобы придать вес скрытому слою. Нейроны в скрытом слое будут иметь три веса каждый. Один поступает с нейронного входа 1, другой - с нейронного входа 2, а третий - с смещающего нейрона (который указан как "третий нейрон" на входном слое, значение которого установлено на 1,0).
Итак, сложность здесь в том, какой слой должен быть назван "Слой 0".
В случае (A), слой 0 является входным слоем, первый слева, и веса сбрасываются из первого скрытого слоя (поскольку у входных данных нет весов), нейрон от 0 до 1, а затем выходной слой, нейрон 0.
Но в случаях (B) и (C) и "DumpWeights()", уровень 0 является первым справа, что означает выходной слой, а веса сбрасываются справа налево и вверх-вниз внутри каждого слоя.
Всегда, внутри каждого слоя, веса сбрасываются по порядку, нейрон от 0 до n, а внутри каждого нейрона - вес, приходящий от верхнего нейрона в слое слева вниз до последнего нейрона, или смещение, если оно существует в левом слое.
Выходные весовые результаты выглядят примерно так:
Case (A)
-3.61545321823196, -2.7522256580709645, 3.509680820551957,
-7.2744584719809806, -6.05682131778526, 7.6850785784618676,
-35.025902985103983, 31.763309640942925,
Case (B)
-35.025902985104
31.7633096409429
-3.61545321823196
-2.75222565807096
3.50968082055196
-7.27445847198098
-6.05682131778526
7.68507857846187
Давайте посмотрим тогда:
**Output Layer** (being it called 0 or N... you decide, I prefer N)
**Neuron 0** (the only one there)
weight 2,0,0 = -35.025902985104 (where 2 is layer, 0 is first neuron in hidden layer and 0 is output neuron)
weight 2,1,0 = 31.7633096409429
**Hidden Layer** (I choose 1)
**Neuron 0** (first one)
weight 1,0,0 = -3.61545321823196 (where 1 is layer, 0 is first neuron in input layer and 0 is this neuron)
weight 1,1,0 = -2.75222565807096
weight 1,2,0 = 3.50968082055196
**Neuron 1** (last one)
weight 1,0,1 = -7.27445847198098
weight 1,1,1 = -6.05682131778526
weight 1,2,1 = 7.68507857846187 (where 1 is layer, 2 is bias in input layer and 1 is this neuron)
Обратите внимание: ваш пример в вопросе был результатом DumpWeights()
:
61.11812639080170, -70.09419692460420, 2.58264325902522, 2.59015713019213, 1.16050691499417, 1.16295830927117.
Это соответствует Случаю (B), только запятая. Первые два числа принадлежат выходному нейрону, а последние принадлежат третьему и четвертому первому нейрону, скрытому слою и пятому и шестому второму нейрону, скрытому слою.
Я включил сюда CSV для примера Excel, используя ваши данные:
,,=+A2,2.58264325902522,,,,,,,
0,,=+A4,2.59015713019213,=C1*D1+C2*D2,=1/(1+EXP(-1*(E2))),,,,,
,,=+A2,1.16050691499417,,,,=+F2,61.1181263908017,,
0,,=+A4,1.16295830927117,=C3*D3+C4*D4,=1/(1+EXP(-1*(E4))),,=+F4,-70.0941969246042,=H3*I3+H4*I4,=1/(1+EXP(-1*(J4)))
,,,,,,,,,,
,,=+A7,2.58264325902522,,,,,,,
1,,=+A9,2.59015713019213,=C6*D6+C7*D7,=1/(1+EXP(-1*(E7))),,,,,
,,=+A7,1.16050691499417,,,,=+F7,61.1181263908017,,
0,,=+A9,1.16295830927117,=C8*D8+C9*D9,=1/(1+EXP(-1*(E9))),,=+F9,-70.0941969246042,=H8*I8+H9*I9,=1/(1+EXP(-1*(J9)))
,,,,,,,,,,
,,=+A12,2.58264325902522,,,,,,,
0,,=+A14,2.59015713019213,=C11*D11+C12*D12,=1/(1+EXP(-1*(E12))),,,,,
,,=+A12,1.16050691499417,,,,=+F12,61.1181263908017,,
1,,=+A14,1.16295830927117,=C13*D13+C14*D14,=1/(1+EXP(-1*(E14))),,=+F14,-70.0941969246042,=H13*I13+H14*I14,=1/(1+EXP(-1*(J14)))
,,,,,,,,,,
,,=+A17,2.58264325902522,,,,,,,
1,,=+A19,2.59015713019213,=C16*D16+C17*D17,=1/(1+EXP(-1*(E17))),,,,,
,,=+A17,1.16050691499417,,,,=+F17,61.1181263908017,,
1,,=+A19,1.16295830927117,=C18*D18+C19*D19,=1/(1+EXP(-1*(E19))),,=+F19,-70.0941969246042,=H18*I18+H19*I19,=1/(1+EXP(-1*(J19)))
,,,,,,,,,,
DumpWeights() = ,,,,,,,,,,
"61.11812639080170, -70.09419692460420, 2.58264325902522, 2.59015713019213, 1.16050691499417, 1.16295830927117",,,,,,,,,,
Это должно сделать это:)
(для записи я использовал Encog v3.2.0)
На случай, если кто-нибудь наткнется на это в будущем.
Вес, выводимый кодировкой, представляет собой слой N Нейрон:0,1,n, смещение, вплоть до нейрона слоя 0:0,1,n, смещение
Выяснив правильный выходной сигнал для обратной связи с функцией, я смог правильно проверить его в соответствии с заданным выводом.