Scalable likelihood-based inference for limited dependent variable models¶
作者: David T. Frazier, Ruben Loaiza-Maya, Didier Nibbering
主题: 统计计算 / 算法
相关性: 6/10
链接: https://arxiv.org/abs/2608.13851
一、领域脉络与小综述¶
这个方向是什么¶
这个子方向解决的根本问题是:如何对有限因变量模型(LDVM)进行基于似然的统计推断。LDVM 的核心特征是观测到的因变量是潜在连续变量的受限变换(离散、截断、归并),因此似然函数涉及高维潜变量积分,通常没有闭式解。该方向的成熟度是:经典方法(SML、MSS、EM、数值积分)在低维设定下可行,但在现代大规模数据(百万级观测、高维选择集/随机效应)下计算上不可行,迫使研究者使用缺乏似然推断保证的近似方法。本文试图填补的正是这个“大规模 LDVM 的可行似然推断”缺口。
发展脉络(history)¶
-
奠基工作:Tobin (1958) 提出了 Tobit 模型,奠定了 LDVM 的框架。Dempster et al. (1977) 的 EM 算法提供了处理潜变量模型的一般框架,但 E-step 在 LDVM 中通常是计算瓶颈。Robbins and Monro (1951) 的随机逼近理论是后续所有 SGA 方法的基石。
-
主要进展(模拟方法):为了绕过高维积分,一系列基于模拟的方法被提出:
- SML (Simulated Maximum Likelihood):Börsch-Supan and Hajivassiliou (1993) 用蒙特卡洛近似替代似然贡献(如 MNP 的选择概率)。作者指出其关键缺陷是“对于固定的模拟次数,模拟得分是有偏的”,因此一致性要求模拟次数随样本量发散,在高维选择集中尤其苛刻。
- MSS (Method of Simulated Scores):Hajivassiliou and McFadden (1998) 直接模拟得分方程并求解。作者指出其理论性质虽好,但“在实践中难以实现”,因为它要求模拟的得分函数在候选参数值上保持稳定,这在潜变量受经济模型约束(如高维选择模型)时极具挑战。作者在数值实验中直接展示了 MSS 对模拟次数、条件模拟器精度和算法初始化的敏感性。
- 数值积分:Rabe-Hesketh et al. (2005) 使用高斯-埃尔米特求积,但其成本随随机效应维度指数增长,超过三维即不可行。
-
当前 Frontier 与本文位置:近期工作如 Ding et al. (2024) 针对 probit 模型提出了基于期望传播的确定性近似,但作者指出其“聚焦于估计而非推断”。本文提出的 SEGA 方法,其核心创新在于:不试图在每次迭代中消除模拟噪声,而是利用随机逼近和迭代平均来在优化路径上削减模拟噪声。作者声称这是“首个证明模拟潜变量得分可以在随机优化算法中使用,同时保持与不可行 MLE 渐近等价的结果”。
子线索聚类¶
- 直接近似似然/得分:SML (Börsch-Supan and Hajivassiliou, 1993)、数值积分 (Rabe-Hesketh et al., 2005)。这类方法试图在每次参数评估时精确计算或近似似然/得分,计算成本随问题维度(选择集大小、随机效应维数)急剧增长。
- 模拟得分方程:MSS (Hajivassiliou and McFadden, 1998)。这类方法通过模拟来近似得分方程,然后求解。其核心困难在于模拟噪声的控制和对条件模拟器稳定性的要求。
- EM 类型方法:EM (Dempster et al., 1977)、Monte Carlo EM (Wei and Tanner, 1990)、期望传播 (Ding et al., 2024)。这类方法交替进行 E-step(计算潜变量条件期望)和 M-step(最大化期望增广似然)。E-step 通常是计算瓶颈,且 M-step 可能仍涉及非平凡优化。
- 随机梯度方法:SEGA(本文)。这类方法将无偏的增广数据得分嵌入 SGA 算法,通过迭代平均来获得低方差估计,避免了对似然或得分的精确近似。
这个方向在追问的核心问题¶
- 如何在大规模 LDVM 中实现计算可行的似然推断? 现有方法(SML、MSS、EM)的计算成本随问题规模(n, J, r)增长过快。
- 如何控制模拟噪声,使其不影响推断的渐近有效性? SML 需要模拟次数发散,MSS 需要固定随机数种子,两者都有局限性。
- 能否在保持 MLE 渐近效率的同时,避免高维积分? 这是所有模拟方法的终极目标。
- 如何为 SGA 类型的算法提供可靠的方差估计和推断工具? 标准 SGA 的推断理论(如 Chen et al., 2020)通常基于数据子采样,而本文的噪声源是潜变量模拟,路径依赖,需要新的理论。
⚠️ 作者的 framing¶
作者将缺口 frame 成:现有方法(SML、MSS、EM、数值积分)在“大规模”LDVM 下均不可行,而 SEGA 通过“在优化路径上平均模拟噪声”这一策略,首次实现了对这类模型的可扩展似然推断。作者淡化了以下竞争路线: - 贝叶斯方法:作者在实证部分引用了 Loaiza-Maya and Nibbering (2023) 和 Loaiza-Maya et al. (2024) 的贝叶斯方法,但将其定位为“近似贝叶斯估计方法”,暗示其缺乏频率学派似然推断的保证。这回避了一个问题:变分贝叶斯或 MCMC 方法在多大程度上能提供可比的推断质量? - 专门化的 EM 类型方法:作者引用了 Ding et al. (2024) 的期望传播方法,但仅指出其“聚焦于估计而非推断”,并未深入比较两者在推断上的优劣。
什么明显该被引/该存在、却没出现在 intro 里? - 关于 SGA 推断的近期理论工作:作者引用了 Chen et al. (2020) 关于 SGA 统计推断的工作,但该工作主要针对数据子采样。是否有其他关于“非标准噪声源”(如潜变量模拟)的 SGA 理论工作?这是一个值得研究者去查的问题。 - 关于“Polyak–Ruppert 平均”在非凸问题中的理论:作者假设目标函数是强凹的(Assumption 2),这在许多 LDVM 中可能不成立。是否有关于 SEGA 在非凸设定下的理论工作?作者未提及。
张力¶
未见明显对立引用。所有被引工作都承认 LDVM 中高维积分带来的计算挑战,只是在应对策略上有所不同。作者对 MSS 的批评(对调参敏感、数值不稳定)是直接的,但 MSS 的支持者可能会争辩,通过精心设计的模拟器和足够多的模拟次数,MSS 在中等规模问题中仍可工作。本文的实验设计(故意对 MSS 不利的初始化)也暗示了这种张力。
二、最核心、最简单的例子 / 数学问题¶
第一步:把符号、模型、可观测数据交代清楚¶
-
符号:
i = 1, ..., n: 观测个体索引。yi: 第i个个体的可观测受限因变量(如离散选择、截断值)。xi: 第i个个体的可观测协变量向量。di = (yi, xi): 第i个个体的完整可观测数据。zi: 第i个个体的不可观测潜变量(如潜在效用、潜在需求)。这是想要但观测不到的量。θ ∈ Θ ⊂ R^{d_θ}: 待估的参数向量,是 estimand。p_θ(y_i | z_i, x_i): 给定潜变量和协变量下,观测数据的条件分布。通常由观测规则y_i = T(z_i)决定,是退化的。p_θ(z_i | x_i): 潜变量的条件分布(如多元正态)。ℓ_n(θ) = log p_θ(y_{1:n} | x_{1:n}): 对数似然函数,涉及对z_{1:n}的高维积分。m_n(θ) = (1/n) ∇_θ ℓ_n(θ): 归一化的不可行得分函数(因为涉及积分)。ˆθ_n: 不可行的 MLE,即argmax_θ ℓ_n(θ)。p_θ(z_{1:n} | d_{1:n}): 给定观测数据下,潜变量的条件后验分布。这是 SEGA 进行模拟的关键分布。ˆm_n(θ) = (1/n) ∇_θ log p_θ(y_{1:n}, ˜z_{1:n} | x_{1:n}): 可行的增广数据得分估计量,其中˜z_{1:n} ~ p_θ(· | d_{1:n})。这是 SEGA 的核心构建块。θ^{(k)}_n: SEGA 算法在第k次迭代的参数值。¯θ_n: Polyak–Ruppert 平均后的 SEGA 估计量。η^{(k)}_n: 第k次迭代的学习率(步长)。
-
模型:
- 数据生成机制:对于每个个体
i,首先从p_θ(z_i | x_i)生成潜变量z_i,然后通过已知的观测规则y_i = T(z_i)生成可观测的y_i。参数θ控制z_i的分布。 - 统计模型:研究者假设一个参数化的潜变量模型
p_θ(z_i | x_i)(如线性回归加正态误差)和一个已知的观测规则T。目标是估计θ。 - 已知/未知:
T是已知的。p_θ(z_i | x_i)的函数形式已知,但参数θ未知。p_θ(y_i | z_i, x_i)由T决定,是已知的。
- 数据生成机制:对于每个个体
-
可观测数据:
- 实际能观测到:
d_i = (y_i, x_i),即每个个体的受限因变量和协变量。我们有n个这样的独立同分布观测。 - 想要但观测不到:潜变量
z_i。它是我们推断θ的障碍,因为似然p_θ(y_i | x_i)需要对z_i积分。SEGA 的策略是不计算这个积分,而是通过从p_θ(z_i | d_i)中模拟z_i来获得一个无偏的得分估计。
- 实际能观测到:
第二步:讲最小内核¶
本文的最小内核是:用一个单次模拟的潜变量来构造一个无偏的得分估计,并将其嵌入随机梯度上升算法中,最终证明该算法的输出与不可行的 MLE 渐近等价。
最简特例:一个简单的随机截距 Probit 模型
考虑一个最简单的二值选择 Probit 模型,只有一个随机截距:
- y_i ∈ {0, 1}: 可观测的二值选择。
- z_i = α_i + ε_i,其中 α_i ~ N(0, σ^2_α) 是随机截距(潜变量),ε_i ~ N(0, 1) 是独立误差。θ = (β, σ^2_α),其中 β 是固定效应系数(假设只有一个协变量 x_i)。
- 观测规则:y_i = 1 如果 z_i > 0,否则 y_i = 0。
- 可观测数据:(y_i, x_i)。不可观测:α_i。
不可行的 MLE:对数似然为 ℓ_n(θ) = Σ_i log ∫ Φ( (β x_i + α_i) * (2y_i - 1) ) φ(α_i; 0, σ^2_α) dα_i。这个一维积分可以用数值积分计算,但为了说明 SEGA 的思想,我们假设它也是“不可行的”。
SEGA 的核心思路:
1. Fisher 恒等式:不可行的得分 m_n(θ) 可以写成 m_n(θ) = (1/n) E_{α_{1:n} | d_{1:n}} [ ∇_θ log p_θ(y_{1:n}, α_{1:n} | x_{1:n}) ]。也就是说,得分是增广数据得分在潜变量后验分布下的期望。
2. 无偏估计:如果我们从后验 p_θ(α_{1:n} | d_{1:n}) 中抽取一个样本 ˜α_{1:n},那么 ˆm_n(θ) = (1/n) ∇_θ log p_θ(y_{1:n}, ˜α_{1:n} | x_{1:n}) 就是 m_n(θ) 的一个无偏估计。在这个特例中,∇_θ log p_θ(y_i, ˜α_i | x_i) 有闭式解(因为给定 α_i,模型退化为标准 Probit)。
3. 随机梯度上升:我们不再求解 m_n(θ) = 0,而是迭代地执行 θ^{(k+1)} = θ^{(k)} + η^{(k)} ˆm_n(θ^{(k)})。每次迭代,我们只需要从 p_{θ^{(k)}}(α_{1:n} | d_{1:n}) 中抽取一次 ˜α_{1:n},然后计算 ˆm_n(θ^{(k)})。
4. 迭代平均:由于 ˆm_n(θ) 是噪声的,单个 θ^{(k)} 可能波动很大。我们使用 Polyak–Ruppert 平均 ¯θ_n = (1/(k-k_0)) Σ_{j=k_0+1}^k θ^{(j)} 来获得一个低方差的估计。
为什么这个特例抓住了核心:
- 它包含了 SEGA 的所有关键要素:潜变量(α_i)、不可行的积分、Fisher 恒等式、单次模拟的无偏得分、SGA 更新、迭代平均。
- 它去掉了论文中为一般性服务的复杂技术假设(如强凹性、高阶矩条件),让我们能直观地看到“用模拟噪声替代积分”这一核心思想。
- 论文的一般情形(MNP、随机效应 Tobit)只是这个特例的“加壳”:潜变量维度更高(z_i 是向量)、条件后验更复杂(截断多元正态)、增广数据得分更繁琐,但数学本质完全相同。
三、这篇论文做了什么¶
三句话¶
- 研究了什么问题:针对有限因变量模型(LDVM)中似然函数涉及高维潜变量积分、导致经典 MLE 不可行的问题,提出了一种可扩展的随机梯度上升算法 SEGA。
- 核心工具/方法:利用 Fisher 恒等式,将不可处理的似然得分替换为基于单次潜变量条件抽样的无偏增广数据得分,并将其嵌入随机梯度上升算法中,最后通过 Polyak–Ruppert 平均获得最终估计量。
- 主要结论:在固定样本量下,SEGA 迭代值随迭代次数增加而集中于不可行的 MLE;当迭代次数随样本量足够快增长时,SEGA 估计量与不可行的 MLE 一阶渐近等价,并继承了其极限分布(包括在误设定下的三明治形式)。
关键设定与假设¶
- 模型类:论文考虑一类具有潜变量表示的 LDVM,其结构由 (1) 和 (2) 式给出。关键特征是
p_θ(y_i | z_i, x_i)通常是退化的(由观测规则y_i = T(z_i)决定),而p_θ(z_i | x_i)是参数化的(如多元正态)。 - 假设 1(模拟得分的正则条件):
- (i) 条件无偏性:
E[ˆm_n(θ^{(k)}_n; z^{(k)}_{1:n}) | F_k] = m_n(θ^{(k)}_n)。这是 Fisher 恒等式的直接结果,是 SEGA 的理论基石。它要求潜变量是从其精确条件分布中抽取的(或 Gibbs 链已处于平稳分布)。 - (ii) 有界二阶矩:在 MLE 处,模拟得分的条件二阶矩一致有界。这是一个标准的技术假设,用于控制噪声方差。
- (iii) 均方 Lipschitz:模拟得分在参数上是均方 Lipschitz 连续的。这确保了得分函数的平滑性,是 SGA 收敛分析的标准条件。
- (i) 条件无偏性:
- 假设 2(强凹性):样本对数似然
ℓ_n(θ)是µ-强凹的。这是一个非常强的假设,它保证了目标函数有唯一的全局最大值,并且梯度指向该最大值。作者承认“这比渐近似然理论所需的条件更强”,并指出“在更弱的局部条件下可以得到渐近版本”。这个假设是获得有限样本浓度界的关键,但在许多实际 LDVM(如 MNP)中可能不成立(似然面可能有多个局部极大值或平坦区域)。 - 假设 3(步长):步长
η^{(k)}_n ∝ η_n k^{-α},其中α ∈ (1/2, 1)。这是经典的 Robbins–Monro 步长范围,保证了Σ η^{(k)}_n = ∞和Σ {η^{(k)}_n}^2 < ∞,这是 SGA 收敛的必要条件。 - 假设 4(迭代次数):
√n / k^{α/2}_n = o(1)。这确保了优化误差(来自 SEGA)相对于采样误差(来自 MLE)是渐近可忽略的。 - 假设 5(初始值):初始值与 MLE 的差距有界二阶矩。这是一个温和的条件。
相比已有文献的放宽/强化: - 放宽:相比 MSS,SEGA 不要求潜变量模拟使用固定的随机数种子,这大大简化了实现。 - 强化:相比标准 SGA 理论(如 Chen et al., 2020),SEGA 的噪声源是潜变量模拟而非数据子采样,且噪声分布随参数变化(路径依赖)。论文的理论贡献在于处理了这种非标准噪声。同时,强凹性假设(Assumption 2)比许多 SGA 理论中要求的凸性更强。
主要结果¶
- 定理 1(有限样本浓度):在假设 1-3 下,SEGA 迭代值
θ^{(k)}_n与 MLEˆθ_n的p阶矩偏差以O(k^{-pα/2})的速率收敛。这个界依赖于初始值。直觉:随着迭代次数k增加,SGA 迭代值以多项式速率向 MLE 收缩。速率k^{-α/2}由步长衰减速率α决定。 - 推论 1.1(平均估计量的浓度):Polyak–Ruppert 平均后的估计量
¯θ_n满足与定理 1 相同的浓度界。直觉:平均操作不会恶化收敛速率。 - 定理 2(渐近等价于 MLE):在假设 1-5 下,如果
√n(ˆθ_n - θ_0) ⇒ N(0, V),那么√n(¯θ_n - θ_0) ⇒ N(0, V)。直觉:只要迭代次数k_n增长得足够快(满足假设 4),SEGA 的优化误差就是o_p(1/√n)的,因此 SEGA 估计量¯θ_n与不可行的 MLEˆθ_n共享相同的渐近分布。这意味着我们可以使用标准的 MLE 推断工具(如三明治方差估计)来进行基于 SEGA 的推断。
证明路线与技术技巧¶
-
整体路线:
- 重写更新方程:将 SEGA 更新方程 (21) 重写为“不可行梯度上升更新 + 模拟噪声项”。关键在于证明模拟噪声项
{m_n(θ^{(k)}_n) - ˆm_n(θ^{(k)}_n, ˜z^{(k)}_{1:n})}是一个鞅差序列(MDS)。 - 控制单步更新:分析
∥θ^{(k)}_n - ˆθ_n∥^2的条件期望。利用强凹性(假设 2)控制梯度项,利用 Lipschitz 和有界二阶矩(假设 1)控制噪声项,得到一个关于∥δ_k∥^2的递归不等式。 - 解递归:利用步长假设(假设 3)和引理 2(来自 Chen et al., 2020),解这个递归不等式,得到定理 1 的浓度界。
- 从有限样本到渐近:利用推论 1.1 的浓度界和 Markov 不等式,证明
√n∥¯θ_n - ˆθ_n∥ = o_p(1)。然后通过 Slutsky 定理,从√n(ˆθ_n - θ_0)的渐近正态性推出√n(¯θ_n - θ_0)的渐近正态性。
- 重写更新方程:将 SEGA 更新方程 (21) 重写为“不可行梯度上升更新 + 模拟噪声项”。关键在于证明模拟噪声项
-
关键跳跃点:
- 处理路径依赖的噪声:标准 SGA 的噪声是独立同分布的(来自数据子采样),而 SEGA 的噪声
˜z^{(k)}_{1:n}依赖于当前参数θ^{(k)}_n,因此是路径依赖的。论文的关键跳跃在于利用 Fisher 恒等式证明,条件于过去的信息集F_k,模拟噪声的期望为零,即它是一个 MDS。这使得作者可以借用标准 SGA 的分析框架。 - 从单个迭代到平均估计量:单个迭代
θ^{(k)}_n的噪声很大,但 Polyak–Ruppert 平均可以降低方差。证明平均估计量的浓度界(推论 1.1)需要巧妙地处理求和项,利用凸性将范数放入求和内部,然后应用定理 1 的界。
- 处理路径依赖的噪声:标准 SGA 的噪声是独立同分布的(来自数据子采样),而 SEGA 的噪声
-
技术技巧点名:
- Fisher 恒等式:用于将不可行的得分表示为增广数据得分的条件期望,从而构造无偏估计。这是整个方法的基石。
- 鞅差序列(MDS):用于刻画模拟噪声的结构,使得可以应用 SGA 的经典分析工具。
- Polyak–Ruppert 平均:用于降低 SGA 迭代的方差,获得一个更稳定的估计量。
- 引理 2(来自 Chen et al., 2020):一个用于解特定形式递归不等式的技术引理,是获得有限样本浓度界的关键。
- Louis 恒等式:用于构造方差估计量
ˆV,将不可行的观测 Hessian 表示为增广 Hessian 的条件期望加上一个修正项。
真实例子与应用¶
本文包含两个大规模真实数据应用,这是其实证贡献的核心。
-
Multinomial Probit (MNP) 模型:意大利面品牌选择
- 数据:来自 Dunnhumby 平台的超过 100 万次意大利面购买记录,涉及 10 个品牌。
- 方法应用:使用 SEGA 估计一个无约束的 MNP 模型(包含所有品牌间的协方差),并基于 SEGA 估计量计算价格弹性、构建置信区间、进行 Wald 检验。
- 结果:
- 成功估计了无约束模型,得到了自有和交叉价格弹性的点估计和窄置信区间(图 1)。
- 通过 Wald 检验,明确拒绝了三种常见的协方差结构约束(
Σ = I_J、对角矩阵、等相关矩阵),检验统计量远大于 1% 临界值。这证明了 SEGA 能够揭示被约束模型掩盖的复杂替代模式。
- 想说明什么:SEGA 使得在大规模、高维选择集下,对 MNP 模型进行完整的、基于似然的推断成为可能,而这是 SML、MSS 等方法无法做到的。它允许研究者检验那些为了计算可行性而强加的、可能不合理的模型约束。
-
随机效应 Tobit 模型:家庭层面意大利面需求
- 数据:来自同一数据源,包含 203,965 个家庭在 62 周内的购买记录,关注自有品牌意大利面。
- 方法应用:使用 SEGA 估计一个包含随机截距、随机价格系数和随机滞后销售系数的 Tobit 模型。
- 结果:
- 揭示了显著的异质性:滞后销售效应强的家庭,其基线购买倾向更低,但对价格更敏感(图 2)。
- 通过 Wald 检验,明确拒绝了随机效应独立的假设(
H_0: Ω_{0p} = Ω_{0ℓ} = Ω_{pℓ} = 0)以及基线购买倾向与状态依赖无关的假设(H_0: Ω_{0ℓ} = 0)。检验统计量均远大于 1% 临界值。
- 想说明什么:SEGA 使得在包含数十万个体和多个随机效应来源的大规模面板数据中,对 Tobit 模型进行灵活的异质性建模和假设检验成为可能。它允许研究者探究不同来源的异质性之间的相关性,而这是传统方法(如数值积分)无法处理的。
🔎 结论是否比证明窄¶
- 强凹性假设(Assumption 2):论文的主要理论结果(定理 1、2)都依赖于全局强凹性假设。然而,作者在实证部分应用的 MNP 和 Tobit 模型,其似然面通常不是全局强凹的。作者在附录 SC.2 中为 MNP 模型验证了假设 2,但依赖于一个局部条件(Assumption 6(5)),即“在
ˆθ_n的一个邻域内,Hessian 是负定的”。这实际上是一个局部强凹性条件,而非全局的。因此,定理 1 和 2 的结论在严格意义上只适用于ˆθ_n附近的一个邻域,而非整个参数空间。作者在正文中承认“渐近版本可以在更弱的局部条件下获得”,但并未给出具体证明。这是一个值得注意的“结论比证明窄”的地方。 - 潜变量模拟的精确性:假设 1(i) 要求潜变量是从其精确条件分布中抽取的。在实践中,对于 MNP 和 Tobit 模型,作者使用 Gibbs 采样来近似这个分布。作者假设“Gibbs 链处于平稳分布”,但这在有限次迭代中并不严格成立。因此,理论上的无偏性在实际中只是近似成立。作者在数值实验中通过增加 Gibbs 步数来缓解这个问题,但并未在理论上量化这个近似误差。
四、开放问题¶
- 弱化强凹性假设:能否在更现实的局部强凹性或凸性 + 正则化条件下,建立 SEGA 的有限样本浓度和渐近等价性?这扎根于论文的 Assumption 2 及其在附录中的局部版本验证。
- 自适应步长与停止规则的理论保证:论文在实践中使用了 ADADELTA 自适应步长,但理论分析(Assumption 3)要求步长遵循
k^{-α}的衰减规律。能否为 ADADELTA 或类似的自适应方法在 SEGA 框架下提供理论保证?同时,能否设计一个基于||∆^{(j)}||的、有理论支持的停止规则?这扎根于论文第 2.3 节关于 ADADELTA 和停止规则的讨论。 - 非精确模拟的影响:当潜变量只能通过 MCMC(如 Gibbs)近似模拟时,模拟误差如何影响 SEGA 的有限样本和渐近性质?能否量化所需的 MCMC 步数?这扎根于论文对 Gibbs 采样在 MSS 中不稳定性的讨论(Section 2.6),以及假设 1(i) 对“精确抽取”的要求。
- 方差估计的计算成本:论文的方差估计
ˆV需要使用 Botev (2017) 的方法生成S = 20,000个独立同分布潜变量样本,这比 SEGA 点估计本身的计算成本高得多。能否设计一个更高效的、基于 SEGA 迭代路径本身的方差估计方法(如批均值法)?这扎根于论文第 2.5.2 节关于方差估计计算成本的讨论。
Maintained by 陈星宇 · Homepage · Source on GitHub