コンテンツにスキップ

第6章 回帰分析

1 単回帰分析

教師有り学習の中でも、分類を行うのでは無く、数値を導き出すのが回帰分析です。例えば気温が上がればアイスクリームの売り上げが伸びる、という場合、気温がどれぐらいならアイスクリームの売り上げがどれぐらいになるか、を予測することができます。

この場合、結果は分類のように数パターンでは無く、あらゆる数値があり得ますので、使うアルゴリズムも変わってきます。 そこで使うのが回帰分析というアルゴリズムです。

まずは今回使うデータを読み込み表示してみます。気温とアイスクリームの売り上げです。 ファイル ice.ipynb に以下を記述します。

import pandas as pd
df =  pd.read_csv("ice.csv")
df

今回は気温を元に売上を予測します。 気温と売上には相関関係があります。散布図で確認してみましょう。

import seaborn as sns
sns.set(font=["Meiryo", "Yu Gothic"])

df.plot.scatter(x="気温", y="売上")

気温が上がると売上が上がります。 回帰直線を引いてみましょう。

import seaborn as sns
sns.set(font=["Meiryo", "Yu Gothic"])

sns.regplot(data=df, x="気温", y="売上", line_kws={"color":"red"})

このような場合、この場合、元となる特徴量が1つ(気温)しかありません。このような回帰分析を単回帰分析といいいます。特徴量が複数の場合、重回帰分析と言います。

2 外れ値の削除

散布図を見ると外れ値が左上にあることに気がつきます。回帰分析の場合、外れ値に大きく影響されやすいため、外れ値は削除しなくてはなりません。 外れ値を探し、そのインデックスを求めてdropで削除します。

no = df[(df["気温"]<=25) & (df["売上"]>=80)].index
df = df.drop(no)

3 モデルの構築と学習

まずは単回帰分析を行ってみましょう。特徴量を x に、正解ラベルを y に入れます。

x = df[["気温"]]
y = df["売上"]

また、学習用とテスト用のデータに分割します。

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.2)

今回使用するアルゴリズムは線形回帰(LinearRegression)です。インポートし、学習します。

from sklearn.linear_model import LinearRegression

model = LinearRegression()
# 学習する
model.fit(x_train, y_train)

線形回帰分析は以下のような式を作り予測をします。

×特徴量1正解

では予測を行ってみましょう。

model.predict([[32.0]])

4 決定係数

では、評価を出してみましょう。評価は分類の場合と違い、正解率ではありません。決定係数という値になり、この値が1.0に近いほどモデルの式がデータに合致していることになります。

model.score(x_test, y_test)

また、どれぐらい誤差があったか、その平均値を「平均絶対誤差」(mean absolute error)として表示できます。

from sklearn.metrics import mean_absolute_error
pred = model.predict(x_test)

# y_pred :予測値
# y_true :正解
mean_absolute_error(y_pred = pred, y_true = y_test)

5 回帰式

線形回帰分析は以下のような式(回帰式)を作り予測をします。

  • A×特徴量+定数

Aを係数、定数を切片といいます。 係数は model.coef_ と切片 model.intercept_ で表示できます。

print("係数", model.coef_)
print("切片", model.intercept_)

例えば係数が3.54、切片が-15.4 の場合、以下のようにして売上を計算しています。

気温×3.54 - 15.4

6 重回帰分析

次に、特徴量が3つ(気温と湿度とイベント参加人数)のデータを回帰分析してみます。このような回帰分析を重回帰分析といいいます。

気温、湿度、イベント人数とアイスクリームの売り上げです。 ファイル ice2.ipynb に以下を記述します。

import pandas as pd
df =  pd.read_csv("ice2.csv")
df

気温・湿度・イベント人数と売上の相関関係を散布図行列で確認してみましょう。

import seaborn as sns
sns.set(font=["Meiryo"])
sns.pairplot(data=df, kind="reg")

気温が上がると売上が上がります。湿度、イベント人数も同様です。

7 重回帰分析の学習

では、モデルを構築し、学習してみましょう。そのやり方は単回帰分析と変わりません。

まずは特徴量を x に、正解ラベルを y に入れ、学習用とテスト用のデータに分割し学習を行います。

x = df[["気温", "湿度", "イベント人数"]]
y = df["売上"]

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.2)

from sklearn.linear_model import LinearRegression
model = LinearRegression()

# 学習する
model.fit(x_train, y_train)

予測するときには、気温と湿度、イベント人数を指定します。 気温30度、湿度60%、イベント人数2000人の予測を行ってみます。

model.predict([[30, 60, 2000]])

決定係数を見てみましょう。

model.score(x_test, y_test)

8 影響度

重回帰分析でも回帰式は同様です。ただし、特徴量が複数になります。

  • A×特徴量1+B×特徴量2+C×特徴量3+切片

係数 model.coef_ 切片 model.intercept_ を確認しましょう。係数は複数あります。

# データフレームで係数表示
import pandas as pd
pd.DataFrame(model.coef_, index=x_train.columns, columns=["係数"])
#切片
model.intercept_

例えば係数が[2.65, 1.29, 0.007]、切片が-141.18 の場合、以下のようにして売上を計算しています。

  • 気温×2.65+湿度×1.29+イベント人数×0.007 - 141.18

係数の大きさを見ると、その特徴量に与える影響度が分かります。上の例の場合、気温は1度上がるたびに売上が2.65増えます。湿度が1上がるたびに売上が1.29増えます。ですから、気温の方が売上に与える影響が大きいとなります。

9 標準化

係数の大きさを影響度と考えると、イベント人数は売上にほとんど影響を与えないとなりますが、果たしてそうでしょうか。散布図を見るとイベント人数が増えると売上が明らかに増えているのにおかしい感じがします。

これはイベント人数の数値と気温や湿度の数値に大きな差があるため起きる現象です。イベント人数は何千人もの数値が普通にあるのに、気温は二桁しかありません。数値の大きさが大幅に異なるのでイベント人数の影響度は大きく下がってしまったのです。

このような数値の大きさによる影響を避けるために、全ての数値を一定の平均値になるように変換する処理を行います。これを標準化(正規化)といいます。具体的には平均値を0、分散1になるように数値を変換します。

これもscikit-learnで行うことが出来ます。 ただし、標準化モジュールはDataFrameでしかデータを受け取れませんので、売上をyに抜き出す際に[]を1つ増やし、DataFrameとして抜き出します。

import pandas as pd
df =  pd.read_csv("ice2.csv")
x = df[["気温","湿度","イベント人数"]]
y = df[["売上"]]

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.2)

まず、x_trainを標準化してみます。

from sklearn.preprocessing import StandardScaler

sc_model_x = StandardScaler()
sc_model_x.fit(x_train)

sc_x = sc_model_x.transform(x_train) # 標準化

# 標準化されたx_trainを確認
sc_x

0を平均とした値になります。 そして標準化した値を元に学習を行います。

# 学習する
model.fit(sc_x, y_train)

決定係数を出す場合にはxのテストデータも元のデータと同じ標準化モデルで標準化してから行います。

sc_x_test = sc_model_x.transform(x_test)

model.score(sc_x_test, y_test)

ここで係数を出すと標準化前とは大きく異なる値が出ているはずです。

import pandas as pd
pd.DataFrame(model.coef_[0], index=x_train.columns, columns=["係数"])

これにより影響度の大きさを正しく算出することが出来ます。

10 パイプライン

標準化を行い、さらに線形回帰を行う、という一連の流れをまとめることができます。これをパイプラインと言います。

これまでの流れをパイプラインでまとめてみます。

# パイプラインの構築
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import make_pipeline

pipeline = make_pipeline(StandardScaler(), LinearRegression())

# 学習
pipeline.fit(x_train, y_train)

# 評価
pipeline.score(x_test, y_test)

係数を表示するにはpipeline.named_stepsでモデル名を小文字で指定し、モデルを取得してから表示します。

model = pipeline.named_steps["linearregression"]
pd.DataFrame(model.coef_[0], index=x_train.columns, columns=["係数"])