{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":10338,"databundleVersionId":862042,"sourceType":"competition"}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# =================================================================\n# ■■■ セル 1：セットアップ ■■■\n# =================================================================\n# まず、このセルだけを実行してください。\n# 実行が完了したら、次のセルを実行してください。\n# =================================================================\nprint(\"■ ステップ0：ライブラリの準備\")\n\n# TensorFlowと、それと互換性のあるNumpyのバージョンを正確に指定してインストール\n!pip install -q tensorflow==2.15.0 keras==2.15.0 numpy==1.26.4\n\n# DICOMファイルを扱うためのライブラリをインストール\n!pip install -q pydicom\n\nprint(\"\\n▶ ライブラリのインストールが完了しました。\")\nprint(\"▶▶▶ 次のセルに進んでください。◀◀◀\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-07-14T03:37:58.420499Z","iopub.execute_input":"2025-07-14T03:37:58.421162Z","iopub.status.idle":"2025-07-14T03:39:02.063085Z","shell.execute_reply.started":"2025-07-14T03:37:58.421136Z","shell.execute_reply":"2025-07-14T03:39:02.061641Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =================================================================\n# ■■■ セル 2：メインの実験コード ■■■\n# =================================================================\n# 上のセル1の実行が完了した後に、このセルを実行してください。\n# =================================================================\nprint(\"■ ステップ1：ライブラリのインポートとデータ準備\")\n\n# 必要なライブラリをインポート\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport os\nimport cv2\nimport pydicom\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.utils import class_weight\nfrom tqdm.notebook import tqdm\n\n# TensorFlowとKerasをインポート\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.applications import ResNet50\nfrom tensorflow.keras.layers import Dense, GlobalAveragePooling2D, Dropout\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.optimizers import Adam\n\n# グラフのスタイルを設定\nsns.set_style(\"whitegrid\")\n\n# Kaggle環境でのファイルパスを定義\nkaggle_input_path = '/kaggle/input/rsna-pneumonia-detection-challenge/'\nlabels_path = os.path.join(kaggle_input_path, 'stage_2_train_labels.csv')\ndetailed_info_path = os.path.join(kaggle_input_path, 'stage_2_detailed_class_info.csv')\ndicom_image_dir = os.path.join(kaggle_input_path, 'stage_2_train_images')\n\n# 作業用フォルダを作成\nworking_dir = '/kaggle/working/'\npng_image_dir = os.path.join(working_dir, 'train_images_png')\nos.makedirs(png_image_dir, exist_ok=True)\n\n# --- 1. メタデータの作成 ---\ndf_labels = pd.read_csv(labels_path)\ndf_detailed = pd.read_csv(detailed_info_path)\ndf_combined = pd.merge(df_labels, df_detailed, on='patientId')\n\nages = []\nsexes = []\nview_positions = []\n\nprint(\"DICOMファイルからメタ情報を抽出中...\")\nfor patient_id in tqdm(df_combined['patientId'].unique()):\n    dcm_path = os.path.join(dicom_image_dir, f\"{patient_id}.dcm\")\n    dcm_data = pydicom.dcmread(dcm_path, stop_before_pixels=True)\n    ages.append(dcm_data.PatientAge)\n    sexes.append(dcm_data.PatientSex)\n    view_positions.append(dcm_data.ViewPosition)\n\nmeta_dict = {\n    'patientId': df_combined['patientId'].unique(),\n    'Age': ages,\n    'Sex': sexes,\n    'ViewPosition': view_positions\n}\ndf_meta = pd.DataFrame(meta_dict)\n\ndf_full = pd.merge(df_combined, df_meta, on='patientId')\ndf_full['Age'] = df_full['Age'].astype(int)\n\n# --- 2. 画像分類タスク用にデータを整形 ---\ndf_class = df_full[['patientId', 'Target']].drop_duplicates().copy()\ndf_class['Target'] = df_class['Target'].astype(str)\ndf_class['filename'] = df_class['patientId'].apply(lambda x: f\"{x}.png\")\n\n# --- 3. PNGへの変換 ---\nprint(\"DICOMをPNGに変換中...\")\nfor patient_id in tqdm(df_class['patientId']):\n    dcm_path = os.path.join(dicom_image_dir, f\"{patient_id}.dcm\")\n    png_path = os.path.join(png_image_dir, f\"{patient_id}.png\")\n    \n    dcm_data = pydicom.dcmread(dcm_path)\n    image = dcm_data.pixel_array\n    cv2.imwrite(png_path, image)\n\n# --- 4. 最終的な学習・検証データの作成 ---\ndf_train, df_val = train_test_split(df_class, test_size=0.2, random_state=42, stratify=df_class['Target'])\n\nprint(f\"\\nデータ総数: {len(df_class)}件\")\nprint(f\"学習用データ数: {len(df_train)}件\")\nprint(f\"検証用データ数: {len(df_val)}件\")\nprint(\"▶ 全てのデータ準備完了\")\n\n\n# =================================================================\n# ■■■ ステップ2：関数定義 ■■■\n# =================================================================\nprint(\"\\n■ ステップ2：学習・評価用の関数を定義\")\n\ndef train_classification_model(train_df, val_df, epochs=5, image_size=224, batch_size=32):\n    # --- 1. ImageDataGeneratorの準備 ---\n    train_datagen = ImageDataGenerator(\n        rescale=1./255, rotation_range=15, width_shift_range=0.1,\n        height_shift_range=0.1, zoom_range=0.1, horizontal_flip=True,\n        brightness_range=[0.9, 1.1]\n    )\n    val_datagen = ImageDataGenerator(rescale=1./255)\n\n    train_generator = train_datagen.flow_from_dataframe(\n        dataframe=train_df, directory=png_image_dir, x_col='filename', y_col='Target',\n        target_size=(image_size, image_size), batch_size=batch_size, class_mode='binary'\n    )\n    validation_generator = val_datagen.flow_from_dataframe(\n        dataframe=val_df, directory=png_image_dir, x_col='filename', y_col='Target',\n        target_size=(image_size, image_size), batch_size=batch_size, class_mode='binary', shuffle=False\n    )\n    \n    # --- 2. クラスの重みを計算 ---\n    weights = class_weight.compute_class_weight(\n        'balanced', classes=np.unique(train_generator.classes), y=train_generator.classes\n    )\n    class_weights = {i : weights[i] for i in range(len(weights))}\n    \n    # --- 3. モデルの構築 ---\n    base_model = ResNet50(weights='imagenet', include_top=False, input_shape=(image_size, image_size, 3))\n    base_model.trainable = False\n    x = base_model.output\n    x = GlobalAveragePooling2D()(x)\n    x = Dropout(0.5)(x)\n    x = Dense(128, activation='relu')(x)\n    predictions = Dense(1, activation='sigmoid')(x)\n    model = Model(inputs=base_model.input, outputs=predictions)\n    \n    # --- 4. 最初の学習 ---\n    model.compile(optimizer=Adam(learning_rate=0.001), loss='binary_crossentropy', metrics=['accuracy'])\n    print(f\"--- {len(train_df)}件のデータで学習開始 (Phase 1) ---\")\n    model.fit(\n        train_generator, epochs=epochs, validation_data=validation_generator,\n        class_weight=class_weights, verbose=2\n    )\n    \n    # --- 5. ファインチューニング ---\n    base_model.trainable = True\n    for layer in base_model.layers[:100]:\n        layer.trainable = False\n    model.compile(optimizer=Adam(learning_rate=0.00001), loss='binary_crossentropy', metrics=['accuracy'])\n    print(f\"--- ファインチューニング開始 (Phase 2) ---\")\n    model.fit(\n        train_generator, epochs=epochs, validation_data=validation_generator,\n        class_weight=class_weights, verbose=2\n    )\n\n    # --- 6. 最終評価 ---\n    loss, accuracy = model.evaluate(validation_generator)\n    print(f\"--- 評価完了 ---\")\n    return accuracy\n\nprint(\"▶ 関数定義完了\")\n\n\n# =================================================================\n# ■■■ ステップ3：実験A 実行 ■■■\n# =================================================================\nprint(\"\\n■ ステップ3：実験Aの実行\")\n\n# 学習に使うデータの上限を設定\nMAX_TRAIN_SAMPLES = 6000\ndf_train_lean = df_train.sample(n=MAX_TRAIN_SAMPLES, random_state=42)\n\n# 検証するデータ量を定義\ndata_sizes = [500, 1000, 2000, 4000, 6000] \nresults_A = []\n\n# 各データサイズで学習と評価を繰り返す\nfor size in data_sizes:\n    df_train_subset = df_train_lean.sample(n=size, random_state=42)\n    print(f\"\\nデータ量 {size}件 での処理を開始...\")\n    accuracy = train_classification_model(df_train_subset, df_val, epochs=5, image_size=128, batch_size=32)\n    print(f\"★★ データ量 {size}件 の最終スコア(Accuracy): {accuracy:.4f} ★★\")\n    results_A.append({'size': size, 'accuracy': accuracy})\n\nprint(\"\\n▶ 全ての実験が完了しました。\")\n\n\n# =================================================================\n# ■■■ ステップ4：結果可視化 ■■■\n# =================================================================\nprint(\"\\n■ ステップ4：実験Aの結果を可視化\")\n\ndf_results_A = pd.DataFrame(results_A)\ndf_results_A.to_csv('/kaggle/working/experiment_A_results.csv', index=False) # 結果をCSVで保存\n\nplt.figure(figsize=(10, 6))\nsns.lineplot(x='size', y='accuracy', data=df_results_A, marker='o', color='royalblue')\nplt.title('Experiment A: Model Accuracy vs. Data Quantity', fontsize=16)\nplt.xlabel('Number of Training Samples Used', fontsize=12)\nplt.ylabel('Accuracy (Performance)', fontsize=12)\nplt.xticks(data_sizes)\nplt.ylim(0.6, 1.0) # 精度に合わせてY軸を調整\nplt.grid(True, which='both', linestyle='--', linewidth=0.5)\nplt.savefig('/kaggle/working/experiment_A_chart.png') # グラフを画像で保存\nplt.show()\n\nprint(\"\\n実験結果のCSVとグラフが /kaggle/working/ に保存されました。\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-14T03:39:02.065130Z","iopub.execute_input":"2025-07-14T03:39:02.065402Z"}},"outputs":[],"execution_count":null}]}