Core ML图像分类模型对不同图像输出相同预测结果排查
问题:Core ML猫狗图像分类模型预测结果始终固定
我正在探索Apple Core ML框架,用Create ML制作了一个猫狗图像分类模型,数据集用的是Kaggle的猫狗图像分类数据集。生成.mlmodel文件后,在iOS App里预测时,不管输入猫还是狗的图像,输出结果全一样:{"cats": 0.6281524444894766, "dogs": 0.3718475555105234}。我试过多种图像缩放、像素转换(pixelBuffer)的方法,换了测试集里不同的图像,结果还是没变。请问这是怎么回事?
App代码
override func viewDidLoad() { if let img = UIImage(named: "dog_29.jpg") { predictImage(image: img) } else { print("image not captured") } } private func predictImage(image: UIImage) { let inputImageSize: CGFloat = 299.0 let minLen = min(image.size.width, image.size.height) let resizedImage = image.resize(to: CGSize(width: inputImageSize * image.size.width / minLen, height: inputImageSize * image.size.height / minLen)) guard let pixelBuffer = resizedImage.pixelBuffer() else { fatalError() } do { let config = MLModelConfiguration() let model = try CatsAndDogs(configuration:config) let result = try model.prediction(image: pixelBuffer) print(result.target) print(result.targetProbability) print(result) } catch { print("image classification error") } }
图像缩放与像素转换代码
func resize(to newSize: CGSize) -> UIImage { UIGraphicsBeginImageContextWithOptions(CGSize(width: newSize.width, height: newSize.height), true, 1.0) self.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)) let resizedImage = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() return resizedImage } func pixelBuffer() -> CVPixelBuffer? { let width = self.size.width let height = self.size.height let attrs = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue, kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue] as CFDictionary var pixelBuffer: CVPixelBuffer? let status = CVPixelBufferCreate(kCFAllocatorDefault, Int(width), Int(height), kCVPixelFormatType_32ARGB, attrs, &pixelBuffer) guard let resultPixelBuffer = pixelBuffer, status == kCVReturnSuccess else { return nil } CVPixelBufferLockBaseAddress(resultPixelBuffer, CVPixelBufferLockFlags(rawValue: 0)) let pixelData = CVPixelBufferGetBaseAddress(resultPixelBuffer) let rgbColorSpace = CGColorSpaceCreateDeviceRGB() guard let context = CGContext(data: pixelData, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(resultPixelBuffer), space: rgbColorSpace, bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue) else { return nil } context.translateBy(x: 0, y: height) context.scaleBy(x: 1.0, y: -1.0) UIGraphicsPushContext(context) self.draw(in: CGRect(x: 0, y: 0, width: width, height: height)) UIGraphicsPopContext() CVPixelBufferUnlockBaseAddress(resultPixelBuffer, CVPixelBufferLockFlags(rawValue: 0)) return resultPixelBuffer }
可能的原因及解决方法
1. 模型训练环节存在问题
- 检查数据集划分:确认训练集、验证集、测试集是否正确分离,标签是否对应正确。如果训练时猫的样本数量远多于狗,模型会倾向于输出“cats”类别,导致结果固定。
- 查看Create ML训练报告:如果验证集准确率极低,说明模型根本没学会分类逻辑,只是在输出占比最高的类别。
2. 图像预处理与模型输入要求不匹配
这是最常见的问题,Core ML模型对输入的图像尺寸、像素格式、颜色空间、归一化规则要求严格,必须和训练时一致:
- 输入尺寸不符:你当前只做了短边缩放,没有裁剪成模型要求的正方形(比如训练时设置的299×299)。非正方形输入会导致模型识别特征混乱。
解决:缩放后裁剪中心区域的正方形,代码示例:// 缩放后裁剪中心正方形 let cropRect = CGRect(x: (resizedImage.size.width - inputImageSize)/2, y: (resizedImage.size.height - inputImageSize)/2, width: inputImageSize, height: inputImageSize) guard let croppedCGImage = resizedImage.cgImage?.cropping(to: cropRect) else { fatalError() } let finalImage = UIImage(cgImage: croppedCGImage) - 像素格式/颜色空间不匹配:你的pixelBuffer用了
kCVPixelFormatType_32ARGB,但训练时Create ML默认的输入可能是RGB格式(无Alpha通道)。同时CGContext的bitmapInfo设置为noneSkipFirst,可能导致颜色通道顺序错乱。
解决:右键.mlmodel文件→Show Quick Look→查看Input Description,确认模型要求的像素格式和颜色空间,调整pixelBuffer创建参数。比如换成kCVPixelFormatType_32RGB并对应修改bitmapInfo。 - 缺失图像归一化:多数图像模型要求像素值归一化到特定范围(比如0-1或-1到1),Create ML训练时会自动处理,但App端需要手动实现。当前代码没有归一化步骤,输入像素值不在模型预期范围内。
解决:在生成pixelBuffer后,对像素数据进行归一化处理,或者在绘制图像时调整颜色空间适配模型要求。
3. 模型初始化方式问题
你每次预测都重新初始化CatsAndDogs模型,虽然不会直接导致结果固定,但可能存在配置或缓存异常。建议将模型初始化移到viewDidLoad中,只初始化一次:
var model: CatsAndDogs? override func viewDidLoad() { super.viewDidLoad() do { model = try CatsAndDogs(configuration: MLModelConfiguration()) } catch { print("model init error") } // 加载图像并预测的代码 }
4. 测试图像加载验证
确认测试图像是否被正确加载:UIImage(named:)如果找不到图像,可能会复用之前的缓存(或返回nil)。可以在predictImage中打印图像的尺寸、哈希值,验证每次输入的是不同图像。
内容的提问来源于stack exchange,提问作者Gypsa
相关产品推荐
相关产品推荐

