基于 Keras 的图像分类器

引言

深度学习是使用人工神经网络进行机器学习的一个子集,目前已经被证明在图像分类方面非常强大。尽管这些算法的内部工作在数学上是严格的,但 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张照片。这很简单!

 

数据整理

为了将我们的数据转换成对我们的模型有利的格式,我们需要使用 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))})
    )
)

 

c911f676466c0375df48a9609f97b167.png

现在,我们希望将数据集拆分为训练数据和测试数据。幸运的是,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)

 

601b608e3ddcb696135c816b6df6e7c5.png

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

 

我们有我们的图像和标签到一个可接受的形式,现在我们必须从文件路径加载我们的图像。我们将 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()

 

d74d4ff5bb7f0b074e1114b37cf4d5b4.png

酷!现在让我们继续从我们的训练集中绘制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()

 

df7858d93373c65ab49fc0d392ac98ec.png

在构建分类器之前,我们需要做的最后一件事是将所有的图像和标签加载到数组中,以便加载到模型中。对于我们的图像,我们首先创建一个空的 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()

 

723e75e26073c01db0c9fa7e29cd8fad.png

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

 

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

# 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()

 

28f548cfb3cdc0b1d4718aecdc8d1e79.png

披萨

# 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()

 

3989bd584a435e278c99ddaaba7469ad.png

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

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Greek Alphabet Letters Symbols

Upper CaseLower CaseGreek Letter NameEnglish EquivalentSoundΑαAlphaa ΒβBetab ΓγGammag ΔδDeltad ΕεEpsilone ΖζZetaz ΗηEtah ΘθThetath ΙιIotai ΚκKappak ΛλLambdal ΜμMum ΝνNun ΞξXix ΟοOmicrono ΠπPip ΡρRhor Σσ,…

JQuery ajax 提交数据提示:Uncaught TypeError:Illegal invocation

JQuery ajax 提交数据提示:Uncaught TypeError:Illegal invocation 1 问题描述 用jQuery Ajax向DRF接口提交数据的时候,console提示:Uncaught TypeError:Illegal invocation(未捕获的异常:非法调用)。 这个问题可能有两种原因导…

可以写进简历的软件测试项目实战经验(包含电商、银行、app等)

前言: 今天给大家带来几个软件测试项目的实战总结及经验,适合想自学、转行或者面试的朋友,可以写进简历里的那种哦。 1、项目名称: 家电购 项目描述: “家电购”商城系统是基于 web 浏览器的电子商务系统,通过互联网…

基于SpringBoot+Vue的二手物品交易平台

基于SpringBootVue的二手物品交易平台的设计与实现~ 开发语言:Java数据库:MySQL技术:SpringBootMyBatisVue工具:IDEA/Ecilpse、Navicat、Maven 系统展示 主页 详情 管理员界面 摘要 本项目是基于Spring Boot 和 Vue 技术栈构建…

【Java 进阶篇】JQuery 遍历 —— `each()` 方法的奇妙之旅

在前端的世界里,操作元素是我们开发者最为频繁的任务之一。为了更好地操控页面上的元素,JQuery 提供了许多强大的工具,其中 each() 方法是一颗璀璨的明星。本文将深入探讨 each() 方法的原理和用法,带你踏上一场遍历之旅。 起步&…

【C++面向对象】14. 命名空间

文章目录 【 1. 命名空间的定义 】【 2. using 指令 】2.1 using 指定命名空间的全部2.2 using 指定命名空间的部分 【 3. 不连续的命名空间 】【 4. 嵌套的命名空间 】 问题的背景:假设这样一种情况,当一个班上有两个名叫 Zara 的学生时,为了…

linux三次握手、四次挥手

TCP协议是一个安全的、面向连接的、流式传输协议,所谓的面向连接就是三次握手,对于程序猿来说只需要在客户端调用connect()函数,三次握手就自动进行了。先通过下图看一下TCP协议的格式,然后再介绍三次握手的具体流程。 1.tcp协议…

C语言查找幸运数字(ZZULIOJ1056:幸运数字)

题目描述 小明对某些数字有偏爱,例如,他喜欢7的倍数,而不喜欢4的倍数,如果一个整数是7的倍数,而不是4的倍数,小明会认为这个数字是他的幸运数字。现在给定两个整数m和n,请你帮小明找m到n范围内的…

入门后端开发得学什么?这份超详细的后端开发学习路线图值得推荐!

后端开发, 无疑是一个极为关键的领域,涉及到我们每日互联网生活的每个细节。每当你在网上浏览、搜索或进行购物等活动时,背后都有大量的后端技术作为支撑。而随着技术的日益进步,人们对于高效、稳定和安全的网络服务的需求也越来越高。 另一…

熟悉 Unity HDRP设置以提高性能

HDRP Version 10 了解如何利用高清晰度渲染管道(HDRP)设置,以最大限度地提高性能,并一次实现强大的图形。 随着Unity 2020 LTS及以后的HDRP版本10的发布,HDRP包继续优先考虑其用户友好的界面,灵活的功能,稳定性和总体…

Leetcode_48:旋转图像

题目描述: 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1: 输入:matrix [[1,2,3],…

【L2GD】: 无环局部梯度下降

文章链接:Federated Learning of a Mixture of Global and Local Models 发表期刊(会议): ICLR 2021 Conference(机器学习顶会) 往期博客:FLMix: 联邦学习新范式——局部和全局的结合 目录 1.背景介绍2. …

DataCamp在线学习平台

DataCamp(https://www.datacamp.com/blog)是一个在线学习平台,专注于数据科学和分析领域的教育。该平台提供丰富的课程,涵盖了从数据处理到机器学习和深度学习的各个方面。以下是DataCamp的主要特点: 互动学习&#x…

【python】OpenCV—Image Pyramid(8)

文章目录 1 图像金字塔2 拉普拉斯金字塔 1 图像金字塔 高斯金字塔 在 OpenCV 中使用函数 cv2.pyrDown(),实现图像高斯金字塔操作中的向下采样,使用函数 cv2.pyrUp() 实现图像金字塔操作中的向上采样 import cv2img cv2.imread(C://Users/Administrat…

MIB 6.1810实验Xv6 and Unix utilities(2)sleep

难度:easy Implement a user-level sleep program for xv6, along the lines of the UNIX sleep command. Your sleep should pause for a user-specified number of ticks. A tick is a notion of time defined by the xv6 kernel, namely the time between two interrupts f…

数据分析 - 离散概率分布的运用

期望公式 期望的方差 期望的标准差

微服务架构演进

系统架构演变 没有最好的架构,只有最合适的架构;架构发展过程:单体架构》垂直架构》SOA 面向服务架构》微服务架构;推荐看看《淘宝技术这十年》; 单体架构 互联网早期,一般的网站应用流量较小&#xff0…

Hive数据表操作--学习笔记

1,Hive数据表操作 1,建表语句和内外部表 ①创建内部表 create [external] table [if not exists] 表名( 字段名 字段类型 [comment 注释], 字段名 字段类型 [comment 注释], ... ) [row format delimited fields terminated by 指定分隔符];&#xff0…

行内样式、内部样式、外部样式

行内样式: 该元素的所在本行中使用style标记来写样式 内部样式: 在head标签中使用style标记来写样式 外部样式: 在head标签中使用link标记引用外部样式 注意优先级: 行内样式>内部样式>外部样式 代码…

【Java 进阶篇】JQuery 遍历 —— For 循环的奇妙之旅

在前端开发的世界里,遍历是一个常见而重要的操作。它让我们能够浏览并操纵文档中的元素,为用户提供更加丰富和交互性的体验。而在 JQuery 中,遍历的方式多种多样,其中 for 循环是一种简单而灵活的选择。在本篇博客中,我…