マルチターゲット回帰の方法について
タイトルにあるようにKaggleのコンペティション(SteelPrice)で、複数のターゲットが存在する。学習を行うことになり、初めてであったので記録として残そうと思います。
"まとめ"にプログラムの全容を記載しています。解説いらんという方は、そちらを見てください。
環境
- Python
- GoogleColabratory
- Lasso回帰
- MultiOutputRegressor
データの相関関係
今回ターゲットとなるデータが
['Pastry', 'Z_Scratch', 'K_Scatch', 'Stains', 'Dirtiness', 'Bumps', 'Other_Faults']
となっている。またデータ数も多いのでターゲットカラムとそれ以外のカラムの相関関係を出力したい訳です。
後々学習の際に見直すことを考えてファイルで出力します。
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('train.csv')
correlation_matrix = df.corr()
target_features = ['Pastry', 'Z_Scratch', 'K_Scatch', 'Stains', 'Dirtiness', 'Bumps', 'Other_Faults']
other_features = [col for col in correlation_matrix.columns if col not in target_features]
correlation_df = correlation_matrix[target_features].loc[other_features]
correlation_df.to_csv('correlation_matrix.csv')
うむ。うまくいったが、関連度の低いターゲットがいっぱいいる...
欠損値はあるのか
下記を実行して欠損値があるならそのカラムを出力するようにします。
import pandas as pd
df = pd.read_csv('train.csv')
missing_values = df.isna().sum()
columns_with_missing_values = missing_values[missing_values > 0]
if columns_with_missing_values.empty:
print("No missing values found.")
else:
print("Missing values found in the following columns:")
print(columns_with_missing_values)
実行結果
No missing values found.
うむ。なし!置換や代入がないのでスタート地点はみんな同じか。
カラムも全部出力しておくか
import pandas as pd
data = pd.read_csv('data.csv')
all_columns = list(data.columns)
print("All Columns:", all_columns)
縦だと場所を取るので、わざとリストで出力しました。
All Columns: ['id', 'X_Minimum', 'X_Maximum', 'Y_Minimum', 'Y_Maximum', 'Pixels_Areas', 'X_Perimeter', 'Y_Perimeter', 'Sum_of_Luminosity', 'Minimum_of_Luminosity', 'Maximum_of_Luminosity', 'Length_of_Conveyer', 'TypeOfSteel_A300', 'TypeOfSteel_A400', 'Steel_Plate_Thickness', 'Edges_Index', 'Empty_Index', 'Square_Index', 'Outside_X_Index', 'Edges_X_Index', 'Edges_Y_Index', 'Outside_Global_Index', 'LogOfAreas', 'Log_X_Index', 'Log_Y_Index', 'Orientation_Index', 'Luminosity_Index', 'SigmoidOfAreas', 'Pastry', 'Z_Scratch', 'K_Scatch', 'Stains', 'Dirtiness', 'Bumps', 'Other_Faults']
学習と正答率
ここまでデータの状態がわかったので、MultiOutputRegressorとLasso回帰を使ってマルチターゲット回帰を行っていきます。
ライブラリの用意
こちらを使用します。
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.multioutput import MultiOutputRegressor
from sklearn.linear_model import Lasso
from sklearn.metrics import mean_squared_error
ここは脳死でデータの読み込み
df = pd.read_csv('train.csv')
学習データの定義
ターゲット複数に対してそれぞれ特徴量を定義します。今回は相関関係の高い順に3つ使いました。(3つ目以降のほとんどが相関係数が低すぎて使えなさそう...3つも怪しいくらい)
X_target1 = df[['Orientation_Index', 'Outside_Global_Index', 'Edges_Y_Index']]
y_target1 = df['Pastry']
X_target2 = df[['Length_of_Conveyer', 'TypeOfSteel_A300', 'TypeOfSteel_A400']]
y_target2 = df['Z_Scratch']
X_target3 = df[['Log_X_Index', 'Outside_X_Index', 'Pixels_Areas']] #相関が高いものが多い
y_target3 = df['K_Scatch']
X_target4 = df[['Log_Y_Index', 'LogOfAreas', 'SigmoidOfAreas']]
y_target4 = df['Stains']
X_target5 = df[['Orientation_Index', 'Square_Index', 'Edges_X_Index']]
y_target5 = df['Dirtiness']
X_target6 = df[['LogOfAreas', 'TypeOfSteel_A400', 'Pixels_Areas']]
y_target6 = df['Bumps']
X_target7 = df[['Pixels_Areas', 'Outside_X_Index', 'Minimum_of_Luminosity']]
y_target7 = df['Other_Faults']
trainデータを学習用とテスト用に分割
8対2の割合で学習データとテストデータに分割。再現性がないと困るのでseed値は1で固定します。
X_train_target1, X_test_target1, y_train_target1, y_test_target1 = train_test_split(X_target1, y_target1, test_size=0.2, random_state=1)
X_train_target2, X_test_target2, y_train_target2, y_test_target2 = train_test_split(X_target2, y_target2, test_size=0.2, random_state=1)
X_train_target3, X_test_target3, y_train_target3, y_test_target3 = train_test_split(X_target3, y_target3, test_size=0.2, random_state=1)
X_train_target4, X_test_target4, y_train_target4, y_test_target4 = train_test_split(X_target4, y_target4, test_size=0.2, random_state=1)
X_train_target5, X_test_target5, y_train_target5, y_test_target5 = train_test_split(X_target5, y_target5, test_size=0.2, random_state=1)
X_train_target6, X_test_target6, y_train_target6, y_test_target6 = train_test_split(X_target6, y_target6, test_size=0.2, random_state=1)
X_train_target7, X_test_target7, y_train_target7, y_test_target7 = train_test_split(X_target7, y_target7, test_size=0.2, random_state=1)
データの前処理
ここに来てなんとエラー
ValueError: y must have at least two dimensions for multi-output regression but has only one.
"y_train_target1" などのラベルが1次元になっており、"MultiOutputRegressor"を使用する場合は、少なくとも2次元である必要があるみたいなので、データを二次元配列に変換します。
y_train_target1 = y_train_target1.values.reshape(-1, 1)
y_train_target2 = y_train_target2.values.reshape(-1, 1)
y_train_target3 = y_train_target3.values.reshape(-1, 1)
y_train_target4 = y_train_target4.values.reshape(-1, 1)
y_train_target5 = y_train_target5.values.reshape(-1, 1)
y_train_target6 = y_train_target6.values.reshape(-1, 1)
y_train_target7 = y_train_target7.values.reshape(-1, 1)
プログラム書いてるくせに書き方がキモいとか言われそう...(for文知らないのかよ!)
学習モデルの定義
Lasso回帰を使用します。alpha値は0.001にしておきます。仮です。
model = MultiOutputRegressor(Lasso(alpha=0.001))
なんとこれだけでマルチターゲット回帰の宣言終わり!簡単でいいですね。
学習&予測
学習と予測は連続して行わないと、エラー吐かれます。ダメなのはわかるのですが、行けそうな気もしていたので学習ですね!
model.fit(X_train_target1, y_train_target1)
predictions_target1 = model.predict(X_test_target1)
model.fit(X_train_target2, y_train_target2)
predictions_target2 = model.predict(X_test_target2)
model.fit(X_train_target3, y_train_target3)
predictions_target3 = model.predict(X_test_target3)
model.fit(X_train_target4, y_train_target4)
predictions_target4 = model.predict(X_test_target4)
model.fit(X_train_target5, y_train_target5)
predictions_target5 = model.predict(X_test_target5)
model.fit(X_train_target6, y_train_target6)
predictions_target6 = model.predict(X_test_target6)
model.fit(X_train_target7, y_train_target7)
predictions_target7 = model.predict(X_test_target7)
正答率の計算
回帰を使用しているので、平均二乗誤差(Mean Squared Error)を使用してモデルの性能評価を行います。
mse_target1 = mean_squared_error(y_test_target1, predictions_target1)
mse_target2 = mean_squared_error(y_test_target2, predictions_target2)
mse_target3 = mean_squared_error(y_test_target3, predictions_target3)
mse_target4 = mean_squared_error(y_test_target4, predictions_target4)
mse_target5 = mean_squared_error(y_test_target5, predictions_target5)
mse_target6 = mean_squared_error(y_test_target6, predictions_target6)
mse_target7 = mean_squared_error(y_test_target7, predictions_target7)
MSEの評価
MSEを出力します。
print("MSE 'Pastry' :", mse_target1)
print("MSE 'Z_Scratch' :", mse_target2)
print("MSE 'K_Scatch' :", mse_target3)
print("MSE 'Stains' :", mse_target4)
print("MSE 'Dirtiness' :", mse_target5)
print("MSE 'Bumps' :", mse_target6)
print("MSE 'Other_Faults' :", mse_target7)
結果
見やすいように数字の位置を合わせています。
MSE 'Pastry' : 0.06834896739943769
MSE 'Z_Scratch' : 0.04940205934849414
MSE 'K_Scatch' : 0.06815981558091297
MSE 'Stains' : 0.02792760176886392
MSE 'Dirtiness' : 0.023607892570564698
MSE 'Bumps' : 0.16304136101402303
MSE 'Other_Faults' : 0.21524543436960605
特徴量の性能の良かった'K_Scatch'の性能が悪い...かなり相関係数の高いものを使用してるので、特徴量を追加して精度がどうなるか分かりませんが追加するかー。
'Bumps'と'Other_Faults'に関しては少なくとも、もう一桁制度をあげたい。
モデルもそのデータによって再度alpha値を調整した方がいいのか...めんどくさい!
さて、調整します。
データの調整
とりあえず、特徴量を全体的に3つずつ追加してみる。
X_target1 = df[['Orientation_Index', 'Outside_Global_Index', 'Edges_Y_Index', 'Log_X_Index', 'Length_of_Conveyer', 'Outside_X_Index']]
y_target1 = df['Pastry']
X_target2 = df[['Length_of_Conveyer', 'TypeOfSteel_A300', 'TypeOfSteel_A400', 'X_Maximum', 'X_Minimum', 'Outside_X_Index']]
y_target2 = df['Z_Scratch']
X_target3 = df[['Log_X_Index', 'Outside_X_Index', 'Pixels_Areas', 'LogOfAreas', 'X_Perimeter', 'Edges_Y_Index']] #相関が高いものが多い
y_target3 = df['K_Scatch']
X_target4 = df[['Log_Y_Index', 'LogOfAreas', 'SigmoidOfAreas', 'Edges_X_Index', 'Luminosity_Index', 'Minimum_of_Luminosity']]
y_target4 = df['Stains']
X_target5 = df[['Orientation_Index', 'Square_Index', 'Edges_X_Index', 'Outside_Global_Index', 'Log_X_Index', 'Edges_Y_Index']]
y_target5 = df['Dirtiness']
X_target6 = df[['LogOfAreas', 'TypeOfSteel_A400', 'Pixels_Areas', 'TypeOfSteel_A300', 'Outside_X_Index', 'X_Perimeter']]
y_target6 = df['Bumps']
X_target7 = df[['Pixels_Areas', 'Outside_X_Index', 'Minimum_of_Luminosity', 'Log_X_Index', 'Steel_Plate_Thickness', 'X_Perimeter']]
y_target7 = df['Other_Faults']
として実行してみる。
結果
ファイル名をつけるために適当な拡張子を付けました。ただ出力された値を記載しているだけです。
MSE 'Pastry' : 0.06834896739943769
MSE 'Z_Scratch' : 0.04940205934849414
MSE 'K_Scatch' : 0.06815981558091297
MSE 'Stains' : 0.02792760176886392
MSE 'Dirtiness' : 0.023607892570564698
MSE 'Bumps' : 0.16304136101402303
MSE 'Other_Faults' : 0.21524543436960605
MSE 'Pastry' : 0.0676160173976996
MSE 'Z_Scratch' : 0.04840064473757987
MSE 'K_Scatch' : 0.06848476108322626
MSE 'Stains' : 0.02663592697816165
MSE 'Dirtiness' : 0.02353702191233374
MSE 'Bumps' : 0.16301131144048964
MSE 'Other_Faults' : 0.20807185260098826
懸念していた'K_Scatch'の精度は落ちていますね。学習勾配を変更した方が良さそうですね。
他についても精度は良くなっていますが、まだ精度が良いとは言えないレベル。こっちも学習勾配の変化が必要そう。
'K_Scatch'は相関の低い2つ学習データを削除し、4つでの学習にします。それ以外の特徴量はそのままで学習勾配を0.001から0.01に変化させてどうなるかを確認します。
変更箇所は
X_target1 = df[['Orientation_Index', 'Outside_Global_Index', 'Edges_Y_Index', 'Log_X_Index', 'Length_of_Conveyer', 'Outside_X_Index']]
y_target1 = df['Pastry']
X_target2 = df[['Length_of_Conveyer', 'TypeOfSteel_A300', 'TypeOfSteel_A400', 'X_Maximum', 'X_Minimum', 'Outside_X_Index']]
y_target2 = df['Z_Scratch']
X_target3 = df[['Log_X_Index', 'Outside_X_Index', 'Pixels_Areas', 'LogOfAreas']] #相関が高いものが多い
y_target3 = df['K_Scatch']
X_target4 = df[['Log_Y_Index', 'LogOfAreas', 'SigmoidOfAreas', 'Edges_X_Index', 'Luminosity_Index', 'Minimum_of_Luminosity']]
y_target4 = df['Stains']
X_target5 = df[['Orientation_Index', 'Square_Index', 'Edges_X_Index', 'Outside_Global_Index', 'Log_X_Index', 'Edges_Y_Index']]
y_target5 = df['Dirtiness']
X_target6 = df[['LogOfAreas', 'TypeOfSteel_A400', 'Pixels_Areas', 'TypeOfSteel_A300', 'Outside_X_Index', 'X_Perimeter']]
y_target6 = df['Bumps']
X_target7 = df[['Pixels_Areas', 'Outside_X_Index', 'Minimum_of_Luminosity', 'Log_X_Index', 'Steel_Plate_Thickness', 'X_Perimeter']]
y_target7 = df['Other_Faults']
model = MultiOutputRegressor(Lasso(alpha=0.01))
ですね。はい、実行。
精度の良かった前のデータを持ってきます。'K_Scatch'は精度の良かった最初のデータです。↓
MSE 'Pastry' : 0.0676160173976996
MSE 'Z_Scratch' : 0.04840064473757987
MSE 'K_Scatch' : 0.06815981558091297
MSE 'Stains' : 0.02663592697816165
MSE 'Dirtiness' : 0.02353702191233374
MSE 'Bumps' : 0.16301131144048964
MSE 'Other_Faults' : 0.20807185260098826
今回の実行結果です。↓
MSE 'Pastry' : 0.06820767544813114
MSE 'Z_Scratch' : 0.04898136736422356
MSE 'K_Scatch' : 0.07592953406629283
MSE 'Stains' : 0.028910339561494438
MSE 'Dirtiness' : 0.02423738881462277
MSE 'Bumps' : 0.16589850482986754
MSE 'Other_Faults' : 0.20832321573505103
はい、ダメーー!思い切って100分の1倍にします。0.00001ですね。
MSE 'Pastry' : 0.06741909207920335
MSE 'Z_Scratch' : 0.04807136953579291
MSE 'K_Scatch' : 0.059975997453810354
MSE 'Stains' : 0.0260076623441441
MSE 'Dirtiness' : 0.02347539503162303
MSE 'Bumps' : 0.16243374781873085
MSE 'Other_Faults' : 0.207402213170553
精度は良くなっているが、良い制度とは言えない。
一回提出してみる。
予測&提出
MultiOutputRegressorを使用していましたが、意味がないことがわかったので、使わないようにします。
ライブラリ
import pandas as pd
from sklearn.linear_model import Lasso
データの読み込み
脳死です。
train_df = pd.read_csv('train.csv')
test_df = pd.read_csv('test.csv')
特徴量の定義
今回はfor文を使用して学習もできるよ!書けるし!ってことで、このように特徴量を定義します。
features_by_target = {
'Pastry': ['Orientation_Index', 'Outside_Global_Index', 'Edges_Y_Index', 'Log_X_Index', 'Length_of_Conveyer', 'Outside_X_Index'],
'Z_Scratch': ['Length_of_Conveyer', 'TypeOfSteel_A300', 'TypeOfSteel_A400', 'X_Maximum', 'X_Minimum', 'Outside_X_Index'],
'K_Scatch': ['Log_X_Index', 'Outside_X_Index', 'Pixels_Areas', 'LogOfAreas'],
'Stains': ['Log_Y_Index', 'LogOfAreas', 'SigmoidOfAreas', 'Edges_X_Index', 'Luminosity_Index', 'Minimum_of_Luminosity'],
'Dirtiness': ['Orientation_Index', 'Square_Index', 'Edges_X_Index', 'Outside_Global_Index', 'Log_X_Index', 'Edges_Y_Index'],
'Bumps': ['LogOfAreas', 'TypeOfSteel_A400', 'Pixels_Areas', 'TypeOfSteel_A300', 'Outside_X_Index', 'X_Perimeter'],
'Other_Faults': ['Pixels_Areas', 'Outside_X_Index', 'Minimum_of_Luminosity', 'Log_X_Index', 'Steel_Plate_Thickness', 'X_Perimeter'],
}
予測結果を格納容器を定義
マルチターゲットなのでこのように定義します。
predictions_df = pd.DataFrame(columns=['id'] + list(features_by_target.keys()))
predictions_df['id'] = test_df['id']
学習&予測
for文で短くなりすぎ!最初から書けば良かった。
for target, features in features_by_target.items():
X_train = train_df[features]
y_train = train_df[target]
model = Lasso(alpha=0.00001)
model.fit(X_train, y_train)
predictions_df[target] = model.predict(test_df[features])
ファイルの書き出し
結果を代入してcsvファイルとして書き出します。
predictions_df.to_csv('multioutput_predictions.csv', index=False)
まぁこれだけですよね。
提出
みなさん気になるスコアですね。「提出物は、予測された確率と地上の真実目標を使用して、ROC曲線の下の領域を使用して評価されます。」とのことなので、1に近ければ良いですね。現時点で、
スコア: 0.81729
順位: 1398
1位の方のスコアが
スコア: 0.89772
なので密集していますね。0.85
くらいは行きたいな。
まとめ
相関関係&欠損値
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('train.csv')
correlation_matrix = df.corr()
target_features = ['Pastry', 'Z_Scratch', 'K_Scatch', 'Stains', 'Dirtiness', 'Bumps', 'Other_Faults']
other_features = [col for col in correlation_matrix.columns if col not in target_features]
correlation_df = correlation_matrix[target_features].loc[other_features]
correlation_df.to_csv('correlation_matrix.csv')
missing_values = df.isna().sum()
columns_with_missing_values = missing_values[missing_values > 0]
if columns_with_missing_values.empty:
print("No missing values found.")
else:
print("Missing values found in the following columns:")
print(columns_with_missing_values)
トレーニング用
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import Lasso
from sklearn.metrics import mean_squared_error
X_target1 = df[['Orientation_Index', 'Outside_Global_Index', 'Edges_Y_Index', 'Log_X_Index', 'Length_of_Conveyer', 'Outside_X_Index']]
y_target1 = df['Pastry']
X_target2 = df[['Length_of_Conveyer', 'TypeOfSteel_A300', 'TypeOfSteel_A400', 'X_Maximum', 'X_Minimum', 'Outside_X_Index']]
y_target2 = df['Z_Scratch']
X_target3 = df[['Log_X_Index', 'Outside_X_Index', 'Pixels_Areas', 'LogOfAreas']] #相関が高いものが多い
y_target3 = df['K_Scatch']
X_target4 = df[['Log_Y_Index', 'LogOfAreas', 'SigmoidOfAreas', 'Edges_X_Index', 'Luminosity_Index', 'Minimum_of_Luminosity']]
y_target4 = df['Stains']
X_target5 = df[['Orientation_Index', 'Square_Index', 'Edges_X_Index', 'Outside_Global_Index', 'Log_X_Index', 'Edges_Y_Index']]
y_target5 = df['Dirtiness']
X_target6 = df[['LogOfAreas', 'TypeOfSteel_A400', 'Pixels_Areas', 'TypeOfSteel_A300', 'Outside_X_Index', 'X_Perimeter']]
y_target6 = df['Bumps']
X_target7 = df[['Pixels_Areas', 'Outside_X_Index', 'Minimum_of_Luminosity', 'Log_X_Index', 'Steel_Plate_Thickness', 'X_Perimeter']]
y_target7 = df['Other_Faults']
model = Lasso(alpha=0.00001)
model.fit(X_target1, y_target1)
predictions_target1 = model.predict(X_target1)
model.fit(X_target2, y_target2)
predictions_target2 = model.predict(X_target2)
model.fit(X_target3, y_target3)
predictions_target3 = model.predict(X_target3)
model.fit(X_target4, y_target4)
predictions_target4 = model.predict(X_target4)
model.fit(X_target5, y_target5)
predictions_target5 = model.predict(X_target5)
model.fit(X_target6, y_target6)
predictions_target6 = model.predict(X_target6)
model.fit(X_target7, y_target7)
predictions_target7 = model.predict(X_target7)
mse_target1 = mean_squared_error(y_target1, predictions_target1)
mse_target2 = mean_squared_error(y_target2, predictions_target2)
mse_target3 = mean_squared_error(y_target3, predictions_target3)
mse_target4 = mean_squared_error(y_target4, predictions_target4)
mse_target5 = mean_squared_error(y_target5, predictions_target5)
mse_target6 = mean_squared_error(y_target6, predictions_target6)
mse_target7 = mean_squared_error(y_target7, predictions_target7)
print("MSE 'Pastry' :", mse_target1)
print("MSE 'Z_Scratch' :", mse_target2)
print("MSE 'K_Scatch' :", mse_target3)
print("MSE 'Stains' :", mse_target4)
print("MSE 'Dirtiness' :", mse_target5)
print("MSE 'Bumps' :", mse_target6)
print("MSE 'Other_Faults' :", mse_target7)
予測用
import pandas as pd
from sklearn.linear_model import Lasso
train_df = pd.read_csv('train.csv')
test_df = pd.read_csv('test.csv')
features_by_target = {
'Pastry': ['Orientation_Index', 'Outside_Global_Index', 'Edges_Y_Index', 'Log_X_Index', 'Length_of_Conveyer', 'Outside_X_Index'],
'Z_Scratch': ['Length_of_Conveyer', 'TypeOfSteel_A300', 'TypeOfSteel_A400', 'X_Maximum', 'X_Minimum', 'Outside_X_Index'],
'K_Scatch': ['Log_X_Index', 'Outside_X_Index', 'Pixels_Areas', 'LogOfAreas'],
'Stains': ['Log_Y_Index', 'LogOfAreas', 'SigmoidOfAreas', 'Edges_X_Index', 'Luminosity_Index', 'Minimum_of_Luminosity'],
'Dirtiness': ['Orientation_Index', 'Square_Index', 'Edges_X_Index', 'Outside_Global_Index', 'Log_X_Index', 'Edges_Y_Index'],
'Bumps': ['LogOfAreas', 'TypeOfSteel_A400', 'Pixels_Areas', 'TypeOfSteel_A300', 'Outside_X_Index', 'X_Perimeter'],
'Other_Faults': ['Pixels_Areas', 'Outside_X_Index', 'Minimum_of_Luminosity', 'Log_X_Index', 'Steel_Plate_Thickness', 'X_Perimeter'],
}
predictions_df = pd.DataFrame(columns=['id'] + list(features_by_target.keys()))
predictions_df['id'] = test_df['id']
for target, features in features_by_target.items():
X_train = train_df[features]
y_train = train_df[target]
model = Lasso(alpha=0.00001)
model.fit(X_train, y_train)
predictions_df[target] = model.predict(test_df[features])
predictions_df.to_csv('multioutput_predictions.csv', index=False)
後日談
id以外の全カラムを使用し、正規化し他ものをデータとし学習させ、予測すると
スコア: 0.85509
となりました。まさかの脳筋が一番点数高いという...あとはモデル選択かな...
import pandas as pd
from sklearn.linear_model import Lasso
from sklearn.preprocessing import StandardScaler
train_df = pd.read_csv('train.csv')
X_train = train_df.drop(columns=['id', 'Pastry', 'Z_Scratch', 'K_Scatch', 'Stains', 'Dirtiness', 'Bumps', 'Other_Faults'])
targets = ['Pastry', 'Z_Scratch', 'K_Scatch', 'Stains', 'Dirtiness', 'Bumps', 'Other_Faults']
y_train = train_df[targets]
scaler = StandardScaler()
X_train_normalized = scaler.fit_transform(X_train)
model = Lasso(alpha=0.001)
model.fit(X_train_normalized, y_train)
test_df = pd.read_csv('test.csv')
X_test = test_df.drop(columns=['id'])
X_test_normalized = scaler.transform(X_test)
predictions_df = pd.DataFrame(columns=['id'] + targets)
predictions_df['id'] = test_df['id']
test_predictions = model.predict(X_test_normalized)
for i, target in enumerate(targets):
predictions_df[target] = test_predictions[:, i]
predictions_df.to_csv('test_predictions.csv', index=False)