基于PyTorch图像特征工程的深度学习图像加强

介绍

在深度学习黑客竞赛中表现出色的技巧(或者坦率地说,是任何数据科学黑客竞赛) 一般归结为特征工程。 当您得到的数据不足以创建一个成功的深度学习模型时,你能发挥多少创造力?python

我是根据本身参加屡次深度学习黑客竞赛的经验而谈的,在此次深度黑客竞赛中,咱们得到了包含数百张图像的数据集——根本不足以赢得甚至完成排行榜的顶级排名。那咱们怎么处理这个问题呢?数组

答案? 好吧,那要看数据科学家的技能了! 这就是咱们的好奇心和创造力脱颖而出的地方。 这就是特征工程背后的理念——在现有特征的状况下,咱们能多好地提出新特征。当咱们处理图像数据时,一样的想法也适用。网络

这就是图像加强的主要做用。这一律念不只仅局限于黑客竞赛——咱们在工业和现实世界中 深度学习模型项目中都使用了它!

image_augmentation
图像加强功能帮助我扩充现有数据集,而无需费时费力。 并且我相信您会发现这项技术对您本身的项目很是有帮助。app

所以,在本文中,咱们将了解图像加强的概念,为什么有用以及哪些不一样的图像加强技术。 咱们还将实现这些图像加强技术,以使用PyTorch构建图像分类模型。dom

目录

  1. 为何须要图像加强?
  2. 不一样的图像加强技术
  3. 选择正确的加强技术的基本准则
  4. 案例研究:使用图像加强解决图像分类问题

为何须要图像加强?

深度学习模型一般须要大量的数据来进行训练。一般,数据越多,模型的性能越好。可是获取海量数据面临着自身的挑战。不是每一个人都有大公司的雄厚财力。机器学习

缺乏数据使得咱们的深度学习模型可能没法从数据中学习模式或功能,所以在未见过的数据上可能没法提供良好的性能。ide

那么在那种状况下咱们该怎么办?咱们可使用图像加强技术,而无需花费几天的时间手动收集数据。函数

图像加强是生成新图像以训练咱们的深度学习模型的过程。这些新图像是使用现有的训练图像生成的,所以咱们没必要手动收集它们。

Image-Augmentation
有多种图像加强技术,咱们将在下一节讨论一些常见的和使用最普遍的技术。性能

不一样的图像加强技术

图像旋转

图像旋转是最经常使用的加强技术之一。它能够帮助咱们的模型对对象方向的变化变得健壮。即便咱们旋转图像,图像的信息也保持不变。汽车就是一辆汽车,即便咱们从不一样的角度看它:学习

Screenshot-from-2019-11-26-13-05-26

所以,咱们可使用此技术,经过从原始图像建立旋转图像来增长数据量。让咱们看看如何旋转图像:

# 导入全部必需的库
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)

Screenshot-from-2019-11-26-13-09-10

这是原始图像。如今让咱们看看如何旋转它。我将使用skimage 库的旋转功能来旋转图像:

print('Rotated Image')
#rotating the image by 45 degrees
rotated = rotate(image, angle=45, mode = 'wrap')
#plot the rotated image
io.imshow(rotated)

Screenshot-from-2019-11-26-13-10-26

很好!将模式设置为“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')

Screenshot-from-2019-11-26-13-17-05

translation超参数定义图像应移动的像素数。这里,我把图像移了(25,25)个像素。您能够随意设置此超参数的值。

我再次使用“wrap”模式,它用图像的剩余像素填充输入边界以外的点。在上面的输出中,您能够看到图像的高度和宽度都移动了25像素。

翻转图像

翻转是旋转的延伸。 它使咱们能够在左右以及上下方向上翻转图像。 让咱们看看如何实现翻转:

#flip image left-to-right
flipLR = np.fliplr(image)


plt.imshow(flipLR)
plt.title('Left to Right Flipped')

Screenshot-from-2019-11-26-13-23-12

在这里,我使用了NumPy的fliplr 函数从左向右翻转图像。 它翻转每一行的像素值,而且输出确认相同。 相似地,咱们能够沿上下方向翻转图像:

# 上下翻转图像
flipUD = np.flipud(image)


plt.imshow(flipUD)
plt.title('Up Down Flipped')

Screenshot-from-2019-11-26-13-24-51

这就是咱们能够翻转图像并制做更通用的模型的方法,该模型将学习到原始图像以及翻转后的图像。 向图像添加随机噪声也是图像加强技术。 让咱们经过一个例子来理解它。

给图像添加噪点

图像噪声是一个重要的加强步骤,使咱们的模型可以学习如何分离图像中的信号和噪声。这也使得模型对输入的变化更加健壮。

咱们将使用“skipage”库的“random_noise”函数为原始图像添加一些随机噪声

我将噪声的标准差取为0.155(您也能够更改此值)。请记住,增长此值将为图像添加更多噪声,反之亦然:

# 要添加到图像中的噪声的标准差
sigma=0.155
# 向图像添加随机噪声
noisyRandom = random_noise(image,var=sigma**2)


plt.imshow(noisyRandom)
plt.title('Random Noise')

Screenshot-from-2019-11-26-13-31-00

咱们能够看到随机噪声已添加到原始图像中。 试一下不一样的标准误差的值,看看获得的不一样结果。

模糊图像

全部摄影爱好者都会当即理解这个想法。

图像有不一样的来源。 所以,每一个来源的图像质量都将不一样。 有些图像的质量可能很高,而另外一些则可能不好劲。

在这种状况下,咱们可使图像模糊。 那将有什么帮助? 好吧,这有助于使咱们的深度学习模型更强大。

让咱们看看咱们如何作到这一点。 咱们将使用高斯滤波器来模糊图像:

# 模糊图像
blurred = gaussian(image,sigma=1,multichannel=True)


plt.imshow(blurred)
plt.title('Blurred Image')

Screenshot-from-2019-11-26-13-34-39

Sigma是高斯滤波器的标准差。我将其视为1。sigma值越高,模糊效果越强。 将* Multichannel *设置为true可确保分别过滤图像的每一个通道。

一样,您能够尝试使用不一样的sigma值来更改模糊度。

这些是一些图像加强技术,有助于使咱们的深度学习模型健壮且可推广。这也有助于增长训练集的大小。

咱们即将完成本教程的实现部分。在此以前,让咱们看看一些基本的准则,以决定正确的图像加强技术。

选择正确的加强技术的基本准则

我认为在根据您试图解决的问题来决定加强技术时,有一些准则是很重要的。如下是这些准则的简要概述:

  1. 任何模型构建过程的第一步都是确保输入的大小与模型所指望的大小相匹配。咱们还必须确保全部图像的大小应该类似。为此,咱们能够调整咱们的图像到适当的大小。
  2. 假设您正在处理一个分类问题,而且样本数据量相对较少。在这种状况下,可使用不一样的加强技术,如图像旋转、图像噪声、翻转、移位等。请记住,全部这些操做都适用于对图像中对象位置可有可无的分类问题。
  3. 若是您正在处理一个对象检测任务,其中对象的位置是咱们要检测的,这些技术可能不合适。
  4. 图像像素值的标准化是保证模型更好更快收敛的一个很好的策略。若是模型有特定的要求,咱们必须根据模型的要求对图像进行预处理。

如今,不用再等了,让咱们继续到模型构建部分。咱们将应用本文讨论的加强技术生成图像,而后使用这些图像来训练模型。

咱们将研究紧急车辆与非紧急车辆的分类问题。若是你看过我之前的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()

Screenshot-from-2019-11-27-13-04-48

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

Screenshot-from-2019-11-27-13-06-04

数据集中共有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)

Screenshot-from-2019-11-27-14-00-22

我将“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])

Screenshot-from-2019-11-27-14-03-14

咱们为训练集中的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)

Screenshot-from-2019-11-27-14-05-39

这证明了咱们已经加强了图像并增长了训练集的大小。让咱们将这些加强图像进行可视化:

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')

Screenshot-from-2019-11-27-14-06-46

这里的第一个图像是来自数据集的原始图像。其他四幅图像分别使用不一样的图像加强技术(旋转、从左向右翻转、上下翻转和添加随机噪声)生成的。

咱们的数据集如今已经准备好了。是时候定义咱们的深度学习模型的结构,而后在加强过的训练集上对其进行训练了。咱们先从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)

Screenshot-from-2019-11-27-14-24-41

训练模型

为咱们的深度学习模型训练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)

Screenshot-from-2019-11-27-14-26-37

这是训练阶段的summary。你会注意到,随着epoch的增长,训练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))

Screenshot-from-2019-11-27-14-31-21

训练集的准确率超过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)

Screenshot-from-2019-11-27-14-32-41
验证准确性约为78%。 很好!

尾注

当咱们开始得到的训练数据较少时,咱们可使用图像加强技术。

在本文中,咱们介绍了大多数经常使用的图像加强技术。 咱们学习了如何旋转,移动和翻转图像。 咱们还学习了如何为图像添加随机噪声或使其模糊。 而后,咱们讨论了选择正确的加强技术的基本准则。

您能够在任何图像分类问题上尝试使用这些图像加强技术,而后比较使用加强和不使用加强的性能。 随时在下面的评论部分中分享您的结果。

并且,若是您不熟悉深度学习,计算机视觉和图像数据,那么建议您完成如下课程:

原文连接:https://www.analyticsvidhya.c...

欢迎关注磐创AI博客站:
http://panchuang.net/

sklearn机器学习中文官方文档:
http://sklearn123.com/

欢迎关注磐创博客资源汇总站:
http://docs.panchuang.net/

相关文章
相关标签/搜索