機(jī)器學(xué)習(xí)就是從數(shù)據(jù)中提取信息。所以你可能想知道,我們可以從合成數(shù)據(jù)中學(xué)到什么?雖然我們本質(zhì)上可能并不關(guān)心我們自己融入人工數(shù)據(jù)生成模型的模式,但此類(lèi)數(shù)據(jù)集仍然可用于教學(xué)目的,幫助我們?cè)u(píng)估學(xué)習(xí)算法的屬性并確認(rèn)我們的實(shí)現(xiàn)是否按預(yù)期工作。例如,如果我們創(chuàng)建的數(shù)據(jù)的正確參數(shù)是先驗(yàn)已知的,那么我們可以驗(yàn)證我們的模型實(shí)際上可以恢復(fù)它們。
%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. 生成數(shù)據(jù)集
對(duì)于這個(gè)例子,我們將使用低維來(lái)簡(jiǎn)潔。以下代碼片段生成 1000 個(gè)示例,這些示例具有從標(biāo)準(zhǔn)正態(tài)分布中提取的二維特征。生成的設(shè)計(jì)矩陣X屬于R1000×2. 我們通過(guò)應(yīng)用地面真值線(xiàn)性函數(shù)生成每個(gè)標(biāo)簽,通過(guò)加性噪聲破壞它們?,為每個(gè)示例獨(dú)立且相同地繪制:
(3.3.1)y=Xw+b+?.
為了方便起見(jiàn),我們假設(shè)?取自均值為正態(tài)分布μ=0和標(biāo)準(zhǔn)差 σ=0.01. 請(qǐng)注意,對(duì)于面向?qū)ο蟮脑O(shè)計(jì),我們將代碼添加到__init__子類(lèi)的方法中d2l.DataModule (在3.2.3 節(jié)中介紹)。允許設(shè)置任何額外的超參數(shù)是一種很好的做法。我們用 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
下面,我們將真實(shí)參數(shù)設(shè)置為w=[2,?3.4]? 和b=4.2. 稍后,我們可以根據(jù)這些真實(shí)值檢查我們估計(jì)的參數(shù)。
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一個(gè)向量組成R2 每一行l(wèi)abels都是一個(gè)標(biāo)量。讓我們看一下第一個(gè)條目。
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. 讀取數(shù)據(jù)集
訓(xùn)練機(jī)器學(xué)習(xí)模型通常需要多次遍歷數(shù)據(jù)集,一次獲取一小批示例。然后使用此數(shù)據(jù)更新模型。為了說(shuō)明這是如何工作的,我們實(shí)現(xiàn)了該 方法,通過(guò)(在第 3.2.1 節(jié)中介紹 )get_dataloader將其注冊(cè)到類(lèi)中 。它采用批量大小、特征矩陣和標(biāo)簽向量,并生成大小為 的小批量 。因此,每個(gè)小批量包含一個(gè)特征和標(biāo)簽的元組。請(qǐng)注意,我們需要注意我們是處于訓(xùn)練模式還是驗(yàn)證模式:在前者中,我們希望以隨機(jī)順序讀取數(shù)據(jù),而對(duì)于后者,能夠以預(yù)定義的順序讀取數(shù)據(jù)可能對(duì)于調(diào)試目的很重要。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)
為了建立一些直覺(jué),讓我們檢查第一個(gè)小批量數(shù)據(jù)。每個(gè)小批量特征都為我們提供了它的大小和輸入特征的維度。同樣,我們的小批量標(biāo)簽將具有由 給出的匹配形狀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)
雖然看似無(wú)害,但調(diào)用 iter(data.train_dataloader())說(shuō)明了 Python 面向?qū)ο笤O(shè)計(jì)的強(qiáng)大功能。請(qǐng)注意,我們 在創(chuàng)建對(duì)象后SyntheticRegressionData向類(lèi)添加了一個(gè)方法。盡管如此,該對(duì)象受益于事后向類(lèi)添加功能。data
在整個(gè)迭代過(guò)程中,我們獲得不同的小批量,直到整個(gè)數(shù)據(jù)集都用完(試試這個(gè))。雖然上面實(shí)現(xiàn)的迭代有利于教學(xué)目的,但它的效率低下,可能會(huì)讓我們?cè)趯?shí)際問(wèn)題上遇到麻煩。例如,它要求我們將所有數(shù)據(jù)加載到內(nèi)存中,并執(zhí)行大量隨機(jī)內(nèi)存訪問(wèn)。在深度學(xué)習(xí)框架中實(shí)現(xiàn)的內(nèi)置迭代器效率要高得多,它們可以處理諸如存儲(chǔ)在文件中的數(shù)據(jù)、通過(guò)流接收的數(shù)據(jù)以及動(dòng)態(tài)生成或處理的數(shù)據(jù)等來(lái)源。接下來(lái)讓我們嘗試使用內(nèi)置迭代器實(shí)現(xiàn)相同的方法。
3.3.3. 數(shù)據(jù)加載器的簡(jiǎn)潔實(shí)現(xiàn)
我們可以調(diào)用框架中現(xiàn)有的 API 來(lái)加載數(shù)據(jù),而不是編寫(xiě)我們自己的迭代器。和以前一樣,我們需要一個(gè)具有特征X 和標(biāo)簽的數(shù)據(jù)集y。除此之外,我們?cè)O(shè)置了batch_size內(nèi)置的數(shù)據(jù)加載器,讓它有效地處理混洗示例。
@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)
新數(shù)據(jù)加載器的行為與之前的數(shù)據(jù)加載器一樣,只是它更高效并且增加了一些功能。
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提供的數(shù)據(jù)加載器支持內(nèi)置__len__方法,所以我們可以查詢(xún)它的長(zhǎng)度,即批次數(shù)。
len(data.train_dataloader())
32
len(data.train_dataloader())
32
len(data.train_dataloader())
32
len(data.train_dataloader())
32
3.3.4. 概括
數(shù)據(jù)加載器是一種抽象加載和操作數(shù)據(jù)過(guò)程的便捷方式。這樣,相同的機(jī)器學(xué)習(xí) 算法無(wú)需修改即可處理許多不同類(lèi)型和來(lái)源的數(shù)據(jù)。數(shù)據(jù)加載器的優(yōu)點(diǎn)之一是它們可以組合。例如,我們可能正在加載圖像,然后有一個(gè)后期處理過(guò)濾器來(lái)裁剪或修改它們。因此,數(shù)據(jù)加載器可用于描述整個(gè)數(shù)據(jù)處理管道。
至于模型本身,二維線(xiàn)性模型是我們可能遇到的最簡(jiǎn)單的模型。它使我們能夠測(cè)試回歸模型的準(zhǔn)確性,而不必?fù)?dān)心數(shù)據(jù)量不足或方程組未定。我們將在下一節(jié)中充分利用它。
3.3.5. 練習(xí)
如果樣本數(shù)量不能除以批量大小,將會(huì)發(fā)生什么。如何通過(guò)使用框架的 API 指定不同的參數(shù)來(lái)更改此行為?
如果我們想要生成一個(gè)巨大的數(shù)據(jù)集,其中參數(shù)向量的大小w和示例的數(shù)量 num_examples都很大怎么辦?
如果我們不能將所有數(shù)據(jù)保存在內(nèi)存中會(huì)怎樣?
如果數(shù)據(jù)保存在磁盤(pán)上,您將如何打亂數(shù)據(jù)?您的任務(wù)是設(shè)計(jì)一種不需要太多隨機(jī)讀取或?qū)懭氲母咝惴āL崾荆簜坞S機(jī)排列生成器 允許您設(shè)計(jì)重新洗牌而不需要顯式存儲(chǔ)排列表(Naor 和 Reingold,1999)。
實(shí)現(xiàn)一個(gè)數(shù)據(jù)生成器,在每次調(diào)用迭代器時(shí)動(dòng)態(tài)生成新數(shù)據(jù)。
您將如何設(shè)計(jì)一個(gè)隨機(jī)數(shù)據(jù)生成器,使其在每次調(diào)用時(shí)生成相同的數(shù)據(jù)?
-
數(shù)據(jù)加載器
+關(guān)注
關(guān)注
0文章
2瀏覽量
5758 -
pytorch
+關(guān)注
關(guān)注
2文章
808瀏覽量
13319
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論