先考虑一个最简单的情形,即生成的布尔型张量中,每个元素等于 True 的概率都为
50
%
50\%
50%。
思路很简单,我们只需创建两个形状相同的整型张量,里面的元素非 0 0 0 即 1 1 1,然后判断它们是否相等:
def randbool(*size):
return torch.randint(2, size) == torch.randint(2, size)
例如:
print(randbool(3, 3))
# tensor([[False, False, False],
# [False, True, True],
# [ True, False, True]])
这是因为,对于每一个元素,它在第一个张量中的取值只有两种可能: 0 0 0 或 1 1 1,在第二个张量中的取值也只有两种可能: 0 0 0 或 1 1 1,因此共有四种情形:
0
=
=
0
⇒
True
0
=
=
1
⇒
False
1
=
=
0
⇒
False
1
=
=
1
⇒
True
0==0⇒True0==1⇒False1==0⇒False1==1⇒True
可以看出 True 的概率为
2
/
4
=
0.5
2/4=0.5
2/4=0.5。
我们还可以通过程序来验证 True 的概率:
s = 0
for i in range(10 ** 5):
s += randbool(4, 4).sum().item()
s = s / 10 ** 5
print(s / 16)
# 0.499908125
将其封装成函数便于之后使用
def true_prob(fun):
s = 0
for i in range(10 ** 6):
s += fun(10, 10).sum().item()
return s / 10 ** 8
再来考虑一个也比较简单的情形,即生成的布尔型张量中,每个元素等于 True 的概率都为
1
/
3
1/3
1/3。
假设对于每一个元素,它在第一个张量中的取值只有两种可能: 0 , 1 0,1 0,1,但在第二个张量中的取值有三种可能: 0 , 1 , 2 0,1,2 0,1,2,因此共有六种情形:
0
=
=
0
⇒
True
0
=
=
1
⇒
False
0
=
=
2
⇒
False
1
=
=
0
⇒
False
1
=
=
1
⇒
True
1
=
=
2
⇒
False
0==0⇒True0==1⇒False0==2⇒False1==0⇒False1==1⇒True1==2⇒False
可以看出 True 的概率为
2
/
6
=
1
/
3
2/6=1/3
2/6=1/3。
代码实现如下:
def randbool(*size):
return torch.randint(2, size) == torch.randint(3, size)
验证:
print(true_prob(randbool))
# 0.33325818
有没有可能指定 True 的概率为任意概率
p
p
p 呢?
答案是可以的,不过我们先来解释一下为什么之前的方法会行不通。
假设第一个张量的取值范围为
0
,
1
,
⋯
,
m
−
1
0,1,\cdots,m-1
0,1,⋯,m−1,第二个张量的取值范围为
0
,
1
,
⋯
,
n
−
1
0,1,\cdots,n-1
0,1,⋯,n−1,则总共会产生
m
n
mn
mn 个判断条件,而这
m
n
mn
mn 个判断条件里只有
min
(
m
,
n
)
\min(m,n)
min(m,n) 个为 True,因此 True 的概率为
P
(
True
)
=
min
(
m
,
n
)
m
n
=
{
1
n
,
m
≤
n
1
m
,
m
>
n
=
1
max
(
m
,
n
)
P(\text{True})=\frac{\min(m,n)}{mn}= {1n,m≤n1m,m>n
∀ p ∈ ( 0 , 1 ) \forall p\in(0,1) ∀p∈(0,1),令 P ( True ) = p P(\text{True})=p P(True)=p,则有 max ( m , n ) = 1 p \max(m,n)=\frac1p max(m,n)=p1,左边是整数,右边是浮点数,因此无法建立相等关系。即使对右边取整,我们也无法良好地去逼近这个概率 p p p。
例如,取 p = 0.7 p=0.7 p=0.7,则 1 p ≈ 1.428 \frac1p\approx 1.428 p1≈1.428,对其取整后为 1 1 1 或 2 2 2,此时令 max ( m , n ) \max(m,n) max(m,n) 为 1 1 1 或 2 2 2,则 P ( True ) P(\text{True}) P(True) 为 0.5 0.5 0.5 或 1 1 1,与真实的 0.7 0.7 0.7 相差甚远。
对于以上情形,只有当
p
p
p 充分小时才能良好地近似
p
p
p,而我们希望的是
∀
p
∈
(
0
,
1
)
\forall p\in(0,1)
∀p∈(0,1) 都能有良好的近似。为此考虑不再将张量中每个元素的值限定为整数,而是将其放松至
(
0
,
1
)
(0,1)
(0,1) 区间内的浮点数。对于每个元素,如果其值小于
p
p
p,则将该元素设置为 True,否则为 False,如下:
def randbool(size, p=0.5):
return torch.rand(*size) < p
def true_prob(fun):
s = 0
for i in range(10 ** 6):
s += fun((10, 10), 0.7).sum().item()
return s / 10 ** 8
print(true_prob(randbool))
# 0.69995753