#%matplotlib inline
import random
import torch
from d2l import torch as d2l
def synthetic_data(w, b, num_examples): #@save
"""生成y=Xw+b+噪声"""
X = torch.normal(0, 1, (num_examples, len(w)))
y = torch.matmul(X, w) + b#matmul是矩阵相乘的方法
y += torch.normal(0, 0.01, y.shape)
return X, y.reshape((-1, 1))#最后返回两个数据,y.reshape((-1,1)),其中-1代表行根据有多少数据来定,列是第2列,0代表第一列
def data_iter(batch_size, features, labels):
num_examples = len(features)
indices = list(range(num_examples))#range生成从0到num_examples的数据,然后变为list列表,最后赋值给indices
# 这些样本是随机读取的,没有特定的顺序,shuffle为随机打乱方法,在random里
random.shuffle(indices)
for i in range(0, num_examples, batch_size):#从0到num_examples,每一步是batch_size,也就是一个小批量的大小
batch_indices = torch.tensor(
indices[i: min(i + batch_size, num_examples)])#利用切片,每次切片出一个小批量转换为tensor后赋值
yield features[batch_indices], labels[batch_indices]#yield是一个迭代器
def linreg(X, w, b): #@save
"""线性回归模型"""
return torch.matmul(X, w) + b
def squared_loss(y_hat, y): #@save
"""均方损失"""
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
def sgd(params, lr, batch_size): #@save
"""小批量随机梯度下降"""
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
d2l.set_figsize()#定义图像尺寸是默认的大小
d2l.plt.scatter(features[:,1].detach().numpy(), labels.detach().numpy(), 5);
batch_size = 10
for X, y in data_iter(batch_size, features, labels):#最后X,y从函数中拿出数据,是一个小批量的数据,小批量为10,所以X输出10行
print(X, '\n', y)
break
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss
for epoch in range(num_epochs):
for X, y in data_iter(1, features, labels):#batch_size=1
l = loss(net(X, w, b), y) # X和y的小批量损失
# 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,
# 并以此计算关于[w,b]的梯度
l.sum().backward()
print(X)#这里是测试
print((torch.mm(X,w)+b-y)*X)
print(w.grad) #这样可以看l对w的梯度
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}')
print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差: {true_b - b}')
专注于看这个函数
for epoch in range(num_epochs):
for X, y in data_iter(1, features, labels):
l = loss(net(X, w, b), y) # X和y的小批量损失
# 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,
# 并以此计算关于[w,b]的梯度
l.sum().backward()
print(X)
print((torch.mm(X,w)+b-y)*X)
print(w.grad) #这样可以看l对w的梯度
sgd([w, b], lr, batch_size) # 使用参数的梯度更新参数
这里的data_iter函数中的1为batch_size,我想看一下w的梯度到底怎样求的,X原来是(10,1)现在我取batch_size=1,说明我只提取他的第一行也就是([[0.7145,-0.9413]])
这里可设X=,W=,b就当做0,y的size=(10,1)这里同样取他的第一行
L=,
然后=+=
而上面代码的X=,也就相当于=+=
所以得到的L的size=(1,2),而W==是有两个参数的,所以W.grad也有两个参数,这里可以看到第2个tensor的值和第三个tensor的值一样
如果batch_size=2呢?
for epoch in range(num_epochs):
for X, y in data_iter(2, features, labels):
l = loss(net(X, w, b), y) # X和y的小批量损失
# 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,
# 并以此计算关于[w,b]的梯度
l.sum().backward()
print(X)
print((torch.mm(X,w)+b-y)*X)
print(w.grad) #这样可以看l对w的梯度
这里X的size=(2,2),设X= ,W=
那么=+=
这里*x1和*x3对应,*x2和*x4对应
所以可以看到第三个tensor的0.0062是第二个tensor的第一行和第一列的总和,也就是=0.0064-0.0002=0.0062。而=-0.0165+0.0002=-0.0163
这样不管W是(1,1)还是(10,1)或者size=(3,2),我们都能知道W的grad为什么有时候是size=(1,2)的tensor,因为L对W里的每一个小参数都求了偏导,同理求b的偏导也一样的方法