archive./Schoolwork

[Python] 인공지능입문 과제 - Genetic Algorithm

FATKITTY 2020. 7. 15. 02:02
반응형

 

 


 

What this program is trying to do (comments)

 

## GA Algorithm

 

# GA Cycle :

 

population (chromosome) -> reproduction (fitness) -> selection

-> genetic operation (crossover, mutation) -- new generation --> ......

 

# Algorithm :

 

begin

t <-- 0 (generation)

initialize P(t)

evaluate P(t)

while (not termination-condition) do

begin

t <-- t + 1

select P(t) from P(t-1)

alter P(t)

evaluation P(t)

end

end

 

# Example :

 

f(x) = x * sin(10 * pi * x) = 1.0

then, find x from the range [-1, 2] that maximizes the f(x)

-> adopt GA

Set 3,000,000 points from [-1, 2]

Map above decimal value into binary value ; 2**21 < 3,000,000 < 2**22 = 4,194,304

 

- Initial population -> population set as 100

: Create a population of chromosome with binary vector of 22 bits (randomly).

 

- Evaluation function

: eval(v) = f(x) = -1.0 + x * 3 / 4194304

 

- Genetic operator

: typical operators : mutation, crossover

 

1. mutation ; alters one or more genes

2. crossover ; crossover point can be randomly selected

-> judge whether the success of phenotype decreases or increases

 

 

 


 

Code

 

code written in python3

__author__ = "fatkitty"


import numpy as np
from random import randint
import random

def RandBi():
    bi_X = ""  # binary string
    bi_X_arr = np.random.randint(2, size = 22)
    for i in bi_X_arr:
        bi_X += str(i)
    return bi_X

def Decimal(k):  #binary -> decimal
    return int(k, 2)

def Eval(k):
    return (-1.0 + k * 3 / 4194304)

bi_Dict = {}  # [binary code, evaluation] for each chromosomes
sorted_Dict = {}  # eval_Dict sorted by value [binary code, evaluation] in a descending order
selected_Dict = {}  # the top chromosomes selected
randomV = []


# Population - 100 sets of chromosomes
for i in range(100):
        globals()['v{}'.format(i)] = RandBi()
        globals()['eval_v{}'.format(i)] = Eval(Decimal(globals()['v{}'.format(i)]))
        bi_Dict['v%d'%(i + 1)] = [globals()['v{}'.format(i)], globals()['eval_v{}'.format(i)]]
        print("v%d = "%(i + 1), bi_Dict['v%d'%(i + 1)][0], "  ->   eval(v%d) ="%(i + 1), bi_Dict['v%d'%(i + 1)][1])

# initial sequence
sorted_Dict = sorted(bi_Dict.items(), key=lambda t : t[1], reverse = True) # for selection of top 50


# Selection - 50 of the fittest
# - select the subset with the best fitness values in order to act as parents for the next generation
def Selection():
    for i in range(100):
        selected_Dict[sorted_Dict[i][0]] = [sorted_Dict[i][1][0], sorted_Dict[i][1][1]]

    print ("\n-----  50 suitable chromosomes selected  -----")


def RandomV(): # select 2 random chromosomes for crossover
    # to avoid overlapping
    ranlist = list(selected_Dict)
    ran_num = random.sample(ranlist, 2)
    return ran_num


# Crossover - 50 sets and 1 child / 25 sets and 2 children
# - merge pairs from the selected parents to generate new offspring child solution
def Crossover(sel_r1, sel_r2, c):  # start the crossover starting from index of (crossover point + 1)
    i = randint(0, 21)  # crossover point
    sel_r1_cross = sel_r1[:i] + sel_r2[i:]
    sel_r2_cross = sel_r2[:i] + sel_r1[i:]

    # Mutation - very small probability
    # - child can have a feature that was not inherited from the parents
    # Here, we have set the mutation probability as 1%
    probR1 = randint(0, 100)
    probR2 = randint(0, 100)

    print("\n**-- crossover point :", i, "--**\n")

    if (probR1 == 0):
        print(randomV[0] + "* generate 1% mutation")
        m = randint(0, 21)  # mutation point
        result = (int(sel_r1_cross[m]) + 1) % 2
        # if sel_r1_cross[m] was originally 0, result = (0+1)%2, if it was 1 then result = (1+1)%2
        sel_r1_cross = sel_r1_cross[:m] + str(result) + sel_r1_cross[(m + 1):]
        print("**-- mutation point :", m, "--**    " + sel_r1_cross)

    if (probR2 == 0):
        print(randomV[1] + "* generate 1% mutation")
        m = randint(0, 21)  # mutation point
        result = (int(sel_r2_cross[m]) + 1) % 2
        sel_r2_cross = sel_r2_cross[:m] + str(result) + sel_r2_cross[(m + 1):]
        print("**-- mutation point :", m, "--**    " + sel_r2_cross)

    # evaluation of sel_r1_cross and sel_r2_cross
    eval_r1 = Eval(Decimal(sel_r1_cross))
    eval_r2 = Eval(Decimal(sel_r2_cross))
    
    globals()['v{}'.format(c)] = sel_r1_cross
    globals()['eval_v{}'.format(c)] = eval_r1
    bi_Dict['v%d'%(c+ 1)] = [globals()['v{}'.format(c)], globals()['eval_v{}'.format(c)]]
    c += 1
    globals()['v{}'.format(c)] = sel_r2_cross
    globals()['eval_v{}'.format(c)] = eval_r2
    bi_Dict['v%d'%(c + 1)] = [globals()['v{}'.format(c)], globals()['eval_v{}'.format(c)]]

    print (randomV[0]+"*", " : ", sel_r1_cross, "  ->   eval("+randomV[0]+'*) =', eval_r1)
    print (randomV[1]+"*", " : ", sel_r2_cross, "  ->   eval("+randomV[1]+'*) =', eval_r2)

    # decrease or increase
    if (Eval(Decimal(sel_r1)) > eval_r1):
        print ("=> ", randomV[0], "fitness decreased")
    elif (eval_r1 > Eval(Decimal(sel_r1))):
        print ("=> ", randomV[0], "fitness increased")
    elif (eval_r1 == Eval(Decimal(sel_r1))):
        print ("=> ", randomV[0], "fitness stays the same")

    if (Eval(Decimal(sel_r2)) > eval_r2):
        print ("     ", randomV[1], "fitness decreased")
    elif (eval_r2 > Eval(Decimal(sel_r2))):
        print ("     ", randomV[1], "fitness increased")
    elif (eval_r2 == Eval(Decimal(sel_r2))):
        print ("     ", randomV[1], "fitness stays the same")


def sort_child():  # sort the selected_Dict again in every generation
    sorted_Dict = sorted(bi_Dict.items(), key=lambda t : t[1], reverse = True)


if __name__ == "__main__":
    generation = 1  # initial population is the first generation

    while (generation < 6):  # terminate when generation hits 5
        print ("\n==============  %d th generation  =============="%generation)

        if (generation > 1):
            for i in range(100):
                # Override the top 50 in bi_Dict[0]~[49].
                #If not, then only the last 50 would keep being overridden by new children,
                # and the top 50 would stay the same throughout every generation.
                globals()['v{}'.format(i)] = sorted_Dict[i][1][0]
                globals()['eval_v{}'.format(i)] = sorted_Dict[i][1][1]
                bi_Dict['v%d'%(i + 1)] = [globals()['v{}'.format(i)], globals()['eval_v{}'.format(i)]]

        Selection()
        check = 50
        while (check < 99):
            randomV = RandomV() # 2 sets of chromosomes for Crossover
            # repeat crossover 25 times (25 * 2)
            Crossover(bi_Dict[randomV[0]][0], bi_Dict[randomV[1]][0], check)
            check += 2
        sort_child()
        print("\n")
        for i in range(100):
            print("v%d = "%(i + 1), bi_Dict['v%d'%(i + 1)][0], "  ->   eval(v%d) ="%(i + 1), bi_Dict['v%d'%(i + 1)][1])
        generation += 1

 


 

Result

 

초기 1세대부터 마지막 5세대에 이르기까지의 과정이 모두 출력됨.

아래는 random 결과의 마지막 세대에 관해 출력된 정보.

 

 

============== 5 th generation ==============

 

----- 50 suitable chromosomes selected -----

 

**-- crossover point : 1 --**

 

v69* : 0101111110110101111111 -> eval(v69*) = 0.1216118335723877

v18* : 1100011101110100101111 -> eval(v18*) = 1.3373749256134033

=> v69 fitness increased

v18 fitness decreased

 

**-- crossover point : 6 --**

 

v97* : 0000110000101111010101 -> eval(v97*) = -0.8572084903717041

v45* : 1001000101101100100011 -> eval(v45*) = 0.7041876316070557

=> v97 fitness decreased

v45 fitness increased

 

**-- crossover point : 9 --**

 

v14* : 1110011010111001111000 -> eval(v14*) = 1.7038211822509766

v48* : 1000011101111000010100 -> eval(v48*) = 0.5875387191772461

=> v14 fitness decreased

v48 fitness increased

 

**-- crossover point : 14 --**

 

v98* : 0000010011100010110100 -> eval(v98*) = -0.9427423477172852

v46* : 1000110010100000100001 -> eval(v46*) = 0.647972822189331

=> v98 fitness increased

v46 fitness decreased

 

**-- crossover point : 21 --**

 

v82* : 0010101110011010001001 -> eval(v82*) = -0.48903775215148926

v31* : 1100000111000101001001 -> eval(v31*) = 1.2707431316375732

=> v82 fitness stays the same

v31 fitness stays the same

 

**-- crossover point : 10 --**

 

v47* : 1000101100101000001011 -> eval(v47*) = 0.6307451725006104

v77* : 0011110110110100110111 -> eval(v77*) = -0.27687716484069824

=> v47 fitness decreased

v77 fitness increased

 

**-- crossover point : 10 --**

 

v32* : 1011111111000011110101 -> eval(v32*) = 1.2472455501556396

v49* : 1000000110010001110001 -> eval(v49*) = 0.5183913707733154

=> v32 fitness decreased

v49 fitness increased

 

**-- crossover point : 3 --**

 

v47* : 1000011011011010011101 -> eval(v47*) = 0.5803124904632568

v70* : 0100101100110100110111 -> eval(v70*) = -0.11867403984069824

=> v47 fitness decreased

v70 fitness increased

 

**-- crossover point : 1 --**

 

v49* : 1111010000110100001011 -> eval(v49*) = 1.8617632389068604

v5* : 1000000110000011110101 -> eval(v5*) = 0.5177533626556396

=> v49 fitness increased

v5 fitness decreased

 

**-- crossover point : 4 --**

 

v53* : 0000100011111010010000 -> eval(v53*) = -0.8947944641113281

v83* : 0010110000101111010101 -> eval(v83*) = -0.4822084903717041

=> v53 fitness decreased

v83 fitness increased

 

**-- crossover point : 0 --**

 

v58* : 1110101001101100101000 -> eval(v58*) = 1.7471599578857422

v8* : 1000110010100000100001 -> eval(v8*) = 0.647972822189331

=> v58 fitness increased

v8 fitness decreased

 

**-- crossover point : 12 --**

 

v39* : 1010000111000100110111 -> eval(v39*) = 0.8957302570343018

v47* : 1000101100110110000100 -> eval(v47*) = 0.6313810348510742

=> v39 fitness decreased

v47 fitness increased

 

**-- crossover point : 5 --**

 

v61* : 1000110100011001101000 -> eval(v61*) = 0.6535167694091797

v16* : 1110001100101000001011 -> eval(v16*) = 1.6619951725006104

=> v61 fitness increased

v16 fitness decreased

 

**-- crossover point : 16 --**

 

v84* generate 1% mutation

**-- mutation point : 8 --** 0010011110101100011001

v1* : 1111110110111101111110 -> eval(v1*) = 1.9735398292541504

v84* : 0010011110101100011001 -> eval(v84*) = -0.5350773334503174

=> v1 fitness increased

v84 fitness increased

 

**-- crossover point : 18 --**

 

v18* : 1101111110110101110001 -> eval(v18*) = 1.6216018199920654

v72* : 1000110010100000101111 -> eval(v72*) = 0.6479828357696533

=> v18 fitness decreased

v72 fitness increased

 

**-- crossover point : 3 --**

 

v93* : 0001010000110100001011 -> eval(v93*) = -0.7632367610931396

v5* : 1111100111101111100111 -> eval(v5*) = 1.9289371967315674

=> v93 fitness decreased

v5 fitness increased

 

**-- crossover point : 5 --**

 

v27* : 1100110011100010110100 -> eval(v27*) = 1.4010076522827148

v57* : 0000000011100011110111 -> eval(v57*) = -0.9895694255828857

=> v27 fitness increased

v57 fitness decreased

 

**-- crossover point : 11 --**

 

v60* : 1100000111001111100111 -> eval(v60*) = 1.2712223529815674

v82* : 1111100111100101001001 -> eval(v82*) = 1.9284579753875732

=> v60 fitness increased

v82 fitness decreased

 

**-- crossover point : 0 --**

 

v45* : 1001100001011101101111 -> eval(v45*) = 0.7855408191680908

v42* : 1001000000101111010101 -> eval(v42*) = 0.6896665096282959

=> v45 fitness increased

v42 fitness decreased

 

**-- crossover point : 4 --**

 

v14* : 1110111111000001101001 -> eval(v14*) = 1.809645414352417

v90* : 0001011011111000010100 -> eval(v90*) = -0.7308206558227539

=> v14 fitness increased

v90 fitness decreased

 

**-- crossover point : 6 --**

 

v37* : 1010111100110100110111 -> eval(v37*) = 1.0532009601593018

v47* : 1000100001101001101010 -> eval(v47*) = 0.5985865592956543

=> v37 fitness increased

v47 fitness decreased

 

**-- crossover point : 2 --**

 

v90* : 0010001000000011111000 -> eval(v90*) = -0.6013851165771484

v17* : 1101011011111000010100 -> eval(v17*) = 1.519179344177246

=> v90 fitness increased

v17 fitness decreased

 

**-- crossover point : 5 --**

 

v67* : 1111001100111101010000 -> eval(v67*) = 1.8504600524902344

v35* : 1011010000110100001011 -> eval(v35*) = 1.1117632389068604

=> v67 fitness decreased

v35 fitness increased

 

**-- crossover point : 11 --**

 

v22* : 1101001100111011000010 -> eval(v22*) = 1.475358486175537

v2* : 1111110101110110111010 -> eval(v2*) = 1.9702868461608887

=> v22 fitness increased

v2 fitness decreased

 

**-- crossover point : 6 --**

 

v44* : 1001001100110100110111 -> eval(v44*) = 0.7250759601593018

v47* : 1000100110010000110101 -> eval(v47*) = 0.6120984554290771

=> v44 fitness increased

v47 fitness decreased

 

 

v1 = 1111110110111101011001 -> eval(v1) = 1.9735133647918701

v2 = 1111110101111011000010 -> eval(v2) = 1.970475673675537

v3 = 1111100100101101110111 -> eval(v3) = 1.9200680255889893

v4 = 1111011011111111011011 -> eval(v4) = 1.8945047855377197

v5 = 1111010000110100001011 -> eval(v5) = 1.8617632389068604

v6 = 1110110010101010001011 -> eval(v6) = 1.7734148502349854

v7 = 1110101010010010001111 -> eval(v7) = 1.7488815784454346

v8 = 1110101001101100101000 -> eval(v8) = 1.7471599578857422

v9 = 1110101001101010000010 -> eval(v9) = 1.7470412254333496

v10 = 1110100111011001000110 -> eval(v10) = 1.7404065132141113

v11 = 1110100000010101100110 -> eval(v11) = 1.719738483428955

v12 = 1110011101111010001010 -> eval(v12) = 1.712623119354248

v13 = 1110011101011000011000 -> eval(v13) = 1.7110767364501953

v14 = 1110011011111000010100 -> eval(v14) = 1.706679344177246

v15 = 1110010100100111111101 -> eval(v15) = 1.685422658920288

v16 = 1110010100011001101000 -> eval(v16) = 1.6847667694091797

v17 = 1110001000000011111000 -> eval(v17) = 1.6486148834228516

v18 = 1101111110110101111111 -> eval(v18) = 1.6216118335723877

v19 = 1101110000101001001100 -> eval(v19) = 1.5800104141235352

v20 = 1101100111011101010110 -> eval(v20) = 1.5531010627746582

v21 = 1101010001000101111100 -> eval(v21) = 1.4875764846801758

v22 = 1101001100110110111010 -> eval(v22) = 1.4751696586608887

v23 = 1101000100010100000110 -> eval(v23) = 1.4501385688781738

v24 = 1100111101001000000111 -> eval(v24) = 1.4290821552276611

v25 = 1100101110100100101100 -> eval(v25) = 1.3864450454711914

v26 = 1100101101101011101100 -> eval(v26) = 1.383835792541504

v27 = 1100100011100011110111 -> eval(v27) = 1.3541805744171143

v28 = 1100100010011010010111 -> eval(v28) = 1.350816011428833

v29 = 1100010011111011111100 -> eval(v29) = 1.3084077835083008

v30 = 1100001101111010010110 -> eval(v30) = 1.2907567024230957

v31 = 1100000111000101001001 -> eval(v31) = 1.2707431316375732

v32 = 1011111111010001110001 -> eval(v32) = 1.2478835582733154

v33 = 1011111110000101111111 -> eval(v33) = 1.2444145679473877

v34 = 1011111101101111111000 -> eval(v34) = 1.2434024810791016

v35 = 1011001100111101010000 -> eval(v35) = 1.1004600524902344

v36 = 1011001100010000111111 -> eval(v36) = 1.0984337329864502

v37 = 1010110001101001101010 -> eval(v37) = 1.0204615592956543

v38 = 1010011101101101110010 -> eval(v38) = 0.9620566368103027

v39 = 1010000111000110000100 -> eval(v39) = 0.8957853317260742

v40 = 1010000011110010001101 -> eval(v40) = 0.88608717918396

v41 = 1001110010001101001011 -> eval(v41) = 0.8345873355865479

v42 = 1001100001011101101111 -> eval(v42) = 0.7855408191680908

v43 = 1001000111111101000101 -> eval(v43) = 0.710803747177124

v44 = 1001000110010000110101 -> eval(v44) = 0.7058484554290771

v45 = 1001000000101111010101 -> eval(v45) = 0.6896665096282959

v46 = 1000110010100010110100 -> eval(v46) = 0.6480779647827148

v47 = 1000101100110100110111 -> eval(v47) = 0.6313259601593018

v48 = 1000011100111001111000 -> eval(v48) = 0.5846805572509766

v49 = 1000000110000011110101 -> eval(v49) = 0.5177533626556396

v50 = 0111110000101001010100 -> eval(v50) = 0.4550161361694336

v51 = 0101111110110101111111 -> eval(v51) = 0.1216118335723877

v52 = 1100011101110100101111 -> eval(v52) = 1.3373749256134033

v53 = 0000110000101111010101 -> eval(v53) = -0.8572084903717041

v54 = 1001000101101100100011 -> eval(v54) = 0.7041876316070557

v55 = 1110011010111001111000 -> eval(v55) = 1.7038211822509766

v56 = 1000011101111000010100 -> eval(v56) = 0.5875387191772461

v57 = 0000010011100010110100 -> eval(v57) = -0.9427423477172852

v58 = 1000110010100000100001 -> eval(v58) = 0.647972822189331

v59 = 0010101110011010001001 -> eval(v59) = -0.48903775215148926

v60 = 1100000111000101001001 -> eval(v60) = 1.2707431316375732

v61 = 1000101100101000001011 -> eval(v61) = 0.6307451725006104

v62 = 0011110110110100110111 -> eval(v62) = -0.27687716484069824

v63 = 1011111111000011110101 -> eval(v63) = 1.2472455501556396

v64 = 1000000110010001110001 -> eval(v64) = 0.5183913707733154

v65 = 1000011011011010011101 -> eval(v65) = 0.5803124904632568

v66 = 0100101100110100110111 -> eval(v66) = -0.11867403984069824

v67 = 1111010000110100001011 -> eval(v67) = 1.8617632389068604

v68 = 1000000110000011110101 -> eval(v68) = 0.5177533626556396

v69 = 0000100011111010010000 -> eval(v69) = -0.8947944641113281

v70 = 0010110000101111010101 -> eval(v70) = -0.4822084903717041

v71 = 1110101001101100101000 -> eval(v71) = 1.7471599578857422

v72 = 1000110010100000100001 -> eval(v72) = 0.647972822189331

v73 = 1010000111000100110111 -> eval(v73) = 0.8957302570343018

v74 = 1000101100110110000100 -> eval(v74) = 0.6313810348510742

v75 = 1000110100011001101000 -> eval(v75) = 0.6535167694091797

v76 = 1110001100101000001011 -> eval(v76) = 1.6619951725006104

v77 = 1111110110111101111110 -> eval(v77) = 1.9735398292541504

v78 = 0010011110101100011001 -> eval(v78) = -0.5350773334503174

v79 = 1101111110110101110001 -> eval(v79) = 1.6216018199920654

v80 = 1000110010100000101111 -> eval(v80) = 0.6479828357696533

v81 = 0001010000110100001011 -> eval(v81) = -0.7632367610931396

v82 = 1111100111101111100111 -> eval(v82) = 1.9289371967315674

v83 = 1100110011100010110100 -> eval(v83) = 1.4010076522827148

v84 = 0000000011100011110111 -> eval(v84) = -0.9895694255828857

v85 = 1100000111001111100111 -> eval(v85) = 1.2712223529815674

v86 = 1111100111100101001001 -> eval(v86) = 1.9284579753875732

v87 = 1001100001011101101111 -> eval(v87) = 0.7855408191680908

v88 = 1001000000101111010101 -> eval(v88) = 0.6896665096282959

v89 = 1110111111000001101001 -> eval(v89) = 1.809645414352417

v90 = 0001011011111000010100 -> eval(v90) = -0.7308206558227539

v91 = 1010111100110100110111 -> eval(v91) = 1.0532009601593018

v92 = 1000100001101001101010 -> eval(v92) = 0.5985865592956543

v93 = 0010001000000011111000 -> eval(v93) = -0.6013851165771484

v94 = 1101011011111000010100 -> eval(v94) = 1.519179344177246

v95 = 1111001100111101010000 -> eval(v95) = 1.8504600524902344

v96 = 1011010000110100001011 -> eval(v96) = 1.1117632389068604

v97 = 1101001100111011000010 -> eval(v97) = 1.475358486175537

v98 = 1111110101110110111010 -> eval(v98) = 1.9702868461608887

v99 = 1001001100110100110111 -> eval(v99) = 0.7250759601593018

v100 = 1000100110010000110101 -> eval(v100) = 0.6120984554290771

 

 

점수 100/100

반응형