线性回归的从零开始实现

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) # X和y的小批量损失
# 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,
# 并以此计算关于[w,b]的梯度
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}’)
为何再次运行后loss就固定不变了
epoch 1, loss 0.000050
epoch 2, loss 0.000050
epoch 3, loss 0.000050

d2l的包出问题了吗?实验环境就是Google Colab。
前天跑的还好好的,昨天开始图片生成就有问题了。运行到:
d2l.plt.scatter(features[:, (1)].detach().numpy(), labels.detach().numpy(), 1);
这这一步会出现以下问题:

ImportError Traceback (most recent call last)
/usr/local/lib/python3.7/dist-packages/IPython/core/formatters.py in call(self, obj)
332 pass
333 else:
→ 334 return printer(obj)
335 # Finally look for special method names
336 method = get_real_method(obj, self.print_method)

12 frames
/usr/local/lib/python3.7/dist-packages/matplotlib/backends/backend_svg.py in ()
16 import matplotlib as mpl
17 from matplotlib import _api, cbook, font_manager as fm
—> 18 from matplotlib.backend_bases import (
19 _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase,
20 RendererBase)

ImportError: cannot import name ‘_check_savefig_extra_args’ from ‘matplotlib.backend_bases’ (/usr/local/lib/python3.7/dist-packages/matplotlib/backend_bases.py)

1 Like

因为他已经训练到一个符合要求的水准了,除非你重置一下w和b
#初始化参数权重w,偏置b=0
w=torch.normal(0,0.01,size=(2,1),requires_grad=True)
b=torch.zeros(1,requires_grad=True)

你好,我也遇到了这个问题,请问是怎么解决的呀?

我觉得就是重命名一个接口把,net指代一切算法,这样你训练的代码就不用调整了,只要把net指向对应的算法即可

因为python的机制不用于C的函数传参,python传进去的是参数本身也可以说是C++中参数的引用,因此在函数中对参数进行操作会影响函数域外的变量
运行如下代码来检验

class Tes(object):
    def __init__(self):
        self.num = 10
        pass

    pass


def func(inc: Tes) -> Tes:
    inc.num += 10
    print(id(inc))
    return inc


def func2(inc):
    inc += 10
    print(id(inc))
    return inc


a1 = Tes()
print(id(a1))
a2 = func(a1)

print(id(a2) == id(a1)) #此输出为True 对于非python基础类型的成立

b1 = 9999
print(id(b1))
b2 = func2(b1)
print(id(b2) == id(b1))  #此输出为False  

这里是为了在计算的时候确保是进行按元素减法

batch_size为标量 所以除法是没问题的
这里可能存在疑问的是当param是w时,其是一个(2,1)的张量 ,其导数也是一个同shape的张量,可以看一下之前的自动求导部分,标量对于矩阵张量求导结果是一个转置的矩阵,即(1,2)的矩阵,这里并不会影响后续计算,据猜测是发生了转置操作以适配原有的w的shape,但是我在进行代码测试时发现此时竟然产生了广播操作

a = torch.ones((2, 1))
b = torch.ones((1, 2)) * 0.6
c=a-b
print(f'被减数shape为{a.shape}\n'
      f'减数的shape为{b.shape}\n'
      f'结果的shape为{c.shape}\n'

此处发生了广播操作

最终解

参数的梯度值和参数本身,其shape是一致的,那么也就不存在之前的广播,但是这又和沐神3之前PPT上的分子布局不同。先埋个坑 等我回来填

test1=torch.randn((1,2),requires_grad=True)
test2=torch.mm(test1,test1.T)
test2.shape
test2.backward()

test1.grad.shape == test1.shape

data_iter的最后一次取的样本个数可能小于batch size,那这样的话在sgd的时候还是除以batch size是不是不合适?感觉还是在计算loss的时候直接给出均值好一点

我也想知道,如果不导入dl2,会影响学习吗?或者代码实现吗?

因为优化算法中用了‘-=’,pytorch规定tensor带require_grad 不能进行“+=”或者“-=”(in-place操作),如果不进行in-place操作而是写成param =param-lr * param.grad / batch_size会导致param地址变动,后面对导数清零的时候就会报错,因为此时w.grad已经是’Nonetype’了
with torch.no_grad():封装可以暂时关闭计算图,也就是在语句内进行的张量运算不改变已有的计算图,这样就可以进行in-place操作了
然而这段代码有除此之外的写法吗?求高人指点