机器学习就是从数据中提取信息。所以你可能想知道,我们可以从合成数据中学到什么?虽然我们本质上可能并不关心我们自己融入人工数据生成模型的模式,但此类数据集仍然可用于教学目的,帮助我们评估学习算法的属性并确认我们的实现是否按预期工作。例如,如果我们创建的数据的正确参数是先验已知的,那么我们可以验证我们的模型实际上可以恢复它们。
%matplotlib inline import random import torch from d2l import torch as d2l
%matplotlib inline import random from mxnet import gluon, np, npx from d2l import mxnet as d2l npx.set_np()
%matplotlib inline import random import jax import numpy as np import tensorflow as tf import tensorflow_datasets as tfds from jax import numpy as jnp from d2l import jax as d2l
No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)
%matplotlib inline import random import tensorflow as tf from d2l import tensorflow as d2l
3.3.1. 生成数据集
对于这个例子,我们将使用低维来简洁。以下代码片段生成 1000 个示例,这些示例具有从标准正态分布中提取的二维特征。生成的设计矩阵X属于R1000×2. 我们通过应用地面真值线性函数生成每个标签,通过加性噪声破坏它们ϵ,为每个示例独立且相同地绘制:
(3.3.1)y=Xw+b+ϵ.
为了方便起见,我们假设ϵ取自均值为正态分布μ=0和标准差 σ=0.01. 请注意,对于面向对象的设计,我们将代码添加到__init__子类的方法中d2l.DataModule (在3.2.3 节中介绍)。允许设置任何额外的超参数是一种很好的做法。我们用 save_hyperparameters(). batch_size稍后将确定。
class SyntheticRegressionData(d2l.DataModule): #@save """Synthetic data for linear regression.""" def __init__(self, w, b, noise=0.01, num_train=1000, num_val=1000, batch_size=32): super().__init__() self.save_hyperparameters() n = num_train + num_val self.X = torch.randn(n, len(w)) noise = torch.randn(n, 1) * noise self.y = torch.matmul(self.X, w.reshape((-1, 1))) + b + noise
class SyntheticRegressionData(d2l.DataModule): #@save """Synthetic data for linear regression.""" def __init__(self, w, b, noise=0.01, num_train=1000, num_val=1000, batch_size=32): super().__init__() self.save_hyperparameters() n = num_train + num_val self.X = np.random.randn(n, len(w)) noise = np.random.randn(n, 1) * noise self.y = np.dot(self.X, w.reshape((-1, 1))) + b + noise
class SyntheticRegressionData(d2l.DataModule): #@save """Synthetic data for linear regression.""" def __init__(self, w, b, noise=0.01, num_train=1000, num_val=1000, batch_size=32): super().__init__() self.save_hyperparameters() n = num_train + num_val key = jax.random.PRNGKey(0) key1, key2 = jax.random.split(key) self.X = jax.random.normal(key1, (n, w.shape[0])) noise = jax.random.normal(key2, (n, 1)) * noise self.y = jnp.matmul(self.X, w.reshape((-1, 1))) + b + noise
class SyntheticRegressionData(d2l.DataModule): #@save """Synthetic data for linear regression.""" def __init__(self, w, b, noise=0.01, num_train=1000, num_val=1000, batch_size=32): super().__init__() self.save_hyperparameters() n = num_train + num_val self.X = tf.random.normal((n, w.shape[0])) noise = tf.random.normal((n, 1)) * noise self.y = tf.matmul(self.X, tf.reshape(w, (-1, 1))) + b + noise
下面,我们将真实参数设置为w=[2,−3.4]⊤ 和b=4.2. 稍后,我们可以根据这些真实值检查我们估计的参数。
data = SyntheticRegressionData(w=torch.tensor([2, -3.4]), b=4.2)
data = SyntheticRegressionData(w=np.array([2, -3.4]), b=4.2)
data = SyntheticRegressionData(w=jnp.array([2, -3.4]), b=4.2)
data = SyntheticRegressionData(w=tf.constant([2, -3.4]), b=4.2)
每行由features一个向量组成R2 每一行labels都是一个标量。让我们看一下第一个条目。
print('features:', data.X[0],'nlabel:', data.y[0])
features: tensor([-0.0499, -0.2817]) label: tensor([5.0533])
print('features:', data.X[0],'nlabel:', data.y[0])
features: [2.2122064 1.1630787] label: [4.684836]
print('features:', data.X[0],'nlabel:', data.y[0])
features: [-0.86997527 -3.2320356 ] label: [13.438176]
print('features:', data.X[0],'nlabel:', data.y[0])
features: tf.Tensor([-0.8617247 0.9828964], shape=(2,), dtype=float32) label: tf.Tensor([-0.86415064], shape=(1,), dtype=float32)
3.3.2. 读取数据集
训练机器学习模型通常需要多次遍历数据集,一次获取一小批示例。然后使用此数据更新模型。为了说明这是如何工作的,我们实现了该 方法,通过(在第 3.2.1 节中介绍 )get_dataloader将其注册到类中 。它采用批量大小、特征矩阵和标签向量,并生成大小为 的小批量 。因此,每个小批量包含一个特征和标签的元组。请注意,我们需要注意我们是处于训练模式还是验证模式:在前者中,我们希望以随机顺序读取数据,而对于后者,能够以预定义的顺序读取数据可能对于调试目的很重要。SyntheticRegressionDataadd_to_classbatch_size
@d2l.add_to_class(SyntheticRegressionData) def get_dataloader(self, train): if train: indices = list(range(0, self.num_train)) # The examples are read in random order random.shuffle(indices) else: indices = list(range(self.num_train, self.num_train+self.num_val)) for i in range(0, len(indices), self.batch_size): batch_indices = torch.tensor(indices[i: i+self.batch_size]) yield self.X[batch_indices], self.y[batch_indices]
@d2l.add_to_class(SyntheticRegressionData) def get_dataloader(self, train): if train: indices = list(range(0, self.num_train)) # The examples are read in random order random.shuffle(indices) else: indices = list(range(self.num_train, self.num_train+self.num_val)) for i in range(0, len(indices), self.batch_size): batch_indices = np.array(indices[i: i+self.batch_size]) yield self.X[batch_indices], self.y[batch_indices]
@d2l.add_to_class(SyntheticRegressionData) def get_dataloader(self, train): if train: indices = list(range(0, self.num_train)) # The examples are read in random order random.shuffle(indices) else: indices = list(range(self.num_train, self.num_train+self.num_val)) for i in range(0, len(indices), self.batch_size): batch_indices = jnp.array(indices[i: i+self.batch_size]) yield self.X[batch_indices], self.y[batch_indices]
@d2l.add_to_class(SyntheticRegressionData) def get_dataloader(self, train): if train: indices = list(range(0, self.num_train)) # The examples are read in random order random.shuffle(indices) else: indices = list(range(self.num_train, self.num_train+self.num_val)) for i in range(0, len(indices), self.batch_size): j = tf.constant(indices[i : i+self.batch_size]) yield tf.gather(self.X, j), tf.gather(self.y, j)
为了建立一些直觉,让我们检查第一个小批量数据。每个小批量特征都为我们提供了它的大小和输入特征的维度。同样,我们的小批量标签将具有由 给出的匹配形状batch_size。
X, y = next(iter(data.train_dataloader())) print('X shape:', X.shape, 'ny shape:', y.shape)
X shape: torch.Size([32, 2]) y shape: torch.Size([32, 1])
X, y = next(iter(data.train_dataloader())) print('X shape:', X.shape, 'ny shape:', y.shape)
X shape: (32, 2) y shape: (32, 1)
X, y = next(iter(data.train_dataloader())) print('X shape:', X.shape, 'ny shape:', y.shape)
X shape: (32, 2) y shape: (32, 1)
X, y = next(iter(data.train_dataloader())) print('X shape:', X.shape, 'ny shape:', y.shape)
X shape: (32, 2) y shape: (32, 1)
虽然看似无害,但调用 iter(data.train_dataloader())说明了 Python 面向对象设计的强大功能。请注意,我们 在创建对象后SyntheticRegressionData向类添加了一个方法。尽管如此,该对象受益于事后向类添加功能。data
在整个迭代过程中,我们获得不同的小批量,直到整个数据集都用完(试试这个)。虽然上面实现的迭代有利于教学目的,但它的效率低下,可能会让我们在实际问题上遇到麻烦。例如,它要求我们将所有数据加载到内存中,并执行大量随机内存访问。在深度学习框架中实现的内置迭代器效率要高得多,它们可以处理诸如存储在文件中的数据、通过流接收的数据以及动态生成或处理的数据等来源。接下来让我们尝试使用内置迭代器实现相同的方法。
3.3.3. 数据加载器的简洁实现
我们可以调用框架中现有的 API 来加载数据,而不是编写我们自己的迭代器。和以前一样,我们需要一个具有特征X 和标签的数据集y。除此之外,我们设置了batch_size内置的数据加载器,让它有效地处理混洗示例。
@d2l.add_to_class(d2l.DataModule) #@save def get_tensorloader(self, tensors, train, indices=slice(0, None)): tensors = tuple(a[indices] for a in tensors) dataset = torch.utils.data.TensorDataset(*tensors) return torch.utils.data.DataLoader(dataset, self.batch_size, shuffle=train) @d2l.add_to_class(SyntheticRegressionData) #@save def get_dataloader(self, train): i = slice(0, self.num_train) if train else slice(self.num_train, None) return self.get_tensorloader((self.X, self.y), train, i)
@d2l.add_to_class(d2l.DataModule) #@save def get_tensorloader(self, tensors, train, indices=slice(0, None)): tensors = tuple(a[indices] for a in tensors) dataset = gluon.data.ArrayDataset(*tensors) return gluon.data.DataLoader(dataset, self.batch_size, shuffle=train) @d2l.add_to_class(SyntheticRegressionData) #@save def get_dataloader(self, train): i = slice(0, self.num_train) if train else slice(self.num_train, None) return self.get_tensorloader((self.X, self.y), train, i)
JAX is all about NumPy like API with device acceleration and the functional transformations, so at least the current version doesn’t include data loading methods. With other libraries we already have great data loaders out there, and JAX suggests using them instead. Here we will grab TensorFlow’s data loader, and modify it slightly to make it work with JAX.
@d2l.add_to_class(d2l.DataModule) #@save def get_tensorloader(self, tensors, train, indices=slice(0, None)): tensors = tuple(a[indices] for a in tensors) # Use Tensorflow Datasets & Dataloader. JAX or Flax do not provide # any dataloading functionality shuffle_buffer = tensors[0].shape[0] if train else 1 return tfds.as_numpy( tf.data.Dataset.from_tensor_slices(tensors).shuffle( buffer_size=shuffle_buffer).batch(self.batch_size)) @d2l.add_to_class(SyntheticRegressionData) #@save def get_dataloader(self, train): i = slice(0, self.num_train) if train else slice(self.num_train, None) return self.get_tensorloader((self.X, self.y), train, i)
@d2l.add_to_class(d2l.DataModule) #@save def get_tensorloader(self, tensors, train, indices=slice(0, None)): tensors = tuple(a[indices] for a in tensors) shuffle_buffer = tensors[0].shape[0] if train else 1 return tf.data.Dataset.from_tensor_slices(tensors).shuffle( buffer_size=shuffle_buffer).batch(self.batch_size) @d2l.add_to_class(SyntheticRegressionData) #@save def get_dataloader(self, train): i = slice(0, self.num_train) if train else slice(self.num_train, None) return self.get_tensorloader((self.X, self.y), train, i)
新数据加载器的行为与之前的数据加载器一样,只是它更高效并且增加了一些功能。
X, y = next(iter(data.train_dataloader())) print('X shape:', X.shape, 'ny shape:', y.shape)
X shape: torch.Size([32, 2]) y shape: torch.Size([32, 1])
X, y = next(iter(data.train_dataloader())) print('X shape:', X.shape, 'ny shape:', y.shape)
X shape: (32, 2) y shape: (32, 1)
X, y = next(iter(data.train_dataloader())) print('X shape:', X.shape, 'ny shape:', y.shape)
X shape: (32, 2) y shape: (32, 1)
X, y = next(iter(data.train_dataloader())) print('X shape:', X.shape, 'ny shape:', y.shape)
X shape: (32, 2) y shape: (32, 1)
例如,框架API提供的数据加载器支持内置__len__方法,所以我们可以查询它的长度,即批次数。
len(data.train_dataloader())
32
len(data.train_dataloader())
32
len(data.train_dataloader())
32
len(data.train_dataloader())
32
3.3.4. 概括
数据加载器是一种抽象加载和操作数据过程的便捷方式。这样,相同的机器学习 算法无需修改即可处理许多不同类型和来源的数据。数据加载器的优点之一是它们可以组合。例如,我们可能正在加载图像,然后有一个后期处理过滤器来裁剪或修改它们。因此,数据加载器可用于描述整个数据处理管道。
至于模型本身,二维线性模型是我们可能遇到的最简单的模型。它使我们能够测试回归模型的准确性,而不必担心数据量不足或方程组未定。我们将在下一节中充分利用它。
3.3.5. 练习
如果样本数量不能除以批量大小,将会发生什么。如何通过使用框架的 API 指定不同的参数来更改此行为?
如果我们想要生成一个巨大的数据集,其中参数向量的大小w和示例的数量 num_examples都很大怎么办?
如果我们不能将所有数据保存在内存中会怎样?
如果数据保存在磁盘上,您将如何打乱数据?您的任务是设计一种不需要太多随机读取或写入的高效算法。提示:伪随机排列生成器 允许您设计重新洗牌而不需要显式存储排列表(Naor 和 Reingold,1999)。
实现一个数据生成器,在每次调用迭代器时动态生成新数据。
您将如何设计一个随机数据生成器,使其在每次调用时生成相同的数据?
-
数据加载器
+关注
关注
0文章
2浏览量
5746 -
pytorch
+关注
关注
2文章
803浏览量
13148
发布评论请先 登录
相关推荐
评论