2015-08-19 4 views
2

Я знаю, что этот вопрос был поднят миллион раз, однако я не могу понять, почему этот код бросает ошибку, я отслеживал критический цикл FOR, вызывающий ошибку, однако я не вижу в этом ничего плохого.Delphi - Index Index Of Bounds (4)

Я получаю сообщение об ошибке - «Список 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; 

Ошибка возникает на второй цикл, вот где я установил размер скрытого массива.

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); 

Так, как я это вижу, цикл выполняется только три раза, так почему она пытается индексировать 4-е место? неважно, почему, что более важно, КАК?

Если кто-то может пролить некоторый свет на причины и возможные исправления, я был бы когда-либо благодарен

Ради ЗАВЕРШЕНИЕ, вот полный блок ..

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. 
+0

Гораздо лучше q, чем ваш последний. В вашем втором цикле присвойте Hidden [I] локальной переменной того же типа, а затем в новой строке вызовите CalcOutput в локальном var. Это скажет вам, является ли это ссылкой на массив Hidden [I], которая вызывает исключение или вызов CalcOutput. Из быстрого взгляда, я предполагаю, что ваш «для I: = 0 для Connections.Count» должен сказать «Соединения».Count - 1 ", и это может быть источником вашей проблемы. – MartynA

+0

Если пробойный цикл обращается к 4-му индексу, он выполняет итерацию 5 раз, а не 3 раза. Кроме того, я не вижу возможного пути для цикла * second * сбой с этой ошибкой, но я вижу, как цикл * first * может быть, если вы не будете осторожны, - подумайте о том, что произойдет, если 'Length (InputVals)' больше, чем 'Length (Input)'. Кроме того, 'TNetwork.Create() 'имеет некоторые утечки памяти при заполнении массивов' Input' и 'Hidden'. Вы назначаете 2 объекта' Input [Length (Input) -1] '(утечка одного из них) и 2 объекта на' Hidden [Length (Скрытый) -1] '. И кто несет ответственность за освобождение объектов' TConnection'? –

+0

@MartynA Спасибо, не пробуйте ваше предложение и отчитайтесь как можно скорее –

ответ

3
for I := 0 to Connections.Count do 

You запустите конец списка здесь. Допустимые индексы: 0 - Connections.Count-1 включительно. Вы заходите слишком далеко.

Повторяйте эту ошибку повторно. Разумеется, вам нужно исправить это повсюду.

Индекс списка вне границ ошибки обычно рассматриваются при выполнении вне границ доступа класса коллекции, как TList или TStringList.

С другой стороны, ошибки границ границ непредсказуемы, если вы не включили проверку диапазона. Если вы это сделаете, и вы должны, то вы получите ошибку времени выполнения для таких событий. Вы хотите включить проверку диапазона.

+0

Мой бог ... Я все время искал неправильное место для ошибки LOL ..Спасибо @David, ошибка, связанная с функцией calcOutput, как вы сказали, я также исправил все недопустимые для циклов. –

+0

Странно, что у меня есть проверка диапазона включена, она не поймала ошибку ... –

+0

@ KazutoKirigaya Проверка диапазона не проверяет динамические списки, как в вашем случае. Компилятор понятия не имеет, сколько элементов может быть в этом списке и с помощью оператора 'for', даже не знает, что на нем должно быть' - 1'. Оператор 'for' может быть итерированием массива на основе 1. –

2

Это небольшое дополнение, а не альтернатива, на ответ Дэвида.

Особенно, когда динамические массивы участвуют, делая что-то вроде

for I := 0 to Length(Hidden)-1 do 
    begin 
    Hidden[I].CalcOutput; 
    end; 

является своего рода преждевременной оптимизации, потому что, если исключение происходит на линии

Hidden[I].CalcOutput; 

, он не может быть легко кто-то не полностью справляется с отладчиком Delphi, как его использовать и что сообщение об исключении действительно ссылается (что не всегда очевидно), чтобы определить, возникает ли исключение при индексировании массива Hidden [] или вызова на CalcOutput на свой I-й элемент. Так, по крайней мере, для целей отладки, это может быть полезно, чтобы сделать что-то вроде этого:

var 
    H : THiddenNeuron; 
[...] 
    for I := 0 to Length(Hidden) -1 do 
    begin 
    H := Hidden[I]; 
    H.CalcOutput; 
    end; 

, а затем становится легко различать два возможных места исходный код может быть пойдет не так.

+0

Я понимаю эту концепцию и полностью имею смысл, поэтому я внес изменения в свой код, где бы он ни был применим, каждый день изучая новые вещи .... Спасибо –

+0

Теперь Дэвид эксперт по этой следующей теме, но не сделал бы это он тяжелее распределений кучи (таким образом влияя на производительность)? Или это на самом деле было бы совершенно противоположным? Потому что я могу себе представить, как вызывать 'Hidden [I] .CalcOutput;' для каждой итерации цикла каждый раз может выполнять собственное внутреннее распределение кучи, тогда как объявление одной переменной «H» будет только одной. Какой из них правильный? –

+0

На самом деле я задал новый вопрос по этой теме: http://stackoverflow.com/questions/32107543/which-scenario-is-lighter-heavier-on-heap-allocations –