AI技術で宝くじ当選番号を予測、億万長者の夢を実現する?
概要
本稿はある数学教授の宝くじ当選の物語を紹介してから、Pythonを使ってNUMBERS3過去25年分の当選番号を取得するクローラーを実現した上、LSTMアルゴリズムで次回の当選番号を予測するプログラムを実現します。
1. 数学教授の宝くじ当選伝説
「宝くじにあたったら、何をするか」と考えた人が多いでしょう。「流行っているAIが強そうですが、明日の当選番号を正しく予測してもらえるか」と考えている方もいるでしょう。それは実現可能かどうか、一緒に見てみましょう。
本題に入る前に、数学教授が何度も宝くじ当選の話を紹介します。
宝くじ当選番号はランダムですので、研究しても運がないと当たらないと思いますが、ある数学者は宝くじを4回も的中させ、世界中に大きな話題になりました。
賞金金額は下記の通り。
第一回: 540万ドル
第二回: 200万ドル
第三回: 300万ドル
第四回:1000万ドル
合 計:2040万ドル
この教授はスタンフォード大学の統計学博士、数学教授を務めているJoan R Gintherです。統計学者ですので、メデイアは統計学の知識を使っていたと断言しています。本当に統計学の知識活用なのか、教授の幸運なのかわかりませんが、宝くじはそもそも統計学の原理をもとに作ったものですね。
2. 数字選択型の宝くじ
日本国内の数字選択型の宝くじはBINGO5、LOTO7、LOTO6、MINI LOTO、NUMBERS3、NUMBERS4などあります。
本稿は数字の数が一番少ない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学習ロトまだ甘いです。
まだ先は遠いです
コメントありがとうございます。
文末にも書いてありますが、宝くじの当選番号はランダムですので、予測できないと思います。
単純に勉強のモチベーションを上げるための投稿です。