AI技術で宝くじ当選番号を予測、億万長者の夢を実現する?

概要

本稿はある数学教授の宝くじ当選の物語を紹介してから、Pythonを使ってNUMBERS3過去25年分の当選番号を取得するクローラーを実現した上、LSTMアルゴリズムで次回の当選番号を予測するプログラムを実現します。

1. 数学教授の宝くじ当選伝説

「宝くじにあたったら、何をするか」と考えた人が多いでしょう。「流行っているAIが強そうですが、明日の当選番号を正しく予測してもらえるか」と考えている方もいるでしょう。それは実現可能かどうか、一緒に見てみましょう。

本題に入る前に、数学教授が何度も宝くじ当選の話を紹介します。

宝くじ当選番号はランダムですので、研究しても運がないと当たらないと思いますが、ある数学者は宝くじを4回も的中させ、世界中に大きな話題になりました。

賞金金額は下記の通り。

第一回: 540万ドル
第二回: 200万ドル
第三回: 300万ドル
第四回:1000万ドル


合 計:2040万ドル

Joan R Ginther

この教授はスタンフォード大学の統計学博士、数学教授を務めているJoan R Gintherです。統計学者ですので、メデイアは統計学の知識を使っていたと断言しています。本当に統計学の知識活用なのか、教授の幸運なのかわかりませんが、宝くじはそもそも統計学の原理をもとに作ったものですね。

2. 数字選択型の宝くじ

日本国内の数字選択型の宝くじはBINGO5、LOTO7、LOTO6、MINI LOTO、NUMBERS3、NUMBERS4などあります。

Lottery

本稿は数字の数が一番少ないNUMBERS3を例として説明します。数字の数が多く、ルールがもっと複雑なLOTOシーリズも似ています。

NUMBERS3は1994年10月7日から始まり、週1回抽選でしたが、2004年7月1日より毎日(営業日)抽選になりました。0〜9の数字を3桁を選んで、1口200円かかります。

NUMBER3宝くじのルールは下記の通りです。

No.購入タイプルール
1ストレート数字、順序完全一致
2ボックス数字一致
3セット1と2半々セット購入
4ミニ後2桁数字、顺序一致

3. Pythonを使って過去の当選番号スクレイピング

基礎データがなければ、機械学習、ディープランニングもできないです。予測するにあたって最初やらないといけないのは、過去の当選番号を取得することです。

みずほ銀行の宝くじページは1回目からすべてのデータを公開しています。
https://www.mizuhobank.co.jp/retail/takarakuji/numbers/backnumber/index.html

当選番号掲載ページの構造を分析して、2009年10月7日(第2700回)を境目として、前後のデータ提供方式が変わります。前者はHTMLしかないですが、後者はAPIを利用してCSVフォーマットのデータを取れます。両方が取れる情報量も異なります。前者は当選番号しかないことに対して、後者は詳細なデータ、例えば、金額、人数なども取れます。

余計な話をやめて、ソースを見ましょう。

3-1. 宝くじClass

プログラムは簡単ですが、このクラスはいらないかもしれません。Pythonを勉強するため、できるだけ多くの要素を入れています。

import requests
import csv
import re
import datetime

class Lottery:
    def __init__(self):
        self.index = ""     # 回数
        self.date = ""      # 抽選日
        self.num1 = ""      # 数字1
        self.num2 = ""      # 数字2
        self.num3 = ""      # 数字3

3-2. スクローラーClass

スクローラークラスはデータ取得、CSVファイル保存、及び日付変換関数を含めています。処理詳細はコメントを参照ください。

class Crawler:

    # 初期化
    def __init__(self, old_url, new_url):
        self.old_url = old_url
        self.new_url = new_url

    # データ取得
    def get_data(self, begin_number, end_number):
        data = []

        # 旧フォーマット
        # 略

        # 新フォーマット
        if begin_number > 2700:
            for index in range(begin_number, end_number+1):
                print(index)
                new_data = self._get_new_data(index, self.new_url)
                data.append(new_data)

        return data

    # 旧フォーマット
    def _get_old_data(self, index, url):
        # 略
        pass

    # 新フォーマット
    def _get_new_data(self, index, url):
        url = url.format(index)
        result = requests.get(url)
        result.encoding = "Shift_JIS"
        lines = result.text.splitlines()

        lot = Lottery()

        # 回数
        lot.index = index

        # 抽選日
        record = lines[1].split(",")[2]
        lot.date = self._get_date_from_string_jp(record)

        # 番号
        num = lines[3].split(",")[1]
        lot.num1 = num[0]
        lot.num2 = num[1]
        lot.num3 = num[2]

        return lot

    # 和暦から日付取得
    def _get_date_from_string_jp(self, str):
        pattern = r'([0-9]+)'
        ymd = re.findall(pattern, str)
        if str[0:2] == '平成':
            year = int(ymd[0]) + 1988
        elif str[0:2] == '令和':
            year = 2019
        else:
            raise "error"
        date = datetime.date(year, int(ymd[1]), int(ymd[2]))
        return date

    # CSVファイル保存
    def save(self, file_name, columns, data):

        # ファイルを開く
        f = open(file_name, 'w')
        writer = csv.writer(f)

        # タイトル
        writer.writerow(columns)

        # 当選番号
        for datum in data:
            writer.writerow([datum.index, datum.date, datum.num1, datum.num2, datum.num3])

        # ファイルを閉じる
        f.close()

3-3. メインプログラム

スクローラークラスを呼び出して、データスクレイピングを行いCSVファイルに保存します。
ウェブサイトにご迷惑をかけないよう、2009年10月7日(第2700回)以降の取得処理のみ掲載します。また、取得API URLも公開しなくて、必要であれば、お問合せください。過去のデータはここからダウンロード可能です。

if __name__ == '__main__':

    old_url = ""
    # ウェブサイトに負荷をかからないよう、API URLを本サイト内のお問合せフォームを利用してお問合せください
    new_url = "*******"

    # スクローラーインスタンス生成
    crawler = Crawler(old_url, new_url)

    # データ取得(開始回数、終了回数)
    data = crawler.get_data(5200, 5235)

    # CSVファイルに保存
    crawler.save("data.csv", ["no", "date", "num1", "num_2", "num_3"], data)

3-4. 実行環境

該当プログラムはPython3.6以降実行可能、pipコマンドでrequest、csvなどのライブラリをインストールしてください。

4. AIのアルゴリズムLSTMを使って未来の当選番号をあたってみよう

よく言われているAIやディープランニングは、様々なネットワークニューラルネットワークがあります、例えば、RNN、CNN、GAN。本稿はLSTMの原理を説明しませんが、ご興味があれば、ディバッグしながら学びましょう。

4-1. PredictionLSTM Class

予測LSTMクラスはパラメータ設定、データ生成、モデル作成、訓練、予測処理関数を実装して、いくつの損失関数も定義しています。「_create_model_by_tflearn」関数にはGRUアルゴリズムをコメントアウトしていますが,LSTMをGRUに変えて結果を比較することも可能です。

import numpy as np
import pandas as pd
import tflearn
import time
from sklearn import preprocessing


class PredictionLSTM:

    def __init__(self):
        # LSTM訓練パラメータ設定
        self.steps_of_history = 10
        self.steps_of_furture = 1
        self.units = 6
        self.epochs = 100
        self.batch_size = 1

    # データセット作成
    def create_dataset(self, data):
        x, y = [], []
        for i in range(0, len(data) - self.steps_of_history, self.steps_of_furture):
            a = i + self.steps_of_history
            x.append(data[i:a])
            y.append(data[a])
        x = np.reshape(np.array(x), [-1, self.steps_of_history, 1])
        y = np.reshape(np.array(y), [-1, 1])
        return x, y

    # 予測用データ作成
    def create_predict_dataset(self, data):
        latest_x = np.array([data[-self.steps_of_history:]])
        latest_x = np.reshape(latest_x, (-1, self.steps_of_history, 1))
        return latest_x

    # 訓練、評価データ分割
    def split_dataset(self, x, y, test_size=0.1):
        pos = round(len(x) * (1 - test_size))
        train_x, train_y = x[:pos], y[:pos]
        test_x, test_y = x[pos:], y[pos:]
        return train_x, train_y, test_x, test_y

    # モデル作成
    def _create_model_by_tflearn(self):
        net = tflearn.input_data(shape=[None, self.steps_of_history, 1])

        # LSTM
        net = tflearn.lstm(net, n_units=self.units)

        # GRU
        # net = tflearn.gru(net, n_units=self.units)

        # GRU 複数層
        # net = tflearn.gru(net, n_units=self.units, return_seq=True)
        # net = tflearn.gru(net, n_units=self.units)

        net = tflearn.fully_connected(net, 1, activation='linear')
        net = tflearn.regression(net, optimizer='adam', learning_rate=0.001, loss='mean_square')

        model = tflearn.DNN(net, tensorboard_verbose=0)

        return model

    # 訓練
    def train(self, train_x, train_y):
        model = self._create_model_by_tflearn()
        model.fit(train_x, train_y, validation_set=0.1, batch_size=self.batch_size, n_epoch=self.epochs)

        return model

    # 予測
    def predict(self, model, data):
        return model.predict(data)

    # 評価誤差アルゴリズム
    # RMSE(Root Mean Squared Error)
    def rmse(self, y_pred, y_true):
        return np.sqrt(((y_true - y_pred) ** 2).mean())

    # RMSLE(Root Mean Squared Logarithmic Error)
    def rmsle(self, y_pred, y_true):
        return np.sqrt(np.square(np.log(y_true + 1) - np.log(y_pred + 1)).mean())

    # MAE(Mean Absolute Error)
    def mae(self, y_pred, y_true):
        return np.mean(np.abs((y_true - y_pred)))

    # MAPE(Mean Absolute Percentage Error)
    def mape(self, y_pred, y_true):
        return np.mean(np.abs((y_true - y_pred) / y_true)) * 100

4-2. メインプログラム

PredictionLSTMクラスを呼び出して、データ作成、訓練、評価及び予測を行い、最後に「Next prediction」は次回の予測番号です。

if __name__ == "__main__":
    START_TIME = time.time()

    # データを読み込む
    dataframe = pd.read_csv('./data.csv')
    dataframe = dataframe[['num_1', 'num_2', 'num_3']]

    data = dataframe.index.map(lambda _:int(str(dataframe.num_1[_]) + str(dataframe.num_2[_]) + str(dataframe.num_3[_])))
    data = data.values.reshape(data.shape[0], 1).astype(dtype=np.float32)

    # 正規化
    scaler = preprocessing.MinMaxScaler()
    data = scaler.fit_transform(data)

    # LSTMインスタンス作成
    lstm = PredictionLSTM()

    # 訓練、評価データ作成
    x, y = lstm.create_dataset(data)
    train_x, train_y, test_x, test_y = lstm.split_dataset(x, y)

    # 訓練
    model = lstm.train(train_x, train_y)

    # 評価
    train_predict = lstm.predict(model, train_x)
    test_predict = lstm.predict(model, test_x)

    # RMSE(Root Mean Squared Error)
    train_score = lstm.rmse(train_y, train_predict)
    test_score = lstm.rmse(test_y, test_predict)
    print("Train Score: {0:.3f} RMSE".format(train_score))
    print("Test  Score: {0:.3f} RMSE".format(test_score))

    # 予測
    latest_x = lstm.create_predict_dataset(data)
    next_prediction = model.predict(latest_x)
    next_prediction = scaler.inverse_transform(next_prediction)
    print("Next prediction: {0:.0f}".format(list(next_prediction)[0][0]))

    print("Time: {0:.1f}sec".format(time.time() - START_TIME))

4-3. 実行環境

スクローラープログラムと同様に、Python3.6以降に実行できます。tensorflow、sklearn、tflearnなどのライブラリをインストールしないといけないです。

5. 後書き

言わなくてもご存知だと思いますが、100%次回の当選番号を予測するのは不可能です。AIの計算結果でも確率になります。下記の3つの観点を言いたかったです。

① やり方を変えて、技術学習は楽になると思います。
② AIは何でもできるわけではない、あくまでアルゴリズムであり、100%の結果を出せないです。
③ 宝くじの当選番号は確かにランダムのことを証明したいです。

様々な宝くじ分析ツールがありますが、正直100%当選番号を予測できるプログラムはまだないでしょう。

注意
上記のソースコードは学習目的以外利用禁止です。本サイトは責任を一切負わないです。

ソースコードや過去データをまとめてここからダウンロードできます。

AI技術で宝くじ当選番号を予測、億万長者の夢を実現する?” に対して1件のコメントがあります。

  1. 渡部利勝 より:

    宝くじ当選を高いレベルで当てるアプリはないですか

  2. ビッグマン より:

    夢の当選したい予測
    次世代AI学習ロトまだ甘いです。
    まだ先は遠いです

    1. admin より:

      コメントありがとうございます。
      文末にも書いてありますが、宝くじの当選番号はランダムですので、予測できないと思います。
      単純に勉強のモチベーションを上げるための投稿です。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA