{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":56537,"databundleVersionId":8015876,"sourceType":"competition"},{"sourceId":8409068,"sourceType":"datasetVersion","datasetId":5004471}],"dockerImageVersionId":30732,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nos.environ[\"KERAS_BACKEND\"] = \"jax\"\n\nimport gc\nimport numpy as np\nimport pandas as pd\nimport polars as pl\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nimport tensorflow as tf\nimport jax\nimport jax.numpy as jnp\n\nimport keras\n\nfrom sklearn import metrics\n\nfrom tqdm.notebook import tqdm\n\n# 버전 확인\nprint(tf.__version__)\nprint(jax.__version__)\n\n# 인터랙티브 모드 확인\ndef is_interactive():\n    return 'runtime' in get_ipython().config.IPKernelApp.connection_file\n\nprint('Interactive?', is_interactive())","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-06-13T02:18:56.350856Z","iopub.execute_input":"2024-06-13T02:18:56.35126Z","iopub.status.idle":"2024-06-13T02:19:10.864133Z","shell.execute_reply.started":"2024-06-13T02:18:56.351232Z","shell.execute_reply":"2024-06-13T02:19:10.863148Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"SEED = 42\nkeras.utils.set_random_seed(SEED)\ntf.random.set_seed(SEED)\ntf.config.experimental.enable_op_determinism()\n\nDATA = \"/kaggle/input/leap-atmospheric-physics-ai-climsim\"\nDATA_TFREC = \"/kaggle/input/leap-train-tfrecords\"\n\n# 샘플 데이터를 불러와 타겟 컬럼을 확인\nsample = pl.read_csv(os.path.join(DATA, \"sample_submission.csv\"), n_rows=1)\nTARGETS = sample.select(pl.exclude('sample_id')).columns\nprint(len(TARGETS))","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:19:10.866071Z","iopub.execute_input":"2024-06-13T02:19:10.866952Z","iopub.status.idle":"2024-06-13T02:19:11.047321Z","shell.execute_reply.started":"2024-06-13T02:19:10.866916Z","shell.execute_reply":"2024-06-13T02:19:11.046383Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def _parse_function(example_proto):\n    # TFRecord 파일에서 데이터를 파싱하는 함수\n    feature_description = {\n        'x': tf.io.FixedLenFeature([556], tf.float32),\n        'targets': tf.io.FixedLenFeature([368], tf.float32)\n    }\n    e = tf.io.parse_single_example(example_proto, feature_description)\n    return e['x'], e['targets']\n\ntrain_files = [os.path.join(DATA_TFREC, \"train_%.3d.tfrec\" % i) for i in range(100)]\nvalid_files = [os.path.join(DATA_TFREC, \"train_%.3d.tfrec\" % i) for i in range(100, 101)]\n\nBATCH_SIZE = 3072\n\ntrain_options = tf.data.Options()\ntrain_options.deterministic = True\n\n# 훈련 데이터셋 구성\nds_train = (\n    tf.data.Dataset.from_tensor_slices(train_files)\n    .with_options(train_options)\n    .shuffle(100)\n    .interleave(\n        lambda file: tf.data.TFRecordDataset(file).map(_parse_function, num_parallel_calls=tf.data.AUTOTUNE),\n        num_parallel_calls=tf.data.AUTOTUNE,\n        cycle_length=10,\n        block_length=1000,\n        deterministic=True\n    )\n    .shuffle(4 * BATCH_SIZE)\n    .batch(BATCH_SIZE)\n    .prefetch(tf.data.AUTOTUNE)\n)\n\n# 검증 데이터셋 구성\nds_valid = (\n    tf.data.TFRecordDataset(valid_files)\n    .map(_parse_function)\n    .batch(BATCH_SIZE)\n    .prefetch(tf.data.AUTOTUNE)\n)","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:19:11.048543Z","iopub.execute_input":"2024-06-13T02:19:11.048834Z","iopub.status.idle":"2024-06-13T02:19:13.306957Z","shell.execute_reply.started":"2024-06-13T02:19:11.04881Z","shell.execute_reply":"2024-06-13T02:19:13.306063Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 피처별 인덱스 정의\ninput_feature_indices = {\n    'state_t': range(0, 60),\n    'state_q0001': range(60, 120),\n    'state_q0002': range(120, 180),\n    'state_q0003': range(180, 240),\n    'state_u': range(240, 300),\n    'state_v': range(300, 360),\n    'state_ps': [360],\n    'pbuf_SOLIN': [361],\n    'pbuf_LHFLX': [362],\n    'pbuf_SHFLX': [363],\n    'pbuf_TAUX': [364],\n    'pbuf_TAUY': [365],\n    'pbuf_COSZRS': [366],\n    'cam_in_ALDIF': [367],\n    'cam_in_ALDIR': [368],\n    'cam_in_ASDIF': [369],\n    'cam_in_ASDIR': [370],\n    'cam_in_LWUP': [371],\n    'cam_in_ICEFRAC': [372],\n    'cam_in_LANDFRAC': [373],\n    'cam_in_OCNFRAC': [374],\n    'cam_in_SNOWHLAND': [375],\n    'pbuf_ozone': range(376, 436),\n    'pbuf_CH4': range(436, 496),\n    'pbuf_N2O': range(496, 556)\n}\n\n# 피처 설명 매핑\nfeature_descriptions = {\n    'state_t': 'Air Temperature',\n    'state_q0001': 'Specific Humidity',\n    'state_q0002': 'Cloud Liquid Mixing Ratio',\n    'state_q0003': 'Cloud Ice Mixing Ratio',\n    'state_u': 'Zonal Wind Speed',\n    'state_v': 'Meridional Wind Speed',\n    'state_ps': 'Surface Pressure',\n    'pbuf_SOLIN': 'Solar Insolation',\n    'pbuf_LHFLX': 'Surface Latent Heat Flux',\n    'pbuf_SHFLX': 'Surface Sensible Heat Flux',\n    'pbuf_TAUX': 'Zonal Surface Stress',\n    'pbuf_TAUY': 'Meridional Surface Stress',\n    'pbuf_COSZRS': 'Cosine of Solar Zenith Angle',\n    'cam_in_ALDIF': 'Albedo for Diffuse Longwave Radiation',\n    'cam_in_ALDIR': 'Albedo for Direct Longwave Radiation',\n    'cam_in_ASDIF': 'Albedo for Diffuse Shortwave Radiation',\n    'cam_in_ASDIR': 'Albedo for Direct Shortwave Radiation',\n    'cam_in_LWUP': 'Upward Longwave Flux',\n    'cam_in_ICEFRAC': 'Sea-Ice Areal Fraction',\n    'cam_in_LANDFRAC': 'Land Areal Fraction',\n    'cam_in_OCNFRAC': 'Ocean Areal Fraction',\n    'cam_in_SNOWHLAND': 'Snow Depth over Land',\n    'pbuf_ozone': 'Ozone Volume Mixing Ratio',\n    'pbuf_CH4': 'Methane Volume Mixing Ratio',\n    'pbuf_N2O': 'Nitrous Oxide Volume Mixing Ratio'\n}\n\n# 타겟별 인덱스 정의\ntarget_feature_indices = {\n    'ptend_t': range(0, 60),\n    'ptend_q0001': range(60, 120),\n    'ptend_q0002': range(120, 180),\n    'ptend_q0003': range(180, 240),\n    'ptend_u': range(240, 300),\n    'ptend_v': range(300, 360),\n    'cam_out_NETSW': [360],\n    'cam_out_FLWDS': [361],\n    'cam_out_PRECSC': [362],\n    'cam_out_PRECC': [363],\n    'cam_out_SOLS': [364],\n    'cam_out_SOLL': [365],\n    'cam_out_SOLSD': [366],\n    'cam_out_SOLLD': [367]\n}\n\n# 타겟 설명 매핑\ntarget_descriptions = {\n    'ptend_t': 'Heating Tendency',\n    'ptend_q0001': 'Moistening Tendency',\n    'ptend_q0002': 'Cloud Liquid Mixing Ratio Change',\n    'ptend_q0003': 'Cloud Ice Mixing Ratio Change',\n    'ptend_u': 'Zonal Wind Acceleration',\n    'ptend_v': 'Meridional Wind Acceleration',\n    'cam_out_NETSW': 'Net Shortwave Flux',\n    'cam_out_FLWDS': 'Downward Longwave Flux',\n    'cam_out_PRECSC': 'Snow Rate',\n    'cam_out_PRECC': 'Rain Rate',\n    'cam_out_SOLS': 'Downward Visible Direct Solar Flux',\n    'cam_out_SOLL': 'Downward Near-Infrared Direct Solar Flux',\n    'cam_out_SOLSD': 'Downward Diffuse Solar Flux',\n    'cam_out_SOLLD': 'Downward Diffuse Near-Infrared Solar Flux'\n}\n","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:19:13.309958Z","iopub.execute_input":"2024-06-13T02:19:13.310334Z","iopub.status.idle":"2024-06-13T02:19:13.325967Z","shell.execute_reply.started":"2024-06-13T02:19:13.310294Z","shell.execute_reply":"2024-06-13T02:19:13.325098Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 데이터 추출을 위해 샘플 데이터셋에서 일부 샘플을 가져옴\nfeatures_train, targets_train = [], []\nfor x, y in ds_train.take(1):\n    features_train = x.numpy()\n    targets_train = y.numpy()\n\nfeatures_train_df = pd.DataFrame(features_train)\n\nplt.figure(figsize=(20, 15))\nfor i, (feature, name) in enumerate(feature_descriptions.items()):\n    plt.subplot(5,5, i + 1)\n    feature_idx = input_feature_indices[feature]\n    feature_data = features_train_df.iloc[:, feature_idx].values.flatten()\n    sns.histplot(feature_data, kde=True)\n    plt.title(f'{name} Distribution')\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:19:13.327469Z","iopub.execute_input":"2024-06-13T02:19:13.327857Z","iopub.status.idle":"2024-06-13T02:20:16.366277Z","shell.execute_reply.started":"2024-06-13T02:19:13.3278Z","shell.execute_reply":"2024-06-13T02:20:16.36537Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 기본 통계량 계산\nstats = {}\nfor feature, indices in input_feature_indices.items():\n    stats[feature] = features_train_df.iloc[:, indices].describe()\n\nfor feature, description in stats.items():\n    print(f\"{feature_descriptions[feature]} (feature: {feature}) Statistics:\\n\", description)","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:20:16.367435Z","iopub.execute_input":"2024-06-13T02:20:16.367716Z","iopub.status.idle":"2024-06-13T02:20:17.430547Z","shell.execute_reply.started":"2024-06-13T02:20:16.367692Z","shell.execute_reply":"2024-06-13T02:20:17.429569Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 누락된 데이터 확인\nmissing_data_summary = features_train_df.isnull().sum()\nprint(\"Missing data summary:\")\nprint(missing_data_summary[missing_data_summary > 0])","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:20:17.431648Z","iopub.execute_input":"2024-06-13T02:20:17.431927Z","iopub.status.idle":"2024-06-13T02:20:17.444175Z","shell.execute_reply.started":"2024-06-13T02:20:17.431903Z","shell.execute_reply":"2024-06-13T02:20:17.439121Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 입력 데이터를 정규화하는 레이어\nnorm_x = keras.layers.Normalization()\nnorm_x.adapt(ds_train.map(lambda x, y: x).take(20 if is_interactive() else 1000))\n\n# 정규화된 데이터 시각화\nplt.scatter(\n    norm_x.mean.squeeze(),\n    norm_x.variance.squeeze() ** 0.5,\n    marker=\".\",\n    alpha=0.5\n)\nplt.xscale('log')\nplt.yscale('log')\n","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:20:17.447748Z","iopub.execute_input":"2024-06-13T02:20:17.448437Z","iopub.status.idle":"2024-06-13T02:20:22.367708Z","shell.execute_reply.started":"2024-06-13T02:20:17.448412Z","shell.execute_reply":"2024-06-13T02:20:22.366759Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 타겟 데이터를 정규화하는 레이어\nnorm_y = keras.layers.Normalization()\nnorm_y.adapt(ds_train.map(lambda x, y: y).take(20 if is_interactive() else 1000))\n\n# 타겟 데이터의 평균과 표준편차를 계산\nmean_y = norm_y.mean\nstdd_y = keras.ops.maximum(1e-10, norm_y.variance ** 0.5)\n\n# 타겟 데이터 시각화\nplt.scatter(\n    mean_y.squeeze(),\n    stdd_y.squeeze(),\n    marker=\".\",\n    alpha=0.5\n)\nplt.xscale('log')\nplt.yscale('log')","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:20:22.368768Z","iopub.execute_input":"2024-06-13T02:20:22.369013Z","iopub.status.idle":"2024-06-13T02:20:25.715054Z","shell.execute_reply.started":"2024-06-13T02:20:22.368992Z","shell.execute_reply":"2024-06-13T02:20:25.714071Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 타겟 데이터의 최소값과 최대값을 계산\nmin_y = np.min(np.stack([np.min(yb, 0) for _, yb in ds_train.take(20 if is_interactive() else 1000)], 0), 0, keepdims=True)\nmax_y = np.max(np.stack([np.max(yb, 0) for _, yb in ds_train.take(20 if is_interactive() else 1000)], 0), 0, keepdims=True)\n","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:24:57.965097Z","iopub.execute_input":"2024-06-13T02:24:57.965816Z","iopub.status.idle":"2024-06-13T02:25:02.882246Z","shell.execute_reply.started":"2024-06-13T02:24:57.965786Z","shell.execute_reply":"2024-06-13T02:25:02.881114Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 사용자 정의 메트릭 클래스\n@keras.saving.register_keras_serializable(package=\"MyMetrics\", name=\"ClippedR2Score\")\nclass ClippedR2Score(keras.metrics.Metric):\n    def __init__(self, name='r2_score', **kwargs):\n        super().__init__(name=name, **kwargs)\n        self.base_metric = keras.metrics.R2Score(class_aggregation=None)\n        \n    def update_state(self, y_true, y_pred, sample_weight=None):\n        self.base_metric.update_state(y_true, y_pred, sample_weight=None)\n        \n    def result(self):\n        return keras.ops.mean(keras.ops.clip(self.base_metric.result(), 0.0, 1.0))\n        \n    def reset_states(self):\n        self.base_metric.reset_states()","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:25:15.440428Z","iopub.execute_input":"2024-06-13T02:25:15.441132Z","iopub.status.idle":"2024-06-13T02:25:15.448462Z","shell.execute_reply.started":"2024-06-13T02:25:15.441104Z","shell.execute_reply":"2024-06-13T02:25:15.447331Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 학습 설정\nepochs = 20\nlearning_rate = 1e-3\n\nepochs_warmup = 1\nepochs_ending = 2\nsteps_per_epoch = int(np.ceil(len(train_files) * 100_000 / BATCH_SIZE))\n\n# 학습률 스케줄러\nlr_scheduler = keras.optimizers.schedules.CosineDecay(\n    1e-4, \n    (epochs - epochs_warmup - epochs_ending) * steps_per_epoch, \n    warmup_target=learning_rate,\n    warmup_steps=steps_per_epoch * epochs_warmup,\n    alpha=0.1\n)\n\n# 학습률 스케줄러 시각화\nplt.plot([lr_scheduler(it) for it in range(0, epochs * steps_per_epoch, steps_per_epoch)]);\n","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:25:17.746326Z","iopub.execute_input":"2024-06-13T02:25:17.7472Z","iopub.status.idle":"2024-06-13T02:25:18.763941Z","shell.execute_reply.started":"2024-06-13T02:25:17.747146Z","shell.execute_reply":"2024-06-13T02:25:18.763015Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from keras import layers\n\nkeras.utils.clear_session()\n\n# 입력 데이터를 시퀀스 형태로 변환하는 함수\ndef x_to_seq(x):\n    x_seq0 = keras.ops.transpose(keras.ops.reshape(x[:, 0:60 * 6], (-1, 6, 60)), (0, 2, 1))\n    x_seq1 = keras.ops.transpose(keras.ops.reshape(x[:, 60 * 6 + 16:60 * 9 + 16], (-1, 3, 60)), (0, 2, 1))\n    x_flat = keras.ops.reshape(x[:, 60 * 6:60 * 6 + 16], (-1, 1, 16))\n    x_flat = keras.ops.repeat(x_flat, 60, axis=1)\n    return keras.ops.concatenate([x_seq0, x_seq1, x_flat], axis=-1)\n\n\n# 모델을 구성하는 함수\ndef build_lstmConv(activation='relu', dropout_rate=0.1, hidden_units=64):  # 드롭아웃 추가\n    return keras.Sequential([\n        layers.Conv1D(256, 3, padding='same', activation=activation),\n        layers.BatchNormalization(),\n        layers.Dropout(dropout_rate),  # 드롭아웃 레이어\n        layers.Conv1D(128, 3, padding='same', activation=activation),\n        layers.BatchNormalization(),\n        layers.Dropout(dropout_rate),  # 드롭아웃 레이어\n        layers.Conv1D(64, 3, padding='same', activation=activation),\n        layers.BatchNormalization(),\n        \n        layers.LSTM(hidden_units, return_sequences=True),\n        layers.Dropout(dropout_rate),\n        layers.LSTM(hidden_units, return_sequences=True),\n        layers.Dropout(dropout_rate),\n        layers.LSTM(hidden_units, return_sequences=True),\n    ])\n","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:25:20.848281Z","iopub.execute_input":"2024-06-13T02:25:20.849073Z","iopub.status.idle":"2024-06-13T02:25:21.120173Z","shell.execute_reply.started":"2024-06-13T02:25:20.849042Z","shell.execute_reply":"2024-06-13T02:25:21.119171Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 모델 정의\nprint(ds_train.element_spec[0].shape[1:])\nX_input = x = keras.layers.Input(ds_train.element_spec[0].shape[1:])\nx = keras.layers.Normalization(mean=norm_x.mean, variance=norm_x.variance)(x)\nprint(\"Shape before transformation:\", x.shape) \nx = x_to_seq(x)\nprint(\"Shape after transformation:\", x.shape) \n\n","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:26:34.720262Z","iopub.execute_input":"2024-06-13T02:26:34.720651Z","iopub.status.idle":"2024-06-13T02:26:34.730847Z","shell.execute_reply.started":"2024-06-13T02:26:34.720622Z","shell.execute_reply":"2024-06-13T02:26:34.729897Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from keras import layers, models\nfrom keras.layers import Bidirectional, Attention\n\ndef build_improved_model(activation='relu', dropout_rate=0.1):\n    X_input = x = keras.layers.Input(ds_train.element_spec[0].shape[1:])\n    x = keras.layers.Normalization(mean=norm_x.mean, variance=norm_x.variance)(x)\n    x = x_to_seq(x)\n    \n    # Initial Conv1D layer\n    x = layers.Conv1D(128, 3, padding='same', activation=activation)(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Dropout(dropout_rate)(x)\n    \n    # More Conv1D layers with residual connections\n    residual = x\n    x = layers.Conv1D(64, 5, padding='same', activation=activation)(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Conv1D(32, 5, padding='same', activation=activation)(x)\n    x = layers.BatchNormalization()(x)\n    \n    residual = layers.Conv1D(32, 1, padding='same')(residual)\n    x = layers.Add()([x, residual])\n    \n    # Bidirectional LSTM with attention\n    x = Bidirectional(layers.LSTM(64, return_sequences=True))(x)\n    x = Bidirectional(layers.LSTM(64, return_sequences=True))(x)\n    attention = Attention()([x, x])\n    \n    # Global pooling and final dense layer\n    x = layers.GlobalAveragePooling1D()(attention)\n    x = layers.Dense(128, activation=activation)(x)\n    x = layers.Dropout(dropout_rate)(x)\n    outputs = layers.Dense(368)(x) \n\n    model = models.Model(X_input, outputs)\n    return model\n\n# 모델 빌드 및 컴파일\nmodel = build_improved_model()\n\nmodel.compile(\n    loss='mse', \n    optimizer=keras.optimizers.Adam(lr_scheduler),\n    metrics=[ClippedR2Score()]\n)\n\nmodel.build(tuple(ds_train.element_spec[0].shape))\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:51:15.577772Z","iopub.execute_input":"2024-06-13T02:51:15.578634Z","iopub.status.idle":"2024-06-13T02:51:15.800598Z","shell.execute_reply.started":"2024-06-13T02:51:15.578602Z","shell.execute_reply":"2024-06-13T02:51:15.799711Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 타겟 데이터를 정규화하여 훈련 데이터셋 구성\nds_train_target_normalized = ds_train.map(lambda x, y: (x, (y - mean_y) / stdd_y))\nds_valid_target_normalized = ds_valid.map(lambda x, y: (x, (y - mean_y) / stdd_y))\n\nprint(ds_train_target_normalized)\nprint(ds_train_target_normalized.element_spec[0].shape)\n","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:51:19.836457Z","iopub.execute_input":"2024-06-13T02:51:19.836932Z","iopub.status.idle":"2024-06-13T02:51:19.864039Z","shell.execute_reply.started":"2024-06-13T02:51:19.8369Z","shell.execute_reply":"2024-06-13T02:51:19.863184Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 모델 학습\nearly_stopping = keras.callbacks.EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)\n\nhistory = model.fit(\n    ds_train_target_normalized,\n    validation_data=ds_valid_target_normalized,\n    epochs=20,\n    verbose=1 if is_interactive() else 2,\n    callbacks=[\n        keras.callbacks.ModelCheckpoint(filepath='model.keras'),\n        early_stopping\n    ]\n)","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:51:21.448283Z","iopub.execute_input":"2024-06-13T02:51:21.44905Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 학습 및 검증 손실 시각화\nplt.plot(history.history['loss'], color='tab:blue')\nplt.plot(history.history['val_loss'], color='tab:red')\nplt.yscale('log')","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:20:31.732221Z","iopub.status.idle":"2024-06-13T02:20:31.732541Z","shell.execute_reply.started":"2024-06-13T02:20:31.732387Z","shell.execute_reply":"2024-06-13T02:20:31.7324Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 검증 데이터에 대한 R2 점수 계산\nscores_valid = np.array([metrics.r2_score(y_valid[:, i], p_valid[:, i]) for i in range(len(TARGETS))])\nplt.plot(scores_valid.clip(-1, 1))","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:20:31.734867Z","iopub.status.idle":"2024-06-13T02:20:31.735807Z","shell.execute_reply.started":"2024-06-13T02:20:31.735544Z","shell.execute_reply":"2024-06-13T02:20:31.735566Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 성능이 낮은 타겟의 개수와 클리핑된 점수 평균 계산\nmask = scores_valid <= 1e-3\nf\"Number of under-performing targets: {sum(mask)}\"\n# 출력: 'Number of under-performing targets: 85'\n\nf\"Clipped score: {scores_valid.clip(0, 1).mean()}\"\n# 출력: 'Clipped score: 0.4763027189716805'\n\ndel y_valid, p_valid\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:20:31.736941Z","iopub.status.idle":"2024-06-13T02:20:31.737411Z","shell.execute_reply.started":"2024-06-13T02:20:31.73715Z","shell.execute_reply":"2024-06-13T02:20:31.737188Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 제출 준비\nsample = pl.read_csv(\"/kaggle/input/leap-atmospheric-physics-ai-climsim/sample_submission.csv\")\ndf_test = (\n    pl.scan_csv(\"/kaggle/input/leap-atmospheric-physics-ai-climsim/test.csv\")\n    .select(pl.exclude(\"sample_id\"))\n    .cast(pl.Float32)\n    .collect()\n)\n\n# 테스트 데이터를 모델에 입력하여 예측\np_test = model.predict(df_test.to_numpy(), batch_size=4 * BATCH_SIZE) * stdd_y + mean_y\np_test = np.array(p_test)\np_test[:, mask] = mean_y[:, mask]  # 성능이 낮은 타겟의 값을 평균으로 대체\n\n# 특정 타겟의 값을 조정\ndf_p_test = pd.DataFrame(p_test, columns=TARGETS)\nfor idx in range(12, 30):\n    df_p_test[f\"ptend_q0002_{idx}\"] = -df_test[f\"state_q0002_{idx}\"].to_numpy() / 1200\n\np_test = df_p_test.values\n\n# 제출 파일 생성\nsubmission = sample.to_pandas()\nsubmission[TARGETS] = p_test\nsubmission_path = \"submission.csv\"\npl.from_pandas(submission[[\"sample_id\"] + TARGETS]).write_csv(submission_path)\n\nprint(f\"Submission file saved to {submission_path}\")","metadata":{"execution":{"iopub.status.busy":"2024-06-13T02:20:31.739058Z","iopub.status.idle":"2024-06-13T02:20:31.739483Z","shell.execute_reply.started":"2024-06-13T02:20:31.739311Z","shell.execute_reply":"2024-06-13T02:20:31.739325Z"},"trusted":true},"execution_count":null,"outputs":[]}]}