{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":10338,"databundleVersionId":862042,"sourceType":"competition"}],"dockerImageVersionId":31041,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"\n# ========== 环境设置 ==========\n!pip install pytorch-fid scikit-learn torchmetrics --quiet\n\nimport os\nimport numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader, Dataset\nfrom torchvision import transforms, models\nfrom torchmetrics import AUROC\nfrom sklearn.metrics import classification_report, confusion_matrix\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom PIL import Image\nfrom pytorch_fid import fid_score\nimport gc\n\n# 设置随机种子确保可复现性\ntorch.manual_seed(42)\nnp.random.seed(42)\n\n# 设备配置\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"使用设备: {DEVICE}\")\n\n# ========== 路径设置 ==========\nDATA_DIR = \"/kaggle/input/rsna-pneumonia-detection-challenge\"\nPROCESSED_DIR = \"/kaggle/working/processed_images\"\nGAN_CHECKPOINT = \"/kaggle/working/checkpoints/epoch_48.pth\"  # 使用训练好的生成器\nOUTPUT_DIR = \"/kaggle/working/results\"\nos.makedirs(OUTPUT_DIR, exist_ok=True)\nos.makedirs(\"/kaggle/working/generated_images\", exist_ok=True)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-06-30T05:49:35.784077Z","iopub.execute_input":"2025-06-30T05:49:35.784667Z","iopub.status.idle":"2025-06-30T05:51:00.990664Z","shell.execute_reply.started":"2025-06-30T05:49:35.784643Z","shell.execute_reply":"2025-06-30T05:51:00.989962Z"}},"outputs":[{"name":"stdout","text":"\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m363.4/363.4 MB\u001b[0m \u001b[31m4.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m0:00:01\u001b[0m00:01\u001b[0m\n\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m664.8/664.8 MB\u001b[0m \u001b[31m2.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m0:00:01\u001b[0m00:01\u001b[0m\n\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m211.5/211.5 MB\u001b[0m \u001b[31m8.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m0:00:01\u001b[0m00:01\u001b[0m\n\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m56.3/56.3 MB\u001b[0m \u001b[31m30.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m00:01\u001b[0m\n\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m127.9/127.9 MB\u001b[0m \u001b[31m13.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m00:01\u001b[0m\n\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m207.5/207.5 MB\u001b[0m \u001b[31m8.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m0:00:01\u001b[0m00:01\u001b[0m\n\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m21.1/21.1 MB\u001b[0m \u001b[31m54.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m00:01\u001b[0m\n\u001b[?25h使用设备: cuda\n","output_type":"stream"}],"execution_count":4},{"cell_type":"code","source":"# ============================================================\n# 阶段1: 加载预训练生成器\n# ============================================================\ndef stage_1_load_generator():\n    print(\"\\n\" + \"=\"*50)\n    print(\"阶段1: 加载预训练生成器\")\n    print(\"=\"*50)\n    \n    class Generator(nn.Module):\n        \"\"\"与训练时相同的生成器架构\"\"\"\n        def __init__(self, latent_dim=128, img_size=128, channels=1):\n            super().__init__()\n            self.init_size = img_size // 4\n            self.l1 = nn.Sequential(\n                nn.utils.spectral_norm(nn.Linear(latent_dim, 128 * self.init_size ** 2))\n            )\n            self.conv_blocks = nn.Sequential(\n                nn.BatchNorm2d(128),\n                nn.Upsample(scale_factor=2),\n                nn.utils.spectral_norm(nn.Conv2d(128, 128, 3, stride=1, padding=1)),\n                nn.BatchNorm2d(128, 0.8),\n                nn.LeakyReLU(0.2, inplace=True),\n                self._residual_block(128),\n                nn.Upsample(scale_factor=2),\n                nn.utils.spectral_norm(nn.Conv2d(128, 64, 3, stride=1, padding=1)),\n                nn.BatchNorm2d(64, 0.8),\n                nn.LeakyReLU(0.2, inplace=True),\n                nn.utils.spectral_norm(nn.Conv2d(64, channels, 3, stride=1, padding=1)),\n                nn.Tanh()\n            )\n        \n        def _residual_block(self, channels):\n            return nn.Sequential(\n                nn.utils.spectral_norm(nn.Conv2d(channels, channels, 3, padding=1)),\n                nn.InstanceNorm2d(channels),\n                nn.LeakyReLU(0.2),\n                nn.utils.spectral_norm(nn.Conv2d(channels, channels, 3, padding=1)),\n                nn.InstanceNorm2d(channels)\n            )\n\n        def forward(self, z):\n            out = self.l1(z)\n            out = out.view(out.shape[0], 128, self.init_size, self.init_size)\n            img = self.conv_blocks(out)\n            return img\n\n    # 加载预训练生成器\n    generator = Generator().to(DEVICE)\n    checkpoint = torch.load(GAN_CHECKPOINT)\n    generator.load_state_dict(checkpoint['generator'])\n    generator.eval()\n    print(f\"已加载生成器检查点: {GAN_CHECKPOINT}\")\n    \n    return generator","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T06:08:18.917809Z","iopub.execute_input":"2025-06-30T06:08:18.918089Z","iopub.status.idle":"2025-06-30T06:08:18.926211Z","shell.execute_reply.started":"2025-06-30T06:08:18.918068Z","shell.execute_reply":"2025-06-30T06:08:18.925596Z"}},"outputs":[],"execution_count":10},{"cell_type":"code","source":"# ============================================================\n# 阶段2: 生成图像和FID评估\n# ============================================================\ndef stage_2_generate_and_evaluate(generator):\n    print(\"\\n\" + \"=\"*50)\n    print(\"阶段2: 生成图像和FID评估\")\n    print(\"=\"*50)\n    \n    def generate_and_save_images(num_images=500, class_label=1):\n        \"\"\"生成并保存图像用于FID评估\"\"\"\n        print(f\"生成 {num_images} 张{'肺炎' if class_label == 1 else '正常'}图像...\")\n        save_dir = f\"/kaggle/working/generated_images/class_{class_label}\"\n        os.makedirs(save_dir, exist_ok=True)\n        \n        # 检查是否已生成足够图像\n        existing_images = len([f for f in os.listdir(save_dir) if f.endswith('.png')])\n        if existing_images >= num_images:\n            print(f\"已存在 {existing_images} 张图像，跳过生成\")\n            return\n        \n        # 批量生成\n        for i in range(0, num_images, 100):\n            batch_size = min(100, num_images - i)\n            z = torch.randn(batch_size, 128).to(DEVICE)\n            with torch.no_grad():\n                generated = generator(z)\n            \n            # 保存生成图像\n            for j in range(batch_size):\n                img = generated[j].cpu().permute(1, 2, 0).numpy()\n                img = (img * 127.5 + 127.5).astype(np.uint8)\n                img = Image.fromarray(img.squeeze(), 'L')\n                img.save(os.path.join(save_dir, f\"gen_{i+j}.png\"))\n        \n        print(f\"生成完成! 图像保存至: {save_dir}\")\n\n    # 生成500张肺炎图像用于评估\n    generate_and_save_images(num_images=500, class_label=1)\n\n    # 准备真实肺炎图像路径\n    real_pneumonia_dir = os.path.join(PROCESSED_DIR, \"pneumonia\")\n    if not os.path.exists(real_pneumonia_dir):\n        print(\"准备真实图像数据集...\")\n        # 创建按类别分组的真实图像目录\n        os.makedirs(os.path.join(PROCESSED_DIR, \"normal\"), exist_ok=True)\n        os.makedirs(os.path.join(PROCESSED_DIR, \"pneumonia\"), exist_ok=True)\n        \n        # 加载标签信息\n        labels_df = pd.read_csv(\"/kaggle/working/preprocessed_labels.csv\")\n        for _, row in labels_df.iterrows():\n            src = os.path.join(PROCESSED_DIR, f\"{row['patientId']}.png\")\n            dst_dir = os.path.join(PROCESSED_DIR, \"normal\" if row['label'] == 0 else \"pneumonia\")\n            dst = os.path.join(dst_dir, f\"{row['patientId']}.png\")\n            if os.path.exists(src):\n                os.rename(src, dst)\n    \n    # 计算FID分数\n    def calculate_fid(real_dir, fake_dir):\n        \"\"\"计算FID分数\"\"\"\n        print(\"计算FID分数...\")\n        fid_value = fid_score.calculate_fid_given_paths(\n            [real_dir, fake_dir],\n            batch_size=32,\n            device=DEVICE,\n            dims=2048\n        )\n        print(f\"FID分数: {fid_value:.2f}\")\n        \n        # 保存FID分数\n        with open(os.path.join(OUTPUT_DIR, \"fid_score.txt\"), \"w\") as f:\n            f.write(str(fid_value))\n            \n        return fid_value\n\n    fid_score_value = calculate_fid(\n        real_dir=os.path.join(PROCESSED_DIR, \"pneumonia\"),\n        fake_dir=\"/kaggle/working/generated_images/class_1\"\n    )\n\n    # 可视化生成图像\n    def visualize_generated_images(n=10):\n        \"\"\"可视化生成的肺炎图像\"\"\"\n        fig, axes = plt.subplots(2, 5, figsize=(15, 6))\n        for i in range(n):\n            img_path = f\"/kaggle/working/generated_images/class_1/gen_{i}.png\"\n            img = Image.open(img_path)\n            ax = axes[i//5, i%5]\n            ax.imshow(img, cmap='gray')\n            ax.set_title(f\"生成图像 {i+1}\")\n            ax.axis('off')\n        plt.suptitle(f'GAN生成的肺炎X光片 (FID={fid_score_value:.2f})', fontsize=16)\n        plt.tight_layout()\n        plt.savefig(os.path.join(OUTPUT_DIR, \"generated_samples.jpg\"))\n        plt.show()\n    \n    visualize_generated_images()\n    \n    return fid_score_value","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T05:49:23.253211Z","iopub.execute_input":"2025-06-30T05:49:23.253495Z","iopub.status.idle":"2025-06-30T05:49:23.266042Z","shell.execute_reply.started":"2025-06-30T05:49:23.253475Z","shell.execute_reply":"2025-06-30T05:49:23.265311Z"}},"outputs":[],"execution_count":2},{"cell_type":"code","source":"# ============================================================\n# 阶段3: 准备数据集\n# ============================================================\ndef stage_3_prepare_datasets():\n    print(\"\\n\" + \"=\"*50)\n    print(\"阶段3: 准备数据集\")\n    print(\"=\"*50)\n    \n    # 加载数据集划分\n    train_df = pd.read_csv(\"/kaggle/working/train_split.csv\")\n    val_df = pd.read_csv(\"/kaggle/working/val_split.csv\")\n    test_df = pd.read_csv(\"/kaggle/working/test_split.csv\")\n    \n    # 创建数据集目录结构（按类别分组）\n    def prepare_class_dirs(df, target_dir):\n        print(f\"准备目录: {target_dir}\")\n        normal_dir = os.path.join(target_dir, \"normal\")\n        pneumonia_dir = os.path.join(target_dir, \"pneumonia\")\n        \n        os.makedirs(normal_dir, exist_ok=True)\n        os.makedirs(pneumonia_dir, exist_ok=True)\n        \n        existing_files = len(os.listdir(normal_dir)) + len(os.listdir(pneumonia_dir))\n        if existing_files >= len(df):\n            print(f\"目录 {target_dir} 已存在 {existing_files} 个文件，跳过创建\")\n            return\n        \n        for _, row in df.iterrows():\n            src = os.path.join(PROCESSED_DIR, \"normal\" if row['label'] == 0 else \"pneumonia\", f\"{row['patientId']}.png\")\n            dst_dir = normal_dir if row['label'] == 0 else pneumonia_dir\n            dst = os.path.join(dst_dir, f\"{row['patientId']}.png\")\n            if os.path.exists(src) and not os.path.exists(dst):\n                os.link(src, dst)  # 创建硬链接节省空间\n    \n    # 准备训练/验证/测试集目录\n    prepare_class_dirs(train_df, \"/kaggle/working/train_data\")\n    prepare_class_dirs(val_df, \"/kaggle/working/val_data\")\n    prepare_class_dirs(test_df, \"/kaggle/working/test_data\")\n    \n    print(\"数据集准备完成!\")\n","metadata":{"trusted":true,"execution":{"execution_failed":"2025-06-29T17:27:23.141Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# 阶段4: 定义分类模型和数据集\n# ============================================================\ndef stage_4_define_model_and_dataset():\n    print(\"\\n\" + \"=\"*50)\n    print(\"阶段4: 定义分类模型和数据集\")\n    print(\"=\"*50)\n    \n    # 数据预处理\n    transform = transforms.Compose([\n        transforms.Resize((128, 128)),\n        transforms.ToTensor(),\n        transforms.Normalize([0.5], [0.5])\n    ])\n    \n    class PneumoniaDataset(Dataset):\n        \"\"\"肺炎分类数据集\"\"\"\n        def __init__(self, root_dir, transform=None, augment_ratio=0.0, class_label=None):\n            \"\"\"\n            root_dir: 包含normal和pneumonia子目录的路径\n            augment_ratio: 增强比例 (0.0-1.0)\n            class_label: 指定类别 (None表示全部)\n            \"\"\"\n            self.root_dir = root_dir\n            self.transform = transform\n            self.image_paths = []\n            self.labels = []\n            \n            # 加载真实图像\n            if class_label is None or class_label == 0:\n                normal_dir = os.path.join(root_dir, \"normal\")\n                if os.path.exists(normal_dir):\n                    normal_imgs = [os.path.join(normal_dir, f) for f in os.listdir(normal_dir)]\n                    self.image_paths.extend(normal_imgs)\n                    self.labels.extend([0] * len(normal_imgs))\n            \n            if class_label is None or class_label == 1:\n                pneumonia_dir = os.path.join(root_dir, \"pneumonia\")\n                if os.path.exists(pneumonia_dir):\n                    pneumonia_imgs = [os.path.join(pneumonia_dir, f) for f in os.listdir(pneumonia_dir)]\n                    self.image_paths.extend(pneumonia_imgs)\n                    self.labels.extend([1] * len(pneumonia_imgs))\n            \n            # 添加生成图像（只增强肺炎类）\n            if augment_ratio > 0 and (class_label is None or class_label == 1):\n                gen_dir = \"/kaggle/working/generated_images/class_1\"\n                if os.path.exists(gen_dir):\n                    num_augment = int(len(pneumonia_imgs) * augment_ratio)\n                    gen_imgs = [os.path.join(gen_dir, f) for f in os.listdir(gen_dir)][:num_augment]\n                    self.image_paths.extend(gen_imgs)\n                    self.labels.extend([1] * len(gen_imgs))\n            \n            print(f\"数据集大小: {len(self.image_paths)} (增强比例: {augment_ratio})\")\n        \n        def __len__(self):\n            return len(self.image_paths)\n        \n        def __getitem__(self, idx):\n            img_path = self.image_paths[idx]\n            label = self.labels[idx]\n            \n            # 加载图像\n            img = Image.open(img_path).convert('L')\n            \n            if self.transform:\n                img = self.transform(img)\n                \n            return img, label\n\n    class PneumoniaClassifier(nn.Module):\n        def __init__(self):\n            super().__init__()\n            # 使用预训练的DenseNet121\n            self.base_model = models.densenet121(pretrained=True)\n            \n            # 修改第一层卷积适配单通道输入\n            original_first_conv = self.base_model.features.conv0\n            self.base_model.features.conv0 = nn.Conv2d(\n                1, \n                original_first_conv.out_channels,\n                kernel_size=original_first_conv.kernel_size,\n                stride=original_first_conv.stride,\n                padding=original_first_conv.padding,\n                bias=False\n            )\n            \n            # 修改分类层\n            num_features = self.base_model.classifier.in_features\n            self.base_model.classifier = nn.Sequential(\n                nn.Linear(num_features, 256),\n                nn.ReLU(),\n                nn.Dropout(0.3),\n                nn.Linear(256, 1),\n                nn.Sigmoid()\n            )\n        \n        def forward(self, x):\n            return self.base_model(x)\n    \n    return transform, PneumoniaDataset, PneumoniaClassifier\n","metadata":{"trusted":true,"execution":{"execution_failed":"2025-06-29T17:27:23.141Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# 阶段5: 训练分类模型\n# ============================================================\ndef stage_5_train_classification_models(transform, DatasetClass, ModelClass):\n    print(\"\\n\" + \"=\"*50)\n    print(\"阶段5: 训练分类模型\")\n    print(\"=\"*50)\n    \n    # 实验设置：不同增强比例\n    augment_ratios = [0.0, 0.5, 1.0, 1.5]\n    results = {}\n    \n    for ratio in augment_ratios:\n        print(f\"\\n{'='*30}\")\n        print(f\"开始训练: 增强比例 = {ratio}\")\n        print(f\"{'='*30}\")\n        \n        # 检查是否已有结果\n        result_file = os.path.join(OUTPUT_DIR, f\"result_ratio_{ratio}.pth\")\n        if os.path.exists(result_file):\n            print(f\"增强比例 {ratio} 的结果已存在，跳过训练\")\n            results[ratio] = torch.load(result_file)\n            continue\n        \n        # 创建数据集\n        train_dataset = DatasetClass(\n            \"/kaggle/working/train_data\",\n            transform=transform,\n            augment_ratio=ratio\n        )\n        val_dataset = DatasetClass(\"/kaggle/working/val_data\", transform=transform)\n        test_dataset = DatasetClass(\"/kaggle/working/test_data\", transform=transform)\n        \n        # 创建数据加载器\n        train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=2)\n        val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False, num_workers=2)\n        test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False, num_workers=2)\n        \n        # 初始化模型\n        model = ModelClass().to(DEVICE)\n        criterion = nn.BCELoss()\n        optimizer = optim.Adam(model.parameters(), lr=1e-4, weight_decay=1e-5)\n        scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, 'max', patience=2, factor=0.5)\n        \n        # 训练参数\n        best_auc = 0.0\n        history = {'train_loss': [], 'val_auc': []}\n        \n        # 训练循环\n        for epoch in range(10):\n            model.train()\n            running_loss = 0.0\n            \n            for images, labels in train_loader:\n                images, labels = images.to(DEVICE), labels.float().to(DEVICE)\n                \n                optimizer.zero_grad()\n                outputs = model(images).squeeze()\n                loss = criterion(outputs, labels)\n                loss.backward()\n                optimizer.step()\n                \n                running_loss += loss.item() * images.size(0)\n            \n            # 验证集评估\n            model.eval()\n            val_labels = []\n            val_preds = []\n            \n            with torch.no_grad():\n                for images, labels in val_loader:\n                    images, labels = images.to(DEVICE), labels.cpu()\n                    outputs = model(images).squeeze().cpu()\n                    \n                    val_labels.extend(labels.numpy())\n                    val_preds.extend(outputs.numpy())\n            \n            # 计算AUC\n            auroc = AUROC(task=\"binary\")\n            auc = auroc(torch.tensor(val_preds), torch.tensor(val_labels))\n            \n            # 学习率调度\n            scheduler.step(auc)\n            \n            # 记录历史\n            epoch_loss = running_loss / len(train_dataset)\n            history['train_loss'].append(epoch_loss)\n            history['val_auc'].append(auc.item())\n            \n            print(f\"Epoch {epoch+1}/10 - Loss: {epoch_loss:.4f}, Val AUC: {auc:.4f}\")\n            \n            # 保存最佳模型\n            if auc > best_auc:\n                best_auc = auc\n                torch.save(model.state_dict(), os.path.join(OUTPUT_DIR, f\"best_model_aug_{ratio}.pth\"))\n        \n        # 测试集评估\n        model.load_state_dict(torch.load(os.path.join(OUTPUT_DIR, f\"best_model_aug_{ratio}.pth\")))\n        model.eval()\n        \n        test_labels = []\n        test_preds = []\n        \n        with torch.no_grad():\n            for images, labels in test_loader:\n                images, labels = images.to(DEVICE), labels.cpu()\n                outputs = model(images).squeeze().cpu()\n                \n                test_labels.extend(labels.numpy())\n                test_preds.extend(outputs.numpy())\n        \n        # 计算指标\n        test_preds_binary = [1 if p > 0.5 else 0 for p in test_preds]\n        report = classification_report(test_labels, test_preds_binary, output_dict=True)\n        cm = confusion_matrix(test_labels, test_preds_binary)\n        auc = auroc(torch.tensor(test_preds), torch.tensor(test_labels)).item()\n        \n        # 保存结果\n        result = {\n            'accuracy': report['accuracy'],\n            'precision': report['weighted avg']['precision'],\n            'recall': report['weighted avg']['recall'],\n            'f1': report['weighted avg']['f1-score'],\n            'auc': auc,\n            'confusion_matrix': cm,\n            'history': history\n        }\n        \n        # 保存到文件\n        torch.save(result, result_file)\n        results[ratio] = result\n        \n        print(f\"\\n增强比例 {ratio} 结果:\")\n        print(f\"准确率: {result['accuracy']:.4f}\")\n        print(f\"AUC: {result['auc']:.4f}\")\n        print(f\"F1分数: {result['f1']:.4f}\")\n        \n        # 清理内存\n        del model, train_loader, val_loader, test_loader\n        gc.collect()\n        torch.cuda.empty_cache()\n    \n    return results","metadata":{"trusted":true,"execution":{"execution_failed":"2025-06-29T17:27:23.141Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# 阶段6: 结果可视化与报告\n# ============================================================\ndef stage_6_visualize_and_report(results, fid_score_value):\n    print(\"\\n\" + \"=\"*50)\n    print(\"阶段6: 结果可视化与报告\")\n    print(\"=\"*50)\n    \n    # 确保结果目录存在\n    os.makedirs(OUTPUT_DIR, exist_ok=True)\n    \n    # 可视化不同增强比例下的性能\n    def plot_results(results):\n        \"\"\"可视化不同增强比例下的性能\"\"\"\n        ratios = list(results.keys())\n        accuracies = [results[r]['accuracy'] for r in ratios]\n        aucs = [results[r]['auc'] for r in ratios]\n        f1_scores = [results[r]['f1'] for r in ratios]\n        \n        plt.figure(figsize=(15, 5))\n        \n        plt.subplot(1, 3, 1)\n        plt.plot(ratios, accuracies, 'o-', markersize=8)\n        plt.title('准确率 vs 增强比例')\n        plt.xlabel('增强比例')\n        plt.ylabel('准确率')\n        plt.grid(True)\n        \n        plt.subplot(1, 3, 2)\n        plt.plot(ratios, aucs, 'o-', color='green', markersize=8)\n        plt.title('AUC vs 增强比例')\n        plt.xlabel('增强比例')\n        plt.ylabel('AUC')\n        plt.grid(True)\n        \n        plt.subplot(1, 3, 3)\n        plt.plot(ratios, f1_scores, 'o-', color='red', markersize=8)\n        plt.title('F1分数 vs 增强比例')\n        plt.xlabel('增强比例')\n        plt.ylabel('F1分数')\n        plt.grid(True)\n        \n        plt.tight_layout()\n        plt.savefig(os.path.join(OUTPUT_DIR, \"augmentation_performance.jpg\"))\n        plt.show()\n        \n        # 保存结果表格\n        result_df = pd.DataFrame({\n            '增强比例': ratios,\n            '准确率': accuracies,\n            'AUC': aucs,\n            'F1分数': f1_scores\n        })\n        result_df.to_csv(os.path.join(OUTPUT_DIR, \"results_summary.csv\"), index=False)\n        print(result_df)\n\n    # 混淆矩阵比较\n    def plot_confusion_matrices(results):\n        ratios = list(results.keys())\n        plt.figure(figsize=(15, 10))\n        for i, ratio in enumerate(ratios):\n            cm = results[ratio]['confusion_matrix']\n            plt.subplot(2, 2, i+1)\n            sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', \n                       xticklabels=['正常', '肺炎'], \n                       yticklabels=['正常', '肺炎'])\n            plt.title(f'增强比例: {ratio}\\n准确率: {results[ratio][\"accuracy\"]:.4f}')\n            plt.xlabel('预测标签')\n            plt.ylabel('真实标签')\n        \n        plt.tight_layout()\n        plt.savefig(os.path.join(OUTPUT_DIR, \"confusion_matrices.jpg\"))\n        plt.show()\n\n    # 训练历史比较\n    def plot_training_history(results):\n        ratios = list(results.keys())\n        plt.figure(figsize=(15, 5))\n        \n        plt.subplot(1, 2, 1)\n        for ratio in ratios:\n            plt.plot(results[ratio]['history']['train_loss'], 'o-', label=f'增强比例 {ratio}')\n        plt.title('训练损失')\n        plt.xlabel('Epoch')\n        plt.ylabel('Loss')\n        plt.legend()\n        plt.grid(True)\n        \n        plt.subplot(1, 2, 2)\n        for ratio in ratios:\n            plt.plot(results[ratio]['history']['val_auc'], 'o-', label=f'增强比例 {ratio}')\n        plt.title('验证集AUC')\n        plt.xlabel('Epoch')\n        plt.ylabel('AUC')\n        plt.legend()\n        plt.grid(True)\n        \n        plt.tight_layout()\n        plt.savefig(os.path.join(OUTPUT_DIR, \"training_history.jpg\"))\n        plt.show()\n\n    # 执行可视化\n    plot_results(results)\n    plot_confusion_matrices(results)\n    plot_training_history(results)\n\n    # 生成实验报告\n    def generate_report(results, fid_score):\n        \"\"\"生成实验报告\"\"\"\n        report = f\"\"\"\n        RSNA肺炎检测数据增强实验报告\n        =====================================\n        \n        实验概述:\n        - 使用GAN生成肺炎X光片进行数据增强\n        - 评估不同增强比例对分类模型性能的影响\n        - 生成图像质量评估(FID分数): {fid_score:.2f}\n        \n        实验设置:\n        - 分类模型: DenseNet121 (微调)\n        - 增强比例: {', '.join(map(str, results.keys()))}\n        - 训练周期: 10\n        - 设备: {DEVICE}\n        \n        关键结果:\n        \"\"\"\n        \n        # 添加结果表格\n        result_df = pd.DataFrame({\n            '增强比例': list(results.keys()),\n            '准确率': [results[r]['accuracy'] for r in results],\n            'AUC': [results[r]['auc'] for r in results],\n            'F1分数': [results[r]['f1'] for r in results]\n        })\n        \n        report += \"\\n\" + result_df.to_string(index=False)\n        \n        # 性能提升分析\n        base_acc = results[0.0]['accuracy']\n        best_acc = max([results[r]['accuracy'] for r in results])\n        improvement = (best_acc - base_acc) * 100\n        \n        report += f\"\\n\\n性能提升分析:\"\n        report += f\"\\n- 基准准确率: {base_acc:.4f}\"\n        report += f\"\\n- 最佳准确率: {best_acc:.4f} (提升: {improvement:.2f}%)\"\n        \n        # 结论\n        report += f\"\"\"\n        \n        结论:\n        {'数据增强显著提升了模型性能 (提升>5%)' if improvement > 5 else \n         '数据增强有一定效果但未达目标' if improvement > 0 else \n         '数据增强未改善模型性能'}\n        \n        局限性:\n        - GAN生成图像质量有待提高 (FID分数可进一步降低)\n        - 实验仅使用单一分类架构\n        - 未考虑生成图像的多样性评估\n        \n        建议:\n        - 尝试条件GAN生成特定类别图像\n        - 结合传统增强方法 (旋转、平移等)\n        - 使用更先进的分类架构 (如EfficientNet)\n        \"\"\"\n        \n        # 保存报告\n        with open(os.path.join(OUTPUT_DIR, \"experiment_report.txt\"), \"w\") as f:\n            f.write(report)\n        \n        print(report)\n    \n    # 生成最终报告\n    generate_report(results, fid_score_value)\n\n    print(\"实验完成! 所有结果保存在:\", OUTPUT_DIR)\n    # ============================================================\n# 主执行流程 - 分阶段运行\n# ============================================================\nif __name__ == \"__main__\":\n    # 阶段1: 加载生成器\n    generator = stage_1_load_generator()\n    \n    # 阶段2: 生成图像和评估\n    fid_score_value = stage_2_generate_and_evaluate(generator)\n    \n    # 阶段3: 准备数据集\n    stage_3_prepare_datasets()\n    \n    # 阶段4: 定义模型和数据集类\n    transform, DatasetClass, ModelClass = stage_4_define_model_and_dataset()\n    \n    # 阶段5: 训练分类模型\n    results = stage_5_train_classification_models(transform, DatasetClass, ModelClass)\n    \n    # 阶段6: 结果可视化和报告\n    stage_6_visualize_and_report(results, fid_score_value)","metadata":{"trusted":true,"execution":{"execution_failed":"2025-06-29T17:27:23.141Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}