機械学習ライブラリsklearnを使わずにPythonで単回帰分析

私たちは「import sklearn」という何文字かのおまじないをプログラムの最初のほうに書いておくだけで数学的なことをほとんど何も考えなくても機械学習をすることができます。

そこで私は、

まがりかど

scikit-learnは便利だけど中で何が起きているかしりたーい。
sciki-learnなしでも機械学習できるようになりたーい。

と思ったのでちょっとやってみました。

今回何するのか

ということで今回はscikit-learnのボストンの住宅価格データセットを使って単回帰分析をしていきたいと思います。犯罪率とか空気のきれいさとかから住宅価格を予想します。

さっきまでscikit-learnを使わない使わないって言ってたのにいきなり使うのかと思う人もいるかもしれませんが、データセットを読み込むだけに使うので怒らないでください。機械学習モデルは使いません。

許してくださいね。

流れとしては、

  1. 特徴ベクトルから一つ選ぶ
  2. 単回帰分析

今回やっていくのは単回帰分析なので説明変数が一種類しか使えません。

なのでいくつかある特徴ベクトルから一番適しているものを選ぶ必要があります。

そしてその特徴ベクトルで単回帰分析を行っていまーす。

はーい。

ということで書いていきます

どの特徴にするか選びましょう

まずどんな特徴ベクトルがあるか確認します。

from sklearn import datasets#sklearnからデータセットが入っているやつをインポート
boston=datasets.load_boston()ボストンのデータをロード
print(boston.DESCR)#すべてのパラメータとその説明を表示
        - CRIM     per capita crime rate by town
        - ZN       proportion of residential land zoned for lots over 25,000 sq.ft.
        - INDUS    proportion of non-retail business acres per town
        - CHAS     Charles River dummy variable (= 1 if tract bounds river; 0 otherwise)
        - NOX      nitric oxides concentration (parts per 10 million)
        - RM       average number of rooms per dwelling
        - AGE      proportion of owner-occupied units built prior to 1940
        - DIS      weighted distances to five Boston employment centres
        - RAD      index of accessibility to radial highways
        - TAX      full-value property-tax rate per $10,000
        - PTRATIO  pupil-teacher ratio by town
        - B        1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town
        - LSTAT    % lower status of the population
        - MEDV     Median value of owner-occupied homes in $1000's

英語がごちゃごちゃ書いててよくわからないのでDeepLで翻訳します

        - CRIM 町別一人当たりの犯罪率
        - ZN 25,000平方フィートを超える土地のために区画された住宅地の割合
        - INDUS 町ごとの非小売業の割合
        - CHAS チャールズ川のダミー変数(トラクトが川に接している場合は1、そうでない場合は0)。
        - NOX 硝酸態窒素酸化物濃度(千万分の一
        - RM 1住戸あたりの平均部屋数
        - AGE 1940年以前に建てられた持ち家の割合
        - DISボストンの5つの雇用センターまでの加重距離
        - RAD 放射状の高速道路へのアクセス性の指標
        - TAX 10,000ドルあたりの全価額固定資産税の税率
        - PTRATIO 町の生徒数と教師数の比率
        - B 1000(Bk - 0.63)^2 ここで、Bkは町ごとの黒人の割合である。
        - LSTAT 人口の下位ステータスの割合
        - MEDV 1000ドル台の持ち家の中央値

らしいです。

まあそんなにちゃんと読まなくても大体わかればいいという感じです。

kaggleとかのデータ分析とかだったら結構パラメータの意味とか考えたりとかする必要があるとおもいますが今回は簡単にちょっと単回帰分析やってみようという感じなのでサラーっと読み飛ばしてください。

相関係数から判断しますよー

x,y=boston.data,boston.target#代入
df_x=pd.DataFrame(x)データフレーム作成
df_y=pd.DataFrame(y)#同じく
df_x.columns=boston.feature_names#カラムの名前設定
df_y.columns=["target"]#同じく
df=pd.concat([df_x,df_y],axis=1)#df_xとdf_yを合体する
corr=df.corr()#相関係数ですね
print(corr.loc[:,"target"])

さっきのつづきですよ。

そういえば、いまさらというかんじですがpandasの使い方はここでは詳しくは解説しませんので知っておいてください。

コメントに書いてある通りという感じですが、ここではtarget(価格)とそれぞれのパラメータとの相関係数を取っています。

ちなみに相関係数というのは名前の通り値と値の相関を表すやつなんですけど、範囲としては相関係数をrとすると

-1<=r<=1

-1に近いほど負の相関が強くて1に近いほど正の相関が強いです。

0は全く相関関係がないです。

なので-1と0では-1のほうが数としては小さいですが相関関係は強いということですね。

ということで実行結果は、

CRIM      -0.388305
ZN         0.360445
INDUS     -0.483725
CHAS       0.175260
NOX       -0.427321
RM         0.695360
AGE       -0.376955
DIS        0.249929
RAD       -0.381626
TAX       -0.468536
PTRATIO   -0.507787
B          0.333461
LSTAT     -0.737663
target     1.000000

この中から相関関係の強いパラメータを一つ選びましょう。

もちろんtargetとtargetは同値なので相関係数は1ですがこれは目的変数なので使えません。

target以外で見ていくとLSTATが一番よさそうじゃないですかね。

-0.7って結構ですよね。

ちなみにLSTATの説明を思い出してみると、
「人口の下位ステータスの割合」ちょっと日本語がおかしいですがまあわかりますね。

給与の低い人の多さと住宅価格が負の相関にあるということは、つまり給与が少ない人が少ないほうが住宅価格は上がるということですね。(そのまんま)

まあそうだろうなというかんじですね。

ということでパラメータが決まりましたね。

わたしきめたLSTATにする。

単回帰分析やってやる

lstat=df_x.loc[:,"LSTAT"]#df_xからlstatを抽出
lstat=lstat.values#dataframeをndarrayにする
lstat=lstat.reshape(-1,1)#1列にする
dfl=pd.DataFrame(lstat)#dataframeにする
dfy=pd.DataFrame(y)#目的変数をdataframeにする
dfa=pd.concat([dfl,dfy],axis=1)#dflとdfyをがっちゃんする
xm=dfa.iloc[:,0].mean()#lstatの平均を取る
ym=dfa.iloc[:,1].mean()#targetの平均を取る
sx=variance(lstat.reshape(-1,))#lstatの分散を取る
sxy=dfa.cov().values[0,1]#lstatとtargetの共分散を取る
a=sxy/sx#直線の傾き
b=ym-a*xm#y切片
plt.scatter(lstat,y)#まき散らす
plt.plot(lstat,a*lstat+b,color="red")
plt.title('boston')     
plt.xlabel('LSTAT')               
plt.ylabel('target')                 
plt.show()

ここについてもコメントに書いてある通りという感じですね。

説明すべきは、直線の傾きがlstatとtargetの共分散になり、切片がtargetの平均から直線の傾きとlstatの平均をかけたものを引いたものになるのかということですね。

いやです。

すみません。結構偏微分とか最小二乗法とか面倒だしほかのサイトですでに証明されているのでわざわざ私が書く意味とか別にないなと思ったので書きません。

はい。

その証明されているサイトのリンクを貼っておきますね。

https://mathtrain.jp/leastsquares

これです。

そしてこの方法で単回帰分析をした結果がこちら。(料理番組みたい)

いいかんじですね。これで予測したらまあまあ精度よさそうですね。

まさに相関係数-0.7っていうかんじですね。

グラフから相関係数当てとかしてみたいですね。面白そうじゃないですか?

私そういうの得意なんですよ昔から。

おわり

scikit-learnを使わずに機械学習をしてきましたがどうでした?

たまにはライブラリに頼らずにやってみるのもいいでしょ。

トレーニングデータとテストデータに分けて精度出してみるとかは面倒だったので私はしなかったですけどあなたはやってみてください。(迷惑)

おわりですね。

さようなら