《动手学深度学习》第二章习题答案

《动手学深度学习》第二章地址:https://zh-v2.d2l.ai/chapter_preliminaries/index.html

2.1 练习

  1. 运行本节中的代码。将本节中的条件语句X == Y更改为X < YX > Y,然后看看你可以得到什么样的张量。

代码

X = torch.arange(12, dtype=torch.float32).reshape((3,4))
Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
X>Y, X

结果

(tensor([[False, False, False, False],
         [ True,  True,  True,  True],
         [ True,  True,  True,  True]]),
 tensor([[ True, False,  True, False],
         [False, False, False, False],
         [False, False, False, False]]))
  1. 用其他形状(例如三维张量)替换广播机制中按元素操作的两个张量。结果是否与预期相同?

代码

a = torch.arange(6).reshape((3, 1, 2))
b = torch.arange(2).reshape((1, 2, 1))
c = a + b
a.shape, b.shape, c.shape

结果

(torch.Size([3, 1, 2]), torch.Size([1, 2, 1]), torch.Size([3, 2, 2]))

可以看到结果与预期相同

2.2 练习

创建包含更多行和列的原始数据集。

生成数据

import os
os.makedirs(os.path.join('..', 'data'), exist_ok=True)
data_file = os.path.join('..', 'data', 'house_tiny.csv')
with open(data_file, 'w') as f:
    f.write('NumRooms,Alley,Price,Size\n')  # 列名,新增一列为Size
    f.write('NA,Pave,127500,100\n')  # 每行表示一个数据样本
    f.write('2,NA,106000,150\n')
    f.write('4,NA,178100,200\n')
    f.write('NA,NA,140000,NA\n')
    f.write('3,Pave,NA,NA\n') # 新增一行
  1. 删除缺失值最多的列。

代码

import pandas as pd
data = pd.read_csv(data_file)
data.isna().sum(axis=0)  # 查看缺失值最多的列, 缺失值最多的为Alley列
data.drop('Alley', axis=1, inplace=True)

结果

缺失值个数统计:

NumRooms    2
Alley       3
Price       1
Size        2
dtype: int64

删除缺失值最多的列后的数据

   NumRooms     Price   Size
0       NaN  127500.0  100.0
1       2.0  106000.0  150.0
2       4.0  178100.0  200.0
3       NaN  140000.0    NaN
4       3.0       NaN    NaN
  1. 将预处理后的数据集转换为张量格式

预处理的话,我们考虑前向后向填充,将缺失值填补上

代码

data = data.fillna(method='bfill')
data = data.fillna(method='ffill')
torch.tensor(data.values)

结果

tensor([[2.0000e+00, 1.2750e+05, 1.0000e+02],
        [2.0000e+00, 1.0600e+05, 1.5000e+02],
        [4.0000e+00, 1.7810e+05, 2.0000e+02],
        [3.0000e+00, 1.4000e+05, 2.0000e+02],
        [3.0000e+00, 1.4000e+05, 2.0000e+02]], dtype=torch.float64)

2.3 练习

  1. 证明一个矩阵 的转置的转置是,即 。

对于中的一个元素aij,其在第 i 行第 j 列,转置后出现在第 j 行第 i 列,再转置一次就出现在第 i 行第 j 列,又回到了原来的位置。所以,一个矩阵转置的转置还是它自己。

代码

import numpy as np
A = np.arange(12).reshape(3, 4)
A.T.T == A

结果

array([[ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True]])
  1. 给出两个矩阵 和 ,证明“它们转置的和”等于“它们和的转置”,即 。

假设第 i 行第 j 列的元素是 aij,第 i 行第 j 列的元素是 bij。对于 ,其第 j 行第 i 列的值为 aij + bij;对于 ,其第 j 行第 i 列的值也为 aij + bij,推广至即可知。

代码

import numpy as np
A = np.arange(12).reshape(3, 4)
B = np.arange(12, 24).reshape(3, 4)
(A.T + B.T) == (A + B).T

结果

array([[ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True]])
  1. 给定任意方阵 , 总是对称的吗?为什么?

是的,首先我们看一下对称矩阵的概念:
“ 等于其转置:,则称为对称矩阵”。将 看做一个整体,证明如下:

  1. 我们在本节中定义了形状 (2,3,4) 的张量。的输出结果是什么?

代码

X = torch.arange(24).reshape(2, 3, 4)
len(X)

结果

2
输出的是0轴的数值。

  1. 对于任意形状的张量是否总是对应于X特定轴的长度?这个轴是什么?

总对应第 0 轴的长度,上题已回答。

  1. 运行A/A.sum(axis=1),看看会发生什么。你能分析原因吗?

会出错,原因在于A为5行4列的矩阵,维度为2,而A.sum(axis=1)则为长度为5的向量,二者无法相除,需要维度一致才会有广播机制,也就是我们需要设置keepdims=True

  1. 考虑一个具有形状 (2,3,4) 的张量,在轴0、1、2上的求和输出是什么形状?

课程中已讲过,对哪个轴求和,可以看作是压缩该维度,因此
轴0求和——>[3,4]
轴1求和——>[2,4]
轴2求和——>[2,3]
下面用代码验证一下

代码

X = torch.arange(24).reshape(2, 3, 4)
X.sum(axis=0).shape, X.sum(axis=1).shape, X.sum(axis=2).shape

结果

(torch.Size([3, 4]), torch.Size([2, 4]), torch.Size([2, 3]))
  1. 为linalg.norm函数提供3个或更多轴的张量,并观察其输出。对于任意形状的张量这个函数计算得到什么?

计算得到张量中所有值的 L2范数。
numpy.linalg.norm参数ord默认为None,此时对应的即为L2范数,见下表

ord norm for matrices norm for vectors
None Frobenius norm 2-norm

2.4 练习

书中的函数此处不再赘述

  1. 绘制函数 和其在 处切线的图像。

先求导数: 可得时,,又因为
因此切线为

代码

x = np.arange(0.5, 1.5, 0.01)
plot(x, [x**3 - 1/x, 4*x-4], 'x', 'f(x)', legend=['f(x)', 'Tangent line (x=1)'])

结果

Figure_1.png
  1. 求函数 的梯度。

  1. 函数的梯度是什么?

先对求导,可得:
同理类推:可得
因此其梯度为:[, ,……,]
简写为:

  1. 你可以写出函数 ,其中 ,, 的链式法则吗?

\frac{\partial u} {\partial a} = \frac{\partial u} {\partial x}\frac{\partial x} {\partial a}+\frac{\partial u} {\partial y}\frac{\partial y} {\partial a}+\frac{\partial u} {\partial z}\frac{\partial z} {\partial a}
\frac{\partial u} {\partial b} = \frac{\partial u} {\partial x}\frac{\partial x} {\partial b}+\frac{\partial u} {\partial y}\frac{\partial y} {\partial b}+\frac{\partial u} {\partial z}\frac{\partial z} {\partial b}

2.5 练习

  1. 为什么计算二阶导数比一阶导数的开销要更大?

因为计算二阶导数要在一阶导数的基础上再进行求导,开销肯定会更大。

  1. 在运行反向传播函数之后,立即再次运行它,看看会发生什么。

会发生运行错误,因为前向过程建立的计算图,会在反向传播后释放,所以第二次运行反向传播就会出错。这时在 backward 函数中加上参数 retain_graph=True(保持计算图),就能两次运行反向传播了。

代码

x = torch.arange(12., requires_grad=True)
y = 2 * torch.dot(x, x)
y.backward()
x.grad  #  tensor([ 0.,  4.,  8., 12., 16., 20., 24., 28., 32., 36., 40., 44.])
y.backward() # 会报错
  1. 在控制流的例子中,我们计算d关于a的导数,如果我们将变量a更改为随机向量或矩阵,会发生什么?

会发生运行错误,此处想说明的其实是pytorch中张量无法对张量求导,只能是标量对张量求导,因此如果我们将a修改为向量或矩阵,直接backward将会报错,需要对结果求和成一个标量后方能求导。

代码

a = torch.randn(size=(3,1), requires_grad=True)
d = f(a)
d.backward() # 直接运行这一步会报错
d.sum().backward() # 求和以后运行就没问题了
  1. 重新设计一个求控制流梯度的例子,运行并分析结果。

我们设计一个范数如果大于10,则返回梯度为2,否则返回梯度为1。以下为代码和结果

代码

def f(a):
    if a.norm() > 10:
        b = torch.dot(a, a)
    else:
        b = a
    return b.sum()
a1 = torch.arange(12., requires_grad=True)
a2 = torch.arange(2., requires_grad=True)
d1 = f(a1)
d2 = f(a2)
d1.backward()
d2.backward()
a1.grad, a2.grad

结果

(tensor([ 0.,  2.,  4.,  6.,  8., 10., 12., 14., 16., 18., 20., 22.]), # 范数大于10时,梯度为2
 tensor([1., 1.])) # 范数小于10时,梯度为1
  1. 使 f(x)=sin(x) ,绘制 f(x) 和 df(x)dx 的图像,其中后者不使用 f′(x)=cos(x) 。

代码

x = torch.linspace(-5, 5, 100)
x.requires_grad_(True)
y = torch.sin(x)
y.sum().backward()
y = y.detach()
d2l.plot(x.detach(), [y, x.grad], 'f(x)', "f'(x)", legend=['f(x)', 'Tangent line']) # 不采用cos(x),而使用x.grad
d2l.plt.show()

结果

Figure_1.png

2.6 练习

  1. 进行组实验,每组抽取个样本。改变和,观察和分析实验结果。

书中已有的结果,此处将换为,换为,查看结果

代码

import torch
from torch.distributions import multinomial
from d2l import torch as d2l
fair_probs = torch.ones([6]) / 6
counts = multinomial.Multinomial(20, fair_probs).sample((5000,))
cum_counts = counts.cumsum(dim=0)
estimates = cum_counts / cum_counts.sum(dim=1, keepdims=True)
d2l.set_figsize((6, 4.5))
for i in range(6):
    d2l.plt.plot(estimates[:, i].numpy(),
                 label=("P(die=" + str(i + 1) + ")"))
d2l.plt.axhline(y=0.167, color='black', linestyle='dashed')
d2l.plt.gca().set_xlabel('Groups of experiments')
d2l.plt.gca().set_ylabel('Estimated probability')
d2l.plt.legend()

结果

m=500,n=10 m=5000,n=20
1.png
2.png

对比以上结果可知,第二次的效果更好,更加趋近于 0.167。原因可能是第一张图实验组数为 500 组,而第二张图有 5000 组,实验组数越多,实验偏差就会分布的更加平均,效果就会更好。(此处对比试验最好将第二张图的n也设置为10,不过限于篇幅就不再展示了~大家可以下来多尝试)

  1. 给定两个概率为的事件,计算、的上限和下限。

上限为,下限为

image.png
上限 下限
2022-01-06 11-41-51 的屏幕截图.png
2022-01-06 11-42-04 的屏幕截图.png

上限为,下限为

image.png

上限 下限
2022-01-06 11-44-33 的屏幕截图.png
2022-01-06 11-44-59 的屏幕截图.png
  1. 假设我们有一系列随机变量,例如 、 和 ,其中 只依赖于 ,而 只依赖于 ,你能简化联合概率 吗?

根据题意可得:满足马尔科夫链 下面开始推导

其中 =====>马尔科夫链
因此最终化简为:

  1. 在 2.6.2.6节中,第一个测试更准确。为什么不运行第一个测试两次,而是同时运行第一个和第二个测试?

原因是如果进行两次第一种测试,两次测试没有条件独立性,不能使用上面的公式进行计算,而且两次测试结果大概率相同,效果并不好。

你可能感兴趣的:(《动手学深度学习》第二章习题答案)