としおの読書生活

田舎に住む大学院生であるとしおの読書記録を綴ります。主に小説や新書の内容紹介と感想を書きます。 読書の他にも旅行やパソコン関係などの趣味を詰め込んだブログにしたいです。

タグ:宝くじ

今回の記事では前回の記事で収集したロト6の当選番号を用いて、機械学習を使って当選番号を予測してみました。

どの機械学習の手法を使うか悩んだのですが、今回は過去の当選番号を時系列データとして扱い次の当選番号を予測しようと思ったためSVRを使うことにしました。RNNを使うのもありだとは思ったのですが、学習に時間がかかりそうだったのでとりあえず手軽に実装できるSVRで試してみます。



ライブラリのインストール


今回、SVRを実装するにあたり scikit-learn を使用しました。scikit-learnは以下のコマンドでインストールすることができます。
pip install scikit-learn
また、こちらはインストールしなくてもいいのですがデータをグラフ化して分析するのにはMatplotlibを使用しました。Matplotlibは次のコマンドでインストールすることができます。
pip install matplotlib


Matplotlibを使って過去の当選番号をグラフ化してみる


とりあえず過去の当選番号の第1回目から第100回目までの結果を本数字1~6にわけてグラフ化してみます。
import pandas as pd
import matplotlib.pyplot as plt

train = pd.read_csv("loto6.csv") # 前回の記事で保存したデータの読み込み

# 第1回目から第100回目までの本数字をリスト化
x = train["round"].values[:100] main1 = train["main1"].values[:100] main2 = train["main2"].values[:100] main3 = train["main3"].values[:100] main4 = train["main4"].values[:100] main5 = train["main5"].values[:100] main6 = train["main6"].values[:100]

%matplotlib inline # jupyter notebookで描画 plt.plot(x, y1, color="red", label="main1") plt.plot(x, y2, color="darkorange", label="main2") plt.plot(x, y3, color="yellow", label="main3") plt.plot(x, y4, color="greenyellow", label="main4") plt.plot(x, y5, color="cyan", label="main5") plt.plot(x, y6, color="blue", label="main6") plt.legend(bbox_to_anchor=(1.05, 1)) plt.show()
loto2_1

グラフを見ている限り、ロト6の当選番号に規則性がなくて機械学習で予測するのは無理そうですが、ロマンを追い求めるために予測しようとしているので気にしない方向で行きます。




SVRを使って予測してみる


サポートベクター回帰(SVR)とはサポートベクターマシン(SVM)を回帰分析に応用した手法です。SVRについて詳しく知りたい方は下記のページが参考になると思います。

https://datachemeng.com/supportvectorregression/

今回は第n回目の当選番号を予測するのに、第1回目から第n-1回目までの当選番号を訓練データとしてSVRで予測します。また本番号は6つありますが、各番号が独立したデータと考えます。

上記の方法で1261回目~1340回目までの結果を予測してみたところ5等が3回だけ当たりました。16000円分宝くじを購入して、3000円しか返ってこないのは悲惨です。わざわざ機械学習をつかっているのに乱数で番号を選択した場合とあまり変わらない気がします…。

訓練データの数を変更したりしてみたのですが結果はあまり変わらずSVRでロト6の予測するのは難しそうです。各本番号を独立したデータとして考えたのも失敗した要因なのかもしれません。ハイパーパラメータをもっと細かく調整すればもう少しマシになる可能性もありますが、正直そこまで変わらない気がします…。

一応SVRでロト6の当選番号を予測するコードを載せておきます。コードが無駄に長くて申し訳ありません。
# -*- coding: utf-8 -*-
# loto6の番号をSVRを使って予想

import pandas as pd
import numpy as np
from sklearn import svm

# 訓練データとテストデータの読み込み
train = pd.read_csv("loto6.csv") # 第1~1260回目
test = pd.read_csv("loto6_test.csv") # 第1261~1340回目

train_x = train["round"].values
train_X = train_x[:, np.newaxis] # SVRで学習できるように変換
train_y1 = train["main1"].values
train_y2 = train["main2"].values
train_y3 = train["main3"].values
train_y4 = train["main4"].values
train_y5 = train["main5"].values
train_y6 = train["main6"].values
test_y1 = test["main1"].values
test_y2 = test["main2"].values
test_y3 = test["main3"].values
test_y4 = test["main4"].values
test_y5 = test["main5"].values
test_y6 = test["main6"].values
pre_list = np.array([np.zeros(7)]) # 予測結果を格納

# 学習の設定
my_svr = svm.SVR(kernel="rbf", C=1e1, gamma=0.1, epsilon=0.1)

# 1261回~1340回まで学習と予測を繰り返す
for i in range(1, len(test_y1)+1):
  print(i+1260)
  pre_num = np.array([])
  
  # 学習して結果を予測
  #第1回目~1260+i-1回目までの結果を使って1260+i回目の結果を予測
  my_pre_1 = my_svr.fit(train_X, train_y1).predict(np.array([[1260+i]]))
  my_pre_2 = my_svr.fit(train_X, train_y2).predict(np.array([[1260+i]]))
  my_pre_3 = my_svr.fit(train_X, train_y3).predict(np.array([[1260+i]]))
  my_pre_4 = my_svr.fit(train_X, train_y4).predict(np.array([[1260+i]]))
  my_pre_5 = my_svr.fit(train_X, train_y5).predict(np.array([[1260+i]]))
  my_pre_6 = my_svr.fit(train_X, train_y6).predict(np.array([[1260+i]]))
  
  pre_num = np.append(pre_num, np.round(my_pre_1))
  pre_num = np.append(pre_num, np.round(my_pre_2))
  pre_num = np.append(pre_num, np.round(my_pre_3))
  pre_num = np.append(pre_num, np.round(my_pre_4))
  pre_num = np.append(pre_num, np.round(my_pre_5))
  pre_num = np.append(pre_num, np.round(my_pre_6))
  pre_list = np.insert(pre_list, len(pre_list), pre_num, axis=0) # 四捨五入した結果を予測結果とする
  
  # train_X, train_y1に1260+i回目の結果を追加
  train_X = np.insert(train_X, len(train_X), 1260+i, axis=0)
  train_y1 = np.append(train_y1, test_y1[i-1])
  train_y2 = np.append(train_y2, test_y2[i-1])
  train_y3 = np.append(train_y3, test_y3[i-1])
  train_y4 = np.append(train_y4, test_y4[i-1])
  train_y5 = np.append(train_y5, test_y5[i-1])
  train_y6 = np.append(train_y6, test_y6[i-1])

# 予測結果を出力
pre_list = np.delete(pre_list, 0, 0) # 1行目を削除
df = pd.DataFrame(pre_list, columns = ["main1", "main2", "main3", "main4", "main5", "main6", "bonus", ])
df.index = df.index + 1
df.to_csv('loto6_pre.csv')




最後に


SVRでの予測は失敗に終わりました。気が向いたらRNNを使って予測する記事を作るかもしれません。


前々からロト6の当選番号を機械学習で予測してみたいと思っており、やっと作業に着手し始めたので予測方法をまとめていきます。

機械学習で予測するには過去の当選番号を学習データとして収集する必要があるので、この記事ではみずほ銀行のページからPythonでスクレイピングを行い、過去の当選番号を自動的に収集する方法を説明していきます。



事前準備


今回スクレイピングをするにあたり、以下の3つの技術要素を利用します。
  • BeautifulSoup(スクレイピングを行うライブラリ)
  • Selenium(ブラウザの自動操作を行うツール)
  • PhantomJS(ヘッドレスブラウザ)
最初はBeautifulSoupだけを使用してスクレイピングをしようと思ったのですが、第461回目以降の当選番号が掲載されているページではJavascriptを使ってページが書かれており、BeautifulSoupだけでは集めることができなかったのでSeleniumとPhantomJSを使用することにしました。

以下インストール方法を説明していきます。


1. BeautifuSoupのインストール


BeautifulSoupはpipで導入する場合、以下のコマンドでインストールすることができます。
pip install beautifulsoup4
Anacondaを使っていてpipが使えない場合、以下のコマンドでインストールすることができます。
conda install -c anaconda beautifulsoup4


2. Seleniumのインストール


Seleniumはpipで導入する場合、以下のコマンドでインストールすることができます。
pip install selenium
Anacondaでインストールする場合、以下のコマンドでインストールすることができます。
conda install -c conda-forge selenium


3. PhantomJSのインストール


Windowsで導入する場合の手順は以下の通りになります。
  1. http://phantomjs.org/にアクセスしてサイトからダウンロード
  2. ダウンロードしたファイルを解凍し、c:\の直下に置く
  3. Pathを通す
よくわからない方は以下のページが参考になると思います。
https://gist.github.com/maechabin/7632c460ceede823cbde


Anacondaを使っている場合はPhantomJSもコマンド一つでインストールすることができます。
conda install -c conda-forge phantomjs
このコマンドはWindowsでも使えるため、WindowsでPythonを使うならばAnacondaを使った方が色々と便利そうですね。




過去の当選番号をスクレイピングしていく


環境構築ができたらスクレイピングをしています。今回コードを書く前に当選番号が掲載されているページのURLのパターンと、各ページでの当選番号の取得方法について考えていきます。


当選番号が掲載されているページのURLのパターン


ロト6の過去の当選番号はみずほ銀行のホームページで20回ごとにページを分けて掲載されています。そのためスクレイピングをする際に自動的にページを遷移させる必要があります。ここで一つ問題があり、第1回~第460回までの結果がのっているページのURLと第461回目以降の結果がのっているページのURLはパターンが違うため収集する際に注意しなければなりません。

第1回~第460回までのURLは以下のようになっています。(第1回目~第20回目の例)
https://www.mizuhobank.co.jp/retail/takarakuji/loto/backnumber/loto60001.html

ページごとに赤字で書いている部分が変更しており、
第1回目~第20回目の場合は0001、第21回目~第40回目は0021、第41回目~第60回目までは0041となっており値が20ずつ増えていきます。


第461回目以降のURLは以下のようになっています。(第461回目~第480回目の例)
https://www.mizuhobank.co.jp/retail/takarakuji/loto/backnumber/detail.html?fromto=461_480&type=loto6

これも先ほどと同様に赤字の部分が掲載されている当選番号の回数に伴い変化しています。
第461回目~第480回目の場合は461_480第481回目~第500回目の場合は481_500第501回目~第520回目の場合は501_520となっている。


当選番号の取得


全てのページで過去の当選番号はtableタグの中にあります。そのため最初に
table = soup.find_all("table")
と書いてtableタグないの全ての要素を取得します。取得したtableは以下のようになっています。(第501回目の例)
<table class="typeTK js-lottery-backnumber-temp-sp">
<tbody>
<tr>
<th class="bgf7f7f7 wdt33p">回</th>
<td>第501回</td>
</tr>
<tr>
<th class="bgf7f7f7 wdt33p">抽せん日</th>
<td>2010年6月17日</td>
</tr>
<tr>
<th class="bgf7f7f7 wdt33p">本数字</th>
<td>03 17 21 23 36 38</td>
</tr>
<tr>
<th class="bgf7f7f7 wdt33p">ボーナス数字</th>
<td class="green">07</td>
</tr>
</tbody>
</table>
3番目のtrタグ内のtdタグに当選番号が記載されている(赤字の部分)のでこの部分を抽出するコードは以下のようになります。
main_num = table[1].find_all("tr")[2].find("td")

またボーナス番号も取得したい場合、4番目のtrタグ内のtdタグにボーナス番号が記載されている(青字の部分)のでこの部分を抽出しましょう。



ソースコード


ロト6の当選番号を自動的に収集するコードを以下に載せておきます。
# -*- coding: utf-8 -*-
# ロト6の当選番号を自動的に収集する

import time
import random
import pandas as pd
from selenium import webdriver
from bs4 import BeautifulSoup

# ロト6の当選番号が掲載されているみずほ銀行ページのURL
loto_url1 = 'https://www.mizuhobank.co.jp/retail/takarakuji/loto/backnumber/loto6' # 1~460回目
loto_url2 = 'https://www.mizuhobank.co.jp/retail/takarakuji/loto/backnumber/detail.html?fromto=' # 461回目以降
num = 1

main_num_list = [] # 本数字6桁を格納するリスト
bonus_num_list = [] # ボーナス数字を格納するリスト

# PhantomJSをselenium経由で利用
driver = webdriver.PhantomJS()

while num <= 1241:
  
  # 第1~460回目までの当選ページのURL
  if num < 461:
    url = loto_url1 + str(num).zfill(4) + '.html'
  # 461回目以降当選ページのURL
  else:
    url = loto_url2 + str(num) + '_' + str(num+19) + '&type=loto6'
  
  # PhntomJSで該当ページを取得
  driver.get(url)
  time.sleep(2) # javascriptのページを読み込む時間
  html = driver.page_source
  
  soup = BeautifulSoup(html, "html.parser")
  print(soup.title)

  # ロト6の当選番号がのっているテーブルの取得
  table = soup.find_all("table")
  del table[0]

  for i in table:
    # 本数字の取得
    main_num = i.find_all("tr")[2].find("td")
    main_num_list.append(main_num.string.split(" "))

    # ボーナス数字の取得
    bonus_num = i.find_all("tr")[3].find("td")
    bonus_num_list.append(bonus_num.string)
    
  num += 20 # 次のページに移動するためにnumに20を追加
  time.sleep(random.uniform(1, 3)) # 1~3秒Dos攻撃にならないようにするためにコードを止める

# csvで出力
df = pd.DataFrame(main_num_list, columns = ['main1', 'main2', 'main3', 'main4', 'main5', 'main6'])
df['bonus'] = bonus_num_list
df.index = df.index + 1
df.to_csv('loto6.csv')



最後に


内容の中で何か分からないことがあればコメントで質問してください。答えられる範囲で答えます。

学習データを収集で来たので次の記事は機械学習を使って予測していきたいと思っています。深層学習を使うのならばRNNとか使うのがいいのかな。

SVRで予測する記事を書きました。
http://www.toshioblog.com/archives/16052074.html

Pythonのスクレイピングで分からないことがあれば以下の本が参考になると思うので読んでみてください。




↑このページのトップヘ