본문 바로가기
Programming/AI

[딥러닝] 로또 번호 예측하기 - 쓸데없는 개발일지

by 코딩하는 금융인 2021. 7. 31.

안녕하세요.

오늘은 오로지 재미로 접근한 주제 : 딥러닝으로 로또 번호 예측하기입니다.


본론에 앞서, 로또 번호 1등에 당첨될 확률은 동전을 23번 던져 모든 면을 맞추는 확률과 거의 동일합니다.

(814,5060분의 1 ≒ 2의 23제곱 분의 1)

 

또한, 로또 번호는 동전 던지기와 똑같이 독립시행이므로 예측 불가의 영역입니다.

( 독립시행 : 같은 조건에서 반복 가능하며, 매번 결과가 달라질 수 있는 사건들을 말한다.

                일어나는 하나의 사건이 다음 사건의 시행 결과에 영향을 주지 않는 것)

 

즉, 스팸 문자나 이메일로 유료 로또 예측 서비스를 광고하는 업체들이 말하는 1등 당첨번호는 분명한 사기입니다.

 

하면, 로또 명당이나 해당 사이트에서 광고하는 많은 1등 당첨자의 근거는 무엇일까? (Why?)

이유는 간단합니다. 시행하는 횟수가 많기 때문입니다. 다시 말해서, 해당 사이트나 로또 명당을 이용하는 사람들이 많아서 당첨자도 자연스레 많아진 것입니다.

 

Q. 동전을 10번 던질 때 앞면이 많이 나올까요? 1000번 던질 때 앞면이 많이 나올까요?

-> 당연히 후자입니다. 표본수(N)가 커지면, 당연히 확률은 올라가는건 산수입니다.

 

※ 해당 주제는 유료 서비스에 대한 상업적 광고 목적이 아닌 공부에 목적이 있음을 밝힙니다.


프로젝트 순서

데이터 수집 > 전처리 및 가공 > 딥러닝 모델 구성 및 학습 > 모델 평가 > 실제 로또 구매

 

1. 데이터 수집 및 전처리

아래의 사이트에서 972회차까지의 로또 당첨 번호와 상금을 다운로드해줍니다.

동행복권 사이트 : https://dhlottery.co.kr/gameResult.do?method=byWin 

 

▣ 데이터 확인

필요한 Column : 회차, 당첨번호, 등수별 당첨금액

당첨금액은 오로지 숫자로만 수정 (원 빼고 특성 숫자로 변경)

 

▣ 필요 데이터 추출

수정된 파일(csv)

▣ 원핫인코딩벡터로 변환 for 딥러닝

# 데이터 다운로드
import numpy as np
rows = np.loadtxt("./lottery.csv", delimiter=",")
row_count = len(rows)

# 당첨번호를 원핫인코딩벡터(ohbin)으로 변환
def numbers2ohbin(numbers):

    ohbin = np.zeros(45) #45개의 빈 칸을 만듬

    for i in range(6): #여섯개의 당첨번호에 대해서 반복함
        ohbin[int(numbers[i])-1] = 1 #로또번호가 1부터 시작하지만 벡터의 인덱스 시작은 0부터 시작하므로 1을 뺌
    
    return ohbin

# 원핫인코딩벡터(ohbin)를 번호로 변환
def ohbin2numbers(ohbin):

    numbers = []
    
    for i in range(len(ohbin)):
        if ohbin[i] == 1.0: # 1.0으로 설정되어 있으면 해당 번호를 반환값에 추가한다.
            numbers.append(i+1)
    
    return numbers
    
numbers = rows[:, 1:7]
ohbins = list(map(numbers2ohbin, numbers))

x_samples = ohbins[0:row_count-1]
y_samples = ohbins[1:row_count]

 

2. 딥러닝 구성 및 학습

- 데이터 나누기

Train : 1 ~ 800회 / Validation : 801 ~ 900회 / Test : 901 ~ 972회

# 데이터 나누기
train_idx = (0, 800)
val_idx = (801, 900)
test_idx = (901, len(x_samples))

 

딥러닝 모델 LSTM을 사용하겠습니다.

LSTM은 기존의 RNN을 개선한 모델로, 간단하게 로또에 적용해본다면 이전의 당첨번호가 다음의 당첨번호에 영향을 준다는 가정을 바탕으로 모델을 만들어줍니다. (주로, 주가예측이나 부동산 매매가격 예측에 사용됨)

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras import models

# 모델을 정의합니다.
model = keras.Sequential([
    keras.layers.LSTM(128, batch_input_shape=(1, 1, 45), return_sequences=False, stateful=True),
    keras.layers.Dense(45, activation='sigmoid')
])

# 모델을 컴파일합니다.
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

# 매 에포크마다 훈련과 검증의 손실 및 정확도를 기록하기 위한 변수
train_loss = []
train_acc = []
val_loss = []
val_acc = []

# 최대 100번 에포크까지 수행
for epoch in range(100):

    model.reset_states() # 중요! 매 에포크마다 1회부터 다시 훈련하므로 상태 초기화 필요

    batch_train_loss = []
    batch_train_acc = []
    
    for i in range(train_idx[0], train_idx[1]):
        
        xs = x_samples[i].reshape(1, 1, 45)
        ys = y_samples[i].reshape(1, 45)
        
        loss, acc = model.train_on_batch(xs, ys) #배치만큼 모델에 학습시킴

        batch_train_loss.append(loss)
        batch_train_acc.append(acc)

    train_loss.append(np.mean(batch_train_loss))
    train_acc.append(np.mean(batch_train_acc))

    batch_val_loss = []
    batch_val_acc = []

    for i in range(val_idx[0], val_idx[1]):

        xs = x_samples[i].reshape(1, 1, 45)
        ys = y_samples[i].reshape(1, 45)
        
        loss, acc = model.test_on_batch(xs, ys) #배치만큼 모델에 입력하여 나온 답을 정답과 비교함
        
        batch_val_loss.append(loss)
        batch_val_acc.append(acc)

    val_loss.append(np.mean(batch_val_loss))
    val_acc.append(np.mean(batch_val_acc))

    print('epoch {0:4d} train acc {1:0.3f} loss {2:0.3f} val acc {3:0.3f} loss {4:0.3f}'.format(epoch, np.mean(batch_train_acc), np.mean(batch_train_loss), np.mean(batch_val_acc), np.mean(batch_val_loss)))

 

3. 모델 평가

epoch이 늘어날 때마다 train의 accuracy는 높아지고 loss는 낮아지는 걸 볼 수 있으나 validation(검증 데이터)는 그와 반대로 움직임.

 

즉, 학습 데이터에서는 정확성이 계속 올라가지만, 새로운 데이터에서는 모델의 효용성이 많이 떨어짐을 알 수 있음.

 

∴ 딥러닝 모델이 로또 데이터에서는 크게 의미가 없다.. ㅋㅋㅋ

 

4. 실제 로또 구매

딥러닝 모델을 바탕으로 번호를 뽑아주는 함수를 만들어주고 실제로 5번 번호를 뽑아봤습니다.

# 번호 뽑기
def gen_numbers_from_probability(nums_prob):

    ball_box = []

    for n in range(45):
        ball_count = int(nums_prob[n] * 100 + 1)
        ball = np.full((ball_count), n+1) #1부터 시작
        ball_box += list(ball)

    selected_balls = []

    while True:
        
        if len(selected_balls) == 6:
            break
        
        ball_index = np.random.randint(len(ball_box), size=1)[0]
        ball = ball_box[ball_index]

        if ball not in selected_balls:
            selected_balls.append(ball)

    return selected_balls
    
print('receive numbers')

xs = x_samples[-1].reshape(1, 1, 45)

ys_pred = model.predict_on_batch(xs)

list_numbers = []

for n in range(5):
    numbers = gen_numbers_from_probability(ys_pred[0])
    numbers.sort()
    print('{0} : {1}'.format(n, numbers))
    list_numbers.append(numbers)
receive numbers
0 : [1, 3, 7, 8, 33, 44]
1 : [15, 17, 24, 33, 35, 37]
2 : [1, 14, 15, 20, 26, 35]
3 : [6, 10, 16, 30, 33, 40]
4 : [16, 17, 21, 37, 39, 43]

재밋는 비교를 위해 파이썬 random 함수를 이용하여 간단하게 로또 번호 자동생성기를 만들어보았습니다.

import random
for i in range(5):
    lotto = random.sample(range(1,46),6)
    lotto.sort()
    print("자동 로또 번호는 ", lotto)
자동 로또 번호는 [7, 14, 17, 25, 33, 40]
자동 로또 번호는 [2, 3, 12, 28, 32, 43]
자동 로또 번호는 [1, 6, 10, 19, 28, 33]
자동 로또 번호는 [12, 25, 36, 39, 43, 45]
자동 로또 번호는 [1, 7, 21, 29, 31, 38]

당첨결과를 보시면... 5천원도 당첨이 안된 것을 확인할 수 있습니다.

이렇게 재미로만 접근하시는 걸 추천드리고 포스팅을 마치겠습니다.

 

References
반응형

댓글