神经网络学习笔记(8)- 图像预处理
CNN(卷积神经网络)主要用于图像的训练。在介绍CNN之前,首先面临的问题是,如何来表示图像,以及有哪些预处理操作,能帮助更有效地进行CNN训练。
图像的Tensor表示
一张图可以表示为一个像素矩阵。我们知道计算机表示彩色图像的三原色:RGB,代表红、绿、蓝三个通道。它们的值域都为0~255。任何颜色都可以表示为这三个通道的组合。如(255,0,0)表示为纯红,(0,255,0)表示为纯绿。
通常,在训练CNN前,需要把这三个通道的值映射到-1~1的区域,以便能更高效地进行训练。
有时,色彩的信息并不重要,为了简化模型,则只需要一个灰度通道,即每个像素均对应一个0~1的值。
因此,一张2维图像,可以用3维的Tensor来表示。图像的长宽分别占2维,最后一维是颜色通道。灰度图像的第3维长度为1,彩色图像的第3维长度为3。
在深度神经网络,通常一次性批量地输入数据,因此一次输入的数据维度为4,代表图像的索引维。当然,一次训练中的图像必须在其他3维上满足相同的尺寸。
预处理
图像的处理一般遵循以下的顺序,当然也并非每一步都是必要的。
裁减 大多数训练模型是基于正方形的图片。对于不规整的原始图片,需要进行裁减成模型所需的图像比例。
调整尺寸 模型对像素的个数也会有要求,需将图像缩放成所需的像素尺寸。
数据增强 为了增强模型的鲁棒性,可以通过一些基本的图片变换操作,如缩放、旋转等,生成新的样本。这能有助于缓解模型的过拟合。
标准化 对所有像素进行统计学上的标准化操作(减去均值后除以标准差)。这能保证所有图像具有类似的像素分布(标准正态分布)。
降维 可以将RGB表示的彩色图片转化为只有一个通道的灰度图片。
白化 白化能使特征之间降低相关性。常用的有PCA和ZCA方法。
示例:数据的矩阵格式转换、标准化和白化算法
import numpy as np
X = data
#数据源为10000张图片,每张图是3x32x32=3072的一维表示。 X.shape = (10000, 3072)。
#而模型需要的格式为(样本数,32x32x3),注意到颜色通道的维度的顺序不同。
#因为数据源将所有图片信息放在一维列表里,需要先reshape成(10000, 3, 32, 32)。可以有一维为-1,表示自适应。
X = X.reshape((-1, 3, 32, 32))
#转置成(10000, 32, 32, 3)的顺序
X = X.transpose(0, 2, 3, 1)
#再次把剩下的3维压成1维
X = X.reshape(-1, 3 * 32 * 32)
#标准化
X = X - X.mean(axis=0)
X = X / np.std(X, axis=0)
#白化ZCA算法
X_subset = X[:1000] #取前1000个样本
cov = np.cov(X_subset, rowvar=True) #计算协方差矩阵。cov.shape=(1000, 1000)
U, S, V = np.linalg.svd(cov) #奇异值分解 尺寸分别为:(1000, 1000), (1000,), (1000, 1000)
epsilon = 1e-5
zca_matrix = np.dot(U, np.dot(np.diag(1.0 / np.sqrt(S + epsilon)), U.T))#尺寸为(1000, 1000)
zca = np.dot(zca_matrix, X_subset) #zca结果与原样本尺寸相同 (1000, 3072)
示例:用PyTorch处理数据集CIFAR10。包括对batch化的大数据样本进行裁减、缩放、数据增强、标准化等。
import torch
import torchvision
import torchvision.transforms as transforms
#计算所有样本的均值和标准差
transform = transforms.Compose([
transforms.Resize(256),
transforms.ToTensor()
])
dataset = torchvision.datasets.CIFAR10(root='./datasets/cifar10/train', download=True, transform=transform)
dataloader = torch.utils.data.DataLoader(dataset,
batch_size=16,
shuffle=True,
num_workers=2)
pop_mean = []
pop_std = []
for i, data in enumerate(dataloader, 0):
# shape (batch_size, 3, height, width)
numpy_image = data[0].numpy()
#聚合0,2,3维 => shape (3,)
batch_mean = np.mean(numpy_image, axis=(0, 2, 3))
batch_std = np.std(numpy_image, axis=(0, 2, 3))
pop_mean.append(batch_mean)
pop_std.append(batch_std)
pop_mean = np.array(pop_mean)
pop_std = np.array(pop_std)
# shape (迭代次数, 3) -> 在第0维上聚合 -> shape (3,)
pop_mean = pop_mean.mean(axis=0)
pop_std = pop_std.mean(axis=0) #注:这里将每个batch的标准差计算均值作为总体的标准差,并非精确,仅为近似值。
#进行实际转换。注意到ToTensor后才能标准化,与上面计算均值与方差的过程相同。但是标准化后,必定会产生负数,换句话说,该图像无法被直接显示。
transform = transforms.Compose([
transforms.Resize(256), #缩放
transforms.RandomResizedCrop(224),#截取
transforms.ColorJitter(), #变色(数据增强)
transforms.RandomHorizontalFlip(),#翻折(数据增强)
transforms.ToTensor(), #伴随着映射到0~1区间
transforms.Normalize(pop_mean, #标准化
pop_std)
])
trainset = torchvision.datasets.CIFAR10(root='./datasets/cifar10/train', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=16,
shuffle=True, num_workers=2)
images_batch, labels_batch = iter(trainloader).next()