English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
preview
모집단 최적화 알고리즘: 물고기 떼 검색(FSS)

모집단 최적화 알고리즘: 물고기 떼 검색(FSS)

MetaTrader 5 | 6 11월 2023, 13:46
267 0
Andrey Dik
Andrey Dik

콘텐츠

1. 소개
2. 알고리즘에 대한 설명
3. 테스트 결과


1. 소개

물고기 떼는 특정 지역에 모여 있는 물고기의 무리를 통틀어 일컫는 말입니다. 물고기 떼는 구조화될 수도 있고 구조화되지 않을 수도 있습니다. 구조화되지 않은 떼는 먹이 또는 둥지와 같은 특정 지역 자원 근처에 무작위로 모인 다양한 종과 크기의 집단일 수 있습니다.

또한 집단이 상호 작용하는 사회적 방식으로 모이는 경우 이를 떼 짓기라고 할 수 있습니다. 그들 대부분은 수명주기의 동일한 단계에 있으며 서로 활발하게 접촉하고 있으며 언제든지 그룹 구성원에게 도움이 되는 생물학적 활동과 조직적인 활동을 나타낼 수 있습니다.
개인과 달리 군집적인 생활 방식은 포식자로부터 더 잘 보호받을 수 있다는 집단 생활의 이점과 먹이를 얻기 위한 경쟁이 치열 해진다는 점 사이에서의 타협입니다.

물고기는 자연에서 여러 가지 방식으로 떼를 형성합니다. 일반적으로 이들은 같은 종의 개체들로만 구성된 큰 떼를 선호합니다. 떼의 구성원 중에 외모가 눈에 띄거나 다른 점이 있는 구성원은 포식자의 주요 표적이 됩니다. 이 사실은 물고기들이 왜 같은 개체가 모여 있는 떼를 선호하는지 이유를 설명합니다. 이렇게 하면 떼 전체의 동질성을 확보할 수 있습니다.

물고기들이 같은 속도와 같은 방향으로 동시에 헤엄칠 때 떼는 매우 엄격하게 조직됩니다. 이는 같은 종, 같은 나이, 같은 크기의 물고기가 서로 일정한 거리를 두고 움직이기 때문입니다. 물고기 떼는 마치 집단 지능과 공통의 마음을 가진 것처럼 복잡한 기동을 수행할 수 있습니다.
떼 형성의 미묘한 부분, 특히 이동과 먹이를 주는 방식에 대해서는 아직 완전히 밝혀진 것이 없습니다.

군집적인 행동을 설명하기 위해 방향감각 향상, 사냥 동기화, 포식자 혼란, 포획 위험 감소 등 많은 가설이 제시되었습니다. 떼에 속한 물고기들은 가까운 거리에서 서로의 행동을 제어하는 정보를 공유하는 것 같습니다. 한 물고기가 먹이를 주는 행동을 하는 것이 다른 물고기의 적극적인 먹이 탐색을 빠르게 자극합니다. 떼를 지어 헤엄치는 물고기는 가느다란 지골을 따라 빠르게 오르내리거나 축을 중심으로 회전하면서 서로 충돌을 피하기 위해 떼의 모양을 바꾸기도 합니다. 이러한 기동에는 매우 빠른 반응 시스템이 필요합니다. 떼를 지어 사는 생활 방식은 물고기가 이웃과의 관계에서 작은 위치 변화에 대해 즉시 반응할 수 있는 감각 시스템을 가지고 있음을 의미합니다.

보다 완벽한 그림을 만들기 위해 이러한 동작에 대한 수학적 모델링이 사용됩니다. 가장 일반적인 수학적 모델에서는 떼에 속한 개별적인 동물이 세 가지 기본 규칙을 따른다고 가정합니다:

  1. 이웃과 같은 방향으로 이동하기
  2. 이웃과 가까이 있기
  3. 주변에 있는 이웃과 충돌 피하기


물고기 떼가 헤엄칠 방향을 선택하는 방법에 대한 의문은 아직 해결되지 않았습니다. 이동 중에는 대부분의 멤버들이 어디로 가야 할지 알고 있는 것처럼 보이지만 모든 멤버들이 똑같이 먹이를 구할 수 있다는 것을 알고 있다면 다른 동료들보다 훨씬 대담한 리더가 있습니다. 이렇게 떼를 짓는 방식은 많은 연구자들이 수학적 모델뿐만 아니라 다양한 최적화 문제를 해결하기 위한 알고리즘 모델을 만들도록 했습니다.


2. 알고리즘에 대한 설명

물고기 떼 검색(FSS)은 메타휴리스틱 알고리즘 클래스에 속하는 군집 지능 알고리즘의 하위 군입니다. 2008년 바스토스 필호와 리마 네토가 제안하여 2009년에 처음 출간되었습니다. FSS에서는 단순 에이전트를 물고기라고 하며 각 물고기에는 검색 중에 달성한 '성공'을 나타내는 가중치가 있습니다. 가중치의 값과 변화는 개인 및 집단적인 움직임에 영향을 미칩니다. 내장된 먹이 공급 및 조정된 행동 메커니즘은 떼가 크기를 늘리고 지역 및 전 세계에서 가장 좋은 장소를 찾기 위해 긍정적인 경사 방향으로 이동하도록 합니다. FSS는 멀티모달 검색 공간에서 지속적인 최적화 문제를 해결하기 위해 개발되었습니다. 이를 계기로 다른 연구자들도 바이너리 문제에서의 최적화, 다목적 최적화 등 다른 문제 해결을 위한 옵션을 제안했습니다.

FSS 알고리즘에서는 수족관의 벽이 연구된 함수 정의 영역의 경계라는 조건부 수족관에서 헤엄치고 있는 것으로 단순화됩니다. 물고기의 무게는 먹이(솔루션)를 찾는 데 성공했는지를 나타내는 척도입니다. 또한 물고기의 기억력의 역할도 합니다. 무게의 존재는 알고리즘의 주요 아이디어이며 파티클 스웜과의 차이점입니다. FSS 알고리즘의 이 기능을 사용하면 파티클 스웜의 경우와 마찬가지로 전 세계적으로 가장 좋은 솔루션을 찾아 수정할 필요가 없습니다.

FSS 알고리즘 연산자는 두개의 그룹으로 나뉩니다:
- 먹이 공급 연산자는 지역 탐사의 성공을 공식화합니다.
- 수영 연산자는 개별 물고기와 떼 전체에 대한 마이그레이션 알고리즘을 구현합니다.

먹이 연산자는 물고기의 무게를 계산합니다. 기본적인 아이디어는 물고기가 "먹기"와 "체중 증가"를 위해 양의 경사면을 향해 "헤엄치도록" 하는 것입니다. 전체적으로 무거운 물고기는 전체 검색 프로세스에 더 큰 영향을 미치며 이를 통해 반복에 따라 물고기 떼의 중심이 검색 공간에서 더 나은 위치로 이동하게 됩니다. 주어진 반복에서 가중치의 증가는 피트니스 함수 값의 정규화 된 차이에 비례합니다:

fishes [f].weight = fishes [f].weight + (fishes [f].delta_fitness / max_delta_fit);

설명:

  • weight - 물고기 무게
  • delta_fitness - 피트니스 함수 값 간의 차이
  • max_delta_fitness - 모든 물고기의 체력 함수 차이의 최대 값

플로팅 오퍼레이터는 이동 유형에 따라 세 가지 유형으로 나뉩니다:

- individual;

- instinctive-collective;

- collective-volitional;

개별 수영은 물고기의 현재 위치 근처에서의 로컬 검색으로 해석할 수 있습니다. 각 개별 물고기의 모션 벡터는 무작위로 지정되며 다른 값을 갖습니다.

fishes [f].new_position [d] = fishes [f].current_position [d] + step_ind [d] * r;

설명:

  • new_position - 해당 좌표의 새 위치
  • current_position - 해당 좌표의 현재 위치
  • step_ind - 개별 이동 단계는 다음과 같이 계산됩니다.

initial_step_ind * (rangeMax [d] - rangeMin [d]);

설명:

  • initial_step_ind - 개별 움직임에 대한 알고리즘 매개변수
  • rangeMax 및 rangeMin - 최적화된 인자 값 범위
  • R - 난수 [-1.0;1.0]

개별 수영은 그림 1에 개략적으로 나와 있습니다.

개인

그림 1. 개별 수영. 각 물고기의 이동 벡터는 임의의 방향으로 향하며 다른 스칼라 값을 갖습니다.

개별 수영 후 체력 함수가 측정됩니다. 결과적으로 나오는 물고기의 위치가 개선되지 않으면 이 특정 물고기는 움직이지 않고 제자리에 있는 것으로 간주됩니다. 체력 함수가 향상된 물고기만 새로운 위치로 이동합니다.

개별 수영이 완료되면 본능적-집단적 이동 운영자가 실행됩니다. 먼저 그림 2를 살펴보겠습니다.

수집

그림 2. 본능적-집단 수영 이 움직임은 모든 물고기에 대해 질량 중심 G에 대한 동일한 방향 및 크기의 벡터로 특징 지어집니다.

본능적-집단적 이동은 이전 반복에서 각 물고기의 체력 함수의 변화를 고려하여 물고기 떼의 일반적인 위치를 수정하는 역할을 합니다. 새로운 좌표는 다음과 같이 계산됩니다:

fishes [f].new_position [d] = fishes [f].current_position [d] + collective_instinct [d];

설명:

  • new_position - 해당 좌표에서 물고기의 새 위치
  • current_position - 해당 좌표의 현재 위치
  • collective_instinct - 해당 좌표를 따라 이동한 양을 다음과 같이 계산합니다:

collective_instinct [d] = fishes [f].delta_position [d] * fishes [f].delta_fitness;

설명:

  • delta_position - 개별 수영 단계에서 얻은 현재 위치와 이전 위치의 좌표의 차이
  • delta_fitness - 개별 수영 중 현재 위치 및 이전 위치의 피트니스 함수 변경

본능적-집단적 수영은 물고기 떼가 새로운 장소로 이동하는 그룹 동기식 이동을 공식화하여 새로운 먹이 장소를 찾고 개별 이동은 국지적으로 상황을 개선할 수 있습니다.

이제 집단적-의지적 수영에 대해 알아봅시다. 이 수영은 두 가지 유형으로 나뉩니다:

- 질량의 중심에서 - 전반적으로 떼의 위치가 이전 위치에 비해 개선되지 않은 경우 물고기는 먹이를 더 찾는 것을 상징하는 것으로 볼 수 있는 측면으로 퍼졌습니다 (그림 3).

- 개선이 있을 경우 질량 중심으로 이동합니다. 그런 다음 물고기는 질량 중심으로 이동하여 열을 짜고 먹이에 대한 공격을 상징합니다. 알고리즘적으로 이는 최적화 문제의 해결책을 개선하는 것을 의미합니다(그림 4).

col vol out

그림 3. 집단적 - 의지적 수영. 방향 벡터는 질량 중심 G로부터 나옵니다.

col vol in

그림 4. 집단적 - 의지적 수영. 방향 벡터는 질량 중심 G를 향합니다.


집단적 - 의지적 수영의 계산을 위해 질량의 중심 개념이 도입되었습니다. 집단적 - 의지적 수영 방정식은 다음과 같습니다:

fishes [f].new_position [d] = pos + (((pos - barycenter [d]) * step_vol [d] * r) * search_operator);

설명:

  • pos는 current_position와 동일합니다.
  • search_operator - 이전 이동으로 위치가 개선된 경우 1, 그렇지 않은 경우 -1입니다.

  • step_vol [d] - 집단적 이동 단계, 다음과 같이 계산됩니다:

step_vol [d] = initial_step_vol * (rangeMax [d] - rangeMin [d]);

설명:

  • initial_step_vol - 집단적 이동을 위한 알고리즘 매개변수

  • barycenter [d] - 물고기 무게의 합에 좌표를 곱한 값으로 계산된 질량의 중심:

barycenter [d] += fishes [f].weight * fishes [f].current_position [d];

계산한 후 물고기 떼의 총 무게로 나눕니다:

barycenter [d] /= current_total_weight;

알고리즘의 의사 코드는 다음과 같습니다:

1) 난수로 물고기 위치를 초기화합니다.

2) 개별 이동

3) 피트니스 함수 계산

4) 각 물고기에 대한 가중치 재 정의

5) 본능적 - 집단적 이동

6) 집단적 - 의지적 이동

7) 총 무게를 다시 계산

8) 체력 함수 계산

9) 정지 기준이 충족될 때까지 2) 단계부터 반복

스키마 FSS

그림 5. FSS 알고리즘 블록 다이어그램

코드 설명을 시작하겠습니다.

알고리즘의 가장 간단한 논리 단위는 물고기를 설명하는 구조입니다. 우리는 물고기를 여러 번 초기화해야 하므로 특수 Init () 메서드에서 상대적으로 큰 크기로 이 구조를 "제로화"하여 코드를 약간 최적화 할 수 있도록 하는 것이 좋습니다. 구조에는 현재 위치, 새 위치 및 마지막 이동 이후의 좌표 차이에 대한 좌표 배열이 포함됩니다. 기본 무게는 1000 표준 단위이고 어떤 값이라도 사용할 수 있습니다. 물고기는 또한 현재 피트니스 함수의 값, 이전 피트니스 함수의 값 및 그 차이로 특징 지어집니다. Init() 메서드에서 피트니스 함수는 가능한 최소의 '더블' 값으로 초기화됩니다. 따라서 물고기가 아직 움직이지 않았기 때문에 체력 차이는 0입니다.

//——————————————————————————————————————————————————————————————————————————————
struct S_Fish
{
  void Init (int dimensions)
  {
    ArrayResize (current_position, dimensions);
    ArrayResize (new_position,     dimensions);
    ArrayResize (delta_position,   dimensions);
    weight        = 1000.0;
    fitness       = -DBL_MAX;
    delta_fitness = 0.0;
  }

  double current_position [];
  double new_position     [];
  double delta_position   [];
  double weight;
  double fitness;
  double new_fitness;
  double delta_fitness;
};
//——————————————————————————————————————————————————————————————————————————————

자연적인 커뮤니티 모델을 수학적으로 표현하는 물고기 떼인 C_AO_FSS 클래스를 선언해 보겠습니다. 여기에는 특이한 점이 없습니다. 또한 사용자 프로그램과의 상호 작용에 필요한 두 가지 공개 메서드, 즉 물고기의 좌표와 떼에서의 상호 작용을 고려하는 데 필요한 최적화 함수 값의 범위와 배열이 있습니다.

Init () 초기화 클래스의 공용 메서드에서 우리는 변수를 원래 값으로 재설정하고 배열에 메모리를 할당해야 합니다. 특히 init 및 swimmingRegime 변수에 주의하세요. 우리가 본 의사 코드에 따르면 피트니스 함수의 계산은 두 번 수행됩니다: - 첫 번째는 개별 운동 후, 두 번째는 두 가지 유형의 집단 운동 후입니다. 이는 우리가 채택한 알고리즘-응용 프로그램 연결 체계와 모순되는 것이며 각 반복은 first_method -> calculation_fit_functions -> second_method의 순서를 갖습니다. 이 두 변수는 이 문제를 해결하고 표준 알고리즘의 작업 순서를 변경하기 위해 적용됩니다. 알고리즘을 시작할 때 초기화 변수는 'false'여야 합니다. 이는 알고리즘의 전체 논리가 좌표의 현재 값과 이전 값과 체력 함수의 차이를 사용하기 때문에 물고기를 초기화하고 체력 함수를 계산하고 이동을 다시 하게 할 필요성을 나타내는 플래그입니다. 그렇지 않았다면 값의 차이를 얻을 수 없었을 것입니다. 두 번째로 중요한 플래그는 swimmingRegime입니다. 이를 통해 물고기의 움직임을 설명하는 메서드 호출 순서를 재정의하여 구조와 일치하도록 할 수 있습니다. 1의 값은 "개별" 수영의 호출에 해당하며 그렇지 않을 경우 위에서 고려한 그룹 동작의 순서를 사용합니다.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_FSS::Init (const int    dimensionsP,
                     const int    fishSchSizeP,
                     const double initial_step_indP,
                     const double initial_step_volP)
{
  MathSrand (GetTickCount ());
  init = false;
  swimmingRegime = 1;

  dimensions     = dimensionsP;

  ArrayResize (rangeMax,  dimensions);
  ArrayResize (rangeMin,  dimensions);
  ArrayResize (rangeStep, dimensions);

  num_of_individuos = fishSchSizeP;

  ArrayResize (fishes, num_of_individuos);

  for (int i = 0; i < num_of_individuos; i++)
  {
    fishes [i].Init (dimensions);
  }

  global_best = -DBL_MAX;
  ArrayResize (global_best_position, dimensions);

  total_weight     = num_of_individuos;

  initial_step_ind = initial_step_indP;
  ArrayResize (step_ind, dimensions);

  initial_step_vol = initial_step_volP;
  ArrayResize (step_vol, dimensions);

  ArrayResize (collective_instinct, dimensions);
  ArrayResize (barycenter, dimensions);
}
//——————————————————————————————————————————————————————————————————————————————

각 반복에서 호출되는 첫 번째 공용 메서드 Swimming()은 개별 및 그룹 물고기의 이동에 대한 호출 순서를 결정합니다. 이 메서드가 처음 호출되면 두 개의 initial_step_ind 및 initial_step_vol 알고리즘 매개 변수를 사용하여 개별 및 그룹의 이동 단계가 해당 좌표를 따라 가능한 범위의 일부로 초기화됩니다. 일부 전문가는 각각 0.01 및 0.001로 설정할 것을 권장합니다. 하지만 저는 이 값으로는 좋은 결과를 얻지 못했습니다. 그래서 저는 0.1과 0.8을 사용합니다. 또한 알고리즘을 처음 실행할 때 물고기 위치 좌표의 현재 값은 최적화된 매개변수 범위 내의 난수로 초기화됩니다. 이후 메서드 호출 시 swimmingRegime 플래그에 따라 적절한 움직임 유형이 호출됩니다.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_FSS::Swimming (int i)
{
  //----------------------------------------------------------------------------
  if (!init)
  {
    global_best    = -DBL_MAX;
    swimmingRegime = 1;

    for (int d = 0; d < dimensions; d++)
    {
      step_ind [d] = initial_step_ind * (rangeMax [d] - rangeMin [d]);
      step_vol [d] = initial_step_vol * (rangeMax [d] - rangeMin [d]);
    }

    for (int f = 0; f < num_of_individuos; f++)
    {
      fishes [f].Init (dimensions);

      for (int d = 0; d < dimensions; d++)
      {
        fishes [f].new_position [d] = RNDfromCI (rangeMin [d], rangeMax [d]);
        fishes [f].new_position [d] = SeInDiSp (fishes [f].new_position [d], rangeMin [d], rangeMax [d], rangeStep [d]);
      }
    }
  }
  //----------------------------------------------------------------------------
  else
  {
    switch (swimmingRegime)
    {
    case 1:
      apply_individual_movement ();            //individual movement
      break;
    default:
      apply_instintive_collective_movement (); //instinctively-collective movement
      apply_collective_volitive_movement ();   //collective-volitional movement
      update_total_weight ();                  //recalculate the total weight
      break;
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

각 반복에서 호출되는 두 번째 공용 메서드 Regrouping ()은 주로 수영 메서드(개별, 집단적-본능적, 집단적-의지적)에 대한 호출 순서를 결정하기 위한 것입니다. 호출 순서를 보장하는 메서드의 추가 기능은 값 1과 2를 취하는 swimmingRegime 플래그를 통해 제공됩니다. 이는 제가 채택한 구조의 고전적인 버전에서 물고기 이동 메서드 호출 순서를 재정의하는 데 필요합니다: first_open_method -> _fitness_function_calculation -> second_open_method. Init 플래그에 따라 알고리즘이 첫 번째 반복에 있는 경우 우리는 좌표의 현재 위치와 적합도 함수의 값을 저장하여 그 차이를 추가로 계산할 수 있습니다 왜냐하면 메서드가 적합도 함수를 계산한 후에 호출된다고 가정하기 때문입니다. Init 플래그가 알고리즘이 두 번째 및 후속 반복 중에 있는 경우에는 개별 운동 후 피트니스 함수의 현재 값과 이전 값의 차이와 현재 및 이전 위치의 좌표의 차이를 구해야 합니다. 차이는 위치가 개선된 조건에서 계산되며 그렇지 않은 경우 움직임이 없는 것으로 간주합니다. 따라서 물고기 무게의 값을 초기 상태로 재설정하고 이동과 피트니스 함수의 차이를 0으로 간주합니다. Update_optimal_solution() 메서드를 호출하여 최고의 솔류션을 계속 업데이트 하고 apply_feeding() 메서드로 물고기 먹이 공급을 적용합니다. swimmingRegime 플래그가 1이 아닌 경우 집단적-본능적 및 집단적-의지적 이동 적용된 것입니다. 이들을 실행한 후 swimmingRegime을 1로 설정합니다. 다음은 개별 운동입니다.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_FSS::Regrouping ()
{
  //----------------------------------------------------------------------------
  if (swimmingRegime == 1
  {
    if (!init)
    {
      for (int f = 0; f < num_of_individuos; f++)
      {
        ArrayCopy (fishes [f].current_position, fishes [f].new_position, 0, 0, WHOLE_ARRAY);
        ArrayInitialize (fishes [f].delta_position, 0.0);

        fishes [f].fitness = fishes [f].new_fitness;
        fishes [f].delta_fitness = 0.0;
      }

      init = true;
      return;
    }

    for (int f = 0; f < num_of_individuos; f++)
    {
      //remember the best position for the fish
      if (fishes [f].new_fitness > fishes [f].fitness)
      {
        fishes [f].delta_fitness = fishes [f].new_fitness - fishes [f].fitness; //fabs
        fishes [f].fitness = fishes [f].new_fitness;

        for (int d = 0; d < dimensions; d++)
        {
          fishes [f].delta_position [d] = fishes [f].new_position [d] - fishes [f].current_position [d];
        }

        ArrayCopy (fishes [f].current_position, fishes [f].new_position, 0, 0, WHOLE_ARRAY);
      }
      else
      {
        ArrayInitialize (fishes [f].delta_position, 0.0);
        fishes [f].delta_fitness = 0.0;
      }
    }

    swimmingRegime = 2;
    updates_optimal_solution ();
    apply_feeding ();
    return;
  }

  //----------------------------------------------------------------------------
  swimmingRegime = 1;
  updates_optimal_solution ();
}
//——————————————————————————————————————————————————————————————————————————————

다음과 같은 간단한 프라이빗 메서드를 사용하여 알고리즘의 최적 결과에 도달하면 이를 업데이트합니다. 이를 위해 각 물고기의 피트니스 값 함수의 값을 글로벌 값과 비교하기만 하면 됩니다. 이 값들이 더 낫다면 저장하세요.

//——————————————————————————————————————————————————————————————————————————————
// update the best overall solution
void C_AO_FSS::updates_optimal_solution ()
{
  for (int f = 0; f < num_of_individuos; f++)
  {
    if (fishes [f].fitness > global_best)
    {
      global_best = fishes [f].fitness;
      ArrayCopy (global_best_position, fishes [f].current_position, 0, 0, WHOLE_ARRAY);
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

우리는 이미 위에서 개별 수영을 제공하는 프라이빗 메서드의 방정식을 고려했습니다. 그러므로 여기서는 모든게 간단합니다: 각 물고기의 현재 좌표에 -1.0에서 1.0 범위의 난수를 곱한 개별 단계를 추가하여 양수 및 음수의 방향으로 모두 증분합니다. 최적화된 매개변수의 값이 범위를 벗어나면 테두리 값이 좌표에 대해 설정됩니다.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_FSS::apply_individual_movement ()
{
  // move the fish to new places-----------------------------------------------
  double r = 0.0;

  for (int f = 0; f < num_of_individuos; f++)
  {
    for (int d = 0; d < dimensions; d++)
    {
      r = RNDfromCI (-1.0, 1.0);

      fishes [f].new_position [d] = fishes [f].current_position [d] + step_ind [d] * r;
      fishes [f].new_position [d] = SeInDiSp (fishes [f].new_position [d], rangeMin [d], rangeMax [d], rangeStep [d]);
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

먹이 주기 메서드는 이해하기 어려워서는 안 됩니다. 실제로 물고기의 무게는 물고기 자체의 체력 함수의 차이와 전체 물고기 떼의 차이의 최대 값의 몫으로 결정됩니다. 그러나 모든 물고기에서 최대 차이가 0과 같을 수도 있습니다. 고전적인 버전의 알고리즘에 대한 설명에서는 물고기의 무게는 항상 양수여야 한다고 나와 있습니다. 일반적으로 적합도 함수가 양수의 값을 취하는 경우만 고려하지만 저는 이 요구에 대해 실용적인 의미를 찾지 못했습니다. 제 생각에 물고기의 무게는 음수 값을 취할 수 있습니다. 그러므로 모든 물고기 중에서 체력 함수의 최대 차이(각 물고기의 무게를 정규화해야 하는 값)가 0이면 물고기의 무게는 1로 간주됩니다.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_FSS::apply_feeding ()
{
  double max_delta_fitness = -DBL_MAX;

  //find the maximum weight among fish
  for (int f = 0; f < num_of_individuos; f++)
  {
    if (fishes [f].delta_fitness > max_delta_fitness) max_delta_fitness = fishes [f].delta_fitness;
  }

  //feed the fish
  for (int f = 0; f < num_of_individuos; f++)
  {
    if (max_delta_fitness != 0.0)
    {
      fishes [f].weight = fishes [f].weight + (fishes [f].delta_fitness / max_delta_fitness);
    }
    else fishes [f].weight = 1;
  }
}
//——————————————————————————————————————————————————————————————————————————————

본능적-집단적 움직임의 프라이빗 메서드는 집단적 본능적의 값만큼 증가하는 방식으로 변경되는 각 물고기의 좌표로 구성되며 이는 이전의 이동에서 달성한 체력 함수의 차이와 현재와 이전 반복에서의 위치의 차이의 곱에 지나지 않습니다. 여기서 우리는 최적화된 파라미터의 경계를 넘어설 때 경계 값을 할당합니다.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_FSS::apply_instintive_collective_movement ()
{
  double sum_delta_fitness = 0.0;
  for (int f = 0; f < num_of_individuos; f++)
  {
    sum_delta_fitness += fishes [f].delta_fitness;
  }

  for (int f = 0; f < num_of_individuos; f++)
  {
    for (int d = 0; d < dimensions; d++)
    {
      collective_instinct [d] = fishes [f].delta_position [d] * fishes [f].delta_fitness;

      if (sum_delta_fitness != 0.0)
      {
        collective_instinct [d] /= sum_delta_fitness;
      }

      fishes [f].new_position [d] = fishes [f].current_position [d] + collective_instinct [d];
      fishes [f].new_position [d] = SeInDiSp (fishes [f].new_position [d], rangeMin [d], rangeMax [d], rangeStep [d]);
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

물고기 떼의 질량 중심과 현재 총 무게를 계산하는 집단적-의지적 수영의 프라이빗 메서드. 떼의 총 무게가 증가하게 되면 물고기는 질량 중심을 향해 움직이기 시작하고 그렇지 않으면 중심에서 멀어집니다. 이 방정식은 앞서 자세히 설명했습니다. 물고기 떼의 총 무게는 아래에 설명된 특별한 메서드로 업데이트됩니다.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_FSS::apply_collective_volitive_movement ()
{
  //----------------------------------------------------------------------------
  double current_total_weight = 0.0;

  for (int f = 0; f < num_of_individuos; f++)
  {
    current_total_weight += fishes [f].weight;
  }

  ArrayInitialize (barycenter, 0.0);

  for (int f = 0; f < num_of_individuos; f++)
  {
    for (int d = 0; d < dimensions; d++)
    {
      barycenter [d] += fishes [f].weight * fishes [f].current_position [d];
    }
  }

  for (int d = 0; d < dimensions; d++)
  {
    barycenter [d] /= current_total_weight;
  }

  //----------------------------------------------------------------------------
  double search_operator = current_total_weight > total_weight ? 1.0 : -1.0;
  double r   = 0.0;
  double pos = 0.0;

  for (int f = 0; f < num_of_individuos; f++)
  {
    for (int d = 0; d < dimensions; d++)
    {
      r = RNDfromCI (0.0, 1.0);
      pos = fishes [f].current_position [d];

      fishes [f].new_position [d] = pos + (((pos - barycenter [d]) * step_vol [d] * r) * search_operator);
      fishes [f].new_position [d] = SeInDiSp (fishes [f].new_position [d], rangeMin [d], rangeMax [d], rangeStep [d]);
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

사실은 이것은 물고기의 총 무게를 업데이트하는 바로 그 방법입니다. 여기서는 모든 것이 간단합니다. 각 물고기의 무게를 측정하여 합산하면 됩니다.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_FSS::update_total_weight ()
{
  total_weight = 0.0;

  for (int f = 0; f < num_of_individuos; f++)
  {
    total_weight += fishes [f].weight;
  }
}
//——————————————————————————————————————————————————————————————————————————————


3. 테스트 결과

마지막이자 가장 흥미로운 부분인 결과 부분으로 넘어가겠습니다. 알고리즘에는 흥미로운 트릭이 포함되어 있습니다. 글로벌 최적을 고려할 필요가 없는 것, 질량 중심 및 본능적-의지적 운동 개념의 도입, 전체적으로 위치가 개선되었는지 여부에 따라 떼 중심에서 수렴 또는 산란을 암시하는 것과 같은 트릭 들입니다. 이 모든 것이 연구된 함수에 대한 알고리즘의 원래 동작에 대해 희망을 주는 것들이었습니다.

일반적으로 공간의 탐색 영역에서 행동에는 신기한 것이 있습니다. 꿀벌 알고리즘에서 관찰된 것과 마찬가지로 공간의 개별 부분으로 물고기가 분산되어 있습니다. 그러나 방정식과 FSS의 작동 원리는 떼가 별도의 그룹으로 분산되는 것을 의미하지는 않습니다. 일반적으로 알고리즘의 성능은 좋지 않았으며 전체 결과에서 PSO 알고리즘보다 약간 앞서는 정도에 불과했습니다.

개별 테스트를 고려하면 FSS는 여전히 일부 테스트에서 우수한 성적을 거두었습니다. 특히 매끄러운 피부 함수에서 FSS 알고리즘이 늑대 알고리즘보다 더 나은 것으로 판명되어 좋은 (최고는 아니지만) 결과를 보여 주었습니다. 이 알고리즘은 각 좌표에 대해 증분으로 고려되는 연구 중인 각각의 함수의 표면 기울기와 각각의 반복에서 적합도 함수의 변화를 사용합니다. 스킨 함수가 부드럽기 때문에 알고리즘이 표면 변화에 잘 '밀착'합니다.

포레스트 함수의 경우 알고리즘은 평균보다 낮은 결과를 보여주었습니다. 테스트 함수를 부드럽게 변경하면 알고리즘이 공간을 탐색하는 데 어느 정도 도움이 되었지만 글로벌 최대값을 찾지는 못했습니다. 메가시티를 고려할 때 FSS의 단점은 더욱 두드러집니다. 연구 중인 함수가 개별 에이전트의 현재 위치 근처에서 변경되지 않고 알고리즘이 잠재적으로 더 나은 새로운 장소를 식별하기 위해 큰 점프를 할 수 없는 경우 알고리즘은 실제로 "그 함수를 좋아하지 않는다"고 말합니다. 따라서 근처에 증분이 없는 국부적 극한값에 갇히게 됩니다.

메가시티 테스트는 모든 최적화 알고리즘에 매우 어렵고 비교표의 다른 것들이 일반적으로 FSS보다 훨씬 앞서 있는 것은 아니지만 이산 문제에 대해서는 알고리즘의 능력이 약하다는 것을 인식할 필요가 있습니다. 일부 테스트에서는 무작위 검색이 가장 좋은 결과를 보여주었습니다. 애니메이션에서 볼 수 있듯이 알고리즘 연산에서 '클러스터링'이 관찰되지 않습니다. 이전 글에서 설명한 최적화 알고리즘 '결정화'를 기억하실 것입니다.

FSS 알고리즘 결과:

2022.12.08 13:14:06.033 Test_AO_FSS (EURUSD,M1) =============================
2022.12.08 13:14:08.388 Test_AO_FSS (EURUSD,M1) 1 Skin's; Func 10000 실행 결과: 4.892861444841324
2022.12.08 13:14:08.388 Test_AO_FSS (EURUSD,M1) 점수: 0.99391
2022.12.08 13:14:12.557 Test_AO_FSS (EURUSD,M1) 20 Skin's; Func 10000 실행 결과: 3.11410005347766
2022.12.08 13:14:12.557 Test_AO_FSS (EURUSD,M1) 점수: 0.56920
2022.12.08 13:14:47.176 Test_AO_FSS (EURUSD,M1) 500 Skin's; Func 실행 10000 결과: 1.20519552002827
2022.12.08 13:14:47.176 Test_AO_FSS (EURUSD,M1) 점수: 0.11341
2022.12.08 13:14:47.176 Test_AO_FSS (EURUSD,M1) =============================
2022.12.08 13:14:49.498 Test_AO_FSS (EURUSD,M1) 1 Forest's; Func 10000 실행 결과: 1.5057381856551146
2022.12.08 13:14:49.498 Test_AO_FSS (EURUSD,M1) 점수: 0.85172
2022.12.08 13:14:53.825 Test_AO_FSS (EURUSD,M1) 20 Forest's; Func 10000 실행 결과: 0.21468156279781656
2022.12.08 13:14:53.825 Test_AO_FSS (EURUSD,M1) 점수: 0.12143
2022.12.08 13:15:31.235 Test_AO_FSS (EURUSD,M1) 500 Forest's; Func 10000 실행 결과: 0.056970068652984526
2022.12.08 13:15:31.235 Test_AO_FSS (EURUSD,M1) 점수: 0.03223
2022.12.08 13:15:31.235 Test_AO_FSS (EURUSD,M1) =============================
2022.12.08 13:15:34.066 Test_AO_FSS (EURUSD,M1) 1 Megacity's; Func 10000 실행 결과: 11.0
2022.12.08 13:15:34.066 Test_AO_FSS (EURUSD,M1) 점수: 0.91667
2022.12.08 13:15:38.467 Test_AO_FSS (EURUSD,M1) 20 Megacity's; Func 10000 실행 결과: 1.03
2022.12.08 13:15:38.467 Test_AO_FSS (EURUSD,M1) 점수: 0.08583
2022.12.08 13:16:16.589 Test_AO_FSS (EURUSD,M1) 500 Megacity's; Func 10000 실행 결과: 0.31
2022.12.08 13:16:16.589 Test_AO_FSS (EURUSD,M1) 점수: 0.02583
2022.12.08 13:16:16.589 Test_AO_FSS (EURUSD,M1) =============================
2022.12.08 13:16:16.589 Test_AO_FSS (EURUSD,M1) C_AO_FSS의 모든 점수: 0.4122477188979048

스킨

  피부 테스트 함수에 대한 FSS.

숲

  FSS의 Forest 테스트 함수.

메가시티

  FSS의 메가시티 테스트 함수.

최종 표는 다음과 같습니다. 각 테스트 함수에 대한 알고리즘을 개별적으로 평가할 때 보다 편리하게 사용할 수 있도록 추가 열을 사용하여 특정 작업에 대한 각 알고리즘의 적용 가능성에 대해 보다 공정하게 논쟁할 수 있습니다.

AO

설명

피부

스킨 최종

숲 최종

메가시티(개별)

메가시티 최종

최종 결과

2 params(1 F)

40 params(20F)

1000 params(500 F)

2 params(1 F)

40 params(20F)

1000 params(500 F)

2 params(1 F)

40 params(20F)

1000 params(500 F)

COAm

뻐꾸기 최적화 알고리즘

1.00000

0.85911

0.14316

0.66742

0.99283

0.28787

0.04551

0.44207

1.00000

0.24917

0.03537

0.42818

0.51255778

ACOm

개미 군집 최적화

0.98229

0.79108

0.12602

0.63313

1.00000

0.62077

0.11521

0.57866

0.38333

0.44000

0.02377

0.28237

0.49805222

ABCm

인공 꿀벌 군집 M

1.00000

0.63922

0.08076

0.57333

0.99908

0.20112

0.03785

0.41268

1.00000

0.16333

0.02823

0.39719

0.46106556

ABC

인공 꿀벌 군집

0.99339

0.73381

0.11118

0.61279

0.99934

0.21437

0.04215

0.41862

0.85000

0.16833

0.03130

0.34988

0.46043000

GWO

회색 늑대 최적화

0.99900

0.48033

0.18924

0.55619

0.83844

0.08755

0.02555

0.31718

1.00000

0.10000

0.02187

0.37396

0.41577556

FSS

물고기 떼 검색

0.99391

0.5692

0.11341

0.55884

0.85172

0.12143

0.03223

0.33513

0.91667

0.08583

0.02583

0.34278

0.41224778

PSO

파티클 스웜 최적화

0.99627

0.38080

0.05089

0.47599

0.93772

0.14540

0.04856

0.37723

1.00000

0.09333

0.02233

0.37189

0.40836667

RND

랜덤

0.99932

0.44276

0.06827

0.50345

0.83126

0.11524

0.03048

0.32566

0.83333

0.09000

0.02403

0.31579

0.38163222


확률적(무작위) 휴리스틱 방법은 정확한 해결책을 보장하지는 않지만 일반적으로는 허용 가능한 시간 내에 실용적으로 사용할 수 있을 만큼 가까운 해결책을 찾을 수 있도록 합니다. 일부 알고리즘은 뛰어난 융합 능력을 보여줄 수 있지만 FSS의 경우는 그렇지 않습니다. 일반적으로 물고기 떼에 대한 검색 알고리즘은 파티클 스웜을 기반으로 하는 알고리즘의 특수한 경우입니다. 따라서 장점과 단점을 모두 물려받았습니다. 고유한 알고리즘은 파티클 스웜에서는 관찰되지 않는 물고기 떼(스웜)를 별도의 그룹으로 나누는 것이 포함됩니다. 이 알고리즘은 부드러운 함수에 비교적 잘 대처하지만 FSS가 여러 변수가 있는 함수에 잘 대처할지는 확실하지 않습니다.

장점:
1. 알고리즘은 부드러운 함수들을 상당히 잘 처리합니다.
2. 물고기 떼를 별도의 그룹으로 나눌 수 있으므로 알고리즘이 다른 잠재적으로 좋은 지역 극단을 더 탐색할 수 있습니다.

단점:
1. 개별 테스트에서 결과의 편차가 크다는 것은 알고리즘이 불안정하다는 것을 나타냅니다.
2. 불연속 함수와 중단이 있는 함수에서 매우 약한 수렴이 발생합니다. 이산 함수에는 거의 적용되지 않습니다.
3. 확장성이 약합니다.


MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/11841

Expert Advisor 개발 기초부터(23부): 새로운 주문 시스템(VI) Expert Advisor 개발 기초부터(23부): 새로운 주문 시스템(VI)
이제 주문 시스템을 더욱 유연하게 만들어 볼 것입니다. 여기서는 포지션 스톱 레벨을 훨씬 더 빠르게 변경할 수 있도록 코드를 더 유연하게 변경하는 방법을 알아보겠습니다.
Expert Adviso 개발 기초부터(22부): 새로운 주문 시스템(V) Expert Adviso 개발 기초부터(22부): 새로운 주문 시스템(V)
오늘은 새로운 주문 시스템을 계속 개발할 예정입니다. 새로운 시스템을 구현하는 것은 그리 쉬운 일이 아닙니다. 프로세스를 복잡하게 만드는 문제가 종종 발생하기 때문입니다. 이러한 문제가 나타나면 우리는 개발을 멈추고 우리의 개발 방향에 대해 다시 분석해야 합니다.
MQL5에서 ONNX 모델을 사용하는 방법 MQL5에서 ONNX 모델을 사용하는 방법
ONNX(Open Neural Network Exchange)는 머신 러닝 모델을 나타내기 위해 구축된 개방형 형식입니다. 이 기사에서는 금융 시계열을 예측하기 위해 CNN-LSTM 모델을 만드는 방법에 대해 살펴보겠습니다. 또한 MQL5 Expert Advisor에서 생성된 ONNX 모델을 사용하는 방법도 보여드리겠습니다.
어썸 오실레이터로 트레이딩 시스템 설계하는 방법 알아보기 어썸 오실레이터로 트레이딩 시스템 설계하는 방법 알아보기
이번 글에서는 트레이딩에 유용하게 사용될 수 있는 새로운 기술 도구에 대해 알아보겠습니다. 그것은 어썸 오실레이터(AO) 지표입니다. 이 지표로 거래 시스템을 설계하는 방법에 대해 알아볼 것입니다.