百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

学习使用自然图像数据执行线性过滤

toyiye 2024-06-21 11:55 14 浏览 0 评论

卷积神经网络通常从训练数据中学习有用的特征。根据任务的不同,第一个卷积层的特征可能是训练数据的基本特征。例如,在图像数据中,学习到的特性可以表示边缘和blob。在网络的后续层中,学习到的特性可以表示更抽象、更高级的实体。

在实践中,网络架构的深度几乎总是比只有几个层的深度要深得多,这使得很难从视觉上解释和分析所学习的特性,因为卷积核的总量很大。

在接下来的实验中,我们将图像处理和计算机视觉中边缘检测的典型方法Sobel边缘滤波应用到数据集中,训练我们的模型执行类似的线性映射。我们还试图从内核大小比Sobel过滤情况稍微大一点的数据中学习更任意的线性过滤器。

这个演示希望建立了关于神经网络中卷积层如何对输入数据进行操作的直觉,卷积核如何在训练过程中对进程进行加权,以及神经网络的训练如何被视为一个最小化问题。

首先,我们必须对输入的图像数据X应用线性过滤器,以获得原始图像的经过过滤的Y版本。在形式上,线性滤波操作可以概括为:

对于我们可以想到的任何参数集(卷积核)或输入数据,线性滤波器是一个定义明确的操作。

我们现在可以构建一个单层,单核,卷积神经网络,它近似于线性滤波操作。毕竟,在两种情况下,在线性滤波器和卷积神经网络中执行的计算与卷积核参数的差异完全相同,我们即将从数据中学习。

我们可以在线性滤波器和卷积神经网络之间建立以下连接:

学习任务现在可以表示为最小化问题,其中线性滤波器的输出和卷积神经网络的输出之间的均方误差被最小化:

应用于图像数据的线性滤波器的参数称为卷积核。我们将通过首先在x方向上然后在y方向上用称为Sobel算子的3×3卷积核过滤图像数据来开始我们的实验。

对于实验,我们将使用在TensorFlow上运行的Keras框架。

Sobel 滤波器在x方向上

首先,我们必须为图像预处理定义一些辅助功能。加载数据集,将图像转换为灰度,对图像强度范围进行归一化,并对数据集中的每个图像执行线性滤波。

import numpy as np

import os

import cv2

import keras

from keras.models import Sequential, Model

from keras.layers import Conv2D

from keras import optimizers

import matplotlib.pyplot as plt

def make_grayscale(img):

# Transform color image to grayscale

gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

return gray_img

def filter_image_sobelx(img):

# Perform filtering to the input image

sobelx = cv2.Sobel(img, cv2.CV_32F, 1, 0, ksize=3)

return sobelx

def normalize_image255(img):

# Changes the input image range from (0, 255) to (0, 1)

img = img/255.0

return img

def normalize_image(img):

# Normalizes the input image to range (0, 1) for visualization

img = img - np.min(img)

img = img/np.max(img)

return img

# Get the paths to the training images

data_dir = 'data'

folderpaths = [os.path.join(data_dir, o) for o in os.listdir(data_dir) if os.path.isdir(os.path.join(data_dir,o))]

imagepaths = []

for folderpath in folderpaths:

temppaths = [os.path.join(folderpath, fname) for fname in os.listdir(folderpath) if fname.endswith('.jpg')]

imagepaths += temppaths

# Load and pre-process the training data

images = []

grayimages = []

filteredimages = []

np.random.shuffle(imagepaths)

for imagepath in imagepaths:

print(imagepath)

img = cv2.imread(imagepath).astype(np.float32)

img = normalize_image255(img)

gray_img = make_grayscale(img)

filtered_img = filter_image_sobelx(gray_img)

images.append(img)

grayimages.append(gray_img)

filteredimages.append(filtered_img)

images = np.array(images, dtype='float32')

grayimages = np.array(grayimages, dtype='float32')

filteredimages = np.array(filteredimages, dtype='float32')

# Expand the image dimension to conform with the shape required by keras and tensorflow, inputshape=(..., h, w, nchannels).

grayimages = np.expand_dims(grayimages, -1)

filteredimages = np.expand_dims(filteredimages, -1)

print("images shape: {}".format(images.shape))

print("grayimages shape: {}".format(grayimages.shape))

print("filteredimages shape: {}".format(filteredimages.shape))

# Visualize an arbitrary image and the filtered version of it

margin_img = np.ones(shape=(256, 10, 3))

combined_image = np.hstack((img, margin_img, np.dstack((gray_img,)*3), margin_img, np.dstack((normalize_image(filtered_img),)*3)))

cv2.imwrite('OriginalGrayFiltered_sobelx.png', (255.0*combined_image).astype(np.uint8))

作为训练和测试数据,我们将利用计算收集的城市和自然场景类别 - 数据集(Oliva, A. & Torralba, A. (2001).Modeling the Shape of the Scene: A Holistic Representation of the Spatial Envelope) 由计算视觉认知实验室收集,麻省理工学院。(http://cvcl.mit.edu/Papers/IJCV01-Oliva-Torralba.pdf)

原始数据集由八个类别的自然场景彩色图像(分辨率:256 x 256)组成,我们使用三个类别:街道,城市中心和高层建筑。这样,我们获得足够大小的训练和测试集(764个训练样本和192个测试样本),以便训练不会过度拟合,并且训练也可以通过在合理的时间范围内使用更温和的硬件来执行。所选类别代表具有强边缘的自然场景(人类构建结构的大范围),这有助于我们比较结果。

下面,我们可以观察属于数据集的原始图像的可视化,灰度转换和图像的Sobel过滤版本:

原始图像(左),灰度图像(中)和Sobel过滤图像的x方向(右

我们将仅在单通道图像上使用线性滤波器。在实践中,这意味着训练模型以将灰度转换图像映射到Sobel滤波图像。

接下来,我们定义模型:单层,单核,具有线性激活的卷积网络,即激活函数是身份。卷积核大小选择为3 x 3,以符合Sobel滤波器大小。

具有Nesterov momentum 的随机梯度下降优化器用于训练100个时期的模型。在每个时期,保存卷积层权重以进一步可视化。

input_height, input_width = gray_img.shape

def linearcnn_model():

# Returns a convolutional neural network model with a single linear convolution layer

model = Sequential()

model.add(Conv2D(1, (3,3), padding='same', input_shape=(input_height, input_width, 1)))

return model

model = linearcnn_model()

sgd = optimizers.SGD(lr=1e-2, decay=1e-6, momentum=0.9, nesterov=True)

model.compile(optimizer=sgd, loss='mean_squared_error', metrics=['accuracy'])

model.summary()

number_of_epochs = 100

loss = []

val_loss = []

convweights = []

for epoch in range(number_of_epochs):

history_temp = model.fit(grayimages, filteredimages,

batch_size=4,

epochs=1,

validation_split=0.2)

loss.append(history_temp.history['loss'][0])

val_loss.append(history_temp.history['val_loss'][0])

convweights.append(model.layers[0].get_weights()[0].squeeze())

训练结束后,我们可以绘制训练和验证损失,以确定训练是否过度拟合

# Plot the training and validation losses

plt.close('all')

plt.plot(loss)

plt.plot(val_loss)

plt.title('Model loss')

plt.xlabel('epoch')

plt.ylabel('loss')

plt.legend(['Training loss', 'Validation loss'], loc='upper right')

plt.show()

plt.savefig('trainingvalidationlossgx.png')

在整个训练过程中,训练和验证的损失是稳定的,并且模型似乎是收敛的。

现在可以将每个epoch 的保存权重可视化为矩阵格式的数值,以及更可视的格式,其中数值表示可视化中的像素强度值。声明用于执行可视化的功能,并且为每个epoch 创建可视化。

def visualize_matrix(M, epoch=1):

"""

Create a visualization of an arbitrary matrix.

"""

fig = plt.figure(figsize=(10,5))

ax1 = plt.subplot(1,2,1)

ax2 = plt.subplot(1,2,2)

title = "Epoch {}".format(epoch)

fig.suptitle(title, fontsize=20)

height, width = M.shape

Mud = np.flipud(M) # Now the i-index complies with matplotlib axes

coordinates = [(i,j) for i in range(height) for j in range(width)]

print(coordinates)

for coordinate in coordinates:

i,j = coordinate

value = np.round(Mud[i,j], decimals=2)

relcoordinate = (j/float(width), i/float(height))

ax1.annotate(value, relcoordinate, ha='left', va='center',

size=22, alpha=0.7, family='serif')

padding = 0.25

wmargin = (width-1)/float(width) + padding

hmargin = (height-1)/float(height) + padding

hcenter = np.median(range(height))/float(height)

print(hcenter)

hcenter = hcenter + 0.015 # Offset due to the character alignment

bracket_d = 0.4

bracket_b = 0.05

bracket_paddingl = 0.05

bracket_paddingr = -0.05

ax1.plot([-bracket_paddingl, -bracket_paddingl],[hcenter-bracket_d, hcenter+bracket_d], 'k-', lw=2, alpha=0.7)

ax1.plot([-bracket_paddingl, -bracket_paddingl+bracket_b], [hcenter-bracket_d, hcenter-bracket_d], 'k-', lw=2, alpha=0.7)

ax1.plot([-bracket_paddingl, -bracket_paddingl+bracket_b], [hcenter+bracket_d, hcenter+bracket_d], 'k-', lw=2, alpha=0.7)

ax1.plot([wmargin-bracket_paddingr, wmargin-bracket_paddingr],[hcenter-bracket_d, hcenter+bracket_d], 'k-', lw=2, alpha=0.7)

ax1.plot([wmargin-bracket_paddingr-bracket_b, wmargin-bracket_paddingr], [hcenter-bracket_d, hcenter-bracket_d], 'k-', lw=2, alpha=0.7)

ax1.plot([wmargin-bracket_paddingr-bracket_b, wmargin-bracket_paddingr], [hcenter+bracket_d, hcenter+bracket_d], 'k-', lw=2, alpha=0.7)

ax1.set_xlim([-padding, wmargin+0.06])

ax1.set_ylim([-padding, hmargin])

ax1.get_xaxis().set_visible(False)

ax1.get_yaxis().set_visible(False)

ax1.axis('off')

matshowplt = ax2.matshow(M, cmap='gray', vmin=-2, vmax=2)

cbar = plt.colorbar(matshowplt, ax=ax2, fraction=0.046, pad=0.04)

cbar.ax.tick_params(labelsize=18)

cbar.ax.get_yaxis().labelpad = 20

cbar.ax.set_ylabel('Weight value', rotation=270, fontsize=20)

ax2.get_xaxis().set_visible(False)

ax2.get_yaxis().set_visible(False)

plt.tight_layout()

plt.subplots_adjust(wspace=0.5)

return fig

savefolder = 'images/'

for i in range(len(convweights)):

savepath = savefolder+'weightfigure'+str(i)+'.png'

print(savepath)

M = convweights[i]

fig = visualize_matrix(M, epoch=i+1)

fig.savefig(savepath)

plt.close(fig)

现在我们已经为每个epoch创建了可视化,我们可以从中创建一个gif,看看权重如何进展。

import imageio

from natsort import natsorted

# Get the paths to the convolution weight visualization images

image_dir = 'images/'

imagepaths = [os.path.join(image_dir, fname) for fname in os.listdir(image_dir) if fname.endswith('.png')]

imagepaths = natsorted(imagepaths)

with imageio.get_writer('weightmoviegx.gif', mode='I') as writer:

for impath in imagepaths:

image = imageio.imread(impath)

writer.append_data(image)

随着模型的训练,卷积层权重的进展。权重值在x方向上接近Sobel算子

在gif中,随着学习的进展,卷积核权重如何向Sobel x方向滤波器收敛是非常明显的。前10到15个epochs的收敛速度很快,之后收敛率明显下降。如果学习任务比我们在本实验中使用的简单线性过滤操作更复杂,我们仍然会看到类似的行为。卷积核值将收敛到某种最佳配置,从数据中提取有用的特征。在该实验中,有用的特征是由Sobel算子给出的图像x方向上的边缘。我们能够找到几乎精确的卷积核参数,这些参数在第一手资料中产生了训练数据,主要是因为我们的问题陈述非常简单。然而,

为了测试模型,我们可以看到模型的预测与x方向上的Sobel滤波相比如何。

predicted_img = model.predict(np.array([np.expand_dims(gray_img, -1)]))

predicted_img = predicted_img.squeeze()

margin_img = np.ones(shape=(256, 10, 3))

combined_image = np.hstack((np.dstack((normalize_image(predicted_img),)*3), margin_img, np.dstack((normalize_image(filtered_img),)*3)))

cv2.imwrite('PredictedFiltered_sobelx.png', (255.0*combined_image).astype(np.uint8))

模型的输出(左)和使用Sobel算子在x方向(右)过滤的相同图像

在图中(顶部),我们可以观察模型的输出和Sobel滤波在x方向上的并排结果。视觉检查,两个图像看起来相似。事实上,在强度值中,当学习的卷积核收敛到接近原始Sobel算子的值时,应该只发现图像之间的微小差异。

Sobel 滤波器在y方向上

同样可以使用相同的代码对Sobel算子在y方向进行线性滤波。我们所要做的就是改变图像滤波函数,使其在y方向而不是x方向上进行滤波,再次加载和过滤训练数据,并用新的数据对模型进行训练

def filter_image(img):

# Perform filtering to the input image

sobely = cv2.Sobel(img, cv2.CV_32F, 0, 1, ksize=3)

return sobely

在下面的图中,我们可以观察到Sobel过滤器现在如何强调垂直方向(y方向)的图像强度边缘。

原始图像(左),灰度图像(中)和Sobel过滤后的y方向图像(右)

再一次,我们可以观察当网络从训练数据中学习时,卷积核如何在y方向上对Sobel滤波器进行加权。收敛行为与Sobel算子在x方向上的前一种情况非常相似

随着模型的训练,卷积层权重的进展。权重值在y方向上接近Sobel算子

同样,模型的输出和相同测试图像的Sobel滤波版本(如下图所示)具有相似的外观。对于人眼来说,不可能分辨出两幅图像之间的区别

模型的输出(左)和使用Sobel算子在y方向(右)过滤的相同图像

笑脸过滤器

之前学到的Sobel滤波器非常简单,只需要学习少量参数。让我们看看,如果我们可以学习具有更大内核的线性滤波器。

我们将在下面的实验中使用的滤镜内核是一个32 x 32像素的笑脸。加载滤波器内核并通过使用笑脸内核过滤灰度图像来创建训练数据。由于内核大,内核基本上延伸到图像边界之外。图像边界用零填充以抵消由于卷积而导致的图像分辨率的缩小。

def load_kernel(path='kernels/smiley_32.png'):

# Load a kernel image into numpy array

kernel = cv2.imread(path, 0)

kernel = cv2.bitwise_not(kernel).astype(np.float32)

# Normalize according to the L2-norm

kernel = kernel/np.linalg.norm(kernel)

return kernel

def filter_image(img, kernel):

# Perform filtering to the input image

convolved = cv2.filter2D(img, cv2.CV_32F, kernel, borderType=cv2.BORDER_CONSTANT)

return convolved

The normalized smiley face filter kernel

在下面的图中,我们可以看到笑脸过滤后的图像与原始的和灰度转换后的图像相比是什么样子

原始图像(左),灰度图像(中)和笑脸过滤图像(右)

我们的模型再次是一个简单的单层,单核,卷积神经网络,其中激活函数是同一性的。这次,我们根据笑脸内核的大小将内核大小设置为32 x 32。作为对先前实验的增强,我们将基本随机梯度下降优化器更改为更强大的Adam优化器。

input_height, input_width = gray_img.shape

def linearcnn_model():

# Returns a convolutional neural network model with a single linear convolution layer

model = Sequential()

model.add(Conv2D(1, (32,32), padding='same', input_shape=(input_height, input_width, 1)))

return model

model = linearcnn_model()

adam = optimizers.Adam(lr=1e-3)

model.compile(optimizer=adam, loss='mean_squared_error', metrics=['accuracy'])

model.summary()

number_of_epochs = 100

loss = []

val_loss = []

convweights = []

for epoch in range(number_of_epochs):

history_temp = model.fit(grayimages, filteredimages,

batch_size=1,

epochs=1,

validation_split=0.2)

loss.append(history_temp.history['loss'][0])

val_loss.append(history_temp.history['val_loss'][0])

convweights.append(model.layers[0].get_weights()[0].squeeze())

该模型被训练100个epochs,并且在每个时期存储卷积核权重。训练和验证损失在大约10个epochs内快速收敛,之后在两个损失值中都可以看到小的波动

现在可以将存储的卷积核权重值可视化并组合成gif。结果很有启发性:该模型似乎很好地学习了原始的笑脸过滤内核,这可以在下面的gif中观察到。卷积核权重相对较早地采用笑脸的形状,大约在十个epochs之后,但是权重仍包含大量噪声。随着训练的进行,噪声逐渐消失,相邻的权重值相对于彼此变得更加恒定。

通过分析验证损失和卷积核权重的进展,可以进行重要的观察。即使在第十个epoch之后验证损失看起来平缓,卷积核权重仍然朝向原始的笑脸内核产生足够的量。训练和验证损失曲线的线性标度可能会不时产生误导,因为初始损失??值可能在损失测量的后期改进中主导损失可视化。

随着模型的训练,卷积层权重的进展。权重值收敛于笑脸过滤器附近。

现在我们有一个训练有素的模型,我们可以可视化和比较模型的输出和笑脸过滤测试图像的结果。在下图中,我们可以观察模型和笑脸过滤器内核如何产生与测试图像类似的外观。类似于Sobel滤波图像,以及由数据学习Sobel滤波器的模型产生的图像,很难将笑脸滤波图像与模型的输出区分开。

模型的输出(左)和用笑脸内核(右)过滤的相同图像

最后的话

我希望线性滤波器的三个实验能充分地说明,当网络从数据中学习时,卷积核的权值是如何发展的。此外,我希望您能够了解一些知识,以便理解卷积层对输入数据的操作方式。这些实验的结果并没有直接推广到卷积神经网络在图像分类中使用的情况,而是为理解卷积层背后的现象和学习作为一个优化问题提供了基础。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码