{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":11000,"databundleVersionId":875412,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport pandas as pd\nimport numpy as np\nimport keras\nfrom keras.layers import (BatchNormalization,Flatten,Convolution1D,Activation,Input,Dense,LSTM, GRU)\nfrom keras import losses, models, optimizers\nfrom sklearn.metrics import mean_absolute_error\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import StandardScaler\nfrom tqdm import tqdm\nimport warnings\n\n!pip install git+https://github.com/nengo/keras-lmu --no-deps\n!pip install humanfriendly\nimport humanfriendly\nfrom keras_lmu import LMU\nfrom time import time\n\nwarnings.filterwarnings('ignore')\n%matplotlib inline ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T06:10:14.816907Z","iopub.execute_input":"2024-11-30T06:10:14.817483Z","iopub.status.idle":"2024-11-30T06:10:58.218356Z","shell.execute_reply.started":"2024-11-30T06:10:14.817409Z","shell.execute_reply":"2024-11-30T06:10:58.216840Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Dataset\n\nСчитываем тренировочный датасет. Из-за большого размера считываем его по чанкам, т.к. так эффективнее и не переполняется RAM","metadata":{}},{"cell_type":"code","source":"chunksize=10000000\ndata_path = '../input/LANL-Earthquake-Prediction/train.csv'\ntotal = 63\n\ndf_iter = pd.read_csv(data_path, \n                    dtype={'acoustic_data': np.int16, 'time_to_failure':np.float64}, chunksize=chunksize, iterator=True)\ndf = None\nfor iter_num, chunk in tqdm(enumerate(df_iter, 1), total=total):\n    df = chunk if df is None else pd.concat([df, chunk])\nprint(f'Dataset shape:{df.shape}')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T06:10:58.220787Z","iopub.execute_input":"2024-11-30T06:10:58.221857Z","iopub.status.idle":"2024-11-30T06:16:34.589647Z","shell.execute_reply.started":"2024-11-30T06:10:58.221813Z","shell.execute_reply":"2024-11-30T06:16:34.587321Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Разбиваем его на train и test (в нашем случае в отношении 70/30)","metadata":{}},{"cell_type":"code","source":"# Делим на train и test\n# ВАЖНО: не перемешиваем данные, т.к. имеем дело с временным рядом, где порядок данных несет информацию\ndata_train, data_test = train_test_split(df, test_size=0.3, shuffle=False)\nprint(f'Train shape: {data_train.shape}')\nprint(f'Test shape: {data_test.shape}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T06:16:34.592906Z","iopub.execute_input":"2024-11-30T06:16:34.593542Z","iopub.status.idle":"2024-11-30T06:16:50.972517Z","shell.execute_reply.started":"2024-11-30T06:16:34.593448Z","shell.execute_reply":"2024-11-30T06:16:50.971228Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Data Exploration\nИзобразим на графике данные по сейсмической активности и данные по вермени до сейсмического толчка","metadata":{}},{"cell_type":"code","source":"acoustic = df['acoustic_data'].values[::100]\ntime_to_failure = df['time_to_failure'].values[::100]\n\ndef plot_acc_ttf_data(title='Acoustic data and time to failure: sampled 1%'):\n    fig ,ax1 = plt.subplots(figsize=(12,9))\n    plt.title(title)\n    plt.plot(acoustic,color='r')\n    ax1.set_ylabel('train_acoustic_df',color='r')\n    plt.legend(['acoustic-data'],loc=(0.01,0.95))\n    ax2 = ax1.twinx()\n    plt.plot(time_to_failure,color='b')\n    ax2.set_ylabel('time to failure',color='b')\n    plt.legend(['time_to_failure'],loc=(0.01,0.9))\n    plt.grid(True)\nplot_acc_ttf_data()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T06:16:50.974258Z","iopub.execute_input":"2024-11-30T06:16:50.974708Z","iopub.status.idle":"2024-11-30T06:16:54.316813Z","shell.execute_reply.started":"2024-11-30T06:16:50.974671Z","shell.execute_reply":"2024-11-30T06:16:54.315535Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Видим, что время толчка совпадает с пиками сигнала. Однако при ближайшем рассмотрении","metadata":{}},{"cell_type":"code","source":"# del time_to_failure\n# del acoustic\n\nacoustic = df['acoustic_data'].values[:6291455]\ntime_to_failure = df['time_to_failure'].values[:6291455]\nplot_acc_ttf_data()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T06:16:54.319608Z","iopub.execute_input":"2024-11-30T06:16:54.320021Z","iopub.status.idle":"2024-11-30T06:16:56.913637Z","shell.execute_reply.started":"2024-11-30T06:16:54.319974Z","shell.execute_reply":"2024-11-30T06:16:56.912392Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Видим, что измерение чуть запаздывает. Это скорее всего связано с оборудованием, замеряющим сигнал","metadata":{}},{"cell_type":"markdown","source":"### 2.3 Data Preparation\nУ нас очень много данных (600 млн. строк), что занимает слишком много памяти и при этом несет довольно мало информации. Обработаем данные так, чтобы уменьшить количество данных и при этом увеличить информацию в каждом признаке","metadata":{}},{"cell_type":"markdown","source":"Исходные данные нам больше не нужны, отчищаем память","metadata":{}},{"cell_type":"code","source":"del df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T06:16:56.914921Z","iopub.execute_input":"2024-11-30T06:16:56.915261Z","iopub.status.idle":"2024-11-30T06:16:56.920776Z","shell.execute_reply.started":"2024-11-30T06:16:56.915228Z","shell.execute_reply":"2024-11-30T06:16:56.919470Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Поэтому мы будем искать различные аггрегации по сегментам датасета","metadata":{}},{"cell_type":"code","source":"def extract_features_from_segment(seg_index: int, segment, df: pd.DataFrame):\n    \"\"\"\n    Эта функция различным образом аггрегирует исходный датасет и преобразует его в датасет\n    с новыми признаками\n    \"\"\"\n    acoustic_data = pd.Series(segment['acoustic_data'].values)\n\n    # Создаем признаки по следующим аггрегациям:\n    # - среднее арифметическое\n    df.loc[seg_index, 'mean'] = acoustic_data.mean()\n    # - стандартное отклонение\n    df.loc[seg_index, 'std'] = acoustic_data.std()\n    # - максимальное значение\n    df.loc[seg_index, 'max'] = acoustic_data.max()\n    # - минимальное значение\n    df.loc[seg_index, 'min'] = acoustic_data.min()\n    # - коэффициент эксцесса\n    df.loc[seg_index, 'kurt'] = acoustic_data.kurtosis()\n    # - коэффициент асимметрии\n    df.loc[seg_index, 'skew'] = acoustic_data.skew()\n    \n    #Создаем скользящее окно размера 100\n    # - окно для скользящего стандартного отклонения\n    roll_std = acoustic_data.rolling(window = 100).std().dropna().values\n    # - окно для скользящего среднего\n    roll_mean = acoustic_data.rolling(window= 100).mean().dropna().values\n    \n    # Считаем признаки по скользящему среднему\n    # - среднее от скользящего среднего\n    df.loc[seg_index, 'rolling_mean_mean'] =roll_mean.mean()\n    # - стандартное отклонение от скользящего среднего\n    df.loc[seg_index, 'rolling_mean_std'] =roll_mean.std()\n    \n    # Считаем признаки по скользящему стандартного отклонения\n    # - среднее от скользящего стандартного отклонения\n    df.loc[seg_index, 'rolling_std_mean'] = roll_std.mean()\n     # - стандартное отклонение от скользящего стандартного отклонения\n    df.loc[seg_index, 'rolling_std_std'] = roll_std.std()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T06:16:56.922298Z","iopub.execute_input":"2024-11-30T06:16:56.922733Z","iopub.status.idle":"2024-11-30T06:16:56.940189Z","shell.execute_reply.started":"2024-11-30T06:16:56.922692Z","shell.execute_reply":"2024-11-30T06:16:56.938642Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Также нам нужно преобразовать таргет (идея взята из [этого ноутбука](https://www.kaggle.com/code/dkaraflos/1-geomean-nn-and-6featlgbm-2-259-private-lb#Train-the-NN)) следующим образом:\n- оставляем изначальный таргет ttf - время до сейсмического толчка\n- создаем новый таргет tsf - время после сейсмического толчка (второстепенный таргет для предотвращения переобучения модели)\n- создаем новый таргет binary - флаг, меньше ли ttf, чем значение 0.5 (оценивает близость к толчку. Второстепенный таргет для предотвращения переобучения модели)","metadata":{}},{"cell_type":"code","source":"def generate_tsf(targets: pd.DataFrame):\n    # create time since failure target variable\n    # This will be used in the NN as an additional objective\n    targets['tsf']=targets['target']-targets['target'].shift(1).fillna(0)\n    targets['tsf']=np.where(targets['tsf']>1.5, targets['tsf'], 0)\n    targets['tsf'].iloc[0]=targets['target'].iloc[0]\n    \n    temp_max=0\n    for i in tqdm(range(targets.shape[0])):\n        if targets['tsf'].iloc[i]>0:\n            temp_max=targets['tsf'].iloc[i]\n        else:\n            targets['tsf'].iloc[i]=temp_max\n            \n    targets['tsf']=targets['tsf']-targets['target']\n\ndef generate_ttf_flag(targets: pd.DataFrame):\n    target=targets['target'].copy().values\n    target[target>=0.5] = 1\n    target[target<0.5] = 0\n    target = 1-target\n    targets['binary']=target\n    del target\n\ndef change_target(y: pd.DataFrame):\n    generate_tsf(y)\n    generate_ttf_flag(y)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T06:16:56.941827Z","iopub.execute_input":"2024-11-30T06:16:56.942324Z","iopub.status.idle":"2024-11-30T06:16:56.962955Z","shell.execute_reply.started":"2024-11-30T06:16:56.942284Z","shell.execute_reply":"2024-11-30T06:16:56.961559Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Создаем функцию, объединяющую выше приведенные для обработки датасета","metadata":{}},{"cell_type":"code","source":"def preprocess(data: pd.DataFrame, rows:int = 150_000):\n    segments_num = int(np.floor(data.shape[0] / rows))\n    result_X = pd.DataFrame(index=range(segments_num), dtype=np.float64)\n    result_y = pd.DataFrame(index=range(segments_num), dtype=np.float64, columns=['target'])\n    for i in tqdm(range(segments_num)):\n        segment = data_train.iloc[i * rows:i * rows + rows]\n        extract_features_from_segment(i, segment, result_X)\n        result_y.loc[i, 'target'] = segment['time_to_failure'].values[-1]\n    scaler = StandardScaler()\n    # print(result_X.head(10))\n    scaler.fit(result_X)\n    result_X = scaler.transform(result_X)\n    change_target(result_y)\n    return result_X, result_y","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T06:16:56.964757Z","iopub.execute_input":"2024-11-30T06:16:56.965200Z","iopub.status.idle":"2024-11-30T06:16:56.980854Z","shell.execute_reply.started":"2024-11-30T06:16:56.965160Z","shell.execute_reply":"2024-11-30T06:16:56.979574Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Обрабатываем тестовый и тренировочный датасет","metadata":{}},{"cell_type":"code","source":"X_test, y_test = preprocess(data_test)\nX_train, y_train = preprocess(data_train)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T06:16:56.982648Z","iopub.execute_input":"2024-11-30T06:16:56.983081Z","iopub.status.idle":"2024-11-30T06:18:14.490564Z","shell.execute_reply.started":"2024-11-30T06:16:56.983025Z","shell.execute_reply":"2024-11-30T06:18:14.489187Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_test.head(10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T09:38:20.664829Z","iopub.execute_input":"2024-11-30T09:38:20.669689Z","iopub.status.idle":"2024-11-30T09:38:20.712031Z","shell.execute_reply.started":"2024-11-30T09:38:20.669537Z","shell.execute_reply":"2024-11-30T09:38:20.710250Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Посмотрим на получившийся датафрейм","metadata":{}},{"cell_type":"code","source":"pd.DataFrame(\n    X_train,\n    columns=[\n        'mean', 'std', 'max',\n        'max', 'kurt', 'skew',\n        'rolling_mean_mean', 'rolling_mean_std',\n        'rolling_std_mean', 'rolling_std_std']\n).head(10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T06:18:14.492257Z","iopub.execute_input":"2024-11-30T06:18:14.492744Z","iopub.status.idle":"2024-11-30T06:18:14.521511Z","shell.execute_reply.started":"2024-11-30T06:18:14.492703Z","shell.execute_reply":"2024-11-30T06:18:14.520147Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Model and Training\n","metadata":{}},{"cell_type":"markdown","source":"Создаем модель","metadata":{}},{"cell_type":"code","source":"\ndef get_model(rnn: str, hidden_size: int, add_conv: bool, memory: int, order: int, theta: int):\n    # Входной слой\n    inp = Input(shape=(1,X_train.shape[1]))\n    # Метод, который позволяет повысить производительность и стабилизировать работу искусственных нейронных сетей.\n    # Суть данного метода заключается в том, что некоторым слоям нейронной сети на вход подаются данные,\n    # предварительно обработанные и имеющие нулевое математическое ожидание и единичную дисперсию.\n    x = BatchNormalization()(inp)\n\n    # Для сравнения попробуем помимо LMU обучить также и LSTM и GRU\n    if rnn == 'LSTM':\n        x = LSTM(hidden_size,return_sequences=True)(x)\n    elif rnn == 'GRU':\n        x = GRU(hidden_size,return_sequences=True)(x)\n    elif rnn == 'LMU': \n        x = LMU(\n            memory_d=memory,\n            order=order,\n            theta=theta,\n            hidden_cell=keras.layers.SimpleRNNCell(units=hidden_size),\n            return_sequences = True)(x)\n    else:\n        raise ValueError('Incorrect RNN name')\n\n    # Нашел фишку с добавлением сверточных слоев в этом ноутбуке:\n    # https://www.kaggle.com/code/dkaraflos/1-geomean-nn-and-6featlgbm-2-259-private-lb#Train-the-NN\n    if add_conv:\n        x = Convolution1D(128, (2),activation='relu', padding=\"same\")(x)\n        x = Convolution1D(84, (2),activation='relu', padding=\"same\")(x)\n        x = Convolution1D(64, (2),activation='relu', padding=\"same\")(x)\n\n    # Разворачиваем X в одноразмерный вектор\n    x = Flatten()(x)\n    \n    # Прогоняем через пару полносвязных слоев\n    x = Dense(64, activation=\"relu\")(x)\n    x = Dense(32, activation=\"relu\")(x)\n    \n    # Выходы нашей модели\n    ttf = Dense(1, activation='relu',name='regressor')(x) # Время до толчка\n    tsf = Dense(1)(x) # Время после толчка\n    classifier = Dense(1, activation='sigmoid')(x) # Флаг, что TTF < 0.5\n    \n    model = models.Model(inputs=inp, outputs=[ttf,tsf,classifier])    \n    opt = optimizers.Nadam(learning_rate=0.008)\n\n    # У нас три таргета: Время до толчка (TTF), Время после толчка(TSF), Флаг, что TTF < 0.5 (classifier)\n    # Мы делаем веса так, чтобы в основном оптимизировать модель по значению TTF\n    # Оптимизация для TSF и classifier помогает уменьшить переобучение и помогает в обобщении\n    model.compile(optimizer=opt, loss=['mae','mae','binary_crossentropy'],loss_weights=[8,1,1],metrics=['mae','mae','accuracy'])\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T06:18:14.523237Z","iopub.execute_input":"2024-11-30T06:18:14.523728Z","iopub.status.idle":"2024-11-30T06:18:14.537742Z","shell.execute_reply.started":"2024-11-30T06:18:14.523672Z","shell.execute_reply":"2024-11-30T06:18:14.536217Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Создаем набор аргументов для создания разных моделей","metadata":{}},{"cell_type":"code","source":"args = [\n    # ['LSTM', 128, False, 0, 0, 0],\n    # ['LSTM', 128, True, 0, 0, 0],\n    \n    # ['GRU', 128, False, 0, 0, 0],\n    # ['GRU', 128, True, 0, 0, 0],\n    # Значения order и theta взяты из примера:\n    # https://github.com/nengo/keras-lmu/blob/main/docs/basic-usage.rst\n    # ['LMU', 128, False, 1, 1, 300],\n    # ['LMU', 128, True, 1, 1, 300],\n    # ['LMU', 128, False, 1, 1, 784],\n    # ['LMU', 128, True, 1, 1, 784],\n\n    # ['LMU', 128, False, 5, 5, 300],\n    # ['LMU', 128, True, 5, 5, 300],\n    # ['LMU', 128, False, 5, 5, 784],\n    # ['LMU', 128, True, 5, 5, 784],\n\n    # ['LMU', 128, False, 10, 10, 300],\n    # ['LMU', 128, True, 10, 10, 300],\n    # ['LMU', 128, False, 10, 10, 784],\n    # ['LMU', 128, True, 10, 10, 784],\n\n    # ['LMU', 128, False, 15, 15, 300],\n    # ['LMU', 128, True, 15, 15, 300],\n    # ['LMU', 128, False, 15, 15, 784],\n    # ['LMU', 128, True, 15, 15, 784],\n\n    # ['LMU', 128, False, 20, 20, 300],\n    # ['LMU', 128, True, 20, 20, 300],\n    # ['LMU', 128, False, 20, 20, 784],\n    # ['LMU', 128, True, 20, 20, 784],\n\n    # ['LMU', 128, False, 30, 30, 300],\n    # ['LMU', 128, True, 30, 30, 300],\n\n    # ['LMU', 128, False, 50, 50, 300],\n    # ['LMU', 128, True, 50, 50, 300],\n    ['LMU', 128, False, 20, 20, 200],\n    ['LMU', 128, True, 20, 20, 200],\n\n    ['LMU', 128, False, 20, 20, 100],\n    ['LMU', 128, True, 20, 20, 100],\n\n    ['LMU', 128, False, 20, 20, 50],\n    ['LMU', 128, True, 20, 20, 50],\n    # С memory > 10 уже тренировка идет долго\n    \n]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T08:32:04.995286Z","iopub.execute_input":"2024-11-30T08:32:04.995829Z","iopub.status.idle":"2024-11-30T08:32:05.003970Z","shell.execute_reply.started":"2024-11-30T08:32:04.995785Z","shell.execute_reply":"2024-11-30T08:32:05.002736Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Функции для обучения и тестирования моделей","metadata":{}},{"cell_type":"code","source":"def train_model(model):\n    model.fit(\n        X_train.reshape(X_train.shape[0], 1, X_train.shape[1]),\n        [y_train['target'], y_train['tsf'], y_train['binary']],\n        epochs=1000,\n        batch_size=256,\n        verbose=0, # Убрать вывод в консоль\n    )\n\ndef test_model(model):\n    prediction = model.predict(X_test.reshape(X_test.shape[0], 1, X_test.shape[1]))[0].ravel()\n    mae_score = mean_absolute_error(y_test['target'], prediction)\n    return prediction, mae_score","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T08:32:05.006915Z","iopub.execute_input":"2024-11-30T08:32:05.007502Z","iopub.status.idle":"2024-11-30T08:32:05.026186Z","shell.execute_reply.started":"2024-11-30T08:32:05.007402Z","shell.execute_reply":"2024-11-30T08:32:05.024864Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Делаем функцию для запуска обучения и проверки всех моделей из args","metadata":{}},{"cell_type":"code","source":"def train_and_test_models():\n    predictions = []\n    scores = []\n    for i, arg in enumerate(args):\n        print(f\"Начато обучение модели {arg[0]}, с {arg[1]} скрытыми слоями\")\n        print(f\"Наличие сверточных слоев: {arg[2]}\")\n        if arg[3] != 0:\n            print(f\"Параметры LMU: memory = {arg[3]}, order={arg[4]}, theta={arg[5]}\")\n        print()\n        model = get_model(*arg)\n        print(\"Модель создана\")\n        start = time()\n        train_model(model)\n        end = time()\n        print(f\"Модель обучена за {humanfriendly.format_timespan(end - start)}\")\n        pred, score = test_model(model)\n        predictions.append(pred)\n        scores.append(score)\n        print(f'MAE = {score}')\n        print(f\"-----------------\")\n    return predictions, scores","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T08:32:05.028379Z","iopub.execute_input":"2024-11-30T08:32:05.028938Z","iopub.status.idle":"2024-11-30T08:32:05.047769Z","shell.execute_reply.started":"2024-11-30T08:32:05.028871Z","shell.execute_reply":"2024-11-30T08:32:05.046337Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Запускаем обучение и тестирование всех моделей","metadata":{}},{"cell_type":"code","source":"all_predictions, all_scores = train_and_test_models()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T08:32:05.049689Z","iopub.execute_input":"2024-11-30T08:32:05.050056Z","iopub.status.idle":"2024-11-30T08:52:22.265876Z","shell.execute_reply.started":"2024-11-30T08:32:05.050012Z","shell.execute_reply":"2024-11-30T08:52:22.264318Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Функция для визуализации регрессии","metadata":{}},{"cell_type":"code","source":"def plot_regression(predictions, scores):\n    figure, axes = plt.subplots(2, 2, figsize=(12, 12))\n    plt.subplots_adjust(hspace=0.5)\n    axes = axes.reshape(-1)\n    for i, ax in enumerate(axes):\n        ax.scatter(y_test['target'], y_test['target'], label='true', zorder=2)\n        ax.scatter(y_test['target'], predictions[i], label='test')\n        ax.set_xlabel('Actual values')\n        ax.set_ylabel('Predicted values')\n        ax.set_title(f'{args[i]},\\n MAE = {scores[i]}')\n        ax.legend()\n    plt.savefig('result.png')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T08:52:22.269166Z","iopub.execute_input":"2024-11-30T08:52:22.269746Z","iopub.status.idle":"2024-11-30T08:52:22.278167Z","shell.execute_reply.started":"2024-11-30T08:52:22.269689Z","shell.execute_reply":"2024-11-30T08:52:22.276732Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plot_regression(all_predictions, all_scores)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-30T08:52:22.279802Z","iopub.execute_input":"2024-11-30T08:52:22.280387Z","iopub.status.idle":"2024-11-30T08:52:24.206860Z","shell.execute_reply.started":"2024-11-30T08:52:22.280330Z","shell.execute_reply":"2024-11-30T08:52:24.205636Z"}},"outputs":[],"execution_count":null}]}