2.1 数据操作
入门
x = torch.arange(16)
# 结果 tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
可以通过张量的shape属性来访问张量(沿每个轴的长度: x.shape
张量中元素的总数: x.numel()
要想改变一个张量的形状而不改变元素数量和元素值,可以调用reshape函数。可以通过-1来调用此自动计算出维度的功能。 即我们可以用x.reshape(-1,4)或x.reshape(3,-1)来取代x.reshape(3,4)。
X = x.reshape(2, 8)
# 结果
tensor([[ 0, 1, 2, 3, 4, 5, 6, 7],
[ 8, 9, 10, 11, 12, 13, 14, 15]])
使用全0、全1、其他常量,或者从特定分布中随机采样的数字]来初始化矩
print(torch.zeros((2, 3, 4))) # 形状为(2,3,4)的张量全0
print (torch.ones((2, 3, 4)))# 全1
print (torch.randn(3, 4)) # 均值为0、标准差为1的标准高斯分布(正态分布)中随机采样
print (torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])) # 包含数值的Python列表(或嵌套列表),来为所需张量中的每个元素赋予确定值]
# 结果
tensor([[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]],
[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]]])
tensor([[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]],
[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]]])
tensor([[ 1.2121, 0.0843, -1.4460, 0.1503],
[ 1.0111, -0.3631, 1.5734, -1.1158],
[ 0.0337, -0.4524, 0.2675, -0.1819]])
tensor([[2, 1, 4, 3],
[1, 2, 3, 4],
[4, 3, 2, 1]])
torch的广播机制(broadcast mechanism)
“广播”这一术语用于描述如何在形状不一的数组上应用算术运算。
在满足特定限制的前提下,较小的数组“广播至”较大的数组,使两者形状互相兼容。广播提供了一个向量化数组操作的机制,这样遍历就发生在C层面,而不是Python层面。广播可以避免不必要的数据复制,通常导向高效的算法实现。不过,也存在不适用广播的情形(可能导致拖慢计算过程的低效内存使用)。
可广播的一对张量需满足以下规则:
- 每个张量至少有一个维度。
- 迭代维度尺寸时,从尾部的维度开始,维度尺寸
或者相等,
或者其中一个张量的维度尺寸为 1 ,
或者其中一个张量不存在这个维度。
例如:
import torch
# 示例1:相同形状的张量总是可广播的,因为总能满足以上规则。
x = torch.empty(3, 6, 4)
y = torch.empty(3, 6, 4)
# 示例2:不可广播( a 不满足第一条规则)。
a = torch.empty((0,)) # 维度为0 输出 :tensor([])
b = torch.empty(2, 2)
# 示例3:a 和 b 可广播:
a = torch.empty(5, 4, 3, 1)
b = torch.empty( 4, 1, 1)
# 倒数第一个维度:两者的尺寸均为1
# 倒数第二个维度:b的尺寸为1
# 倒数第三个维度:两者尺寸相同
# 倒数第四个维度:b该维度不存在
# 示例4:不可广播,因为倒数第三个维度:4 != 3
a = torch.empty(6, 4, 3, 1)
b = torch.empty( 3, 1, 1)
索引和切片
第一个元素的索引是0,最后一个元素索引是-1; [-1]选择最后一个元素,可以用[1:3]选择第二个和第三个元素]:
X[-1], X[1:3]
# 结果
(tensor([ 8., 9., 10., 11.]),
tensor([[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]]))
除读取外,我们还可以通过指定索引来将元素写入矩阵
X[1, 2] = 999
# 结果
tensor([[ 0., 1., 2., 3.],
[ 4., 5., 999., 7.],
[ 8., 9., 10., 11.]])
[0:2, : ]访问第1行和第2行,其中“:”代表沿轴1(列)的所有元素。
X[0:2, :] = 12
# 结果
tensor([[12., 12., 12., 12.],
[12., 12., 12., 12.],
[ 8., 9., 10., 11.]])
节省内存
如果我们用Y = X + Y,我们将取消引用Y指向的张量,而是指向新分配的内存处的张量。
before = id(Y)
print('before:',before)
Y = Y + X
print('after:',id(Y))
id(Y) == before
# 结果
before: 1934625279808
after: 1934642804752
False
我们可以使用切片表示法将操作的结果分配给先前分配的数组,例如Y[:] = <expression>
Z = torch.zeros_like(Y)
print(Z)
print('id(Z):', id(Z))
Z[:] = X + Y
print('id(Z):', id(Z))
# 结果
tensor([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
id(Z): 1934642815840
id(Z): 1934642815840
如果在后续计算中没有重复使用X, 我们也可以使用X[:] = X + Y或X += Y来减少操作的内存开销。
before = id(X)
X += Y
id(X) == before
# 结果
True
转换为其他Python对象
print(type(X))
A = X.numpy()
B = torch.tensor(A)
type(A), type(B)
# 结果
<class 'torch.Tensor'>
(numpy.ndarray, torch.Tensor)
要(将大小为1的张量转换为Python标量),我们可以调用item函数或Python的内置函数。
a = torch.tensor([3.5])
a, a.item(), float(a), int(a)
# 结果
(tensor([3.5000]), 3.5, 3.5, 3)
练习
- 运行本节中的代码。将本节中的条件语句
X == Y
更改为X < Y
或X > Y
,然后看看你可以得到什么样的张量。
import torch
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]])
print(X > Y)
print(X < Y)
#结果
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]])
- 用其他形状(例如三维张量)替换广播机制中按元素操作的两个张量。结果是否与预期相同?
import torch
a = torch.arange(3).reshape((1, 3, 1))
b = torch.arange(4).reshape((2, 1, 2))
print(a)
print(b)
print(a+b)
print((a + b).size())
# 结果
tensor([[[0],
[1],
[2]]])
tensor([[[0, 1]],
[[2, 3]]])
tensor([[[0, 1],
[1, 2],
[2, 3]],
[[2, 3],
[3, 4],
[4, 5]]])
torch.Size([2, 3, 2])
2.2 数据预处理
在Python中常用的数据分析工具中,我们通常使用pandas软件包。pandas可以与张量兼容.
读取数据集
创建一个人工数据集,并存储在CSV(逗号分隔值)文件
import os
os.makedirs(os.path.join('..', 'data'), exist_ok=True) # 新建文件夹
print(os.path.join('..', 'data'))
data_file = os.path.join('..', 'data', 'house_tiny.csv') # 文件名
print(data_file)
with open(data_file, 'w') as f:
f.write('NumRooms,Alley,Price,doors,windows\n') # 列名
f.write('NA,Pave,127500,1,4\n') # 每行表示一个数据样本
f.write('2,NA,106000,2,NA\n')
f.write('4,NA,178100,4,10\n')
f.write('NA,NA,140000,NA,8\n')
..\data
..\data\house_tiny.csv
…/data/house_tiny.csv 文件如下:
从创建的CSV文件中加载原始数据集
import pandas as pd
data = pd.read_csv(data_file)
print(data)
NumRooms Alley Price doors windows
0 NaN Pave 127500 1.0 4.0
1 2.0 NaN 106000 2.0 NaN
2 4.0 NaN 178100 4.0 10.0
3 NaN NaN 140000 NaN 8.0
处理缺失值
注意,“NaN”项代表缺失值。 为了处理缺失的数据,典型的方法包括插值法和删除法,将data分成inputs和outputs
插值法:位置索引iloc
删除法:drop
inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = inputs.fillna(inputs.mean())
print(inputs)
NumRooms Alley
0 3.0 Pave
1 2.0 NaN
2 4.0 NaN
3 3.0 NaN
[对于inputs中的类别值或离散值,我们将“NaN”视为一个类别。] 由于“巷子类型”(“Alley”)列只接受两种类型的类别值“Pave”和“NaN”, pandas可以自动将此列转换为两列“Alley_Pave”和“Alley_nan”。 缺少巷子类型的行会将“Alley_Pave”和“Alley_nan”分别设置为0和1。
inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)
NumRooms Alley_Pave Alley_nan
0 3.0 1 0
1 2.0 0 1
2 4.0 0 1
3 3.0 0 1
转换为张量格式
import torch
X, y = torch.tensor(inputs.values), torch.tensor(outputs.values)
print(inputs.values)
print(outputs.values)
array([[3., 1., 0.],
[2., 0., 1.],
[4., 0., 1.],
[3., 0., 1.]])
array([127500, 106000, 178100, 140000], dtype=int64)
练习
- 删除缺失值最多的列。
方案一 删除缺失值最多的列
#1. 删除缺失值最多的列。
print(data)
NA_data = data.isnull() # 判断缺乏值,有则为True
print(NA_data)
NA_sum = NA_data.sum()
print(NA_sum)
print(NA_sum.idxmax())
data1 = data.drop(NA_sum.idxmax(),axis=1)
print(data1)
NumRooms Alley Price doors windows
0 NaN Pave 127500 1.0 4.0
1 2.0 NaN 106000 2.0 NaN
2 4.0 NaN 178100 4.0 10.0
3 NaN NaN 140000 NaN 8.0
NumRooms Alley Price doors windows
0 True False False False False
1 False True False False True
2 False True False False False
3 True True False True False
NumRooms 2
Alley 3
Price 0
doors 1
windows 1
dtype: int64
Alley
NumRooms Price doors windows
0 NaN 127500 1.0 4.0
1 2.0 106000 2.0 NaN
2 4.0 178100 4.0 10.0
3 NaN 140000 NaN 8.0
方案二 删除缺失值最多的列
print(data)
print(data.count())# 按列取得非缺失值的个数
print(data.count().idxmin())
data2 = data.drop(data.count().idxmin(),axis=1)
data2
NumRooms Alley Price doors windows
0 NaN Pave 127500 1.0 4.0
1 2.0 NaN 106000 2.0 NaN
2 4.0 NaN 178100 4.0 10.0
3 NaN NaN 140000 NaN 8.0
NumRooms 2
Alley 1
Price 4
doors 3
windows 3
dtype: int64
Alley
NumRooms Price doors windows
0 NaN 127500 1.0 4.0
1 2.0 106000 2.0 NaN
2 4.0 178100 4.0 10.0
3 NaN 140000 NaN 8.0
- 将预处理后的数据集转换为张量格式。
torch.tensor(data2.values)
tensor([[ nan, 1.2750e+05, 1.0000e+00, 4.0000e+00],
[2.0000e+00, 1.0600e+05, 2.0000e+00, nan],
[4.0000e+00, 1.7810e+05, 4.0000e+00, 1.0000e+01],
[ nan, 1.4000e+05, nan, 8.0000e+00]], dtype=torch.float64)
2.3 线性代数
小结
- 实例化标量
x = torch.tensor(3.0)
- 创建一维张量
x = torch.arange(4)
- 访问张量的长度
len(x)
- 获取张量形状
x.shape
- 创建5行4列的矩阵
A = torch.arange(20).reshape(5, 4)
,矩阵转置A.T
- 指定元素的张量
B = torch.tensor([[1, 2, 3], [2, 0, 4], [3, 4, 5]])
- 张量
X = torch.arange(24).reshape(2, 3, 4) # 通道数 行数 列数
- 计算其元素的和
x.sum()
求行和 :A_sum_axis1 = A.sum(axis=1)
列和:A_sum_axis0 = A.sum(axis=0
- 计算总和或均值时保持轴数不变
sum_A = A.sum(axis=1, keepdims=True)
- 某个轴计算A元素的累积总和
A.cumsum(axis=0)
- 点积
torch.dot(x, y)
- 向量积A*x
torch.mv(A, x)
- 矩阵乘法
torch.mm(A, B)
- 求2范数:
torch.norm(u)
, 1范数:torch.abs(u).sum()
,计算矩阵的Frobenius范数:torch.norm(torch.ones((4, 9)))
练习
- 证明一个矩阵 A \mathbf{A} A的转置的转置是 A \mathbf{A} A,即 ( A ⊤ ) ⊤ = A (\mathbf{A}^\top)^\top = \mathbf{A} (A⊤)⊤=A
A.T.T == A
# 输出结果
tensor([[True, True, True, True],
[True, True, True, True],
[True, True, True, True],
[True, True, True, True],
[True, True, True, True]])
- 给出两个矩阵 A \mathbf{A} A和 B \mathbf{B} B,证明“它们转置的和”等于“它们和的转置”,即 A ⊤ + B ⊤ = ( A + B ) ⊤ \mathbf{A}^\top + \mathbf{B}^\top = (\mathbf{A} + \mathbf{B})^\top A⊤+B⊤=(A+B)⊤
B = torch.ones(5,4)
A.T + B.T == (A + B).T
tensor([[True, True, True, True, True],
[True, True, True, True, True],
[True, True, True, True, True],
[True, True, True, True, True]])
-
给定任意方阵 A \mathbf{A} A, A + A ⊤ \mathbf{A} + \mathbf{A}^\top A+A⊤总是对称的吗?为什么?
( A + A T ) T = A T + A (A+A^{T})^{T} = A^{T} +A (A+AT)T=AT+A -
本节中定义了形状 ( 2 , 3 , 4 ) (2,3,4) (2,3,4)的张量X。len(X)的输出结果是什么?
# 4
X = torch.arange(24).reshape(2,3,4)
len(X) # 2 输出结果为通道数
- 对于任意形状的张量
X
,len(X)
是否总是对应于X
特定轴的长度?这个轴是什么?
(2,3,4)分别对应0轴,1轴,2轴,**len(X)**
** 输出结果对应于X的0轴。**
- 运行
A/A.sum(axis=1)
,看看会发生什么。请分析一下原因?
A/A.sum(axis=1)
# 运行错误,因为A.sum(axis)向量是一个(1,5)不能形成boradcasting
---------------------------------------------------------------------------
A/A.sum(axis=1),A,A.sum(axis=1) # 当A是4*4矩阵,A.sum(axis=1)为1*4时可以计算,因为可以进行广播
# 运行结果
(tensor([[0.0000, 0.0455, 0.0526, 0.0556],
[0.6667, 0.2273, 0.1579, 0.1296],
[1.3333, 0.4091, 0.2632, 0.2037],
[2.0000, 0.5909, 0.3684, 0.2778]]),
tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]]),
tensor([ 6, 22, 38, 54]))
- 考虑一个具有形状 ( 2 , 3 , 4 ) (2,3,4) (2,3,4)的张量,在轴0、1、2上的求和输出是什么形状?
X = torch.arange(24).reshape(2,3,4)
X,X.sum(axis=0),X.sum(axis=1),X.sum(axis=2) # 可见,0轴3*4,1轴2*4,2轴2*3
# 运行结果
(tensor([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]]),
tensor([[12, 14, 16, 18],
[20, 22, 24, 26],
[28, 30, 32, 34]]),
tensor([[12, 15, 18, 21],
[48, 51, 54, 57]]),
tensor([[ 6, 22, 38],
[54, 70, 86]]))
- 为
linalg.norm
函数提供3个或更多轴的张量,并观察其输出。对于任意形状的张量这个函数计算得到什么?
B = torch.ones(2,4,5)
C = torch.ones(2,3,4,5)
torch.norm(B),torch.norm(C)
# 输出结果
(tensor(6.3246), tensor(10.9545))
--------------------------------------------------------------------
torch.norm(B)*torch.norm(B),torch.norm(C)*torch.norm(C)
# 输出结果
(tensor(40.), tensor(120.0000))
2.4 微积分
- 优化(optimization):用模型拟合观测数据的过程;
- 泛化(generalization):数学原理和实践者的智慧,能够指导我们生成出有效性超出用于训练的数据集本身的模型。
小结
%matplotlib inline
加在语句块的第一句,作用就是这样图象就会出现在Notebook里面,而不是一个新窗口里。只在Jupyter Notebook上有用,加到pycharm上没有用- #@save是一个特殊的标记,会将对应的函数、类或语句保存在d2l包中
- 使用svg格式在Jupyter中显示绘图:
backend_inline.set_matplotlib_formats('svg')
练习
- 绘制函数 y = f ( x ) = x 3 − 1 x y = f(x) = x^3 - \frac{1}{x} y=f(x)=x3−x1和其在 x = 1 x = 1 x=1处切线的图像。
import numpy as np
from matplotlib import pyplot as plt
def f(x):
return x**3-x**(-1)
def get_tangent(f, x, point): # 切线
h = 1e-4
grad = (f(point + h) - f(point)) / h
return grad*(x-point)+f(point)
# 画图
x = np.arange(0, 3, 0.1)
y = f(x)
y_tangent = get_tangent(f,x,point=1) # 切线
plt.plot(x,y,label='f(x)')
plt.plot(x,y_tangent,'m--',label='Tangent line (x=1)')
plt.legend()
plt.grid()
- 求函数 f ( x ) = 3 x 1 2 + 5 e x 2 f(\mathbf{x}) = 3x_1^2 + 5e^{x_2} f(x)=3x12+5ex2的梯度。
- 函数 f ( x ) = ∥ x ∥ 2 f(\mathbf{x}) = \|\mathbf{x}\|_2 f(x)=∥x∥2的梯度是什么?
- 尝试写出函数 u = f ( x , y , z ) u = f(x, y, z) u=f(x,y,z),其中 x = x ( a , b ) x = x(a, b) x=x(a,b), y = y ( a , b ) y = y(a, b) y=y(a,b), z = z ( a , b ) z = z(a, b) z=z(a,b)的链式法则。
2.5 自动微分
深度学习框架通过自动计算导数,即自动微分**(automatic differentiation)来加快求导。系统会构建一个_计算图(computational graph), 来跟踪计算是哪些数据通过哪些操作组合起来产生输出。 自动微分使系统能够随后反向传播梯度。 这里,反向传播_(**backpropagate)意味着跟踪整个计算图,填充关于每个参数的偏导数
小结
- 在我们计算𝑦关于𝐱的梯度之前,需要一个地方来存储梯度。
x=torch.arange(4.0,requires_grad=True)
,等价于x.requires_grad_(True)
- **调用反向传播函数来自动计算y关于x每个分量的梯度 **
y = 2 * torch.dot(x, x)
y.backward()
x.grad # 得到x每个分量的梯度
- **现在计算x的另一个函数, **在默认情况下,PyTorch会累积梯度,我们需要清除之前的值
x.grad.zero_()
- 当调用向量的反向计算时,我们通常会试图计算一批训练样本中每个组成部分的损失函数的导数。
- 对非标量调用backward需要传入一个gradient参数,该参数指定微分函数关于self的梯度。通常情况下,gradient参数也称为输入梯度(input gradient),其是一个 tensor,其形状与需要求导的 tensor 一样,并且在需要求导的 tensor 上执行 backward 函数时,该 tensor 需要传入 gradient 参数。对于如何确定 gradient 参数的值,通常有以下几种方法:
-
全部设为 1:对于有些情况下,输入梯度对输出的影响是相同的,这种情况下可以将 gradient 设为全 1。
-
从网络的输出处开始反向传播:对于需要求导的 tensor,在向前传播时跟踪其所经过的每个操作,并将反向传播的梯度流沿途保存下来。最后,将梯度流作为 gradient 参数传入 backward 函数中。
-
手动指定一个梯度值:通过手动指定输入梯度,可以观察模型的响应,从而更好地理解模型的行为。
y.backward(torch.ones(len(x)))
等价于y.sum().backward()
- 分离计算:**将某些计算移动到记录的计算图之外 **
u = y.detach() # 将u作为常数处理
- 自动微分的一个好处是: [即使构建函数的计算图需要通过Python控制流(例如,条件、循环或任意函数调用),我们仍然可以计算得到的变量的梯度
练习
- 为什么计算二阶导数比一阶导数的开销要更大?
二阶导数是一阶导数的导数,计算二阶导数需要用到一阶导数,所以开销会比一阶导数更大
- 在运行反向传播函数之后,立即再次运行它,看看会发生什么。
会报错,因为进行一次backward之后,计算图中的中间变量在计算完后就会被释放,之后无法进行二次backward了,如果想进行第二次backward,可以将retain_graph置为True。
- 在控制流的例子中,我们计算
d
关于a
的导数,如果将变量a
更改为随机向量或矩阵,会发生什么?
将变量a
更改为随机向量或矩阵,会报错
原因可能是在执行 loss.backward() 时没带参数,即可能默认是与 loss.backward(torch.Tensor(1.0)) 相同的。
def f(a):
b = a * 2
while b.norm() < 1000:
b = b * 2
if b.sum() > 0:
c = b
else:
c = 100 * b
return c
a = torch.randn(size=(3,), requires_grad=True)
d = f(a)
d.backward(torch.ones_like(d))
a.grad == d / a
print(a.grad)
# 输出结果
tensor([True, True, True])
tensor([2048., 2048., 2048.])
- 重新设计一个求控制流梯度的例子,运行并分析结果。
# 4 订单数为10,y=x^2;订单数为20时,y=x^3
import torch
def f(x, order):
if order == 10:
y = x**2
elif order == 20:
y = x**3
else:
return x
return y
x = torch.randn(size=(), requires_grad=True)
print(x)
y = f(x, order=10)
y.backward()
print(x.grad)
x.grad.zero_() # 清除梯度
y = f(x, order=20)
y.backward()
print(x.grad)
# 输出结果
tensor(-0.0091, requires_grad=True)
tensor(-0.0183)
tensor(0.0003)
- 使 f ( x ) = sin ( x ) f(x)=\sin(x) f(x)=sin(x),绘制 f ( x ) f(x) f(x)和 d f ( x ) d x \frac{df(x)}{dx} dxdf(x)的图像,其中后者不使用 f ′ ( x ) = cos ( x ) f'(x)=\cos(x) f′(x)=cos(x)。
# 5
import numpy as np
import torch
from matplotlib import pyplot as plt
def function(x):
return torch.sin(x)
def get_derivative(function, x): # 求导数
h = 1e-4 # 步长
return (function(x+h) - function(x)) / h
x = torch.arange(0.01,10.0,0.01)
y = function(x)
y_derivative = get_derivative(function, x)
plt.plot(x,y,label='function(x)')
plt.plot(x,y_derivative,'m--',label='y_derivative')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()
2.6 概率
小结
- 抽取一个样本:
fair_probs = torch.ones([6]) / 6
multinomial.Multinomial(1, fair_probs).sample()
# 结果
tensor([0., 0., 0., 1., 0., 0.])
- 抽多个样本:
multinomial.Multinomial(10, fair_probs).sample()
# 输出
tensor([4., 1., 1., 1., 1., 2.])
- 500组实验,每组抽取10个样本
counts = multinomial.Multinomial(10, fair_probs).sample((500,))
- 𝐴和𝐵是独立的,表述为𝐴⊥𝐵。𝑃(𝐴,𝐵∣𝐶)=𝑃(𝐴∣𝐶)𝑃(𝐵∣𝐶)。 这个情况表示为𝐴⊥𝐵∣𝐶。
- 贝叶斯定理:𝑃(𝐴∣𝐵)=𝑃(𝐵∣𝐴)𝑃(𝐴)/𝑃(𝐵)
- 边际化: 𝑃(𝐵)=∑𝐴𝑃(𝐴,𝐵)
- 条件概率: 0≤𝑃(𝐴=𝑎,𝐵=𝑏) / 𝑃(𝐴=𝑎)≤1
- 期望(expectation,或平均值): 𝐸[𝑋]=∑𝑥𝑃(𝑋=𝑥)
- 方差: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OlTJqPD9-1679496228468)(null#card=math&code=\mathrm{Var}[X] = E\left[(X - E[X])]^2\right] =E[X^2] - E[X]^2.&id=QDjir)
练习
- 进行 m = 500 m=500 m=500组实验,每组抽取 n = 10 n=10 n=10个样本。改变 m m m和 n n n,观察和分析实验结果。
# 1000组实验,每组抽取20个样本
print(counts)
counts = multinomial.Multinomial(20, fair_probs).sample((1000,))
cum_counts = counts.cumsum(dim=0)
estimates = cum_counts / cum_counts.sum(dim=1, keepdims=True)
# print( cum_counts.sum(dim=1, keepdims=True))
print(estimates)
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();
tensor([[5., 0., 2., 6., 3., 4.],
[4., 4., 3., 4., 3., 2.],
[1., 4., 4., 4., 4., 3.],
...,
[6., 2., 2., 3., 4., 3.],
[2., 4., 5., 1., 2., 6.],
[6., 3., 4., 2., 2., 3.]])
tensor([[0.2500, 0.0000, 0.1000, 0.3000, 0.1500, 0.2000],
[0.2250, 0.1000, 0.1250, 0.2500, 0.1500, 0.1500],
[0.1667, 0.1333, 0.1500, 0.2333, 0.1667, 0.1500],
...,
[0.1670, 0.1632, 0.1627, 0.1712, 0.1689, 0.1670],
[0.1670, 0.1632, 0.1628, 0.1711, 0.1688, 0.1671],
[0.1671, 0.1632, 0.1629, 0.1710, 0.1688, 0.1671]])
根据上面的输出概率,可见都在1/6附近,相对于m=500的实验,更接近1/6。通过对比两次实验,可以看出实验数越多,概率越接近0.167。
- 给定两个概率为 P ( A ) P(\mathcal{A}) P(A)和 P ( B ) P(\mathcal{B}) P(B)的事件,计算 P ( A ∪ B ) P(\mathcal{A} \cup \mathcal{B}) P(A∪B)和 P ( A ∩ B ) P(\mathcal{A} \cap \mathcal{B}) P(A∩B)的上限和下限。(提示:使用友元图来展示这些情况。)
- 假设我们有一系列随机变量,例如 A A A、 B B B和 C C C,其中 B B B只依赖于 A A A,而 C C C只依赖于 B B B,能简化联合概率 P ( A , B , C ) P(A, B, C) P(A,B,C)吗?(提示:这是一个马尔可夫链。)
- 在2.6.2.6节中,第一个测试更准确。为什么不运行第一个测试两次,而是同时运行第一个和第二个测试?
因为在测试艾滋病病毒时候,第一个测试和第二个测试可以看作具有不同的特性(可以认为是不同测试针对的靶点有异),再次使用第一个测试,如果没有其他因素干扰,结果应该是一样的。所以同时使用第一个和第二个测试,针对不同的靶点,测试起来更有说服力,同时第一个测试和第二个测试独立也更有可能。然而重复第一个测试会使这两次有较强的相关性。