3.2.0 头文件
import random
import torch
from d2l import torch as d2l
3.2.1 关于yield关键字
# 关于yield关键字
def foo():
print("starting...")
while True:
res = yield 4
print("res:",res)
g = foo() #因为该函数含有yield关键字,所以这里并不会执行该函数
#调用next方法后,foo函数开始执行,输出(starting...),然后进入while循环,遇到yield后将后面的4返回,输出(4)
#下一次调用next方法后,从之前中断的yield之后开始执行,输出(res: None),之后遇到yield后将后面的4返回,输出(4)
print(next(g))
# 输出:
# starting...
# 4
print(next(g))
# 输出:
# res: None
# 4
3.2.2 制作数据集
# 定义一个数据集生成器 num_examples为数据集大小,w为真实的权重w,b为真实的偏离量b
def synthetic_data(w, b, num_examples):
"""生成y=Xw+b+噪声"""
X = torch.normal(0, 1, (num_examples, len(w))) #生成的特征X服从均值为0,方差为1的正态分布,(num_examples, len(w))为特征X的尺寸,num_examples表示特征的个数,len(w)表示特征的维度
y = torch.matmul(X, w) + b #生成每个特征对应的真实标签
y += torch.normal(0, 0.01, y.shape) # 将每个标签加上一个随机的噪声
return X, y.reshape((-1, 1))
# 定义真实的权重参数
true_w = torch.tensor([2, -3.4])
# 定义真实的偏移量
true_b = 4.2
# 生成数据集,features为特征集合,labels为标签集合
features, labels = synthetic_data(true_w, true_b, 1000)
# print(features)
# 输出:
# tensor([[-1.2232, -1.1287],
# [ 0.7761, 1.3506],
# [ 1.8760, -0.1813],
# ...,
# [-0.3016, 0.9798],
# [-1.2308, -1.4371],
# [ 0.2694, -0.8200]])
# print(labels)
# 输出:
# tensor([[ 6.7570e+00],
# [ 1.1429e+00],
# [ 8.2363e+00],
# ...,
# [ 1.7683e+00],
# [ 2.7000e+00],
# [ 5.0721e+00]])
3.2.3 迭代读取数据集
# 定义一个数据集读取迭代器
# batch_size:批量大小
# features:特征集合
# labels:标签集合
def data_iter(batch_size, features, labels):
num_examples = len(features) # 计算特征的个数
indices = list(range(num_examples)) # 所有特征的下标编号,从0~num_examples-1
# 这些样本是随机读取的,没有特定的顺序
random.shuffle(indices) # 将所有特征的下标编号顺序打乱
# 每次next操作,都会从数据集中返回一个批量的特征和相应的标签集合
for i in range(0, num_examples, batch_size):
batch_indices = torch.tensor(
indices[i: min(i + batch_size, num_examples)])
yield features[batch_indices], labels[batch_indices]
# 定义批量大小
batch_size = 10
# 获取数据集中的一个批量的特征集合和对应的标签集合
for X, y in data_iter(batch_size, features, labels):
print(X, '\n', y)
break
# 输出:
# tensor([[-5.7894e-01, -2.4479e+00],
# [ 2.0853e-01, 3.2863e-01],
# [ 1.0836e-01, -3.0798e-01],
# [-6.8576e-01, -1.2096e+00],
# [ 1.0776e+00, 1.9446e-01],
# [-1.9330e+00, 1.4515e+00],
# [ 1.0839e+00, -9.5073e-01],
# [-1.3466e+00, 1.7328e+00],
# [ 1.0802e-03, 7.4219e-01],
# [ 6.2577e-01, 1.0039e+00]])
# tensor([[11.3540],
# [ 3.4885],
# [ 5.4594],
# [ 6.9395],
# [ 5.6864],
# [-4.5882],
# [ 9.6101],
# [-4.3811],
# [ 1.6704],
# [ 2.0378]])
3.2.4 随机初始化模型参数
# 随机初始化权重w,均值为0,方差为0.01的正态分布
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
# print(w)
# 输出:
# tensor([[ 0.0110],
# [-0.0161]], requires_grad=True)
# 随机初始化偏移量b,初始值为0
b = torch.zeros(1, requires_grad=True)
# print(b)
# 输出:
# tensor([0.], requires_grad=True)
3.2.5 定义网络模型
# 定义线性回归模型
# X:为一个批量的特征集合
# w:随机初始化的权重
# b:为初始值为0的偏移量
def linreg(X, w, b):
return torch.matmul(X, w) + b
3.2.6 定义损失函数
# 定义均方损失函数
# y_hat:为一个批量的预测值
# y:为一个批量的实际值
def squared_loss(y_hat, y):
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
3.2.7 定义优化器
# 定义随机梯度下降优化器
# params:权重和偏移量
# lr:学习率
# batch_size:批量大小
def sgd(params, lr, batch_size):
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
3.2.8 训练过程
# 定义学习率
lr = 0.03
# 定义训练轮数
num_epochs = 3
# 定义网络模型
net = linreg
# 定义损失函数
loss = squared_loss
# 对于每一轮训练过程
for epoch in range(num_epochs):
# 对于每一个批量
for X, y in data_iter(batch_size, features, labels):# 从数据集中取出一个批量的特征集合和对应的标签集合
# 求一个批量的预测损失值
l = loss(net(X, w, b), y)
# 用随机梯度下降优化器来更新权重和偏移量
l.sum().backward()
sgd([w, b], lr, batch_size)
# 一轮训练完成后,在整个数据集上计算损失
with torch.no_grad():
# 当前轮训练完成后,利用得到的权重和参数来计算在整个数据集上的损失均值
train_l = loss(net(features, w, b), labels)
print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')
# 输出:
# epoch 1, loss 0.033427
# epoch 2, loss 0.000117
# epoch 3, loss 0.000052
# 计算经过学习后,得到的权重和偏移量与真实值之间的误差
print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差: {true_b - b}')
# 输出:
# w的估计误差: tensor([-0.0001, -0.0002], grad_fn=<SubBackward0>)
# b的估计误差: tensor([0.0006], grad_fn=<RsubBackward1>)
本小节的完整代码如下
import random
import torch
from d2l import torch as d2l
# ------------------------------yield关键字------------------------------------
def foo():
print("starting...")
while True:
res = yield 4
print("res:",res)
g = foo() #因为该函数含有yield关键字,所以这里并不会执行该函数
#调用next方法后,foo函数开始执行,输出(starting...),然后进入while循环,遇到yield后将后面的4返回,输出(4)
#下一次调用next方法后,从之前中断的yield之后开始执行,输出(res: None),之后遇到yield后将后面的4返回,输出(4)
print(next(g))
# 输出:
# starting...
# 4
print(next(g))
# 输出:
# res: None
# 4
# ------------------------------制作数据集------------------------------------
# 定义一个数据集生成器 num_examples为数据集大小,w为真实的权重w,b为真实的偏离量b
def synthetic_data(w, b, num_examples):
"""生成y=Xw+b+噪声"""
X = torch.normal(0, 1, (num_examples, len(w))) #生成的特征X服从均值为0,方差为1的正态分布,(num_examples, len(w))为特征X的尺寸,num_examples表示特征的个数,len(w)表示特征的维度
y = torch.matmul(X, w) + b #生成每个特征对应的真实标签
y += torch.normal(0, 0.01, y.shape) # 将每个标签加上一个随机的噪声
return X, y.reshape((-1, 1))
# 定义真实的权重参数
true_w = torch.tensor([2, -3.4])
# 定义真实的偏移量
true_b = 4.2
# 生成数据集,features为特征集合,labels为标签集合
features, labels = synthetic_data(true_w, true_b, 1000)
# print(features)
# 输出:
# tensor([[-1.2232, -1.1287],
# [ 0.7761, 1.3506],
# [ 1.8760, -0.1813],
# ...,
# [-0.3016, 0.9798],
# [-1.2308, -1.4371],
# [ 0.2694, -0.8200]])
# print(labels)
# 输出:
# tensor([[ 6.7570e+00],
# [ 1.1429e+00],
# [ 8.2363e+00],
# ...,
# [ 1.7683e+00],
# [ 2.7000e+00],
# [ 5.0721e+00]])
# ------------------------------迭代读取数据集------------------------------------
# 定义一个数据集读取迭代器
# batch_size:批量大小
# features:特征集合
# labels:标签集合
def data_iter(batch_size, features, labels):
num_examples = len(features) # 计算特征的个数
indices = list(range(num_examples)) # 所有特征的下标编号,从0~num_examples-1
# 这些样本是随机读取的,没有特定的顺序
random.shuffle(indices) # 将所有特征的下标编号顺序打乱
# 每次next操作,都会从数据集中返回一个批量的特征和相应的标签集合
for i in range(0, num_examples, batch_size):
batch_indices = torch.tensor(
indices[i: min(i + batch_size, num_examples)])
yield features[batch_indices], labels[batch_indices]
# 定义批量大小
batch_size = 10
# 获取数据集中的一个批量的特征集合和对应的标签集合
for X, y in data_iter(batch_size, features, labels):
print(X, '\n', y)
break
# 输出:
# tensor([[-5.7894e-01, -2.4479e+00],
# [ 2.0853e-01, 3.2863e-01],
# [ 1.0836e-01, -3.0798e-01],
# [-6.8576e-01, -1.2096e+00],
# [ 1.0776e+00, 1.9446e-01],
# [-1.9330e+00, 1.4515e+00],
# [ 1.0839e+00, -9.5073e-01],
# [-1.3466e+00, 1.7328e+00],
# [ 1.0802e-03, 7.4219e-01],
# [ 6.2577e-01, 1.0039e+00]])
# tensor([[11.3540],
# [ 3.4885],
# [ 5.4594],
# [ 6.9395],
# [ 5.6864],
# [-4.5882],
# [ 9.6101],
# [-4.3811],
# [ 1.6704],
# [ 2.0378]])
# ------------------------------随机初始化模型参数------------------------------------
# 随机初始化权重w,均值为0,方差为0.01的正态分布
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
# print(w)
# 输出:
# tensor([[ 0.0110],
# [-0.0161]], requires_grad=True)
# 随机初始化偏移量b,初始值为0
b = torch.zeros(1, requires_grad=True)
# print(b)
# 输出:
# tensor([0.], requires_grad=True)
# ------------------------------定义网络模型------------------------------------
# 定义线性回归模型
# X:为一个批量的特征集合
# w:随机初始化的权重
# b:为初始值为0的偏移量
def linreg(X, w, b):
return torch.matmul(X, w) + b
# ------------------------------定义损失函数------------------------------------
# 定义均方损失函数
# y_hat:为一个批量的预测值
# y:为一个批量的实际值
def squared_loss(y_hat, y):
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
# ------------------------------定义优化器------------------------------------
# 定义随机梯度下降优化器
# params:权重和偏移量
# lr:学习率
# batch_size:批量大小
def sgd(params, lr, batch_size):
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
# ------------------------------训练过程------------------------------------
# 定义学习率
lr = 0.03
# 定义训练轮数
num_epochs = 3
# 定义网络模型
net = linreg
# 定义损失函数
loss = squared_loss
# 对于每一轮训练过程
for epoch in range(num_epochs):
# 对于每一个批量
for X, y in data_iter(batch_size, features, labels):# 从数据集中取出一个批量的特征集合和对应的标签集合
# 求一个批量的预测损失值
l = loss(net(X, w, b), y)
# 用随机梯度下降优化器来更新权重和偏移量
l.sum().backward()
sgd([w, b], lr, batch_size)
# 一轮训练完成后,在整个数据集上计算损失
with torch.no_grad():
# 当前轮训练完成后,利用得到的权重和参数来计算在整个数据集上的损失均值
train_l = loss(net(features, w, b), labels)
print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')
# 输出:
# epoch 1, loss 0.033427
# epoch 2, loss 0.000117
# epoch 3, loss 0.000052
# 计算经过学习后,得到的权重和偏移量与真实值之间的误差
print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差: {true_b - b}')
# 输出:
# w的估计误差: tensor([-0.0001, -0.0002], grad_fn=<SubBackward0>)
# b的估计误差: tensor([0.0006], grad_fn=<RsubBackward1>)