利用者スタッフのrickyだ。
今回は、人工知能や機械学習という分野をよく知ってもらうべく、
例によってポケモンのステータスを使って、その一部を公開していこうと思う。
まず、初めに断っておくが、これはpython及びjupyter-notebookの経験者には、
見て一発で理解できるように書いている(つもり)だが、
重要なステップについての説明をところどころはしょっているのは否定できない。
他の利用者やまったくの未経験者が、これを読んでいて理解できるということを、考えて書いていない(今のところは)。
無論、最終的には、ある程度の習熟している利用者のさらなる理解につながることを最終的な目標としている。
が、編集者の時間も有限である。
pythonの基本の更に基本までを事細やかに書いていたら、流石にキリがない。(本当は時間があれば書きたかったのだ)
すまないが、今のところは、ある程度は自分で学習してもらいたい。
ささやかながら、事業所内の本棚に、何冊か使い古しの本を置いている。(本記事に興味を持ってくれた。利用者へのせめてものはなむけだ。)
In[1] :
import pandas as pd
import numpy as np
dt = pd.read_csv("pokemon.csv")
dt = dt[['pokedex_number','name','japanese_name','type1', 'type2', \
'hp','attack','defense','sp_attack', 'sp_defense', 'speed', \
'is_legendary','generation']]
dt
Out[1] :
今回は、図や表を用いて説明していきたいと思う。
そのために、matplotlibとseaborn(グラフを表示するためのライブラリ)を用いる。
In[2] :
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
sns.set()
matplotlibはデフォルトで日本語を読み込まないので、
デフォルトでIPAGothicを設定する。
In[3] :
from matplotlib import rcParams
rcParams['font.family'] = 'IPAGothic'
例題に入る前に、リザードンのデータを抽出してみようと思う。
抽出の条件はOut[1]からindexの3:5を取る。
In[4] :
charmander = dt.loc[3:5]
charmander
Out[4] :
pokedex_number | name | japanese_name | type1 | type2 | hp | attack | defense | sp_attack | sp_defense | speed | is_legendary | generation | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
3 | 4 | Charmander | Hitokageヒトカゲ | fire | NaN | 39 | 52 | 43 | 60 | 50 | 65 | 0 | 1 |
4 | 5 | Charmeleon | Lizardoリザード | fire | NaN | 58 | 64 | 58 | 80 | 65 | 80 | 0 | 1 |
5 | 6 | Charizard | Lizardonリザードン | fire | flying | 78 | 104 | 78 | 159 | 115 | 100 | 0 | 1 |
ここで問題点が明らかになっただろう。
例によって例のごとく、リザードンの列がどう見てもリザードンのものではないのだ。(リザードンYのものとなっている。)
ここでのミッションはヒトカゲ、リザード、リザードンYのステータスから、
リザードンのとくこうを予想せよという問いだ(いかにも人工知能、機械学習らしい響きだろう)。
In[5] :
charmander_SA = charmander["sp_attack"]
charmander_SA
Out[5] :
3 60
4 80
5 159
Name: sp_attack, dtype: int64
今の状態で、ヒトカゲ、リザード、リザードンYの順番で配列に並んでいるので、
本来はリザードンのステータスが入る部分に欠損値を入れてやる。
In[6] :
charmander_SA = np.array([charmander_SA.loc[3],charmander_SA.loc[4],np.nan,charmander_SA.loc[5]])
charmander_SA
Out[6] :
array([ 60., 80., nan, 159.])
分かりやすく、上記を図表へとまとめる。
In[7] :
fig, ax = plt.subplots()
ax.scatter(x = ["ヒトカゲ","リザード","リザードン","メガリザードンY"],
y = charmander_SA)
ax.set(
title = "ヒトカゲからメガリザードンYのとくこうの遷移",
ylabel = "とくこうの種族値",
ylim = (0,200)
)
Out[7] :
数列の状態だとピンとこなかった人もいるかもしれないが、
リザードンのとくこうのステータスは
おそらくこのへんだろうと予想できるだろう人が大多数を占めるに違いない。(実際には既に知っている人もいるだろうと思う。)
そう、これを見た閲覧者のとった思考こそが、まさに線形回帰そのものなのである。
それでは実際に、線形回帰アルゴリズムを使ってみよう。
この過程には、scikit-learnという機械学習ライブラリを使う。
In[8] :
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
なお、これからためすアルゴリズムは線形回帰というものだが、
その形が必ず一直線(1次式)であるとは限らない。
二次式や、それ以上の次数を取るものも、線形回帰のうちに入るのだ。(教科書にそう書いてあった)
ゆえに、線形回帰(1次式)を多次式へと応用するための関数を作成する。
In[9] :
def PolynomialRegression(degree = 2,**kwargs):
return make_pipeline(PolynomialFeatures(degree),LinearRegression(**kwargs))
(追記予定)
In[10] :
model = PolynomialRegression()
model.fit(np.array([0,10,30])[:,np.newaxis],charmander_SA[~np.isnan(charmander_SA)])
Out[10] :
Pipeline(memory=None,
steps=[('polynomialfeatures', PolynomialFeatures(degree=2, include_bias=True, interaction_only=False)), ('linearregression', LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None,
normalize=False))])
なお、今まで x軸 をポケモン名で置いてきたが、
これは、一連の数列に対する関数の結果である。
つまり、連続した値でなければ意味がない。
ここでは、ヒトカゲからリザードンYまでの値が linear であると仮定して、
そのxの値を 0 ~ 30までの10刻みとしておく。
(ヒトカゲ : 0, リザード : 10,リザードン : 20, リザードンY : 30 だ)
In[11] :
x_fit = np.linspace(0,30,30)
y_fit = model.predict(x_fit[:,np.newaxis])
y_fit
Out[11] :
array([ 60. , 61.46611177, 63.07134364, 64.8156956 ,
66.69916766, 68.72175981, 70.88347206, 73.1843044 ,
75.62425684, 78.20332937, 80.921522 , 83.77883472,
86.77526754, 89.91082045, 93.18549346, 96.59928656,
100.15219976, 103.84423306, 107.67538644, 111.64565993,
115.75505351, 120.00356718, 124.39120095, 128.91795482,
133.58382878, 138.38882283, 143.33293698, 148.41617122,
153.63852556, 159. ])
立てた線形回帰モデルにリザードンの値を当てはめて、四捨五入した値が、
scikit-learnが出したリザードンのとくこうの予測値だ。
In[12] :
result = int(model.predict([[20]]).round())
"線形回帰アルゴリズムによって予測された、リザードンのとくこうは %s です" % result
Out[12] :
'線形回帰アルゴリズムによって予測された、リザードンのとくこうは 113 です'
In[13] :
fig, ax = plt.subplots()
ax.scatter(x = np.array([0,10,20,30]),y = charmander_SA)
ax.plot(x_fit,y_fit,color='tab:blue')
ax.set_xticks(np.arange(0,30 + 1, 10))
ax.annotate(
"予測された値 : %d" % result,
xy = (20,result),
xycoords = "data",
xytext = (-30,-30),
textcoords = "offset points",
bbox = dict(
boxstyle = "round4,pad=.5",
fc = "0.9",
ec = "black"
),
arrowprops = dict(
arrowstyle="->",
connectionstyle="angle,angleA=0,angleB=80,rad=20",
color = "black"
)
)
ax.set( title = "ヒトカゲからメガリザードンYのとくこうの遷移 + 予測データ",
ylabel = "とくこうの種族値",
ylim = (0,200)
)
Out[13] :
上の図をよく見てほしい。
Out[7]の図を見たときに、閲覧者の脳裏にひらめいたグラフと一致してないだろうか?
勿論、これは予測値であるので、実際のリザードンのステータスとは多少誤差がある。
In[14] :
"リザードンのとくこうのステータス 予測値 : %d と 実数値 %d には %d の誤差があります。" % (result,109,result - 109)
Out[14] :
'リザードンのとくこうのステータス 予測値 : 113 と 実数値 109 には 4 の誤差があります。'
いかがだっただろうか?
人工知能や機械学習などと、やれ大層な名前がまかり通っているが、
その種明かしをしてみると、
今あるデータで
どのアルゴリズムを用いて
未知の数を 導き出すか?
という手順を踏んでいるにすぎない。
そして、それはモノと環境さえあれば、今や素人でも簡単に使えるものとなっているのだ。
できればもっと追記したい。