Delphi: EInvalidOp в классе нейронных сетей (TD-лямбда)

У меня есть следующий проект для класса нейронной сети. Эту нейронную сеть следует учить с помощью TD-лямбды. Он начинается с вызова функции getRating().

Но, к сожалению, существует ошибка EInvalidOp (недопустимая операция с точкой загрузки) после примерно 1000 итераций в следующих строках:

neuronsHidden[j] := neuronsHidden[j]+neuronsInput[t][i]*weightsInput[i][j]; // input -> hidden

weightsHidden[j][k] := weightsHidden[j][k]+LEARNING_RATE_HIDDEN*tdError[k]*eligibilityTraceOutput[j][k]; // adjust hidden->output weights according to TD-lambda

Почему эта ошибка? Я не могу найти ошибку в своем коде:(Вы можете мне помочь? Заранее большое спасибо!

learningMode: Boolean; // does the network learn and change its weights?
neuronsInput: Array[1..MAX_TIMESTEPS] of Array[1..NEURONS_INPUT] of Extended;
neuronsHidden: Array[1..NEURONS_HIDDEN] of Extended;
neuronsOutput: Array[1..NEURONS_OUTPUT] of Extended;
weightsInput: Array[1..NEURONS_INPUT] of Array[1..NEURONS_HIDDEN] of Extended;
weightsHidden: Array[1..NEURONS_HIDDEN] of Array[1..NEURONS_OUTPUT] of Extended;

[...]

function HyperbolicTangent;
begin
  if x > 5500 then // prevent overflow
    result := 1
  else
    result := (Exp(2*x)-1)/(Exp(2*x)+1);
end;
[...]

3 ответа

Решение

Наиболее вероятная причина вашей ошибки - переполнение аккумуляторов. neuronsHidden или же weightsHidden, Я ничего не знаю о нейронных сетях, поэтому не могу дать никаких объяснений, почему это так.

Как побочный вопрос, я ставлю под сомнение использование Extended переменные с плавающей точкой. Обычно это просто приводит к крайне низкой производительности, намного медленнее, чем при использовании Double, Вы можете подумать, что это дает вам больше возможностей для больших чисел, но на самом деле, если это чрезмерное переполнение той природы, которую я подозреваю, тогда используйте Extended никогда бы тебя не спас.

UPDATE OP указывает, что переполнение приводит к другому классу исключений. Таким образом, для EInvalidOp Я подозреваю какой-то сбой квадратного корня или триггера или что-то в этом роде. Или, возможно, сигнальный NaN, но, поскольку вы, очевидно, не используете неинициализированные данные, я не буду это продолжать.

Теперь я вижу, что на вас повлияло странное решение Embarcadero прервать их реализацию. Tanh, Раньше он отлично работал на старых версиях Delphi (например, D6), но недавно был сломан. Используемая версия не совсем подходит для большого отрицательного ввода и использует два вызова Exp когда достаточно одного Я использую эту версию:

const
  MaxTanhDomain = 5678.22249441322; // Ln(MaxExtended)/2

function Tanh(const X: Extended): Extended;
begin
  if X>MaxTanhDomain then begin
    Result := 1.0
  end else if X<-MaxTanhDomain then begin
    Result := -1.0
  end else begin
    Result := Exp(X);
    Result := Result*Result;
    Result := (Result-1.0)/(Result+1.0);
  end;
end;

Не ответ, просто предложение; Две строки кода, которые вы показываете, включают только умножение и сложение, очень простые операции. Как насчет регистрации значений при сбое, может быть, увидев значения, вы можете что-то выяснить.

Самая досадная проблема с остановкой на исключении состоит в том, что вы не можете проверить переменные, включенные в это исключение. Чтобы обойти это ограничение, я иногда оборачиваюсь хлопотной операцией в try-except заблокировать и поместить точку останова в except обработчик; Delphi сначала остановится на исключении, я нажму на run, а затем остановлюсь на моей точке останова. В точке останова я могу свободно проверять все переменные, используемые в операторе, генерирующем ошибки, поэтому я могу выяснить, что не так.

// In place of:
neuronsHidden[j] := neuronsHidden[j]+neuronsInput[t][i]*weightsInput[i][j];

var saveNerusonsHidden: Double;
try
  saveNeuronsHidden := neuronsHidden[j]; // saved, to be sure I can inspect the original value
  neuronsHidden[j] := neuronsHidden[j]+neuronsInput[t][i]*weightsInput[i][j];
except on E:EInvalidOp do
  begin
    // Breakpoint here, so you can inspect the values of neuronsInput[t][i], wightsInput[i][j] and saveNeuronsHidden
    raise;
  end;
end;

Вы можете получить EInvalidOp при расчете очень больших или очень маленьких чисел.

При получении этой ошибки, может быть, вы можете отладить / просмотреть значения в массиве и сделать частичный расчет в списке наблюдения?

Другие вопросы по тегам