草庐IT

深度学习炼丹-数据标准化

armcvai 2023-03-28 原文

前言

一般机器学习任务其工作流程可总结为如下所示 pipeline

在工业界,数据预处理步骤对模型精度的提高的发挥着重要作用。对于机器学习任务来说,广泛的数据预处理一般有四个阶段(视觉任务一般只需 Data Transformation): 数据清洗(Data Cleaning)、数据整合(Data Integration)、数据转换(Data Transformation)和数据缩减(Data Reduction)。

1,Data Cleaning: 数据清理是数据预处理步骤的一部分,通过填充缺失值、平滑噪声数据、解决不一致和删除异常值来清理数据。

2,Data Integration: 用于将存在于多个源中的数据合并到一个更大的数据存储中,如数据仓库。例如,将来自多个医疗节点的图像整合起来,形成一个更大的数据库。

3,Data Transformation : 在完成 Data Cleaning 后,我们需要使用以下数据转换策略更改数据的值、结构或格式。

  • Generalization: 使用概念层次结构将低级或粒度数据转换为高级信息。例如将城市等地址中的原始数据转化为国家等更高层次的信息。
  • Normalization: 目的是将数字属性按比例放大或缩小以适合指定范围。Normalization 常见方法:
    • Min-max normalization:将数据映射到 \([0,1]\) 区间。
    • Z-Score normalization:把每个特征值中的所有数据,变成平均值为0,标准差为1的数据,最后为正态分布。
    • Decimal scaling normalization

4,Data Reduction 数据仓库中数据集的大小可能太大而无法通过数据分析和数据挖掘算法进行处理。一种可能的解决方案是获得数据集的缩减表示,该数据集的体积要小得多,但会产生相同质量的分析结果。常见的数据缩减策略如下:

  • Data cube aggregation
  • Dimensionality reduction: 降维技术用于执行特征提取。数据集的维度是指数据的属性或个体特征。该技术旨在减少我们在机器学习算法中考虑的冗余特征的数量。降维可以使用主成分分析(PCA)等技术来完成。
  • Data compression: 通过使用编码技术,数据的大小可以显着减小。
  • Discretization: 数据离散化用于将具有连续性的属性划分为具有区间的数据。这样做是因为连续特征往往与目标变量相关的可能性较小。例如,属性年龄可以离散化为 18 岁以下、18-44 岁、44-60 岁、60 岁以上等区间。

对于计算机视觉任务来说,在训练 CNN 模型之前,对于输入样本特征数据做标准化normalization,也叫归一化)预处理data preprocessing)操作是最常见的步骤。

一,Normalization 概述

后续内容对 Normalization 不再使用中文翻译,是因为目前中文翻译有些歧义,根据我查阅的博客资料,翻译为“归一化”比较多,但仅供可参考。

1.1,Normalization 定义

Normalization 操作被用于对数据属性进行缩放,使其落在较小的范围之内(即变化到某个固定区间中),比如 [-1,1] 和 [0, 1],简单理解就是特征缩放过程。很多机器学习算法都受益于 Normalization 操作,比如:

  • 通常对分类算法有用。
  • 在梯度下降等机器学习算法的核心中使用的优化算法很有用。
  • 对于加权输入的算法(如回归和神经网络)以及使用距离度量的算法(如 K 最近邻)也很有用。

1.2,什么情况需要 Normalization

当我们处理的数据具有不同尺度(范围)different scale)时,通常就需要进行 normalization 操作了。

数据具有不同尺度的情况会导致一个重要属性(在较低尺度上)的有效性被稀释,因为其他属性可能具有更大范围(尺度)的值,简单点理解就是范围(scale)大的属性在模型当中更具优先级,具体示例如下图所示。

总结起来就是,当数据存在多个属性但其值具有不同尺度(scale)时,这可能会导致我们在做数据挖掘操作时数据模型表现不佳,因此这时候执行 normalization 操作将所有属性置于相同的尺寸内是很有必要的。

1.3,为什么要做 Normalization

  1. 样本的各个特征的取值要符合概率分布,即 \([0,1]\)(也可理解为降低模型训练对特征尺度的敏感度)。输入数据特征取值范围和输出标签范围一样,从损失函数等高线图来分析,不做 Normalization 的训练过程会更曲折。

  2. 神经网络假设所有的输入输出数据都是标准差为1,均值为0,包括权重值的初始化,激活函数的选择,以及优化算法的设计。

  3. 避免一些不必要的数值问题

    因为激活函数 sigmoid/tanh 的非线性区间大约在 [−1.7,1.7]。意味着要使神经元的激活函数有效,线性计算输出的值的数量级应该在1(1.7所在的数量级)左右。这时如果输入较大,就意味着权值必须较小,一个较大,一个较小,两者相乘,就引起数值问题了。

  4. 梯度更新

    若果输出层的数量级很大,会引起损失函数的数量级很大,这样做反向传播时的梯度也就很大,这时会给梯度的更新带来数值问题。

  5. 学习率

    特征数据数值范围不同,正确的梯度更新方向需要的学习率也会不同(如果梯度非常大,学习率就必须非常小),即不同神经元权重 \(w_1\)\(w_2\) 所需的学习率也不同。因此,学习率(学习率初始值)的选择需要参考输入的范围,这样不如直接将数据标准化,这样学习率就不必再根据数据范围作调整

1.4,Data Normalization 常用方法

1,z-Score Normalization

zero-mean Normalization,有时也称为 standardization,将数据特征缩放成均值为 0,方差为 1 的分布,对应公式:

\[{x}' = \frac{x-mean(x)}{\sigma} \]

其中:

  • \(x\):原始值
  • \(mean(x)\):表示变量 \(x\) 的均值(有些地方用 \(\mu =\frac{1}{N}\sum_{i=1}^{N} x_i\)
  • \(\sigma\): 表示变量的标准差(总体标准差数学定义 \(\sigma = \sqrt{\frac{1}{N} \sum_{i=1}^{N}(x_i - \mu)^2}\)
  • \({x}'\) 是数据缩放后的新值

经过 zero-mean Normalization 操作之后的数据正态分布函数曲线图会产生如下所示转换。

其中,正态分布函数曲线中均值和标准差值的确定参考下图。

2,Min-Max Normalization

执行线性操作,将数据范围缩放到 \([0,1]\) 区间内,对应公式:

\[{x}' = \frac{x - min(x)}{max(x) - min(x)} \]

其中 \(max(x)\) 是变量最大值,\(min(x)\) 是变量最小值。

1.5,代码实现

z-Score Normalization 方法既可以调用相关 python 库的 api 实现,也可以自己实现相关功能。

以下是使用 sklearn 相关类和基于 numpy 库实现 z-Score Normalization 功能,并给出数据直方图对比的示例代码。

代码不是特别规范,仅供参考,当作功能理解和实验测试用。

## 输出高清图像
%config InlineBackend.figure_format = 'retina'
%matplotlib inline
import numpy as np
from sklearn import preprocessing
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns

np.random.seed(42)

plt.figure(dpi = 200)
plt.figure(figsize=(20, 15))
# X_train = np.array([[ 10., -1.7,  21.4],
#                     [ 2.4,  0.,  0.6],
#                     [ 0.9,  1., -1.9]])
# 生成指定 size 和 范围 [low,high) 的随机浮点数
X_train = np.random.uniform(low=0.0, high=100.0, size = (100, 15))
# 1, 绘制原始数据的直方图
plt.subplot(3, 1, 1)
plt.title("original data distribution")
sns.distplot(X_train, color='y')

# 2, 应用 sklearn 库的 z-Score Normalization 类,并绘制直方图
scaler = preprocessing.StandardScaler().fit(X_train)
X_scaled = scaler.transform(X_train)
plt.subplot(3, 1, 2)
plt.title("z-Score Normalization by sklearn")
sns.distplot(X_scaled, color='r')

# 3,利用 numpy 函数实现 z-Score Normalization,并绘制直方图
def z_Score_Normalization(data):
    data_mat = np.array(data)
    data_z_np = (data_mat - np.mean(data_mat, axis=0)) / np.std(data_mat, axis=0)
    return data_z_np

data_scaled = z_Score_Normalization(X_train)
plt.subplot(3, 1, 3)
plt.title("z-Score Normalization by numpy")
sns.distplot(data_scaled, color='g')

程序输出结果如下。可以看出经过 z-Score Normalization 操作之后,原始数据的分布转化成平均值为 \(\mu=0\),标准差为 \(\sigma = 1\) 的正太分布(称为标准正态分布)。

Min-Max Normalization 方法的实现比较简单,以下是基于 numpy 库实现 Min-Max Normalization 功能的示例代码:

# 导入必要的库
import numpy as np
# 定义数据集
X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
def Min_Max_Normalization(X):
    # 计算数据集的最小值和最大值
    Xmin = X.min()
    Xmax = X.max()
    # 计算最小-最大规范化
    X_norm = (X - Xmin) / (Xmax - Xmin)
    return X_norm
# 打印结果
print(Min_Max_Normalization(X))

程序输出结果如下,可以看出原始数组数据都被缩放到 \([0, 1]\) 范围内了。

二,normalize images

2.1,图像 normalization 定义

当我们使用卷积神经网络解决计算机视觉任务时,一般需要对输入图像数据做 normalization 来完成预处理工作,常见的图像 normalization 方法有两种: min-max normalizationzero-mean normalization

1,以单张图像的 zero-mean Normalization 为例,它使得图像的均值和标准差分别变为 0.01.0。因为是多维数据,与纯表格数据不同,它首先需要从每个输入通道中减去通道平均值,然后将结果除以通道标准差。因此可定义两种 normalization 形式如下所示:

# min-max Normalization
output[channel] = (input[channel] - min[channel]) / (max[channel] - min[channel])
# zero-mean Normalization
output[channel] = (input[channel] - mean[channel]) / std[channel]

2.2,图像 normalization 的好处

图像 normalization 有助于使数据处于一定范围内并减少偏度skewness),从而有助于模型更快更好地学习。归一化还可以解决梯度递减和爆炸的问题。

2.3,PyTorch 实践图像 normalization

Pytorch 框架中,图像变换(image transformation)是指将图像像素的原始值改变为新值的过程。其中常见的 transformation 操作是使用 torchvision.transforms.ToTensor() 方法将图像变换为 Pytorch 张量(tensor),它实现了将像素范围为 [0, 255] 的 PIL 图像转换为形状为(C,H,W)且范围为 [0.0, 1.0] 的 Pytorch FloatTensor。另外,torchvision.transforms.normalize() 方法实现了逐 channel 的对图像进行标准化(均值变为 0,标准差变为 1)。总结如下:

  • min-max Normalization: 对应 torchvision.transforms.ToTensor() 方法
  • zero-mean Normalization: 对应 torchvision.transforms.Normalize() 方法,利用用均值和标准差对张量图像进行 zero-mean Normalization。

ToTensor() 函数的语法如下:

"""
Convert a ``PIL Image`` or ``numpy.ndarray`` to tensor.
Converts a PIL Image or numpy.ndarray (H x W x C) in the range
[0, 255] to a torch.FloatTensor of shape (C x H x W) in the range [0.0, 1.0].

Args:
    pic (PIL Image or numpy.ndarray): Image to be converted to tensor.
Returns:
    Tensor: Converted image.
"""

Normalize() 函数的语法如下:

Syntax: torchvision.transforms.Normalize()

Parameter:
    * mean: Sequence of means for each channel.
    * std: Sequence of standard deviations for each channel.
    * inplace: Bool to make this operation in-place.
Returns: Normalized Tensor image.

torch.mean() 函数的语法如下(Tensor.mean() 和 torch.mean() 函数功能是一样):

Syntax: torch.mean(input, dim, keepdim=False, *, dtype=None, out=None)

Parameters:
		* input(Tensor): the input tensor.
		* dim(int or tuple of ints): the dimension or dimensions to reduce.
		* keepdim (bool): whether the output tensor has dim retained or not.

Returns: the mean value of each row of the input tensor in the given dimension dim. If dim is a list of dimensions, reduce over all of them.

If keepdim is True, the output tensor is of the same size as input except in the dimension(s) dim where it is of size 1. Otherwise, dim is squeezed (see torch.squeeze()), resulting in the output tensor having 1 (or len(dim)) fewer dimension(s).

torch.mean() 函数实现返回给定维度 dim 中输入张量的平均值。关于 dim 参数可以这么理解,dim 就是返回的张量中会减少的 dim,不管 dim 是整数还是列表。如果输入是 3 特征数据张量,且 dim = (1,2),则表示求每个通道的平均值。示例代码如下:

import torch
x = torch.rand(3, 4, 8) 
y1 = x.mean((1,2)) # 等效于 torch.mean(x, (1, 2))
y2 = x.mean(2)
print(y1.shape)
print(y2.shape)
"""
输出结果:
	torch.Size([3])
	torch.Size([3, 4])
"""

在 PyTorch 中对图像执行 zero-mean Normalization 的步骤如下:

  1. 加载原图像;
  2. 使用 ToTensor() 函数将图像转换为 Tensors;
  3. 计算 Tensors 的均值和方差;
  4. 使用 Normalize() 函数执行 zero-mean Normalization 操作。

下面给出利用 PyTorch 实践 Normalization 操作的详细代码和输出图。

# import necessary libraries
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
import torchvision.transforms as transforms
import matplotlib.pyplot as plt

def show_images(imgs, num_rows, num_cols, titles=None, scale=8.5):
    """Plot a list of images.

    Defined in :numref:`sec_utils`"""
    figsize = (num_cols * scale, num_rows * scale)
    _, axes = plt.subplots(num_rows, num_cols, figsize=figsize)
    axes = axes.flatten()
    for i, (ax, img) in enumerate(zip(axes, imgs)):
        try:
            img = np.array(img)
        except:
            pass
        ax.imshow(img)
        ax.axes.get_xaxis().set_visible(False)
        ax.axes.get_yaxis().set_visible(False)
        if titles:
            ax.set_title(titles[i])
    return axes

def normalize_image(image_path):
    img = Image.open(img_path) # load the image
    # 1, use ToTensor function
    transform = transforms.Compose([
        transforms.ToTensor()
    ])
    img_tensor = transform(img) # transform the pIL image to tensor
    # 2, calculate mean and std by tensor's attributes
    mean, std = img_tensor.mean([1,2]), img_tensor.std([1,2])
    # 3, use Normalize function
    transform_norm = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize(mean, std)
    ])
    img_normalized = transform_norm(img) # get normalized image

    img_np = np.array(img) # convert PIL image to numpy array
    # print array‘s shape mean and std
    print(img_np.shape) # (height, width, channel), (768, 1024, 3)
    print("mean and std before normalize:")
    print("Mean of the image:", mean)
    print("Std of the image:", std)
    return img_np, img_tensor, img_normalized

def convert_tensor_np(tensor):
    img_arr = np.array(tensor)
    img_tr = img_arr.transpose(1, 2, 0)
    return img_tr

if __name__ == '__main__': 
    img_path = 'Koalainputimage.jpeg'
    img_np, img_tensor, img_normalized = normalize_image(img_path)
    # transpose tensor to numpy array and shape of (3,,) to shape of (,,3)
    img_normalized1 = convert_tensor_np(img_tensor)
    img_normalized2 = convert_tensor_np(img_normalized)
    show_images([img_np, img_normalized1, img_normalized2], 1, 3, titles=["orignal","min-max normalization", "zero-mean normalization"])

1,程序输出和两种 normalization 操作效果可视化对比图如下所示:

2,原图和两种 normalization 操作后的图像像素值正太分布可视化对比图如下所示:

像素值分布可视化用的代码如下。

# plot the pixel values
plt.hist(img_np.ravel(), bins=50, density=True)
plt.xlabel("pixel values")
plt.ylabel("relative frequency")
plt.title("distribution of pixels")

参考资料

有关深度学习炼丹-数据标准化的更多相关文章

  1. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  2. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  3. ruby - 将 spawn() 的标准输出/标准错误重定向到 Ruby 中的字符串 - 2

    我想使用spawn(针对多个并发子进程)在Ruby中执行一个外部进程,并将标准输出或标准错误收集到一个字符串中,其方式类似于使用Python的子进程Popen.communicate()可以完成的操作。我尝试将:out/:err重定向到一个新的StringIO对象,但这会生成一个ArgumentError,并且临时重新定义$stdxxx会混淆子进程的输出。 最佳答案 如果你不喜欢popen,这是我的方法:r,w=IO.pipepid=Process.spawn(command,:out=>w,:err=>[:child,:out])

  4. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  5. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  6. ruby-on-rails - 标准化文件名的字符串,删除重音和特殊字符 - 2

    我正在尝试找到一种方法来规范化字符串以将其作为文件名传递。到目前为止我有这个:my_string.mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/n,'').downcase.gsub(/[^a-z]/,'_')但第一个问题:-字符。我猜这个方法还有更多问题。我不控制名称,名称字符串可以有重音符、空格和特殊字符。我想删除所有这些,用相应的字母('é'=>'e')替换重音符号,并将其余的替换为'_'字符。名字是这样的:“Prélèvements-常规”“健康证”...我希望它们像一个没有空格/特殊字符的文件名:“prelevements_routin

  7. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  8. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  9. ruby-on-rails - 创建 ruby​​ 数据库时惰性符号绑定(bind)失败 - 2

    我正在尝试在Rails上安装ruby​​,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf

  10. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

随机推荐