Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【Hackathon 5th No.19】Update rfcs of ContinuousBernoulli and MultivariateNormal API #783

Merged
merged 6 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# paddle.distribution.continuous_bernoulli 设计文档
# paddle.distribution.ContinuousBernoulli 设计文档

|API名称 | paddle.distribution.continuous_bernoulli |
|API名称 | paddle.distribution.ContinuousBernoulli |
|---|---|
|提交作者<input type="checkbox" class="rowselector hidden"> | NKNaN |
|提交时间<input type="checkbox" class="rowselector hidden"> | 2023-09-27 |
|版本号 | V1.1 |
|版本号 | V1.2 |
|依赖飞桨版本<input type="checkbox" class="rowselector hidden"> | develop版本 |
|文件名 | 20230927_api_design_for_continuous_bernoulli.md<br> |
|文件名 | 20230927_api_design_for_ContinuousBernoulli.md<br> |


# 一、概述
## 1、相关背景
连续伯努利分布是一种定义在 $[0, 1]$ 闭区间上的概率分布, 它具有一个描述分布函数的形状的参数 $\lambda \in (0, 1)$, 该参数对连续伯努利概率密度函数的影响如下: $f(x|\lambda) \propto \lambda^x (1 - \lambda)^{1-x}$。它属于指数分布族, 可以被看作连续版的伯努利分布。而在机器学习中, 伯努利分布可以推导出二元数据的交叉熵损失, 于是连续伯努利分布就可以为连续型数据提供一种类似交叉熵的损失衡量方法。例如在 VAE 中, 若数据非二元分布而是分布在 $[0, 1]$ 区间上, [The continuous Bernoulli: fixing a pervasive error in
variational autoencoders](https://proceedings.neurips.cc/paper_files/paper/2019/file/f82798ec8909d23e55679ee26bb26437-Paper.pdf)指出: 如果用传统做法先将其转化为二元 $\{0, 1\}$ 分布, 再将先验分布选择为普通的伯努利分布, 这样的做法是不能够很好的用统计理论来进行解释的, 因为在非监督学习中为了fit模型去修改数据这样的做法本身就有问题, 会丢失很多信息等等。 于是这篇论文提出了这种新的分布作为 VAE 的先验分布用以处理连续型数据。因此为提升飞桨的概率分布 API 丰富度, 从而为更多的机器学习应用提供可能, 需要扩充 API `paddle.distribution.continuous_bernoulli`。
variational autoencoders](https://proceedings.neurips.cc/paper_files/paper/2019/file/f82798ec8909d23e55679ee26bb26437-Paper.pdf)指出: 如果用传统做法先将其转化为二元 $\{0, 1\}$ 分布, 再将先验分布选择为普通的伯努利分布, 这样的做法是不能够很好的用统计理论来进行解释的, 因为在非监督学习中为了fit模型去修改数据这样的做法本身就有问题, 会丢失很多信息等等。 于是这篇论文提出了这种新的分布作为 VAE 的先验分布用以处理连续型数据。因此为提升飞桨的概率分布 API 丰富度, 从而为更多的机器学习应用提供可能, 需要扩充 API `paddle.distribution.ContinuousBernoulli`。

## 2、功能目标
参考 Paddle 现有 distribution,增加 ContinuousBernoulli 分布类的概率统计与随机采样,包括如下方法:
Expand Down Expand Up @@ -653,9 +653,10 @@ class ContinuousBernoulli(distribution.AutoCompositeTensorDistribution):

## 命名与参数设计
```python
paddle.distribution.continuous_bernoulli(probs)
paddle.distribution.ContinuousBernoulli(probs, lims=(0.499, 0.501))
```
参数 `probs` 为 ContinuousBernoulli 分布的参数。
- 参数 `probs` 为 ContinuousBernoulli 分布的参数。
- 参数 `lims` 表示非稳定计算区域。

例如,随机变量 $X$ 服从 ContinuousBernoulli 分布,即 $X \sim ContinuousBernoulli(\lambda)$ ,对应的参数 `probs`$=\lambda$。

Expand All @@ -667,16 +668,16 @@ paddle.distribution.continuous_bernoulli(probs)

```python
class ContinuousBernoulli(Distribution):
def __init__(self, probs, eps=1e-4):
def __init__(self, probs, lims=(0.499, 0.501)):
super().__init__(batch_shape=self.probs.shape, event_shape=())

...

```

`ContinuousBernoulli` 类的初始化参数是 `probs` ,类包含的方法及实现方案如下:
`ContinuousBernoulli` 类的初始化参数是 `probs` 和 `lims` ,类包含的方法及实现方案如下:

记参数 `probs`$=\lambda$, `eps` 为非稳定计算区域范围: $[0.5-eps, 0.5+eps]$
记参数 `probs`$=\lambda$, `lims` 为非稳定计算区域范围

- `mean` 计算均值

Expand All @@ -690,6 +691,10 @@ E(X) =
\end{aligned}
\right.
```
在非稳定计算区域做泰勒展开:
```math
E(X) = \frac{1}{2} + \frac{1}{3}(\lambda - 0.5) + \frac{16}{45}(\lambda - 0.5)^2
```

- `variance` 计算方差

Expand All @@ -703,14 +708,36 @@ Var(X) =
\end{aligned}
\right.
```
在非稳定计算区域做泰勒展开:
```math
Var(X) = \frac{1}{12} + \frac{1}{15}(\lambda - 0.5)^2 + \frac{128}{945}(\lambda - 0.5)^4
```

- `entropy` 熵计算

熵的计算方法: $H = - \sum_x f(x) \log{f(x)}$
熵的计算方法: $H = - \sum_x f(x) \log{f(x)}$
```math
\begin{aligned}
H &= -\int_x C(\lambda) \lambda^x (1-\lambda)^{1-x} \log\{C(\lambda) \lambda^x (1-\lambda)^{1-x}\} dx \\
& = -\int_0^1 C \lambda^x (1-\lambda)^{1-x} \left[ \log C + x \log \lambda + (1 -x) \log (1 - \lambda)\right] dx \\
& = -\left[ C \log C \int_0^1 \lambda^x (1-\lambda)^{1-x} dx + C \log \lambda \int_0^1 x \lambda^x (1-\lambda)^{1-x} + C \log(1 - \lambda) \int_0^1 (1-x) \lambda^x (1-\lambda)^{1-x} \right] \\
& = - \left[ \log C + \mathbb{E}(X) \log \lambda + \mathbb{E}(1 - X) \log(1 - \lambda) \right] \\
& = -\log C + \left[ \log (1 - \lambda) -\log \lambda \right] \mathbb{E}(X) - \log(1 - \lambda)
\end{aligned}
```

- `kl_divergence` 相对熵计算

相对熵的计算方法: $D_{KL}(\lambda_1, \lambda_2) = \sum_x f_1(x) \log{\frac{f_1(x)}{f_2(x)}}$
相对熵的计算方法: $D_{KL}(\lambda_1, \lambda_2) = \sum_x f_1(x) \log{\frac{f_1(x)}{f_2(x)}}$
```math
\begin{aligned}
\mathcal{D}_{KL}(p_1|| p_2) &= \int_x p_1(x)\log\frac{p_1(x)}{p_2(x)} dx \\
& = \int_0^1 C_1 \lambda_1^x (1-\lambda_1)^{1-x} \{\log[C_1 \lambda_1^x (1-\lambda_1)^{1-x}] - \log[C_2 \lambda_2^x (1-\lambda_2)^{1-x}]\} dx \\
& = -H - [C_1 \log C_2 \int_0^1 \lambda_1^x (1-\lambda_1)^{1-x} dx + C_1 \log \lambda_2 \int_0^1 x \lambda_1^x (1-\lambda_1)^{1-x} dx + C_1 \log (1-\lambda_2) \int_0^1 (1-x) \lambda_1^x (1-\lambda_1)^{1-x} dx] \\
& = -H - [\log C_2 + \log \lambda_2 \mathbb{E}_1(X) + \log (1-\lambda_2) \mathbb{E}_1(1-X) ] \\
& = - H - \{\log C_2 + [\log \lambda_2 - \log (1-\lambda_2)] \mathbb{E}_1(X) + \log (1-\lambda_2) \}
\end{aligned}
```

- `sample` 随机采样

Expand All @@ -732,6 +759,10 @@ C(\lambda) =
\end{aligned}
\right.
```
$C$ 在非稳定计算区域做泰勒展开:
```math
C = 2 + \frac{4}{3}(\lambda - 0.5)^2 + \frac{104}{45}(\lambda - 0.5)^4
```

- `log_prob` 对数概率密度

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# paddle.distribution.multivariate_normal 设计文档
# paddle.distribution.MultivariateNormal 设计文档

|API名称 | paddle.distribution.multivariate_normal |
|API名称 | paddle.distribution.MultivariateNormal |
|---|---|
|提交作者<input type="checkbox" class="rowselector hidden"> | NKNaN |
|提交时间<input type="checkbox" class="rowselector hidden"> | 2023-09-27 |
|版本号 | V1.0 |
|版本号 | V1.1 |
|依赖飞桨版本<input type="checkbox" class="rowselector hidden"> | develop版本 |
|文件名 | 20230927_api_design_for_multivariate_normal.md<br> |
|文件名 | 20230927_api_design_for_MultivariateNormal.md<br> |


# 一、概述
## 1、相关背景
提升飞桨 API 丰富度, 需要扩充 API `paddle.distribution.multivariate_normal`。
提升飞桨 API 丰富度, 需要扩充 API `paddle.distribution.MultivariateNormal`。

## 2、功能目标
参考 Paddle 现有 distribution,增加 MultivariateNormal 分布类的概率统计与随机采样,包括如下方法:
Expand Down Expand Up @@ -346,17 +346,19 @@ class MultivariateNormalTriL(
`tfp.distributions.MultivariateNormalTriL` 继承自 `tfp.distribution.mvn_linear_operator.MultivariateNormalLinearOperator`

# 四、对比分析
Pytorch 与 Tensorflow 实现方式大体类似,都是通过基本的概率计算得到相应的概率属性
Pytorch 的 `MultivariateNormal` 类支持用户输入 `covariance_matrix` , `precision_matrix` , `scale_tril` 其中任意一种矩阵用来表示多元正态分布的方差,类中的计算将输入的任意一种转化为 `scale_tril` 然后进行计算,速度更快。而 Tf 中则是根据不同的输入矩阵类型设计了不同的类,如: `tfp.distributions.MultivariateNormalTriL` , `tfp.distributions.MultivariateNormalFullCovariance` , `tfp.distributions.MultivariateNormalDiag` 等。Pytorch 的设计简洁,能够涵盖的情况更广,因此参照 Pytorch 进行本 API 设计

# 五、设计思路与实现方案

## 命名与参数设计
```python
paddle.distribution.multivariate_normal(loc, sacle)
paddle.distribution.MultivariateNormal(loc, covariance_matrix=None, precision_matrix=None, scale_tril=None)
```
参数 `loc`,`sacle`为 MultivariateNormal 分布的参数。
- 参数 `loc`,`covariance_matrix` 为 MultivariateNormal 分布的参数。
- 参数 `precision_matrix` , `scale_tril` 均与 `covariance_matrix` 可以相互转化,是一对一映射关系:`precision_matrix` 是 `covariance_matrix` 的逆矩阵, `scale_tril` 是 `covariance_matrix` 的cholesky分解矩阵(下三角形)。

例如,随机变量 $X$ 服从 MultivariateNormal 分布,即 $X \sim MVN(\mu, \Sigma)$ ,对应的参数 `loc`$=\mu$,`sacle`$=\Sigma$。
例如,随机变量 $X$ 服从 MultivariateNormal 分布,即 $X \sim MVN(\mu, \Sigma)$ ,对应的参数 `loc`$=\mu$,`covariance_matrix`$=\Sigma$。
而 `precision_matrix`$={\Sigma}^{-1}$ , 若令 `scale_tril`$=A$ ,则 $A A^{\intercal} = \Sigma$,。

## 底层OP设计
本次任务的设计思路与已有概率分布保持一致,不涉及底层 OP 的开发。
Expand All @@ -366,14 +368,18 @@ paddle.distribution.multivariate_normal(loc, sacle)

```python
class MultivariateNormal(Distribution):
def __init__(self, loc, scale):
super().__init__(batch_shape=self.loc.shape, event_shape=())
def __init__(self, loc, covariance_matrix=None, precision_matrix=None, scale_tril=None):
super().__init__(batch_shape = paddle.broadcast_shape(
covariance_matrix.shape[:-2], loc.shape[:-1]
),
event_shape = loc.expand(batch_shape+[-1]).shape[-1:]
))

...

```

`MultivariateNormal` 类的初始化参数是 `loc`,`sacle` ,类包含的方法及实现方案如下:
`MultivariateNormal` 类的初始化参数是 `loc` 以及 `covariance_matrix`,`precision_matrix`,`scale_tril` 三者中的任意一个,类包含的方法及实现方案如下:

记参数 `loc`$=\mu$,`sacle`$=\Sigma$。

Expand All @@ -387,11 +393,41 @@ class MultivariateNormal(Distribution):

- `entropy` 熵计算

熵的计算方法: $H = - \sum_x f(x) \log{f(x)}$
熵的计算方法: $H = - \sum_x f(x) \log{f(x)}$
```math
\begin{aligned}
H &= -\int_x f(x) \log f(x) dx \\
& = -\int_{x \in \mathbb{R}^n} f(x) \{ -\frac{n}{2}\log(2\pi) -\frac{1}{2} (x-\mu)^{\intercal} \Sigma^{-1} (x-\mu) - \frac{1}{2}\log (\det\Sigma) \} dx \\
& = -\int_{x \in \mathbb{R}^n} f(x) \{ -\frac{n}{2}\log(2\pi) -\frac{1}{2} [A^{-1}(x-\mu)]^{\intercal}[A^{-1}(x-\mu)] - \log (\det A) \} dx \\
& = \frac{n}{2} \log(2\pi) + \log {\det A} + \frac{1}{2}\int_{x \in \mathbb{R}^n} [A^{-1}(x-\mu)]^{\intercal}[A^{-1}(x-\mu)] f(x) dx\\
& = \frac{n}{2} \log(2\pi) + \log {\det A} + \frac{1}{2} \mathbb{E}[(X-\mu)^{\intercal} \Sigma^{-1} (X - \mu)] \\
& = \frac{n}{2} \log(2\pi) + \log {\det A} + \frac{1}{2} \mathbb{E}[tr[(X-\mu)^{\intercal} \Sigma^{-1} (X - \mu)] ] \\
& = \frac{n}{2} \log(2\pi) + \log {\det A} + \frac{1}{2} \mathbb{E}[tr[\Sigma^{-1} (X - \mu) (X-\mu)^{\intercal}]] \\
& = \frac{n}{2} \log(2\pi) + \log {\det A} + \frac{1}{2} tr[\mathbb{E}[\Sigma^{-1} (X - \mu) (X-\mu)^{\intercal}]] \\
& = \frac{n}{2} \log(2\pi) + \log {\det A} + \frac{1}{2} tr[\Sigma^{-1} \mathbb{E}[ (X - \mu) (X-\mu)^{\intercal}]] \\
& = \frac{n}{2} \log(2\pi) + \log {\det A} + \frac{1}{2} tr[\Sigma^{-1} \Sigma] \\
& = \frac{n}{2} \log(2\pi) + \log {\det A} + \frac{n}{2}
\end{aligned}
```
其中 $A$ 指 `scale_tril`。

- `kl_divergence` 相对熵计算

相对熵的计算方法: $D_{KL}(\mu_1, \mu_2, \Sigma_1, \Sigma_2) = \sum_x f_1(x) \log{\frac{f_1(x)}{f_2(x)}}$
相对熵的计算方法: $D_{KL}(\mu_1, \mu_2, \Sigma_1, \Sigma_2) = \sum_x f_1(x) \log{\frac{f_1(x)}{f_2(x)}}$
```math
\begin{aligned}
\mathcal{D}_{KL}(f_1|| f_2) &= \int_x f_1(x)\log\frac{f_1(x)}{f_2(x)} dx \\
& = \int_{x \in \mathbb{R}^n} f_1(x)\left\{\left[ -\frac{n}{2} \log(2\pi) - \log(\det A_1) - \frac{1}{2}(x-\mu_1)^{\intercal} \Sigma_1^{-1} (x - \mu_1) \right] + \left[ \frac{n}{2} \log(2\pi) + \log(\det A_2) + \frac{1}{2}(x-\mu_2)^{\intercal} \Sigma_21^{-1} (x - \mu_2)\right]\right\} dx \\
& = \log(\det A_2) - \log(\det A_1) +\frac{1}{2}\mathbb{E}_1[(X-\mu_2)^{\intercal} \Sigma_2^{-1} (X - \mu_2)] -\frac{n}{2} \\
& = \log(\det A_2) - \log(\det A_1) +\frac{1}{2}tr [\Sigma_2^{-1}\mathbb{E}_1[ (X - \mu_2) (X-\mu_2)^{\intercal} ]] -\frac{n}{2} \\
& = \log(\det A_2) - \log(\det A_1) -\frac{n}{2} +\frac{1}{2}tr [\Sigma_2^{-1}\mathbb{E}_1[ XX^{\intercal} -X \mu_2^{\intercal} - \mu_2 X^{\intercal} + \mu_2\mu_2^{\intercal}]] \\
& = \log(\det A_2) - \log(\det A_1) -\frac{n}{2} +\frac{1}{2}tr [\Sigma_2^{-1} [ Var_1(X) + \mathbb{E}_1(X)\mathbb{E}_1(X)^{\intercal} -\mu_1\mu_2^{\intercal} - \mu_2 \mu_1^{\intercal} + \mu_2\mu_2^{\intercal}]] \\
& = \log(\det A_2) - \log(\det A_1) -\frac{n}{2} +\frac{1}{2}tr [\Sigma_2^{-1} [ \Sigma_1 + \mu_1\mu_1^{\intercal} -\mu_1\mu_2^{\intercal} - \mu_2 \mu_1^{\intercal} + \mu_2\mu_2^{\intercal}]] \\
& = \log(\det A_2) - \log(\det A_1) -\frac{n}{2} +\frac{1}{2}tr [\Sigma_2^{-1} \Sigma_1 + (\mu_1 - \mu_2)^{\intercal} \Sigma_2^{-1} (\mu_1 - \mu_2)] \\
& = \log(\det A_2) - \log(\det A_1) -\frac{n}{2} +\frac{1}{2}[tr [\Sigma_2^{-1} \Sigma_1] + (\mu_1 - \mu_2)^{\intercal} \Sigma_2^{-1} (\mu_1 - \mu_2)] \\
\end{aligned}
```
其中 $A$ 指 `scale_tril`。

- `sample` 随机采样

Expand All @@ -411,12 +447,10 @@ class MultivariateNormal(Distribution):


# 六、测试和验收的考量
`MultivariateNormal` 类测试以 Numpy 作为基准,验证API的正确性。
1. 使用 Numpy 实现所有 MultivariateNormal 的API,集成为 `MultivariateNormalNumpy` 类,用以验证本次任务开发的 API 的正确性。

2. 使用同样的参数实例化 `MultivariateNormal` 类和 `MultivariateNormalNumpy` 类,并调用 `mean`、`variance`、`entropy`、`log_prob`、`kl_divergence`等方法,测试结果是否相等(容许一定误差)。参数 `rate` 的支持的数据类型需测试详尽。
`MultivariateNormal` 类测试以 scipy.stats.multivariate_normal 作为基准,验证API的正确性。
1. 使用 scipy.stats.multivariate_normal 的相关方法,验证 `mean`、`variance`、`entropy`、`log_prob`、`kl_divergence` 方法的结果是否一致(容许一定误差)。

3. 使用 `MultivariateNormal` 类的 `sample` 方法生成5000个样本,测试这些这样的均值和标准差是否正确。
2. 使用 `MultivariateNormal` 类的 `sample` 方法生成5000个样本,测试这些这样的均值和标准差是否正确。


# 七、可行性分析和排期规划
Expand Down