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 при расчете очень больших или очень маленьких чисел.
При получении этой ошибки, может быть, вы можете отладить / просмотреть значения в массиве и сделать частичный расчет в списке наблюдения?