如何在R中读取图像并用于VGG16预训练模型训练?
问题解决:修复图像形状与标签格式以适配VGG16训练
核心问题分析
- 图像维度错误:手动读取图像时将每个图像转为一维向量,拼接后得到单一维度数组,无法识别为「样本数-宽-高-通道」的标准输入格式。
- 标签格式不兼容:使用字符串标签直接训练,而
categorical_crossentropy损失函数要求输入独热编码的数值标签。 - 通道数不匹配:VGG16预训练模型要求输入3通道图像,但当前数据集为单通道灰度图。
分步解决方案
1. 修复图像读取函数
调整read_img,保留图像的三维结构(宽、高、通道),避免转为一维向量:
library(tensorflow) library(keras) library(magick) read_img <- function(file) { img <- image_read(file) # 强制调整为48x48固定尺寸 img_resized <- image_resize(img, "48x48!") # 将图像转为数组,确保形状为(48,48,1)(灰度图单通道) img_array <- as.array(image_data(img_resized)) dim(img_array) <- c(48, 48, 1) return(img_array) }
2. 修正数据集加载函数
将所有图像数组组合成「(样本数, 48, 48, 1)」的四维数组,同时将字符串标签转为整数编码:
library(abind) read_emotions_dataset <- function(directory) { emotions <- c("anger", "contempt", "disgust", "fear", "happy", "sadness", "surprise") # 为每个情绪分配0-6的整数标签(适配keras索引规则) emotion_labels <- seq_along(emotions) - 1 image_list <- list() labels <- c() for (i in seq_along(emotions)) { emotion <- emotions[i] emotion_dir <- file.path(directory, emotion) emotion_files <- list.files(path = emotion_dir, full.names = TRUE) # 读取当前情绪下的所有图像 current_images <- lapply(emotion_files, read_img) image_list <- c(image_list, current_images) # 添加对应整数标签 labels <- c(labels, rep(emotion_labels[i], length(emotion_files))) } # 将图像列表转为四维数组:(样本数, 48, 48, 1) images <- abind(image_list, along = 0) list(images = images, labels = labels, image_files = unlist(image_list)) }
3. 加载并预处理数据集
加载数据后,将单通道图像转为3通道(复制灰度通道三次),并对标签进行独热编码:
# 加载训练、测试、验证集 emotions_train <- read_emotions_dataset("../data/emotions/work/train") emotions_test <- read_emotions_dataset("../data/emotions/work/test") emotions_val <- read_emotions_dataset("../data/emotions/work/validation") # 提取图像与标签 train_images <- emotions_train$images train_labels <- emotions_train$labels val_images <- emotions_val$images val_labels <- emotions_val$labels test_images <- emotions_test$images test_labels <- emotions_test$labels # 单通道转3通道:复制灰度通道三次以适配VGG16输入要求 train_images_3ch <- train_images %>% abind(., ., ., along = 3) val_images_3ch <- val_images %>% abind(., ., ., along = 3) test_images_3ch <- test_images %>% abind(., ., ., along = 3) # 归一化像素值到0-1区间 train_images_3ch <- train_images_3ch / 255 val_images_3ch <- val_images_3ch / 255 test_images_3ch <- test_images_3ch / 255 # 将整数标签转为独热编码格式 train_labels_onehot <- to_categorical(train_labels, num_classes = 7) val_labels_onehot <- to_categorical(val_labels, num_classes = 7) test_labels_onehot <- to_categorical(test_labels, num_classes = 7)
4. 修正模型训练代码
确保输入形状与数据匹配,使用正确的标签格式进行训练:
# 加载VGG16预训练模型,指定输入形状为(48,48,3) vgg16 <- application_vgg16(weights = "imagenet", include_top = FALSE, input_shape = c(48, 48, 3)) # 冻结预训练层,避免训练时覆盖权重 for (layer in vgg16$layers) layer$trainable <- FALSE # 添加自定义分类顶层 x <- layer_flatten(vgg16$output) x <- layer_dense(x, 128, activation = "relu") x <- layer_dropout(x, 0.5) predictions <- layer_dense(x, 7, activation = "softmax") # 构建完整模型 model <- keras_model(inputs = vgg16$input, outputs = predictions) # 编译模型(使用独热标签对应categorical_crossentropy) model %>% compile( optimizer = optimizer_adam(), loss = "categorical_crossentropy", metrics = c("accuracy") ) # 启动训练 history <- model %>% fit( x = train_images_3ch, y = train_labels_onehot, epochs = 10, batch_size = 128, validation_data = list(val_images_3ch, val_labels_onehot) ) # 评估模型在测试集上的性能 model %>% evaluate(test_images_3ch, test_labels_onehot)
关键说明
- 手动读取图像时必须保留空间维度,再按样本维度拼接,避免一维化导致的形状错误。
- VGG16预训练模型针对RGB图像训练,因此需要将单通道灰度图复制为3通道以匹配输入格式。
- 使用
categorical_crossentropy时,标签必须转换为独热编码,不能直接使用字符串或原始整数。
内容的提问来源于stack exchange,提问作者Haidepzai
相关产品推荐
相关产品推荐

