如何用Keras实现结合图像与预提取特征的CNN分类模型?
Got it! 要实现这种同时用图像和预提取结构化特征的分类模型,Keras的函数式API是最佳选择——它天生支持多输入多输出的模型架构。下面我给你一步步拆解实现过程,代码都给你写好,细节也给你讲清楚:
1. 数据准备:先把图像和特征数据处理好
首先得把两类数据都整理成模型能接受的格式:
- 图像数据:加载后归一化到
0-1区间,形状为(样本数, 64, 64, 3) - 结构化特征:用
pandas读取CSV后,用标准化工具(比如StandardScaler)统一特征尺度,最终形状为(样本数, 14)
示例代码片段:
import numpy as np import pandas as pd from PIL import Image from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split # 加载图像数据(假设图像路径存在CSV的'image_path'列里) df = pd.read_csv('your_features.csv') images = [] for img_path in df['image_path']: img = Image.open(img_path).resize((64,64)) images.append(np.array(img)/255.0) # 归一化到0-1 images = np.array(images) # 加载并标准化结构化特征 features = df.drop(['image_path', 'label'], axis=1).values # 去掉路径和标签列 scaler = StandardScaler() features = scaler.fit_transform(features) # 加载标签并处理(假设是多分类,转成独热编码) labels = pd.get_dummies(df['label']).values # 划分训练测试集 images_train, images_test, features_train, features_test, labels_train, labels_test = train_test_split( images, features, labels, test_size=0.2, random_state=42 )
2. 构建双分支模型
我们需要分别搭建图像特征提取分支和结构化特征分支,最后把两个分支的输出融合:
图像分支(CNN特征提取)
针对64x64的RGB图像,用卷积+池化层提取视觉特征:
from keras.layers import Input, Conv2D, MaxPooling2D, GlobalAveragePooling2D, Dense, Dropout, concatenate from keras.models import Model from keras.optimizers import Adam # 图像输入层 image_input = Input(shape=(64, 64, 3), name='image_input') x = Conv2D(32, (3,3), activation='relu', padding='same')(image_input) x = MaxPooling2D((2,2))(x) x = Conv2D(64, (3,3), activation='relu', padding='same')(x) x = MaxPooling2D((2,2))(x) x = Conv2D(128, (3,3), activation='relu', padding='same')(x) x = GlobalAveragePooling2D()(x) # 用全局平均池化替代Flatten,减少参数避免过拟合 x = Dropout(0.3)(x) # 加Dropout防止过拟合 image_branch = Model(inputs=image_input, outputs=x)
结构化特征分支(全连接层处理)
针对14维的预提取特征,用全连接层做特征变换:
# 特征输入层 feature_input = Input(shape=(14,), name='feature_input') y = Dense(64, activation='relu')(feature_input) y = Dropout(0.2)(y) y = Dense(32, activation='relu')(y) feature_branch = Model(inputs=feature_input, outputs=y)
融合分支+最终分类层
把两个分支的输出拼接,再通过全连接层输出分类结果:
# 融合两个分支的特征 combined_features = concatenate([image_branch.output, feature_branch.output]) z = Dense(64, activation='relu')(combined_features) output = Dense(labels.shape[1], activation='softmax')(z) # labels.shape[1]是分类类别数 # 定义完整模型 model = Model(inputs=[image_branch.input, feature_branch.input], outputs=output)
3. 编译与训练模型
训练时需要同时传入图像和特征两类输入:
# 编译模型(多分类用categorical_crossentropy,二分类换binary_crossentropy+激活sigmoid) model.compile(optimizer=Adam(learning_rate=0.001), loss='categorical_crossentropy', metrics=['accuracy']) # 开始训练 history = model.fit( [images_train, features_train], labels_train, batch_size=32, epochs=20, validation_data=([images_test, features_test], labels_test) )
4. 预测新样本
预测时同样需要传入图像和对应特征:
# 示例:预测单个样本 sample_image = np.expand_dims(images_test[0], axis=0) # 加batch维度 sample_feature = np.expand_dims(features_test[0], axis=0) pred_probs = model.predict([sample_image, sample_feature]) pred_class = np.argmax(pred_probs, axis=1)[0] # 取概率最大的类别
关键注意事项
- 特征标准化:结构化特征一定要做标准化,不然尺度差异会让模型忽略小数值特征
- 过拟合防控:可以在分支或融合层后加
Dropout,或者用L2正则化 - 大数据场景:如果25000张图内存装不下,自定义一个数据生成器,每次yield
([batch_images, batch_features], batch_labels) - 模型调优:可以尝试调整卷积层数量、全连接层神经元数,或者用预训练模型(比如ResNet50)作为图像分支的基础
内容的提问来源于stack exchange,提问作者Sreeram TP
相关产品推荐
相关产品推荐

