1. はじめに
本記事は、ChatGPT を活用してみたい ChatGPT 初心者エンジニア向けに、活用例を紹介する「ChatGPTと始める」シリーズ第4弾です。今回はデータ分析(回帰)を行います!
(第1弾はGUI, 第2弾はCSV可視化、第3弾は機械学習アプリといずれもいいね数は100越えと大好評です。ありがとうございます。)
記事の背景
エンジニアであれば、試験データや業務データなど、データ分析に使用するデータの量には困らないと思いますが、いざそれを分析しようとすると、
で、どうすればいいんだ...?
と、データ分析手法を色々と知っていたとしても、実際に手を動かし始めるまでに、大きなハードルがあることが多いのではないかと思います。
そこで、そのハードルをなるべく下げるために、まずは何から始めればいいか ChatGPT に相談するところからスタートし、コードの作成、分析まで、すべて手伝ってもらおうと思います!
ぜひ業務で使ってみて、ChatGPT を使えばここまで仕事が早く進むのかと、体感いただければと思います。
2. 実施内容を相談しよう!
2.1. 使用するデータセット
今回はデータセットとして、アメリカ合衆国マサチューセッツ州ボストン市の住宅価格を予測する Boston Housing Dataset を使用します。
このデータセットには506の観測値があり、それぞれがボストン市内の異なる地域を表しています。各観測値は、以下の14の変数を含み、説明変数(1から13)を使用して目的変数(14:MEDV)を予測する回帰モデルを構築します。
- CRIM:人口1人当たりの犯罪発生率
- ZN:25,000平方フィート以上の住宅区画の割合
- INDUS:非小売業の土地面積の割合
- CHAS:チャールズ川沿いかどうか(1:沿い、0:沿っていない)
- NOX:窒素酸化物濃度(pphm単位)
- RM:住居あたりの平均部屋数
- AGE:1940年以前に建てられた家屋の割合
- DIS:ボストンの5つの雇用センターまでの重み付け距離
- RAD:環状高速道路へのアクセスしやすさの指数
- TAX:10,000ドルあたりの固定資産税率
- PTRATIO:生徒と教師の比率(生徒1人あたりの教師数)
- B:1000(Bk - 0.63)^2 ここで、Bkはアフリカ系アメリカ人居住者の割合
- LSTAT:低所得者の割合
- MEDV:所有者居住住宅の中央値の価格(単位:1000ドル)
(今回は、csvデータを kaggle からダウンロードして使用しました)
記事の内容は、このデータに限らず汎用的に使用できる方法です。
2.2. 本記事において ChatGPT に相談する際の注意点
この記事では制約条件として、
- ChatGPT にデータセットの中身が何であるか、何を分析したいのか、詳細を教えないこと
とします。理由としては、
皆様、仕事でChatGPTの使用は許可されていますか?
私は、もちろん許可されていません!
(フットワークが軽い会社はいいですよね...)
そのため例えば「自動車の燃費が」とか「説明変数としては平均速度が」なんて業務情報を直接 ChatGPT にしゃべってしまうと、その瞬間に打ち首になる可能性があります。
そんなサラリーマンの不安に対応するために、ここではあえて、ChatGPT には具体的なデータセットの中身や数値は教えずに、それでも効率的であるということを紹介したいと思います!
2.3. 実施手順を聞いてみよう!
まず実施手順を聞いてみます。
今回のように、比較的長い作業を行う場合は、先に ChatGPT と自分が考えているイメージが合っているか確認した方がよいです。
プロンプトのポイント
- ChatGPT に与える情報として、最低限必要なのはデータの数と、説明変数・目的変数のデータ数、データ型になります。
- 業務上の制約がないときは、目的や背景、変数の概要やデータサンプルなど、自分がデータ分析を行うときに必要な情報を ChatGPT にも追加してあげてください。
- 残りは、Pythonでコードを生成するときの依頼事項になります。このプロンプトの詳細は過去のシリーズ記事と同じです。
回答はこちらです。
はい。ありがとうございます。いいですね!
(私はデータ分析の専門家ではないので、この時点で GPT 先生の方が圧倒的に詳しいと思います。)
次章から、実際に手を動かしていきます。
3. データの前処理をしよう!
この章は長いので、記事の概要を把握したい方は、とばして次章の 「4. 線形モデルで学習しよう」 に進んでください。
3.1. 欠損値と外れ値処理
ここからは前章でChatGPTが提案してくれた実施手順の順番に ChatGPT に方法を聞きつつ、都度不明点について相談します。
import pandas as pd
def load_data(file_name):
"""
CSVファイルを読み込んでデータフレームに変換する関数。
Args:
file_name (str): CSVファイル名
Returns:
pd.DataFrame: CSVファイルを読み込んだデータフレーム
"""
return pd.read_csv(file_name)
def main():
file_name = "data.csv"
data = load_data(file_name)
print(data.head()) # データの先頭5行を表示
if __name__ == "__main__":
main()
指示通りのコメントもあって、流用しやすく関数化された Python コードが出てきました。
実行すると、エラーも発生せず、以下のようにデータが読み取れているのが確認できます。
$ python data01.py
Unnamed: 0 crim zn indus chas nox rm age dis rad tax ptratio black lstat medv
0 1 0.00632 18.0 2.31 0 0.538 6.575 65.2 4.0900 1 296 15.3 396.90 4.98 24.0
1 2 0.02731 0.0 7.07 0 0.469 6.421 78.9 4.9671 2 242 17.8 396.90 9.14 21.6
2 3 0.02729 0.0 7.07 0 0.469 7.185 61.1 4.9671 2 242 17.8 392.83 4.03 34.7
3 4 0.03237 0.0 2.18 0 0.458 6.998 45.8 6.0622 3 222 18.7 394.63 2.94 33.4
4 5 0.06905 0.0 2.18 0 0.458 7.147 54.2 6.0622 3 222 18.7 396.90 5.33 36.2
続けて、欠損値、外れ値の確認に進みます。
これ以降、長くなる話は以下のように折りたたんでいます。記事全体が長いので、気になる人だけ展開して見てください
ChatGPTが生成したコードやその解説
初心者向けの解説
欠損値、外れ値確認用コード
import pandas as pd
def check_missing_values(data):
"""
データフレーム内の欠損値の数を表示する関数。
Args:
data (pd.DataFrame): データフレーム
"""
print("Missing values for each column:")
print(data.isnull().sum())
def check_outliers(data):
"""
データフレーム内の各変数の基本統計量を表示する関数。
Args:
data (pd.DataFrame): データフレーム
"""
print("Basic statistics for each column:")
print(data.describe())
def main():
file_name = "data.csv"
data = pd.read_csv(file_name)
# カラム名を設定(適宜変更してください)
data.columns = ["label1", "label2", "label3", ..., "label14"]
# 欠損値の確認
check_missing_values(data)
# 外れ値の確認
check_outliers(data)
if __name__ == "__main__":
main()
これを実行すると、以下の結果が表示され、データに欠損がないことがわかります。
Missing values for each column:
Unnamed: 0 0
crim 0
zn 0
indus 0
chas 0
nox 0
rm 0
age 0
dis 0
rad 0
tax 0
ptratio 0
black 0
lstat 0
medv 0
dtype: int64
また、外れ値に関しても、勉強を兼ねて色々と聞いてみました。長くなるので折りたたみます。
外れ値ってどうやって調べればいい? (長いです)
さきほどのコードを実行すると、各変数の基本統計量(最小値、第1四分位数、中央値、平均、第3四分位数、最大値など)も表示されます。
Basic statistics for each column:
Unnamed: 0 crim zn indus chas nox rm age dis rad tax ptratio black lstat medv
count 506.000000 506.000000 506.000000 506.000000 506.000000 506.000000 506.000000 506.000000 506.000000 506.000000 506.000000 506.000000 506.000000 506.000000 506.000000
mean 253.500000 3.613524 11.363636 11.136779 0.069170 0.554695 6.284634 68.574901 3.795043 9.549407 408.237154 18.455534 356.674032 12.653063 22.532806
std 146.213884 8.601545 23.322453 6.860353 0.253994 0.115878 0.702617 28.148861 2.105710 8.707259 168.537116 2.164946 91.294864 7.141062 9.197104
min 1.000000 0.006320 0.000000 0.460000 0.000000 0.385000 3.561000 2.900000 1.129600 1.000000 187.000000 12.600000 0.320000 1.730000 5.000000
25% 127.250000 0.082045 0.000000 5.190000 0.000000 0.449000 5.885500 45.025000 2.100175 4.000000 279.000000 17.400000 375.377500 6.950000 17.025000
50% 253.500000 0.256510 0.000000 9.690000 0.000000 0.538000 6.208500 77.500000 3.207450 5.000000 330.000000 19.050000 391.440000 11.360000 21.200000
75% 379.750000 3.677083 12.500000 18.100000 0.000000 0.624000 6.623500 94.075000 5.188425 24.000000 666.000000 20.200000 396.225000 16.955000 25.000000
max 506.000000 88.976200 100.000000 27.740000 1.000000 0.871000 8.780000 100.000000 12.126500 24.000000 711.000000 22.000000 396.900000 37.970000 50.000000
偏っている変数とそうでない変数があることはなんとなくわかりますが、これをどう使えばよいのかよくわからなかったので聞いてみました。
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
def detect_outliers_iqr(data):
"""
IQRを利用して外れ値を検出する関数。
Args:
data (pd.DataFrame): データフレーム
Returns:
pd.DataFrame: 外れ値を検出した結果のデータフレーム
"""
outliers = pd.DataFrame(columns=data.columns)
for column in data.columns:
Q1 = data[column].quantile(0.25)
Q3 = data[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers_column = data[(data[column] < lower_bound) | (data[column] > upper_bound)]
outliers = pd.concat([outliers, outliers_column])
return outliers
def plot_boxplots(data):
"""
データフレーム内の各変数の箱ひげ図を描画する関数。
Args:
data (pd.DataFrame): データフレーム
"""
sns.set(style="whitegrid")
plt.figure(figsize=(15, 10))
sns.boxplot(data=data)
plt.xticks(rotation=90)
plt.show()
def main():
file_name = "data.csv"
data = pd.read_csv(file_name)
# カラム名を設定(適宜変更してください)
data.columns = ["label1", "label2", "label3", ..., "label14"]
# IQRを利用した外れ値検出
outliers = detect_outliers_iqr(data)
print("Outliers detected using IQR:")
print(outliers)
# 箱ひげ図を描画
plot_boxplots(data)
if __name__ == "__main__":
main()
ChatGPTが出してくれたこのコードを実行すると,以下の箱ひげ図が表示されました。
箱ひげ図の見方は以下になります。
データの分布に偏りがあるため、箱からはみ出たデータがたくさんあります。
今回使用するデータの性質上、偏りがあること自体は問題はないと思いますが、実際にどのようにデータが分布しているのか確認するためのヒストグラムもついでに出してもらいました(ラベルが上です)。
目的変数(左下)が滑らかな分布なので、外れ値は特に処理しなくてよいかなと判断しました。
結果的に、外れ値は処理しなくても問題なさそうだと考えたので、なにもしていません。
3.2. データの正規化/標準化
つぎにデータの正規化/標準化に進みます。
正規化と標準化を行うコード
from sklearn.preprocessing import MinMaxScaler, StandardScaler
def normalize_data(data):
"""
データを正規化する関数。
Args:
data (pd.DataFrame): データフレーム
Returns:
pd.DataFrame: 正規化されたデータフレーム
"""
scaler = MinMaxScaler()
normalized_data = scaler.fit_transform(data)
return pd.DataFrame(normalized_data, columns=data.columns)
def standardize_data(data):
"""
データを標準化する関数。
Args:
data (pd.DataFrame): データフレーム
Returns:
pd.DataFrame: 標準化されたデータフレーム
"""
scaler = StandardScaler()
standardized_data = scaler.fit_transform(data)
return pd.DataFrame(standardized_data, columns=data.columns)
def main():
file_name = "data.csv"
data = pd.read_csv(file_name)
# カラム名を設定(適宜変更してください)
data.columns = ["label1", "label2", "label3", ..., "label14"]
# データを正規化
normalized_data = normalize_data(data)
print("Normalized data:")
print(normalized_data.head())
# データを標準化
standardized_data = standardize_data(data)
print("Standardized data:")
print(standardized_data.head())
if __name__ == "__main__":
main()
今回は、(最終的に全部試したのですが)標準化を中心に使っていきます。
3.3. 探索的データ解析
ここから、目次の2項に進みますが、2項の探索的データ解析のうち、目的変数と説明変数の分布についてはすでに外れ値調査のところで確認したため(折りたたんだところで紹介しました)省略します。
説明変数と目的変数の相関の確認から調べます。
相関係数を調べるためのコード
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
def show_correlations(data, feature_columns, target_column):
"""
相関係数を表示する関数。
Args:
- data: pandas DataFrame, データ
- feature_columns: list of str, 説明変数の列名のリスト
- target_column: str, 目的変数の列名
"""
# 説明変数と目的変数を含むデータフレーム
all_columns = feature_columns + [target_column]
sub_data = data[all_columns]
# 相関係数を計算
correlations = sub_data.corr()
# 相関係数を表示
print("Correlations:")
print(correlations)
def plot_scatter_matrix(data, feature_columns, target_column):
"""
散布図行列をプロットする関数。
Args:
- data: pandas DataFrame, データ
- feature_columns: list of str, 説明変数の列名のリスト
- target_column: str, 目的変数の列名
"""
# 説明変数と目的変数を含むデータフレーム
all_columns = feature_columns + [target_column]
sub_data = data[all_columns]
# 散布図行列をプロット
sns.pairplot(sub_data)
plt.show()
def main():
# CSVファイルからデータを読み込む(仮に"data.csv"とします)
data = pd.read_csv("data.csv")
# 説明変数の列名のリスト(仮に"feature1", "feature2", ...とします)
feature_columns = ["feature1", "feature2", "feature3", "feature4"]
# 目的変数の列名(仮に"target"とします)
target_column = "target"
# 相関係数を表示
show_correlations(data, feature_columns, target_column)
# 散布図行列をプロット
plot_scatter_matrix(data, feature_columns, target_column)
if __name__ == "__main__":
main()
これを実行すると、以下の Correlations と散布図が表示されます。
Correlations:
crim zn indus chas nox rm age dis rad tax ptratio black lstat medv
crim 1.000000 -0.200469 0.406583 -0.055892 0.420972 -0.219247 0.352734 -0.379670 0.625505 0.582764 0.289946 -0.385064 0.455621 -0.388305
zn -0.200469 1.000000 -0.533828 -0.042697 -0.516604 0.311991 -0.569537 0.664408 -0.311948 -0.314563 -0.391679 0.175520 -0.412995 0.360445
indus 0.406583 -0.533828 1.000000 0.062938 0.763651 -0.391676 0.644779 -0.708027 0.595129 0.720760 0.383248 -0.356977 0.603800 -0.483725
chas -0.055892 -0.042697 0.062938 1.000000 0.091203 0.091251 0.086518 -0.099176 -0.007368 -0.035587 -0.121515 0.048788 -0.053929 0.175260
nox 0.420972 -0.516604 0.763651 0.091203 1.000000 -0.302188 0.731470 -0.769230 0.611441 0.668023 0.188933 -0.380051 0.590879 -0.427321
rm -0.219247 0.311991 -0.391676 0.091251 -0.302188 1.000000 -0.240265 0.205246 -0.209847 -0.292048 -0.355501 0.128069 -0.613808 0.695360
age 0.352734 -0.569537 0.644779 0.086518 0.731470 -0.240265 1.000000 -0.747881 0.456022 0.506456 0.261515 -0.273534 0.602339 -0.376955
dis -0.379670 0.664408 -0.708027 -0.099176 -0.769230 0.205246 -0.747881 1.000000 -0.494588 -0.534432 -0.232471 0.291512 -0.496996 0.249929
rad 0.625505 -0.311948 0.595129 -0.007368 0.611441 -0.209847 0.456022 -0.494588 1.000000 0.910228 0.464741 -0.444413 0.488676 -0.381626
tax 0.582764 -0.314563 0.720760 -0.035587 0.668023 -0.292048 0.506456 -0.534432 0.910228 1.000000 0.460853 -0.441808 0.543993 -0.468536
ptratio 0.289946 -0.391679 0.383248 -0.121515 0.188933 -0.355501 0.261515 -0.232471 0.464741 0.460853 1.000000 -0.177383 0.374044 -0.507787
black -0.385064 0.175520 -0.356977 0.048788 -0.380051 0.128069 -0.273534 0.291512 -0.444413 -0.441808 -0.177383 1.000000 -0.366087 0.333461
lstat 0.455621 -0.412995 0.603800 -0.053929 0.590879 -0.613808 0.602339 -0.496996 0.488676 0.543993 0.374044 -0.366087 1.000000 -0.737663
medv -0.388305 0.360445 -0.483725 0.175260 -0.427321 0.695360 -0.376955 0.249929 -0.381626 -0.468536 -0.507787 0.333461 -0.737663 1.000000
Correlations の結果を見ると、目的変数 medv に対して、rm と lstat が相関係数 0.7 程度で大きく、次に indus, tax, ptratio が 0.5 と中程度のようです。
ただし、相関係数 0.7 あった rm と lstat については、それぞれの説明変数間の相関係数も-0.6 と大きいです。
このようなときは、どうしたらよいのでしょうか?
ChatGPT に相談してみた結果、説明変数として以下の順番で試してみようと思います。
- 相関関数が 0.7 程度の変数(rm と lstat)を使用する
- 相関関数が 0.5 程度の変数を追加する(indus, tax, ptratio)
- 全部の説明変数を使用する
- 場合によっては、PCAで次元削減する
4. 線形モデルで学習しよう!
4.1. 学習用データとテスト用データの分割
学習を始める前に、交差検証用にデータセットを分割します。
学習・テスト用データ作成コードと解説
データを学習用データとテスト用データに分割する方法について説明します。Pythonでは、scikit-learnという機械学習ライブラリがあります。このライブラリには、データを分割するためのtrain_test_split
関数が用意されています。
まず、scikit-learnをインストールしていない場合は、以下のコマンドでインストールしてください。
pip install scikit-learn
次に、以下のコードを使ってデータを学習用データとテスト用データに分割できます。
from sklearn.model_selection import train_test_split
def split_data(data, target, test_size=0.2, random_state=42):
"""
データを学習用データとテスト用データに分割する関数。
Args:
data (DataFrame): 説明変数のデータ
target (Series): 目的変数のデータ
test_size (float): テスト用データの割合(デフォルトは0.2)
random_state (int): 乱数生成のシード値(デフォルトは42)
Returns:
X_train, X_test, y_train, y_test: 分割後の学習用データとテスト用データ
"""
X_train, X_test, y_train, y_test = train_test_split(
data, target, test_size=test_size, random_state=random_state
)
return X_train, X_test, y_train, y_test
# main関数でデータ分割を実行
def main():
# ここでデータを読み込んで、説明変数と目的変数に分割してください。
# 例:
# data = ...
# target = ...
X_train, X_test, y_train, y_test = split_data(data, target)
print("X_train:")
print(X_train.head())
print("X_test:")
print(X_test.head())
print("y_train:")
print(y_train.head())
print("y_test:")
print(y_test.head())
if __name__ == "__main__":
main()
このコードは、説明変数のデータ(data
)と目的変数のデータ(target
)を受け取り、学習用データとテスト用データに分割します。test_size
はテスト用データの割合を指定し、random_state
は乱数生成のシード値を指定します。データがランダムに分割されるため、random_state
を指定しておくことで、再現性を確保できます。
4.2. 線形モデル
ここから線形モデルで学習を行います。
教えてもらった中から、まずは線形回帰と、リッジ回帰を試してみようと思います。
ChatGPTが出してくれたこれまでのコードをくっつけるように要求されたので完成版を貼っておきます。
import numpy as np
from sklearn.linear_model import LinearRegression, Ridge
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
import pandas as pd
from sklearn.metrics import r2_score
from sklearn.preprocessing import MinMaxScaler, StandardScaler
def split_data(data, target, test_size=0.2, random_state=42):
"""
データを学習用データとテスト用データに分割する関数。
Args:
data (DataFrame): 説明変数のデータ
target (Series): 目的変数のデータ
test_size (float): テスト用データの割合(デフォルトは0.2)
random_state (int): 乱数生成のシード値(デフォルトは42)
Returns:
X_train, X_test, y_train, y_test: 分割後の学習用データとテスト用データ
"""
X_train, X_test, y_train, y_test = train_test_split(
data, target, test_size=test_size, random_state=random_state
)
return X_train, X_test, y_train, y_test
def train_model(X_train, y_train, X_test, y_test, model_type='linear_regression', alpha=1.0):
"""
線形回帰またはリッジ回帰のモデルを学習し、テストデータに対する予測精度を評価する。
Parameters
----------
X_train: DataFrame
学習データの説明変数
y_train: DataFrame
学習データの目的変数
X_test: DataFrame
テストデータの説明変数
y_test: DataFrame
テストデータの目的変数
model_type: str, optional (default='linear_regression')
使用するモデルのタイプ。'linear_regression' または 'ridge_regression'
alpha: float, optional (default=1.0)
リッジ回帰の正則化パラメータ(リッジ回帰を使用する場合のみ)
Returns
-------
trained_model: LinearRegression or Ridge
学習済みのモデル
rmse: float
テストデータに対する平均二乗誤差の平方根(RMSE)
"""
if model_type == 'linear_regression':
model = LinearRegression()
elif model_type == 'ridge_regression':
model = Ridge(alpha=alpha)
else:
raise ValueError("Invalid model_type")
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
rmse = mean_squared_error(y_test, y_pred, squared=False)
r2 = r2_score(y_test, y_pred)
return model, rmse, r2
def standardize_data(data):
"""
データを標準化する関数。
Args:
data (pd.DataFrame): データフレーム
Returns:
pd.DataFrame: 標準化されたデータフレーム
"""
scaler = StandardScaler()
standardized_data = scaler.fit_transform(data)
return pd.DataFrame(standardized_data, columns=data.columns)
def main():
# CSVファイルからデータを読み込む(仮に"data.csv"とします)
import_df= pd.read_csv("data.csv")
# データを標準化する
df = standardize_data(import_df)
# 説明変数を絞る場合
feature_columns = ['rm','lstat']
# feature_columns = ['rm','lstat','indus','tax','ptratio']
# 全部の説明変数を使う場合
#feature_columns = df.columns.to_list()
#feature_columns.pop(0)
#feature_columns.remove("medv")
# 目的変数の設定
data = df.loc[:, feature_columns]
target = df["medv"]
# データの分割
X_train, X_test, y_train, y_test = split_data(data, target)
# 学習の実行
lr_model, lr_rmse, lr_r2 = train_model(X_train, y_train, X_test, y_test, model_type='linear_regression')
print("Linear Regression RMSE:", lr_rmse)
print("Linear Regression R^2:", lr_r2)
ridge_model, ridge_rmse, ridge_r2 = train_model(X_train, y_train, X_test, y_test, model_type='ridge_regression', alpha=1.0)
print("Ridge Regression RMSE:", ridge_rmse)
print("Ridge Regression R^2:", ridge_r2)
if __name__ == '__main__':
main()
これを実行すると以下の結果が表示されました。
Linear Regression RMSE: 0.6083546779594221
Linear Regression R^2: 0.573957741502586
Ridge Regression RMSE: 0.608195135682935
Ridge Regression R^2: 0.5741811731334953
上記の結果は、説明変数として相関が最も高い2個(rm, lstat)を使ったものになりますが、これを 3.3節で計画したように、説明変数を目的変数の相関の大きい順から 2個→5個→すべて のようにパターンを変えて実行すると、R2スコア は0.57→0.62→0.67 のように増加しました(R2スコアは1に近いほどよいです)。
今回のデータとモデルでは、説明変数は使えるだけ使った方が、よい結果になるようです。
一番よい結果になった、説明変数としてすべての変数を使った場合について、ChatGPT に評価をお願いしてみました。
ChatGPT のコメント通りアルファを調整したり、PCA (次元削減) を行ってみたりしたのですが、大きく変化がなかったため、別のモデルを適用したいと思います。
5. 機械学習モデルで学習しよう!
他に回帰分析としてどのようなモデルが使えるか聞いてみます。
(記事を書くのに)どれが良さそうかと考えて... 線形回帰と書いているもの以外を全部実装してもらうことにしました。
ここで、これまで出力されたコードに、テストデータの評価指標(R2スコアなど)しかないことに気づいたので、併せて学習用データの評価指標も表示するよう依頼しています。
機械学習回帰モデルを加えたコード
import numpy as np
from sklearn.linear_model import LinearRegression, Ridge
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
import pandas as pd
from sklearn.metrics import r2_score
from sklearn.preprocessing import MinMaxScaler, StandardScaler
from sklearn.neighbors import KNeighborsRegressor
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.svm import SVR
def split_data(data, target, test_size=0.2, random_state=42):
"""
データを学習用データとテスト用データに分割する関数。
Args:
data (DataFrame): 説明変数のデータ
target (Series): 目的変数のデータ
test_size (float): テスト用データの割合(デフォルトは0.2)
random_state (int): 乱数生成のシード値(デフォルトは42)
Returns:
X_train, X_test, y_train, y_test: 分割後の学習用データとテスト用データ
"""
X_train, X_test, y_train, y_test = train_test_split(
data, target, test_size=test_size, random_state=random_state
)
return X_train, X_test, y_train, y_test
def train_model(X_train, y_train, X_test, y_test, model_type='linear_regression', alpha=1.0, k=5):
"""
線形回帰またはリッジ回帰のモデルを学習し、テストデータに対する予測精度を評価する。
Parameters
----------
X_train: DataFrame
学習データの説明変数
y_train: DataFrame
学習データの目的変数
X_test: DataFrame
テストデータの説明変数
y_test: DataFrame
テストデータの目的変数
model_type: str, optional (default='linear_regression')
使用するモデルのタイプ。'linear_regression' または 'ridge_regression'
alpha: float, optional (default=1.0)
リッジ回帰の正則化パラメータ(リッジ回帰を使用する場合のみ)
Returns
-------
trained_model: LinearRegression or Ridge
学習済みのモデル
rmse: float
テストデータに対する平均二乗誤差の平方根(RMSE)
"""
if model_type == 'linear_regression':
model = LinearRegression()
elif model_type == 'ridge_regression':
model = Ridge(alpha=alpha)
elif model_type == 'knn_regression':
model = KNeighborsRegressor(n_neighbors=k)
elif model_type == 'decision_tree_regression':
model = DecisionTreeRegressor()
elif model_type == 'random_forest_regression':
model = RandomForestRegressor()
elif model_type == 'gradient_boosting_regression':
model = GradientBoostingRegressor()
elif model_type == 'svr':
model = SVR()
else:
raise ValueError("Invalid model_type")
model.fit(X_train, y_train)
y_pred_test = model.predict(X_test)
y_pred_train = model.predict(X_train)
rmse_test = mean_squared_error(y_test, y_pred_test, squared=False)
r2_test = r2_score(y_test, y_pred_test)
rmse_train = mean_squared_error(y_train, y_pred_train, squared=False)
r2_train = r2_score(y_train, y_pred_train)
return model, rmse_test, r2_test, rmse_train, r2_train
def standardize_data(data):
"""
データを標準化する関数。
Args:
data (pd.DataFrame): データフレーム
Returns:
pd.DataFrame: 標準化されたデータフレーム
"""
scaler = StandardScaler()
standardized_data = scaler.fit_transform(data)
return pd.DataFrame(standardized_data, columns=data.columns)
def main():
# CSVファイルからデータを読み込む(仮に"data.csv"とします)
import_df= pd.read_csv("data.csv")
# データを標準化する
df = standardize_data(import_df)
# feature_columns = ['rm','lstat']
# feature_columns = ['rm','lstat','indus','tax','ptratio']
feature_columns = df.columns.to_list()
feature_columns.pop(0)
feature_columns.remove("medv")
data = df.loc[:, feature_columns]
target = df["medv"]
X_train, X_test, y_train, y_test = split_data(data, target)
# 以下のコードで各モデルを学習・評価します。
# 必要に応じて、alphaやkなどのハイパーパラメータを調整してください。
for model_type in ['linear_regression', 'ridge_regression', 'knn_regression', 'decision_tree_regression', 'random_forest_regression', 'gradient_boosting_regression', 'svr']:
model, rmse_test, r2_test, rmse_train, r2_train = train_model(X_train, y_train, X_test, y_test, model_type=model_type)
print(f"{model_type} Test RMSE: {rmse_test}")
print(f"{model_type} Test R^2: {r2_test}")
print(f"{model_type} Train RMSE: {rmse_train}")
print(f"{model_type} Train R^2: {r2_train}")
print("----")
if __name__ == '__main__':
main()
これを実行すると以下の結果が表示されます。
linear_regression Test RMSE: 0.5364166292540398
linear_regression Test R^2: 0.6687594935356322
linear_regression Train RMSE: 0.5063155571752505
linear_regression Train R^2: 0.7508856358979672
----
ridge_regression Test RMSE: 0.5366751346847491
ridge_regression Test R^2: 0.6684401592810275
ridge_regression Train RMSE: 0.5063313819719012
ridge_regression Train R^2: 0.7508700636102706
----
knn_regression Test RMSE: 0.4946699331742575
knn_regression Test R^2: 0.7183109257429845
knn_regression Train RMSE: 0.3934579403570154
knn_regression Train R^2: 0.8495636267791526
----
decision_tree_regression Test RMSE: 0.3827256591991545
decision_tree_regression Test R^2: 0.8313780396754408
decision_tree_regression Train RMSE: 6.628279193576708e-17
decision_tree_regression Train R^2: 1.0
----
random_forest_regression Test RMSE: 0.3316634536781134
random_forest_regression Test R^2: 0.8733706982687588
random_forest_regression Train RMSE: 0.15477221689931248
random_forest_regression Train R^2: 0.9767221838953121
----
gradient_boosting_regression Test RMSE: 0.2760124393513248
gradient_boosting_regression Test R^2: 0.9123006680633332
gradient_boosting_regression Train RMSE: 0.14335420352795925
gradient_boosting_regression Train R^2: 0.9800300447996301
----
svr Test RMSE: 0.39400542527434923
svr Test R^2: 0.8212922535756355
svr Train RMSE: 0.3303824300017988
svr Train R^2: 0.8939305999039542
----
線形モデルでは、R2スコアは最大 0.67 だったのに比べて、新しく追加したモデルではすべて 0.7 以上と、スコアがかなりよくなったように見えます。
ChatGPT に解説を依頼しました。
パラメータは一切調整していないので他の手法がよくなる可能性はありますが、今回のデータでは、線形回帰よりもランダムフォレスト回帰や勾配ブースティング回帰などの非線形回帰かつアンサンブルを行っている手法の方が良さそうです。
記事を書くために、追加でやってみるかと適当に試してみただけなのに、この短時間でR2スコアが 0.7 程度から 0.9 程度まで上がるのかと驚きました(過学習の懸念があるため、仕事の場合はもう少し掘り下げる必要があるとは思います)。
今回のこの記事を書くためのChatGPTと相談作業全体でかかった時間は4時間程度で、色々と余計なことを聞いた分を含めても非常に効率的ではないかなと思います!
6. まとめ
今回は、ChatGPTとBoston Housingデータセットを使って回帰分析を行ってみました。
皆様ぜひとも自分のデータで、お手元で色々と聞いてみながら遊んでみてください!
参考になれば嬉しいです。
モチベーションupのために、ぜひとも、参考になればいいねをお願いします!