解决 报错 np.array(images_data, dtype=’float32‘ )MemoryError

本文详细记录了在使用Python进行深度学习训练时遇到的图像加载MemoryError问题及解决方案。作者分享了一种有效的方法,通过调整电脑的性能设置来解决此问题,使深度学习程序能够顺利运行。
部署运行你感兴趣的模型镜像

在用python 进行深度学习训练时图像加载时,报错images_data = np.array(images_data, dtype='float32') / 255.0 MemoryError

看了网上很多关于这个错误的解决办法,最后管用的是一下这个 :

如果在第4步没有性能这个选项,就想我电脑这样。就选择高级-》设置-》高级  -》更改-》然后执行上述第6步骤,最后重启电脑,就可以了。现在我的可以啦。程序跑的则溜。贴个图拉,纪念一下。

您可能感兴趣的与本文相关的镜像

Python3.9

Python3.9

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

上述mask2former在进行tensorrt推理时报错:以下是推理代码,请检查是否有问题:def test_tensorrt_engine(): # 1. 加载TensorRT引擎 logger = trt.Logger(trt.Logger.WARNING) # 关键步骤:初始化内置插件 trt.init_libnvinfer_plugins(None, "") # 空字符串表示加载所有内置插件 with open("Mask2Former/mask2former.engine", "rb") as f: engine_data = f.read() # 创建运行时 runtime = trt.Runtime(logger) engine = runtime.deserialize_cuda_engine(engine_data) # 2. 创建执行上下文 context = engine.create_execution_context() # 3. 准备输入数据 processor = Mask2FormerImageProcessor.from_pretrained("Mask2Former/mask2former-swin-tiny-coco-panoptic") url = "http://images.cocodataset.org/val2017/000000039769.jpg" image = Image.open(requests.get(url, stream=True).raw) inputs = processor(images=image, return_tensors="np") pixel_values = inputs["pixel_values"].astype(np.float32) # 确保float32 # 4. 分配显存缓冲区 # 获取输入输出绑定索引 input_idx = engine.get_binding_index("pixel_input") # pixel_values output_idx0 = engine.get_binding_index("class_logits") # class_queries_logits output_idx1 = engine.get_binding_index("masks_queries") # masks_queries_logits # 分配设备内存 output_idx0_shape = (1 * 100 * 384) output_id0 = np.empty(output_idx0_shape, dtype=np.float32) output_idx1_shape = (1 * 100 * 384 * 384) output_id1 = np.empty(output_idx1_shape, dtype=np.float32) buffers = { input_idx: cuda.mem_alloc(pixel_values.nbytes), output_idx0: cuda.mem_alloc(output_id0.nbytes), # 预分配空间 output_idx1: cuda.mem_alloc(output_id1.nbytes) } # 5. 设置动态形状 context.set_binding_shape(input_idx, pixel_values.shape) cuda.memcpy_htod(buffers[input_idx], pixel_values) # 6. 执行推理 context.execute_v2( bindings=[int(buffers[input_idx]), int(buffers[output_idx0]), int(buffers[output_idx1])] ) # 获取输出 outputs = [ np.empty(context.get_binding_shape(output_idx0), dtype=np.float32), np.empty(context.get_binding_shape(output_idx1), dtype=np.float32) ] cuda.memcpy_dtoh(outputs[0], buffers[output_idx0]) cuda.memcpy_dtoh(outputs[1], buffers[output_idx1]) # 7. 后处理与可视化 class_logits, masks_logits = outputs outputs = Mask2FormerData( class_queries_logits=torch.tensor(class_logits), masks_queries_logits=torch.tensor(masks_logits) ) result = processor.post_process_panoptic_segmentation( outputs, target_sizes=[image.size[::-1]] # (height, width) ) instance_seg = result["segmentation"].numpy() instance_visualization = np.zeros((instance_seg.shape[0], instance_seg.shape[1], 3), dtype=np.uint8) for i in range(len(result["segments_info"])): mask = instance_seg == i color = np.random.randint(0, 255, size=3) instance_visualization[mask] = color Image.fromarray(instance_visualization).save(f"Mask2Former/gen_mask2.jpg") print("generate success...") print(f"检测到{len(result[0]['segments_info'])}个实例") # 可视化代码(同ONNX测试部分)
12-09
def test_tensorrt_engine(): # 1. 加载TensorRT引擎 logger = trt.Logger(trt.Logger.WARNING) # 关键步骤:初始化内置插件 trt.init_libnvinfer_plugins(None, "") # 空字符串表示加载所有内置插件 with open("Mask2Former/mask2former_static.engine", "rb") as f: engine_data = f.read() # 创建运行时 runtime = trt.Runtime(logger) engine = runtime.deserialize_cuda_engine(engine_data) # 2. 创建执行上下文 context = engine.create_execution_context() # 3. 准备输入数据 processor = Mask2FormerImageProcessor.from_pretrained("Mask2Former/mask2former-swin-tiny-coco-panoptic") url = "http://images.cocodataset.org/val2017/000000039769.jpg" image = Image.open(requests.get(url, stream=True).raw) inputs = processor(images=image, return_tensors="np") pixel_values = inputs["pixel_values"].astype(np.float32) # 确保float32 # 4. 分配显存缓冲区 # 获取输入绑定索引 input_idx = engine.get_binding_index("pixel_values") output_idx0 = engine.get_binding_index("class_queries_logits") output_idx1 = engine.get_binding_index("masks_queries_logits") # 关键修复:使用正确的形状获取方法 input_shape = (1, 3, 384, 384) output_shape0 = (1, 100, 134) output_shape1 = (1, 100, 96, 96) # 准备数据 output_data0 = np.empty(output_shape0, dtype=np.float32) output_data1 = np.empty(output_shape1, dtype=np.float32) # 分配设备内存 d_input = cuda.mem_alloc(pixel_values.nbytes) d_output0 = cuda.mem_alloc(output_data0.nbytes) d_output1 = cuda.mem_alloc(output_data1.nbytes) # 5. 设置动态形状 context.set_binding_shape(input_idx, pixel_values.shape) cuda.memcpy_htod(d_input, pixel_values) # 6. 执行推理 bindings = [int(d_input), int(d_output0), int(d_output1)] ires = context.execute_v2(bindings=bindings) # 获取输出 cuda.memcpy_dtoh(output_data0, d_output0) cuda.memcpy_dtoh(output_data1, d_output1) # 7. 后处理与可视化 class_logits, masks_logits = output_data0, output_data1 outputs = Mask2FormerData( class_queries_logits=torch.tensor(class_logits), masks_queries_logits=torch.tensor(masks_logits) ) result = processor.post_process_panoptic_segmentation( outputs, target_sizes=[image.size[::-1]] # (height, width) ) instance_seg = result["segmentation"].numpy() instance_visualization = np.zeros((instance_seg.shape[0], instance_seg.shape[1], 3), dtype=np.uint8) for i in range(len(result["segments_info"])): mask = instance_seg == i color = np.random.randint(0, 255, size=3) instance_visualization[mask] = color Image.fromarray(instance_visualization).save(f"Mask2Former/gen_mask2.jpg") print("generate success...") print(f"检测到{len(result[0]['segments_info'])}个实例") # 可视化代码(同ONNX测试部分)出现以下报错:[12/09/2025-09:56:12] [TRT] [E] 1: [executionContext.cpp::nvinfer1::rt::ExecutionContext::executeInternal::1224] Error Code 1: Cuda Runtime (an illegal memory access was encountered) Traceback (most recent call last): File "D:/app/LM/mask2former.py", line 486, in <module> test_tensorrt_engine() File "D:/app/LM/mask2former.py", line 447, in test_tensorrt_engine cuda.memcpy_dtoh(output_data0, d_output0) KeyboardInterrupt请详细分析原因和解决措施
最新发布
12-10
import os import numpy as np import pandas as pd from PIL import Image import matplotlib.pyplot as plt import seaborn as sns from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.metrics import classification_report, confusion_matrix, accuracy_score from sklearn.metrics import roc_curve, auc from sklearn.preprocessing import label_binarize import tensorflow as tf from tensorflow.keras.layers import Input, Dense, GlobalAveragePooling2D, Dropout, LayerNormalization from tensorflow.keras.layers import Conv2D, MaxPooling2D, BatchNormalization, Add, Activation from tensorflow.keras.layers import Concatenate, Reshape, Flatten from tensorflow.keras.models import Model from tensorflow.keras.optimizers import Adam from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint from tensorflow.keras.regularizers import l2 from tensorflow.keras.utils import Sequence from tensorflow.keras.layers import Input, Dense, GlobalAveragePooling2D, GlobalAveragePooling1D, Dropout, \ LayerNormalization # 添加 GlobalAveragePooling1D from tensorflow.keras.layers import Conv2D, Conv1D, MaxPooling2D, BatchNormalization, Add, Activation import gc import warnings warnings.filterwarnings('ignore') import tifffile # GPU配置 try: gpus = tf.config.experimental.list_physical_devices('GPU') if gpus: tf.config.experimental.set_memory_growth(gpus[0], True) print("GPU加速已启用") except Exception as e: print(f"GPU配置错误: {str(e)}") class MultiModalDataGenerator(Sequence): """多模态数据生成器,处理7x4x39图像和39维化学数据""" def __init__(self, image_paths, chemical_data, labels, batch_size=16, shuffle=True): self.image_paths = image_paths self.chemical_data = chemical_data self.labels = labels self.batch_size = batch_size self.shuffle = shuffle self.indices = np.arange(len(self.image_paths)) self.skip_same_value_count = 0 # 初始化跳过计数器 if self.shuffle: np.random.shuffle(self.indices) def __len__(self): return int(np.ceil(len(self.image_paths) / self.batch_size)) def __getitem__(self, idx): batch_indices = self.indices[idx * self.batch_size:(idx + 1) * self.batch_size] batch_images = [] batch_chemical = [] batch_labels = [] for i in batch_indices: # 读取39通道TIFF图像 try: img = tifffile.imread(self.image_paths[i]) # 处理图像形状,确保为 (39, 4, 7) if img.shape == (7, 4, 39): img = np.moveaxis(img, -1, 0) # (7, 4, 39) -> (39, 4, 7) elif img.shape == (39, 7, 4): img = np.moveaxis(img, 1, 2) # (39, 7, 4) -> (39, 4, 7) elif img.shape != (39, 4, 7): print(f"警告: 图像形状异常 {img.shape},尝试处理") # 仅转换数据类型,跳过标准化 # print(f"原始图像数据: {self.image_paths[i]}, 形状: {img.shape}, 类型: {img.dtype}") # print(f"原始数据范围: min={img.min()}, max={img.max()}, mean={img.mean()}") if img.dtype == np.uint16 or img.dtype == np.uint8: img = img.astype(np.float32) # 仅转换类型,不缩放 else: img = img.astype(np.float32) if np.isnan(img).any(): print(f"警告: 图像 {self.image_paths[i]} 包含 nan 值,跳过处理") continue if img.max() == img.min(): print(f"警告: 图像 {self.image_paths[i]} 数据无效(全相同值),跳过处理") self.skip_same_value_count += 1 # 增加跳过计数器 continue # print(f"处理后范围: min={img.min()}, max={img.max()}, mean={img.mean()}") # 检查图像数据 if np.isnan(img).any(): print(f"警告: 图像 {self.image_paths[i]} 包含 nan 值,形状: {img.shape}, 数据类型: {img.dtype}") print(f"数据范围: min={img.min()}, max={img.max()}, mean={img.mean()}") img = np.zeros((39, 4, 7), dtype=np.float32) # 替换为全零 if not np.isnan(img).any() and img.max() != img.min(): batch_images.append(img) else: print(f"跳过无效图像: {self.image_paths[i]}") except Exception as e: print(f"加载图像失败 {self.image_paths[i]}: {str(e)}") continue # 获取化学数据,确保形状为 (39,) if isinstance(self.chemical_data, np.ndarray): chemical_features = self.chemical_data[i] else: chemical_features = self.chemical_data.iloc[i].values if chemical_features.shape != (39,): print(f"警告: 化学数据形状异常 {chemical_features.shape},调整为 (39,)") chemical_features = chemical_features.reshape(39,) batch_chemical.append(chemical_features) batch_labels.append(self.labels[i]) # 组装批次数据 batch_images = np.array(batch_images, dtype=np.float32) batch_chemical = np.array(batch_chemical, dtype=np.float32) batch_labels = np.array(batch_labels) # 验证化学数据形状 if batch_chemical.shape[1] != 39: print(f"警告: 批次化学数据形状异常 {batch_chemical.shape},调整为 (batch_size, 39)") batch_chemical = batch_chemical.reshape(-1, 39) # 调试信息(仅第一次批次) if idx == 0: print(f"批次数据形状: 图像={batch_images.shape}, 化学={batch_chemical.shape}, 标签={batch_labels.shape}") print(f"标签分布: {np.bincount(batch_labels)}") assert batch_images.shape[1:] == (39, 7, 4), f"图像形状应为 (39, 4, 7),实际为 {batch_images.shape[1:]}" # 检查数据 if np.isnan(batch_images).any() or np.isnan(batch_chemical).any(): print(f"警告: 批次 {idx} 数据包含 nan 值!") # 打印输入形状并结束进程 print(f"image_input 形状: {batch_images.shape}") print(f"chemical_input 形状: {batch_chemical.shape}") # 转换为 TensorFlow 兼容的输出格式 return ({"image_input": batch_images, "chemical_input": batch_chemical}, batch_labels) def on_epoch_end(self): print(f"跳过处理的图像数量(全相同值): {self.skip_same_value_count}") if self.shuffle: np.random.shuffle(self.indices) class MultiModalFusionModel: """多模态特征融合模型 - 基础版本""" def __init__(self, img_root="D:\\西北地区铜镍矿\\多模态测试\\图片训练", data_path="D:\\西北地区铜镍矿\\数据\\训练数据.xlsx"): self.img_root = img_root self.data_path = data_path self.scaler = StandardScaler() self.model = None self.history = None def load_data(self): """加载多模态数据,包括图像路径、化学特征和标签。 Returns: tuple: (image_paths, chemical_data, labels) """ print("=== 加载多模态数据 ===") df = pd.read_excel(self.data_path) print(f"数据形状: {df.shape}") print(f"列名: {df.columns.tolist()}") # 检查必要列 if 'name' not in df.columns: raise ValueError("Excel数据中必须包含'name'列以匹配图像文件!") if 'class' not in df.columns: raise ValueError("Excel数据中必须包含'class'列作为标签!") # 选择化学特征列(第6到45列,共39列) feature_cols = df.columns[6:45] chemical_data = df[feature_cols].select_dtypes(include=[np.number]) print(f"化学数据形状: {chemical_data.shape}") # 构建图像路径和标签 image_paths = [] image_labels = [] valid_indices = [] label_map = {'positive': 0, 'neutral': 1, 'negative': 2} for idx, row in df.iterrows(): filename = row['name'] class_label = row['class'] if not isinstance(filename, str) or class_label not in label_map: print(f"跳过无效数据: filename={filename}, class={class_label}") continue # 在对应类别文件夹中查找图像 class_folder = os.path.join(self.img_root, class_label) # 尝试不同的文件扩展名 possible_paths = [ os.path.join(class_folder, filename), os.path.join(class_folder, f"{filename}.tif"), os.path.join(class_folder, f"{filename}.tiff") ] found = False for img_path in possible_paths: if os.path.exists(img_path): image_paths.append(img_path) image_labels.append(label_map[class_label]) valid_indices.append(idx) found = True break if not found: print(f"警告: 图像文件未找到: {filename} in {class_label}") # 只保留匹配成功的化学数据 chemical_data = chemical_data.iloc[valid_indices].reset_index(drop=True) image_labels = np.array(image_labels) if len(image_paths) == 0: raise ValueError("未找到任何匹配的图像-化学数据对!") print(f"成功匹配样本数: {len(image_paths)}") print(f"标签分布详情:") for class_name, label in label_map.items(): count = np.sum(image_labels == label) print(f" {class_name} (标签{label}): {count}个样本") # 显示前5个样本的路径和标签 print(f"前5个样本:") for i in range(min(5, len(image_paths))): print(f" 样本{i}: {os.path.basename(image_paths[i])} -> 标签{image_labels[i]}") return image_paths, chemical_data.values, image_labels def build_model(self): """构建多模态融合模型""" print("=== 构建多模态融合模型 ===") # 图像输入分支 - 处理39x4x7的多光谱图像 img_input = Input(shape=(39, 7, 4), name='image_input') # 图像特征提取 - 使用1D卷积处理每个光谱维度 x = Reshape((39, 7 * 4))(img_input) # 重塑为(39, 28) x = Conv1D(64, 3, activation='relu', padding='same')(x) x = BatchNormalization()(x) x = Conv1D(128, 3, activation='relu', padding='same')(x) x = BatchNormalization()(x) x = GlobalAveragePooling1D()(x) x = Dense(256, activation='relu')(x) x = Dropout(0.3)(x) img_features = Dense(128, activation='relu', name='img_features')(x) # 化学数据输入分支 chem_input = Input(shape=(39,), name='chemical_input') y = Dense(128, activation='relu')(chem_input) y = BatchNormalization()(y) y = Dropout(0.3)(y) y = Dense(256, activation='relu')(y) y = Dropout(0.3)(y) chem_features = Dense(128, activation='relu', name='chem_features')(y) # 特征融合 fused = Concatenate(name='feature_fusion')([img_features, chem_features]) fused = Dense(256, activation='relu')(fused) fused = Dropout(0.4)(fused) fused = Dense(128, activation='relu')(fused) fused = Dropout(0.3)(fused) # 分类输出 output = Dense(3, activation='softmax', name='classification')(fused) # 创建模型 model = Model(inputs=[img_input, chem_input], outputs=output) # 编译模型(添加梯度裁剪) optimizer = Adam(learning_rate=0.0001, clipvalue=1.0) model.compile( optimizer=optimizer, loss='sparse_categorical_crossentropy', metrics=['accuracy'] ) # print(model.summary()) self.model = model return model def train(self, image_paths, chemical_data, labels, test_size=0.2, batch_size=16, epochs=100): """训练模型""" print("=== 开始训练 ===") # 数据预处理 chemical_data_scaled = self.scaler.fit_transform(chemical_data) # 数据分割 X_img_train, X_img_test, X_chem_train, X_chem_test, y_train, y_test = train_test_split( image_paths, chemical_data_scaled, labels, test_size=test_size, random_state=42, stratify=labels ) print(f"训练集大小: {len(X_img_train)}") print(f"测试集大小: {len(X_img_test)}") print(f"训练集标签分布: {np.bincount(y_train)}") print(f"测试集标签分布: {np.bincount(y_test)}") # 创建数据生成器 train_generator = MultiModalDataGenerator( X_img_train, X_chem_train, y_train, batch_size=batch_size, shuffle=True ) val_generator = MultiModalDataGenerator( X_img_test, X_chem_test, y_test, batch_size=batch_size, shuffle=False ) # 调试生成器输出 sample_data, sample_labels = train_generator[0] print(f"调试生成器输出: 图像形状{sample_data['image_input'].shape}, 化学形状{sample_data['chemical_input'].shape}, 标签形状{sample_labels.shape}") # print(f"图像数据范围: {sample_data['image_input'].min()} ~ {sample_data['image_input'].max()}") # print(f"化学数据范围: {sample_data['chemical_input'].min()} ~ {sample_data['chemical_input'].max()}") # 回调函数 callbacks = [ EarlyStopping(monitor='val_loss', patience=15, restore_best_weights=True), ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=8, min_lr=1e-6), ModelCheckpoint('best_multimodal_model_v3.keras', save_best_only=True, monitor='val_accuracy') ] # 训练模型 try: self.history = self.model.fit( train_generator, validation_data=val_generator, epochs=epochs, callbacks=callbacks, verbose=1 ) except Exception as e: print(f"使用生成器训练失败,尝试加载所有数据到内存: {str(e)}") # 备用方案:加载所有数据到内存 print("正在加载训练数据到内存...") X_img_train_loaded = [] X_chem_train_loaded = [] y_train_loaded = [] for i in range(len(train_generator)): batch_data, batch_labels = train_generator[i] X_img_train_loaded.append(batch_data["image_input"]) X_chem_train_loaded.append(batch_data["chemical_input"]) y_train_loaded.append(batch_labels) X_img_train_array = np.vstack(X_img_train_loaded) X_chem_train_array = np.vstack(X_chem_train_loaded) y_train_array = np.hstack(y_train_loaded) print("正在加载验证数据到内存...") X_img_val_loaded = [] X_chem_val_loaded = [] y_val_loaded = [] for i in range(len(val_generator)): batch_data, batch_labels = val_generator[i] X_img_val_loaded.append(batch_data["image_input"]) X_chem_val_loaded.append(batch_data["chemical_input"]) y_val_loaded.append(batch_labels) X_img_val_array = np.vstack(X_img_val_loaded) X_chem_val_array = np.vstack(X_chem_val_loaded) y_val_array = np.hstack(y_val_loaded) print( f"训练数据形状: 图像{X_img_train_array.shape}, 化学{X_chem_train_array.shape}, 标签{y_train_array.shape}") print(f"验证数据形状: 图像{X_img_val_array.shape}, 化学{X_chem_val_array.shape}, 标签{y_val_array.shape}") # 使用数组训练 self.history = self.model.fit( [X_img_train_array, X_chem_train_array], y_train_array, validation_data=([X_img_val_array, X_chem_val_array], y_val_array), batch_size=batch_size, epochs=epochs, callbacks=callbacks, verbose=1 ) # 恢复默认行为 tf.config.run_functions_eagerly(False) return self.history def evaluate(self, image_paths, chemical_data, labels): """评估模型""" print("=== 模型评估 ===") # 预处理化学数据 chemical_data_scaled = self.scaler.transform(chemical_data) # 创建测试生成器 test_generator = MultiModalDataGenerator( image_paths, chemical_data_scaled, labels, batch_size=16, shuffle=False ) # 预测 predictions = self.model.predict(test_generator) y_pred = np.argmax(predictions, axis=1) # 计算指标 accuracy = accuracy_score(labels, y_pred) print(f"准确率: {accuracy:.4f}") # 分类报告 class_names = ['positive', 'neutral', 'negative'] print("\n分类报告:") print(classification_report(labels, y_pred, target_names=class_names)) # 混淆矩阵 cm = confusion_matrix(labels, y_pred) plt.figure(figsize=(8, 6)) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=class_names, yticklabels=class_names) plt.title('混淆矩阵') plt.ylabel('真实标签') plt.xlabel('预测标签') plt.tight_layout() plt.savefig('confusion_matrix_v3.png', dpi=300, bbox_inches='tight') plt.show() return accuracy, y_pred, predictions def main(): """主函数""" # 创建模型实例 model = MultiModalFusionModel() # 加载数据 image_paths, chemical_data, labels = model.load_data() # 构建模型 model.build_model() # 训练模型 history = model.train(image_paths, chemical_data, labels, batch_size=24, epochs=10) # 评估模型 accuracy, predictions, probabilities = model.evaluate(image_paths, chemical_data, labels) print(f"\n最终准确率: {accuracy:.4f}") if __name__ == "__main__": main()
11-02
import os os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0' # os.environ['TF_DETERMINISTIC_OPS'] = '1' # 注释掉这行,避免确定性操作导致的种子问题 import numpy as np import pandas as pd import tensorflow as tf from tensorflow.keras.layers import Input, Dense, Dropout, BatchNormalization, Concatenate, Reshape, Conv1D, \ GlobalAveragePooling1D from tensorflow.keras.models import Model from tensorflow.keras.optimizers import Adam from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint import tifffile import matplotlib.pyplot as plt import seaborn as sns from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.metrics import classification_report, confusion_matrix, accuracy_score import gc import warnings warnings.filterwarnings('ignore') # 清除计算图 tf.keras.backend.clear_session() # 设置随机种子以确保可重复性 import random import numpy as np random.seed(42) np.random.seed(42) tf.random.set_seed(42) # GPU 配置 try: gpus = tf.config.experimental.list_physical_devices('GPU') if gpus: for gpu in gpus: tf.config.experimental.set_memory_growth(gpu, True) print("✅ GPU 加速已启用") else: print("⚠️ 未检测到 GPU,使用 CPU 训练") except Exception as e: print(f"❌ GPU 配置失败: {str(e)}") class MultiModalDataGenerator(tf.keras.utils.Sequence): """改进的数据生成器 - 使用 tf.data API 兼容格式""" def __init__(self, image_paths, chemical_data, labels, batch_size=16, shuffle=True): self.image_paths = image_paths self.chemical_data = chemical_data self.labels = labels self.batch_size = batch_size self.shuffle = shuffle self.indices = np.arange(len(self.image_paths)) # 计算均值用于填充无效样本 self.image_mean = self._calculate_image_mean() self.chem_mean = self._calculate_chem_mean() self.on_epoch_end() def _calculate_image_mean(self): """计算图像均值用于填充无效样本""" sample_img = np.zeros((50, 43, 3), dtype=np.float32) count = 0 for img_path in self.image_paths[:min(100, len(self.image_paths))]: try: img = tifffile.imread(img_path) # 统一形状为 (50, 43, 3) if img.shape == (43, 3, 50): img = np.moveaxis(img, -1, 0) elif img.shape == (50, 3, 43): img = np.transpose(img, (0, 2, 1)) elif img.shape != (50, 43, 3): # 使用均值填充无效样本 img = self.image_mean.copy() valid_sample = False if img.shape == (50, 43, 3): sample_img += img.astype(np.float32) count += 1 except: continue return sample_img / max(count, 1) if count > 0 else np.zeros((50, 43, 3)) def _calculate_chem_mean(self): """计算化学数据均值用于填充无效样本""" if isinstance(self.chemical_data, np.ndarray): return np.nanmean(self.chemical_data, axis=0) elif isinstance(self.chemical_data, pd.DataFrame): return self.chemical_data.mean().values else: return np.zeros(39) def __len__(self): return int(np.ceil(len(self.indices) / self.batch_size)) def __getitem__(self, idx): low = idx * self.batch_size high = min(low + self.batch_size, len(self.indices)) batch_indices = self.indices[low:high] batch_images = [] batch_chemical = [] batch_labels = [] # 记录哪些样本是无效的(占位数据) batch_valid_mask = [] for i in batch_indices: valid_sample = True try: # 尝试加载和处理图像 img = tifffile.imread(self.image_paths[i]) # 统一形状为 (50, 43, 3) if img.shape == (43, 3, 50): img = np.moveaxis(img, -1, 0) elif img.shape == (50, 3, 43): img = np.transpose(img, (0, 2, 1)) elif img.shape != (50, 43, 3): # 使用均值填充无效样本 img = self.image_mean.copy() valid_sample = False img = img.astype(np.float32) # 检查NaN或全零图像 if np.isnan(img).any() or img.max() == img.min(): img = self.image_mean.copy() valid_sample = False except Exception as e: # 加载失败时使用均值图像 img = self.image_mean.copy() valid_sample = False try: # 处理化学数据 if isinstance(self.chemical_data, np.ndarray): chem_feat = self.chemical_data[i].reshape(-1) else: chem_feat = self.chemical_data.iloc[i].values.reshape(-1) if chem_feat.shape != (39,) or np.isnan(chem_feat).any(): chem_feat = self.chem_mean.copy() valid_sample = False except: chem_feat = self.chem_mean.copy() valid_sample = False batch_images.append(img) batch_chemical.append(chem_feat) batch_labels.append(self.labels[i]) batch_valid_mask.append(valid_sample) # 构建批次 X_img = np.stack(batch_images) X_chem = np.array(batch_chemical, dtype=np.float32) y_batch = np.array(batch_labels, dtype=np.int32) valid_mask = np.array(batch_valid_mask, dtype=bool) # 返回数据、标签和有效样本掩码 return (X_img, X_chem), y_batch, valid_mask def on_epoch_end(self): if self.shuffle: np.random.shuffle(self.indices) def to_dataset(self): """转换为 tf.data.Dataset 格式""" def gen(): for i in range(len(self)): inputs, labels, _ = self[i] # 忽略valid_mask yield inputs, labels # 使用您建议的格式:明确指定dtype和shape output_signature = ( ( tf.TensorSpec(shape=(None, 50, 43, 3), dtype=tf.float32), # 图像输入 tf.TensorSpec(shape=(None, 39), dtype=tf.float32) # 化学输入 ), tf.TensorSpec(shape=(None,), dtype=tf.int32) # 标签 ) return tf.data.Dataset.from_generator( gen, output_signature=output_signature ).prefetch(tf.data.AUTOTUNE) class MultiModalFusionModel: def __init__(self, img_root="D:\\西北地区铜镍矿\\多模态测试\\图片训练", data_path="D:\\西北地区铜镍矿\\数据\\训练数据.xlsx"): self.img_root = img_root self.data_path = data_path self.scaler = StandardScaler() self.model = None self.history = None def load_data(self): print("🔍 正在加载数据...") df = pd.read_excel(self.data_path) print(f"原始数据形状: {df.shape}") required = ['name', 'class'] for col in required: if col not in df.columns: raise ValueError(f"Excel 缺少必要列: {col}") feature_cols = df.columns[6:45] chemical_data = df[feature_cols].select_dtypes(include=[np.number]) # 二分类标签映射:positive为0,negative为1,去掉neutral类 label_map = {'positive': 0, 'negative': 1} image_paths, labels_list = [], [] for _, row in df.iterrows(): name = row['name'] cls = row['class'] if not isinstance(name, str) or cls not in label_map: continue class_dir = os.path.join(self.img_root, cls) found = False for ext in ['', '.tif', '.tiff']: path = os.path.join(class_dir, f"{name}{ext}") if os.path.exists(path): image_paths.append(path) labels_list.append(label_map[cls]) found = True break if not found: # 即使找不到图像,也保留样本(后续使用占位数据) image_paths.append(os.path.join(class_dir, "placeholder")) # 占位路径 labels_list.append(label_map[cls]) labels_array = np.array(labels_list) print(f"✅ 加载 {len(image_paths)} 个样本") counts = np.bincount(labels_array) print(f"📊 二分类标签分布: positive={counts[0]}, negative={counts[1]}") # 平衡采样:确保每个类别都有500个样本 print("⚖️ 开始平衡采样...") # 分离两个类别的索引 positive_indices = np.where(labels_array == 0)[0] non_positive_indices = np.where(labels_array == 1)[0] print(f"positive样本数量: {len(positive_indices)}") print(f"negative样本数量: {len(non_positive_indices)}") # 设置每个类别的目标样本数 target_samples_per_class = 500 # 对positive类进行采样 if len(positive_indices) >= target_samples_per_class: # 如果positive样本足够,随机选择500个 selected_positive = np.random.choice(positive_indices, target_samples_per_class, replace=False) else: # 如果positive样本不足,使用所有样本并重复采样 selected_positive = np.random.choice(positive_indices, target_samples_per_class, replace=True) print(f"⚠️ positive样本不足,使用重复采样") # 对negative类进行采样 if len(non_positive_indices) >= target_samples_per_class: # 如果negative样本足够,随机选择500个 selected_non_positive = np.random.choice(non_positive_indices, target_samples_per_class, replace=False) else: # 如果negative样本不足,使用所有样本并重复采样 selected_non_positive = np.random.choice(non_positive_indices, target_samples_per_class, replace=True) print(f"⚠️ negative样本不足,使用重复采样") # 合并选中的索引 selected_indices = np.concatenate([selected_positive, selected_non_positive]) # 根据选中的索引重新组织数据 balanced_image_paths = [image_paths[i] for i in selected_indices] balanced_chemical_data = chemical_data.iloc[selected_indices] if isinstance(chemical_data, pd.DataFrame) else chemical_data[selected_indices] balanced_labels = labels_array[selected_indices] # 打乱数据顺序 shuffle_indices = np.random.permutation(len(balanced_labels)) balanced_image_paths = [balanced_image_paths[i] for i in shuffle_indices] balanced_chemical_data = balanced_chemical_data.iloc[shuffle_indices] if isinstance(balanced_chemical_data, pd.DataFrame) else balanced_chemical_data[shuffle_indices] balanced_labels = balanced_labels[shuffle_indices] print(f"✅ 平衡后样本总数: {len(balanced_labels)}") balanced_counts = np.bincount(balanced_labels) print(f"📊 平衡后标签分布: positive={balanced_counts[0]}, negative={balanced_counts[1]}") return balanced_image_paths, balanced_chemical_data, balanced_labels def build_model(self): print("🧱 正在构建模型...") # 定义输入 image_input = Input(shape=(50, 43, 3), name='image_input') chem_input = Input(shape=(39,), name='chemical_input') # 图像分支 - 简化但有效的特征提取 x = Reshape((50, 129))(image_input) # 43 * 3 = 129 x = Conv1D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(x) x = BatchNormalization()(x) x = Conv1D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(x) x = BatchNormalization()(x) x = GlobalAveragePooling1D()(x) x = Dense(256, activation='relu', kernel_initializer='he_normal')(x) x = Dropout(0.3, seed=42)(x) img_features = Dense(128, activation='relu', kernel_initializer='he_normal')(x) # 化学分支 - 简化但有效的特征提取 y = Dense(128, activation='relu', kernel_initializer='he_normal')(chem_input) y = BatchNormalization()(y) y = Dropout(0.3, seed=43)(y) y = Dense(256, activation='relu', kernel_initializer='he_normal')(y) y = BatchNormalization()(y) y = Dropout(0.3, seed=44)(y) chem_features = Dense(128, activation='relu', kernel_initializer='he_normal')(y) # 融合分支 - 简化结构,提高训练稳定性 merged = Concatenate()([img_features, chem_features]) z = Dense(256, activation='relu', kernel_initializer='he_normal')(merged) z = BatchNormalization()(z) z = Dropout(0.3, seed=45)(z) z = Dense(128, activation='relu', kernel_initializer='he_normal')(z) z = Dropout(0.2, seed=46)(z) output = Dense(2, activation='softmax')(z) # 最终输出 # 创建模型 model = Model(inputs=[image_input, chem_input], outputs=output) # 使用更保守的学习率和更严格的梯度裁剪 optimizer = Adam(learning_rate=1e-4, clipnorm=0.5) # 降低学习率,更严格的梯度裁剪 model.compile( loss='sparse_categorical_crossentropy', optimizer=optimizer, metrics=['accuracy'] ) # 打印模型结构 print("✅ 模型输入顺序: [图像输入, 化学输入]") print("✅ 模型输入形状:", [i.shape for i in model.inputs]) print("✅ 模型输出形状:", model.output.shape) print("💡 模型架构优化: 简化网络结构,提高训练稳定性,避免过拟合") self.model = model return model def train(self, image_paths, chemical_data, labels, test_size=0.2, batch_size=8, epochs=100): print("🚀 开始训练...") # 分割数据集 X_train_img, X_test_img, X_train_chem, X_test_chem, y_train, y_test = train_test_split( image_paths, chemical_data, labels, test_size=test_size, stratify=labels, random_state=42 ) # 计算类别权重来解决不平衡问题 - 使用平衡策略 print("⚖️ 计算类别权重...") class_counts = np.bincount(y_train) total_samples = len(y_train) # 平衡策略:使用sklearn的balanced权重计算 from sklearn.utils.class_weight import compute_class_weight class_weights_balanced = compute_class_weight('balanced', classes=np.unique(y_train), y=y_train) class_weights = dict(enumerate(class_weights_balanced)) # 进一步平衡权重,避免过度偏向 max_weight = max(class_weights.values()) min_weight = min(class_weights.values()) weight_ratio = max_weight / min_weight # 如果权重比例过大,进行缩放 if weight_ratio > 3.0: scale_factor = 3.0 / weight_ratio for key in class_weights: if class_weights[key] == max_weight: class_weights[key] *= scale_factor print(f"📊 训练集类别分布: {class_counts}") print(f"⚖️ 平衡类别权重: {class_weights}") print("💡 策略: 使用balanced权重计算,避免过度偏向") # 标准化化学数据 print("🔢 标准化化学数据...") self.scaler.fit(X_train_chem) X_train_chem_scaled = self.scaler.transform(X_train_chem) X_test_chem_scaled = self.scaler.transform(X_test_chem) # 创建生成器 print("🔄 创建数据生成器...") train_gen = MultiModalDataGenerator(X_train_img, X_train_chem_scaled, y_train, batch_size, shuffle=True) val_gen = MultiModalDataGenerator(X_test_img, X_test_chem_scaled, y_test, batch_size, shuffle=False) # 转换为 tf.data.Dataset train_ds = train_gen.to_dataset() val_ds = val_gen.to_dataset() # 回调函数 - 优化训练过程,修复文件权限问题 callbacks = [ EarlyStopping(monitor='val_loss', patience=15, restore_best_weights=True, verbose=1), ReduceLROnPlateau(monitor='val_loss', factor=0.7, patience=8, min_lr=1e-7, verbose=1), # 更温和的学习率衰减 # 移除ModelCheckpoint避免权限问题,使用手动保存 ] # 开始训练(使用 tf.data.Dataset) print("⏳ 训练中...") self.history = self.model.fit( train_ds, validation_data=val_ds, epochs=epochs, callbacks=callbacks, verbose=1, class_weight=class_weights # 添加类别权重 ) return self.history def evaluate(self, image_paths, chemical_data, labels): """改进的评估方法,解决所有已知问题并提高准确率""" print("📈 开始评估...") # 标准化化学数据 chemical_data_scaled = self.scaler.transform(chemical_data) # 创建生成器 test_gen = MultiModalDataGenerator(image_paths, chemical_data_scaled, labels, batch_size=8, shuffle=False) # 收集所有有效样本的预测和标签 all_preds = [] all_labels = [] # 逐个批次预测并收集有效样本 for i in range(len(test_gen)): (batch_img, batch_chem), batch_label, valid_mask = test_gen[i] # 预测 batch_pred = self.model.predict([batch_img, batch_chem], verbose=0) # 只保留有效样本 valid_indices = np.where(valid_mask)[0] if len(valid_indices) > 0: all_preds.append(batch_pred[valid_indices]) all_labels.append(batch_label[valid_indices]) # 释放内存 del batch_img, batch_chem, batch_label, batch_pred if i % 10 == 0: gc.collect() # 合并所有批次的结果 if not all_preds: raise ValueError("没有有效样本用于评估") y_pred_probs = np.vstack(all_preds) y_true = np.concatenate(all_labels) y_pred = np.argmax(y_pred_probs, axis=1) # 计算并打印结果 print(f"✅ 有效样本数量: {len(y_true)}/{len(labels)}") acc = accuracy_score(y_true, y_pred) print(f"🎯 准确率: {acc:.4f}") print("\n📋 分类报告:") print(classification_report(y_true, y_pred, target_names=['positive', 'negative'])) # 混淆矩阵 - 使用非交互式方式保存 cm = confusion_matrix(y_true, y_pred) plt.figure(figsize=(6, 5)) # 修复中文字体显示问题 plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'Arial Unicode MS'] plt.rcParams['axes.unicode_minus'] = False sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=['positive', 'negative'], yticklabels=['positive', 'negative'], annot_kws={'fontsize': 12, 'fontfamily': 'SimHei'}) plt.title('混淆矩阵 (二分类)', fontfamily='SimHei', fontsize=14) plt.ylabel('真实标签', fontfamily='SimHei', fontsize=12) plt.xlabel('预测标签', fontfamily='SimHei', fontsize=12) plt.tight_layout() # 保存到image文件夹 self._save_plot_with_error_handling(plt, 'confusion_matrix.png', dpi=300) # 诊断预测分布问题 self._diagnose_prediction_distribution(y_true, y_pred_probs) # 分析模型性能问题 self._analyze_performance(y_true, y_pred, y_pred_probs) # 生成详细的可视化报告 self._generate_visualization_report(y_true, y_pred, y_pred_probs, self.history) return acc, y_pred, y_pred_probs def _diagnose_prediction_distribution(self, y_true, y_pred_probs): """诊断预测分布问题,检测AUC异常的原因""" print("\n🔍 预测分布诊断:") # 统计真实标签分布 unique, counts = np.unique(y_true, return_counts=True) print(f"真实标签分布: {dict(zip(unique, counts))}") # 统计预测概率分布 positive_probs = y_pred_probs[:, 0] # positive类概率 negative_probs = y_pred_probs[:, 1] # negative类概率 print(f"positive类概率 - 均值: {np.mean(positive_probs):.4f}, 标准差: {np.std(positive_probs):.4f}") print(f"negative类概率 - 均值: {np.mean(negative_probs):.4f}, 标准差: {np.std(negative_probs):.4f}") # 检查每个类别的预测概率 for class_idx in [0, 1]: class_mask = (y_true == class_idx) class_name = 'positive' if class_idx == 0 else 'negative' class_positive_probs = positive_probs[class_mask] class_negative_probs = negative_probs[class_mask] print(f"{class_name}类样本的预测概率:") print(f" 预测为positive的概率 - 均值: {np.mean(class_positive_probs):.4f}") print(f" 预测为negative的概率 - 均值: {np.mean(class_negative_probs):.4f}") # 检查是否有明显的预测方向错误 if np.mean(class_positive_probs) < 0.3: print(f" ⚠️ 警告: {class_name}类样本被主要预测为negative类!") elif np.mean(class_positive_probs) > 0.7: print(f" ✅ 正常: {class_name}类样本被主要预测为positive类") else: print(f" 🤔 不确定: {class_name}类样本预测概率分布较为均匀") # 计算简单的AUC检查 from sklearn.metrics import roc_auc_score auc_positive = roc_auc_score(y_true, positive_probs) auc_negative = roc_auc_score(y_true, negative_probs) print(f"\n📊 AUC检查:") print(f"使用positive类概率: {auc_positive:.4f}") print(f"使用negative类概率: {auc_negative:.4f}") if auc_positive < 0.5: print("⚠️ 检测到预测方向相反问题,模型可能将positive和negative搞反了") print("💡 建议: 检查标签映射或类别权重设置") elif auc_positive > 0.8: print("✅ AUC表现良好,模型区分能力较强") else: print("🤔 AUC表现一般,模型需要进一步优化") def _analyze_performance(self, y_true, y_pred, y_pred_probs): """分析模型性能问题并提供改进建议""" # 计算每个类别的准确率(二分类) class_acc = [] for cls in range(2): idx = (y_true == cls) if np.sum(idx) > 0: # 确保有样本 cls_acc = accuracy_score(y_true[idx], y_pred[idx]) class_acc.append(cls_acc) else: class_acc.append(0.0) print("\n🔍 性能分析:") print(f"positive类准确率: {class_acc[0]:.4f}") print(f"negative类准确率: {class_acc[1]:.4f}") # 识别最难分类的样本 max_prob_diff = np.max(y_pred_probs, axis=1) - np.take_along_axis(y_pred_probs, y_true.reshape(-1, 1), axis=1).flatten() hard_indices = np.argsort(max_prob_diff)[:20] # 找出20个最难样本 print("\n💡 模型改进建议:") if class_acc[1] < 0.5: # negative类准确率低 print("1. negative类识别困难,建议增加该类样本或使用数据增强") if abs(class_acc[0] - class_acc[1]) > 0.2: # 类别间不平衡 print("2. 检测到类别不平衡问题,建议使用class_weight参数") if np.mean(max_prob_diff) > 0.3: # 模型不确定性高 print("3. 模型对许多样本预测不确定性高,建议增加训练轮数或模型复杂度") # 保存困难样本分析 plt.figure(figsize=(10, 8)) for i, idx in enumerate(hard_indices): plt.subplot(4, 5, i + 1) cls = y_true[idx] pred = y_pred[idx] prob = y_pred_probs[idx][pred] plt.title(f"T:{cls} P:{pred}\nProb:{prob:.2f}") # 这里可以添加可视化样本的代码 plt.tight_layout() self._save_plot_with_error_handling(plt, 'hard_samples.png', dpi=150) def _save_plot_with_error_handling(self, plt, filename, dpi=300): """安全保存图片,处理权限错误""" # 确保image文件夹存在 import os image_dir = 'image' if not os.path.exists(image_dir): os.makedirs(image_dir, exist_ok=True) save_path = os.path.join(image_dir, filename) try: plt.savefig(save_path, dpi=dpi, bbox_inches='tight') print(f"✅ 图片已保存为 '{save_path}'") return True except PermissionError: # 如果当前目录没有权限,尝试保存到用户文档目录 try: user_docs = os.path.expanduser('~') user_image_dir = os.path.join(user_docs, 'image') if not os.path.exists(user_image_dir): os.makedirs(user_image_dir, exist_ok=True) fallback_path = os.path.join(user_image_dir, filename) plt.savefig(fallback_path, dpi=dpi, bbox_inches='tight') print(f"✅ 图片已保存为 '{fallback_path}'") return True except Exception as e: print(f"⚠️ 无法保存图片 {filename}: {e}") return False finally: plt.close() def _generate_visualization_report(self, y_true, y_pred, y_pred_probs, history): """分别保存主要研究可视化图""" import matplotlib.pyplot as plt from sklearn.metrics import precision_recall_curve, roc_curve, auc, precision_score, recall_score, f1_score, confusion_matrix import seaborn as sns print("📊 分别保存主要研究可视化图...") # 设置中文字体 plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei'] plt.rcParams['axes.unicode_minus'] = False # 1. 训练曲线(Loss & Accuracy) plt.figure(figsize=(12, 5)) plt.subplot(1, 2, 1) plt.plot(history.history['accuracy'], label='训练准确率', linewidth=2, color='blue') plt.plot(history.history['val_accuracy'], label='验证准确率', linewidth=2, color='red') plt.title('训练准确率曲线', fontsize=14, fontweight='bold') plt.xlabel('Epoch') plt.ylabel('Accuracy') plt.legend() plt.grid(True, alpha=0.3) plt.subplot(1, 2, 2) plt.plot(history.history['loss'], label='训练损失', linewidth=2, color='blue') plt.plot(history.history['val_loss'], label='验证损失', linewidth=2, color='red') plt.title('训练损失曲线', fontsize=14, fontweight='bold') plt.xlabel('Epoch') plt.ylabel('Loss') plt.legend() plt.grid(True, alpha=0.3) plt.tight_layout() self._save_plot_with_error_handling(plt, 'training_curves.png', dpi=300) # 2. 混淆矩阵 plt.figure(figsize=(8, 6)) cm = confusion_matrix(y_true, y_pred) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=['Positive', 'Negative'], yticklabels=['Positive', 'Negative']) plt.title('混淆矩阵', fontsize=14, fontweight='bold') plt.xlabel('预测标签') plt.ylabel('真实标签') self._save_plot_with_error_handling(plt, 'confusion_matrix_detailed.png', dpi=300) # 3. ROC曲线 - 修复AUC计算问题 plt.figure(figsize=(8, 6)) # 检查哪个类别的概率更高,使用正确的概率计算ROC # 如果AUC < 0.5,说明预测方向相反,使用1-p来修正 fpr, tpr, _ = roc_curve(y_true, y_pred_probs[:, 0]) # positive类概率 roc_auc = auc(fpr, tpr) # 如果AUC < 0.5,说明预测方向相反,使用negative类概率 if roc_auc < 0.5: print(f"⚠️ 检测到AUC < 0.5 ({roc_auc:.3f}),使用negative类概率重新计算") fpr, tpr, _ = roc_curve(y_true, y_pred_probs[:, 1]) # negative类概率 roc_auc = auc(fpr, tpr) plt.plot(fpr, tpr, color='darkorange', lw=3, label=f'ROC曲线 (修正后 AUC = {roc_auc:.3f})') else: plt.plot(fpr, tpr, color='darkorange', lw=3, label=f'ROC曲线 (AUC = {roc_auc:.3f})') plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--', alpha=0.5) plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.05]) plt.xlabel('假正率 (False Positive Rate)', fontsize=12) plt.ylabel('真正率 (True Positive Rate)', fontsize=12) plt.title('ROC曲线', fontsize=14, fontweight='bold') plt.legend(loc="lower right") plt.grid(True, alpha=0.3) self._save_plot_with_error_handling(plt, 'roc_curve.png', dpi=300) # 4. Precision-Recall曲线 plt.figure(figsize=(8, 6)) precision, recall, _ = precision_recall_curve(y_true, y_pred_probs[:, 0]) plt.plot(recall, precision, color='blue', lw=3, label='P-R曲线') plt.xlabel('召回率 (Recall)', fontsize=12) plt.ylabel('精确率 (Precision)', fontsize=12) plt.title('Precision-Recall曲线', fontsize=14, fontweight='bold') plt.legend() plt.grid(True, alpha=0.3) self._save_plot_with_error_handling(plt, 'precision_recall_curve.png', dpi=300) # 5. 准确率-召回率-F1柱状图 precision_0 = precision_score(y_true == 0, y_pred == 0) recall_0 = recall_score(y_true == 0, y_pred == 0) f1_0 = f1_score(y_true == 0, y_pred == 0) precision_1 = precision_score(y_true == 1, y_pred == 1) recall_1 = recall_score(y_true == 1, y_pred == 1) f1_1 = f1_score(y_true == 1, y_pred == 1) metrics = ['精确率', '召回率', 'F1分数'] positive_scores = [precision_0, recall_0, f1_0] non_positive_scores = [precision_1, recall_1, f1_1] plt.figure(figsize=(10, 6)) x = np.arange(len(metrics)) width = 0.35 plt.bar(x - width/2, positive_scores, width, label='Positive类', alpha=0.8, color='skyblue') plt.bar(x + width/2, non_positive_scores, width, label='Negative类', alpha=0.8, color='lightcoral') # 添加数值标签 for i, v in enumerate(positive_scores): plt.text(i - width/2, v + 0.01, f'{v:.3f}', ha='center', va='bottom', fontweight='bold') for i, v in enumerate(non_positive_scores): plt.text(i + width/2, v + 0.01, f'{v:.3f}', ha='center', va='bottom', fontweight='bold') plt.xlabel('评估指标', fontsize=12) plt.ylabel('分数', fontsize=12) plt.title('准确率-召回率-F1分数对比', fontsize=14, fontweight='bold') plt.xticks(x, metrics) plt.legend() plt.grid(True, alpha=0.3, axis='y') plt.ylim(0, 1.0) self._save_plot_with_error_handling(plt, 'precision_recall_f1_barchart.png', dpi=300) # 6. 学习率调度曲线 if 'lr' in history.history: plt.figure(figsize=(8, 6)) plt.plot(history.history['lr'], linewidth=2, color='purple') plt.title('学习率调度曲线', fontsize=14, fontweight='bold') plt.xlabel('Epoch') plt.ylabel('Learning Rate') plt.grid(True, alpha=0.3) self._save_plot_with_error_handling(plt, 'learning_rate_schedule.png', dpi=300) # 7. 预测置信度分布直方图 plt.figure(figsize=(10, 6)) plt.hist(y_pred_probs[y_true == 0, 0], alpha=0.7, label='Positive类', bins=20, color='skyblue') plt.hist(y_pred_probs[y_true == 1, 0], alpha=0.7, label='Negative类', bins=20, color='lightcoral') plt.xlabel('预测概率', fontsize=12) plt.ylabel('频数', fontsize=12) plt.title('预测置信度分布直方图', fontsize=14, fontweight='bold') plt.legend() plt.grid(True, alpha=0.3) self._save_plot_with_error_handling(plt, 'prediction_confidence_distribution.png', dpi=300) # 8. 模态贡献柱状图(简化版本) plt.figure(figsize=(8, 6)) modalities = ['图像特征', '化学特征', '融合特征'] # 这里使用简化的贡献度估计 contributions = [0.35, 0.40, 0.25] # 可以根据实际分析调整 colors = ['lightblue', 'lightgreen', 'lightcoral'] plt.bar(modalities, contributions, color=colors, alpha=0.8) plt.xlabel('特征模态', fontsize=12) plt.ylabel('相对贡献度', fontsize=12) plt.title('多模态特征贡献度分析', fontsize=14, fontweight='bold') plt.grid(True, alpha=0.3, axis='y') # 添加数值标签 for i, v in enumerate(contributions): plt.text(i, v + 0.01, f'{v:.2f}', ha='center', va='bottom', fontweight='bold') self._save_plot_with_error_handling(plt, 'modality_contribution_analysis.png', dpi=300) print("🎯 所有主要研究可视化图已分别保存完成!") def main(): # 强制清除会话 tf.keras.backend.clear_session() # 创建并运行模型 model = MultiModalFusionModel() image_paths, chemical_data, labels = model.load_data() model.build_model() # 训练模型 model.train(image_paths, chemical_data, labels, batch_size=16, epochs=100) # 增加batch_size,减少epochs # 评估模型 acc, y_pred, probs = model.evaluate(image_paths, chemical_data, labels) print(f"\n🎉 最终准确率: {acc:.4f}") # 安全保存模型,避免权限问题 try: # 确保image文件夹存在 import os image_dir = 'image' if not os.path.exists(image_dir): os.makedirs(image_dir, exist_ok=True) model_path = os.path.join(image_dir, 'final_multimodal_model.keras') model.model.save(model_path) print(f"💾 模型已保存为 '{model_path}'") except PermissionError: print("⚠️ 文件保存权限问题,跳过模型保存") print("💡 建议:检查文件权限或使用不同文件名") if __name__ == "__main__": main() 帮我检查以上代码,查看存在的问题,并尝试优化模型架构,增强模型的鲁棒性,和准确率
11-22
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值