PyTorch - 35 - PyTorch数据集标准化 - Torchvision.Transforms.Normalize()

Data Normalization

数据规范化的概念是一个通用概念,指的是将数据集的原始值转换为新值的行为。 新值通常相对于数据集本身进行编码,并以某种方式缩放。

Feature Scaling

因此,有时使用的数据标准化的另一个名称是特征缩放。该术语指的是以下事实:在对数据进行规范化时,我们经常将给定数据集的不同特征转换为相似的比例。

在这种情况下,我们不仅在考虑值的数据集,还在考虑具有多个特征(每个都有其on值)的元素的数据集。

例如,假设我们正在处理一个人的数据集,并且我们的数据集中有两个相关特征,即年龄和体重。在这种情况下,我们可以观察到这两个特征集的大小或比例是不同的,即平均权重大于年龄。

在比较或使用机器学习算法进行计算时,大小上的差异可能会成为问题。因此,这可能是我们可能希望通过特征缩放将这些特征的值缩放到某个类似比例的原因之一。

Normalize A Dataset In Code

让我们跳入一个代码示例。第一步是初始化我们的数据集,因此在本示例中,我们将使用到目前为止在该系列中一直使用的Fashion MNIST数据集。

train_set = torchvision.datasets.FashionMNIST(
    root='./data'
    ,train=True
    ,download=True
    ,transform=transforms.Compose([
        transforms.ToTensor()
    ])
)

通过将每个颜色通道的均值和标准差值传递给Normalize()变换,PyTorch允许我们使用刚刚看到的标准化过程来标准化数据集。

torchvision.transforms.Normalize(
      [meanOfChannel1, meanOfChannel2, meanOfChannel3] 
    , [stdOfChannel1, stdOfChannel2, stdOfChannel3] 
)

由于数据集中的图像只有一个通道,因此我们只需要传递均值和标准差值即可。为此,我们需要首先计算这些值。有时,这些值可能会在线发布到某个地方,因此我们可以通过这种方式获取它们。但是,如果有疑问,我们可以手动计算。

有两种方法可以完成。简单的方法,更困难的方法。如果数据集足够小以一次全部放入内存,则可以轻松实现。否则,我们必须遍历稍难的数据。

Calculating mean And std The Easy Way

简单的方法很容易。 我们要做的就是使用数据加载器加载数据集,并获得包含所有数据的单个批处理张量。 为此,我们将批次大小设置为等于训练集长度。

loader = DataLoader(train_set, batch_size=len(train_set), num_workers=1)
data = next(iter(loader))
data[0].mean(), data[0].std()

(tensor(0.2860), tensor(0.3530))

在这里,我们可以简单地使用相应的PyTorch张量方法获得均值和标准差值。

Calculating mean And std The Hard Way

困难的方法之所以困难,是因为我们需要手动实现均值和标准差的公式,并对较小的数据集进行迭代。

首先,我们创建一个具有较小批处理大小的数据加载器。

loader = DataLoader(train_set, batch_size=1000, num_workers=1)

然后,我们计算我们的值或像素总数:

num_of_pixels = len(train_set) * 28 * 28

请注意,28 * 28是数据集中的图像的高度和宽度。现在,我们通过遍历每批像素求和像素值,并用该总和除以像素总数来计算平均值。

total_sum = 0
for batch in loader: total_sum += batch[0].sum()
mean = total_sum / num_of_pixels

接下来,我们通过遍历每批来计算平方误差的总和,这使我们能够通过将平方误差的总和除以像素总数并求平方根来计算标准差。

sum_of_squared_error = 0
for batch in loader: 
    sum_of_squared_error += ((batch[0] - mean).pow(2)).sum()
std = torch.sqrt(sum_of_squared_error / num_of_pixels)

这给我们:

mean, std
(tensor(0.2860), tensor(0.3530))

Using The mean And std Values

我们的任务是使用这些值将数据集中的像素值转换为相应的标准化值。为此,我们仅在这次将规范化转换传递到转换组合时才创建一个新的train_set。

train_set_normal = torchvision.datasets.FashionMNIST(
    root='./data'
    ,train=True
    ,download=True
    ,transform=transforms.Compose([
          transforms.ToTensor()
        , transforms.Normalize(mean, std)
    ])
)

请注意,转换的顺序在合成内很重要。图像作为Python PIL对象加载,因此由于Normalize()转换需要张量作为输入,因此必须在Normalize()转换之前添加ToTensor()转换。

现在,我们的数据集具有Normalize()变换,当数据由数据加载器加载时,数据将被归一化。请记住,对于每个图像,以下变换将应用于图像中的每个像素。

这具有相对于数据集的平均值和标准偏差重新缩放我们的数据的效果。让我们通过重新计算这些值来看看实际情况。

loader = DataLoader(
      train_set_normal
    , batch_size=len(train_set)
    , num_workers=1
)
data = next(iter(loader))
data[0].mean(), data[0].std()

(tensor(1.2368e-05), tensor(1.0000))

Training With Normalized Data

现在让我们看看使用和不使用标准化数据的训练如何影响训练过程。为了进行此测试,我们将在每种条件下进行20个纪元。

让我们创建一个训练集字典,我们可以使用它在整个课程中构建的框架中运行测试。

trainsets = {
    
    
    'not_normal': train_set
    ,'normal': train_set_normal
}

现在,我们可以将这两个train_sets添加到我们的配置中,并在运行循环中访问值。

params = OrderedDict(
      lr = [.01]
    , batch_size = [1000]
    , num_workers = [1]
    , device = ['cuda']
    , trainset = ['not_normal', 'normal']
)
m = RunManager()
for run in RunBuilder.get_runs(params):

    device = torch.device(run.device)
    network = Network().to(device)
    loader = DataLoader(
          trainsets[run.trainset]
        , batch_size=run.batch_size
        , num_workers=run.num_workers
    )
    optimizer = optim.Adam(network.parameters(), lr=run.lr)

    m.begin_run(run, network, loader)
    for epoch in range(20):
        m.begin_epoch()
        for batch in loader:

            images = batch[0].to(device)
            labels = batch[1].to(device)
            preds = network(images) # Pass Batch
            loss = F.cross_entropy(preds, labels) # Calculate Loss
            optimizer.zero_grad() # Zero Gradients
            loss.backward() # Calculate Gradients
            optimizer.step() # Update Weights

            m.track_loss(loss, batch)
            m.track_num_correct(preds, labels)
        m.end_epoch()
    m.end_run()
m.save('results')

另外,您听说过批量标准化吗? 我们将批处理规范化或批处理规范化是在网络层内部在每个层的输出激活上执行的相同过程。 酷吧? 下一个见!

猜你喜欢

转载自blog.csdn.net/weixin_48367136/article/details/112561050