====== ロト6予想 ======
MLPのお題としてロト6の番号予想を行ないます。ただし、限りなくランダムに近いものであり、有意な法則はないはずですので、有用なものは作れないです。
===== データの用意 =====
過去の当選番号をまとめているサイトが幾つかあります、
csv形式で公開しているものが使いやすいので、下記のサイトからダウンロードしました。
再配布は禁止されているので、直接ダウンロードしてください。
* [[https://loto6.thekyo.jp/download/index|ロト6 ダウンロード(Excel/Excel+VBA/CSV) (KYO's LOTO)]]
===== 学習コード =====
上記のcsvファイルを、loto6.csvとして配置し、実行します。
import torch
import random
from torch.utils.data import DataLoader
import numpy as np
import pandas as pd
# Loto6.csvを読み込む
loto6_file_path = 'loto6.csv'
df = pd.read_csv(loto6_file_path)
# 独自のデータセットクラス
class MyDataset(torch.utils.data.Dataset):
def __init__(self, df):
self._df = df
def __len__(self):
# 入力データと正解データはDataFrameの直近のペアで組み合わせるので、
# データ数はDataFrameの総行数-1となる
return len(self._df) - 1
def __getitem__(self, idx):
input_data = df.iloc[idx, 2:9].values.astype(int)
# 1origin to 0origin
input_data = input_data - 1
target_size = 43
input_data_onehot = np.eye(target_size)[input_data]
# axisはより外側の軸が小さい値になる
input_data_onehot = input_data_onehot.sum(axis = 0)
# 正解データにはボーナス数字は含まない
output_data = df.iloc[idx+1, 2:8].values.astype(int)
# 1origin to 0origin
output_data = output_data - 1
target_size = 43
output_data_onehot = np.eye(target_size)[output_data]
# axisはより外側の軸が小さい値になる
output_data_onehot = output_data_onehot.sum(axis = 0)
input_tensor = torch.FloatTensor(input_data_onehot)
output_tensor = torch.FloatTensor(output_data_onehot)
return input_tensor, output_tensor
# DNNモデル(多層パーセプトロン)
class MLPR(torch.nn.Module):
def __init__(self, n_input, n_hidden, n_output):
super(MLPR, self).__init__()
self.l1 = torch.nn.Linear(n_input, n_hidden)
self.l2 = torch.nn.Linear(n_hidden, n_hidden)
self.l3 = torch.nn.Linear(n_hidden, n_hidden)
self.l4 = torch.nn.Linear(n_hidden, n_output)
def forward(self, x):
h1 = self.l1(x)
h2 = torch.relu(h1)
h3 = self.l2(h2)
h4 = torch.relu(h3)
h5 = self.l3(h4)
h6 = torch.relu(h5)
h7 = self.l4(h6)
h8 = torch.sigmoid(h7)
return h8
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print('using device:', device)
myds = MyDataset(df)
train_loader = DataLoader(myds, batch_size=200, shuffle=False)
model = MLPR(43, 500, 43)
# 二値分類なので交差エントロピーを損失関数に使う
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
model.to(device)
criterion.to(device)
# 学習プロセス
# 学習が進みに応じた損失関数の変化を見る。検証プロセスは無し。
from torch.autograd import Variable
loss_history = []
for epoch in range(1000):
total_loss = 0
for x_train, y_train in train_loader:
# Variableは微分機能内包
x_train = Variable(x_train)
y_train = Variable(y_train)
optimizer.zero_grad()
x_train = x_train.to(device)
y_train = y_train.to(device)
y_pred = model(x_train)
loss = criterion(y_pred, y_train)
loss.backward()
optimizer.step()
total_loss += loss.item()
loss_history.append(total_loss)
if (epoch +1) % 100 == 0:
print(epoch + 1, total_loss)
torch.save(model.state_dict(), 'loto6_model_weights.pth')
===== 学習結果 =====
直前の回の当選番号を元に今回の当選番号を導くための学習をさせます。
各番号をone-hot-vector形式で指定し、0~1の範囲で出力します。
データ総数は2007で、バッチサイズは200なので、
最大の損失は43*2007/200=430くらいになります。
using device: cpu
100 243.7310848236084
200 241.97664260864258
300 237.96832656860352
400 232.6889190673828
500 228.08361434936523
600 223.40085792541504
700 219.68487739562988
800 217.33679389953613
900 213.1663055419922
1000 212.26333236694336
大体240くらいから始まって、210くらいに収束します。
エポックを重ねればさらに収束していきますが、210あたりでだいぶ速度が遅くなります。
過学習のフェーズに入っていると思われます。
===== 推論 =====
上記で作成したモデルのパラメータは、"loto6_model_weights.pth"というファイルで保存しています。
これを用いて推論させます。
毎回学習させても良いのですが、それなりに時間が掛かるので、
学習済モデルを使う方が速いです。
* {{ :loto6_model_weights.zip |モデルパラメータファイル}}
model = MLPR(43, 500, 43)
model.load_state_dict(torch.load('loto6_model_weights.pth'))
model.to(device)
# 直前の当選番号を入力値として用意する
input_data = np.array([2, 4, 5, 6, 19, 39, 42])
# 0-originに変換する
input_data_0origin = input_data - 1
# one-hot-vectorに変換する
target_size = 43
input_data_onehot = np.eye(target_size)[input_data_0origin].sum(axis=0)
# Pytorch tensor型に変換する
input_tensor = torch.FloatTensor(input_data_onehot)
# バッチ処理用の次元を追加する
input_tensor = input_tensor.unsqueeze(0)
# デバイスに転送する(GPU)
input_tensor = input_tensor.to(device)
# 推論する(学習プロセスではないので勾配は計算しない)
with torch.no_grad():
prediction = model(input_tensor)
# バッチ処理用の次元を取り除く
prediction_np = prediction.squeeze(0).cpu().numpy()
# 確率の高い上位6つの値を取り出す
predicted_indices = np.argsort(prediction_np)[-6:][::-1]
# 0 originから 1 originに戻す
predicted_numbers = predicted_indices + 1
print("Predicted numbers:", predicted_numbers)