Reputation: 93
I know this question has been brought up a million times, however, I cant understand why this code is throwing the error, I have tracked down the culprit FOR loop causing the error, however, I don't see anything wrong with it.
I'm getting the error - "List Index Out Of Bounds(4)"
function TNetwork.FeedForward(InputVals : array of Real) : Real;
var
I : Integer;
begin
for I := 0 to Length(InputVals)-1 do
begin
Input[I].Input(InputVals[I]);
end;
for I := 0 to Length(Hidden)-1 do
begin
Hidden[I].CalcOutput;
end;
Output.CalcOutput;
Result := Output.GetOutput;
end;
The error occurs on the second For Loop, here is where I set the size of the hidden array.
constructor TNetwork.Create(Inputs, HiddenTotal : Integer);
var
C : TConnection;
I, J : Integer;
begin
LEARNING_CONSTANT := 0.5;
SetLength(Input,Inputs+1);
SetLength(Hidden,HiddenTotal+1);
So, as I see it, the loop executes only three times, so why is it trying to index the 4th space? never mind why, more importantly, HOW?
If someone could shed some light on the cause, and a possible fix, I would be ever grateful
For completion's sake, here is the complete unit..
unit NeuralNetwork_u;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, Math;
type
TConnection = Class;
TNeuron = class(TObject)
protected
Output : Real;
Connections : TList;
isBias : Boolean;
public
Constructor Create; overload;
Constructor Create(BiasValue : Integer); overload;
procedure CalcOutput;
procedure AddConnection( Con : TConnection );
function GetOutput : Real;
Function F( X : Real ) : Real;
end;
TConnection = class
private
nFrom, nTo : TNeuron;
Weight : Real;
public
constructor Create(a , b : TNeuron) ; overload;
constructor Create(a, b : TNeuron ; W : Real) ; overload;
function GetFrom : TNeuron;
function GetTo : TNeuron;
function GetWeight : Real;
procedure AdjustWeight(DeltaWeight : Real);
end;
type TInputNeuron = class(TNeuron)
public
procedure Input (D : Real);
end;
type THiddenNeuron = class(TNeuron)
private
public
end;
type TOutputNeuron = Class(TNeuron)
private
public
end;
type TNetwork = class(TObject)
private
LEARNING_CONSTANT : Real;
public
Input : array of TInputNeuron;
Hidden : array of THiddenNeuron;
Output : TOutputNeuron;
constructor Create(Inputs,HiddenTotal : Integer);
function FeedForward(InputVals : array of Real) : Real;
function Train(Inputs : array of Real ; Answer : Real) : Real;
function TrainOnFile(Epochs : Integer ; TrainingFile : String) : Real;
end;
implementation
constructor TNeuron.Create;
begin
Output := 0;
Connections := TList.Create;
isBias := False;
end;
Constructor TNeuron.Create(BiasValue : Integer);
begin
Output := BiasValue;
Connections := TList.Create;
isBias := True;
end;
procedure TNeuron.CalcOutput;
var
Sum : Real;
Bias : Real;
C : TConnection ;
NeuronFrom, NeuronTo : TNeuron;
I : Integer;
begin
if isBias then
else
begin
Sum := 0;
Bias := 0;
for I := 0 to Connections.Count do
begin
C := Connections[I];
NeuronFrom := C.GetFrom;
NeuronTo := C.GetTo;
if NeuronTo = self then
begin
if NeuronFrom.isBias then
begin
Bias := NeuronFrom.GetOutput * C.GetWeight;
end
else
begin
Sum := Sum + NeuronFrom.GetOutput * C.GetWeight;
end;
end;
end;
Output := F(Bias + Sum);
end;
end;
procedure TNeuron.AddConnection(Con : TConnection);
begin
Connections.Add(Con) ;
end;
function TNeuron.GetOutput : Real;
begin
Result := Output;
end;
function TNeuron.F( X : Real ) : Real;
begin
Result := 1.0 /(1.0 + Exp(-X));
end;
procedure TInputNeuron.Input ( D : Real);
begin
Output := D;
end;
constructor TConnection.Create(a, b : TNeuron);
begin
nFrom := a;
nTo := b;
Weight := Random * 2 - 1;
end;
constructor TConnection.Create(a, b : TNeuron ; w : Real);
begin
nFrom := a;
nTo := b;
Weight := w;
end;
function TConnection.GetFrom : TNeuron;
begin
Result := nFrom;
end;
function TConnection.GetTo : TNeuron;
begin
Result := nTo;
end;
function TConnection.GetWeight;
begin
Result := Weight;
end;
procedure Tconnection.AdjustWeight(DeltaWeight : Real);
begin
Weight := Weight + DeltaWeight;
end;
constructor TNetwork.Create(Inputs, HiddenTotal : Integer);
var
C : TConnection;
I, J : Integer;
begin
LEARNING_CONSTANT := 0.5;
SetLength(Input,Inputs+1);
SetLength(Hidden,HiddenTotal+1);
for I := 0 to Length(Input)-1 do
begin
Input[I] := TInputNeuron.Create;
end;
for I := 0 to Length(Hidden)-1 do
begin
Hidden[I] := THiddenNeuron.Create;
end;
Input[Length(Input)-1] := TInputNeuron.Create(1);
Hidden[Length(Hidden)-1] := THiddenNeuron.Create(1);
Output := TOutputNeuron.Create;
for I := 0 to Length(Input)-1 do
begin
for J := 0 to Length(Hidden)-1 do
begin
C := TConnection.Create(Input[I],Hidden[J]);
Input[I].AddConnection(C);
Hidden[J].AddConnection(C);
end;
end;
for I := 0 to Length(Hidden)-1 do
begin
C := TConnection.Create(Hidden[I],Output);
Hidden[I].AddConnection(C);
Output.AddConnection(C);
end;
end;
function TNetwork.FeedForward(InputVals : array of Real) : Real;
var
I : Integer;
begin
for I := 0 to Length(InputVals)-1 do
begin
Input[I].Input(InputVals[I]);
end;
for I := 0 to Length(Hidden)-1 do
begin
Hidden[I].CalcOutput;
end;
Output.CalcOutput;
Result := Output.GetOutput;
end;
function TNetwork.Train(Inputs : array of Real ; Answer : Real) : Real;
var
rResult : Real;
deltaOutput, rOutput, deltaWeight, Sum, deltaHidden : Real;
Connections : TList;
C : TConnection;
Neuron : TNeuron;
I, J : Integer;
begin
rResult := FeedForward(Inputs);
deltaOutput := rResult * (1 - rResult) * (Answer - rResult);
Connections := Output.Connections;
for I := 0 to Connections.Count do
begin
C := Connections[I];
Neuron := C.GetFrom;
rOutput := Neuron.Output;
deltaWeight := rOutput * deltaOutput;
C.AdjustWeight(LEARNING_CONSTANT * deltaWeight);
end;
for I := 0 to Length(Hidden) do
begin
Connections := Hidden[I].Connections;
Sum := 0;
for J := 0 to Connections.Count do
begin
C := Connections[J];
if c.GetFrom = Hidden[I] then
begin
Sum := Sum + (C.GetWeight * deltaOutput);
end;
end;
for J := 0 to Connections.Count do
begin
C := Connections[I];
if C.GetTo = Hidden[I] then
begin
rOutput := Hidden[I].GetOutput;
deltaHidden := rOutput * ( 1 - rOutput);
deltaHidden := deltaHidden * Sum;
Neuron := C.GetFrom;
deltaWeight := Neuron.GetOutput * deltaHidden;
C.AdjustWeight(LEARNING_CONSTANT * deltaWeight);
end;
end;
end;
Result := rResult;
end;
function TNetwork.TrainOnFile(Epochs : Integer ; TrainingFile : string) : Real;
var
FileT : TStringList;
Inputss : array of Real;
Outputss : Real;
I, C : Integer;
sTemp : String;
NumInputs, NumOutputs : Integer;
begin
// Load File
FileT := TStringList.Create;
try
FileT.LoadFromFile(TrainingFile);
except
raise Exception.Create('Training File Does Not Exist');
end;
for I := 0 to FileT.Count-1 do
begin
sTemp := FileT[I];
if I = 0 then
begin
// get Configurators
Delete(sTemp,1,Pos(' ',stemp)); // no Longer need training Set count
NumInputs := StrToInt(Copy(sTemp,1,Pos(' ',sTemp)-1));
Delete(sTemp,1,Pos(' ',sTemp));
NumOutputs := StrToInt(Copy(sTemp,1,Length(sTemp)));
SetLength(Inputss,NumInputs+1);
end
else
begin
for C := 0 to NumInputs-1 do
begin
Inputss[C] := StrToFloat(Copy(sTemp,1,Pos(' ',sTemp)-1));
Delete(sTemp,1,Pos(' ',sTemp));
end;
Outputss := StrToFloat(Copy(sTemp,1,Length(sTemp)));
Train(Inputss,Outputss);
end;
end;
end;
end.
Upvotes: 3
Views: 17115
Reputation: 30735
This is a minor supplement, not alternative, to @David's answer.
Especially when dynamic arrays are involved, doing something like
for I := 0 to Length(Hidden)-1 do
begin
Hidden[I].CalcOutput;
end;
is a kind of premature optimisation, because if an exception occurs on the
Hidden[I].CalcOutput;
line, it may not be easy for someone not fully au fait with Delphi's debugger, how to use it and what the exception message is actually referring to (which isn't always obvious) to tell whether the exception is arising on the indexing of the Hidden[] array, or the call to CalcOutput on its Ith item. So, at least for debugging purposes, it can be useful to do something like this:
var
H : THiddenNeuron;
[...]
for I := 0 to Length(Hidden) -1 do
begin
H := Hidden[I];
H.CalcOutput;
end;
and then it becomes easy to distinguish the two possible places the original code might be going wrong.
Upvotes: 3
Reputation: 613481
for I := 0 to Connections.Count do
You run off the end of the list here. Valid indices are 0
to Connections.Count-1
inclusive. You go one too far.
You make this mistake repeatedly. You need to fix it everywhere, of course.
The list index out of bounds error is generally seen when you perform an out of bounds access of a collection class like TList
or TStringList
.
On the other hand, array bounds errors are unpredictable unless you have enabled range checking. If you do that, and you should, then you get a runtime error for such events. You'll want to enable range checking.
Upvotes: 4