본문 바로가기
프로젝트/딥러닝

딥러닝 LSTM을 활용한 로또번호 생성 및 예측하기

by 꿈돌이. 2022. 3. 30.

몇 달전, AI교육 프로그램에서 '인공지능을 활용한 로또번호 생성 및 예측' 수업을 받은 적이 있었다. LSTM이라는 딥러닝 기법을 활용해서 6자리의 로또 번호를 예측하는 것인데, 로또를 구매해본 사람으로써 흥미로운 수업이였기 때문에 이번 프로젝트를 공부할겸 유튜브를 참고하여 작성해보았다. 그럼 데이터 수집부터 로또번호 생성 및 예측까지 자세히 알아보도록 하자.

 

 

 

1. 개요

수 많은 데이터 사이에 나타나는 일정한 패턴이 있다. 일정 시간 간격으로 배치된 같은 형식의 데이터들의 열(sequence)을 ‘시계열 데이터’라고 하는데, 어떤 법칙에서 생성되어 나오는지를 파악한 뒤에 그 내용을 분석해서 이해하고, 분석한 것을 바탕으로 데이터의 특성과 패턴을 찾아 활용해야 한다. 딥러닝에는 순환 신경망 즉, RNN (Recurrent Neural Network)에 기법 중 LSTM(Long Short-Term Memory)라는 장단기 메모리를 활용하여, 로또 당첨번호를 예측한다. 1회차부터 현재까지 로또 당첨번호를 수집하고, 그 다음 로또 번호를 예측할 수 있는 시스템을 LSTM 기법으로 구현한다.

 

2. 개발환경

  • 개발 언어 : Python
  • 주요 패키지 : Tensorflow, Keras, Numpy
  • Google Colab
  • 데이터셋 : 로또 회차별 당첨번호 

 

3. 데이터 수집

데이터 수집을 위해, 동행복권 사이트에서 1회차부터 최근 회차까지 로또 당첨번호를 엑셀 파일로 다운로드 받는다. 다운로드 받은 파일은 실습환경에 사용하기 좋게 scv 파일로 변환한다.

 

4. 데이터 전처리

구글 코랩 환경에서 프로젝트를 진행하였고, 데이터 전처리를 위해 코랩에서 다운로드 받은 scv 파일을 업로드한다. 그리고 원핫인코딩(One-Hot Encoding) 작업을 수행한다.

 

# 당첨번호를 원핫인코딩(ohe)으로 변환
def numbers_ohe(numbers):

    ohe = np.zeros(45) #45개의 빈공간 생성

    for i in range(6): #반복할 6개의 번호
        ohe[int(numbers[i])-1] = 1 #로또번호가 1부터 시작하지만, 인덱스 시작은 0부터 시작하므로 -1뺀다.
    
    return ohe

# 원핫인코딩(ohe)를 번호로 변환
def ohe_numbers(ohe):

    numbers = []
    
    for i in range(len(ohe)):
   
        if ohe[i] == 1.0:  #1.0으로 설정되어 있으면 해당 번호를 반환값에 추가
            numbers.append(i+1)
    
    return numbers

 

 

원핫인코딩과 번호를 출력하면 다음과 같은 결과가 나타난다.

numbers = rows[:, 1:7]
ohes = list(map(numbers_ohe, numbers))

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

# 원핫인코딩으로 출력
print(("ohes"))
print("X[0] : " + str(x_samples[0]))
print("Y[0] : " + str(y_samples[0]))

# 번호로 출력
print("numbers")
print("X[0] : " + str(ohe_numbers(x_samples[0])))
print("Y[0] : " + str(ohe_numbers(y_samples[0])))

 

5. 모델 구성 및 학습

데이터셋을 구성하기 위해 훈련셋과 검증셋, 시험셋으로 분리하여 1회차부터 1004회차 당첨번호를 나눠준다. 훈련셋에 가장 많은 데이터를 부여한 후 검증셋과 시험셋에 각각 데이터를 부여한다.

 

# 데이터셋 구성 - 훈련셋, 검증셋, 시험셋으로 분리
train_idx = (0, 800)   # 훈련셋 800회
val_idx = (801, 950)   # 검증셋 150회
test_idx = (951, len(x_samples))   # 시험셋 53회

 

LSTM 모델을 생성하고, 모델을 컴파일한다. 주요 라이브러리인 Tensorflow와 Keras를 사용한다. 에포크를 최대 100회 수행하여 학습시킨다. (0~99회)

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

# LSTM 모델을 생성
model = keras.Sequential([
    keras.layers.LSTM(128, batch_input_shape=(1, 1, 45), return_sequences=False, stateful=True),
    keras.layers.Dense(45, activation='sigmoid')
])
# 매 에포크마다 훈련과 검증의 손실 및 정확도를 기록하기 위한 변수
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)))

 

예측한 당첨번호로 얼마의 상금을 받을 수 있는지 평균값을 구한다.

# 100회차부터 최근 회차까지 1~5등 상금의 평균을 낸다.
mean_reward = [ np.mean(rows[99:, 8]),
              np.mean(rows[99:, 9]),
              np.mean(rows[99:, 10]),
              np.mean(rows[99:, 11]),
              np.mean(rows[99:, 12])]

 

6. 모델 평가

1회차 당첨번호를 모델에 입력하고, 총 10번의 당첨번호를 생성한 뒤 2회차와 비교하여 등수와 상금을 계산한다. 마찬가지로 2회차 당첨번호를 모델에 입력하고, 10번의 당첨번호를 생성한 뒤 3회차와 비교하여 계산한다. 이런식으로 1003회차까지 진행시킨다.

 

train_total_reward = []
train_total_grade = np.zeros(6, dtype=int)

val_total_reward = []
val_total_grade = np.zeros(6, dtype=int)

test_total_reward = []
test_total_grade = np.zeros(6, dtype=int)

model.reset_states()

print('[No. ] 1등 2등 3등 4등 5등 6등 Rewards')

for i in range(len(x_samples)):
    xs = x_samples[i].reshape(1, 1, 45)
    ys_pred = model.predict_on_batch(xs)   # 모델의 출력값을 얻음
    
    sum_reward = 0
    sum_grade = np.zeros(6, dtype=int)   # 6등까지 변수

    for n in range(10):   # 10번 수행
        numbers = gen_numbers_from_probability(ys_pred[0])
        
        # i회차 입력 후 나온 출력을 i+1회차와 비교
        grade, reward = calc_reward(rows[i+1,1:7], rows[i+1,7], numbers) 
        
        sum_reward += reward
        sum_grade[grade] += 1

        if i >= train_idx[0] and i < train_idx[1]:
            train_total_grade[grade] += 1
        elif i >= val_idx[0] and i < val_idx[1]:
            val_total_grade[grade] += 1
        elif i >= test_idx[0] and i < test_idx[1]:
            val_total_grade[grade] += 1
    
    if i >= train_idx[0] and i < train_idx[1]:
        train_total_reward.append(sum_reward)
    elif i >= val_idx[0] and i < val_idx[1]:
        val_total_reward.append(sum_reward)
    elif i >= test_idx[0] and i < test_idx[1]:
        test_total_reward.append(sum_reward)
                        
    print('[{0:4d}] {1:3d} {2:3d} {3:3d} {4:3d} {5:3d} {6:3d} {7:15,d}'.format(i+1, sum_grade[0], sum_grade[1], sum_grade[2], sum_grade[3], sum_grade[4], sum_grade[5], int(sum_reward)))

print('Total') 
print('==============================')    
print('Train {0:5d} {1:5d} {2:5d} {3:5d} {4:5d} {5:5d} {6:15,d}'.format(train_total_grade[0], train_total_grade[1], train_total_grade[2], train_total_grade[3], train_total_grade[4], train_total_grade[5], int(sum(train_total_reward))))
print('Val   {0:5d} {1:5d} {2:5d} {3:5d} {4:5d} {5:5d} {6:15,d}'.format(val_total_grade[0], val_total_grade[1], val_total_grade[2], val_total_grade[3], val_total_grade[4], val_total_grade[5], int(sum(val_total_reward))))
print('Test  {0:5d} {1:5d} {2:5d} {3:5d} {4:5d} {5:5d} {6:15,d}'.format(test_total_grade[0], test_total_grade[1], test_total_grade[2], test_total_grade[3], test_total_grade[4], test_total_grade[5], int(sum(test_total_reward))))
print('==============================')

 

훈련셋 구간인 800회차까지는 기가 막힐정도로 1등 당첨이 되지만, 아래 사진에서 알 수 있듯이 800회차가 넘어가는 순간 1등은 커녕 5등도 겨우 당첨된다는 것을 확인할 수 있다.

 

7. 로또 당첨번호 예측

학습한 모델을 이용해서 로또 당첨번호를 생성한다. 총 10개의 번호를 부여받아 로또 당첨을 예측해본다. (0~9회)

 

# 학습한 모델로 다음 회차 번호예측

print('receive numbers')

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

ys_pred = model.predict_on_batch(xs)

list_numbers = []

for n in range(10):
    numbers = gen_numbers_from_probability(ys_pred[0])
    numbers.sort()
    print('{0} : {1}'.format(n, numbers))
    list_numbers.append(numbers)

 

끝내며. . .

모델 평가 부분에서 확인하였지만, 딥러닝을 사용한다고 해도 당첨번호를 예측하기가 어렵다. 그렇지만 실제로 부여받은 번호를 써먹어볼 수 있기 때문에 흥미로운 프로젝트였다고 생각한다. 내일은 딥러닝을 돌려서 나온 당첨번호로 재미삼아 로또를 구매해봐야겠다.

댓글