Delphi - 列表索引越界(4)

Delphi - List Index Out Of Bounds(4)

我知道这个问题已经被提出了一百万次,但是,我不明白为什么这段代码会抛出错误,我已经找到了导致错误的 FOR 循环的罪魁祸首,但是,我没有看到任何东西错了。

我遇到错误 - "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;

错误发生在第二个For循环,这里是我设置隐藏数组大小的地方。

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 次 space?别管为什么,更重要的是,怎么做?

如果有人能阐明原因并提供可能的修复方法,我将不胜感激

为了完整起见,这里是完整的单元..

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.
for I := 0 to Connections.Count do

你运行在这里结束列表。有效索引为 0Connections.Count-1(含)。你走得太远了。

你反复犯这个错误。当然,您需要在任何地方修复它。

列表索引越界错误通常出现在对集合class执行越界访问时,如TListTStringList

另一方面,除非启用范围检查,否则数组边界错误是不可预测的。如果你这样做,而且你应该这样做,那么你会收到此类事件的 运行 时间错误。您需要启用范围检查。

这是对@David 的回答的次要补充,而不是替代。

尤其是当涉及到动态数组时,做一些类似

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

是一种过早的优化,因为如果在

上出现异常
    Hidden[I].CalcOutput;

行,对于不完全熟悉 Delphi 的调试器、如何使用它以及异常消息实际指的是什么(并不总是很明显)的人来说可能并不容易判断异常是在索引 Hidden[] 数组时出现,还是在其第 I 项上调用 CalcOutput 时出现。因此,至少出于调试目的,这样做可能很有用:

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

然后很容易区分原始代码可能出错的两个地方。