Jenetics 설치 및 예제
이 포스트는 유전 알고리즘(Genetic Algorithms)에서 파생되었습니다. 유전 알고리즘으로 구현된 라이브러리인 Jenetics에 대해 알아봅시다.
준비
JDK8이 필요합니다. 현 시점에서 가장 최신 버전인데 바로 사용하는 라이브러리군요. -_-; 일단 오라클에서 JDK8을 설치하고 다음절을 진행합니다.
jenetics에서 Maven을 클릭하면 플랫폼별로 받을 수 있는 링크로 연결됩니다. 음, 전 우선 Maven 프로젝트로 구성하겠습니다. 따라서 POM.xml 파일에 jenectics 부분을 추가합시다. 그리고 zip 파일도 다운로드 받습니다.
예제 검토
다운로드 받은 zip 파일의 압축을 해제 하면 example 디렉토리가 있습니다. 그 중 Sorting.java라는 파일의 소스를 검토해봤습니다.
/* * Java Genetic Algorithm Library (@__identifier__@). * Copyright (c) @__year__@ Franz Wilhelmstötter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Author: * Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at) */ package org.jenetics.example; import java.util.function.Function; import java.util.stream.IntStream; import org.jenetics.Chromosome; import org.jenetics.EnumGene; import org.jenetics.Genotype; import org.jenetics.Optimize; import org.jenetics.PartiallyMatchedCrossover; import org.jenetics.PermutationChromosome; import org.jenetics.SwapMutator; import org.jenetics.engine.Engine; import org.jenetics.engine.EvolutionResult; import org.jenetics.engine.EvolutionStatistics; import org.jenetics.engine.limit; import org.jenetics.stat.DoubleMomentStatistics; import org.jenetics.util.LCG64ShiftRandom; import org.jenetics.util.RandomRegistry; public class Sorting { private static int dist(Chromosome<EnumGene<Integer>> path, int i, int j) { // 연속된 두 공간의 차이를 구한 후 이것을 곱한다. // 따라서 i값이 3이고 j값이 9라면 거리는 36이 나온다. return (path.getGene(i).getAllele() - path.getGene(j).getAllele())* (path.getGene(i).getAllele() - path.getGene(j).getAllele()); } private static int length(final Genotype<EnumGene<Integer>> genotype) { // 거리간의 합을 합한다. // 만약 10개의 항목을 정렬한다면 최소 아홉번을 비교한다. // 이때 모든 항목이 정렬되었다면 최소값은 "9"가 될 것이다. return IntStream.range(1, genotype.getChromosome().length()) .map(i -> dist(genotype.getChromosome(), i, i - 1)) .sum(); } public static void main(final String[] args) { // GA를 위한 랜덤 레지스트리 설정 RandomRegistry.setRandom(new LCG64ShiftRandom.ThreadLocal()); // 엔진 구성 final Engine<EnumGene<Integer>, Integer> engine = Engine // 마지막에 정렬할 개수를 정한다. .builder((Function<Genotype<EnumGene<Integer>>, Integer>) Sorting::length, PermutationChromosome.ofInteger(20)) // 거리간의 합이 제일 작은 것이 목표다. .optimize(Optimize.MINIMUM) // 인구 크기를 정한다. 클수록 오랜 세대를 보낸다. .populationSize(1000) // .survivorsSelector(new RouletteWheelSelector<>()) // .offspringSelector(new TruncationSelector<>()) // 다음 세대를 평가하기 위한 생존 확률? // 이 값이 너무 낮아도 편차가 상당히 커진다. .offspringFraction(0.9).alterers( new SwapMutator<>(0.01), // 돌연변이 확률 new PartiallyMatchedCrossover<>(0.3)) // 교차 확률 .build(); // 진화 통계 final EvolutionStatistics<Integer, DoubleMomentStatistics> statistics = EvolutionStatistics.ofNumber(); // 진화 결과 final EvolutionResult<EnumGene<Integer>, Integer> result = engine .stream() // 100세대 이내로 더 좋은 유전 형질이 나타나지 않으면 멈춤 .limit(limit.bySteadyFitness(100)) // 스트림 길이가 2500 넘어가면 멈춤? .limit(2500) // 통계 변수 삽입 .peek(statistics) // 제일 좋은 진화 결과 취합 .collect(EvolutionResult.toBestEvolutionResult()); // 통계 출력 System.out.println(statistics); // 제일 좋은 유전자 출력 System.out.println(result.getBestPhenotype()); } }
대부분의 내용은 주석을 달았습니다. 전부 맞다고 하기엔 애매하고 참조 정도로 봐주시면 되겠습니다.
+---------------------------------------------------------------------------+ | Time statistics | +---------------------------------------------------------------------------+ | Selection: sum=0.115449051000 s; mean=0.000630869131 s | | Altering: sum=0.502491419000 s; mean=0.002745854749 s | | Fitness calculation: sum=0.098742281000 s; mean=0.000539575306 s | | Overall execution: sum=0.733507542000 s; mean=0.004008237934 s | +---------------------------------------------------------------------------+ | Evolution statistics | +---------------------------------------------------------------------------+ | Generations: 183 | | Altered: sum=132,528; mean=724.196721311 | | Killed: sum=0; mean=0.000000000 | | Invalids: sum=0; mean=0.000000000 | +---------------------------------------------------------------------------+ | Population statistics | +---------------------------------------------------------------------------+ | Age: max=14; mean=0.968546; var=2.071197 | | Fitness: | | min = 70.000000000000 | | max = 2156.000000000000 | | mean = 169.365551912568 | | var = 64200.536123362730 | +---------------------------------------------------------------------------+ [4|6|7|9|11|14|17|19|18|16|15|13|12|10|8|5|3|2|1|0] --> 70
위는 결과를 출력해본건데 소스에 있는대로 하면 딱히 잘 맞진 않네요. 정렬할 항목을 줄이거나 populationSize를 크게 주면 정렬이 잘 됩니다.