基于卷积神经网络的图像二分类检测模型训练与推理实现教程 | 幽络源

news2024/12/16 1:37:04

前言

对于本教程,说白了,就是期望能通过一个程序判断一张图片是否为某个物体,或者说判断一张图片是否为某个缺陷。因为本教程是针对二分类问题,因此主要处理 是 与 不是 的问题,比如我的模型是判断一张图片是否为苹果,那么拿一张图片给模型去推理,他会得出这张图是苹果的概率,如果概率大于0.5(这个概率在0~1之间),那么就判断为是苹果。

教程内容

使用了Python的 TensorFlow 和 Keras 库 构建卷积神经网络来完成二分类模型训练,以及使用模型完成对一张图片的推理。原文链接:基于卷积神经网络的图像二分类检测模型训练与推理实现教程 | 幽络源

大致步骤

1.确定环境与库

2.准备数据集并且划分

3.数据集的命名问题注意事项

4.编写训练代码完成模型训练

5.编写推理代码

6.测试二分类检测结果

7.根据结果优化数据集

步骤1.确定环境与库

Python环境是必备的,我这里所使用的Python版本为3.12.3

其次还需要以下库,依次执行如下命令即可

pip install tensorflow
pip install pillow
pip install scipy

如图

1

2

步骤2.准备数据集并且划分

我这里以判断图片是否为冲沟缺陷 来准备数据集,首先创建数据集的目录结构,结构如下

data/
    train/
        true_sample/ 
        false_sample/  
    val/
        true_sample/
        false_sample/

QQ_1734065732662

目录解释:

data:作为数据集的根目录

train和val分别为训练集、验证集目录

true_sample:正类样本,也就是我这里需要把含有冲沟缺陷的图放到这个目录

false_sample:负类样本,也就是这里需要将不含有冲沟缺陷的图片放进这个目录

如图,我向train和val的true_sample目录加入了一些含有冲沟缺陷的图片

3

对于负类样本,也不是无脑的只要不是冲沟就往里面放,而是放置你认为训练出的模型可能会将什么识别为正类样本。比如滑坡和冲沟其实是有联系的,但不完全等同于,所以我需要将滑坡相关的,但是没有冲沟情况的图片放入false_sample中,期望模型不要误判。再比如一个苹果,你可能需要把红色气球作为父类样本,防止模型将红气球判断为是苹果,如图是我的负类样本

4

步骤3.数据集的命名问题注意事项

关于数据集的命名,这里其实有一个坑,但是先说避免坑的做法:就像步骤2一样,你的正类样本所放置的目录命名为true_sample、负类样本所放置的目录命名为false_sample就行了。(如果看不懂下面的解释,按照这里做法做就是了)

然后我来解释下是什么坑,对于这个二分类模型训练,训练出来的模型,无非是识别 是 与 不是 的问题,但是模型怎么区分我的哪个目录放置的为是,哪个目录放置的为不是呢,步骤4会给出训练代码,训练代码中的加载数据集时有一行如下代码

class_mode='binary'  # 二分类(冲沟缺陷 vs. 非冲沟缺陷)

这表示我们要做二分类模型训练,加上这行代码,在加载数据集时,Keras 会自动将这些文件夹的名称作为标签,分别命名为1 和 0,如果被命名为标签1 的目录,则在推理时,概率越接近于1,则越表示是标为1的目录的样本,反之概率越接近于0,则越表示是标为0的目录的样本。而keras自动命名标签1和0时是根据目录名首字母的顺序来的字,字母靠前的标为0,后者为1,true_sample的首字母为t,false_sample的首字母为f,因此false_sample标为0,true_sample标为1,这是符合我们的正常预期的。

反面例子:

如果我把正类样本放置于名为defect的目录,负类样本放置于no_defect目录会怎样呢,按照如上解释,defect目录会被标为0,no_defect目录会被标为1,这就和我们预期相反了,什么意思呢。我把正类样本放置defect目录中,其推理结果将会是越接近0,则越表示为正类了,因此这里特别需要注意(如果你要自定义目录名的话)。

步骤4.编写训练代码完成模型训练

先直接上训练代码

from tensorflow.keras import layers, models
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import tensorflow as tf


train_dir='data/train'
val_dir='data/val'

# 设置图像的尺寸和批量大小,不用改,保持150是最平衡的
IMG_HEIGHT = 150
IMG_WIDTH = 150
BATCH_SIZE = 12

# 数据预处理与增强
train_datagen = ImageDataGenerator(
    rescale=1./255,  # 将像素值归一化到 [0, 1] 区间
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True
)

validation_datagen = ImageDataGenerator(rescale=1./255)

# 加载训练和验证数据
train_generator = train_datagen.flow_from_directory(
    train_dir,  # 训练数据目录
    target_size=(IMG_HEIGHT, IMG_WIDTH),  # 图像尺寸
    batch_size=BATCH_SIZE,
    class_mode='binary'  # 二分类(冲沟缺陷 vs. 非冲沟缺陷)
)

train_class_labels = train_generator.class_indices
print("训练集自动标签映射关系为:"+str(train_class_labels))

validation_generator = validation_datagen.flow_from_directory(
    val_dir,  # 验证数据目录
    target_size=(IMG_HEIGHT, IMG_WIDTH),
    batch_size=BATCH_SIZE,
    class_mode='binary'
)

val_class_labels = validation_generator.class_indices
print("测试集自动标签映射关系为:"+str(val_class_labels))

# 将数据生成器转换为 tf.data.Dataset 并应用 repeat() 方法
train_dataset = tf.data.Dataset.from_generator(
    lambda: train_generator,
    output_signature=(
        tf.TensorSpec(shape=(None, IMG_HEIGHT, IMG_WIDTH, 3), dtype=tf.float32),
        tf.TensorSpec(shape=(None,), dtype=tf.int32)
    )
)
train_dataset = train_dataset.repeat()  # 确保数据重复

validation_dataset = tf.data.Dataset.from_generator(
    lambda: validation_generator,
    output_signature=(
        tf.TensorSpec(shape=(None, IMG_HEIGHT, IMG_WIDTH, 3), dtype=tf.float32),
        tf.TensorSpec(shape=(None,), dtype=tf.int32)
    )
)
validation_dataset = validation_dataset.repeat()  # 确保数据重复

# 构建模型
model = models.Sequential([
    layers.InputLayer(shape=(IMG_HEIGHT, IMG_WIDTH, 3)),  # 添加 Input 层
    layers.Conv2D(32, (3, 3), activation='relu'),
    layers.MaxPooling2D((2, 2)),
    layers.Conv2D(64, (3, 3), activation='relu'),
    layers.MaxPooling2D((2, 2)),
    layers.Conv2D(128, (3, 3), activation='relu'),
    layers.MaxPooling2D((2, 2)),
    layers.Flatten(),
    layers.Dense(128, activation='relu'),
    layers.Dense(1, activation='sigmoid')  # 输出层,二分类问题
])

# 编译模型
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

# 训练模型
model.fit(
    train_dataset,
    steps_per_epoch=train_generator.samples // BATCH_SIZE,
    epochs=30,
    validation_data=validation_dataset,
    validation_steps=validation_generator.samples // BATCH_SIZE
)

# 保存模型
model.save('defect_detector_model.keras')  # 使用 .keras 格式保存模型

使用这段代码训练数据集你唯一需要注意的是保持代码文件于数据集文件在同一目录,或者使用绝对路径,如图

QQ_1734070943655

我们启动训练代码,可以看到控制台在按照规定的轮次30在训练中,而且可以看到我在训练代码中加入了输出标签映射关系来确保正类与负类的映射关系正确,如图

QQ_1734071390702

训练后,你会得到一个名为defect_detector_nodel.keras的文件,推理时会使用该模型进行推理

步骤5.编写推理代码

代码如下:

import os
from tensorflow.keras.models import load_model
import numpy as np
from tensorflow.keras.preprocessing import image

# 加载训练好的模型
model = load_model('defect_detector_model.keras')  # 注意加载的是 .keras 格式

# 设置输入图像的目标尺寸(与训练时相同)
IMG_HEIGHT = 150
IMG_WIDTH = 150


# 定义函数来加载并预测图像
def predict_image(img_path):
    # 加载图像并进行预处理
    img = image.load_img(img_path, target_size=(IMG_HEIGHT, IMG_WIDTH))
    img_array = image.img_to_array(img)  # 将图像转换为数组
    img_array = np.expand_dims(img_array, axis=0)  # 扩展维度,成为一个 batch
    img_array = img_array / 255.0  # 归一化处理(与训练时一致)

    # 预测图像类别
    prediction = model.predict(img_array)  # 返回的是一个包含概率的数组
    return prediction[0][0]  # 提取预测的概率值

picPath=r"测试图.jpg"
confidence = predict_image(picPath)
print("有冲沟缺陷的概率为:"+str(confidence))

这段推理代码中,我们加载了刚才训练出的模型,然后使用了一张名为测试图.jpg的图片来进行推理,然后输出他有缺陷的概率

步骤6.测试二分类检测结果

我这里就不用一张图片来测试了,我这里指定一个目录,进行整个目录来测试里面的图片,还是附上我这个推理代码吧

import os
from tensorflow.keras.models import load_model
import numpy as np
from tensorflow.keras.preprocessing import image

# 加载训练好的模型
model = load_model('defect_detector_model.keras')  # 注意加载的是 .keras 格式

# 设置输入图像的目标尺寸(与训练时相同)
IMG_HEIGHT = 150
IMG_WIDTH = 150


# 定义函数来加载并预测图像
def predict_image(img_path):
    # 加载图像并进行预处理
    img = image.load_img(img_path, target_size=(IMG_HEIGHT, IMG_WIDTH))
    img_array = image.img_to_array(img)  # 将图像转换为数组
    img_array = np.expand_dims(img_array, axis=0)  # 扩展维度,成为一个 batch
    img_array = img_array / 255.0  # 归一化处理(与训练时一致)

    # 预测图像类别
    prediction = model.predict(img_array)  # 返回的是一个包含概率的数组
    return prediction[0][0]  # 提取预测的概率值


# 测试目录,包含要进行推理的图像
testDir = r"D:\virtualTemp\pythonProject\CNN分类检测\data\train\true_sample"
pics = os.listdir(testDir)
# 遍历目录中的所有图片并进行预测
for pic in pics:
    picPath = os.path.join(testDir, pic)  # 获取图片的完整路径
    # 获取预测结果的置信度
    confidence = predict_image(picPath)
    # 输出图像的置信度和类别
    print(f"{pic} 置信度: {confidence:.4f}, 预测结果: {'有缺陷' if confidence >= 0.5 else '无缺陷'}")

我先使用正类样本来测试,先看看拿训练的数据如何,然后再用另外的图片来测试

结果如下图,正类样本中只有一张图判定为了无冲沟,但是我正类样本中其实都应当是冲沟,而我有101张图,因此这里正确率为99.009%

QQ_1734071615033

拿训练的数据来说话可能没有说服力,现在我使用爬图器来批量的爬取一些图片,需要的可以这里拿=> 幽络源爬图器

如图我爬取了3轮桥梁破损图,2轮冲沟地貌图,对于冲沟图,最好是手动删一些莫名奇妙的图,便于验证

QQ_1734072068792

QQ_1734072170259

ok,然后先测试桥梁破损,如果足够符合预期,足够表示模型很好,那么推理出的有缺陷数量应该没有或者很少才对,结果如下

QQ_1734072462049

看起来结果并不好,90张图中,居然有44张判定为了有冲沟缺陷,正确率只有46/90=51.11%,再测试下正类检测呢,如图48张图中只有11张判定为了无,还是不错的。

步骤7.根据结果优化数据集

在步骤6的测试中可知,所训练的模型对正类比较适应,对负类的学习还有所欠缺,处理方法有如下

1.调整判定指标confidence,一般为0.5,可以调大以提高正确率,但是不推荐这么做

2.加大训练轮次

3.训练时的父类样本图片多加一些

ok,方法1我不是很推荐,现在首先加大训练次数到100,然后多爬取一些非冲沟图加入到负类样本之中,当然,桥梁破损的图也放进去一些,然后重新训练获取模型。

训练完后还是按照步骤6中来测试桥梁破损,如图,这一次,90张图中判定为有缺陷的只有7个了,非常不错,正确率提高到了82/90=91.11%

QQ_1734073419635

结语

以上是幽络源的基于卷积神经网络的图像二分类检测模型训练与推理实现教程,对Python、Java感兴趣的小伙伴可加群交流

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2260226.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot集成JWT和Redis实现鉴权登录功能

目前市面上有许多鉴权框架,鉴权原理大同小异,本文简单介绍下利用JWT和Redis实现鉴权功能,算是抛砖引玉吧。 主要原理就是“令牌主动失效机制”,主要包括以下4个步骤: (1)利用拦截器LoginInterceptor实现所有接口登录拦…

初识Robot Framework测试框架

Robot Framework 是一款用 Python 编写的通用型测试框架,具备良好的可扩展性,支持关键字驱动,可以同时测试多种类型的客户端(Web、PC、移动端)或者接口,也可以进行分布式测试执行。常用于验收测试和验收测试…

Windows安装elasticsearch、Kibana以及IK分词器

一、下载 1.下载elasticsearch 访问官网Download Elasticsearch | Elastic,下载elasticsearch 2.下载 Kibana 访问Download Kibana Free | Get Started Now | Elastic ,下载 Kibana 3. IK分词器下载 访问Gitee 极速下载/elasticsearch-analysis-ik选…

socket编程UDP-实现停等机制(接收确认、超时重传)

在下面博客中,我介绍了利用UDP模拟TCP连接、按数据包发送文件的过程,并附上完整源码。 socket编程UDP-文件传输&模拟TCP建立连接脱离连接(进阶篇)_udp socket发送-CSDN博客 下面博客实现的是滑动窗口机制: sock…

PHP项目从 php5.3 版本升级到 php8.3 版本时的一些问题和解决方法记录

一个原来的项目,因为业务需要,进行了PHP版本升级,从php5.3直接升级到php8.3。变化挺大的,原程序中有很多不再兼容,在此处进行一下记录。 一、Deprecated: 显式转换问题 报错内容:Deprecated: Implicit con…

在Liunx中安装JDK、Tomcat、mysql、lrzsz、Nginx

一.软件安装方式 在Linux系统中,安装软件的方式主要有四种,这四种安装方式的特点如下: 二.安装JDK 上述我们介绍了Linux系统软件安装的四种形式,接下来我们就通过第一种(二进制发 布包)形式来安装JDK。 在/下创建soft目录&…

LeetCode-hot100-73

https://leetcode.cn/problems/largest-rectangle-in-histogram/description/?envTypestudy-plan-v2&envIdtop-100-liked 84. 柱状图中最大的矩形 已解答 困难 相关标签 相关企业 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#x…

leetcode-73.矩阵置零-day5

class Solution {public void setZeroes(int[][] mat) {int m mat.length, n mat[0].length;// 1. 扫描「首行」和「首列」记录「首行」和「首列」是否该被置零boolean r0 false, c0 false;for (int i 0; i < m; i) {if (mat[i][0] 0) {r0 true;break;}}for (int j …

【Spark】Spark Join类型及Join实现方式

如果觉得这篇文章对您有帮助&#xff0c;别忘了点赞、分享或关注哦&#xff01;您的一点小小支持&#xff0c;不仅能帮助更多人找到有价值的内容&#xff0c;还能鼓励我持续分享更多精彩的技术文章。感谢您的支持&#xff0c;让我们一起在技术的世界中不断进步&#xff01; Sp…

开源 AI 智能名片 S2B2C 商城小程序对私域流量运营的全方位助力

在当今竞争激烈的商业环境中&#xff0c;私域流量运营已成为企业实现可持续发展和提升竞争力的关键策略之一。开源 AI 智能名片 S2B2C 商城小程序凭借其独特的功能与特性&#xff0c;从多个维度为私域流量运营提供了强有力的支持与推动&#xff0c;以下将详细阐述其在各个方面的…

【razor】echo搭配relay功能分析

echo 要搭配relay 实现作者说relay在linux上跑,可以模拟丢包、延迟目前没看到如何模拟。relay监听9200,有俩作用 echopeer1 发relay,replay 把peer1的包给peer2 ,实现p2p能力。 接收端:采集后发送发给relay的 接收端的地址就是自己,的地址就是本地的9200,因此是让relay接…

Phoenix5.1.3安装

环境说明 准备三台服务器&#xff0c;分别为&#xff1a;bigdata141&#xff08;作为HBase主节点&#xff09;、bigdata142、bigdata143&#xff0c;已经搭建好HBase集群&#xff0c;我这边HBase版本为2.2.7另准备一台服务器&#xff0c;bigdata144&#xff0c;作为Phoenix客户…

ASP.NET Core API + MySql

环境 数据库&#xff1a; mysql8.0 后端&#xff1a; vs2022 ASP.NET Core API .net 8 前端&#xff1a; Hbuilderx bootstrap 5.3.0 jquery v3.7.1 bootstrap-table 1.23.5 创建项目 添加资源包 AutoMapper Microsoft.EntityFrameworkCore.Tools 8.0.0 Pomelo.EntityFramew…

小程序维护外包流程和费用

由于某些原因很多老板想要跟换掉小程序原来合作的开发公司&#xff0c;重新把小程序系统维护外包新的公司。小程序系统外包维护是一个涉及多个方面的过程&#xff0c;需要从需求明确、选择团队到持续优化等多个环节进行细致管理。以下就是小程序系统外包维护主要包括几个关键步…

代码随想录算法训练营第三十二天|动态规划理论基础|LC509.肥波那些数|LC70.爬楼梯|LC746.使用最小花费爬楼梯

动态规划理论基础 解释&#xff1a;动态规划&#xff0c;英文&#xff1a;Dynamic Programming&#xff0c;简称DP&#xff1b;如果某一问题有很多重叠子问题&#xff0c;使用动态规划是最有效的。 动态规划五部曲&#xff1a; 1、确定dp数组&#xff08;dp table&#xff09;…

亮相AICon,火山引擎边缘云揭秘边缘AI Agent探索与实践

12月13-14日&#xff0c;AICon 全球人工智能开发与应用大会在北京成功举办。火山引擎边缘智能技术负责人谢皓受邀出席大会&#xff0c;以《AI Agent 在边缘云的探索与实践》为主题&#xff0c;与全球 AI 领域的资深专家&#xff0c;共同深入探讨大模型落地、具身智能、多模态大…

找出1000以内的所有回文数

找出1000以内的所有回文数 方法概述检查回文数的方法伪代码C代码实现代码解析运行结果在计算机科学中,回文数是一种具有对称性质的数,即从左向右读和从右向左读都是相同的。例如,121、1331、12321都是回文数。本文将利用数据结构、C语言和算法的知识来编写一个程序,找出100…

数据保护策略:如何保障重要信息的安全

一、什么是数据安全&#xff1f; 数据安全是保护数字信息免遭盗窃、未经授权的访问和恶意修改的过程。这是一个持续的过程&#xff0c;负责监督信息的收集、存储和传输。 机密性&#xff1a;保护数据免遭未授权方访问。 完整性&#xff1a;保护数据免遭未经授权的修改、损坏…

SpringBoot【八】mybatis-plus条件构造器使用手册!

一、前言&#x1f525; 环境说明&#xff1a;Windows10 Idea2021.3.2 Jdk1.8 SpringBoot 2.3.1.RELEASE 经过上一期的mybatis-plus 入门教学&#xff0c;想必大家对它不是非常陌生了吧&#xff0c;这期呢&#xff0c;我主要是围绕以下几点展开&#xff0c;重点给大家介绍 里…

基于springboot+vue的高校校园交友交流平台设计和实现

文章目录 系统功能部分实现截图 前台模块实现管理员模块实现 项目相关文件架构设计 MVC的设计模式基于B/S的架构技术栈 具体功能模块设计系统需求分析 可行性分析 系统测试为什么我&#xff1f; 关于我项目开发案例我自己的网站 源码获取&#xff1a; 系统功能 校园交友平台…