基于PyTorch图像特征工程的深度学习图像增强
介绍
在深度学习与数据科学结合的黑客竞赛中展现出色技巧通常与特征工程紧密相关(坦率地说,在AnalyticsVidhya的数据科学黑客竞赛平台中可以找到相关比赛)
我是以参加多次深度学习竞赛积累的经验分享的,在那次具体的深度学习竞赛中,我们成功收集到了一张包含数百张图像的数据集——然而这样的数据量根本就不足以帮助我们取得甚至接近顶端水平的成绩。那么我们该如何解决这一问题呢?
那么答案是什么? 其实这取决于数据科学家的专业技能。这也体现了我们独特的好奇心和创造力。这也是特征工程的核心理念——即通过现有特征最大化地提取新的有用信息。同样地,在图像数据处理中这一理念依然有效。
主要由图像增强技术实现的核心功能。
然而这一概念不仅限于此,在工业界及现实应用场景中,我们已在多个[深度学习模型]项目中实现了这一技术的应用。

图像增强模块帮我扩展了现有的数据集,并且无需承受不必要的麻烦;而且我相信这项技术将对您的项目产生积极影响。
因此,在本文中我们将会深入探讨图像增强的概念其重要性以及不同类型的图像增强技术。我们也将开发相应的算法并利用PyTorch框架构建一个高效的图像分类模型。
目录
- 在处理光照不均或模糊图片时,实施图象增強技術变得十分關鍵。
- 現有種類多样的圖像增強技術可用於改善圖像质量。
- 建立可靠的选择标准有助于确保所选方法的有效性。
- 通過具體案例分析來探討如何运用圖像增強技術提升圖像分類性能。
为什么需要图像增强?
深度学习模型一般都需要大量的数据用于训练。一般来说,在其他条件相同的情况下,在实际应用中,随着收集的数据量越大(或数量越多),模型的性能也会越优。然而,在实际应用中,获取海量的数据并面临诸多技术上的挑战。并不是每个人都拥有大公司的雄厚财力去支持这一过程。
由于缺乏足够的训练数据(链接:https://courses.analyticsvidhya.com/courses/computer-vision-using-deep-learning-version2?utm_source=blog&utm_medium=image-augmentation-deep),我们的[深度学习模型][链接]难以从现有数据中提取有效的特征或建立可靠的模式识别能力。这可能导致该模型在面对新的、 unseen 的输入时表现不佳。
在这种情况下
图像增强是通过生成模型创建新的图像以训练我们的深度学习模型的过程。这些新图像是基于现有训练数据集生成的,并且由于这种机制的存在我们无需手动收集大量新增数据。

涉及多种图像增强技术,在后续章节中我们将介绍几种常用的技术及其应用情况
不同的图像增强技术
图像旋转
图像旋转是一种主要的增强手段之一。它有助于使我们的模型对对象方向的变化变得更加稳健。即使我们旋转图像,旋转后的图像信息并未发生改变。汽车就是一种车辆类别:

基于此技术的应用场景中,我们可以利用这一方法:通过生成这些旋转图像以扩充数据集来增强训练集的多样性。让我们详细探讨这一操作的具体步骤:
基于此技术的应用场景中, 我们可以利用这一方法: 通过生成这些旋转图像以扩充数据集来增强训练集的多样性. 让我们详细探讨这一操作的具体步骤:
# 导入所有必需的库
import warnings
warnings.filterwarnings('ignore')
import numpy as np
import skimage.io as io
from skimage.transform import rotate, AffineTransform, warp
from skimage.util import random_noise
from skimage.filters import gaussian
import matplotlib.pyplot as plt
% matplotlib inline
我将展示该图像中多样化的图像增强技术。你可以根据需要探索其他图片。
我们先导入图像并将其可视化:
# reading the image using its path
image = io.imread('emergency_vs_non-emergency_dataset/images/0.jpg')
# shape of the image
print(image.shape)
# displaying the image
io.imshow(image)

这是原始的图像。让我们观察一下如何对其进行旋转。为了实现这一目标,请确保正确调用 skimage 库中的旋转变换功能以完成操作:
print('Rotated Image')
#rotating the image by 45 degrees
rotated = rotate(image, angle=45, mode = 'wrap')
#plot the rotated image
io.imshow(rotated)

很好!将模式设置为“wrap”,用图像的剩余像素填充输入边界之外的点。
平移图像
在这些情况下(可能存在),图像中的元素未完全居中对齐(这可能引起定位问题)。可以通过应用图像平移操作使图像具有平移不变性。
通过移动图像技术的应用,在不影响图像质量的前提下调整对象的位置以使模型更加多样化,并最终能够生成一个更为通用的模型
图像平移操作是一种空间位置变换过程,在该操作中,图像中的每一个对象都会按照一定的偏移量在其原始位置基础上进行重新定位。
在移位操作之后,输入图像中的位置(x,y)处的对象被移位到新位置(X,Y):
- X = x + dx
- Y = y + dy
在下面讨论中,请注意dx和dy分别属于沿不同维度的变化量。接下来,请我们来看看如何将shift应用到图像中去。
# 应用平移操作
transform = AffineTransform(translation=(25,25))
wrapShift = warp(image,transform,mode='wrap')
plt.imshow(wrapShift)
plt.title('Wrap Shift')

该超参数表示图像应移动的具体像素数值。在当前配置中,默认情况下将图像进行了(25,25)像素的平移操作。建议您根据具体情况灵活调整此关键超参数的取值范围。
当我再次应用"wrap"模式时,该模式通过将图像中的剩余像素填充值扩展至输入区域超出边界的部分.观察上述输出时会发现图像的高度和宽度各增加了25个像素.
翻转图像
翻转会拓展旋转的概念 。 这种操作让我们能够在垂直和水平方向上执行图像翻转。 下面我们将探讨如何通过编程来实现这一操作。
#flip image left-to-right
flipLR = np.fliplr(image)
plt.imshow(flipLR)
plt.title('Left to Right Flipped')

在处理图像时,我调用了NumPy的 fliplr 函数以实现水平翻转效果。该函数能够逐行调整像素值,并通过验证结果确认其正确性。同样地,在垂直方向上进行镜像反转:
# 上下翻转图像
flipUD = np.flipud(image)
plt.imshow(flipUD)
plt.title('Up Down Flipped')

这就是通过翻转图片来构建更具通用性的模型的方法,在这一方法中会训练出能够学习原始图片及其翻转版本的能力。 在增强过程中对图片加入随机噪声同样是一种有效的手段。 让我们通过一个实例来理解这一概念吧。
给图像添加噪点
图像去噪过程扮演着关键角色,并且是实现有效图像预处理的重要环节。它帮助模型学会区分信息与干扰,并在此过程中不断优化其特征提取能力。这进一步提升了模型在面对输入变化时的稳健性。
本研究中采用"skipage"库中的"random_noise"模块来引入适当的噪声干扰原始图像
请设定噪声的标准差为0.155(如需调整,请随意更改)。请注意,提高该参数会向图像注入更多随机性;相应地降低该参数则会减少图像的细节。
# 要添加到图像中的噪声的标准差
sigma=0.155
# 向图像添加随机噪声
noisyRandom = random_noise(image,var=sigma**2)
plt.imshow(noisyRandom)
plt.title('Random Noise')

通过观察分析可以看出,在原始图像中已经成功注入了随机噪声。我们可以探索不同标准差设置下的效果变化。
模糊图像
所有摄影爱好者都会立即理解这个想法。
同一幅图像可能源自不同的数据源。 因此,在实际应用中所获得的图像质量也会有所差异。 其中一些图像的质量可能会非常出色,但其他一些则可能难以达到预期的标准。
在这种情况下, 我们可以通过模糊处理来改善图像质量. 那么, 这可能会带来哪些好处呢? 说真的, 这对于提升深度学习模型的性能具有重要意义.
让我们看看我们如何做到这一点。 我们将使用高斯滤波器来模糊图像:
# 模糊图像
blurred = gaussian(image,sigma=1,multichannel=True)
plt.imshow(blurred)
plt.title('Blurred Image')

Sigma被视为高斯滤波器的标准差参数。我在配置中将其设定为1单位。当sigma值增大时,模糊效果也随之增强。通过启用Multichannel选项并设其为true,则能实现对图像每个通道独立进行过滤处理。
同样,您可以尝试使用不同的sigma值来更改模糊度。
这类图像增强技术手段对于提升深度学习模型的鲁棒性和扩展性具有重要意义。此外,它们能够有效地扩展训练数据集规模。
在本教程中,我们计划对图像处理模块进行全面优化与改进。在此之前,请我们深入研究一些基础准则来确定正确的图像增强技术。
选择正确的增强技术的基本准则
我认为,在决定增强技术之前就应明确您的目标。其中一些标准尤其值得重视。以下将为您介绍这些关键标准的基本要点:
- 任何模型构建过程的第一步都是确保输入的大小与模型所期望的大小相匹配。我们还必须确保所有图像的大小应该相似。为此,我们可以调整我们的图像到适当的大小。
- 假设您正在处理一个分类问题,并且样本数据量相对较少。在这种情况下,可以使用不同的增强技术,如图像旋转、图像噪声、翻转、移位等。请记住,所有这些操作都适用于对图像中对象位置无关紧要的分类问题。
- 如果您正在处理一个对象检测任务,其中对象的位置是我们要检测的,这些技术可能不合适。
- 图像像素值的标准化是保证模型更好更快收敛的一个很好的策略。如果模型有特定的要求,我们必须根据模型的要求对图像进行预处理。
目前无需再等待下去,请随我们深入进入模型构建阶段。我们将采用本文探讨的各种增强技术生成图像,并随后将利用这些图像来进行模型训练工作。
我们致力于研究关键性车辆与非关键性车辆的分类任务。如果您曾阅读过我的先前PyTorch相关文章,则对这一主题已有基本了解。
该项目的主要任务是将车辆图像划分为紧急与非紧急两类。您意识到这是一个图像分类问题吗?您可以通过以下链接下载数据集:这里
加载数据集
启动流程吧!我们将首先导入数据到notebook中。随后, 我们将采用图像增强技术来优化模型. 卷积神经网络(CNN)模型将在其中发挥作用.
让我们导入所需的库:
# 导入库
from torchsummary import summary
import pandas as pd
import numpy as np
from skimage.io import imread, imsave
from tqdm import tqdm
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from skimage.transform import rotate
from skimage.util import random_noise
from skimage.filters import gaussian
from scipy import ndimage
现在,我们将读取包含图像名称及其相应标签的CSV文件:
# 加载数据集
data = pd.read_csv('emergency_vs_non-emergency_dataset/emergency_train.csv')
data.head()

0标记该车为非紧急车辆、1标记该车为紧急车辆。请允许我们从数据集中加载所有图像。
# 加载图像
train_img = []
for img_name in tqdm(data['image_names']):
image_path = 'emergency_vs_non-emergency_dataset/images/' + img_name
img = imread(image_path)
img = img/255
train_img.append(img)
train_x = np.array(train_img)
train_y = data['emergency_or_not'].values
train_x.shape, train_y.shape

该数据集中包含1646张图像。我们决定将这些图像划分为训练集和验证集。通过验证集的评估结果可以看出,在面对未曾见过的新数据时,该模型的表现如何?
train_x, val_x, train_y, val_y = train_test_split(train_x, train_y, test_size = 0.1, random_state = 13, stratify=train_y)
(train_x.shape, train_y.shape), (val_x.shape, val_y.shape)

为了保证实验的一致性, 我将参数'test_size'固定为0.1, 这样其中10%的数据被随机选作验证集样本, 而其余90%则用于模型的训练过程。在本研究中使用的训练数据集包含了共计1481张图像, 就当前研究而言, 这可能是一个略显不足的数据规模。
在此基础上,随后我们计划执行这一操作:即使用这些新的训练图像用于构建更大的训练数据集,并这可能有助于提升模型的预测能力。
增强图像
我们将使用前面讨论过的图像增强技术:
final_train_data = []
final_target_train = []
for i in tqdm(range(train_x.shape[0])):
final_train_data.append(train_x[i])
final_train_data.append(rotate(train_x[i], angle=45, mode = 'wrap'))
final_train_data.append(np.fliplr(train_x[i]))
final_train_data.append(np.flipud(train_x[i]))
final_train_data.append(random_noise(train_x[i],var=0.2**2))
for j in range(5):
final_target_train.append(train_y[i])

针对训练集中的1481张图像的每一个体素上产出4张增强图像。
建议将这些增强后的图片组织成一个数组形式,并通过这种方式确保数据集的规模。
len(final_target_train), len(final_train_data)
final_train = np.array(final_train_data)
final_target_train = np.array(final_target_train)

这个结果验证了我们通过对图像进行提升以及扩充训练数据集所取得的进步。为了更好地观察效果,请对这些经过优化的图像进行详细分析。
fig,ax = plt.subplots(nrows=1,ncols=5,figsize=(20,20))
for i in range(5):
ax[i].imshow(final_train[i+30])
ax[i].axis('off')

在本研究中所使用的第一个图像源自数据集中的原始样本。其余四幅图像则通过应用多种图像增强手段包括:旋转、水平翻转、垂直翻转以及添加随机噪声等技术生成。
目前我们的数据集已经完成了准备工作。现在轮到我们来设计深度学习模型架构了,在经过增强处理后的训练数据集上进行模型训练。我们需要从PyTorch中导入所有必要的函数:
# PyTorch 库和模块
import torch
from torch.autograd import Variable
from torch.nn import Linear, ReLU, CrossEntropyLoss, Sequential, Conv2d, MaxPool2d, Module, Softmax, BatchNorm2d, Dropout
from torch.optim import Adam, SGD
我们必须将训练集和验证集转换为PyTorch格式:
# 将训练图像转换为torch格式
final_train = final_train.reshape(7405, 3, 224, 224)
final_train = torch.from_numpy(final_train)
final_train = final_train.float()
# 将target转换为torch格式
final_target_train = final_target_train.astype(int)
final_target_train = torch.from_numpy(final_target_train)
同样,我们将转换验证集:
# 将验证图像转换为torch格式
val_x = val_x.reshape(165, 3, 224, 224)
val_x = torch.from_numpy(val_x)
val_x = val_x.float()
# 将target转换为torch格式
val_y = val_y.astype(int)
val_y = torch.from_numpy(val_y)
模型结构
接下来, 我们将构建模型的架构. 这一过程相对复杂, 因为模型架构包括4个卷积模块, 然后是4个全连接层结构:
torch.manual_seed(0)
class Net(Module):
def __init__(self):
super(Net, self).__init__()
self.cnn_layers = Sequential(
# 定义2D convolution层
Conv2d(3, 32, kernel_size=3, stride=1, padding=1),
ReLU(inplace=True),
# 添加batch normalization层
BatchNorm2d(32),
MaxPool2d(kernel_size=2, stride=2),
# 添加 dropout
Dropout(p=0.25),
# 定义另一个2D convolution层
Conv2d(32, 64, kernel_size=3, stride=1, padding=1),
ReLU(inplace=True),
# 添加batch normalization层
BatchNorm2d(64),
MaxPool2d(kernel_size=2, stride=2),
# 添加 dropout
Dropout(p=0.25),
# 定义另一个2D convolution层
Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
ReLU(inplace=True),
# 添加batch normalization层
BatchNorm2d(128),
MaxPool2d(kernel_size=2, stride=2),
# 添加 dropout
Dropout(p=0.25),
# 定义另一个2D convolution层
Conv2d(128, 128, kernel_size=3, stride=1, padding=1),
ReLU(inplace=True),
# 添加batch normalization层
BatchNorm2d(128),
MaxPool2d(kernel_size=2, stride=2),
# 添加 dropout
Dropout(p=0.25),
)
self.linear_layers = Sequential(
Linear(128 * 14 * 14, 512),
ReLU(inplace=True),
Dropout(),
Linear(512, 256),
ReLU(inplace=True),
Dropout(),
Linear(256,10),
ReLU(inplace=True),
Dropout(),
Linear(10,2)
)
# 定义前向过程
def forward(self, x):
x = self.cnn_layers(x)
x = x.view(x.size(0), -1)
x = self.linear_layers(x)
return x
让我们定义模型的其他超参数,包括优化器、学习率和损失函数:
# defining the model
model = Net()
# defining the optimizer
optimizer = Adam(model.parameters(), lr=0.000075)
# defining the loss function
criterion = CrossEntropyLoss()
# checking if GPU is available
if torch.cuda.is_available():
model = model.cuda()
criterion = criterion.cuda()
print(model)

训练模型
为我们的深度学习模型训练20个epoch:
torch.manual_seed(0)
# 模型的batch size
batch_size = 64
# 训练模型的epoch数
n_epochs = 20
for epoch in range(1, n_epochs+1):
train_loss = 0.0
permutation = torch.randperm(final_train.size()[0])
training_loss = []
for i in tqdm(range(0,final_train.size()[0], batch_size)):
indices = permutation[i:i+batch_size]
batch_x, batch_y = final_train[indices], final_target_train[indices]
if torch.cuda.is_available():
batch_x, batch_y = batch_x.cuda(), batch_y.cuda()
optimizer.zero_grad()
outputs = model(batch_x)
loss = criterion(outputs,batch_y)
training_loss.append(loss.item())
loss.backward()
optimizer.step()
training_loss = np.average(training_loss)
print('epoch: \t', epoch, '\t training loss: \t', training_loss)

在训练阶段产生的总结报告中可以看出,在 epoch 数不断增加的过程中, training loss 减少了。建议将当前模型参数进行保存,在无需重新进行模型训练的情况下能够方便地调用这些参数以继续后续工作。
torch.save(model, 'model.pt')
如果您的终端不在本地环境中进行模型训练,则可以通过此链接获取一个已经经过20个 epoch 训练完成的模型参数资源。
接下来,让我们加载这个模型:
the_model = torch.load('model.pt')
测试我们模型的性能
最后,让我们对训练集和验证集进行预测,并检查各自的准确度:
torch.manual_seed(0)
# 预测训练集
prediction = []
target = []
permutation = torch.randperm(final_train.size()[0])
for i in tqdm(range(0,final_train.size()[0], batch_size)):
indices = permutation[i:i+batch_size]
batch_x, batch_y = final_train[indices], final_target_train[indices]
if torch.cuda.is_available():
batch_x, batch_y = batch_x.cuda(), batch_y.cuda()
with torch.no_grad():
output = model(batch_x.cuda())
softmax = torch.exp(output).cpu()
prob = list(softmax.numpy())
predictions = np.argmax(prob, axis=1)
prediction.append(predictions)
target.append(batch_y)
# 训练准确度
accuracy = []
for i in range(len(prediction)):
accuracy.append(accuracy_score(target[i].cpu(),prediction[i]))
print('training accuracy: \t', np.average(accuracy))

训练集的正确率达到91%,表现非常出色。然而,在期待下一步进展的同时,请保持关注。为了确保评估的一致性,请对验证集进行同样的评估。
# 预测验证集
torch.manual_seed(0)
output = model(val_x.cuda())
softmax = torch.exp(output).cpu()
prob = list(softmax.detach().numpy())
predictions = np.argmax(prob, axis=1)
accuracy_score(val_y, predictions)

验证准确性约为78%。 很好!
尾注
当我们开始获得的训练数据较少时,我们可以使用图像增强技术。
在本文中阐述了主要的图像增强方法。
我们掌握了如何旋转、平移以及翻转操作。
对图像加入随机噪声或使其变得模糊。
探讨了选择恰当增强技术的标准依据。
对于任意的图像分类问题而言,请您建议在各种图像分类问题中应用这些图像增强技术,并对比分析采用与未采用图像增强技术时的性能表现。请随时在下面评论区分享您的实验结果
而且,在缺乏相关知识的情况下(尤其是关于深度学习、计算机视觉以及图像数据的知识),我们强烈推荐您参加以下课程的学习
由 Analytics Vidhya 发布的文章讨论了图像增强在深度学习中的实现及其在 PyTorch 中的应用
欢迎关注磐创AI博客站: http://panchuang.net/
sklearn机器学习中文官方文档: http://sklearn123.com/
欢迎关注磐创博客资源汇总站: http://docs.panchuang.net/
