手把手教你用 Python 搭建一个图像分类器

news2024/11/14 16:37:23

深度学习是使用人工神经网络进行机器学习的一个子集,目前已经被证明在图像分类方面非常强大。

尽管这些算法的内部工作在数学上是严格的,但 Python 库(比如 keras)使这些问题对我们所有人都可以接近。

在本文中,我将介绍一个简单的图像分类器的设计,它使用人工神经网络将食物图像分为两类:披萨或意大利面。

下载图片

为了训练我们的模型,我们将需要下载大量比萨饼和意大利面的图像,这是一个可能非常繁琐的任务,通过 bing-image-downloader Python 库可以非常容易地完成。

# Install Bing image downloader
pip install bing-image-downloader

现在,我们已经安装了 bing-image-downloader,我们可以很容易地抓取500张比萨饼和意大利面的照片用于训练和测试我们的模型!

# Import bing-image-downloader
from bing_image_downloader import downloader
# Download images
downloader.download("pizza", limit=500, output_dir="photos")
downloader.download("pasta", limit=500, output_dir="photos")

这应该需要几分钟,之后你将有两个子目录下的照片称为比萨饼和面食,每个包含500张照片。这很简单!

技术交流

论文探讨、算法实战交流、求职内推、干货分享、解惑答疑,与2000+来自港大、北大、腾讯、科大讯飞、阿里等开发者互动学习。

目前已开通了技术交流群,群友已超过2000人,添加时最好的备注方式为:来源+兴趣方向,方便找到志同道合的朋友。

方式1、微信搜索公众号:机器学习社区,后台回复:加群;
方式2、可以直接加微信号:mlc2060。加的时候备注一下:研究方向
+学校/公司+CSDN,即可。然后就可以拉你进群了。

数据整理

为了将我们的数据转换成对我们的模型有利的格式,我们需要使用 glob、 pandas、 numpy 和 PIL 库。

# Import packages
import glob
import numpy as np
import pandas as pd
from PIL import Image

我们将使用 glob 来收集我们下载的所有图像的文件路径。为此,我们使用 * 通配符分配变量,以对应目录中的所有文件。

# Filepaths for the pizza and pasta images
filepath_pizza = "./photos/pizza/*"
filepath_pasta = "./photos/pasta/*"

现在,我们可以使用 glob 创建列表,其中每个元素都包含文件夹中单个图像的文件路径:

# Collect all the image filepaths into lists
pizza_files = [file for file in glob.iglob(filepath_pizza)]
pasta_files = [file for file in glob.iglob(filepath_pasta)]

使用所有的文件路径,我们现在可以构建一个 pandas 数据框架,我们可以在其中跟踪文件路径及其相关标签(比萨或意大利面)。在这种情况下,我们将给披萨图像一个0的标签,给意大利面图像一个1的标签。为了方便地为这些标签创建数组,我们可以使用 np.zeros()和 np.ones():

# Construct pandas dataframe with all the image filenames and labels
df_photos = (
    pd.DataFrame({"filepath": pizza_files, "label": np.zeros(len(pizza_files))})
    .append(
        pd.DataFrame({"filepath": pasta_files, "label": np.ones(len(pasta_files))})
    )
)

图片

现在,我们希望将数据集拆分为训练数据和测试数据。幸运的是,scikit-learn 有一个非常简单的功能,可以直接在我们的 pandas 数据库上为我们实现这个功能。我们需要设置 test_size ,它是我们用于测试集数据的一个百分比,并且,通过指定 random_state,我们可以使拆分的结果可重复进行以进行后续测试。最后,我们将重新设置并删除新数据流的索引,因为 train_test_split 会自动为我们分配数据,所以旧的索引值现在没有意义了。

# Import train_test_split
from sklearn.model_selection import train_test_split
# Split our dataset
df_train, df_test = train_test_split(df_photos, test_size=0.2, random_state=1)
df_train.reset_index(drop=True, inplace=True)
df_test.reset_index(drop=True, inplace=True)

图片

我们的训练数据集已经将数据与其相关的标签混合在一起

我们有我们的图像和标签到一个可接受的形式,现在我们必须从文件路径加载我们的图像。我们将 PIL.Image 封装到一个函数中,该函数将加载图像,将其调整为128 x 128像素,将其转换为灰度图像,并将亮度正常化为0到1之间的值。

# Wrapper function to load and process images
def process_image(filepath):
    return np.asarray(Image.open(filepath).resize((128, 128)).convert("L")) / 255.0

让我们来测试一下我们的封装函数,我们可以从 pizza_files 文件中加载第一张图片,然后绘制出来看:

# Load image
img = process_images(pizza_files[0])
# Plot image
fig = plt.figure(figsize=(5, 5))
ax = fig.add_subplot(111)
ax.imshow(img, cmap="gray")
ax.set_xticks([])
ax.set_yticks([])
plt.show()

图片

酷!现在让我们继续从我们的训练集中绘制16张图片,连同它们的相关标签,来了解我们的数据是什么样的:

# Plot 16 images from our training set with labels
fig = plt.figure(figsize=(10, 10))
for i in range(16):
    plt.subplot(4, 4, i+1)
    img = process_image(df_train["filepath"].iloc[i])
    plt.imshow(img, cmap="gray")
    plt.grid(False)
    plt.xticks([])
    plt.yticks([])
    if df_train["label"].iloc[i] == 0:
        plt.title("Pizza", size=16)
    else:
        plt.title("Pasta", size=16)
plt.show()

图片

在构建分类器之前,我们需要做的最后一件事是将所有的图像和标签加载到数组中,以便加载到模型中。对于我们的图像,我们首先创建一个空的 numpy 数组,其长度等于训练集中图像的数量。这个数组的每个元素都是一个128 x 128的矩阵,代表一幅图像。我们也可以对我们的测试集图像做同样的事情。

# Create array of training images
train_images = np.empty([df_train.shape[0], 128, 128])
for index, row in df_train.iterrows():
    img = process_image(row.filepath)
    train_images[index] = img
# Create array of test images
test_images = np.empty([df_test.shape[0], 128, 128])
for index, row in df_test.iterrows():
    img = process_image(row.filepath)
    test_images[index] = img

我们可以看到一个示例图像看起来像一个 numpy 数组:

>>> train_images[0]
array([[0.12941176, 0.12156863, 0.12941176, ..., 0.10588235, 0.10588235,
        0.09803922],
       [0.1254902 , 0.1254902 , 0.1254902 , ..., 0.10588235, 0.10196078,
        0.09803922],
       [0.13333333, 0.13333333, 0.1254902 , ..., 0.11372549, 0.10588235,
        0.09803922],
       ...,
       [0.09019608, 0.09411765, 0.09803922, ..., 0.0745098 , 0.07058824,
        0.0745098 ],
       [0.09019608, 0.09411765, 0.09411765, ..., 0.0745098 , 0.0745098 ,
        0.0745098 ],
       [0.09019608, 0.09411765, 0.09411765, ..., 0.0745098 , 0.07058824,
        0.0745098 ]])

制作训练标签和测试标签的数组更加简单 —— 我们已经在数据框的一列中有了值,所以我们只需将这列转换为 numpy 数组:

# Create array of training labels
train_labels = df_train["label"].to_numpy()
# Create array of test labels
test_labels = df_test["label"].to_numpy()

我们现在准备构建和训练我们的模型!

模型构建与训练

我们将使用 keras 创建我们的模型,keras 是一个用于创建人工神经网络的高级 API。我们首先导入所需的软件包:

# Import keras
import tensorflow.keras as keras

我们的模型将由一系列层组成,我们将它们封装在 keras.Sequential() 中。神经网络通常包括:

  • 一个输入层ーー将数据输入其中

  • 一个或多个隐藏层ーー数据流经这些层,这些层具有激活函数,以确定节点的输入如何影响输出

  • 输出层ーー读取最终的输出神经元以确定分类

在我们的示例中,我们将有一个输入层、一个隐藏层和一个输出层。我们的模型将构建如下:

  1. 输入层将图像压平为一维(128 x 128 = 16384个输入节点)

  2. 隐藏层有256个节点——我们将使用的激活函数是一个rectified linear unit,但你可以使用其他方法,例如sigmoid function

  3. 输出层有2个节点(分别对应于比萨和意大利面)ー我们将添加一个softmax function 。因此,每个节点上的值代表了我们的分类器认为图像是披萨或意大利面的概率

# Create our model
model = keras.Sequential([
    keras.layers.Flatten(input_shape=(128, 128)),
    keras.layers.Dense(256, activation="relu"),
    keras.layers.Dense(2, activation="softmax")
])

我们可以查看我们的模型:

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
flatten (Flatten)            (None, 16384)             0         
_________________________________________________________________
dense (Dense)                (None, 256)               4194560   
_________________________________________________________________
dense_1 (Dense)              (None, 2)                 514       
=================================================================
Total params: 4,195,074
Trainable params: 4,195,074
Non-trainable params: 0
_________________________________________________________________

现在我们已经有了网络层,为了构建我们的模型,我们还需要3样东西:1)优化函数,2)损失函数,3)性能指标。

对于优化函数,我们将使用自适应矩估计(ADAM) ,它在较大数据集上往往比梯度下降法更好。我们还将设置我们的优化器的学习速率,以便在更新权重时不会出现大的跳跃。

对于我们的损失函数,我们将使用稀疏绝对交叉熵。

我们的性能指标是准确性,即正确分类照片的比例。

# Set the learning rate
opt = keras.optimizers.Adam(learning_rate=0.000005)
# Compile our model
model.compile(optimizer=opt,           loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True), metrics=['accuracy'])

现在我们可以训练我们的模型了!我们需要决定我们的数据集中有多少比例将用于未来的交叉验证,以及有多少 epoch 将用于我们的训练。在这种情况下,我将使用20% 的验证和运行我们的50个 epoch 的训练。

# Train our model
history = model.fit(train_images, train_labels, epochs=50, validation_split=0.2)

在我们的训练结束时,我们应该看到这样的东西:

Epoch 48/50
20/20 [==============================] - 0s 10ms/step - loss: 0.4522 - accuracy: 0.9453 - val_loss: 0.4958 - val_accuracy: 0.8875
Epoch 49/50
20/20 [==============================] - 0s 9ms/step - loss: 0.4505 - accuracy: 0.9500 - val_loss: 0.4943 - val_accuracy: 0.8750
Epoch 50/50
20/20 [==============================] - 0s 9ms/step - loss: 0.4493 - accuracy: 0.9438 - val_loss: 0.4981 - val_accuracy: 0.8625

酷!我们训练的神经网络在我们的训练数据上达到了94% 的准确率,在交叉验证上达到了86% 。通过将我们的适应输出设置为一个名为 history 的变量,我们可以将性能作为训练 epoch 的函数来绘制:

# Plot accuracy
fig = plt.figure(figsize=(15,5))
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
ax1.plot(history.history['accuracy'])
ax1.plot(history.history['val_accuracy'])
ax1.set_title('Model Accuracy')
ax1.set_ylabel('Accuracy')
ax1.set_xlabel('Epoch')
ax1.legend(['train', 'val'], loc='upper left')
ax2.plot(history.history['loss'])
ax2.plot(history.history['val_loss'])
ax2.set_title('Model Loss')
ax2.set_ylabel('Loss')
ax2.set_xlabel('Epoch')
ax2.legend(['train', 'val'], loc='upper left')
plt.show()

图片

这样的图可以用来检验我们是否过度拟合(即训练和交叉验证准确性和损失之间的巨大差异,在这里训练集是合适的)。

让我们看看我们的模型在我们之前分割出的测试数据上的准确性:

# Evaluate training accuracy
model.evaluate(test_images, test_labels, verbose=2)
7/7 - 0s - loss: 0.5233 - accuracy: 0.8250

我们的分类器在训练数据上有82.5% 的准确率ー现在让我们在一些新的照片上尝试我们的训练模型。

模型预测

现在让我们用一些新的看不见的数据来测试我们的模型。首先,我们可以编写一个封装函数,它接受模型和图像作为 numpy 数组,并返回一个字符串,其中包含预测的类和根据模型该类的概率。

# Function to return prediction and probability
def model_prediction(model, img):
    predictions = model.predict(np.array([img]))
    
    if predictions[0][0] > predictions[0][1]:
        return f"Pizza: {round(100*predictions[0][0], 2)}%"
    else:
        return f"Pasta: {round(100*predictions[0][1], 2)}%"

现在,我们可以用我自己做饭的两张图片来测试这一点:一碗意大利面和一个比萨饼。

意大利面

# Load image of cacio e pepe
cacio_e_pepe = process_image("./cacioepepe.jpg")
# Plot image along with prediction
fig = plt.figure(figsize=(5, 5))
ax = fig.add_subplot(111)
ax.imshow(cacio_e_pepe, cmap="gray")
ax.set_xticks([])
ax.set_yticks([])
ax.set_title(model_prediction(model, cacio_e_pepe))
plt.show()

图片

披萨

# Load image of pizza
home_pizza = process_image("./pizza.jpg")
# Plot image along with prediction
fig = plt.figure(figsize=(5, 5))
ax = fig.add_subplot(111)
ax.imshow(home_pizza, cmap="gray")
ax.set_xticks([])
ax.set_yticks([])
ax.set_title(model_prediction(model, home_pizza))
plt.show()

图片

哇!都是正确的分类!我们现在有一个图像分类器比可以区分比萨饼和面食。从这里,我们可以调整神经网络的一些层,优化器,损失函数,甚至考虑使用卷积,以提高我们模型的准确性。

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

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

相关文章

机器人中的数值优化之最速下降法

本文ppt来自深蓝学院《机器人中的数值优化》 目录 1 迭代方向 2 步长的选择 3 Armijo condition 4 非精确线搜索的优势 1 迭代方向 梯度方向是函数上升最快的方向,而负梯度方向则是函数下降最快的方向,因此最速下降法就是以负梯度方向为迭代方向…

二叉树的构造和相关功能的代码实现及解析

目录 一.二叉树类的定义 二.构造二叉树(构造函数) 三.为二叉树插入节点(insert_value) 四.移除根节点(remove_root,lchild_leaf) 五.移除二叉树中的某值(remove,remove_value) 六.清空二叉树 七.前、中、后序遍历 一.二叉树类的定义 二叉树类的定…

Django入门学习-了解基本模块

目录 MVT设计了解 认识MVT 实际操作 Template: View: 路由配置 Model: 默认的后台管理模块 初始化admin模块 应用中Admin注册 MVT设计了解 认识MVT Django的web设计模型是MVT: Model:数据存储层,处理所有数据相关的业…

idea+ApifoxUploader+Apifox真是内外双修,香

前言 最近部门为整合后端组、前端组、测试组、需求组、产品组等组之间的工作流程,旨在提高协调与高效,其中之一就是希望开发组(后端、前端)开发的接口能及时更新,测试组能做接口测试,后期方便出文档&#x…

大公司为什么禁止SpringBoot项目使用Tomcat?

本文已经收录到Github仓库,该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~ Github地址:https://github.com/…

【2】burpsuite屏蔽浏览器无用流量包方法

0x01 问题描述经常会使用火狐或者谷歌去burpsuite对站点进行测试,但是在测试的过程中burpsuite经常抓到火狐浏览器自身的数据包或者其他无用的数据包,这就对我们工作的效率大有影响,所以这里来告诉大家如何解决此类问题。0x02 问题复现访问网…

星环科技数据治理与数据价值评估实践分享

数据价值评估背景 自2015年8月国务院《促进大数据发展行动纲要》提出“数据已成为国家基础性战略资源”以来,我国出台了诸多政策和法案,推进数据的发展和数据要素的资产化。 2019年10月,第十九届四中全会关于《推进国家治理体系和治理能力现…

Node.js安装详细教程

安装 Node.js 官网下载安装包https://nodejs.org/zh-cn/,一直【下一步】安装即可。 设置Windows操作系统全局环境变量 为什么设置环境变量? 当我们在cmd命令行中输入命令时,系统首先会在当前目录下去找命令对应的可执行程序,如果…

后端校验(hibernate-validator)

目录一、介绍和依赖二、方法的 Model 参数校验三、方法的非 Model 参数校验四、常用注解五、快速失败六、自定义校验规则一、介绍和依赖 hibernate-validator 是 Java 中常用的后端校验框架 https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/【…

提取各种数据结构中的元素将提取结果合并为迭代对象 itertools.chain(*a,b)

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 提取各种数据结构中的元素 将提取结果合并为迭代对象 itertools.chain(*a,b) [太阳]选择题 以下python代码最后输出正确的一项是? from itertools import chain a[(1, A), (2, B), …

Java高效率复习-线程基础[线程]

内容大纲 线程相关概念 并发并行 当只有一个CPU时,会执行并发的效果,在多个应用程序之间快速切换,而有多个CPU时,则多个CPU独立执行,而当进程多于CPU个数时,则会出现并发并行的情况,总有一个CPU…

如何炼就数据分析的思维?

目录 前言结构化思维假说演绎思维指标化思维维度分析思维 前言 面对数据异常,我们经常会出现“好像是A原因引起的?”“貌似和B原因也相关?““有可能是 C操作不当“的主观臆测。 或者,拿到一个分析议题,分析”11 月销售…

@ConfigurationProperties注解使用方法(内含源代码)

ConfigurationProperties注解使用方法(内含源代码) 源代码下载链接地址:https://download.csdn.net/download/weixin_46411355/87400774 目录ConfigurationProperties注解使用方法(内含源代码)源代码下载链接地址&…

怎么识别截图中的文字?这三个方法让你轻松学会

在日常工作或学习中,我们会经常在网上查阅一些资料,当遇到一些优美的句子或者段落时,都会手动摘抄下来,这种记录方式不仅很耗时,还耗费精力,并且现在很多网站都已经不支持文本复制了,遇到这种情…

6.验证面试高频问题整理(附答案)

目录 Q126.top-down phase、bottom-up phase有哪些 Q127.为什么build_phase是top-down phase,connect_phase是bottom-up phase Q128.$size用于packed array和unpacked array分别得到的什么 Q129.class和struct的异同 Q130.class和module的异同 Q131.对象创建的…

MAC系统 LightGBM模型转为pmml格式

一、配置JAVA环境和Maven环境 参考以下两个博客即可 MAC 系统安装 JDK 及环境变量配置_蜗牛的博客-CSDN博客_mac jdk环境变量配置 MAC 系统安装 Maven 及环境变量配置_蜗牛的博客-CSDN博客_mac安装mpv 二、下载JPMML-LightGBM 先在Git上下载 直接下载或使用git clone http…

Aop切面编程原理和Spring实现

Aop切面编程概念 AOP切面编程一般可以帮助我们在不修改现有代码的情况下,对程序的功能进行拓展,往往用于实现 日志处理,权限控制,性能检测,事务控制等 AOP实现的原理就是动态代理,在有接口的情况下,使用JDK动态代理,在没有接口的情况下使用cglib动态代理 为Dao层所有的add方法…

字体反爬,一种来自字体设计师的跨行反爬案例 | 案例 28

本篇博客涉及的内容非常有价值,尤其是在反爬领域。 核心内容为自定义字体文件反爬。 文章目录准备工作在 Web 页面中使用字体文件整理文字编码Flask 中随机一串数字,渲染到前台总结准备工作 在正式编写代码前,需要先安装 FontCreator &#…

GeoServer学习笔记-01GeoSever运行编译

一、运行1. 下载GeoServerGitHub仓库地址:https://github.com/geoserver/geoserver2.本地代码工具打开项目在idea里,文件->新建->来自现有的源代码项目,选择项目的pom文件加载项目。3.idea编译环境设置(1)设置jd…

图论(5)最小生成树简单应用

活动 - AcWing 参考:《算法竞赛进阶指南》-lyd 目录 一、基础算法 二、 1.最短网络(prim板子) 2.局域网(kruskal板子) 3.繁忙的都市 4.1143. 联络员 5.连接格点(预处理) 一、基础算法…