
近期,我們整理和開源了一個基于LaTeX的科技繪圖項目,并將其取名為awesome-latex-drawing(GitHub網址為:https://github.com/xinychen/awesome-latex-drawing),案例包括貝葉斯網絡、圖模型、矩陣/張量示意圖以及技術框架,所有案例均取自于我們近期的研究工作。截至目前,awesome-latex-drawing項目已在GitHub社區獲得超過500次標星 (star)。
需要說明的是,本文轉載自awesome-latex-drawing項目,如需閱讀本文代碼以及相關的說明文檔,歡迎訪問tutorial/Bayesian_nets.md。
一、基本介紹
貝葉斯網絡是一種應用極為廣泛的概率圖模型,由節點和有向邊構成,能夠通過有向邊來描述變量之間的條件依賴關系。給定一個已經構造好的貝葉斯網絡,通常可以采用一些貝葉斯推斷算法對其中的變量進行推斷或者學習,這些貝葉斯推斷算法中既包括最為人所熟知的馬爾可夫鏈蒙特卡洛算法,也包括形式上略顯抽象的變分推斷算法。
直觀清晰的貝葉斯網絡結構圖可以幫助讀者和研究者更好地理解貝葉斯網絡,而LaTeX在繪制貝葉斯網絡結構方面具有天然的優勢,一是由于我們在使用LaTeX繪圖的過程中,LaTeX能夠最大程度上支持復雜的公式符號;二則得益于LaTeX的一系列兼容性極好的圖模型繪制包,其中,最具代表性且使用最為廣泛的工具庫(對應于LaTeX則稱為library)莫過于bayesnet
。結合LaTeX中的繪圖工具包tikz
,我們可以使用LaTeX畫出近乎完美的貝葉斯網絡。
bayesnet
庫是基于LaTeX繪圖工具包tikz
構建起來的貝葉斯網絡繪圖工具庫,主要用于繪制貝葉斯網絡、圖模型以及有向圖結構,其GitHub開發主頁為https://github.com/jluttine/tikz-bayesnet。
本文將對幾個具有代表性的貝葉斯網絡案例進行拆分講解,通過介紹各行代碼及其所對應的繪圖圖形,逐步講解如何用LaTeX繪制出復雜的貝葉斯網絡,在開始繪圖之前,我們不妨回顧一下貝葉斯網絡所遵循的幾項繪圖原則:
- 觀測變量或者觀測值要使用灰色節點表示;
- 底層超參數無需用節點表示;
- 除觀測變量和底層超參數外的其他變量要使用白色節點表示;
- 有向邊箭頭的指向表示貝葉斯角度的概率依賴關系。
為了方便廣大讀者體驗LaTeX繪圖,我們在在線LaTeX編輯系統overleaf.com中創建名稱為latex-drawing-tutorial的項目,接下來就在項目中開啟貝葉斯網絡的LaTeX繪圖之旅。
二、繪制貝葉斯網絡的基本語句
使用LaTeX繪制貝葉斯網絡時,我們需要用到一些最為基本的LaTeX命令:
documentclass[tikz, border = 0.1cm]{standalone}
usepackage{tikz}
usetikzlibrary{bayesnet}
usepackage{amsmath, amsthm, amssymb, amsfonts}
tikzset{>=latex}begin{document}
begin{tikzpicture}end{tikzpicture}
end{document}
在這幾行基本命令中,它們各自都發揮著一定的作用,我們不妨來逐行看一下這些命令的“功效”:
- 準備階段:
documentclass[tikz, border = 0.1cm]{standalone}
的作用在于指定所創建文件的類型,確定tikz
繪圖風格的同時可設置繪圖邊框的頁邊距,這里選取的頁邊距是0.1厘米。當然,這里的頁邊距是可以根據我們自身的審美標準進行設置的。usepackage{tikz}
的作用在于啟用LaTeX中的繪圖工具包tikz
。usetikzlibrary{bayesnet}
的作用在于調用繪制貝葉斯網絡所需的bayesnet
庫。usepackage{amsmath, amsthm, amssymb, amsfonts}
的作用在于啟用LaTeX中支持公式編譯的工具包。
- 繪圖階段:
begin{document} end{document}
是LaTeX編輯各類文檔時首先要申明的語句,在begin
和end
之間寫語句才能有效地映射到所創建的文檔中。begin{tikzpicture} end{tikzpicture}
是用于生成tikz
繪圖的基本語句,在tikzpicture
中,我們可以通過指定各個節點(node
)的橫縱坐標來進行繪圖。
三、繪制貝葉斯增強張量分解的貝葉斯網絡
繪圖任務:如何使用LaTeX繪制出如下貝葉斯增強張量分解的貝葉斯網絡?

1) 繪制觀測變量節點
以上述的幾行LaTeX繪圖的基本命令為基礎,我們不妨在begin{tikzpicture} end{tikzpicture}
之間編寫關于繪制觀測變量節點的代碼:首先,我們將觀測變量節點命名為obs
,在node
命令中,指定該節點的位置為坐標原點(0,0),指定節點類型為circle
,另外令節點邊框為黑色(即draw = black
)、節點大小為0.65厘米(即minimum size = 0.65cm
)。
documentclass[tikz, border = 0.1cm]{standalone}
usepackage{tikz}
usetikzlibrary{bayesnet}
usepackage{amsmath, amsthm, amssymb, amsfonts}
tikzset{>=latex}begin{document}
begin{tikzpicture}node[circle, draw = black, fill = gray!20, inner sep = 0pt, minimum size = 0.65cm] (obs) at (0, 0) {{$y_{ijt}$}};end{tikzpicture}
end{document}
將這幾行簡單的代碼復制粘貼到所創建的overleaf項目中,即可得到的觀測變量節點示意圖。另外,從下圖可以看出,左側為代碼區域,右側為畫圖文檔區域。

2) 繪制模型參數節點
與繪制觀測變量節點類似,我們可以通過node
設計模型參數節點。
documentclass[tikz, border = 0.1cm]{standalone}
usepackage{tikz}
usetikzlibrary{bayesnet}
usepackage{amsmath, amsthm, amssymb, amsfonts}
tikzset{>=latex}begin{document}
begin{tikzpicture}node[circle, draw = black, fill = gray!20, inner sep = 0pt, minimum size = 0.65cm] (obs) at (0, 0) {{$y_{ijt}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (ui) at (-0.9, 0.9) {{$boldsymbol{u}_{i}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (vj) at (0, 1.8) {{$boldsymbol{v}_{j}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (xt) at (0.9, 0.95) {{$boldsymbol{x}_{t}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (phi) at (-0.9, -0.7) {{$phi_{i}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (theta) at (0, -2) {{$theta_{j}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (eta) at (0.9, -0.75) {{$eta_{t}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.55cm] (tau) at (2, 0) {{$tau$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.55cm] (mu) at (-2, 0) {{$mu$}};end{tikzpicture}
end{document}
同樣,將這里的代碼復制并粘貼到所創建的overleaf項目中,即可得到觀測變量節點和模型參數節點示意圖。

3) 繪制模型參數節點和觀測變量節點之間的有向邊
緊接著上面繪制觀測變量節點和模型參數節點的代碼,根據已經定義好的節點,可以使用path
來構造有向邊,其中模型參數節點和觀測變量節點之間要用edge
關聯,箭頭方向為->
。
documentclass[tikz, border = 0.1cm]{standalone}
usepackage{tikz}
usetikzlibrary{bayesnet}
usepackage{amsmath, amsthm, amssymb, amsfonts}
tikzset{>=latex}begin{document}
begin{tikzpicture}node[circle, draw = black, fill = gray!20, inner sep = 0pt, minimum size = 0.65cm] (obs) at (0, 0) {{$y_{ijt}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (ui) at (-0.9, 0.9) {{$boldsymbol{u}_{i}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (vj) at (0, 1.8) {{$boldsymbol{v}_{j}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (xt) at (0.9, 0.95) {{$boldsymbol{x}_{t}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (phi) at (-0.9, -0.7) {{$phi_{i}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (theta) at (0, -2) {{$theta_{j}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (eta) at (0.9, -0.75) {{$eta_{t}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.55cm] (tau) at (2, 0) {{$tau$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.55cm] (mu) at (-2, 0) {{$mu$}};path [draw, ->] (ui) edge (obs);
path [draw, ->] (vj) edge (obs);
path [draw, ->] (xt) edge (obs);
path [draw, ->] (tau) edge (obs);
path [draw, ->] (mu) edge (obs);
path [draw, ->] (phi) edge (obs);
path [draw, ->] (theta) edge (obs);
path [draw, ->] (eta) edge (obs);end{tikzpicture}
end{document}
將這里的代碼復制并粘貼到所創建的overleaf項目中,即可得到觀測變量節點、模型參數節點以及有向邊示意圖。

4) 繪制部分觀測變量的元素集合
對于部分觀測變量,它可能只包括矩陣或張量元素的部分索引,因此需要指定出來。一般而言,我們可以用plate
在貝葉斯網絡中進行標注,標注的方法是在plate
命令中羅列需要覆蓋的變量。以第一個plate為例,我們可以將{(obs) (ui) (m) (phi)}
作為所需要覆蓋的集合。
documentclass[tikz, border = 0.1cm]{standalone}
usepackage{tikz}
usetikzlibrary{bayesnet}
usepackage{amsmath, amsthm, amssymb, amsfonts}
tikzset{>=latex}begin{document}
begin{tikzpicture}node[circle, draw = black, fill = gray!20, inner sep = 0pt, minimum size = 0.65cm] (obs) at (0, 0) {{$y_{ijt}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (ui) at (-0.9, 0.9) {{$boldsymbol{u}_{i}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (vj) at (0, 1.8) {{$boldsymbol{v}_{j}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (xt) at (0.9, 0.95) {{$boldsymbol{x}_{t}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (phi) at (-0.9, -0.7) {{$phi_{i}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (theta) at (0, -2) {{$theta_{j}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (eta) at (0.9, -0.75) {{$eta_{t}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.55cm] (tau) at (2, 0) {{$tau$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.55cm] (mu) at (-2, 0) {{$mu$}};path [draw, ->] (ui) edge (obs);
path [draw, ->] (vj) edge (obs);
path [draw, ->] (xt) edge (obs);
path [draw, ->] (tau) edge (obs);
path [draw, ->] (mu) edge (obs);
path [draw, ->] (phi) edge (obs);
path [draw, ->] (theta) edge (obs);
path [draw, ->] (eta) edge (obs);node [text width = 0.2cm] (m) at (-1.1, 0.3) {small{$m$}};
plate[] {plate1} {(obs) (ui) (m) (phi)} { };
node [text width = 0.6cm] (n) at (0, -1.55) {small{$n$}};
plate[] {plate2} {(obs) (vj) (n) (theta)} { };
node [text width = 0.2cm] (f) at (1.1, 0.3) {small{$f$}};
plate[] {plate3} {(obs) (xt) (f) (eta)} { };end{tikzpicture}
end{document}
將這里的代碼復制并粘貼到所創建的overleaf項目中,即可得到部分觀測變量的元素集合示意圖。

5) 繪制超參數節點及其有向邊
在這里,超參數分為待估計超參數和底層超參數,待估計超參數顧名思義是一個變量,而底層超參數則是常量。對于待估計超參數,我們仍要像繪制模型參數一樣繪制它,而底層超參數則不需要帶有“圓圈”,不過需要注意的是,兩者都可以用node
命令進行繪制。
documentclass[tikz, border = 0.1cm]{standalone}
usepackage{tikz}
usetikzlibrary{bayesnet}
usepackage{amsmath, amsthm, amssymb, amsfonts}
tikzset{>=latex}begin{document}
begin{tikzpicture}node[circle, draw = black, fill = gray!20, inner sep = 0pt, minimum size = 0.65cm] (obs) at (0, 0) {{$y_{ijt}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (ui) at (-0.9, 0.9) {{$boldsymbol{u}_{i}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (vj) at (0, 1.8) {{$boldsymbol{v}_{j}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (xt) at (0.9, 0.95) {{$boldsymbol{x}_{t}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (phi) at (-0.9, -0.7) {{$phi_{i}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (theta) at (0, -2) {{$theta_{j}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (eta) at (0.9, -0.75) {{$eta_{t}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.55cm] (tau) at (2, 0) {{$tau$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.55cm] (mu) at (-2, 0) {{$mu$}};path [draw, ->] (ui) edge (obs);
path [draw, ->] (vj) edge (obs);
path [draw, ->] (xt) edge (obs);
path [draw, ->] (tau) edge (obs);
path [draw, ->] (mu) edge (obs);
path [draw, ->] (phi) edge (obs);
path [draw, ->] (theta) edge (obs);
path [draw, ->] (eta) edge (obs);node [text width = 0.2cm] (m) at (-1.1, 0.3) {small{$m$}};
plate[] {plate1} {(obs) (ui) (m) (phi)} { };
node [text width = 0.6cm] (n) at (0, -1.55) {small{$n$}};
plate[] {plate2} {(obs) (vj) (n) (theta)} { };
node [text width = 0.2cm] (f) at (1.1, 0.3) {small{$f$}};
plate[] {plate3} {(obs) (xt) (f) (eta)} { };node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (muv) at (-0.6, 2.8) {small{$boldsymbol{mu}_{v}$}};
node[circle, draw = black, inner sep = 0pt, minimum size = 0.6cm] (lambdav) at (0.6, 2.8) {small{$Lambda_{v}$}};
node[text width = 0.8cm] (gamma) at (2, 0.8) {small{$a_0,b_0$}};
node[text width = 0.8cm] (hyper1) at (-2, 0.8) {small{$mu_{0},tau_{0}$}};
node[text width = 0.8cm] (hyper2) at (1.2, -2) {small{$mu_{0},tau_{0}$}};
node[text width = 0.8cm] (hyper3) at (2.1, -0.75) {small{$mu_{0},tau_{0}$}};
node[text width = 0.8cm] (hyper4) at (-2.1, -0.7) {small{$mu_{0},tau_{0}$}};
node[text width = 0.4cm] (mu0) at (-0.6, 3.6) {small{$boldsymbol{mu}_{0}$}};
node[text width = 0.9cm] (wnu0) at (0.6, 3.6) {small{$W_{0},nu_{0}$}};
node[text width = 0.6cm] (cdots1) at (-1, 1.6) {Largecolor{gray}{$cdots$}};
node[text width = 0.6cm] (cdots2) at (1, 1.6) {Largecolor{gray}{$cdots$}};path [draw, ->] (muv) edge (vj);
path [draw, ->] (lambdav) edge (vj);
path [draw, ->] (lambdav) edge (muv);
path [draw, ->] (mu0) edge (muv);
path [draw, ->] (wnu0) edge (lambdav);
path [draw, ->] (gamma) edge (tau);
path [draw, ->] (hyper1) edge (mu);
path [draw, ->] (hyper2) edge (theta);
path [draw, ->] (hyper3) edge (eta);
path [draw, ->] (hyper4) edge (phi);end{tikzpicture}
end{document}
將這里的代碼復制并粘貼到所創建的overleaf項目中,即可得到我們希望得到的包含了超參數節點及其有向邊的貝葉斯網絡結構圖。

到這里,我們便完成了完整的貝葉斯網絡繪制。
LaTeX相關入門學習材料: