Tikz语法学习(使用Tikz画神经网络全连接层)
前言
在学术论文和博客中,神经网络架构图是不可或缺的可视化工具。虽然Python的matplotlib、TensorBoard等工具可以生成网络图,但LaTeX的TikZ包提供了更精确、更美观的绘图方案。本文将详细介绍如何使用TikZ绘制专业的神经网络图,并通过一个完整的Dueling DQN网络案例来演示。
一、准备工作
1.1 环境配置
首先确保您的LaTeX环境已安装TikZ包。在文档开头添加必要的包:
\documentclass[tikz,border=2pt]{standalone}
\usetikzlibrary{fpu} % 修复ifthenelse错误
\usetikzlibrary{math} % 数学计算功能
`standalone`类允许我们生成独立的图片文件,`border=2pt`设置边框宽度。`fpu`库解决了某些数学运算的兼容性问题,`math`库提供了强大的数学计算功能。
1.2 基本概念
TikZ的核心思想是"用代码画图"。每个图形元素(点、线、圆等)都可以通过坐标和属性来精确控制。对于神经网络图,我们需要掌握:
- 节点绘制和定位
- 连接线绘制
- 颜色和样式设置
- 数学计算和循环
二、TikZ核心语法详解
2.1 坐标系统
TikZ使用笛卡尔坐标系,原点在左下角:
\draw (0,0) -- (2,1); % 从(0,0)到(2,1)的直线
\filldraw (1,1) circle (0.3); % 在(1,1)处画半径为0.3的圆
2.2 节点绘制
神经网络中的神经元通常用圆形表示:
\filldraw[line width=0.5pt, fill=blue!10] (x,y) circle (radius);
- `line width`:边框粗细
- `fill=blue!10`:填充颜色(10%蓝色)
- `circle (radius)`:圆形,指定半径
2.3 连接线绘制
层间连接用直线表示:
\draw (x1,y1) -- (x2,y2); % 普通直线
\draw[-latex] (x1,y1) -- (x2,y2); % 带箭头的直线
2.4 数学计算和循环
TikZ的`math`库提供了强大的数学功能:
\tikzmath{
function paint_nodes(\radius, \gapy, \posx, \num){
\gapy = \gapy + \radius*2; % 计算节点间距
\starty = \gapy*(\num-1)/2; % 计算起始Y坐标(垂直居中)
for \i in {0,...,\num-1}{ % 循环绘制每个节点
\drawy = \starty - \i*\gapy; % 计算当前节点的Y坐标
{
\filldraw[line width=0.5pt, fill=blue!10] (\posx,\drawy) circle (\radius);
};
};
};
}
1. **函数定义**:`function paint_nodes(\radius, \gapy, \posx, \num)`
- `\radius`:节点半径
- `\gapy`:节点间垂直间距
- `\posx`:节点的X坐标(整层节点在同一X位置)
- `\num`:节点数量
2. **间距计算**:`\gapy = \gapy + \radius*2`
- 将节点半径的两倍加到间距中,确保节点不重叠
- 例如:半径0.3,原间距0.2,则实际间距为0.8
3. **垂直居中**:`\starty = \gapy*(\num-1)/2`
- 计算第一个节点的Y坐标,使整层节点垂直居中
- 例如:5个节点,间距0.8,则起始Y坐标为1.6
4. **循环绘制**:`for \i in {0,...,\num-1}`
- 从0到num-1循环,绘制每个节点
- `\drawy = \starty - \i*\gapy`:每个节点比前一个低一个间距
5. **节点绘制**:`\filldraw[line width=0.5pt, fill=blue!10] (\posx,\drawy) circle (\radius)`
- 在计算出的位置绘制填充的圆形节点
- 边框0.5pt,填充10%蓝色
三、实战案例:D3QN网络图
function paint_input_nodes(\radius,\gapy,\posx,\num){
\gapy = \gapy+\radius*2;
\starty = \gapy*(\num-1)/2;
for \i in {0,...,\num-1}{
\drawy = \starty - \i*\gapy;
{
\filldraw[line width=0.5pt, fill=blue!10] (\posx,\drawy) circle (\radius);
};
};
};
这个函数实现了:自动计算节点间距 垂直居中对齐 循环绘制指定数量的节点
function paint_lines(\radius,\gapy,\posx,\num,\nextposx,\nextnum){
\gapy = \gapy+\radius*2; % 计算节点间距
\starty = \gapy*(\num-1)/2; % 当前层起始Y坐标
\startyy = \gapy*(\nextnum-1)/2; % 下一层起始Y坐标
for \i in {0,...,\num-1}{ % 遍历当前层的每个节点
\drawy = \starty - \i*\gapy; % 当前层第i个节点的Y坐标
for \j in {0,...,\nextnum-1}{ % 遍历下一层的每个节点
\drawyy = \startyy - \j*\gapy; % 下一层第j个节点的Y坐标
{
\draw (\posx,\drawy) -- (\nextposx,\drawyy); % 绘制连接线
};
};
};
};
1. **函数参数**:
- `\radius, \gapy`:节点半径和间距(与节点绘制函数一致)
- `\posx, \num`:当前层的X坐标和节点数量
- `\nextposx, \nextnum`:下一层的X坐标和节点数量
2. **间距计算**:`\gapy = \gapy+\radius*2`
- 与节点绘制函数保持一致,确保连接线从节点边缘开始
3. **两层起始坐标计算**:
- `\starty = \gapy*(\num-1)/2`:当前层垂直居中的起始Y坐标
- `\startyy = \gapy*(\nextnum-1)/2`:下一层垂直居中的起始Y坐标
- 两层可能节点数不同,需要分别计算
4. **双重循环结构**:
- **外层循环**:`for \i in {0,...,\num-1}` 遍历当前层的每个节点
- **内层循环**:`for \j in {0,...,\nextnum-1}` 遍历下一层的每个节点
- 这实现了**全连接**:每个节点连接到下一层的所有节点
5. **坐标计算**:
- `\drawy = \starty - \i*\gapy`:当前层第i个节点的Y坐标
- `\drawyy = \startyy - \j*\gapy`:下一层第j个节点的Y坐标
6. **连接线绘制**:`\draw (\posx,\drawy) -- (\nextposx,\drawyy)`
- 从当前层节点到下一层节点绘制直线
- 形成完整的全连接网络
**连接数量计算**:
- 如果当前层有m个节点,下一层有n个节点
- 总共需要绘制 m × n 条连接线
- 例如:7个节点连接到10个节点,需要70条连接线
**视觉效果**: 形成密集的连接网络,体现神经网络的"全连接"特性,连接线密度随层间节点数乘积增加
3.3 特殊输出层处理
D3QN的输出层需要特殊处理,因为价值函数和优势函数有不同的结构:
1. **输出结构不同**
- **普通网络**:所有输出节点均匀分布,使用`paint_lines`函数
- **Dueling DQN**:输出分为两部分,需要特殊处理
function paint_output_connections(\radius,\gapy,\fromposx,\fromnum,\toposx){
\gapy = \gapy+\radius*2;
\starty = \gapy*(\fromnum-1)/2;
% 连接到价值函数节点 - **特点**:所有隐藏层节点都连接到**同一个位置**(Y=3)
% - **含义**:价值函数V(s)只依赖于状态,与动作无关
for \i in {0,...,\fromnum-1}{
\drawy = \starty - \i*\gapy;
{
\draw (\fromposx,\drawy) -- (\toposx,3);
};
};
% 连接到优势函数节点 - **特点**:每个隐藏层节点连接到**4个不同位置**(Y=0,-1,-2,-3)
% - **含义**:优势函数A(s,a)依赖于状态和动作
for \i in {0,...,\fromnum-1}{
\drawy = \starty - \i*\gapy;
for \j in {0,...,3}{
{
\draw (\fromposx,\drawy) -- (\toposx,\j-3);
};
};
};
};
更多推荐
所有评论(0)