最近在b站看李沐老师的深度学习课程,受益颇多。不过觉得光看视频实在是不过瘾,最好还是能实际的玩起来。鉴于我还是习惯使用pycharm,且不需要过多的中间过程展示,所以代码的编写基本都是在pycharm进行。由于李沐老师的代码主要是在Jupyter运行的,在pycharm上使用会略有差异。本篇博客以求解线性优化问题代码为例,来谈一谈在pycharm运行相关代码的一些注意事项。
教程网站:Dive into Deep Learning — Dive into Deep Learning 1.0.0-alpha0 documentation
中文网站:《动手学深度学习》 — 动手学深度学习 2.0.0-beta1 documentation
除了安装常用的pytorch和torchvision等包以外,在运行相关代码时,会看到使用d2l包调用一些包装好的代码,如下:
- import random
- import torch
- import numpy
- from d2l import torch as d2l
这个d2l在pycharm里搜索可能搜不到。我在查阅网上其他人的介绍,通常是在Anaconda的环境中使用pip install来安装。首先我们打开anaconda虚拟环境,激活当前环境,并运行pip install d2l来运行包的安装,步骤如下:
1)使用conda info --envs列出所有的虚拟环境
2)找到你项目所在的虚拟环境,激活:activate your_envs_path
3)pip install d2l
在安装完d2l后,这时我们就可以运行代码了。
和Jupyter不同的是,在pycharm中我们不能直接使用变量名来查看数据,一般都要附加对应的打印或者显示程序才能看到对应的结果,如下:
- import torch
- x = torch.tensor(3.0)
- y = torch.tensor(2.0)
- x + y, x * y, x / y, x**y
在Jupyter中,结果就被直接显示出来了,但是在pycharm中,需要打印:
print(x+y,x*y,x/y,x**y)
同理,图像显示也需要使用相应的显示代码:
- d2l.set_figsize()
- d2l.plt.scatter(f,l,1)
- d2l.plt.show()
显示结果:

下面,我们把pycharm中验证过的完整的线性优化代码列出来,以供参考:
- import random
- import torch
- from d2l import torch as d2l
-
- def synthetic_data(w,b,num_examples):
- X = torch.normal(0,1,(num_examples,len(w)))
- y = torch.matmul(X,w)+b
- y += torch.normal(0, 0.1, y.shape)
- return X,y.reshape((-1,1))
-
- def data_iter(batch_size, features, labels):
- num_examples = len(features)
- indices = list(range(num_examples))
- # 这些样本是随机读取的,没有特定的顺序
- random.shuffle(indices)
- 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]
-
- def linreg(X, w ,b):
- return torch.matmul(X ,w) + b
-
- def squared_loss(y_hat, y):
- return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
-
- def sgd(params, lr, batch_size):
- 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)
- print('features:', features[0],'\nlabel:', labels[0])
-
- a = torch.ones(3)
- b = a.numpy()
- f = features[:,1].numpy()
- l = labels.numpy()
-
- d2l.set_figsize()
- d2l.plt.scatter(f,l,1)
- d2l.plt.show()
-
- batch_size = 10
-
- #for X, y in data_iter(batch_size, features, labels):
- # print(X, '\n', y)
-
- w = torch.normal(0., 0.01, size = (2,1), requires_grad= True)
- b = torch.zeros(1 , requires_grad = True)
- lr = 0.03
- num_epochs = 10
- 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():#是一个上下文管理器,被该语句 wrap 起来的部分将不会track梯度
- 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}')
-
如果未来发现一些新的问题,我会持续更新该博文。
在学习了softmax部分内容后,我发现有一些需要注意的点,首先:
softmax章节里关于Fashion-MNIST数据集可视化的代码:
- def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): #@save
- figsize = (num_cols * scale, num_rows * scale)
- _, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)
- axes = axes.flatten()
- for i, (ax, img) in enumerate(zip(axes, imgs)):
- if torch.is_tensor(img):
- ax.imshow(img.numpy())
- else:
- ax.imshow(img)
- ax.axes.get_xaxis().set_visible(False)
- ax.axes.get_yaxis().set_visible(False)
- if titles:
- ax.set_title(titles[i])
- d2l.plt.show()
- return axes
之前已经介绍过,要在最后添加:d2l.plt.show()。结果如下:

另外,注意下面代码:
- y = torch.tensor([0, 2])
- y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
- y_hat[[0, 1], y]
这段代码其实要注意最后一句,y_hat[[0, 1], y]意思是以tensor y内部的内容作为索引号,对y_bat内部的元素0和1提取对应索引的值。y的第一个元素为0,进而提取y_hat第0个元素内的第0个索引值,即0.1。因此输出的结果为tensor([0.1000, 0.5000])。在定义网络结构和交叉熵的时候,作者的代码写的很巧妙,以至于不太直观,如下:
- def net(X):
- return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)
-
- def cross_entropy(y_hat, y):
- return - torch.log(y_hat[range(len(y_hat)), y])
X.resahpe((-1, W.shape[0]))即按照图片的顺序,将X按照每一幅图拉成一个一维向量。-1表示的是缺损值。假设全部数据是L = [18,1,28,28], 18是一个batchsize的尺寸。W.shape[0]表示18,即数据集的尺寸,那么reshape就是(L/18, 18)。这里是为了乘以W(18, 40)而做的转置。而在交叉熵函数中,y_hat[range(len(y_hat)), y],如提到的,就是按照y的索引,来提取y_hat每一个元素对应索引位置的值,即softmax的最大概率值,即实现公式:
