-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontent.json
1 lines (1 loc) · 211 KB
/
content.json
1
{"meta":{"title":"Blogs","subtitle":"Welcome to bg51717's Wiki and Blog","description":"","author":"bg51717","url":"https://bg51717.github.io","root":"/"},"pages":[{"title":"404","date":"2024-09-30T09:25:30.000Z","updated":"2024-11-02T02:55:49.859Z","comments":true,"path":"404.html","permalink":"https://bg51717.github.io/404","excerpt":"","text":""},{"title":"about","date":"2024-09-30T09:25:30.000Z","updated":"2024-11-02T02:55:49.931Z","comments":true,"path":"about/index.html","permalink":"https://bg51717.github.io/about/","excerpt":"","text":""},{"title":"friends","date":"2018-12-12T13:25:30.000Z","updated":"2024-11-02T02:55:49.932Z","comments":true,"path":"friends/index.html","permalink":"https://bg51717.github.io/friends/","excerpt":"","text":""},{"title":"categories","date":"2024-09-30T09:25:30.000Z","updated":"2024-11-02T02:55:49.931Z","comments":true,"path":"categories/index.html","permalink":"https://bg51717.github.io/categories/","excerpt":"","text":""},{"title":"contact","date":"2024-09-30T09:25:30.000Z","updated":"2024-11-02T02:55:49.932Z","comments":true,"path":"contact/index.html","permalink":"https://bg51717.github.io/contact/","excerpt":"","text":""},{"title":"tags","date":"2024-09-30T10:23:38.000Z","updated":"2024-11-02T02:55:49.932Z","comments":true,"path":"tags/index.html","permalink":"https://bg51717.github.io/tags/","excerpt":"","text":""}],"posts":[{"title":"数学计算库:SymPy","slug":"数学/代码/数学计算库-SymPy","date":"2024-11-19T11:19:17.000Z","updated":"2024-11-21T00:59:30.295Z","comments":true,"path":"/16846/","link":"","permalink":"https://bg51717.github.io/16846/","excerpt":"","text":"介绍 SymPy 是一个功能强大、开源的符号计算库(数值计算也同样支持),涵盖了代数、微积分、线性代数、数论、组合数学、物理学等领域的广泛功能。 这个库可以提供你想象到的所有数学操作,包括但不限于: 符号计算:可以定义符号,然后求解方程或者优化问题的解析解。比如合并同类项,简化,展开,因式分解,求导,微分,解方程,极限,泰勒展开,数论,离散数学,统计,几何学等。 数值计算:作为数学库的必备功能,计算数值答案也是稳定支持的。 公式转 latex:对于部分符号,支持转化为 latex代码。 可视化:提供了基本的可视化功能,包括绘制函数图像,几何图像,向量场,符号计算结果等。 和别的科学计算库相比,SymPy的优势有: 基于 BSD协议,开源免费 基于Python,方便使用,可以在交互窗口使用也可以在代码中作为库引入 轻量化,相比别的计算系统,SymPy需要的依赖少且体积小很多 总之,个人感觉这个开源库提供了你所能想象到的所有数学相关的操作,也是许多常用的框架的前置库之一。当你遇到一些数学问题的时候,不妨先查询 SymPy库能否解决。 教程 由于这个库包罗万象,支持的功能过多,因此通过短短一篇博客了解或者快速查询到需要的功能是不太现实的。 因此建议使用的时候可以直接查看参考资料里的相关文档和伟大的 GPT。 参考资料 Tutorials - SymPy 1.13.3 documentation Chatgpt","categories":[{"name":"数学","slug":"数学","permalink":"https://bg51717.github.io/categories/%E6%95%B0%E5%AD%A6/"},{"name":"代码","slug":"数学/代码","permalink":"https://bg51717.github.io/categories/%E6%95%B0%E5%AD%A6/%E4%BB%A3%E7%A0%81/"}],"tags":[{"name":"数学","slug":"数学","permalink":"https://bg51717.github.io/tags/%E6%95%B0%E5%AD%A6/"},{"name":"SymPy","slug":"SymPy","permalink":"https://bg51717.github.io/tags/SymPy/"}]},{"title":"论文阅读习惯","slug":"科研/论文阅读/论文阅读习惯","date":"2024-11-11T13:20:18.000Z","updated":"2024-11-21T00:58:37.745Z","comments":true,"path":"/22407/","link":"","permalink":"https://bg51717.github.io/22407/","excerpt":"","text":"介绍 发现论文阅读习惯是一个十分重要的事情。 在和师兄的交流当中发现,读完一篇文献总是会遗漏一部分内容,这是因为没有良好的论文阅读习惯导致无法从一个文献中获得足够的信息从而变成自己的东西。 因此考虑使用这篇博客记录、分享和培养自己的阅读习惯,进而形成一种规范。 当然,如果你作为有缘人看到这篇博客,希望我的阅读习惯可以为你带来一些帮助,我也十分欢迎有缘人一起在评论区分享自己的阅读习惯。 关注重点 一篇论文,是为解决一个问题或者公布一个发现而存在的,因此关注的重点应该有。 background:这个论文的背景包括,这个论文是关于什么问题或者现象而存在的,关于目前问题的解决办法或者现象的发现方法。 motivation:这个方法的发现动机是什么?是怎么发现或者提出这个方法的? method:这个方法的过程具体是什么样的?具体是怎么解决问题和发现现象的? contribution(advantages):本文的主要贡献是什么?这个方法的优势,和别的方法的对比是什么? experiment settings:验证方法的实验的设置具体是什么?通过设置可以复现实验。 comparison results:对比结果代表了这个方法的改进以及与过去方法的不同体现在哪里,包括不使用这个方法的效果会怎么样。结果可能会以图标或者文字的方式呈现。 ablation results:消融实验代表了方法的内部组件或者不同设置的对比,可以体现不同组件的作用。呈现方式和对比实验一样。 limitation:局限代表了作者认为方法的局限体现在哪里,可以作为日后改进的参考。 流程 我目前是使用 Zotero7来进行论文的阅读。 大致了解 通过 ChatGPT或 博客文章分享来首先对文章有个大致的了解。 细致阅读+笔记 有了大致的了解后,对文章进行细致的阅读。 使用 Zotero阅读论文的时候,可以在右侧记笔记。 文章不同需要关注的点也可以画出来。 img 颜色 论文重点 method motivation background experiment settings experiment comparison results experiment ablation results contribution(advantages) limitation 其中背景色代表重要的,下划线代表重要性次一点的。 代码 如果可以的话,尽量去尝试阅读代码,观察具体是如何实现的,同时也可以作为学习的过程,解答论文里不理解的点。 博客 如果有足够时间,可以尝试去试着输出,通过输出可能会更好的把握内容。","categories":[{"name":"科研","slug":"科研","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/"},{"name":"论文阅读","slug":"科研/论文阅读","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/%E8%AE%BA%E6%96%87%E9%98%85%E8%AF%BB/"}],"tags":[]},{"title":"OmniQuant: Omnidirectionally Calibrated Quantization for Large Language Models","slug":"科研/论文阅读/OmniQuant-Omnidirectionally-Calibrated-Quantization-for-Large-Language-Models","date":"2024-11-09T09:25:51.000Z","updated":"2024-11-11T13:16:01.202Z","comments":true,"path":"/58958/","link":"","permalink":"https://bg51717.github.io/58958/","excerpt":"","text":"介绍 很多针对LLM的PTQ量化算法在设计参数的时候都添加了太多的先验知识,导致性能不佳,尤其在低比特量化中。为了解决这个问题,本文提出了全向校准量化(OmniQuant)技术。 OmniQuant包含两个组件,可学习权重裁剪 (LWC) 和可学习等效变换 (LET)。 LWC通过优化限幅阈值来调节权重的极值。同时,LET 通过将量化的挑战从激活转移到权重来解决激活异常值。OmniQuant 在使用逐块误差最小化的可微框架内运行。在校准的时候,OmniQuant 冻结了模型原始的全精度权重,仅包含LWC和LET可学习的量化参数。 本文也利用了 LLM.int8()和 AWQ中的几个结论: 相比模型的权重,大模型的激活的异常值(绝对值远超别的参数)的绝对值更大。并且无论是哪个 token或者哪个 Layer,异常值都集中在固定的少数维度。 由于与激活相对应的权重的重要性,权重的量化误差在最终性能中也起着关键作用。 方法 分块量化 和别的针对LLm的PTQ量化方法一样,为了降低LLM量化的复杂度,OmniQuant也是逐个block进行量化的。针对每个block,优化的目标函数为: \\[ \\arg \\min_{\\Theta_1, \\Theta_2} \\left\\| \\mathcal{F}(\\mathbf{W}, \\mathbf{X}) - \\mathcal{F} \\left( Q_w(\\mathbf{W}; \\Theta_1, \\Theta_2), Q_a(\\mathbf{X}, \\Theta_2) \\right) \\right\\| \\] 其中,\\(\\mathcal{F}\\) 表示 LLM 中一个 Transformer 块的映射函数,\\(\\mathbf{W}\\) 和 \\(\\mathbf{X}\\) 分别是全精度的权重和激活,\\(Q_w(\\cdot)\\) 和 \\(Q_a(\\cdot)\\) 分别表示权重和激活的量化器,\\(\\Theta_1\\) 和 \\(\\Theta_2\\) 分别是可学习权重裁剪(LWC)和可学习等效变换(LET)的量化参数。公式中的逐块量化在移动到下一个块之前,依次量化一个 Transformer 块的参数。 以block作为量化优化的最小单位有两个好处: 可以对LWC和LET的参数同时进行训练 可以显著减少资源需求 LWC (Learnable Weight Clipping) LWC考虑在量化函数里面加入可学习的参数,具体来说,LWC 优化了一个Clip过程,其公式如下: \\[ \\mathbf{W}_q = \\text{clamp}\\left(\\left\\lfloor \\frac{\\mathbf{W}}{h} \\right\\rceil + z, 0, 2^N - 1\\right), \\quad \\text{其中 } h = \\frac{\\gamma \\max(\\mathbf{W}) - \\beta \\min(\\mathbf{W})}{2^N - 1}, \\quad z = -\\left\\lfloor \\frac{\\beta \\min(\\mathbf{W})}{h} \\right\\rceil \\] 公式里面的\\(\\left\\lfloor \\cdot \\right\\rceil\\)代表取整操作,\\(N\\)是量化的比特数,\\(\\mathbf{W}_q\\) 和 \\(\\mathbf{W}\\) 分别表示量化后的权重和全精度权重。\\(h\\) 是权重的归一化因子,\\(z\\) 是零点值。clamp 操作将值限制在 \\(N\\) 位整数范围内,具体为 \\([0, 2^N - 1]\\)。在公式中,\\(\\gamma \\in [0, 1]\\) 和 \\(\\beta \\in [0, 1]\\) 是分别针对权重上界和下界的可学习剪辑强度。我们通过 sigmoid 函数对 \\(\\gamma\\) 和 \\(\\beta\\) 进行初始化。因此,在公式 (1) 中,\\(\\Theta_1 = \\{\\gamma, \\beta\\}\\)。 其中\\(h,z\\)是个整型,如果直接学习可能难度较大。 LET (Learnable Equivalent Transformation) 考虑到激活中的异常值是系统性的并且对于特定通道是固定的,follow之前的工作,使用通道级缩放和通道级移位来操纵激活分布。参数方面作者认为之前的工作是手动设计的参数,导致结果可能不是很理想。 OmniQuant 没有引入额外的计算或参数,因为和AWQ量化算法一样,LWC 中的限幅阈值和 LET 中的等效因子可以融合为量化权重。 Linear 线性层接受一个输入标记序列 \\(\\mathbf{X} \\in \\mathbb{R}^{T \\times C_\\text{in}}\\),其中 \\(T\\) 是标记长度,其操作是权重矩阵 \\(\\mathbf{W} \\in \\mathbb{R}^{C_\\text{in} \\times C_\\text{out}}\\) 与偏置向量 \\(\\mathbf{B} \\in \\mathbb{R}^{1 \\times C_\\text{out}}\\) 的乘积。一个数学等价的线性层表示为: \\[ \\mathbf{Y} = \\mathbf{X} \\mathbf{W} + \\mathbf{B} = \\underbrace{\\left(\\mathbf{X} - \\delta\\right) \\odot s}_{\\tilde{\\mathbf{X}}} \\cdot \\underbrace{\\left(s \\odot \\mathbf{W}\\right)}_{\\tilde{\\mathbf{W}}} + \\underbrace{\\left(\\mathbf{B} + \\delta \\mathbf{W}\\right)}_{\\tilde{\\mathbf{B}}} \\] 其中,\\(\\mathbf{Y}\\) 表示输出,\\(s \\in \\mathbb{R}^{1 \\times C_\\text{in}}\\) 和 \\(\\delta \\in \\mathbb{R}^{1 \\times C_\\text{in}}\\) 分别是通道级的缩放和偏移参数。\\(\\tilde{\\mathbf{X}}\\),\\(\\tilde{\\mathbf{W}}\\) 和 \\(\\tilde{\\mathbf{B}}\\) 分别是等效的激活、权重和偏置,符号“\\(\\odot\\)”表示元素级的除法和乘法。 通过公式,激活被转化为量化友好的形式,但代价是增加了权重量化的难度。在此意义上,LWC可以改善通过 LET 实现的权重-激活量化的性能,因为它使权重更加量化友好。最终,我们对转换后的激活和权重进行量化,公式如下: \\[ \\mathbf{Y} = Q_a(\\tilde{\\mathbf{X}}) Q_w(\\tilde{\\mathbf{W}}) + \\tilde{\\mathbf{B}} \\] 其中,\\(Q_a\\) 是普通的 MinMax 量化器,\\(Q_w\\) 是带有可学习权重剪辑(即 LWC)的 MinMax 量化器。 请注意,\\(\\tilde{\\mathbf{X}}\\) 中的缩放和偏移参数可以吸收进前面的归一化或线性层,而 \\(\\tilde{\\mathbf{W}}\\) 中的缩放因子可以融合进原始线性权重 \\(\\mathbf{W}\\) 中。因此,公式中的等效变换可以在不引入额外参数或成本的情况下有效减少量化误差。 OmniQuant在 LLM 的线性层(除了 FFN 的第二个线性层)中使用此等效变换。这可能是因为在应用可学习的等效变换时,非线性层后的特征高度稀疏,导致梯度不稳定。 Attention operation 除了线性层外,注意力操作也占据了计算的一个重要部分。此外,LLM 的自回归模式需要存储每个标记的键值(KV)缓存,这对于长序列带来了大量的内存需求。因此,考虑把\\(Q,K,V\\)矩阵在权重-激活量化设置中量化为低比特值。具体而言,自注意力相似度矩阵的可学习等效变换可写为: \\[ \\mathbf{P} = \\text{Softmax}(\\mathbf{Q} \\mathbf{K}^T) = \\text{Softmax} \\left( \\underbrace{\\mathbf{Q} \\odot s_a}_{\\tilde{\\mathbf{Q}}} \\, \\underbrace{(s_a \\odot \\mathbf{K}^T)}_{\\tilde{\\mathbf{K}}^T} \\right). \\] 其中,\\(s_a \\in \\mathbb{R}^{1 \\times C_\\text{out}}\\) 是相似度矩阵中的缩放因子。量化的相似度矩阵计算表示为 \\(\\mathbf{P} = \\text{Softmax}(Q_a(\\tilde{\\mathbf{Q}}) Q_a(\\tilde{\\mathbf{K}}^T))\\)。这里我们同样使用 MinMax 量化方案 \\(Q_a\\) 对 \\(\\tilde{\\mathbf{Q}}/\\tilde{\\mathbf{K}}\\) 矩阵进行量化。我们可以得出 \\(\\Theta_2 = \\{ \\delta, s, s_a \\}\\) 。 \\(\\tilde{\\mathbf{Q}}\\) 和 \\(\\tilde{\\mathbf{K}}\\) 中的通道级缩放因子,可以分别吸收到查询和键投影的线性权重中。值得一提的是,由于逆变换操作的存在,输出投影线性层中的显式变换在其分布上已按通道维度进行了更改,因此省略了 \\(\\mathbf{V}\\) 的变换。 实验 首先测试了只量化模型权重的结果,和GPTQ和AWQ进行了对比,取得了在WikiText2上最低的困惑度。 也测试了权重和激活都进行量化的表现,对比的baseline是SmoothQuant和LLM-QAT,可以看到甚至比LLM-QAT这种QAT量化方法还要好。 然后还参考AWQ的论文,测试了模型回答问题的准确率。把两个模型的回答连接在一起,让GPT-4判断哪个回答更好,为了去除顺序的影响,会交换顺序让GPT-4再回答一次,结果如下图所示: 最后测试了模型的效率情况,统计了算法在不同规模的Llama上的存储需求和推理速度。 这篇论文的附录罗列了很多细节,需要的可以去看原文。 代码 论文源码:OpenGVLab/OmniQuant: [ICLR2024 spotlight] OmniQuant is a simple and powerful quantization technique for LLMs. 参考资料 [2308.13137] OmniQuant: Omnidirectionally Calibrated Quantization for Large Language Models OpenGVLab/OmniQuant: [ICLR2024 spotlight] OmniQuant is a simple and powerful quantization technique for LLMs.","categories":[{"name":"科研","slug":"科研","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/"},{"name":"论文阅读","slug":"科研/论文阅读","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/%E8%AE%BA%E6%96%87%E9%98%85%E8%AF%BB/"}],"tags":[]},{"title":"OneBit: Towards Extremely Low-bit Large Language Models","slug":"科研/论文阅读/OneBit-Towards-Extremely-Low-bit-Large-Language-Models","date":"2024-11-08T12:33:02.000Z","updated":"2024-11-11T03:20:58.488Z","comments":true,"path":"/341/","link":"","permalink":"https://bg51717.github.io/341/","excerpt":"","text":"介绍 OneBit属于量化方法中的量化感知训练QAT。基于BitNet的OneBit将LLM的权重矩阵量化为1位,用一种新颖的 1 位参数表示方法以更好地量化 LLM,以及一种基于矩阵分解的有效参数初始化方法以提高量化框架的收敛速度。 方法 BitLinear OneBit follow的工作是BitNet,BitNet提出BitLinear,BitLinear的forward函数为: \\[ \\begin{align*} & \\mathbf{W}_{\\pm 1} = \\operatorname{Sign}\\left( \\mathbf{W} - \\operatorname{Mean}(\\mathbf{W}) \\right), \\\\ & \\eta = \\operatorname{Mean} \\left( \\operatorname{Abs} \\left( \\mathbf{W} - \\operatorname{Mean}(\\mathbf{W}) \\right) \\right), \\\\ & \\mathbf{Y} = \\eta \\cdot \\operatorname{LayerNorm}(\\mathbf{X}) \\mathbf{W}_{\\pm 1}^{\\top}, \\end{align*} \\] 其中,\\(W\\)表示量化后的权重矩阵,形状为 \\(m \\times n\\),\\(W_{±1}\\)表示 1 位量化矩阵。\\(X\\)是线性层的输入,\\(Y\\) 是输出。函数 \\(Sign()\\)、\\(Mean()\\) 和 \\(Abs()\\) 分别返回符号矩阵、平均值和绝对值矩阵。 作者认为,缺失的浮点精度仍然破坏了模型性能,因此额外引入了两个始终保持fp16精度的向量: \\[ \\begin{align*} & \\mathbf{W}_{\\pm 1} = \\operatorname{Sign}(\\mathbf{W}), \\\\ & \\mathbf{Y} = \\left[ (\\mathbf{X} \\odot \\mathbf{g}) \\mathbf{W}_{\\pm 1}^{\\top} \\right] \\odot \\mathbf{h}, \\\\ & \\mathbf{Z} = \\operatorname{LayerNorm}(\\mathbf{Y}), \\end{align*} \\] 其中的\\(g\\)和\\(h\\)是fp16精度的向量,\\(Z\\)是最终的输出。同时括号严格要求了计算顺序从而减小计算成本。其余的和BitNet保持一致。 训练的时候,不需要保存一个高精度的参数矩阵,只需要保存两个高精度的向量即可。 SVID 为了可以使用训练好的模型的ckpt来初始化量化模型的权重,这篇论文还引入了Sign-Value-Independent Decomposition (SVID),所以近似初始化方法为, \\[ \\mathbf{W} \\approx \\mathbf{W}_{\\text{sign}} \\odot (\\mathbf{a} \\mathbf{b}^{\\top}) \\] 就是用训练好模型的权重矩阵\\(W\\),分解为量化模型的三部分\\(\\mathbf{W}_{\\text{sign}} , \\mathbf{a} , \\mathbf{b}\\)。 这里提出两个命题来说明合理性: 命题一:使用上面的近似,我们可以得到, \\[ \\mathbf{X} \\mathbf{W}^{\\top} \\approx \\left[ (\\mathbf{X} \\odot \\mathbf{b}^{\\top}) \\mathbf{W}_{\\text{sign}}^{\\top} \\right] \\odot \\mathbf{a}^{\\top} \\] 根据这个命题,可以从近似初始化得到BitLinear的forward公式。 证明过程: 令\\(s_{ij}\\)为1Bit矩阵\\(W_{sign}\\)的一个元素,我们很容易得到:\\(w_{i,j} \\approx s_{i,j} \\cdot a_i b_j\\) 。 因此,我们有, \\[ \\begin{align*} \\left( \\mathbf{X} \\mathbf{W}^{\\top} \\right)_{ij} & = \\sum_k x_{ik} w_{kj} \\\\ & = \\sum_k x_{ik} w_{jk} \\\\ & \\approx \\sum_k x_{ik} s_{jk} a_j b_k \\\\ & = \\sum_k x_{ik} b_k s_{jk} a_j \\\\ & = \\sum_k \\left( \\mathbf{X} \\odot \\mathbf{b}^{\\top} \\right)_{ik} s_{kj}^{\\top} a_j \\\\ & = \\left[ \\left( \\mathbf{X} \\odot \\mathbf{b}^{\\top} \\right) \\mathbf{W}_{\\text{sign}}^{\\top} \\right]_{ij} a_j \\\\ & = \\left\\{ \\left[ \\left( \\mathbf{X} \\odot \\mathbf{b}^{\\top} \\right) \\mathbf{W}_{\\text{sign}}^{\\top} \\right] \\odot \\mathbf{a}^{\\top} \\right\\}_{ij} \\end{align*} \\] 命题二:给定矩阵 $W $和 \\(|W|\\),其中 \\(W = W_{\\text{sign}} \\odot |W|\\)。以如下方式对这些矩阵进行分解:\\(W = \\mathbf{a} \\mathbf{b}^{\\top} + \\mathbf{E}_1\\)和\\(|W| = \\tilde{\\mathbf{a}} \\tilde{\\mathbf{b}}^{\\top} + \\mathbf{E}_2\\),其中\\(E_i\\)表示误差矩阵。就 Frobenius 范数而言,SVD 分解比原始矩阵 $W $更接近: \\[ \\left\\| W - W_{\\text{sign}} \\odot \\tilde{\\mathbf{a}} \\tilde{\\mathbf{b}}^{\\top} \\right\\|_F^2 \\leq \\left\\| W - \\mathbf{a} \\mathbf{b}^{\\top} \\right\\|_F^2. \\] 根据这个命题,可以说明提出符号位可以更好地减小误差。 引理 1 令 \\(\\sigma_i(\\mathbf{W})\\) 表示矩阵 \\(\\mathbf{W}\\) 的第 \\(i\\) 大奇异值。则以下不等式成立: \\[ \\sigma_1(|\\mathbf{W}|) \\geq \\sigma_1(\\mathbf{W}). \\] 引理证明 根据诱导范数的定义,有 \\[ \\sigma_1(\\mathbf{W}) = \\|\\mathbf{W}\\|_2 = \\max_{\\mathbf{x}, \\|\\mathbf{x}\\|_2=1} \\|\\mathbf{W} \\mathbf{x}\\|_2, \\] \\[ \\sigma_1(|\\mathbf{W}|) = \\||\\mathbf{W}|\\|_2 = \\max_{\\mathbf{y}, \\|\\mathbf{y}\\|_2=1} \\||\\mathbf{W}| \\mathbf{y}\\|_2. \\] 注意对于任意 \\(\\mathbf{x}\\),\\(\\|\\mathbf{x}\\|_2 = 1\\),我们有 \\[ \\||\\mathbf{W}| \\mathbf{x}\\|_2^2 = \\sum_i \\left( \\sum_j |w_{ij}||x_j| \\right)^2 \\geq \\sum_i \\left( \\sum_j w_{ij} x_j \\right)^2 = \\|\\mathbf{W} \\mathbf{x}\\|_2^2. \\] 因此 \\[ \\max_{\\mathbf{y}, \\|\\mathbf{y}\\|_2=1} \\||\\mathbf{W}| \\mathbf{y}\\|_2 \\geq \\max_{\\mathbf{x}, \\|\\mathbf{x}\\|_2=1} \\|\\mathbf{W} \\mathbf{x}\\|_2. \\] 该引理得证。 命题证明 考虑通过 SVD 来证明。对于 SVD 分解,因为使用两个向量近似,等同于只保留最大的奇异值和对应的特征向量,rank-1 近似中的误差矩阵 \\(\\mathbf{E}\\) 的范数等于所有奇异值平方和中除去最大奇异值后的总和。我们有 \\[ \\|\\mathbf{E}_1\\|_F^2 = \\sum_{i=2}^n \\sigma_i^2(\\mathbf{W}), \\] \\[ \\|\\mathbf{E}_2\\|_F^2 = \\sum_{i=2}^n \\sigma_i^2(|\\mathbf{W}|). \\] 根据 \\(\\|\\mathbf{W}\\|_F^2 = \\||\\mathbf{W}|\\|_F^2\\),我们有 \\[ \\sum_{i=1}^n \\sigma_i^2(\\mathbf{W}) = \\sum_{i=1}^n \\sigma_i^2(|\\mathbf{W}|). \\] 根据引理 1,我们可以得出 \\[ \\|\\mathbf{E}_1\\|_F^2 \\geq \\|\\mathbf{E}_2\\|_F^2. \\] 根据该命题中的方程,我们可以表示 \\[ \\mathbf{W}_{\\text{sign}} \\odot |\\mathbf{W}| = \\mathbf{W}_{\\text{sign}} \\odot (\\tilde{\\mathbf{a}} \\tilde{\\mathbf{b}}^{\\top} + \\mathbf{E}_2). \\] 因此我们有 \\[ \\mathbf{W} - \\mathbf{W}_{\\text{sign}} \\odot \\tilde{\\mathbf{a}} \\tilde{\\mathbf{b}}^{\\top} = \\mathbf{W}_{\\text{sign}} \\odot \\mathbf{E}_2. \\] 因此 \\[ \\|\\mathbf{W}_{\\text{sign}} \\odot \\mathbf{E}_2\\|_F^2 = \\sum_{i,j} s_{ij}^2 e_{ij}^2 = \\sum_{i,j} e_{ij}^2 = \\|\\mathbf{E}_2\\|_F^2 \\leq \\|\\mathbf{E}_1\\|_F^2, \\] 其中 \\(s_{ij} = \\pm 1\\) 是 \\(\\mathbf{W}_{\\text{sign}}\\) 的元素。由此,该命题中的不等式得证。 知识蒸馏 考虑在量化模型训练的时候加入知识蒸馏,大的教师模型代表量化前的模型,小的学生模型代表量化后的模型。损失由两部分组成,首先是交叉熵损失, \\[ \\mathcal{L}_{\\text{CE}} = -\\frac{1}{n_s} \\sum_{i=1}^{n_s} \\sum_{c} P_c^{T}(o_i) \\log P_c^{S}(o_i) \\] 第二个是所以hidden states归一化后的差值的L2范数, \\[ \\mathcal{L}_{\\text{MSE}} = \\sum_{i=1}^{n_s} \\sum_{j=1}^{n_l} \\left\\| \\frac{q_{i,j}^{T}}{\\|q_{i,j}^{T}\\|_2} - \\frac{q_{i,j}^{S}}{\\|q_{i,j}^{S}\\|_2} \\right\\|_2^2 \\] 总的损失为: \\[ \\mathcal{L}_{\\text{KD}} = \\mathcal{L}_{\\text{CE}} + \\alpha \\mathcal{L}_{\\text{MSE}} \\] 由于符号十分常用,因此这里省去对于其中的符号的解释说明。 训练的时候,使用是模拟量化进行训练,即原来的权重也会进行训练,部署的时候再压缩: 实验 首先测试了该量化方法和一些PTQ方法和QAT方法的困惑度比较,其中的baseline为W2A16量化,本文方法为W1A16,可以看到取得了较大领先, 为了评估实际解决问题的能力,还测试了和一些小模型能力的差异和资源的需求,其中的OneBit是本文方法训练的模型,LowRank LLaMA是通过低秩分解压缩的Llama模型: 代码 论文源码:xuyuzhuang11/OneBit: The homepage of OneBit model quantization framework. 个人认为其中比较重要的部分: OneBit/transformers/src/transformers/models/bitnet.py at main · xuyuzhuang11/OneBit OneBit/scripts/build_start_ckpt.py at main · xuyuzhuang11/OneBit 参考资料 [2402.11295] OneBit: Towards Extremely Low-bit Large Language Models xuyuzhuang11/OneBit: The homepage of OneBit model quantization framework.","categories":[{"name":"科研","slug":"科研","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/"},{"name":"论文阅读","slug":"科研/论文阅读","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/%E8%AE%BA%E6%96%87%E9%98%85%E8%AF%BB/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"量化","slug":"量化","permalink":"https://bg51717.github.io/tags/%E9%87%8F%E5%8C%96/"}]},{"title":"矩阵奇异值分解SVD","slug":"数学/线性代数/矩阵奇异值分解SVD","date":"2024-11-08T05:26:13.000Z","updated":"2024-11-08T17:00:43.626Z","comments":true,"path":"/33996/","link":"","permalink":"https://bg51717.github.io/33996/","excerpt":"","text":"定理陈述 对于任意 \\(m \\times n\\) 的矩阵 \\(A\\),存在一个分解: \\[ A = U \\Sigma V^T \\] 其中: r=rank(A)是矩阵A的秩 \\(U\\) 是一个 \\(m \\times m\\) 的正交矩阵(即 \\(U^T U = I\\)),前r列是矩阵\\(A A^\\top\\)的非零特征值的特征向量。 \\(V\\) 是一个 \\(n \\times n\\) 的正交矩阵(即 \\(V^T V = I\\)),前r列是矩阵$ A^A$ 的非零特征值的特征向量。 \\(\\Sigma\\) 是一个 \\(m \\times n\\) 的对角矩阵,其对角线元素是非负实数,前r个非负实数称为A的奇异值。 分解结果不是唯一的,不同分解的区别主要在于特征向量和奇异值的顺序。 证明 参考自:奇异值分解(SVD)推导证明与应用_简述奇异值分解定理,并进行推导与证明(公式以及矩阵可以写在纸上照下来)-CSDN博客 因为\\(AA^\\top\\)是半正定矩阵,所以存在正交矩阵\\(V_{n\\times n}\\), \\[ V^T (A^T A) V = \\operatorname{diag}(\\lambda_1, \\lambda_2, \\dots, \\lambda_n) \\] 其中\\(\\lambda_1 > \\lambda_2 > \\cdots > \\lambda_r > 0 = \\lambda_{r+1} = \\cdots = \\lambda_n\\)为\\(AA^\\top\\)的n个非负特征根。 令\\(\\Sigma_1 = \\operatorname{diag}(\\sigma_1, \\sigma_2, \\dots, \\sigma_r) = \\operatorname{diag}(\\sqrt{\\lambda_1}, \\sqrt{\\lambda_2}, \\dots, \\sqrt{\\lambda_r}), \\quad V_1 = [\\mathbf{v}_1, \\mathbf{v}_2, \\dots, \\mathbf{v}_r], \\quad V_2 = [\\mathbf{v}_{r+1}, \\mathbf{v}_{r+2}, \\dots, \\mathbf{v}_n],V=[V_1,V_2]\\),所以根据\\(V\\)定义,两个式子左边乘以\\(V\\),得到 \\[ A^\\top A V=V\\Sigma_1 \\] 取其中的r个列向量,有 \\[ A^\\top A V_1=V_1\\Sigma_1 \\] 进一步,我们可以得到 \\[ \\Sigma_1^{-1} V_1^\\top A^\\top A V_1 \\Sigma_1^{-1} = I \\] 令 \\(U_1 = A V_1 \\Sigma_1^{-1}\\),则有 \\(U_1^\\top U_1 = I\\)。此时,我们可以选择 \\(m - r\\) 组标准正交向量与 \\(U_1\\) 的列向量组成一组标准正交基,也即 \\([U_1, U_2]\\) 是一个 \\(m \\times m\\) 阶正交矩阵,且 \\(U_1^\\top U_2 = 0\\)。 另一方面,容易得到: \\[ A^\\top A V_2 = V_2 0 \\Rightarrow V_2^\\top A^\\top A V_2 = 0 \\] 根据矩阵性质\\(A = 0 \\Leftrightarrow A^\\top A = 0\\),我们可以得到\\(AV_2=0\\)。 \\[ U^\\top A V = \\begin{bmatrix} U_1^\\top A V_1 & U_1^\\top A V_2 \\\\ U_2^\\top A V_1 & U_2^\\top A V_2 \\end{bmatrix} = \\begin{bmatrix} \\Sigma_1 & 0 \\\\ U_2^T U_1 \\Sigma_1 & 0 \\end{bmatrix} = \\begin{bmatrix} \\Sigma_1 & 0 \\\\ 0 & 0 \\end{bmatrix} = \\Sigma \\] 即, \\[ U A V^\\top=\\Sigma \\] 几何理解 如果几何变换的方法为\\(XA\\),那么SVD将矩阵的线性变换拆解为三步:旋转(或方向调整)\\(U\\)- 缩放 \\(\\Sigma\\) - 再旋转 \\(V\\),其中的缩放操作可以理解为,首先奇异值会对向量进行缩放: 如果m<n,会额外增加一些为0的维度 如果m>n,会删去一些维度 应用 在机器学习的PCA中使用较多,作为特征工程降低数据维度的一个重要方法。 在一些信号领域也可以用于去噪或者图像领域的数据压缩。 代码 Pytorch: import torch # 示例矩阵 A = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]) # 计算 SVD U, S, V = torch.svd(A) print(\"U:\", U) print(\"S:\", S) # 奇异值向量 print(\"V:\", V) numpy: import numpy as np # 示例矩阵 A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) # 计算 SVD U, sigma, VT = np.linalg.svd(A) print(\"U:\", U) print(\"Sigma:\", sigma) print(\"V^T:\", VT) scikit-learn: from sklearn.decomposition import TruncatedSVD import numpy as np # 示例矩阵 A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) # 实例化 TruncatedSVD,指定保留的奇异值数量 svd = TruncatedSVD(n_components=2) A_reduced = svd.fit_transform(A) print(\"Reduced Matrix (A_reduced):\") print(A_reduced) print(\"Explained Variance Ratio:\", svd.explained_variance_ratio_) 参考资料 奇异值分解(SVD)推导证明与应用_简述奇异值分解定理,并进行推导与证明(公式以及矩阵可以写在纸上照下来)-CSDN博客","categories":[{"name":"数学","slug":"数学","permalink":"https://bg51717.github.io/categories/%E6%95%B0%E5%AD%A6/"},{"name":"线性代数","slug":"数学/线性代数","permalink":"https://bg51717.github.io/categories/%E6%95%B0%E5%AD%A6/%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0/"}],"tags":[{"name":"矩阵","slug":"矩阵","permalink":"https://bg51717.github.io/tags/%E7%9F%A9%E9%98%B5/"},{"name":"SVD","slug":"SVD","permalink":"https://bg51717.github.io/tags/SVD/"}]},{"title":"梯度估计STE","slug":"深度学习/经典模块/梯度估计STE","date":"2024-11-04T12:15:47.000Z","updated":"2024-11-08T12:36:20.417Z","comments":true,"path":"/13277/","link":"","permalink":"https://bg51717.github.io/13277/","excerpt":"","text":"背景 反向传播是现在训练模型的重要方法,但是在部分场景下,会遇到不可微分的函数,从而导致梯度传播失败。比如量化里的取整函数。因此,需要对梯度进行估计然后反向传播。 STE(Straight-Through Estimator)是2013年Yoshua Bengio等人针对梯度估计进行了研究,那篇论文提出了几种梯度估计的方法,并推导出了一些理论性质,然后通过实验证明,STE是效果最好的方法。 由于那篇论文很多篇幅在介绍较为复杂的估计方法,且理论推导也极为复杂,效果没有简洁的STE好,因此不对其进行详细介绍。 应用 恒等函数 STE在反向传播理论推导的时候,把不可微的原子函数(比如量化函数里有放缩和取整两部分,其中取整是不可微的原子函数)替换为恒等函数。 这种应用可以大大减少理论推导的难度,但是在代码里应用反向传播的时候不太方便,以pytorch框架为例,可能需要自己手写函数类 torch.autograd.Function,具体文档可以查看:PyTorch: Defining New autograd Functions — PyTorch Tutorials 2.5.0+cu124 documentation 。 SG函数 苏神在他的博客(VQ-VAE的简明介绍:量子化自编码器 - 科学空间|Scientific Spaces)中提出了一个函数 sg(stop gradient),代表梯度反向传播终止的恒等函数。 所以原来的不可微的原子函数可以写为\\(f(x)=x+sg(x'-x)\\) ,在前向传播的时候和x'相同,在反向传播的时候梯度等于直接对 x反向传播梯度。 这个式子也能用于理论推导,但是不如视为恒等函数麻烦,但是在代码方面容易完成,可以使用pytorch的默认反向传播函数,不需要自定义 torch.autograd.Function(当然自定义也可以完成任务)。 BitNet/bitnet/bitlinear.py at main · kyegomez/BitNet 代码示例: # w_quant 是 w 量化(不可微)后的值 # STE using detach w_quant = w + (weight_quant(w) - w).detach() 这里也给出 torch.autograd.Function的一个示例(OneBit/transformers/src/transformers/models/bitnet.py at main · xuyuzhuang11/OneBit): class SignSTEFunc(torch.autograd.Function): @staticmethod def forward(ctx, input): ctx.save_for_backward(input) return torch.sign(input) @staticmethod def backward(ctx, grad_output): input, = ctx.saved_tensors return grad_output * (1.001 - torch.tanh(input) ** 2) # return grad_output * (1.01 - torch.tanh(input) ** 2) 参考资料 [1308.3432] Estimating or Propagating Gradients Through Stochastic Neurons for Conditional Computation PyTorch: Defining New autograd Functions — PyTorch Tutorials 2.5.0+cu124 documentation VQ-VAE的简明介绍:量子化自编码器 - 科学空间|Scientific Spaces kyegomez/BitNet: Implementation of \"BitNet: Scaling 1-bit Transformers for Large Language Models\" in pytorch OneBit/transformers/src/transformers/models/bitnet.py at main · xuyuzhuang11/OneBit","categories":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"经典模块","slug":"深度学习/经典模块","permalink":"https://bg51717.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/%E7%BB%8F%E5%85%B8%E6%A8%A1%E5%9D%97/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"梯度估计","slug":"梯度估计","permalink":"https://bg51717.github.io/tags/%E6%A2%AF%E5%BA%A6%E4%BC%B0%E8%AE%A1/"}]},{"title":"Welcome to bg51717's Wiki and Blog","slug":"index","date":"2024-11-02T02:55:49.874Z","updated":"2024-11-02T02:55:49.874Z","comments":true,"path":"/14261/","link":"","permalink":"https://bg51717.github.io/14261/","excerpt":"","text":"这是bg51717的个人Wiki和Blog站点,主要是把知识系统的罗列出来以及存放一些特殊bug的处理,当然也会更一些游戏或者二次元相关东西,也希望在成长的过程中可以认识许多志同道合的人。 本人一直认为互联网的开源是社会发展的重要原因之一,因为开源使得技术知识和解决问题的经验可以被记录和传承下去,很多时候在需要的时候可以被人们所发掘。 也希望可以通过博客让自己的思维有条理。很多时候我喜欢观察别人的思路,发现其实人与人的很多思路差距可能没有那么多。除开经验上的差别,很多人能成功的做成一件事很多原因是思维非常有条理,时时刻刻明白自己的应该做什么,下一步思路是什么。不会让自己的思维局限在某个步骤或者门槛上。从而即使在逆境中,也能实现把烂牌打出最好的效果。 在偶尔反思自己的不足的时候,深刻的发现拖延症是致命的,很多事情只要按照条理有计划的进行,结果其实都可以完成。但是拖延容易导致事情出现计划外的变故,进而导致完成的质量。这对于个人的成长来说是极为不利的。很早以前就看到了知行合一这个词,但是一直没有理解其重要性。后来发现,缺少行动力也会导致很多计划的失败。很多事物是需要我们用心去做,而不是用敷衍的态度去进行。在实践中不断地提升自己,革新自己。 不知道此刻在阅读这个博客的你是在何时何地打开的,也不知道你是为了探究什么而点开了这个链接。但是祝你身体健康,万事如意。大家一起学习,互相交流,共同成长! 最后,附上我喜欢的一句话: 世界靠现实主义者维系,靠理想主义者发展。","categories":[],"tags":[]},{"title":"AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration","slug":"科研/论文阅读/AWQ-Activation-aware-Weight-Quantization-for-LLM-Compression-and-Acceleration","date":"2024-10-27T05:35:56.000Z","updated":"2024-11-11T14:55:02.024Z","comments":true,"path":"/14592/","link":"","permalink":"https://bg51717.github.io/14592/","excerpt":"","text":"介绍 本篇博客介绍论文[2306.00978] AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration提出的一种针对权重的训练后量化方法。该方法基于一个发现:模型的权重不是同等重要的,只保护 1%的突出权重可以大大减少量化误差。但是混合精度会导致算法对硬件不友好,具体保护的方法是对模型的权重进行扩大,输入也进行对应的缩小。具体的比例根据激活通过网格搜索获得。由于没有进行反向传播,因此量化模型在与校准数据集不同分布的数据上也能表现良好。并最终实现了推理加速效果。 下图是PTQ大致流程图,和GPTQ算法一样,AWQ算法针对的主要是量化步骤中的参数调整部分: 发现 首先介绍作者根据经验提出的假设:模型的权重不是同等重要的,只保护 1%的突出权重可以大大减少量化误差。下图是示例: 从图里可以看出,在某一层layer的输入中,不同token的异常维度应该是集中在某几个维度中的。在LLM.int8中也有类似的结论,并且因此设计了llm.int8算法: 为了验证这个假设,作者进行了实验,尝试对少部分显著的权重保持原来精度,别的权重量化为int3,然后进行了多组测试。RTN是直接量化,不做调整,作为对比实验。作者分别尝试了使用权重的大小,激活的大小以及随机选择来确定哪些权重的显著的,结果发现根据激活选择的效果最好。 方法 根据发现,只需要根据激活选择少部分权重保持为高精度即可实现优秀的量化算法,但是混合精度是硬件不友好的操作。因此,作者选择别的方法来保护这些权重:通过激活感知缩放来保护显着权重。 考虑权重 \\(\\mathbf{w}\\),线性层操作可以记为\\(y = \\mathbf{w} \\mathbf{x}\\), 对应的量化操作(包含反量化)为\\(y = Q(\\mathbf{w}) \\mathbf{x}\\),\\(N\\)是量化的比特数,\\(\\Delta\\)是缩放因子,量化函数为: \\[ Q(\\mathbf{w}) = \\Delta \\cdot \\text{Round}\\left(\\frac{\\mathbf{w}}{\\Delta}\\right), \\quad \\Delta = \\frac{\\max(|\\mathbf{w}|)}{2^{N-1}} \\] 考虑针对权重中的一个向量 $w $ 乘以 $s > 1 $ ,然后按同等比例缩小 $x $, 我们有 $Q(w s)(x / s) $, 具体为: \\[ Q(w \\cdot s) \\cdot \\frac{x}{s} = \\Delta' \\cdot \\text{Round}\\left(\\frac{w s}{\\Delta}\\right) \\cdot x \\cdot \\frac{1}{s} \\] \\(\\Delta'\\)是新的缩放因子。 这里解释一下为什么放缩针对是一行(这里假设乘号左边是X,右边是W): 然后量化的时候,以列或者行作为量化函数的基本单位: 作者根据经验判断: 量化为整数(Round函数)的时候,误差在0~0.5之间均匀分布,因此,平均误差为0.25。因此在对权重放大后,相对误差会减小,比如之前值是1,相对误差为0.25,1放大2倍后,相对误差只有0.125。 对部分\\(w\\)进行放缩对量化时分组里的极值改变较少,因此\\(\\Delta' \\approx \\Delta\\) 第二点的误差可以表示为\\(\\text{Err}' = \\Delta' \\cdot \\text{RoundErr} \\cdot \\frac{1}{s}\\),与原始误差的比例为\\(\\frac{\\Delta'}{\\Delta} \\cdot \\frac{1}{s}\\)。如果\\(\\Delta' \\approx \\Delta,s>1\\),那么就会降低误差 然后作者选择了一部分参数进行了验证: 根据结果发现,在s=2的时候PPL达到了最小。在s不超过2的时候,基本符合根据经验得到的结论。 对于AWQ算法中的\\(s\\),作者认为这是一个最优化问题: \\[ \\mathbf{s}^* = \\arg \\min_{\\mathbf{s}} \\, \\mathcal{L}(\\mathbf{s}), \\quad \\mathcal{L}(\\mathbf{s}) = \\left\\| Q(\\mathbf{W} \\cdot \\mathbf{s}) \\left(\\mathbf{s}^{-1} \\cdot \\mathbf{X}\\right) - \\mathbf{W} \\mathbf{X} \\right\\| \\] 由于量化函数不可微分,别的近似方法存在收敛不稳定的情况,作者最终根据激活使用网格搜索获得: \\[ \\mathbf{s} = \\mathbf{s}_{\\mathbf{X}}^{\\alpha}, \\quad \\alpha^* = \\arg \\min_{\\alpha} \\mathcal{L}(\\mathbf{s}_{\\mathbf{X}}^{\\alpha}) \\] \\(\\mathbf{s} _{\\mathbf{X}}\\)和输入\\(\\mathbf{X}\\)有关,\\(\\alpha\\)搜索的范围在[0,1]之间。 作者通过实验验证了搜索的有效性: 此外,作者还进行了以下优化: 对扩大化的权重进行裁剪 把对输入的缩小和前一个算子融合(比如,以前一个算子是矩阵乘法作为例子:前一个矩阵权重权重缩小\\(s\\),对前一个算子来说,扩大比例和缩小的比例大小不一样,并且可能方向也不一样,比如一个是列方向,另一个是行方向,因此不会完全抵消 实验 作者首先在LlaMa家族模型中进行了实验,比较在WikiText-2上的困惑度,发现AWQ的效果比GPTQ要好一点。 然后作者选取了80个样本问题,把量化前后模型的回答连接起来,让GPT-4打分判断哪个更好(交换次序后再重复一次)。结果也比之前的方法好。 作者还测试了在多模态大模型上表现: 还测试了在2比特量化下的表现,作者还提出AWQ和GPTQ是可以一起使用的: 然后作者罗列了一下推理加速的效果,可以看到每秒生成的token数目增长了很多: 作者认为,由于AWQ在使用校准数据进行量化的时候没有进行反向传播,因此可以过拟合校准集合: 代码 论文源码:mit-han-lab/llm-awq: [MLSys 2024 Best Paper Award] AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration 算法工具包:AutoGPTQ/AutoGPTQ: An easy-to-use LLMs quantization package with user-friendly apis, based on GPTQ algorithm. 参考资料 [2306.00978] AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration mit-han-lab/llm-awq: [MLSys 2024 Best Paper Award] AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration","categories":[{"name":"科研","slug":"科研","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/"},{"name":"论文阅读","slug":"科研/论文阅读","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/%E8%AE%BA%E6%96%87%E9%98%85%E8%AF%BB/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"量化","slug":"量化","permalink":"https://bg51717.github.io/tags/%E9%87%8F%E5%8C%96/"}]},{"title":"拉格朗日乘数法解条件极值","slug":"数学/微积分/拉格朗日乘数法解条件极值","date":"2024-10-24T09:10:04.000Z","updated":"2024-11-02T02:55:49.880Z","comments":true,"path":"/49444/","link":"","permalink":"https://bg51717.github.io/49444/","excerpt":"","text":"介绍 求解最优化是一类十分常见且难以求解的问题,因此,考虑开一个博客系统性的介绍一下重要解法:拉格朗日乘数法(Lagrange Multiplier Method)。之后再扩展到广义的拉格朗日乘数法。 拉格朗日乘数法的重点是在一些列约束条件下,构造包含隐藏条件的拉格朗日函数等同于优化的目标函数。 隐藏条件指的是在限制条件和拉格朗日函数共同作用下实际暗含的一些条件或方程。 最优化问题通常是指对于给定的某一函数,求其在指定作用域上的全局最小值(因为最小值与最大值可以很容易转化,即最大值问题可以转化成最小值问题) 。 无约束条件 对所有变量求偏导计算极值点后,把极值点代回原函数验证即可。 等式约束条件 求一个函数在等式约束下的最值,即 \\[ \\begin{aligned} \\min & \\quad f(\\mathbf{x}) \\\\ \\text{s.t.} & \\quad h_j(\\mathbf{x}) = 0, \\quad j = 1, 2, \\dots, l \\end{aligned} \\] 定义拉格朗日函数\\(F(x)\\),其中\\(\\lambda_k\\)为引入的变量: \\[ F(\\mathbf{x}, \\lambda) = f(\\mathbf{x}) + \\sum_{j=1}^{l} \\lambda_j h_j(\\mathbf{x}) \\] 求解极值点只需求解方程组: \\[ \\frac{\\partial F}{\\partial x_i} = 0 \\\\ \\frac{\\partial F}{\\partial \\lambda_j} = 0 \\] 下面关于不等式约束下的最优值求解给了通用的证明过程,不过,维基百科列出了一个形象的解释: 考虑有个优化问题: \\[ \\begin{aligned} \\min & \\quad f(x, y) \\\\ \\text{s.t.} & \\quad g(x, y) = c \\end{aligned} \\] 图上画的是两个函数的等高线,箭头表示梯度方向(暂时不考虑梯度正负)。绿色的线是约束条件,代表需要在绿色的线上寻找最优点。最优点显然在两个函数相切的地方,即两个函数梯度共线,\\(\\nabla f(x, y) = \\lambda \\left( \\nabla g(x, y) - C \\right)\\)(\\(\\nabla\\)代表梯度算子,\\(\\lambda\\)是非0实数),这就是等式约束下拉格朗日乘数法的隐藏条件。 从反证法考虑,假如两个梯度不共线,那么沿着约束函数的切向,即与约束函数梯度垂直的方向,一定可以找到一个向量,与约束函数梯度垂直且在目标函数梯度上有分量。那么沿着该向量方向修改变量,可以在约束函数值不变的条件下继续优化目标函数。 不等式条件约束 添加上不等式约束条件,就得到了广义拉格朗日问题,此时的最优化问题为, \\[ \\begin{aligned} \\min & \\quad f(\\mathbf{\\mathbf{x}}) \\\\ \\text{s.t.} & \\quad h_j(\\mathbf{\\mathbf{x}}) = 0, \\quad j = 1, 2, \\dots, p \\\\ & \\quad g_k(\\mathbf{\\mathbf{x}}) \\leq 0, \\quad k = 1, 2, \\dots, q \\end{aligned} \\] 对应的拉格朗日函数L为, \\[ L(\\mathbf{x}, \\lambda, \\mu) = f(\\mathbf{x}) + \\sum_{j=1}^{p} \\lambda_j h_j(\\mathbf{x}) + \\sum_{k=1}^{q} \\mu_k g_k(\\mathbf{x}) \\] 常用的方法是KKT条件,即最优值必须满足: \\[ \\begin{aligned} \\frac{\\partial F}{\\partial x_i} = 0 \\quad &(1)\\\\ \\frac{\\partial F}{\\partial \\lambda_j} = 0 \\quad &(2)\\\\ \\lambda_j \\neq 0 \\quad &(3)\\\\ \\mu_k g_k(\\mathbf{x}) = 0 \\quad &(4)\\\\ g_k(\\mathbf{x}) \\leq 0 \\quad &(5)\\\\ \\mu_k \\geq 0 \\quad &(6) \\end{aligned} \\] 其中前三个式子是等式条件约束里的,后三个式子是不等式条件引入的。 KKT推导 \\(\\max_\\mu L(x,\\mu)\\)代表调整\\(\\mu\\)最大化目标函数\\(L(x,\\mu)\\)。 首先,令(这里的几个变量都是多元变量,即向量,为了书写方便,没有引入 \\mathbf) \\[ \\ L(x, \\lambda, \\mu) = f(x) + \\sum_{k=1}^{q} \\mu_k g_k(x) \\] 引入约束条件, \\[ \\because \\begin{cases} \\mu_k \\geq 0 \\\\ g_k(x) \\leq 0 \\end{cases} \\\\ \\\\ \\therefore \\mu_k g_k(x) \\leq 0 \\\\ \\therefore 根据非负得\\ \\max_\\mu L(x,\\mu)=f(x) \\\\ \\therefore \\min_ x f(x) = \\min_x \\max_ \\mu L(x,\\mu) \\] 所以我们发现调整\\(\\mu\\)最大化\\(L\\)就等于\\(f(x)\\),所以 \\[ \\min_ x f(x) = \\min_x \\max_ \\mu L(x,\\mu) \\] 另一方面, \\[ \\max_\\mu \\min_x L(x, \\mu) = \\max_\\mu \\left[ \\min_x f(x) + \\min_x \\mu g(x) \\right] = \\max_\\mu \\min_x f(x) + \\max_\\mu \\min_x \\mu g(x) = \\min_x f(x) + \\max_\\mu \\min_x \\mu g(x) \\\\ 又\\because \\begin{aligned} \\mu_k \\geq 0, \\quad g_k(x) \\leq 0 \\quad &\\Rightarrow \\quad \\min_x \\mu g(x) = \\begin{cases} 0, & \\text{if } \\mu = 0 \\text{ or } g(x) = 0 \\\\ -\\infty, & \\text{if } \\mu > 0 \\text{ and } g(x) < 0 \\end{cases} \\end{aligned} \\] 所以,引入约束条件\\(\\mu g(X)=0\\),我们得到, \\[ \\max_\\mu \\min_x \\mu g(x) = 0 \\\\ \\therefore \\max_\\mu \\min_x L(x, \\mu) = \\min_x f(x) + \\max_\\mu \\min_x \\mu g(x) = \\min_x f(x) \\] 综上所述, \\[ \\begin{aligned}\\begin{rcases} L(x, \\lambda, \\mu) &= f(x) + \\sum_{k=1}^{q} \\mu_k g_k(x) \\\\ \\mu_k &\\geq 0 \\\\ g_k(x) &\\leq 0 \\\\ \\mu g(X)&=0 \\end{rcases} \\end{aligned} \\Rightarrow \\max_\\mu \\min_x L(x, \\mu)= \\min_x \\max_ \\mu L(x,\\mu)=\\min_ x f(x) \\] 引入等式约束的条件,我们得到了, \\[ \\begin{aligned}\\begin{rcases} \\frac{\\partial F}{\\partial x_i} = 0\\\\ \\frac{\\partial F}{\\partial \\lambda_j} = 0\\\\ \\lambda_j \\neq 0\\\\ \\mu_k g_k(\\mathbf{x}) = 0\\\\ g_k(\\mathbf{x}) \\leq 0\\\\ \\mu_k \\geq 0 \\end{rcases} \\end{aligned} \\Rightarrow \\max_\\mu \\min_x L(x, \\mu)= \\min_x \\max_ \\mu L(x,\\mu)=\\min_ x f(x) \\] 所以我们完成了我们需要证明的:在一些列约束条件下,拉格朗日函数等同于优化的目标函数。 补充KTT下的隐藏条件: \\[ \\frac{\\partial L(x, \\lambda, \\mu)}{\\partial x} \\Bigg|_{x = x^*} = 0 \\quad \\text{表明} \\quad f(x) \\text{在极值点} x^* \\text{处的梯度是含有} h_j(x^*) \\text{和} g_k(x^*) \\text{梯度的线性组合。} \\] 参考资料 拉格朗日乘数 - 维基百科,自由的百科全书 【整理】深入理解拉格朗日乘子法(Lagrange Multiplier) 和KKT条件 - mo_wang - 博客园 KKT条件介绍-CSDN博客","categories":[{"name":"数学","slug":"数学","permalink":"https://bg51717.github.io/categories/%E6%95%B0%E5%AD%A6/"},{"name":"微积分","slug":"数学/微积分","permalink":"https://bg51717.github.io/categories/%E6%95%B0%E5%AD%A6/%E5%BE%AE%E7%A7%AF%E5%88%86/"}],"tags":[{"name":"数学","slug":"数学","permalink":"https://bg51717.github.io/tags/%E6%95%B0%E5%AD%A6/"}]},{"title":"大模型量化~GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers","slug":"科研/论文阅读/大模型量化-GPTQ-Accurate-Post-Training-Quantization-for-Generative-Pre-trained-Transformers","date":"2024-10-24T08:12:19.000Z","updated":"2024-11-02T02:57:00.290Z","comments":true,"path":"/31769/","link":"","permalink":"https://bg51717.github.io/31769/","excerpt":"","text":"介绍 GPTQ算法的原理从数学公式出发,推导出权重的量化顺序和其余参数的调整值,然后根据这些值对block里的所有参数以列为单位进行量化,每次量化可以量化多个列,同时调整其余未量化的列的参数减小量化误差。 GPTQ算法是只针对权重的量化方式。在计算的时候,对应的内核会把需要计算的权重还原回原来的数据类型(比如int4->fp16)从而保证计算的稳定。 下图是PTQ大致流程图,GPTQ算法针对的主要是量化步骤中的参数调整部分:、 GPTQ量化算法最早来自1990年Yann LeCun的OBD算法,然后按照OBS,OBC(OBQ)的顺序进行演化,最终到GPTQ算法。当然,这里的演化算法不都是量化算法,由于量化和剪枝是很像的两个技术,量化调整参数为距离最近的整数,剪枝调整参数为0,所以量化算法和剪枝算法有很多共同点。 OBD:Optimal Brain Damage OBD是一种剪枝方法,考虑目标函数为\\(E\\),模型参数为\\(w_i\\),对目标函数进行泰勒展开,有 \\[ \\Delta E = \\sum_i g_i \\Delta w_i + \\frac{1}{2} \\sum_i h_{ii} \\Delta w_i^2 + \\frac{1}{2} \\sum_{i \\neq j} h_{ij} \\Delta w_i \\Delta w_j + O(\\Delta w^3) \\] 其中\\(g_i = \\frac{\\partial E}{\\partial w_i}\\)是一阶偏导,\\(h_{ij} = \\frac{\\partial^2 E}{\\partial w_i \\partial w_j}\\)是海森矩阵元素。 OBD假设: 目标函数为二阶,不考虑高阶项\\(O(\\Delta w^3)\\) 模型训练充分收敛,一阶偏导为0,\\(g_i=0,\\forall i\\) 每个参数对目标函数影响是独立的,海森矩阵中的交叉项为0,即\\(h_{ij} = 0, \\forall i, j, i \\neq j\\) 于是,上式可以优化为, \\[ \\Delta E = \\frac{1}{2} \\sum_i h_{ii} \\Delta w_i^2 \\] 因此,OBD算法只需要根据每个参数对目标的影响从小到大进行排序,然后进行剪枝即可。 OBS:Optimal Brain Surgeon OBS算法认为参数之间的独立性不成立,因此交叉项需要考虑,因此上式变为, \\[ \\Delta E = \\frac{1}{2} \\sum_i h_{ii} \\Delta w_i^2 + \\frac{1}{2} \\sum_{i \\neq j} h_{ij} \\Delta w_i \\Delta w_j \\] 向量,矩阵形式为, \\[ \\Delta E = \\frac{1}{2} \\Delta \\mathbf{w}^\\mathrm{T} \\mathbf{H} \\Delta \\mathbf{w} \\] 剪枝删除一个权重,那么\\(\\Delta \\mathbf{w}\\)的第 q 维固定为 \\(-w_q\\) ,但其他维度的值不固定,可以用于减少删除该权重带来的目标偏离。即约束条件为, \\[ \\mathbf{e}_q^\\mathrm{T} \\cdot \\Delta \\mathbf{w} + w_q = 0 \\] 其中,\\(\\mathbf{e}_q\\)是one-hot 向量,第q个位置是1,其余位置为0。 所以剪枝转化为最优化问题, \\[ \\min_{\\Delta \\mathbf{w}, q} \\frac{1}{2} \\Delta \\mathbf{w}^\\mathrm{T} \\mathbf{H} \\Delta \\mathbf{w} \\quad \\text{s.t.} \\quad \\mathbf{e}_q^\\mathrm{T} \\cdot \\Delta \\mathbf{w} + w_q = 0 \\] 构造拉格朗日函数, \\[ L = \\frac{1}{2} \\Delta \\mathbf{w}^\\mathrm{T} \\mathbf{H} \\Delta \\mathbf{w} + \\lambda (\\mathbf{e}_q^\\mathrm{T} \\cdot \\Delta \\mathbf{w} + w_q) \\] 求解,得 \\[ \\Delta \\mathbf{w} = -\\frac{w_q}{[\\mathbf{H}^{-1}]_{qq}} \\mathbf{H}^{-1} \\cdot \\mathbf{e}_q \\quad \\text{and} \\quad L = \\frac{1}{2} \\frac{w_q^2}{[\\mathbf{H}^{-1}]_{qq}} \\] 因此,我们可以根据海森矩阵求得下一个剪枝的参数和其他参数调整的值。 每次更新参数都需要重新求解海森矩阵,原论文中提到了数学上的优化方法,但是较为复杂且在后续算法没有使用,此处不予列出。 OBC 由于需要求解全参数的海森矩阵甚至逆矩阵,复杂度极高,在参数规模上去后求解是不现实的。 因此假设: 在一个矩阵中只有同一行的参数是相关的 同时,为了更好的求解,对于一个维度为\\((d_{row},d_{col})\\)参数矩阵设置一个优化的目标函数,\\(\\mathbf{W}_{i,\\cdot},\\hat{\\mathbf{W_{i,\\cdot}}}\\)分别是参数调整前后的权重向量 \\[ E = \\sum_{i=1}^{d_{\\text{row}}} \\| \\mathbf{W}_{i,\\cdot} \\mathbf{X} - \\hat{\\mathbf{W}}_{i,\\cdot} \\mathbf{X} \\|_2^2 \\] 求每行的海森矩阵, \\[ \\frac{\\partial E}{\\partial \\hat{\\mathbf{W}}_{i,\\cdot}^2} = \\mathbf{H}_i = 2 \\mathbf{X} \\mathbf{X}^\\mathrm{T}, \\quad \\forall i = 1, 2, \\dots, d_{\\text{row}} \\] 之后就可以求得矩阵每一行的剪枝顺序。 算法伪代码: 首先遍历每一行,然后重复k次,每次找到对目标函数影响最小的参数p,对p进行剪枝同时更新其他参数,删除海森矩阵的p行p列(不包括\\(H_{pp}\\)),再求逆。(这里罗列的好像是降低复杂度后的数学等效。) OBC论文也罗列了一些别的性能上的优化,在后续算法没有使用,此处不予列出。 此处数学等效的方法是高斯消元,海森矩阵移除k行k列但包括\\(H_{kk}\\),因为左乘矩阵代表对行向量线性组合对,右乘矩阵代表对列向量线性组合,因此通过高斯消元即可实现对0的操作。证明可以从GPTQ的Cholesky证明中基本无条件的迁移。 OBQ OBQ (和OBC是同一篇文章)指出,剪枝是一种特殊的量化(即剪枝的参数等价于量化到 0 点),因此只需要修改一下 OBC 的约束条件即可: \\[ \\mathbf{e}_p^\\mathrm{T} \\cdot \\Delta \\mathbf{w} + w_p = \\text{quant}(w_p) \\] 相应的,权重的调整更新为, \\[ \\Delta \\mathbf{w} = -\\frac{w_p - \\text{quant}(w_p)}{[\\mathbf{H}^{-1}]_{pp}} \\mathbf{H}^{-1} \\cdot \\mathbf{e}_p \\quad \\text{and} \\quad L = \\frac{1}{2} \\frac{(w_p - \\text{quant}(w_p))^2}{[\\mathbf{H}^{-1}]_{pp}} \\] OBQ的算法伪代码,同OBC的很相似: GPTQ OBQ的算法复杂度还是太高了,GPTQ继续进行优化。 GPTQ是逐层量化的,同时做出以下假设: 参数调整前量化网络(量化函数)是确定的,比如量化的缩放因子和零点等 权重参数可以在量化网格内进行调整 按索引顺序量化 将每一行的量化权重选择方式从贪心策略改成按索引顺序选择。 修改索引顺序的好处有两个,首先多个行的量化顺序是一样的,因此可以多行并行处理;第二个是\\(\\mathbf{H} = 2 \\mathbf{X} \\mathbf{X}^\\mathrm{T}\\)说明,海森矩阵只与输入相关,因此不同行可以共用一个海森矩阵。 行里第p个权重调整公式可以为改为, \\[ \\Delta \\mathbf{w} = -\\frac{w_p - \\text{quant}(w_p)}{[\\mathbf{H}_{p:,p:}^{-1}]_{0,0}} \\left( [\\mathbf{H}_{p:,p:}^{-1}]_{:,0} \\right)^\\top \\] 加入多行并行后变为, \\[ \\Delta W_{:,p:} = -\\frac{W_{:,p:} - \\text{quant}(W_{:,p:})}{[\\mathbf{H}_{p:,p:}^{-1}]_{0,0}} \\left( [\\mathbf{H}_{p:,p:}^{-1}]_{:,0} \\right)^\\top =-\\frac{\\left( [\\mathbf{H}_{p:,p:}^{-1}]_{:,0} \\right)^\\top}{[\\mathbf{H}_{p:,p:}^{-1}]_{0,0}} ( W_{:,p:} - \\text{quant}(W_{:,p:})) \\] 逆矩阵的更新公式变为, \\[ [H_{p:,p:}]^{-1} = \\left( [H_{p-1:,p-1:}]^{-1} - \\frac{1}{[H_{p-1:,p-1:}]^{-1}_{0,0}} [H_{p-1:,p-1:}]^{-1}_{:,0} [H_{p-1:,p-1:}]^{-1}_{0,:}\\right)_{1:,1:} \\] Cholesky 分解 关于Cholesky分解的数学介绍可以看:三十分钟理解:矩阵Cholesky分解,及其在求解线性方程组、矩阵逆的应用_cholesky分解法求解线性方程组-CSDN博客 实验过程中发现:在大规模参数矩阵上重复使用海森矩阵逆矩阵的更新公式会产生非正定的海森矩阵逆矩阵,原因可能是数值误差的积累。 作者对初始的\\(H^{-1}\\)进行Cholesky 分解,得到一个上三角矩阵\\(T\\),它的每一行刚好就等于使用更新公式得到的逆矩阵序列的第一行乘以一个常数,即, \\[ C_p T_{p,p:} = \\left[ H_{p:,p:} \\right]^{-1}_{0,:} \\] 图示, 所以,权重的调整更新为, \\[ \\Delta W_{:,p:} = - \\frac{W_{:,p} - \\text{quant}(W_{:,p})}{C_p T_{pp}} C_p T_{p,p:} = - \\frac{W_{:,p} - \\text{quant}(W_{:,p})}{T_{pp}} T_{p,p:} \\] 发现很多资料都没有系统的罗列大致的推导过程,甚至论文基本也是一笔带过,因此考虑在此给出一些浅薄的证明(笔者不是数学系的。 首先介绍一下教程中的Cholesky分解:给定一个\\(n\\times n\\)的实数对称正定矩阵\\(A\\),存在一个对角元全为正数的下三角矩阵,是的\\(A=LL^\\top\\)成立。 \\[ \\begin{align*} \\mathbf{A} &= \\begin{bmatrix} a_{11} & \\mathbf{A}_{21}^T \\\\ \\mathbf{A}_{21} & \\mathbf{A}_{22} \\end{bmatrix}, \\quad \\mathbf{L} = \\begin{bmatrix} l_{11} & 0 \\\\ L_{21} & L_{22} \\end{bmatrix}, \\quad \\mathbf{L}^T = \\begin{bmatrix} l_{11} & L_{21}^T \\\\ 0 & L_{22}^T \\end{bmatrix} \\end{align*} \\] 其中 $a_{11} $ 和 $ l_{11} $是一个标量, \\(\\mathbf{A}_{21}\\) 和 \\(L_{21}\\) 是一个列向量,${22} $是一个 ( n-1 ) 阶的方阵,而 $ L{22} $ 是一个 ( n-1 ) 阶的下三角矩阵。我们有, \\[ \\begin{align*} \\begin{bmatrix} a_{11} & \\mathbf{A}_{21}^T \\\\ \\mathbf{A}_{21} & \\mathbf{A}_{22} \\end{bmatrix} &= \\begin{bmatrix} l_{11} & 0 \\\\ L_{21} & L_{22} \\end{bmatrix} \\begin{bmatrix} l_{11} & L_{21}^T \\\\ 0 & L_{22}^T \\end{bmatrix} &= \\begin{bmatrix} l_{11}^2 & l_{11} L_{21}^T \\\\ l_{11}L_{21} & L_{21} L_{21}^T + L_{22} L_{22}^T \\end{bmatrix} \\end{align*} \\] 我们易得, \\[ \\begin{align*} l_{11} &= \\sqrt{a_{11}} \\\\[10pt] L_{21} &= \\frac{1}{l_{11}} \\mathbf{A}_{21} \\\\[10pt] L_{22} L_{22}^T &= \\mathbf{A}_{22} - L_{21} L_{21}^T \\end{align*} \\] 其中\\(l_{11}\\)和\\(L_{21}\\)我们直接得到,对于\\(L_{22}\\),我们发现又是一个Cholesky分解的过程,递归求解即可。 我们根据公式可以发现, \\[ (\\mathbf{L}^\\top)_{1,:} =\\begin{bmatrix} l_{11} & L^{\\top}_{21} \\end{bmatrix} = \\frac{1}{\\sqrt{a}} \\begin{bmatrix} a_{11} & \\mathbf{A}^{\\top}_{21} \\end{bmatrix} = \\frac{1}{\\sqrt{a}}\\mathbf{A}_{1,:} \\] 所以我们证明了两个矩阵第一列的比例关系,根据分解的递推性和矩阵的对称性,我们就完整证明了这个结论。 海森逆矩阵更新公式证明 首先我们需要证明在海森矩阵删除p行p列参数(不包括\\(H_{pp}\\))的时候,在海森矩阵上会发生类似的操作。 首先,我们需要知道\\((H^{-1})^\\top=(H^T)^{-1}=H^{-1}\\),即海森矩阵的逆矩阵是对称矩阵。 \\[ \\begin{align*} \\mathbf{H}= \\begin{bmatrix} a & \\mathbf{z}^\\top \\\\ \\mathbf{z} & \\mathbf{A} \\end{bmatrix}, \\quad 构造矩阵\\mathbf{B}=\\begin{bmatrix} \\frac{1}{a} & \\mathbf{z}^\\top \\\\ \\mathbf{z} & \\mathbf{A}^{-1} \\end{bmatrix} \\end{align*} \\] 其中,\\(\\mathbf{H}\\)和\\(\\mathbf{B}\\)是n维的对称正定方阵,a\\(是标量,\\)\\(z\\)为n-1维的零向量,那么 \\[ \\mathbf{H} \\times \\mathbf{B}= \\begin{bmatrix} a & \\mathbf{z}^\\top \\\\ \\mathbf{z} & \\mathbf{A} \\end{bmatrix} \\times \\begin{bmatrix} \\frac{1}{a} & \\mathbf{z}^\\top \\\\ \\mathbf{z} & \\mathbf{A}^{-1} \\end{bmatrix} =\\begin{bmatrix} 1 & a\\mathbf{z}^\\top+\\mathbf{z}^\\top\\mathbf{A}^{-1} \\\\ \\frac{1}{a}\\mathbf{z}+\\mathbf{A}\\mathbf{z} & \\mathbf{z}\\mathbf{z}^\\top+\\mathbf{A}\\mathbf{A}^{-1} \\end{bmatrix} =\\begin{bmatrix} 1 & \\mathbf{z}^\\top \\\\ \\mathbf{z} & I_{n-1\\times n-1} \\end{bmatrix} =I_{n\\times n} \\] 说明,说明\\(\\mathbf{B}\\)就是\\(\\mathbf{H}\\)的逆矩阵,因此,得证。我们就可以直接对海森矩阵的逆矩阵进行删除参数即可。 接下来,我们需要证明删除行参数和列参数的公式。为了方便,假设我们有个n维的对称正定方阵\\(\\mathbf{A}\\),令 \\[ \\mathbf{B}=\\mathbf{A}-\\frac{1}{\\mathbf{A}_{11}}\\mathbf{A}_{:,1}\\mathbf{A}_{1,:} \\] 那么, \\[ \\mathbf{B}_{i,1}=\\mathbf{A}_{i,1}-\\frac{1}{\\mathbf{A}_{11}}\\mathbf{A}_{i1}\\mathbf{A}_{11}=0,i \\neq 1 \\] 所以\\(\\mathbf{B}\\)的第一列除了第一个元素全部为0,根据对称性,第一行也全部为0,即实现了对第一行第一列元素的删除。 尽管这里推导的时候是针对第一行或者第一列的,但是很容易外推到任意位置。 到此,我们证明了海森逆矩阵更新公式的正确性,分为两步:首先证明删除参数后海森逆矩阵的格式,然后根据格式证明了更新公式的正确性。 Lazy Batch-Updates 在量化某个参数矩阵的情况下,每次量化一个参数,其他所有未量化的参数都要按公式全都要更新一遍。如果每行的量化并行计算,那么每次更新过程就需要 read + write 一次参数矩阵。如果参数矩阵的维度为\\(k \\times k\\),那么量化这个参数矩阵就需要读写 k 次参数,总共的 IO 量为\\(k^3\\)个元素。当 k 比较大时(>= 4096),需要读写的元素就非常多了,运行时间大都被 IO 占据。 \\[ \\Delta W_{:,p:} = -\\frac{W_{:,p:} - \\text{quant}(W_{:,p:})}{[\\mathbf{H}_{p:,p:}^{-1}]_{0,0}} \\left( [\\mathbf{H}_{p:,p:}^{-1}]_{:,0} \\right)^\\top =-\\frac{\\left( [\\mathbf{H}_{p:,p:}^{-1}]_{:,0} \\right)^\\top}{[\\mathbf{H}_{p:,p:}^{-1}]_{0,0}} ( W_{:,p:} - \\text{quant}(W_{:,p:})) \\] 将参数矩阵按每若干列划分为一个个 group,量化某一列时,group 内的参数立即更新,而 group 后面的列只记录更新量,延迟更新。当一个 group 的参数全部量化完成,再统一对后面的所有参数做一次更新。这就是 Lazy Batch-Updates。根据更新公式我们可以发现,这个更新可以是可以合并同类项的,把一个group的第一项提出来求和,可以一次读写后面的所有group。 对应的算法伪代码为 实验 1.小模型测试 首先在ResNet上面进行了测试。选择AdaQuant、基于贪心策略的 OBQ在精度上保持持平。相比量化前的模型来说,性能也没有下降太多。 然后在小型语言模型上(这是OBQ能使用的最大的模型之一)进行了测试。在4bit得分比之前的方法稍微差一点,在3bit要低的多一点。 但是量化需要时间从1h降到1min。 2.测试了量化时间 提供了一个参考的标准: \"For reference, the straight-through based method ZeroQuant-LKD (Yao et al., 2022) reports a 3 hour runtime (on the same hardware) for a 1.3B model, which would linearly extrapolate to several hundred hours (a few weeks) for 175B models.\" 3.测试了量化模型在文本生成任务的表现 和不进行优化的直接量化(RTN)进行了对比。 4.测试了OPT-175B文本生成的效率 开发了相关kernel(推理时权重会反量化),量化只针对权重,激活不量化。 5.零样本任务评测 测试了和基线相比,在零样本任务上的准确率。 该方法很容易和别的量化网络相结合。量化网络指的是量化的操作,是权重调整后的具体量化步骤,需要确定比如缩放因子,零点,量化的粒度等。 代码解析 论文对应的代码仓库为:IST-DASLab/gptq: Code for the ICLR 2023 paper \"GPTQ: Accurate Post-training Quantization of Generative Pretrained Transformers\". 基于改算法开发的工具包:AutoGPTQ/AutoGPTQ: An easy-to-use LLMs quantization package with user-friendly apis, based on GPTQ algorithm. 参考资料 QLoRA、GPTQ:模型量化概述 - 知乎 LLM 推理加速技术 —— GPTQ 量化技术演进 - 知乎 [2210.17323] GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers AutoGPTQ/AutoGPTQ: An easy-to-use LLMs quantization package with user-friendly apis, based on GPTQ algorithm. IST-DASLab/gptq: Code for the ICLR 2023 paper \"GPTQ: Accurate Post-training Quantization of Generative Pretrained Transformers\". Optimal Brain Damage 三十分钟理解:矩阵Cholesky分解,及其在求解线性方程组、矩阵逆的应用_cholesky分解法求解线性方程组-CSDN博客","categories":[{"name":"科研","slug":"科研","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/"},{"name":"论文阅读","slug":"科研/论文阅读","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/%E8%AE%BA%E6%96%87%E9%98%85%E8%AF%BB/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"量化","slug":"量化","permalink":"https://bg51717.github.io/tags/%E9%87%8F%E5%8C%96/"}]},{"title":"llm.int8","slug":"科研/论文阅读/llm-int8","date":"2024-10-24T07:39:30.000Z","updated":"2024-11-02T02:57:00.281Z","comments":true,"path":"/21264/","link":"","permalink":"https://bg51717.github.io/21264/","excerpt":"","text":"todo:本文还在施工中....... 介绍 llm.int8是第一批针对大模型进行量化的算法,并且其算法也被集成在 bitsandbytes库中,该库也已经被 huggingface集成到代码库当中作为最基本的量化算法之一。 论文地址为:[2208.07339] LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale 对应的源码为:bitsandbytes-foundation/bitsandbytes: Accessible large language models via k-bit quantization for PyTorch. 摘要 LLM.in8()量化针对的是transformer的FNN模块和Attn模块的矩阵乘法。 该量化方法把矩阵中的异常值抽出来保持16位精度,对于其余值量化为8位精度,计算的时候恢复16精度。量化常数的选择是在向量维度进行的(矩阵最后维度)。 介绍 FFN和Attn的矩阵参数在模型总参数占比较高,且矩阵乘法占据了较多的计算资源。以往的量化方法降低了模型的表现并且需要量化后训练。同时没有对超过350M参数进行量化研究。这篇论文第一次提出针对百万级参数量的模型的量化方法且模型性能下降有限。应该也是第一篇把大模型量化到8比特的论文。 量化前置知识 论文这里简单说明了非对称量化理论上能提供更高的精度,但是由于实际限制,用的最多的还是对称量化。 对称量化 对于fp16格式的输入\\(\\mathbf{X}_{f16} \\in \\mathbb{R}^{s \\times h}\\),8比特的量化为: \\[ \\mathbf{X}_{i8} = \\left\\lfloor \\frac{127 \\cdot \\mathbf{X}_{f16}}{\\max_{ij} \\left( |\\mathbf{X}_{f16_{ij}}| \\right)} \\right\\rceil = \\left\\lfloor \\frac{127}{\\|\\mathbf{X}_{f16}\\|_{\\infty}} \\mathbf{X}_{f16} \\right\\rceil = \\left\\lfloor s_{x_{f16}} \\mathbf{X}_{f16} \\right\\rceil \\] 其中\\(\\left\\lfloor \\right\\rceil\\),代表四舍五入取整,即距离最近的整数。 原理介绍 这个量化的方法原理很简单,该方案先做了一个矩阵分解,对绝大部分权重和激活用8bit量化(vector-wise)。对离群特征的几个维度保留16bit,对其做高精度的矩阵乘法。 LLM.int8() 通过三个步骤完成矩阵乘法计算: 从输入的隐含状态中,按列提取异常维度 (离群特征,即大于某个阈值的值)。 对离群特征进行 FP16 矩阵运算,对非离群特征进行量化,做 INT8 矩阵运算; 反量化非离群值的矩阵乘结果,并与离群值矩阵乘结果相加,获得最终的 FP16 结果。 原理分析 这个方法能成功的一个重要原因是作者发现同一个序列的不同token异常维度集中在部分维度且异常维度数目较少。 首先,论文定义异常值的标准为: 值需要大于等于6 影响25%的层 至少出现一个序列在6%的token中 作者对此进行了解释: 实验发现把大于等于6的值设置为异常值,困惑度退化就会停止 异常值特征是在大模型中系统出现的,要么出现在大多数层中,要么不出现;在小模型中概率出现。设置该阈值保证125M的最小模型中异常值只有一个(小模型中第二个出现最多的异常值仅仅出现2%的层) 使用和第二点相同的过程来选择异常值在序列维度的最小出现比例 作者进行了实验,将满足条件的维度设置为0,比较最高可能性输出类别所分配的Softmax概率值。对比是随机选择相同数目的维度进行对比。 实验 todo:量化效果 todo:加速效果 由于多余的反量化操作,在小模型上推理速度有所下降。 bitsandbytes介绍 todo:介绍库的大致使用方法和部分疑点。 bitsandbytes库在量化模型的时候,不需要数据首先直接把权重量化为8bit。在推理的时候,根据输入确定异常维度。输入的异常维度保持fp16,别的维度量化为int8。而量化后的权重,会把异常维度进行反量化为fp16(可能会有轻微损失),别的保持int8。之后的计算就和原理图保持一致。 参考资料 [2208.07339] LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale 大模型量化技术原理-LLM.int8()、GPTQ-CSDN博客 bitsandbytes-foundation/bitsandbytes: Accessible large language models via k-bit quantization for PyTorch.","categories":[{"name":"科研","slug":"科研","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/"},{"name":"论文阅读","slug":"科研/论文阅读","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/%E8%AE%BA%E6%96%87%E9%98%85%E8%AF%BB/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"量化","slug":"量化","permalink":"https://bg51717.github.io/tags/%E9%87%8F%E5%8C%96/"}]},{"title":"pytorch分布式-ddp","slug":"模板/pytorch分布式-ddp","date":"2024-10-09T05:30:13.000Z","updated":"2024-11-02T02:55:49.879Z","comments":true,"path":"/45014/","link":"","permalink":"https://bg51717.github.io/45014/","excerpt":"","text":"介绍 这篇博客主要是关于pytorch分布式ddp(DistributedDataParallel)的介绍和大概的食用(这不是错别字)教程。 数据并行DistributedDataParallel指的是在数据集层面进行多进程的切分,对于模型参数和训练状态等其他部分切分。 首先会介绍一下通信。在pytorch分布式ddp中,各个进程的代码是单独运行的。彼此之间在没有显式通信的时候,是不知道对方的的信息的。因此分布式的重点,所以了解通信的情况,也就了解了分布式的原理和使用的方法。 主要通信的方式有: 环境变量 同步 tensor操作 然后会介绍一下在数据并行下数据如何进行切分。 最后介绍整体pytorch分布式大概的流程和使用方法。 通信 环境变量 常用的环境变量有: WORLD_SIZE 全局进程数 RANK 当前进程全局标识符 LOCAL_RANK 在单个节点中的进程标识符 MASTER_ADDR 主节点IP地址 MASTER_PORT 主节点端口 常用的为前三个,还有一些使用更加少的暂时没有罗列。 环境变量的获取可以: import os os.environ[\"path\"] os.environ.get('KEY_THAT_MIGHT_EXIST') os.getenv('KEY_THAT_MIGHT_EXIST', default_value) # 推荐 同步 引入pytorch分布式包 import torch.distributed as dist 同步所有进程进度 dist.barrier() 在tensor通信的时候,也会起到同步进程的作用。很容易理解,不同步的话tensor的值都没有求得。 tensor通信 广播broadcast,收集gather,分发scatter,全收集all-gather,规约reduce,全规约all-reduce,全对称all-to-all,批量广播broadcast_object_list 数据 在ddp中,只考虑的数据的剪切。那么对于某个进程,只需要计算部分数据即可。某个进程根据LOCAL_RANK获取自己所需的数据的方法有两种: 数据集定义中加入offset,根据offset获取自己只需要的数据,那么进程只能看到自己的数据,比如 for i, segment in enumerate(open(file)): if i % n_gpus != offset: continue 通过设置DataLoader中的sampler控制数据集采样实现数据切分,比如: from torch.utils.data import DataLoader, DistributedSampler sampler = DistributedSampler(dataset, num_replicas=world_size, rank=rank) dataloader = DataLoader(dataset, batch_size=64, sampler=sampler) 使用方法 在通信前,需要进行初始化操作(如果init_process_group不指定部分参数,也会自动从环境变量中获取): # 初始化分布式进程 def setup(): rank = int(os.environ[\"RANK\"]) world_size = int(os.environ[\"WORLD_SIZE\"]) dist.init_process_group(\"nccl\", rank=rank, world_size=world_size) 设置使用的GPU(可以灵活设置,比如每若干个进程共享GPU): rank = int(os.environ[\"RANK\"]) torch.cuda.set_device(rank) # 设置默认GPU device = torch.device(f\"cuda:{rank}\") # 显式指定设备 使用的GPU还会收到环境变量CUDA_VISIBLE_DEVICES的限制 设置默认GPU可以让部分CUDA操作默认在该设备执行 然后包装模型,隐式的进行 tensor的同步和通信(在模型之外计算某些量(如精度、损失值等),可能需要同步): from torch.nn.parallel import DistributedDataParallel as DDP model = SimpleCNN().to(device) model = DDP(model, device_ids=[rank]) 当然,这里也可以切换成别的过程,比如如果不是模型的训练和推理,也可以进行tensor别的计算方法,但是需要手动的进行通信等。 对于一些多个进程只需要完成一次的操作,比如保存模型或者日志记录等,只需要一个进程一般是主进程完成即可: if dist.get_rank() == 0: torch.save(model.state_dict(), \"model_checkpoint.pth\") 代码执行完需要进程组的销毁: def cleanup(): dist.destroy_process_group() 代码执行 如果执行代码直接使用python,那么需要使用pytorch的包启动多进程: import torch.multiprocessing as mp mp.spawn(train, nprocs=world_size, join=True) 如果直接使用 torchrun命令执行代码,则不需要使用 torch.multiprocessing,但需要在命令里添加部分参数,等于调用 torch.multiprocessing的任务交给 torchrun完成: torchrun --nproc_per_node=4 your_script.py 参考资料 Distributed and Parallel Training Tutorials — PyTorch Tutorials 2.4.0+cu121 documentation “最全“PyTorch分布式训练教程来了!_pytorch 训练-CSDN博客 Pytorch中基于NCCL多GPU训练_pytorch nccl-CSDN博客","categories":[{"name":"模板","slug":"模板","permalink":"https://bg51717.github.io/categories/%E6%A8%A1%E6%9D%BF/"}],"tags":[{"name":"模板","slug":"模板","permalink":"https://bg51717.github.io/tags/%E6%A8%A1%E6%9D%BF/"},{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"PyTorch","slug":"PyTorch","permalink":"https://bg51717.github.io/tags/PyTorch/"},{"name":"分布式","slug":"分布式","permalink":"https://bg51717.github.io/tags/%E5%88%86%E5%B8%83%E5%BC%8F/"},{"name":"ddq","slug":"ddq","permalink":"https://bg51717.github.io/tags/ddq/"},{"name":"数据并行","slug":"数据并行","permalink":"https://bg51717.github.io/tags/%E6%95%B0%E6%8D%AE%E5%B9%B6%E8%A1%8C/"},{"name":"Data Parallel","slug":"Data-Parallel","permalink":"https://bg51717.github.io/tags/Data-Parallel/"}]},{"title":"hexo博客2:双主题","slug":"SmallProjects/博客搭建/hexo博客2-双主题","date":"2024-10-03T10:55:45.000Z","updated":"2024-11-02T05:32:24.133Z","comments":true,"path":"/31680/","link":"","permalink":"https://bg51717.github.io/31680/","excerpt":"","text":"介绍 后来发现单纯的wiki风格博客可能确实有些单调了(绝对不是因为我想弄二次元风格的),因此在考虑以后,决定搭建一个双主题的博客,外层是个华丽一点的主题matery,内层是个wiki风格主题Wikitten。 同时可以通过外层的wiki标志点击进入内层的wiki主题。 双主题安装 参考文章:Hexo 同时使用两种主题(博客与 wiki 页面实现统一管理) | 别院牧志 (masantu.com) 把原来wiki主题的 _config.yml复制并重命名为 _config_wiki.yml(wiki主题下的站点文件 安装 hexo-theme-matery主题 cd your-hexo-directory/themes git clone https://github.com/blinkfox/hexo-theme-matery 按照 matery教程配置新的 _config_wiki.yml文件 修改 _config_wiki.yml文件: # URL ## Set your site url here. For example, if you use GitHub Page, set url as 'https://username.github.io/project' url: https://bg51717.github.io/wiki/ root: /wiki/ # permalink: :year/:month/:day/:title/ # permalink_defaults: permalink: /:abbrlink/ abbrlink: alg: crc16 #算法: crc16(default) and crc32 rep: dec #进制: dec(default) and hex permalink_defaults: # Directory source_dir: source public_dir: public/wiki/ 代码块行高问题 在使用 prismjs渲染代码块的时候,可能会遇到图示问题,行高不匹配,其实主要是位置没有对准: 可以参考:hexo 7.3 代码块显示问题 · Issue #928 · blinkfox/hexo-theme-matery 解决办法是把 themes\\hexo-theme-matery\\source\\libs\\prism\\prism.min.css文件里的: [class*=language-].line-numbers>code{position:relative;white-space:inherit} 修改为: [class*=language-].line-numbers>code{position:sticky;white-space:inherit} wiki图片链接问题 由于渲染的站点 url和 root里都有 wiki,因此会导致渲染出来的图片出现两次 /wiki,因此需要代码进行处理,核心函数如下所示: def post_html_file(file_path): \"\"\"处理html文件,只修改图片链接中的重复/wiki/\"\"\" # 如果不是html文件, 返回 if not file_path.endswith(\".html\"): return # 读取文件内容 with open(file_path, \"r\", encoding=\"utf-8\") as file: content = file.read() # 定义正则表达式来匹配图片链接,处理 <img> 标签中的 src 属性 img_pattern = r'<img[^>]+src=\"([^\"]+)\"' # 查找所有匹配的图片链接 matches = re.findall(img_pattern, content) # 遍历所有匹配的图片链接,替换掉重复的 /wiki/ modified_content = content for match in matches: # 如果链接中有重复的 /wiki/,替换为单一的 /wiki/ if \"/wiki/wiki/\" in match: corrected_url = match.replace(\"/wiki/wiki/\", \"/wiki/\") # 替换内容中的旧链接为新的链接 modified_content = modified_content.replace(match, corrected_url) # 将修改后的内容写回文件 with open(file_path, \"w\", encoding=\"utf-8\") as file: file.write(modified_content) 图库APi设置 对于外层主题,需要自己设置相关的图片,笔者从栗次元API-举个栗子 图库API爬取部分图片作为图片。 也有别的食用方法可以参考该网站具体的教程。 搜索链接跳转错误 由于把每篇博客链接到了一个数字,从而起到了缩短博客网址长度和增加搜索引擎爬取的概率,但是可能会导致原有的搜索跳转到随机IP。 原因是:网址返回的是纯数字相对链接,比如 /7369,完整的链接为 https://bg51717.github.io/7369/,但是浏览器可能会把链接解析为ip地址,变成 0.0.28.201,等同于256进制下的 7369,从而导致跳转错误。 因此需要调整搜索代码,每次返回完整链接,具体如下: if (data_url.indexOf('http') !== 0) { // 生成绝对路径,确保基于网站根目录 data_url = window.location.host +'/'+root+'/'+ data_url; data_url = data_url.replace(/\\/+/g, '/'); // 去掉连续的斜杠 data_url = data_url.replace(/^\\//, ''); // 去掉开头的斜杠 data_url = window.location.protocol + '//' + data_url; } 渲染页面 渲染部分的参考主要来自:Hexo 同时使用两种主题(博客与 wiki 页面实现统一管理) | 别院牧志 渲染主页面,生成到/public/ hexo g 删除 db.json 及旧的 public/wiki hexo --config _config_wiki.yml clean 渲染wiki页面 hexo --config _config_wiki.yml g 删除db.json rm db.json 如果每次执行上述步骤都会稍显复杂,因此考虑整理脚本文件 run.py,执行渲染时 python run.py即可: import os import platform import subprocess def run_command(command): \"\"\"实时执行命令并将输出打印到控制台\"\"\" try: # 使用 Popen 来执行命令并实时读取输出 process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding='utf-8') # 实时读取 stdout 和 stderr for stdout_line in iter(process.stdout.readline, \"\"): print(stdout_line, end='') # 实时输出 stdout process.stdout.close() return_code = process.wait() # 等待命令执行结束 # 输出 stderr(如果有) if return_code != 0: for stderr_line in iter(process.stderr.readline, \"\"): print(stderr_line, end='') # 实时输出 stderr process.stderr.close() except subprocess.CalledProcessError as e: print(f\"命令 '{command}' 执行失败,错误信息: {e.stderr}\") def clean_generate_hexo(): \"\"\"执行 hexo clean 和 hexo g 命令,以及相关的配置命令\"\"\" os_type = platform.system() # 根据操作系统选择 rm 或 del remove_command = \"rm db.json\" if os_type != \"Windows\" else \"del db.json\" # 定义需要执行的命令列表 commands = [ \"python -m process.run_before\", \"hexo clean\", \"hexo g\", \"hexo --config _config_wiki.yml clean\", \"hexo --config _config_wiki.yml g\", remove_command, \"python -m process.run_post\", ] # 逐一执行命令 for command in commands: print(f\"\\n正在执行命令: {command}\") run_command(command) if __name__ == \"__main__\": clean_generate_hexo() 参考资料 blinkfox/hexo-theme-matery: A beautiful hexo blog theme with material design and responsive design.一个基于材料设计和响应式设计而成的全面、美观的Hexo主题。国内访问:http://blinkfox.com (github.com) zthxxx/hexo-theme-Wikitten: A theme of Hexo for personal wiki which seems like Wikitten style. (github.com) Hexo 同时使用两种主题(博客与 wiki 页面实现统一管理) | 别院牧志 (masantu.com)","categories":[{"name":"SmallProjects","slug":"SmallProjects","permalink":"https://bg51717.github.io/categories/SmallProjects/"},{"name":"博客搭建","slug":"SmallProjects/博客搭建","permalink":"https://bg51717.github.io/categories/SmallProjects/%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA/"}],"tags":[{"name":"博客","slug":"博客","permalink":"https://bg51717.github.io/tags/%E5%8D%9A%E5%AE%A2/"},{"name":"Hexo","slug":"Hexo","permalink":"https://bg51717.github.io/tags/Hexo/"},{"name":"Wiki","slug":"Wiki","permalink":"https://bg51717.github.io/tags/Wiki/"}]},{"title":"使用dotbot快速同步Linux配置","slug":"工具/使用dotbot快速同步Linux配置","date":"2024-10-03T07:37:15.000Z","updated":"2024-11-02T02:55:49.875Z","comments":true,"path":"/13162/","link":"","permalink":"https://bg51717.github.io/13162/","excerpt":"","text":"介绍 dotfiles指的是 .开头的隐藏文件夹,一般是用户的配置或者软件信息。使用服务器或者Linux的时候,安装一些软件配置自己的使用环境是十分常见的场景。一个优秀的配置和各种软件不仅可以大幅提升工作效率,还可以美化工作环境,加强工作的动力。但是很多时候一个完整的配置是十分复杂且繁琐的,也难以去记忆每次配置时的信息。 因此很多人尝试收集配置文件,创建软连接,然后整理安装脚本,上传到github。实现难度较低。 这篇博客推荐使用基于Git的dotbot来管理dotfiles。自己编写管理脚本可能会导致脚本经常需要修改来使用不同的场合。框架在设计的时候会考虑到大部分场景,因此需要的修改和可能导致的错误较少。 Git子模块 在介绍 dotbot之前需要了解一下 Git子模块(submodule)的观念。当你的仓库依赖于别的仓库的时候,你可以添加一个链接指向被依赖的仓库的某个版本,而不需要去额外复制这些文件。 基本操作 添加子模块: git submodule add <仓库地址> <子模块路径> <仓库地址>是仓库的网络地址,<子模块路径>指的是相当于这个仓库的路径 示例:git submodule add https://github.com/example/library.git libs/library 初始化子模块,当你克隆具有子模块的仓库的时候,需要手动初始化并更新子模块: git submodule init git submodule update 也可以一次更新 git clone --recurse-submodules <仓库地址> 子模块管理,子模块可以像正常的仓库一样进行管理: cd <子模块路径> git pull git add . git commit -m \"Update submodule\" git push 如果需要对子模块更新且不是子模块的作者的话,建议fork仓库作为子模块,fork仓库的管理此处不再阐述。 删除子模块,删除子模块需要调整 .gitmodules和 .git/config配置: git submodule deinit <子模块路径> git rm --cached <子模块路径> rm -rf <子模块路径> 修改子模块url: git submodule set-url <子模块路径> <新的URL> 修改子模块路径: git mv <旧路径> <新路径> git submodule sync 安装 首先你需要一个 dotfiles文件夹(名字可以自定义),里面是你所有的配置文件。 之后进入这个文件夹,添加 dotbot作为子模块: # Initialize Repository git init git submodule add https://github.com/anishathalye/dotbot cp dotbot/tools/git-submodule/install . touch install.config.yaml 也推荐把需要安装的软件作为子模块使用 配置 通过修改 install.config.yaml可以调整安装命令 ./install的工作。 默认的配置为: - defaults: link: relink: true - clean: ['~'] - link: ~/.bashrc: bashrc ~/.zshrc: zshrc ~/.vimrc: vimrc - shell: - [git submodule update --init --recursive, Installing submodules] 几个类别的顺序会影响命令执行时的顺序 主要类别有: defaults:会对所有操作进行的设置 clean:哪些links会被检查是否dead,如果是dead links会被删除 link:创建软链接的源目录和目标目录 shell:希望运行的指令 之后就可以简单的使用git来管理dotfile,并且使用 ./install安装即可。 参考资料 anishathalye/dotbot: A tool that bootstraps your dotfiles ⚡️ (github.com) 管理您的点文件 (anishathalye.com) GitHub does dotfiles - dotfiles.github.io 杰哥","categories":[{"name":"工具","slug":"工具","permalink":"https://bg51717.github.io/categories/%E5%B7%A5%E5%85%B7/"}],"tags":[{"name":"dotfiles","slug":"dotfiles","permalink":"https://bg51717.github.io/tags/dotfiles/"},{"name":"dotbot","slug":"dotbot","permalink":"https://bg51717.github.io/tags/dotbot/"},{"name":"Linux","slug":"Linux","permalink":"https://bg51717.github.io/tags/Linux/"}]},{"title":"zsh+powerlevel10K优化终端使用体验","slug":"工具/zsh-powerlevel10K优化终端使用体验","date":"2024-09-29T13:09:00.000Z","updated":"2024-11-02T02:55:49.874Z","comments":true,"path":"/13107/","link":"","permalink":"https://bg51717.github.io/13107/","excerpt":"","text":"介绍 ZSH(Z shell)类似Bash,是被广泛用于类Unix系统的命令行解释器。在具备Bash的基本功能的同时,还扩展了很多功能,同时对插件的支持和高度定制化使其成为了很多Linux用户的最佳选择。经常使用的功能有:自动补全,历史命令,语法高亮等。 powerlevel10K是ZSH的主题之一,但是扩展了一些额外的功能,比如更多信息的显示,运行时间和当前时间的查看等。 通过灵活使用这两个工具,可以在美化你的终端页面的同时提升你的效率。 以下操作默认使用的Ubuntu系统。 ZSH 安装 Ubuntu: 安装zsh sudo apt install zsh 设置为默认shell sudo chsh -s /bin/zsh # 为其他用户设置:sudo chsh -s /bin/zsh <username> 安装git sudo apt install git 安装oh-my-zsh 手动安装: git clone --depth=1 https://github.com/ohmyzsh/ohmyzsh.git ~/.oh-my-zsh cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc 或 自动安装: sh -c \"$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\" ZSH设置 通过修改配置文件 ~/.zshrc可以修改终端设置。 经常使用的设置应该有: ZSH_THEME:ZSH主题。 plugins:ZSH插件。 同时一些希望终端启动时运行的命令也可以放在该文件里,比如conda的启动。 也就是终端加载的时候会运行的命令,也可以 source ~/.zshrc更新配置文件对终端的设置 插件推荐 sh-completions 这个插件提供了基础的补全功能。目前似乎已经被集成到ZSH项目中。 git clone --depth=1 https://github.com/zsh-users/zsh-completions ${ZSH_CUSTOM:-${ZSH:-~/.oh-my-zsh}/custom}/plugins/zsh-completions zsh-autosuggestions 这个插件可以提供自动补全的功能,比如输入 git,自动补全为 git status。 git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions.git ${ZSH_CUSTOM:-${ZSH:-~/.oh-my-zsh}/custom}/plugins/zsh-autosuggestions Incremental completion on zsh mkdir $ZSH_CUSTOM/plugins/incr curl -fsSL https://mimosa-pudica.net/src/incr-0.2.zsh -o $ZSH_CUSTOM/plugins/incr/incr.zsh echo 'source $ZSH_CUSTOM/plugins/incr/incr.zsh' >> ~/.zshrc source ~/.zshrc zsh-syntax-highlighting git clone --depth=1 https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting powerlevel10K 目前配置的终端可以显示当前的虚拟环境、路径、git情况,以及上个命令的执行时间等。基本需要都被涵盖。这写额外的功能和美化得归功于ZSH主题——powerlevel10K。具体效果图如下所示: 安装 字体 由于会显示一些额外的符号,所以需要安装新的字体。 字体需要在显示终端的设备上安装。比如如果系统在本地,那么在本地安装。如果连接的远程,那么需要在远程上安装ZSH和主题,在本地安装字体。原理不难理解。 字体链接:NerFont 字体链接里有完整的安装教程,可以根据自己的平台进行安装。如果是克隆源码安装的时候注意设置depth,建议从该仓库的releases里选择字体进行安装。 根据平台情况安装然后后设置字体显示即可,一般需要在终端或者编辑器里设置字体选项。考虑到种类较多且教程丰富简单,此处不罗列详细步骤。 Tips:如果设置的是Vscode平台,需要设置整体的字体: Editor:Font Family,单独设置终端字体好像无法正常工作。 主题 使用命令安装到 ~/.oh-my-zsh目录下: git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k 在 ~/.zshrc下启动主题: ZSH_THEME=\"powerlevel10k/powerlevel10k\" 配置 重启终端或者 source ~/.zshrc切换主题,之后会进入配置界面。(配置完成后也可以使用 p10k configure重新进入配置向导) 配置向导就是一些问题来判断你的字体情况和个人偏好,然后写入配置文件 ~/.p10k.sh中,大约有十几个问题。 配置向导的详细介绍可以参考:我的终端环境:与众不同的 zsh 主题 - powerlevel10k本文介绍 zsh 主题 powerlevel10k - 掘金 在配置向导完成后,如果需要一些额外的配置。可以修改配置文件 ~/.p10k.sh。 比如,通过修改 POWERLEVEL9K_LEFT_PROMPT_ELEMENTS和 POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS可以修改两侧显示的元素以及顺序等。 参考资料 Linux Zsh 使用 oh-my-zsh 打造高效便捷的 shell 环境 - sysin | SYStem INside | 软件与技术分享 我的终端环境:与众不同的 zsh 主题 - powerlevel10k本文介绍 zsh 主题 powerlevel10k - 掘金 杰哥","categories":[{"name":"工具","slug":"工具","permalink":"https://bg51717.github.io/categories/%E5%B7%A5%E5%85%B7/"}],"tags":[{"name":"终端","slug":"终端","permalink":"https://bg51717.github.io/tags/%E7%BB%88%E7%AB%AF/"},{"name":"oh-my-zsh","slug":"oh-my-zsh","permalink":"https://bg51717.github.io/tags/oh-my-zsh/"},{"name":"powerlevel10k","slug":"powerlevel10k","permalink":"https://bg51717.github.io/tags/powerlevel10k/"}]},{"title":"安卓手机配置Google","slug":"工具/安卓手机配置Google","date":"2024-08-27T10:14:49.000Z","updated":"2024-11-02T02:55:49.875Z","comments":true,"path":"/61294/","link":"","permalink":"https://bg51717.github.io/61294/","excerpt":"","text":"介绍 这篇博客主要记录了如何在安卓手机上配置谷歌三件套的服务。 对于非华为荣耀手机,可能仅仅需要简单的使用一些第三方的安装软件即可完成,比如 go安装助手等,资源较大且获取难度较低。 而本篇博客主要介绍华为荣耀手机如何获取谷歌三件套的服务和配置支付信息等。 介绍两个并行的方法,当其中一个方法失效的时候,可以用另一个方法的部分替代。 方法是 华谷套件和to-alan/HarmonyOSInstallGMS: 华为安装GMS教程 。 博主前面的流程都使用的是华谷套件,该软件可以在每一步运行完后自动检测是否设置成功。在卸载MicroG后,转为使用方法二进行后续的处理。 目前手机谷歌三件套运行稳定,基本可以提供原生的谷歌三件套服务。 支付方式的添加todo。 参考资料 to-alan/HarmonyOSInstallGMS: 华为安装GMS教程","categories":[{"name":"工具","slug":"工具","permalink":"https://bg51717.github.io/categories/%E5%B7%A5%E5%85%B7/"}],"tags":[{"name":"安卓","slug":"安卓","permalink":"https://bg51717.github.io/tags/%E5%AE%89%E5%8D%93/"},{"name":"Google","slug":"Google","permalink":"https://bg51717.github.io/tags/Google/"}]},{"title":"PyTorch代码转HF","slug":"模板/PyTorch代码转HF","date":"2024-08-06T10:56:13.000Z","updated":"2024-12-09T16:13:26.328Z","comments":true,"path":"/61054/","link":"","permalink":"https://bg51717.github.io/61054/","excerpt":"","text":"介绍 这篇博客主要介绍了怎么把一个已有的Pytorch代码转变成HF支持的格式,然后可以方便的放入HF代码流程中,并且使用一些HF的函数。代码转换主要涉及到以下几个方面: Config Model Trainer Dataset 因为ckpt里面的代码使用的会是相对导入,所以在转换的过程中,建议把 configuration_xxx.py和 modeling_xxx.py文件放在同一个目录下,并且添加 __init__.py文件。 在hf框架下模型的ckpt文件夹里面,我们一般可以看到以下几个文件(假如模型是ltgbert): config.json:存放模型的具体设置,比如num_heads,act_fn等。 configuration_ltgbert.py:存放关于模型 config类的代码。 modeling_ltgbert.py:模型类以及各种组件模块的代码,也会包含一些功能函数。 pytorch_model.bin:模型的权重,存放模型参数具体的值。 special_token_map.json:模型的 tokenizer的特殊token。 tokenizer_config.json:模型的 tokenizer的设置,参考 config.json。 tokenizer.json:模型的 token以及序号,等同于 tokenizer的权重。 部分 ckpt里如果有自定义的 tokenizer,可能还会有相关的代码定义,此处暂不列出。(挖坑 部分 ckpt里模型的权重可能会使用多个文件拆分存放或者多种格式的,此处暂不列出。 模型的设置和类的代码可能不会在文件中出现,因为hf官方会把一些常用的模型的相关代码放在官方库里,该文件夹在github上的网址为:transformers/src/transformers/models at main · huggingface/transformers。 尽管文件的 ckpt里文件较多,但是框架可以帮你自动保存相关文件,当然也可以通过重载函数实现自定义的保存过程。(挖坑 Config 参考:Building custom models (huggingface.co) 代码主要在 configuration_xxx.py。 config主要是在模型基本架构不变的情况下,怎么通过超参设置模型的具体结构,在模型初始化或者加载权重的时候会传入的config进行模型设置。 示例: from transformers import PretrainedConfig from typing import List class LtgBertConfig(PretrainedConfig): model_type = \"LtgBert\" \"\"\"Configuration class to store the configuration of a `LtgBertModel`. \"\"\" def __init__(self, vocab_size_or_config_json_file=16384, hidden_size=768, num_hidden_layers=12, num_attention_heads=12, intermediate_size=3072, hidden_act=\"gelu\", hidden_dropout_prob=0.1, attention_probs_dropout_prob=0.1, max_position_embeddings=512, type_vocab_size=2, initializer_range=0.02, output_all_encoded_layers=False, require_all_hidden_states=True, batch_first=True, **kwargs): self.hidden_size = hidden_size self.num_hidden_layers = num_hidden_layers self.num_attention_heads = num_attention_heads self.hidden_act = hidden_act self.intermediate_size = intermediate_size self.hidden_dropout_prob = hidden_dropout_prob self.attention_probs_dropout_prob = attention_probs_dropout_prob self.max_position_embeddings = max_position_embeddings self.type_vocab_size = type_vocab_size self.initializer_range = initializer_range self.output_all_encoded_layers = output_all_encoded_layers self.require_all_hidden_states = require_all_hidden_states self.batch_first=batch_first if isinstance(vocab_size_or_config_json_file, str) or (sys.version_info[0] == 2 and isinstance(vocab_size_or_config_json_file, unicode)): with open(vocab_size_or_config_json_file, \"r\", encoding='utf-8') as reader: json_config = json.loads(reader.read()) for key, value in json_config.items(): self.__dict__[key] = value elif isinstance(vocab_size_or_config_json_file, int): self.vocab_size = vocab_size_or_config_json_file else: raise ValueError(\"First argument must be either a vocabulary size (int)\" \"or the path to a pretrained model config file (str)\") super(LtgBertConfig, self).__init__(**kwargs) 必须满足: 继承自 PretrainedConfig __init__函数接受 kwargs,并且使用 super()).__init__传递这些参数 model_type的作用是把模型注册到 AutoClass中,建议设置。 Model 参考:Building custom models (huggingface.co) modeling_xxx.py会存放模型的函数,模块,主要类的代码。 函数按照正常定义即可,除了函数以外也可以存放一些常量。 模块基本是需要继承自 nn.Module,关于模块的各种知识参考 pytorch教程。 首先模型需要一个基类,需要是 transformers.PreTrainedModel的子类,然后其 self.model需要是具体的模型主体,需要实现 __init__和 forward函数,并且部分参数需要固定,可以参考官方库里的模型源码。 然后针对每一个下游任务,都需要有对应的类,比如自回归任务需要 LlamaForCausalLM。具体要求基本和模型的基类差不多。但是一般会针对下游任务额外多一个分类头等小组件,然后 forward函数的部分固定参数可能会变化,基本也是根据已有的源码模仿即可。 此外,每个模型的类可能会有一些特殊的成员属性,比如 _tp_plan = {\"lm_head\": \"colwise_rep\"},是用于配合框架进行模型判断的,一般不设置也没有关系,代码执行时如果遇到缺少该属性的时候框架也会提示。 我们可以以 llama模型举例,查看其模型定义文件里所有类: 模块类: class LlamaRMSNorm(nn.Module) class LlamaRotaryEmbedding(nn.Module) class LlamaLinearScalingRotaryEmbedding(LlamaRotaryEmbedding) class LlamaDynamicNTKScalingRotaryEmbedding(LlamaRotaryEmbedding) class LlamaMLP(nn.Module) class LlamaAttention(nn.Module) class LlamaFlashAttention2(LlamaAttention) class LlamaSdpaAttention(LlamaAttention) class LlamaDecoderLayer(nn.Module) 模型类: class LlamaPreTrainedModel(PreTrainedModel) class LlamaModel(LlamaPreTrainedModel) class LlamaForCausalLM(LlamaPreTrainedModel, GenerationMixin) class LlamaForSequenceClassification(LlamaPreTrainedModel) class LlamaForQuestionAnswering(LlamaPreTrainedModel) class LlamaForTokenClassification(LlamaPreTrainedModel) 函数: 对于其中的函数不予列出,因为这没有什么理解难度。 示例: class LtgBertForMaskedLM(PreTrainedModel): config_class=LtgBertConfig def __init__(self,config,activation_checkpointing=False): super().__init__(config) # 这里可以把成员变成类的继承LtgBertForMaskedLM(Bert): self.model=Bert( config=config, activation_checkpointing=activation_checkpointing ) self.require_all_hidden_states=config.require_all_hidden_states self.batch_first=config.batch_first def forward(self, input_ids, attention_mask, masked_lm_labels=None): if self.batch_first: # 模型把batch放在第二个维度 input_ids=input_ids.transpose(0,1) if masked_lm_labels is not None: masked_lm_labels=masked_lm_labels.transpose(0,1) subword_prediction=self.model(input_ids, attention_mask, masked_lm_labels=masked_lm_labels) loss=None if masked_lm_labels is not None: target_ids = masked_lm_labels.flatten() target_ids = target_ids[target_ids != -100] loss = F.cross_entropy(subword_prediction, target_ids) all_hidden_states=None if self.require_all_hidden_states: all_hidden_states=self.model.get_contextualized(input_ids=input_ids,attention_mask=attention_mask) if self.batch_first: if len(subword_prediction.size())>2: subword_prediction=subword_prediction.transpose(0,1) if all_hidden_states is not None: all_hidden_states=[it.transpose(0,1) for it in all_hidden_states] return MaskedLMOutput( loss=loss, logits=subword_prediction, hidden_states=all_hidden_states, attentions=None ) 对于自定义模型,往往每个 AutoClass上都会注册一个模型,因此往往要写多个自定义模型。 config_type的作用是把模型注册到 AutoClass中,建议设置。 由于简约性原则,官方要求 self.model对应原来的模型,比如用Pytorch定义的模型。 forward函数需要注意结果格式,transformers.modeling_outputs里定义了每种模型forward的结果格式。 其中对于每个特定的子任务都有个类似的模型,对于部分函数比如forward建议参考已有的代码进行操作,因为hf框架在使用特定子任务的模型的时候,可能会添加特殊的参数。比如,对于序列分类任务SequenceClassification,其中相关模型的forward为: def forward( self, input_ids: Optional[torch.Tensor] = None, attention_mask: Optional[torch.Tensor] = None, output_attentions: Optional[bool] = None, output_hidden_states: Optional[bool] = None, inputs_embeds: Optional[torch.Tensor] = None, return_dict: Optional[bool] = None, labels: Optional[torch.LongTensor] = None, ) -> Union[Tuple[torch.Tensor], SequenceClassifierOutput]: if self.batch_first: # 模型把batch放在第二个维度 input_ids=input_ids.transpose(0,1) contextualized_embeddings=self.model.get_contextualized(input_ids, attention_mask) if self.batch_first: contextualized_embeddings=contextualized_embeddings.transpose(0,1) logits = self.head(contextualized_embeddings[:, 0, :]) if labels is not None: if self.config.problem_type is None: if self.num_labels == 1: self.config.problem_type = \"regression\" elif self.num_labels > 1 and (labels.dtype == torch.long or labels.dtype == torch.int): self.config.problem_type = \"single_label_classification\" else: self.config.problem_type = \"multi_label_classification\" if self.config.problem_type == \"regression\": loss_fct = nn.MSELoss() if self.num_labels == 1: loss = loss_fct(logits.squeeze(), labels.squeeze()) else: loss = loss_fct(logits, labels) elif self.config.problem_type == \"single_label_classification\": loss_fct = nn.CrossEntropyLoss() loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1)) elif self.config.problem_type == \"multi_label_classification\": loss_fct = nn.BCEWithLogitsLoss() loss = loss_fct(logits, labels) assert output_attentions is None assert output_hidden_states is None return SequenceClassifierOutput( loss=loss, logits=logits, hidden_states=contextualized_embeddings if output_hidden_states else None, attentions=None ) 这里hf框架会在配置中添加problem_type等内容。 注册 如果在ckpt文件夹的 config.json里没有 auto_map指明 AutoClass的注册: \"auto_map\": { \"AutoConfig\": \"configuration_ltgbert.LtgBertConfig\", \"AutoModelForMaskedLM\": \"modeling_ltgbert.LtgBertForMaskedLM\", \"AutoModelForSequenceClassification\": \"modeling_ltgbert.LtgBertForSequenceClassification\", \"AutoModelForCausalLM\": \"modeling_ltgbert.LtgBertForCausalLM\" } 那么需要手动添加,在读取ckpt的代码里添加: AutoConfig.register(\"LtgBert\", LtgBertConfig) AutoModelForMaskedLM.register(LtgBertConfig, LtgBertForMaskedLM) AutoModelForSequenceClassification.register(LtgBertConfig, LtgBertForSequenceClassification) AutoModelForCausalLM.register(LtgBertConfig, AutoModelForCausalLM) 如果希望在保存模型的时候 config.json文件中自动包含 auto_map,可以在模型代码文件里添加以下代码(如果模型是从ckpt里加载的就不需要添加): LtgBertConfig.register_for_auto_class() LtgBertForMaskedLM.register_for_auto_class(\"AutoModelForMaskedLM\") LtgBertForSequenceClassification.register_for_auto_class(\"AutoModelForSequenceClassification\") LtgBertForCausalLM.register_for_auto_class(\"AutoModelForCausalLM\") 后来发现只有注册可能会存在 config.json里 auto_map不完整的情况(原因暂时没有调查),可以考虑直接在 config.__init__里强制指定: def __init__(self,....): ... self.auto_map={ \"AutoConfig\": \"configuration_ltgbert.LtgBertConfig\", \"AutoModelForMaskedLM\": \"modeling_ltgbert.LtgBertForMaskedLM\", \"AutoModelForSequenceClassification\": \"modeling_ltgbert.LtgBertForSequenceClassification\", \"AutoModelForCausalLM\": \"modeling_ltgbert.LtgBertForCausalLM\" } Trainer 训练流程的转换主要设计HF的 Trainer类,可以参考Trainer (huggingface.co)和Trainer (huggingface.co)。 Trainer把训练的流程分为几个过程,通过继承以及重写相关函数即可完成流程的定制,通过参数即可实现超参数的设置,细节阅读参考资料。 Dataset dataset可以继承自 torch.utils.data.dataset,但是需要注意 __getitem__,默认情况该函数返回的需要满足 dict格式,从而实现参数的设置。 参考资料 Building custom models (huggingface.co) Trainer (huggingface.co) Trainer (huggingface.co)","categories":[{"name":"模板","slug":"模板","permalink":"https://bg51717.github.io/categories/%E6%A8%A1%E6%9D%BF/"}],"tags":[{"name":"模板","slug":"模板","permalink":"https://bg51717.github.io/tags/%E6%A8%A1%E6%9D%BF/"},{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"PyTorch","slug":"PyTorch","permalink":"https://bg51717.github.io/tags/PyTorch/"},{"name":"HuggingFace","slug":"HuggingFace","permalink":"https://bg51717.github.io/tags/HuggingFace/"},{"name":"Trainer","slug":"Trainer","permalink":"https://bg51717.github.io/tags/Trainer/"},{"name":"config","slug":"config","permalink":"https://bg51717.github.io/tags/config/"},{"name":"model","slug":"model","permalink":"https://bg51717.github.io/tags/model/"},{"name":"dataset","slug":"dataset","permalink":"https://bg51717.github.io/tags/dataset/"}]},{"title":"随机数种子","slug":"深度学习/工程细节/随机数种子","date":"2024-07-09T10:02:24.000Z","updated":"2024-11-02T02:55:49.881Z","comments":true,"path":"/7369/","link":"","permalink":"https://bg51717.github.io/7369/","excerpt":"","text":"介绍 在深度学习的实际项目中,为了减少随机性,增强项目的复现能力,设置固定随机数种子十分重要,因此这篇文章罗列了一些设置随机种子的方法和减少项目随机性的经验。 通用函数 def set_random_seed(seed): \"\"\"Set random seeds.\"\"\" os.environ['PYTHONHASHSEED'] = str(seed) random.seed(seed) # 设置 Python 内置随机库的种子 np.random.seed(seed) # 设置 NumPy 随机库的种子 torch.manual_seed(seed) # 设置 PyTorch 随机库的种子 torch.cuda.manual_seed(seed) # 为当前 CUDA 设备设置种子 torch.cuda.manual_seed_all(seed) # 为所有 CUDA 设备设置种子 Scikit-learn 在 Scikit-learn中,部分算法需要设置 random_state,比如聚类算法 kmeans。 KMeans(n_clusters=2,random_state=42) 工程经验 由于部分原因,一些python数组或者python集合等,可能顺序也会影响结果的随机性。如果在无法确保顺序是固定的或者顺序是有要求的情况下,尝试对这些中间结果进行排序减少随机性。 参考资料 【Python】深度学习中随机数种子seed的种类和设置方式_seed设置-CSDN博客","categories":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"工程细节","slug":"深度学习/工程细节","permalink":"https://bg51717.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/%E5%B7%A5%E7%A8%8B%E7%BB%86%E8%8A%82/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"随机数","slug":"随机数","permalink":"https://bg51717.github.io/tags/%E9%9A%8F%E6%9C%BA%E6%95%B0/"}]},{"title":"vscode调试python","slug":"杂项/vscode调试python","date":"2024-04-19T14:47:51.000Z","updated":"2024-11-02T02:55:49.879Z","comments":true,"path":"/30403/","link":"","permalink":"https://bg51717.github.io/30403/","excerpt":"","text":"介绍 在学习项目的过程中,很多时候需要通过调试来高效率的了解代码的执行过程,因此这里介绍下怎么使用vscode对python程序进行调试。 方法一:简单图标点击 vscode对一些简单的程序提供了一些可视化的调试方式,对于一些不需要指定参数等简单的调试功能,可以直接点击vscode左上角的几个图标进行debug过程。由于过于简单,此处不做介绍。 方法二:编辑launch.json文件 在工作目录下的 ./vscode/launch.json文件里面,指定了各种debug和程序运行的参数、环境、解释器、目录等基本所有的环境配置。 可以在左下角的添加配置里面快速添加常见的选项。 比如下面所示: { // 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。 // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 \"version\": \"0.2.0\", \"configurations\": [ { \"name\": \"(gdb) 启动\", \"type\": \"cppdbg\", \"request\": \"launch\", \"program\": \"${fileDirname}/${fileBasenameNoExtension}\", \"args\": [], \"stopAtEntry\": false, \"cwd\": \"${fileDirname}\", \"environment\": [], \"externalConsole\": false, \"MIMode\": \"gdb\", \"setupCommands\": [ { \"description\": \"为 gdb 启用整齐打印\", \"text\": \"-enable-pretty-printing\", \"ignoreFailures\": true }, { \"description\": \"将反汇编风格设置为 Intel\", \"text\": \"-gdb-set disassembly-flavor intel\", \"ignoreFailures\": true } ] }, { \"name\": \"py-dbg QLLM\", \"type\": \"debugpy\", \"request\": \"launch\", \"python\": \"/home/bg51717/.conda/envs/QLLM/bin/python\", // \"program\": \"/home/bg51717/project/QLLM/qllm/__main__.py\", \"module\": \"qllm\", \"console\": \"integratedTerminal\", \"args\":[ \"--model=/home/bg51717/project/models/facebook/opt-350m\", \"--method=gptq\", \"--nsamples=64\", \"--wbits=4\", \"--groupsize=128\", \"--save\", \"/home/bg51717/project/QLLM/facebook/opt-350m_gptq4b\", \"--export_onnx\", \"/home/bg51717/project/QLLM/onnx_model/facebook/opt-350m_gptq4b\" ], \"env\": {\"PYTHONPATH\": \"/home/bg51717/project/QLLM/qllm\"}, \"cwd\": \"/home/bg51717/project/QLLM\" } ] } 这里参数非常多(建议使用的时候查询 gpt、搜索引擎、文档等。 这里介绍几个常用的选项。此外,在编辑的时候可以类似 Linux那样使用 ${fileDirname}来引用 vscode程序的当前变量比如工作目录 参数 含义 类型 name 过程名字 str type 过程类型 str python 解释器(使用虚拟环境的时候需要注意指定 str program 程序文件,按照脚本方式运行过程 str module 模块名,按照模块方式运行过程 str args 运行过程的参数 list env 环境变量 dict cwd 工作目录 str 此外,在使用的过程中python里面绝对引入、相对引入等。建议参考python相对导入常见问题和解决方案 - 知乎 (zhihu.com) 。 此处发现那里也有些没有提及的东西。 解决方案错误一:ImportError: attempted relative import with no known parent package 里可以不修改代码,使用 python -m命令+调整工作目录成功运行。(笔者当时遇到一个坑,当时没有注意的调试的是工程目录里的qllm文件还是env里装的py包 方法三:使用debug.py文件引入要调试的文件 如题,建立一个 debug.py文件引入要调试的文件,类似于使用代理进行调试过程。 参考【Python 入门】新手必会 vscode Debug 调试技巧_哔哩哔哩_bilibili 参考资料 python相对导入常见问题和解决方案 - 知乎 (zhihu.com) 【Python 入门】新手必会 vscode Debug 调试技巧_哔哩哔哩_bilibili","categories":[{"name":"杂项","slug":"杂项","permalink":"https://bg51717.github.io/categories/%E6%9D%82%E9%A1%B9/"}],"tags":[{"name":"vscode","slug":"vscode","permalink":"https://bg51717.github.io/tags/vscode/"},{"name":"python","slug":"python","permalink":"https://bg51717.github.io/tags/python/"},{"name":"调试","slug":"调试","permalink":"https://bg51717.github.io/tags/%E8%B0%83%E8%AF%95/"},{"name":"debug","slug":"debug","permalink":"https://bg51717.github.io/tags/debug/"}]},{"title":"nlp常用排行榜","slug":"深度学习/自然语言处理/nlp常用排行榜","date":"2024-04-05T06:56:57.000Z","updated":"2024-11-02T02:55:49.890Z","comments":true,"path":"/63314/","link":"","permalink":"https://bg51717.github.io/63314/","excerpt":"","text":"介绍 在工作和学习的时候发现,很多时候挑选合适的模型和数据集等也是一个重要且麻烦的过程。发现有很多相关的评测的排行榜,根据这些实时更新的排行榜,可以辅助我们进行选择模型等前期工作。 Spaces - Hugging Face 这里罗列了许多关于ai的最新新闻,也能搜索到各种排行榜leaderboard。 nlp任务 MTEB Leaderboard - a Hugging Face Space by mteb Massive Text Embedding Benchmark (MTEB) ,是关于文本嵌入的排行榜,同时关注排行榜的like人数(从某种意义上反应排行榜的效用)。 大模型评测 Open LLM Leaderboard - a Hugging Face Space by HuggingFaceH4 这里提供了各种关于大模型在多维度的数据集上的表现能力,并且支持根据大模型的类型、精度等过滤大模型排行榜。 Big Code Models Leaderboard - a Hugging Face Space by bigcode 这里提供了关于大模型code能力的排行榜。 LMSys Chatbot Arena Leaderboard - a Hugging Face Space by lmsys 这里提供了关于大模型对话能力的排行榜(但是由于不知名原因暂时无法打开)。 Chat with Open Large Language Models (lmsys.org) 这里是关于大模型对话能力的测评网站,也提供了参考用的排行榜。 LLM-Perf Leaderboard - a Hugging Face Space by optimum 这里提供了大模型在给定硬件条件的训练资源后微调的性能排行榜。 Open CoT Leaderboard - a Hugging Face Space by logikon 这里提供了关于大模型CoT(Chain of Thought)的排行榜。 数据集 参考资料","categories":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"自然语言处理","slug":"自然语言处理","permalink":"https://bg51717.github.io/categories/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/"}],"tags":[]},{"title":"pytroch_tutorials杂项","slug":"杂项/huggingface/transformers_tutorials杂项","date":"2024-03-23T08:23:43.000Z","updated":"2024-11-02T02:55:49.875Z","comments":true,"path":"/20669/","link":"","permalink":"https://bg51717.github.io/20669/","excerpt":"","text":"介绍 当作快速过这个资料的笔记,一些关于别的库的介绍是不完全的,考虑在使用的时候从别的信息渠道就行信息的搜集。也可以作为后面待更博客列举? 常用方式 可以参考huggingface transformers教程总结 - 知乎 (zhihu.com) (这篇教程很多是基于tf的,使用时候可以考虑换成pt)和 Hugging Face - Documentation 。 tutorials具体(中文版本) 使用AutoClass加载预训练实例 (huggingface.co) 这里有个注意的点是使用 AutoModelForSequenceClassification可能会在模型后面添加一层来适应下游任务(别的类似的类可能也会有类似的做法),如果想不添加任何层直接使用,考虑使用AutoModel类来加载模型。 预处理 (huggingface.co) 这里介绍了怎么预处理各种数据比如文本,多模态,图片等。 微调预训练模型 (huggingface.co) 这里介绍了怎么使用 transformers.Trainer类和pytorch原生代码来微调模型(pytorch lighting也提供了一个Trainer)。 也介绍了怎么使用huggingface的github上分享的脚本来微调模型huggingface/transformers: 🤗 Transformers: State-of-the-art Machine Learning for Pytorch, TensorFlow, and JAX. (github.com), accelerate提供了accelerate config命令通过命令行交互帮助配置文件。 🤗 加速分布式训练 (huggingface.co) 这里介绍了怎么使用accelerate来进行分布式训练,流程非常简单,基本就是pytorch框架单卡训练的代码改个四五行。 使用 🤗 PEFT 加载adapters (huggingface.co) 这里大致介绍了怎么使用 perf库进行高效的参数微调,具体表现为通过 adapters实现少参数调整,还大致介绍了怎么量化加载、启用、禁止、训练等。 Transformers Agents (huggingface.co) 这里介绍了transformers新版本提供的一个api(测试版可能随时变化),通过自然语言来辅助进行功能实现,类似于通过自然语言编程。 使用LLMs进行生成 (huggingface.co) 这里大致介绍了怎么使用 model.generate()进行更好且多样化生成。 使用 🤗 Tokenizers 中的分词器 (huggingface.co) 大概介绍了怎么训练、保存和加载分词器tokenizer。 用于推理的多语言模型 (huggingface.co) 介绍了一些常用的多语言翻译模型。 创建自定义架构 (huggingface.co) 如题,使用一些配置文件自定义模型架构和各种处理器比如自然语言处理的分词器、语音识别的特征提取器等。 共享自定义模型 (huggingface.co) 这里介绍了怎么自定义模型,推送到hub上面以及怎么在AutoClass(比如AutoModel等)上面注册。 聊天模型的模板 (huggingface.co) 这里介绍了聊天的模板,怎么把多轮对话转变成一个字符串输入模型。并且支持用户自定义模板和使用内置模板等功能。自定义模板的办法使用的是Jinja。 导出为 ONNX (huggingface.co) 这里介绍了怎么通过Optimum库把模型导出为ONNX格式,ONNX模式下为公开具有标准化运算符和数据类型的图,可以进行各种优化和框架间的相互转移。 (这里还介绍了transformers.onnx,但这个库已经停止了维护并且回头会从教程里删去 导出为 TFLite (huggingface.co) TensorFlow Lite 是一个轻量级框架,用于资源受限的设备上,如手机、嵌入式系统和物联网(IoT)设备,部署机器学习模型,其文件扩展名为 .tflite,同样可以通过Optimum库导出。 导出为 TorchScript (huggingface.co) 这里关于torchscirpt的介绍可以参考pytorch的,这里还介绍了使用 Neuron SDK 将 Hugging Face TorchScript 模型部署到 AWS。 性能与可扩展性 (huggingface.co) 这是关于训练和推理的综述,里面有许多指向英语文档的链接。 完全分片数据并行 (huggingface.co) 介绍了怎么使用accelerate把模型参数切片在多个GPU运行以及部分优化。 训练用的定制硬件 (huggingface.co) 这里引入了怎么更好的使用和了解硬件,但只是引入,还是需要后期继续深入学习。 使用Trainer API进行超参数搜索 (huggingface.co) 如题,支持多种超参数搜索框架。 实例化大型模型 (huggingface.co) 这里介绍了怎么加载大模型,降低内存,具体表现为分片,把权重文件分成多个。 调试 (huggingface.co) 这里介绍了怎么使用脚本进行多GPU网络问题调试和上溢下溢检测(通过hook函数统计每个模块的输入和输出的绝对值的极值)。 使用 torch.compile() 优化推理 (huggingface.co) torch.compile() 优化推理过程,并且列出了许多实验结果。 如何创建自定义流水线? (huggingface.co) 这里介绍了怎么自定义流水线pipeline并且注册和推送到云端。 分词器的摘要 (huggingface.co) 这里介绍了许多分词算法,如何构造tokenizer。 Agents和工具 (huggingface.co) 这里介绍了各种各样的agent和tools,agents相较于模型可以使用tools。 Callbacks (huggingface.co) Callbacks可以用来自定义PyTorch [Trainer]中训练循环行为的对象(此功能尚未在TensorFlow中实现),该对象可以检查训练循环状态(用于进度报告、在TensorBoard或其他ML平台上记录日志等),并做出决策(例如提前停止)。 这里介绍了一些常用的callbacks,怎么自定义callbacks,TrainerState代表当前训练状态,TrainerControl控制训练循环。 Configuration (huggingface.co) 介绍了Pretrainedconfig类。 Data Collator (huggingface.co) 这里介绍了各种Data collators来处理数据。 Logging (huggingface.co) 这里介绍了transformers的日志系统。 模型 (huggingface.co) 这里介绍了怎么使用PretrainedModel,由于一定原因,官方似乎推荐PretrainedModel是用于对model的一层包装,forward等函数在model里面实现。 还介绍了ModuleUtilsMixin,这个是py通过多继承扩展模型功能的一个类,可以参考一个例子走近 Python 的 Mixin 类:利用 Python 多继承的魔力_mixin类-CSDN博客学习Mixin类和调用顺序。 Generation (huggingface.co) 这里介绍了GenerationConfig和GenerationMixin来使得模型进行多样化的生成过程,扩展了模型的功能。 导出 🤗 Transformers 模型到 ONNX (huggingface.co) 这里介绍了一些配置类来帮助transformers模型导出到ONNX。 Optimization (huggingface.co) 这里只是列举了几个模型参数跟新过程会用到的优化器等,但不是很全,也不是很深入,连示例过程都没有。 模型输出 (huggingface.co) Transformers库所有模型的输出都是 ModelOutput 的子类的实例,这里介绍了许多输出类和基本的类。 Pipelines (huggingface.co) 这里介绍了许多Pipeline和自定义Pipeline。 Processors (huggingface.co) 这个库大概介绍了Processor类,用于编码或者解码多模型输入并且组合。 量化 🤗 Transformers 模型 (huggingface.co) 这个大致介绍了常见的量化方法并给了简答的示例。 Tokenizer (huggingface.co) 大致介绍了Tokenizer和常用的办法。 Trainer (huggingface.co) 这里介绍了Transformers的Trainer,同时说明了这个Trainer用于别的库的模型时要注意的问题,同时还介绍了一些常用的问题和通过accelerate使用Trainer。 DeepSpeed集成 (huggingface.co) 这里介绍了DeepSpeed的很多常用的配置选项(非常复杂,挖坑todo Feature Extractor (huggingface.co) 这里介绍了Feature负责为音频或视觉模型准备输入特征。这包括从序列中提取特征。 Image Processor (huggingface.co) 这里介绍了对于图像处理的Processor。 这里往后就是介绍一些功能类,可以用于扩展、调试控制等功能 自定义层和工具 (huggingface.co) 这里介绍了一些Transformers库的为模型提供的自定义层和帮助函数(底层还是调用的pytorch或者TensorFlow,但是组合了一些常用的层。 pipelines的工具 (huggingface.co) 这里介绍了一些为Pipeline使用的功能类。 Tokenizers的工具 (huggingface.co) Trainer的工具 (huggingface.co) 用于生成的工具 (huggingface.co) Image Processors的工具 (huggingface.co) FeatureExtractors 的工具 (huggingface.co) 通用工具 (huggingface.co) 时间序列工具 (huggingface.co)","categories":[{"name":"杂项","slug":"杂项","permalink":"https://bg51717.github.io/categories/%E6%9D%82%E9%A1%B9/"},{"name":"huggingface","slug":"huggingface","permalink":"https://bg51717.github.io/categories/huggingface/"}],"tags":[{"name":"transformers_tutorials","slug":"transformers-tutorials","permalink":"https://bg51717.github.io/tags/transformers-tutorials/"}]},{"title":"pytorch_model","slug":"模板/pytorch-model","date":"2024-02-24T09:54:19.000Z","updated":"2024-11-02T02:55:49.879Z","comments":true,"path":"/42347/","link":"","permalink":"https://bg51717.github.io/42347/","excerpt":"","text":"介绍 作为深度学习的基本模板使用,方便使用的时候作为骨架 许多文件可以考虑添加argparse和sh来引入外部配置来抽象过程,增强代码重用性 dataset.py 这个文件提供了各种数据集的定义,自定义数据集需要实习三个主要函数 class MyDataset(torch.utils.data.Dataset): def __init__(self): super().__init__() #todo def __getitem__(self,idx): #todo def __len__(self): #todo models.py 这个文件负责提供各种模型的定义,可以是完全自定义的模型或者预训练模型 class MyModel(torch.nn.Module): def __init__(self) super().__init__() #todo def forward(self,input): #todo def get_model(config): #todo return model criterion.py 这个文件负责提供各种损失函数的定义,可以是完全自定义的损失函数或者框架提供的损失函数 class CustomLoss(nn.Module): def __init__(self): super(CustomLoss, self).__init__() # 在这里初始化你的参数,如果有的话 def forward(self, input, target): # 计算损失的逻辑 # 例如,这里我们使用简单的均方误差作为示例 loss = torch.mean((input - target) ** 2) return loss def get_criterion() #todo optimizer.py 这个文件负责提供各种优化器的定义,可以是完全自定义的优化器或者框架提供的优化器 class CustomOptimizer(torch.optim.Optimizer): def __init__(self, params, lr=0.01, momentum=0.5, weight_decay=0, learning_rate_decay=0.9): defaults = dict(lr=lr, momentum=momentum, weight_decay=weight_decay, learning_rate_decay=learning_rate_decay) super(CustomOptimizer, self).__init__(params, defaults) def step(self, closure=None): \"\"\"Performs a single optimization step.\"\"\" loss = None if closure is not None: loss = closure() for group in self.param_groups: for p in group['params']: if p.grad is None: continue d_p = p.grad.data if group['momentum'] > 0: param_state = self.state[p] if 'momentum_buffer' not in param_state: buf = param_state['momentum_buffer'] = torch.clone(d_p).detach() buf.mul_(group['momentum']) else: buf = param_state['momentum_buffer'] buf.mul_(group['momentum']).add_(d_p, alpha=1 - group['momentum']) d_p = buf if group['weight_decay'] != 0: d_p.add_(p.data, alpha=group['weight_decay']) p.data.add_(d_p, alpha=-lr) return loss def get_Optimizer(): #todo lr_scheduler.py 这个文件负责提供各种学习率调度器的定义,可以是完全自定义的学习率调度器或者框架提供的学习率调度器 class MyLRScheduler(torch.optim.lr_scheduler._LRScheduler): def __init__(self, optimizer, step_size=10, gamma=0.1): self.step_size = step_size self.gamma = gamma super(CustomLRScheduler, self).__init__(optimizer) def get_lr(self): \"\"\"Calculate the learning rate at a given step.\"\"\" return [base_lr * self.gamma ** (self.last_step // self.step_size) for base_lr in self.base_lrs] def step(self, epoch=None): \"\"\"Update the learning rate at the end of the given epoch.\"\"\" if epoch is None: self.last_step += 1 else: self.last_step = epoch + 1 self._last_lr = self.get_lr() for param_group, lr in zip(self.optimizer.param_groups, self._last_lr): param_group['lr'] = lr def get_lr_scheduler() #todo train.py 这个文件负责提供各种训练方法和过程 def train_model(model,criterion,optimizer,scheduler,num_epochs): since=time.time() best_model_wts=copy.deepcopy(model.state_dict()) best_acc=0.0 #每个epoch for epoch in range(num_epochs): print('Epoch {}/{}'.format(epoch,num_epochs-1)) print('-'*10) # 分为训练或者测试阶段 for phase in ['train','val']: if phase=='train': model.train() else: model.eval() running_loss=0.0 running_corrects=0 # 每个批次进行计算损失和反向梯度 for inputs,labels in dataloaders[phase]: inputs=inputs.to(device) labels=labels.to(device) optimizer.zero_grad() with torch.set_grad_enabled(phase=='train'): outputs=model(inputs) _,preds=torch.max(outputs,1) loss=criterion(outputs,labels) if phase=='train': loss.backward() optimizer.step() running_loss+=loss.item()*inputs.size(0) running_corrects+=torch.sum(preds==labels.data) epoch_loss=running_loss/dataset_sizes[phase] epoch_acc=running_corrects/dataset_sizes[phase] print('{} Loss :{:.4f} Acc:{:.4f}'.format(phase,epoch_loss,epoch_acc)) if phase=='val' and epoch_acc>best_acc: best_acc=epoch_acc best_model_wts=copy.deepcopy(model.state_dict()) scheduler.step() print() time_elapsed=time.time()-since print('Training complete in {:.0f}m {:.0f}s'.format(time_elapsed//60,time_elapsed%60)) model.load_state_dict(best_model_wts) return model main.py 主要负责对于各个文件部分的引用,整合代码,基本逻辑为 #数据集 dataset #数据迭代器 dataloader #模型 model #损失函数 criterion #优化器 optimizer #学习率优化器 lr_scheduler #训练 train #保存 save #预测 predict 可选优化 梯度裁剪 torch.nn.utils.clip_grad_norm_ 加载最优参数 ... 其他 可以去pytorch官网找找一些关于模型的优化。 参考资料","categories":[{"name":"模板","slug":"模板","permalink":"https://bg51717.github.io/categories/%E6%A8%A1%E6%9D%BF/"}],"tags":[{"name":"模板","slug":"模板","permalink":"https://bg51717.github.io/tags/%E6%A8%A1%E6%9D%BF/"},{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"}]},{"title":"信息熵","slug":"数学/概率论/信息熵","date":"2024-01-31T11:19:50.000Z","updated":"2024-11-08T05:27:53.593Z","comments":true,"path":"/5656/","link":"","permalink":"https://bg51717.github.io/5656/","excerpt":"","text":"信息熵的公式 计算信息熵的公式为: \\[ H(x)=-\\sum p(x_i)logp(x_i) \\] 其中\\(p(x_i)\\)表示事件结果为\\(x_i\\)的概率 理解 信息熵表示对事件不确定性的一个度量,计算思路为“编码一个事件的最短平均编码长度”(任意进制编码都行,彼此差一个常数,但常用的是二进制以及自然对数) 所以信息熵的计算也可以写作: \\[ H(x)=\\sum p(x_i)f(x_i) \\] 其中\\(p(x_i)\\)表示事件结果为\\(x_i\\)的概率,\\(f(x_i)\\)为编码\\(x_i\\)需要的位数(这也是为什么在比较概率分布的时候,会选择用拟合的概率来计算\\(f(x_i)\\)) Huffman编码树 类比哈夫曼树,根据贪心思想, 出现概率大的结果应该占据相对短的编码 编码结果的种类和编码位数是指数级关系 所以我们得到 \\[ f(x_i)=-logp(x_i) \\] 代入就得到了最终形式。 应用 KL散度 交叉熵损失 参考资料","categories":[{"name":"数学","slug":"数学","permalink":"https://bg51717.github.io/categories/%E6%95%B0%E5%AD%A6/"},{"name":"概率论","slug":"数学/概率论","permalink":"https://bg51717.github.io/categories/%E6%95%B0%E5%AD%A6/%E6%A6%82%E7%8E%87%E8%AE%BA/"}],"tags":[{"name":"数学","slug":"数学","permalink":"https://bg51717.github.io/tags/%E6%95%B0%E5%AD%A6/"},{"name":"信息学","slug":"信息学","permalink":"https://bg51717.github.io/tags/%E4%BF%A1%E6%81%AF%E5%AD%A6/"}]},{"title":"pytroch_tutorials杂项","slug":"杂项/pytroch_tutorials杂项","date":"2023-12-29T08:23:43.000Z","updated":"2024-11-02T02:55:49.878Z","comments":true,"path":"/20668/","link":"","permalink":"https://bg51717.github.io/20668/","excerpt":"","text":"介绍 当作快速过这个资料的笔记,一些关于别的库的介绍是不完全的,考虑在使用的时候从别的信息渠道就行信息的搜集。也可以作为后面待更博客列举? 具体 torchscript Introduction to TorchScript — PyTorch Tutorials 2.2.1+cu121 documentation Loading a TorchScript Model in C++ — PyTorch Tutorials 2.2.1+cu121 documentation 这里介绍了把pytorch模型转换成torchscript的两种方法torch.jit.trace和torch.jit.script,前者会失去控制流信息,后者会保留控制流信息(具体查阅文档)。 转化为torchscript有以下好处: 不需要Python解释器也可以运行,可以被pytorch自带的特殊解释器直接使用 转为torchscript可以进行各种优化,提高运行效率 方便被其他语言调用 ONNX (optional) Exporting a Model from PyTorch to ONNX and Running it using ONNX Runtime — PyTorch Tutorials 2.2.1+cu121 documentation 这里介绍了在高效且跨平台ONNX运行环境部署的基本教程,通过torch.onnx把模型转化为onnx格式并保存,然后读取本地保存onnx格式模型并运行。 剖析pytorch model Profiling your PyTorch Module — PyTorch Tutorials 2.2.1+cu121 documentation 这里介绍了torch.autograd.profiler,在代码里添加with profiler.record_function(\"MASK INDICES\"):可以记录代码运行的指标,比如时间,cpu和内存使用率等,名字可以自定义,支持可视化结果。 Introduction to Holistic Trace Analysis — PyTorch Tutorials 2.2.1+cu121 documentation Trace Diff using Holistic Trace Analysis — PyTorch Tutorials 2.2.1+cu121 documentation 这里介绍了HolisticTraceAnalysis(HTA),一个用来剖析GPU运行情况的库。 把GPU运行时间分为了等待时间、计算时间和非计算时间,方便开发者评测模型运行的情况。还支持查看GPU内部的运行情况,也支持和之前的记录进行对比,也能可视化结果。 troch.fx 操作计算图 (测试版)在 FX 中构建卷积/批量范数热熔器 — PyTorch 教程 2.2.1+cu121 文档 torch.fx 是 PyTorch 提供的一个模块,它允许用户通过操作计算图来转换和优化 PyTorch 模型。这个模块提供了一种方式来表示和操作 PyTorch 模型的抽象,使得开发者可以更容易地对模型进行修改,例如重构模型结构、插入调试代码、优化性能等。 教程里介绍了一种融合相邻层的操作(只能用于eval模式)。比如融合卷积和归一化,融合之前,模型会把卷积结果写回显存,然后在调用归一化并且从显存读取之前结果;融合后变成一种操作,卷积结果不会写回显存,而是直接进行归一化后写会显存。 def _parent_name(target : str) -> Tuple[str, str]: \"\"\" Splits a ``qualname`` into parent path and last atom. For example, `foo.bar.baz` -> (`foo.bar`, `baz`) \"\"\" *parent, name = target.rsplit('.', 1) return parent[0] if parent else '', name def replace_node_module(node: fx.Node, modules: Dict[str, Any], new_module: torch.nn.Module): assert(isinstance(node.target, str)) parent_name, name = _parent_name(node.target) setattr(modules[parent_name], name, new_module) def fuse(model: torch.nn.Module) -> torch.nn.Module: model = copy.deepcopy(model) # The first step of most FX passes is to symbolically trace our model to # obtain a `GraphModule`. This is a representation of our original model # that is functionally identical to our original model, except that we now # also have a graph representation of our forward pass. #获取计算图 fx_model: fx.GraphModule = fx.symbolic_trace(model) modules = dict(fx_model.named_modules()) # The primary representation for working with FX are the `Graph` and the # `Node`. Each `GraphModule` has a `Graph` associated with it - this # `Graph` is also what generates `GraphModule.code`. # The `Graph` itself is represented as a list of `Node` objects. Thus, to # iterate through all of the operations in our graph, we iterate over each # `Node` in our `Graph`. #枚举所有计算图节点 for node in fx_model.graph.nodes: # The FX IR contains several types of nodes, which generally represent # call sites to modules, functions, or methods. The type of node is # determined by `Node.op`. #计算图节点还有别的属性,具体可以查阅相关资料 if node.op != 'call_module': # If our current node isn't calling a Module then we can ignore it. continue # For call sites, `Node.target` represents the module/function/method # that's being called. Here, we check `Node.target` to see if it's a # batch norm module, and then check `Node.args[0].target` to see if the # input `Node` is a convolution. #modules指的是模块,node.target指的是节点名字,node.args应该指的是输入这个节点的前置节点 if type(modules[node.target]) is nn.BatchNorm2d and type(modules[node.args[0].target]) is nn.Conv2d: if len(node.args[0].users) > 1: # Output of conv is used by other nodes continue conv = modules[node.args[0].target] bn = modules[node.target] fused_conv = fuse_conv_bn_eval(conv, bn) replace_node_module(node.args[0], modules, fused_conv) # As we've folded the batch nor into the conv, we need to replace all uses # of the batch norm with the conv. #把使用到node节点输出的地方变成node.args[0]节点输出 node.replace_all_uses_with(node.args[0]) # Now that all uses of the batch norm have been replaced, we can # safely remove the batch norm. fx_model.graph.erase_node(node) #检查计算图(Graph)的完整性和一致性,确定计算图的正确性 fx_model.graph.lint() # After we've modified our graph, we need to recompile our graph in order # to keep the generated code in sync. #recompile()方法的作用是将这些修改后的计算图转换回 Python 代码,这样你就可以创建一个新的 PyTorch 模型实例,它包含了你所做的所有修改 fx_model.recompile() return fx_model (测试版)使用 FX 构建简单的 CPU 性能分析器 — PyTorch 教程 2.2.1+cu121 文档 通过 print(traced_rn18.graph)我们可以查看计算图的信息,这里只选择一行查看,例子: %layer1_1_conv2 : [num_users=1] = call_module[target=layer1.1.conv2](args = (%layer1_1_relu,), kwargs = {}) %layer1_1_conv2:节点名字, [num_users=1]:被几个后续节点使用, call_module:表面这是一个调用模块, target=layer1.1.conv2:调用模块的名字, args = (%layer1_1_relu,):传递给模块的参数(输入模块的之前节点) kwargs = {}:额外关键词参数 可以参照教程写一个继承torch.fx.Interpreter的类,重写run和run_node实现对于计算图的捕获,在运行过程中添加自定义行为。 存储组织 (beta) Channels Last Memory Format in PyTorch — PyTorch Tutorials 2.2.1+cu121 documentation 这里介绍了内存组织方式torch.channels_last,torch.contiguous_format等,同时介绍了一下torch.stride、torch.contiguous和torch.is_contiguous等函数。 有的操作符会保留内存组织格式。 可以通过model.to(memory_format=)把模型转化为适合的组织形式,同时输入也需要相应的转换(框架应该支持自动转换?)。 但由于不是所有的操作符都支持某种内存组织格式,可能需要进行检查,这里给了检查和设置的示例代码。 前向模式自动微分 正向模式自动微分 (Beta) — PyTorch 教程 2.2.1+cu121 文档 这里看的不是很明白。挖个坑(todo... 正向传播一次只能算出相对一个参数的梯度,如果有n个参数,需要计算n次,所以这里的tangent只有一个,而不是每个参数对应一个。 可以参考【自动微分原理】AD的正反向模式 - 知乎 (zhihu.com)。 (这么一看,正向自动微分被方向自动微分爆杀?目前应用少且pytorch也只是测试版本 微分矩阵计算 雅可比派、黑森派、hvp、vhp 等:编写函数转换 — PyTorch 教程 2.2.1+cu121 文档 这里提供了各种使用自动求导计算微分矩阵的方法。 模型组装 模型组装 — PyTorch 教程 2.2.1+cu121 文档 这里介绍了torch.func.stack_model_state、torch.vmap等函数用来组装一系列结构相同参数不同的模型的输出,类似于使用cat连接模型输出,但是增加了底层优化。 单样本梯度 Per-sample-gradients — PyTorch 教程 2.2.1+cu121 文档 在一些要求对每个样本单独计算梯度的特殊情况下,可以使用本篇介绍的方法优化速度。 这里介绍了torch.func.grad、torch.func.functional_call、torch.func.vmap来优化加速(注意不同库有不同的同名函数) 这里grad和vmap都是创建的可调用对象,与别的同名函数不同。 c++接口 Using the PyTorch C++ Frontend — PyTorch Tutorials 2.2.1+cu121 documentation 介绍了怎么使用c++调用 TorchScript 中的动态并行性 TorchScript 中的动态并行性 — PyTorch 教程 2.2.1+cu121 文档 这里介绍了torch.jit.fork和torch.jit.wait两个函数用来并行执行pytorch相关代码。同时也引入了相关的类torch.jit.Future。 c++接口中的自动求导 Autograd in C++ Frontend — PyTorch Tutorials 2.2.1+cu121 documentation 这里都是一些用c++扩展pytorch的内容,后面需要细致学习(挖坑todo 自定义函数求二阶导 Double Backward with Custom Functions — PyTorch Tutorials 2.2.1+cu121 documentation 这篇文章介绍了怎么使用继承自torch.autograd.Function的自定义函数(重新forward和backward),计算导数,并且用torch.autograd.gradcheck使用数值解验证求导以及使用torchviz 可视化图形。 同时,注意在运行过程中,保存求导所需要参数时候建议按照教程分保存输入、保存输出和保存中间结果考虑代码结构,避免bug。 forward函数返回结果似乎和backward参数对应,backward输入参数是每个forward函数返回结果的grad。 保存中间结果的时候需要把中间结果返回,这样可以让pytorch的自动微分系统追踪这些中间结果(个人理解为返回中间结果相当于注册了这些中间结果,在backward的时候设置grad模型就会追踪这些中间结果,理解可能有误,但是使用的时候参考教程应该问题不大)。 自定义函数实现卷积 Fusing Convolution and Batch Norm using Custom Function — PyTorch Tutorials 2.2.1+cu121 documentation 先介绍了自定义卷积函数的构造,这里的once_differentiable指的是正向传播的结果只会反向传播一次,作用为求二阶导的时候会报错,从而起到限制的作用。 然后介绍了自定义批量归一化层的构造,然后介绍了自定义函数混合卷积和批量归一化,从而实现了节省内存的作用。 自定义 C++ 和 CUDA 扩展 Custom C++ and CUDA Extensions — PyTorch Tutorials 2.2.1+cu121 documentation 这里介绍了两个扩展c++组件的方法,“ahead of time ”和“just in time”。 ahead of time:构建setup.py文件用于构建c++扩展,然后在对应的cpp文件里面构建c++扩展代码。 just in time:通过torch.utils.cpp_extension.load直接加载cpp文件,会把编译的中间文件存在一个临时目录里。 这里还介绍了编写混合 C++/CUDA 扩展,由于还没有学过cuda,挖个坑todu... 使用自定义 C++ 运算符扩展 TorchScript Extending TorchScript with Custom C++ Operators — PyTorch Tutorials 2.2.1+cu121 documentation 如题,后面需要细致学习todo,,, 使用自定义 C++ 类扩展 TorchScript Extending TorchScript with Custom C++ Classes — PyTorch Tutorials 2.2.1+cu121 documentation 如题,后面需要细致学习todo,,, 在C++中注册一个分派操作符 Registering a Dispatched Operator in C++ — PyTorch Tutorials 2.2.1+cu121 documentation 由于一个操作在不同的设备上对应不同的底层代码,所以为了抽象化其操作,方便调用,需要在内核注册对应设备对应函数,然后在调用。 比如,第一个把 myadd_cpu注册到cpu上的 myadd函数,当在cpu上运行 myadd函数时候,会调用 myadd_cpu TORCH_LIBRARY_IMPL(myops, CPU, m) { m.impl(\"myadd\", myadd_cpu); } TORCH_LIBRARY_IMPL(myops, CUDA, m) { m.impl(\"myadd\", myadd_cuda); } 然后在使用的时候,通过torch的调度器自动根据设备在内核寻找合适函数调用。 Tensor myadd(const Tensor& self, const Tensor& other) { static auto op = torch::Dispatcher::singleton() .findSchemaOrThrow(\"myops::myadd\", \"\") .typed<decltype(myadd)>(); return op.call(self, other); } 这里还介绍了自动投影机制autocast,用于在运算之前把精度投影到合适的精度。 在 C++ 中扩展新后端的调度程序 Extending dispatcher for a new backend in C++ — PyTorch Tutorials 2.2.1+cu121 documentation 后端(backend)通常指的是支持PyTorch运行的底层系统或框架,这里介绍了如何添加自定义的框架。(大致是需要实现一些基本的运算,别的运算可以表示成这样运算的组合)。 Facilitating New Backend Integration by PrivateUse1 — PyTorch Tutorials 2.2.1+cu121 documentation 这里介绍了使用privateuse1注册新后端的流程。 结合tensorboard的profiler PyTorch Profiler With TensorBoard — PyTorch Tutorials 2.2.1+cu121 documentation 这篇文章介绍了profiler结合tensorboard的使用。 使用Ray Tune进行超参数调优 Hyperparameter tuning with Ray Tune — PyTorch Tutorials 2.2.1+cu121 documentation 这篇文章介绍了怎么使用Ray Tune库进行超参数的搜索。 优化Vision Transformer模型 Optimizing Vision Transformer Model for Deployment — PyTorch Tutorials 2.2.1+cu121 documentation 这篇文章介绍了怎么去优化Vision Transformer模型,列举了一些常见的优化方式。 参数化教程 Parametrizations Tutorial — PyTorch Tutorials 2.2.1+cu121 documentation 这里的参数化指的是类似于对模型部分权重使用的一种限制,应该是在参数修改后调用(比如初始化,参数更新等)的一些nn.module,从而使得模型更加灵活,实现对不同参数的不同处理过程,具体使用过程也是看教程,进行类的注册,使用函数parametrize。 with parametrize.cached():可以开启参数的缓存模型。 可以对于一个参数注册多个参数化模块。 同时参数化模块内部有的函数可以只在特殊时期调用,比如初始化。 可以移除参数化模块。 剪枝教程 Pruning Tutorial — PyTorch Tutorials 2.2.1+cu121 documentation 这里介绍了剪枝和机制,以及钩子函数的引入。 介绍了组合剪枝,通过移除重参数化来永久化剪枝,不同模块针对剪枝,全局剪枝,扩展自己的剪枝函数等。 动态量化 (beta) Dynamic Quantization on an LSTM Word Language Model — PyTorch Tutorials 2.2.1+cu121 documentation (beta) Dynamic Quantization on BERT — PyTorch Tutorials 2.2.1+cu121 documentation 这里介绍了使用torch.quantization.quantize_dynamic动态量化一次常见模型的例子。 计算机视觉的量化迁移学习教程 (beta) Quantized Transfer Learning for Computer Vision Tutorial — PyTorch Tutorials 2.2.1+cu121 documentation 这篇文章介绍了在微调时候插入量化模拟层和反量化层,从而让模型适应量化的过程(QAT)。 PyTorch 中具有 Eager 模式的静态量化 (beta) Static Quantization with Eager Mode in PyTorch — PyTorch Tutorials 2.2.1+cu121 documentation 这个教程演示如何进行训练后静态量化,并说明两种更先进的技术,通道量化和量化感知训练。 从第一性原理挖掘 PyTorch Intel CPU 性能 Grokking PyTorch Intel CPU performance from first principles — PyTorch Tutorials 2.2.1+cu121 documentation Grokking PyTorch Intel CPU performance from first principles (Part 2) — PyTorch Tutorials 2.2.1+cu121 documentation 这里介绍了优化CPU性能的方法和原理,目前来说太高深(挖矿todo 带 Ax 的多目标 NAS Multi-Objective NAS with Ax — PyTorch Tutorials 2.2.1+cu121 documentation 这篇文章介绍了怎么使用Ax平台进行神经网络架构的搜索。 torch.compile介绍 Introduction to torch.compile — PyTorch Tutorials 2.2.1+cu121 documentation 可以使用torch.compile当做装饰器或者函数使用。 同时也列举了几个模式以及和别的几个优化方法的对比。 Inductor CPU backend debugging and profiling Inductor CPU backend debugging and profiling — PyTorch Tutorials 2.2.1+cu121 documentation 这篇文章介绍了怎么对C++后端进行debug和profile的过程。有点复杂,,,(挖坑todo 实现高效缩放点积注意力Transformer (Beta) Implementing High-Performance Transformers with Scaled Dot Product Attention (SDPA) — PyTorch Tutorials 2.2.1+cu121 documentation 这篇文章介绍了怎么优化使用常用的缩放点积注意力。 torch.nested.nested_tensor支持融合不同长度的张量。 知识蒸馏 Knowledge Distillation Tutorial — PyTorch Tutorials 2.2.1+cu121 documentation 这篇教程介绍了知识蒸馏的几个方式,主要都是在损失函数里面添加教师模型和学生模型的各种量化差异来训练学生模型。 分布式(挖坑todo 参考资料 Welcome to PyTorch Tutorials — PyTorch Tutorials 2.2.1+cu121 documentation","categories":[{"name":"杂项","slug":"杂项","permalink":"https://bg51717.github.io/categories/%E6%9D%82%E9%A1%B9/"}],"tags":[{"name":"pytroch_tutorials","slug":"pytroch-tutorials","permalink":"https://bg51717.github.io/tags/pytroch-tutorials/"}]},{"title":"Adam Optimizer","slug":"深度学习/经典模块/Adam-Optimizer","date":"2023-12-29T08:23:43.000Z","updated":"2024-11-02T02:55:49.881Z","comments":true,"path":"/12551/","link":"","permalink":"https://bg51717.github.io/12551/","excerpt":"","text":"背景 传统的随机梯度下降算法SGD(Stochastic Gradient Descent)的式子为: \\[ \\theta_{t+1} \\leftarrow \\theta_{t} -\\alpha \\nabla_{\\theta_{t}}J_{minibatcg}(\\theta_{t}) \\] 其中\\(J\\)为损失函数,\\(\\theta_{t}\\)为t时刻的参数,\\(\\alpha\\)为学习率,\\(\\nabla_{\\theta_{t}}J_{minibatcg}(\\theta_{t})\\)为t时刻梯度. Adam算法初步优化 考虑让梯度更加平滑,有以下式子: $$ m_{t+1} {1}m{t}+(1-{1}){{t}}J{minibatcg}(_{t}) \\ {t+1} {t} -m_{t+1} $$ 优点: 梯度更加平滑,减少了振荡 可以在初始设置更大的学习率 Adam算法最终优化 通过对应\"动量\"这一想法的进一步扩展,我们得到了新的学习算法式子: $$ m_{t+1} _1 m_t + (1 - 1) {t} J{}(t) \\ v{t+1} _2 v_t + (1 - 2)({t} J{}(t) {t} J{}(_t)) \\ _{t+1} _t - $$ 其中\\(\\beta_{1},\\beta_{2}\\)是大小在0和1之间的超参数,\\(\\odot\\)是平方的符号,\\(\\alpha\\)是学习率. 优点: 梯度更加平滑,减少了振荡 可以在初始设置更大的学习率 接收较小或很少更新的模型参数将得到较大的更新 参考资料 11.10. Adam算法 — 动手学深度学习 2.0.0 documentation (d2l.ai) Stanford CS 224N | Natural Language Processing with Deep Learning","categories":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"经典模块","slug":"深度学习/经典模块","permalink":"https://bg51717.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/%E7%BB%8F%E5%85%B8%E6%A8%A1%E5%9D%97/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"优化算法","slug":"优化算法","permalink":"https://bg51717.github.io/tags/%E4%BC%98%E5%8C%96%E7%AE%97%E6%B3%95/"}]},{"title":"依赖分析Dependency Parsing","slug":"深度学习/自然语言处理/依赖分析Dependency-Parsing","date":"2023-12-27T14:21:54.000Z","updated":"2024-11-02T02:55:49.890Z","comments":true,"path":"/26708/","link":"","permalink":"https://bg51717.github.io/26708/","excerpt":"","text":"介绍 这里介绍两种语义结构: Constituency Parsing:句法分析,Context-free grammars(CFGs),上下文无关语法,赋予每个单词一个词性类别,单词组合成短语,短语递归形成更大的短语 Dependency Parsing:直接通过单词与其他单词的关系表示句子的结构,表示单词依赖于(修饰或是其参数)其他单词 Dependency Parsing缺陷 不同的自然语言有不同的组织结构,每种自然语言都有独特的二义问题(ambiguity),即同一个句子通过不同Dependency Parsing分析会得到不同语义树,进而得到不同的语句意思. 总的可能的语义数目极限情况下大概是随着字符串的提示可能是指数级增加,所以有一些方法来解决这些问题. Dependency Structure 通过一个单向边指明依赖关系(同时这个边也会指明依赖关系的种类),进而组成一个树状结构,通常会添加一个虚拟的\"ROOT\"节点作为根节点 Greedy transition-based parsing 初始状态:\\(\\sigma=[ROOT],\\beta=w_1,...,w_n,A=\\empty\\) 三种操作: Shift : 从缓存区\\(\\beta\\)里面移入一个词到栈\\(\\sigma\\)里面 Left-Arc : 将\\((w_j,r,w_i)\\)加入边集合\\(A\\) ,其中\\(w_i\\)是stack上的次顶层的词,\\(w_j\\)是stack上的最顶层的词,然后保留\\(w_j\\)在栈中(堆必须包含两个单词以及 \\(w_i\\)不是 ROOT ) Right-Arc : 将\\((w_i,r,w_j)\\)加入边集合\\(A\\) ,其中\\(w_i\\)是stack上的次顶层的词,\\(w_j\\)是stack上的最顶层的词,然后保留\\(w_i\\)在栈中(堆必须包含两个单词以及 \\(w_i\\)不是 ROOT ) 重复上述操作到指定目标. 实际应用的时候,如何判断选择哪一种操作可以通过机器学习的方式来判断,也可以加入集束搜索来寻找最优解. 评判标准: UAS:找准依赖项的词的比例 LAS:在UAS基础上,还要求边的属性得准确 示例: 参考资料 2021斯坦福CS224N课程笔记~2_context-free grammars and constituency parsing-CSDN博客","categories":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"自然语言处理","slug":"深度学习/自然语言处理","permalink":"https://bg51717.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"自然语言处理","slug":"自然语言处理","permalink":"https://bg51717.github.io/tags/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/"}]},{"title":"矩阵偏分","slug":"数学/线性代数/矩阵偏分","date":"2023-12-24T07:18:43.000Z","updated":"2024-11-02T02:55:49.880Z","comments":true,"path":"/45525/","link":"","permalink":"https://bg51717.github.io/45525/","excerpt":"","text":"在学习深度学习的过程中,很多次遇到了矩阵求导的问题,发现网上很多教程写的不是很好理解,记录自己的浅薄认知. (矩阵求导有分子转置(和分子行数一样)和分母转置(和分母行数一样)两种,两种结果互为转置。本篇介绍的不太一样,因为很多教程也是混杂的(太难蚌了)。 符号 标量:\\(x\\) 向量:\\(\\mathbf{x}\\) () 矩阵:\\(X\\) 逐点乘积:\\(\\odot\\)() 矩阵的迹:\\(tr(X)\\) 加入有两个矩阵\\(A_{m\\times n},B_{m\\times n}\\),其中一个转置乘以另一个得到矩阵\\(C\\),该矩阵的迹为两个矩阵对应位置的元素相乘并相加 ,可以理解为向量的点积在矩阵上的推广,即: \\[ \\begin{align*} \\text{tr}\\left( AB^T \\right) &= a_{11}b_{11} + a_{12}b_{12} + \\cdots + a_{1n}b_{1n} \\\\ &+ a_{21}b_{21} + a_{22}b_{22} + \\cdots + a_{2n}b_{2n} \\\\ &+ \\cdots \\\\ &+ a_{m1}b_{m1} + a_{m2}b_{m2} + \\cdots + a_{mn}b_{mn} \\\\ \\end{align*} \\] 矩阵微分规律 梯度矩阵里的微分: 假设\\(A\\)矩阵维度列表为\\([a_1,a_2,...,a_n]\\) 假设\\(B\\)矩阵维度列表为\\([b_1,b_2,...,b_{m}]\\) \\(A\\)矩阵对\\(B\\)矩阵求偏导的结果\\(C\\),维度上相当于\\(A\\)矩阵维度列表和\\(B\\)矩阵维度列表连接起来 即\\(C\\)矩阵维度列表为\\([a_1,a_2,...,a_n,b_1,b_2,...,b_{m}]\\) 根据维度信息也能很方便的推导各个元素的含义,即为\\(A\\)矩阵的每个元素对\\(B\\)矩阵的每个元素求一个偏导。 当然,高维的矩阵微分应用场景有限,我们很少需要求一个矩阵关于另一个矩阵的偏导。因此这个规律可能因为博主实践较少有误。 在深度学习的场景中,一般是标量对矩阵求导,因此主要介绍标量对矩阵微分的情况。 一般对于向量,默认为列向量,\\(\\mathbf{x}=[x_1,x_2,...,x_n]^{T}\\),我们有标量函数\\(f(\\mathbf{x})\\),那么对应的梯度为 \\[ \\frac{\\partial f}{\\partial \\mathbf{x}}=[\\frac{\\partial f}{\\partial x_1},\\frac{\\partial f}{\\partial x_2},..\\frac{\\partial f}{\\partial x_n}]^\\top \\] 根据这个公式我们可以发现,这个梯度向量的每个元素都是函数对该元素的偏导。 因此,我们有, \\[ \\partial f = (\\frac{\\partial f}{\\partial \\mathbf{x}})^\\top \\partial \\mathbf{x} \\] 对于标量对向量到梯度来说,在左边乘以其梯度的转置就可以对应得到目标的微分。 对于矩阵来说,逐点乘积\\(\\odot\\)然后对应位置求和应该是更广泛的规律。但向量是特殊的,逐点乘积求和和转置相乘的结果是一样的。 对于函数值为标量,变量为二维矩阵的情况,函数值的微分是梯度矩阵和变量矩阵的转置乘积的迹。 \\[ df(X)=tr((\\frac{df(X)}{dX})^\\top \\cdot X) \\] 雅可比矩阵 雅可比矩阵指的是向量对于向量的微分,假如我们有: \\[ F:R^{N}\\to R^{M} ,\\mathbf{y}=F(\\mathbf{x}) \\] 那么雅克比矩阵为, \\[ J_{F:\\mathrm{x\\to y}}= \\left[ \\begin {array}{c c c} \\frac{\\partial y_1}{\\partial x_1}&&\\frac{\\partial y_1}{\\partial x_N}\\\\ &&\\\\ \\frac{\\partial y_M}{\\partial x_1}&&\\frac{\\partial y_M}{\\partial x_N}\\\\ \\end{array} \\right] \\] 其中第\\(i\\)行是\\(y_i\\)关于\\(\\mathbf{x}\\)的微分的转置。 我们有一阶泰勒展开式: \\[ F({\\bf{x}}+d{\\bf{x}})=F({\\bf{x}})+J_{F:x \\to y}d{\\bf{x}}+o(\\|d{\\bf{x}}\\,\\|). \\] Hessian 在向量微积分中,Hessian 矩阵用于表示标量关于向量的二阶偏导数。 假如我们有 \\[ f(\\mathbf{x}) : \\mathbb{R}^N \\to \\mathbb{R} \\] 那么我们有, \\[ H_{x \\to f} = \\frac{d^2 f}{dx \\, dx^\\top} = \\left[ \\frac{\\partial f}{\\partial x_i \\partial x_j} \\right] \\] 通过定义也可以看出,Hessian 矩阵可以写成一阶导数构成的向量关于自变量的雅可比矩阵。 令\\(f\\)关于变量的一阶梯度为, \\[ \\mathbf{a}(\\mathbf{x}) = \\left[ \\frac{\\partial f}{\\partial x_i} \\right] \\] 那么hessian矩阵还可以写为, \\[ H_{x \\to f} = \\frac{d\\mathbf{a}}{d\\mathbf{x}} = \\left[ \\frac{d a_i}{d x_j} \\right] = \\left[ \\frac{\\partial f}{\\partial x_i \\partial x_j} \\right] \\] 对应的,我们也能得到二阶泰勒展开式, \\[ f(\\mathbf{x} + d\\mathbf{x}) = f(\\mathbf{x}) + \\mathbf{a}^\\top d\\mathbf{x} + \\frac{1}{2} d\\mathbf{x}^\\top H_{x \\to f} \\, d\\mathbf{x} + o(\\|d\\mathbf{x}\\|^2) \\] 这里的二次项的推导方式为: \\[ \\begin{align*} &\\frac{1}{2}(\\partial \\mathbf{a})^\\top\\partial \\mathbf{x} \\\\ =&\\frac{1}{2}((\\frac{\\partial \\mathbf{a}}{\\partial \\mathbf{x}})^\\top \\partial \\mathbf{x})^\\top\\partial \\mathbf{x} \\\\ =&\\frac{1}{2} d\\mathbf{x}^\\top \\frac{\\partial \\mathbf{a}}{\\partial \\mathbf{x}} \\ d\\mathbf{x} \\\\ =&\\frac{1}{2} d\\mathbf{x}^\\top H_{x \\to f} d\\mathbf{x} \\\\ \\end{align*} \\] 根据这个例子我们也能很容易总结规律并且推广到更多公式。(从定义出发推导式子,向量乘以自己的梯度转置获得目标的微分) 链式表达式 假设我们有, \\[ \\begin{align*} y &= f(g(h(x))) \\\\ q &= h(x) \\\\ k &= g(q) \\\\ y &= f(k) \\end{align*} \\] 我们希望得到 \\[ \\begin{align*} &\\partial y \\\\ =& (\\frac{\\partial y}{\\partial k})^\\top \\partial k \\\\ =& (\\frac{\\partial y}{\\partial k})^\\top (\\frac{\\partial k}{\\partial q})^\\top \\partial q \\\\ =& (\\frac{\\partial y}{\\partial k})^\\top (\\frac{\\partial k}{\\partial q})^\\top (\\frac{\\partial q}{\\partial x})^\\top \\partial x \\\\ =& (\\frac{\\partial q}{\\partial x} \\frac{\\partial k}{\\partial q} \\frac{\\partial y}{\\partial k})^\\top \\partial x \\\\ \\end{align*} \\] 所以我们有 \\[ \\frac{\\partial y}{\\partial x} = \\frac{\\partial q}{\\partial x} \\frac{\\partial k}{\\partial q} \\frac{\\partial y}{\\partial k} \\] 这个推导前置条件只有:向量左乘其梯度的转置就可以对应得到目标的微分。 别的布局的推导方式也类似。 在数学公式的推导中,链式法则的记忆要点为:能乘维数对就对(可能维度信息其实就包含了部分信息)。 行向量偏导形式 有一种操作把矩阵变成按列堆栈向量化\\(vec\\),然后进行处理,就可以利用部分在低维中的规律进行计算: \\[ vec(X)=[x_{11},x_{21},..,x_{12},x_{22},...,x_{1n},x_{2n},...,x_{nm}]^T \\] 更多介绍阅读矩阵论、弗罗贝尼乌斯内积、克罗内克积等相关资料。 因为博主也不会,只是计算机系的,在深度学习里不会用到所有的数学知识。 合适做法 合适的做法还是对于矩阵的每个元素单独看梯度和贡献,然后根据指标的变化来总结公式。 比如,计算\\(x_{ij}\\)是如何影响\\(y_{ik}\\)的,然后总结梯度公式。 示例1: \\[ Y=X*A,而X \\in R^{n \\times p}, A \\in R^{p \\times m},Y \\in R^{n \\times m},l为标量 \\\\ 已知\\frac{\\partial l}{\\partial Y},求\\frac{\\partial l}{\\partial X} \\\\ 计算x_{ik}的贡献,有 \\frac{\\partial l}{\\partial x_{ik}}=\\sum_{j}a_{kj}\\frac{\\partial l}{\\partial y_{ij}} \\\\ 根据维度信息(i在左侧,k在右侧)总结,得到\\frac{\\partial l}{\\partial X}=\\frac{\\partial l}{\\partial Y}A^T \\] tip:由于一维向量很多时候会被写为列向量,所以有的教程理解不是很方便,但如果接触过pytorch框架,会方便理解很多. 参考资料 矩阵求导的本质与分子布局、分母布局的本质(矩阵求导——本质篇) - 知乎 (zhihu.com) 矩阵求导公式的数学推导(矩阵求导——基础篇) - 知乎 (zhihu.com) 矩阵求导公式的数学推导(矩阵求导——进阶篇) - 知乎 (zhihu.com) 【ML-0-3】矩阵求导-链式法则 - 忆凡人生 - 博客园 (cnblogs.com) [机器学习-数学] 矩阵求导(分母布局与分子布局),以及常用的矩阵求导公式 矩阵论——导数与梯度_矩阵的梯度-CSDN博客","categories":[{"name":"数学","slug":"数学","permalink":"https://bg51717.github.io/categories/%E6%95%B0%E5%AD%A6/"},{"name":"线性代数","slug":"数学/线性代数","permalink":"https://bg51717.github.io/categories/%E6%95%B0%E5%AD%A6/%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"数学","slug":"数学","permalink":"https://bg51717.github.io/tags/%E6%95%B0%E5%AD%A6/"},{"name":"矩阵","slug":"矩阵","permalink":"https://bg51717.github.io/tags/%E7%9F%A9%E9%98%B5/"},{"name":"向量","slug":"向量","permalink":"https://bg51717.github.io/tags/%E5%90%91%E9%87%8F/"}]},{"title":"GloVe","slug":"深度学习/自然语言处理/GloVe","date":"2023-12-22T08:36:28.000Z","updated":"2024-11-02T02:55:49.882Z","comments":true,"path":"/51558/","link":"","permalink":"https://bg51717.github.io/51558/","excerpt":"","text":"介绍 相比Word2Vec的局部训练,GloVe可以使用全局的语料统计信息来训练,可以预先计算全局语料库统计数据来提高训练数据. 带全局语料统计的跳元模型 用\\(q_{ij}\\)表示词\\(w_j\\)的条件概率\\(P(w_j|w_i)\\),在跳元模型中给定词\\(w_i\\),我们有 \\[ q_{ij}=\\frac{exp(\\bm u^T_j\\bm v_i)}{\\sum_{k \\in V}exp(\\bm u^T_k\\bm v_i)} \\] 记录\\(w_i\\)在所有出现地方的上下文构成一个可重集合\\(C_i\\),其中元素j的数目记为\\(x_{ij}\\),损失函数定义为 \\[ -\\sum_{i\\in V}\\sum_{j\\in V}x_{ij}log\\ q_{ij} \\] 用\\(x_i\\)表示\\(|C_i|\\),用\\(p_{ij}\\)表示概率\\(x_{ij}/x_i\\),则损失函数可以改写为 \\[ -\\sum_{i\\in V}x_i\\sum_{j\\in V}p_{ij}log\\ q_{ij} \\] 我们发现\\(-\\sum_{j\\in V}p_{ij}log\\ q_{ij}\\)就是计算全局语料统计的条件分布\\(p_{ij}\\)和模型预测的条件分布\\(q_{ij}\\)的交叉熵。 GloVe模型 考虑到计算成本的问题,以及大量罕见事件被损失建模,GloVe模型基于平方损失对模型进行了以下修改: 使用变量\\(p^{'}_{ij}=x_ij\\)和\\(q^{'}_{ij}=exp(\\bm u^T_{j}\\bm v_i)\\),并取两者对数,所以平方损失为\\((log\\ p^{'}_{ij}-log\\ q^{'}_{ij})^2=(\\bm u^T_{j}\\bm v_i-log\\ x_{ij})^2\\). 为每个词\\(w_i\\)添加中心词偏执\\(b_i\\)和上下文词偏置\\(c_i\\) 用权重函数\\(h(x)\\)替代损失项权重,其中\\(h(x)\\)在[0,1]的间隔内递增. 最后总的损失函数为: \\[ \\sum_{i\\in V}\\sum_{j\\in V}h(x_{ij})(\\bm u^T_{j}\\bm v_i+b_i+c_j-log\\ x_{ij})^2 \\] 由于对称性,有\\(x_{ij}=x_{ji}\\),而Word2Vec没有拟合对称性,GloVe拟合了对称性.因此,在GloVe模型中,词的上下文向量\\(u_i\\)和中心词向量\\(v_i\\)应该是相等的,但在实际应用中,由于训练过程的随机化导致两个向量有轻微不同,因此GloVe将两者相加作为输出向量. 参考资料1也提供了另一种理解GloVe模型的思路,和cs224n里面类似. 参考资料 14.5. 全局向量的词嵌入(GloVe) — 动手学深度学习 2.0.0 documentation (d2l.ai)","categories":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"自然语言处理","slug":"深度学习/自然语言处理","permalink":"https://bg51717.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"自然语言处理","slug":"自然语言处理","permalink":"https://bg51717.github.io/tags/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/"}]},{"title":"Word2Vec","slug":"深度学习/自然语言处理/Word2Vec","date":"2023-12-21T08:53:05.000Z","updated":"2024-11-02T02:55:49.882Z","comments":true,"path":"/5925/","link":"","permalink":"https://bg51717.github.io/5925/","excerpt":"","text":"介绍 传统的词向量是独热编码One-Hot,每个词语对应的向量只有一位为1,其余为0,通过这个1的位置区分向量 Word2Vec是一种Distributed Representation,相比独热编码有以下优点: 维度远小于独热编码 能更好的反馈向量间的关系 每个维度都被充分利用 Word2Vec的网络结构 Word2Vec是轻量级的神经网络,其模型仅仅包括输入层、隐藏层和输出层,模型框架根据输入输出的不同,主要包括CBOW和Skip-gram模型。 CBOW模型,在知道上下文的情况下预测中心词\\(w_t\\) Skip-gram模型,在知道中心词\\(w_t\\)的情况下预测 网络结构基础 下图是两种模型结构的基础,是输入一个词,预测一个词 中间的运算都会使用矩阵方便加速: 一个词语转换成对应的词向量,就会使用独热编码乘以这个\\(W_{V\\times N}\\),(其中V为词库大小,N为词向量维度数) 一个词向量和别的词的上下文向量点乘,也会乘以矩阵\\(W^{'}_{V\\times N}\\),得到\\(\\bm y\\),也就是\\(\\bm u^T\\cdot \\bm v\\) 之后词语k的概率就是softmax: \\[ P(w_k|w_c)=\\frac{\\exp(\\bm u^T_k\\cdot \\bm v_c)}{\\sum \\exp(\\bm u^T_i\\cdot \\bm v_c)}=\\frac{\\exp(\\bm y_k)}{\\sum \\exp(\\bm y_i)} \\] 之后就是追求句子概率最大化: \\[ \\prod P(w_k|w_c) \\] 对数化后取负数就得到了损失函数: \\[ Loss=\\sum_{o \\in context}( \\bm u^T_k\\cdot \\bm v_c-log(\\sum_{i \\in V}exp(\\bm u^T_i\\cdot \\bm v_c))) \\] 通过微分,我们可以获得其相对于中心词向量\\(\\bm v_c\\)的梯度为 其他向量的方法也是类似.这里就不给予推导. CBOW 词袋模型 用上下文预测中心词\\(w_t\\) Skip-gram Model 跳元模型 用中心词\\(w_t\\)预测上下文 近似训练 由于softmax操作的性质,上下文词可以是词表V中的任意项,式子包含与整个词表大小一样多的项的求 和。因此,跳元模型的梯度计算和连续词袋模型的梯度计算都包含求和。不幸的是, 在一个词典上(通常有几十万或数百万个单词)求和的梯度的计算成本是巨大的! 为了降低上述计算复杂度,引入两种近似训练方法:负采样和分层softmax。由于跳元模型和连续词袋 模型的相似性,将以跳元模型为例来描述这两种近似训练方法。 负采样Negative Sampling 负采样指的是加入一些负样本来进行模型的训练. 负采样修改了原目标函数来减少运算成本. 给定中心词\\(w_c\\)的上下文窗口,任意上下文词\\(w_o\\)来自该上下文窗口的被认为是由下式建模概率的事件: \\[ P(D=1|w_c,w_o)=\\sigma(\\bm u^T_o\\bm v_c) \\] 其中 \\[ \\sigma(x)=\\frac{1}{1+exp(-x)} \\] 考虑最大化联合概率 \\[ \\prod^T_{t=1}\\prod_{-m\\leqslant j \\leqslant m,j\\neq 0} P(D=1|w_t,w_{t+j}) \\] 然而这样只考虑了正样本,当且仅当所有词向量都等于无穷大的时候,这个式子会最大化为1,因此考虑加入一些噪声词作为负样本. \\[ \\prod^T_{t=1}\\prod_{-m\\leqslant j \\leqslant m,j\\neq 0} P(w_{t+j}|w_t) \\\\ 其中 P(w_{t+j}|w_t)=P(D=1|w_t,w_{t+j})\\prod^K_{k=1,w_k\\thicksim P(w)}P(D=0|w_t,w_k) \\] 关于条件概率的对数损失函数为: 分层softmax Hierarchical Softmax 这是一颗二叉树,从根节点开始,每次走左节点还是右节点都是概率时间,每个叶子节点都是词,所以根据中心词向量预测上下文可以看作是从根节点到叶子节点路径的概率乘积. 一种构造二叉树的方法是根据词语的出现频率构造哈夫曼树. 在非叶子节点\\(i\\)走右边的概率是 \\[ \\sigma(\\bm x^T_w \\bm \\theta)=\\frac{1}{1+eps(-\\bm x^T_w \\bm \\theta)} \\] 根据中心词\\(w_t\\)预测上下文\\(w_o\\)的概率也很就容易得到. 参考资料 第一篇写的最为详细全面,包括本文各种没有提到的细节,点赞! 参考资料里面第一篇和第三篇其实说的是一个东西,只不过一个是以矩阵的形式给出的. 可以通过奇异值分解减少词的特征维度. 深入浅出Word2Vec原理解析 - 知乎 (zhihu.com) 如何通俗理解Word2Vec (23年修订版)-CSDN博客 14.1. 词嵌入(word2vec) — 动手学深度学习 2.0.0 documentation (d2l.ai)","categories":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"自然语言处理","slug":"深度学习/自然语言处理","permalink":"https://bg51717.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"自然语言处理","slug":"自然语言处理","permalink":"https://bg51717.github.io/tags/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/"}]},{"title":"readme","slug":"课程资源/readme","date":"2023-12-08T15:46:44.000Z","updated":"2024-11-02T02:55:49.931Z","comments":true,"path":"/17140/","link":"","permalink":"https://bg51717.github.io/17140/","excerpt":"","text":"前言 当大四结束的时候,才意识到大学过的十分失败,很多计算机的核心知识其实都是处于一知半解的状态,动手能力也还是有很大的差距.即使在大一的时候,就已经在知乎上面刷到过不少计科混子的自我吐槽和悔恨,并且立志不要这么做,但大四上结束后,发现终究还是浪费了重要的四年和对重要知识的追求. 所以希望能通过一些大神的网上课程资源的推荐,弥补自己计算机教育的不足,希望亡羊补牢,为时未晚. 介绍 首先打算参考cswiki上面的课程和相关资源学习理论知识和提高动手能力. 而本目录大概包括各个课程的: 学习笔记 作业代码 工程代码 参考资料 cswiki","categories":[{"name":"课程资源","slug":"课程资源","permalink":"https://bg51717.github.io/categories/%E8%AF%BE%E7%A8%8B%E8%B5%84%E6%BA%90/"}],"tags":[]},{"title":"文献管理工具zotero","slug":"工具/文献管理工具zotero","date":"2023-11-22T02:12:21.000Z","updated":"2024-11-02T02:55:49.875Z","comments":true,"path":"/43151/","link":"","permalink":"https://bg51717.github.io/43151/","excerpt":"","text":"这里推荐一个文献管理工具,zotero,很好用的文献管理工具,也能很好的辅助写论文,获取文献,免费开源且支持插件,可以参考这个b站up的视频搭建和使用这个工具。 参考资料 Zotero零基础保姆级教程","categories":[{"name":"工具","slug":"工具","permalink":"https://bg51717.github.io/categories/%E5%B7%A5%E5%85%B7/"}],"tags":[{"name":"工具","slug":"工具","permalink":"https://bg51717.github.io/tags/%E5%B7%A5%E5%85%B7/"},{"name":"文献管理","slug":"文献管理","permalink":"https://bg51717.github.io/tags/%E6%96%87%E7%8C%AE%E7%AE%A1%E7%90%86/"}]},{"title":"论文阅读:A_Survey_of_Quantization_Methods_for_Efficient_Neural_Network_Inference","slug":"科研/论文阅读/论文阅读~A_Survey_of_Quantization_Methods_for_Efficient_Neural_Network_Inference","date":"2023-10-23T12:10:11.000Z","updated":"2024-11-08T15:37:17.484Z","comments":true,"path":"/62023/","link":"","permalink":"https://bg51717.github.io/62023/","excerpt":"","text":"论文地址:A Survey of Quantization Methods for Efficient Neural Network Inference 摘要 这篇论文是关于模型量化方向的综述,介绍了量化相关领域的研究,同时也介绍了一些重要的论文。 提到了量化的好处: 加快模型的推理过程 减少模型的存储开销 可以部署到特定设备(比如有的设备只能进行整型运算) 介绍 过去模型的能力有了巨大的提升,其中一个原因就是模型参数的增加,但这也为模型的部署提升了难度。 过去几年关于这方面的尝试有如下几种: 设计高效的神经网络模型架构,包括微观结构和宏观结构 神经网络软件和硬件协同设计 剪枝,减去对模型影响小的参数,分为结构化剪枝(删去部分参数)和非结构化剪枝(删去部分结构,相当于参数按组删去),两个方法各有优劣 知识蒸馏,用“教师”模型训练“学生”模型,可与前面方法结合 量化:分为训练中量化和推理量化 量化和神经科学的发展 这里很多次提到了AutoML和NAS技术调整网络结构 量化历史 量化,作为一种从大的(通常是连续的)集合中的输入值映射到小的(通常是有限的)集合中的输出值的方法,具有很长的历史。早在1897年就有微积分相关工作从事量化研究,同时,量化在信号处理中也非常重要。 香农在信息学和信号处理的研究过程中也针对量化进行过研究。同时,量化在连续数学量的数值求解中研究过。一些问题的良好解法可能由于数字量化会导致巨大误差,从而引出了算法数值稳定性的概念。 神经网络的发展给量化带来了机遇。神经网络的计算是计算密集型任务且目前很多模型都过度参数化。很多问题的解决都是基于一个某种误差度量,因此量化可能会增大误差,但同时也会增加模型的鲁棒性。 量化的基本概念 问题设置和符号 在不失一般性的情况下,让我们关注监督学习问题,即最小化下面所示的损失函数。 \\[ L(\\theta)=\\frac{1}{N} \\sum^{N}_{i=1}l(x_i,y_i;\\theta) \\] (x,y)是输入数据和对应的标签,$ l(x_i,y_i;)$是损失函数,N是数据数目 同时让我们把第\\(i\\)层的输入隐藏激活表示为\\(h_i\\),输出隐藏激活表示为\\(a_i\\),我们假设浮点格式存储的模型参数为\\(\\theta\\). 均匀量化 均匀量化和非均匀量化 均匀量化的一个常用的函数是 \\[ Q(r)=Int(\\frac{r}{S})-Z \\] \\(Q\\)是压缩函数,\\(r\\)是实数类型的输入,\\(S\\)是实数类型的比例因子,\\(Z\\)是整数零点,\\(Int\\)函数通过舍入操作把实数映射到整数 去量化操作 \\[ Q(\\tilde{r})=S(Q(r)+Z) \\] 由于舍入操作\\(\\tilde{r}\\)和\\(r\\)不会严格相等 对称和非对称量化 对称和非对称量化(观察量化前后零点位置 确定放缩因子\\(S\\)的式子为 \\[ S=\\frac{\\beta-\\alpha}{2^{b}-1} \\] \\([\\alpha,\\beta]\\)代表剪切范围,\\(b\\)代表量化位宽. 确定\\([\\alpha,\\beta]\\)的两个方式: \\(\\alpha=r_{min},\\beta=r_{max}\\) \\(-\\alpha=\\beta=max(|r_{min}|,|r_{max}|)\\) 利用实数的最大最小位选定裁剪范围可能会容易被异常数据影响,从而增加不必要的范围.解决这个问题的一种方法是使用百分位数,另一种方法是选择\\(α\\)和\\(β\\),以最小化真实值和量化值之间的KL散度(即信息损失).也有学者对不同的量化范围选取范围方法进行了评估. 确定\\(S\\)的两个方式: \\(\\frac{2max(|r|)}{2^n-1}\\) \\(\\frac{max(|r|)}{2^{n-1}-1}\\) 对称量化使用广泛,因为可以把零点降为0,减少计算成本并且实现更加简单;非对称量化对于范围可能是倾斜的和不对称的情况表现会更加优秀. 非对称激活中的偏移而占据的交叉项是一个静态数据独立项并且可以被偏差吸收(或用于初始化累加器). 范围校准算法:静态与动态量化 动态量化:运行期间计算量化参数,高精度,高开销 静态量化:量化参数预先确定,推理期间为静态,低开销,低精度 量化粒度 分层量化:通过一整个层的数值来计算量化参数,实现简单,精度次优 分组量化:把每一层的多个通道进行分组量化,有助于解决单个通道、激活分布离散的情况,但是计算开销会增加 分通道量化:每一层的每个通道进行量化,更好的精度,更高的计算成本 分卷积核(滤波器)量化:输入通道为\\(n\\),输出通道为\\(m\\),那么应该会有\\(n*m\\)个卷积核,根据卷积核量化会有更高的精度 总结(量化粒度)。通道量化是目前用于量化卷积核的标准方法。它使从业者能够以可忽略不计的开销来调整每个单独的内核的剪切范围。相比之下,子信道量化可能会导致巨大的开销,而且目前还不是标准的选择。 非均匀量化 量化步骤和量化水平被允许是非均匀间隔的 \\[ Q(r)=X_i, \\quad if\\quad r \\in [r_i,r_{i+1}] \\] 非均匀量化对于固定的位宽,可以获得更高的精度 典型的有 钟型分布 对数分布 二进制码,把一个向量拆成多个基向量的和,每个基向量的每个维度的值的绝对值为1 很多把量化问题转化为优化问题,减少原始张量r和量化后张量Q(r)的差异 \\[ \\underset{Q}{min}||Q(r)-r|| \\] 此外,量化器本身也可以和模型参数一起学习,称之为可学习的量化器 还有一些工作使用聚类来减少量化损失 非均匀量化能更好的捕获信息,但是计算成本更高,因此目前主流的还是均匀量化 微调方法 量化可能需要对参数进行微调,有两种方式: 量化感知训练QAT 训练后量化PTQ 左边是QAT,右边是PTQ QAT QAT一种方法展示 反向传播方法有: STE 随机神经元 组合优化 目标传播 Gumbelsoftmax 正则化算子来强制执行要量化的权重(量化过程没有不可微分算符) 也可以考虑量化感知训练的过程种学习量化参数 PTQ PTQ的流程: 由于PTQ不需要训练,所以对数据集的依赖比较低,当然,精度也会下降 因此,PTQ的研究重点都在于减轻PTQ的精度下降: ACIQ 解析地计算了PTQ的最佳剪切范围和通道级位宽设置 OMSE方法在激活时去除信道级量化,并提出通过优化量化张量与相应的浮点张量之间的L2距离来进行PTQ 一种离群值信道分裂(OCS)方法,该方法将包含离群值的信道重复和减半,缓解离群值对PTQ的不利影响 AdaRound表明,简单的圆到最近的量化方法(round-to-nearest)可以反直觉地得到次优解,并且提出了一种自适应四舍五入的方法 AdaQuant提出了一种更通用的方法,允许量化权值根据需要进行变化。 在PTQ中,所有的权值和激活量化参数都是不需要再训练而确定的。因此,PTQ是一种非常快速的神经网络模型量化方法。然而,与QAT相比,这往往以较低的准确性为代价。 Zero-shot Quantization(ZSQ) PTQ的极端场景,量化过程中不使用数据 Level 1: 没有数据且没有微调 (ZSQ + PTQ). Level 2: 没有数据但需要微调 (ZSQ +QAT). ZSQ中一个流行的研究分支是生成与类似于真实数据的合成数据,从中训练目标预先训练的模型。 随机量化 在推理过程中,量化方案总是确定的,小的权重更新可能不会导致任何权重变化,因为舍入操作可能总是返回相同的权重。然而,启用一个随机舍入可能为神经网络提供一个随机的机会,从而更新其参数。 比如,在有的论文里面,INT操作定义为 \\[ INT(x)=\\begin{cases} \\lfloor x \\rfloor ,with \\quad probability \\quad \\lceil x \\rceil-x\\\\ \\lceil x \\rceil ,with \\quad probability \\quad x-\\lfloor x \\rfloor\\\\ \\end{cases} \\] 有的会选择在量化的适合选择随机从量化参数权重子集里选一个进行量化运算 ADVANCED CONCEPTS: QUANTIZATION BELOW 8 BITS 模拟和纯整数量化 部署量化神经网络模型有两种常见方法: 模拟量化(又名假量化):模拟量化中,量化后的模型参数以低精度存储,但运算(例如矩阵乘法和卷积)是用浮点运算进行的,运算之前需要对量化参数进行反量化 纯整数量化(又名定点量化):所有运算都是使用低精度整数算术执行 如下图所示,硬件对低精度的处理会更好,并且低精度能量和面积方面的效率明显更高。 (这里稍微介绍了对于各种激活函数的量化方法,可以留意一下? 二进量化(Dyadic quantization)是一种特殊的量化方法,通过把权重量化为二元数(二元数是分子中具有整数值、分母中具有 2 的幂的有理数),从而把运算转化为加减与位移从而提高效率。 两种量化种纯整数量化应用较多,但是假量化在通信成本高于计算成本的时候也有一定的应用前景。 混合精度量化 使用较低精度的量化时,硬件性能会提高。然而,将模型统一量化为超低精度可能会导致精度显着下降,可以通过混合精度量化来解决这个问题。 每一层都以不同的位精度进行量化,如下图所示。这种方法的一个挑战是,用于选择此位设置的搜索空间与层数成指数关系。人们提出了不同的方法来解决这个巨大的搜索空间。 搜索方法有: 强化学习 转化为神经架构搜索NAS使用DNAS解决 另一类混合精度方法使用周期函数正则化来训练混合精度模型,方法是在学习各自的位宽时自动区分不同的层及其在准确性方面的不同重要性。 HAWQ引入了一种基于模型二阶灵敏度自动查找混合精度设置的方法。 硬件感知量化 量化带来的性能提升和硬件有着密切关系,比如带宽设置、缓存结构等。因此,通过硬件感知量化实现最佳效益十分重要。 蒸馏辅助量化 量化领域的一个有趣的工作是结合模型蒸馏来提高量化精度。在学生模型的训练过程中,模型蒸馏建议利用教师产生的软概率,而不是仅使用真实类别标签,其中可能包含更多的输入信息。 \\[ \\mathcal{L}=\\alpha\\mathcal{H}(y,\\sigma(\\mathcal{z}_{s}))+\\beta\\mathcal{H}(\\sigma(\\mathcal{z}_{t},T),\\sigma(\\mathcal{z}_{s},T)) \\] α 和 β 是调整学生模型损失量和蒸馏损失的加权系数,y 是真实类别标签, \\(\\mathcal{H}\\)是交叉熵损失函数,\\({\\mathcal{z}}_{s}/{\\mathcal{z}}_{t}\\)是学生/教师模型生成的概率,T是温度。 \\[ p_{i}=\\frac{\\exp\\frac{z_{i}}{T}}{\\sum_{j}\\exp\\frac{z_{j}}{T}} \\] 然后有许多关于蒸馏过程的尝试,比如使用软概率、中间层数据、多教师模型等。 极致量化 这里提到了一些极端的量化方法,比如二值化和三值化等,但是极致的量化也会带来巨大的精度损失。因此有许多工作是关于极值量化的。目前大致有三个分支: 量化误差最小化 改进损失函数 改进训练方法 矢量量化 量化的目标是保持精度,而不是单个值的差异。因此有相关工作是把权重聚类分组然后使用中心作为量化值。还可以扩展为矩阵的乘积量化,把矩阵按照子矩阵分组。 量化和硬件处理器 这里罗列了一些处理器,并且介绍了他们的特点。 量化的未来研究 介绍了未来可以研究的几个东西: 量化软件 硬件和神经网络架构协同设计 耦合压缩方法 量化训练 总结和结论 ... 参考资料 A Survey of Quantization Methods for Efficient Neural Network Inference","categories":[{"name":"科研","slug":"科研","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/"},{"name":"论文阅读","slug":"科研/论文阅读","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/%E8%AE%BA%E6%96%87%E9%98%85%E8%AF%BB/"}],"tags":[{"name":"量化","slug":"量化","permalink":"https://bg51717.github.io/tags/%E9%87%8F%E5%8C%96/"}]},{"title":"论文阅读:BitNet_Scaling_1-bit_Transformers_for_Large_Language_Models","slug":"科研/论文阅读/论文阅读~BitNet-Scaling-1-bit-Transformers-for-Large-Language-Models","date":"2023-10-23T12:10:11.000Z","updated":"2024-11-11T14:10:32.913Z","comments":true,"path":"/8948/","link":"","permalink":"https://bg51717.github.io/8948/","excerpt":"","text":"摘要 这篇论文展示了BitNet,一种为大型语言模型设计的可扩展和稳定的1-bit转换器架构。具体地说,引入了BitLinear作为nn.Linar的插入替代,以便从头开始训练1-bit的权重。在语言建模上的实验结果表明,与最先进的8-bit量化方法和FP16 Transformer相比,BitNet在显著降低内存占用和能量消耗的同时,实现了强力的性能。此外,BitNet还展示了一种类似于全精度Transformers的scaling low,这表明它可以在保持效率和性能效益的同时有效地扩展到更大的语言模型。 图1: BitNet从头开始训练1-bit Transformers,以一种高效的方式获得竞争结果。BitNet明显优于最先进的量化方法。随着模型规模的扩大,成本节约变得更加显著,同时实现与FP16训练的模型的竞争性能。 介绍 大模型的推理陈本较高,一个降低成本的重要办法就是量化。 目前LLM的量化方法大多数都是训练后量化PTQ,成本比QAT低但在极低精度表现不好。 LLM量化的另一类方法是量化感知训练QAT,效果更好,但是在低精度下难以收敛。此外,不确定QAT是否遵循scaling low。 本文首次提出1-bit大模型QAT,提出BitNet,LLM的1-bit transformer。采用低精度二进制权重和量化激活,同时在训练期间保持优化器状态和梯度的高精度。BitNet 架构的实现非常简单,只需要替换Transformer 中的线性投影(即 PyTorch 中的 nn.Linear)。 在一系列语言建模基准上评测了BitNet。与最先进的量化方法和fp16的baseline相比有竞争力。有效减少了内存占用和能耗。同时遵循类似的scaling low。 BitNet 模型整体架构图如图所示: 相比transformer,使用BitLinear替代原有的Linear和传统的矩阵乘法,其他组件保持高精度,作者认为原因如下: 残差连接和层归一化对大型语言模型的计算成本可以忽略不计 随着模型变大,QKV 变换的计算成本比参数投影小得多 保留输入/输出嵌入的精度,因为语言模型必须使用高精度概率来执行采样 BitLinear BitLinear把权重二值化为+1或-1。在训练的时候,为了保证反向传播的有效性,模型的参数 $ W ^{n m} $是保持高精度的,但是forward的时候会进行量化操作: \\[ \\tilde{W} = \\text{Sign}(W - \\alpha), \\quad \\text{Sign}(W_{ij}) = \\begin{cases} +1, & \\text{if } W_{ij} > 0, \\\\ -1, & \\text{if } W_{ij} \\leq 0, \\end{cases} \\quad \\alpha = \\frac{1}{nm} \\sum_{ij} W_{ij} \\] 激活也会量化到$[-Q_{b},Q_{b}] (Q_{b}=2^{b-1}) $,同时添加细致扰动防止clip时溢出: \\[ \\tilde{x} = \\text{Quant}(x) = \\text{Clip}\\left( x \\times \\frac{Q_b}{\\gamma}, -Q_b + \\epsilon, Q_b - \\epsilon \\right) \\\\ \\text{Clip}(x, a, b) = \\max(a, \\min(b, x)), \\quad \\gamma = \\lVert x \\rVert_{\\infty} \\] 对于非线性函数之前的激活,会缩放到\\([0,Q_{b}] (Q_{b}=2^{b-1})\\): \\[ \\tilde{x} = \\text{Quant}(x) = \\text{Clip}\\left( (x - \\eta) \\times \\frac{Q_b}{\\gamma}, \\epsilon, Q_b - \\epsilon \\right), \\quad \\eta = \\min_{ij} x_{ij} \\] 其中BitLinear的forward过程为: \\[ \\\\ \\text{LN}(x) = \\frac{x - E(x)}{\\sqrt{\\text{Var}(x)} + \\epsilon}, \\quad \\beta = \\frac{1}{nm} \\lVert W \\rVert_1 \\\\ y = \\tilde{W} \\tilde{x} = \\tilde{W} \\, \\text{Quant}(\\text{LN}(x)) \\times \\frac{\\beta \\gamma}{Q_b} \\] 下面这段分析我也不是很理解,一直不知道约等于是怎么来的? 分析: \\[ \\begin{align*} y &= \\tilde{W} \\tilde{x} \\ ,\\\\ \\text{Var}(y) &= n \\, \\text{Var}(\\tilde{w} \\tilde{x}) \\\\ \\quad &= n E[\\tilde{w}^2] E[\\tilde{x}^2] \\\\ \\quad &= n \\beta^2 E[\\tilde{x}^2] \\approx E[\\tilde{x}^2] \\end{align*} \\] 当使用归一化操作后,\\(Var(y) ≈ E[LN( \\tilde{x} )^2] = 1\\) 同时,为了支持张量并行(一个张量拆分到多卡上),部分参数的需要通过 all-reduce来确定,但是会增加通信成本,因此考虑分组确定,即将权重和激活分为几组,然后独立估计每组的参数: \\[ \\alpha_g = \\frac{G}{nm} \\sum_{ij} W_{ij}^{(g)}, \\quad \\beta_g = \\frac{G}{nm} \\lVert W^{(g)} \\rVert_1 \\] 模型训练 STE 由于有量化操作不可微分,因此考虑引入STE进行梯度的近似。 混合精度 虽然权重和激活被量化为低精度,但梯度和优化器状态以高精度存储,以确保训练的稳定性和准确性。以高精度格式为可学习参数维护一个潜在权重,以累积参数更新。潜在权重在前向传播过程中被动态二值化,并且从不用于推理过程。 增大学习率 潜在权重的小幅度更新可能不会引起1位权重的变化,为了加速收敛,考虑增大学习率的值。实验表明,BitNet 在收敛方面受益于较大的学习率,而 FP16 Transformer 在相同学习率的训练开始时会发散。 计算能效 论文还给出了理论的计算能效,主要关注矩阵乘法部分。 对于transformer里的矩阵乘法,两个矩阵维度分别为\\(m \\times n\\)和\\(n \\times m\\),能效计算公式为: \\[ E_{add} = m \\times (n - 1) \\times p \\times \\hat{E}_{add} \\\\ E_{mul} = m \\times n \\times p \\times \\hat{E}_{mul} \\] 对于BitNet,矩阵权重为1比特的,因此矩阵乘法的能耗主要由加法决定,和transformer的加法能耗公式相同,而用于放缩的乘法能耗公式为: \\[ E_{mul} = (m \\times p + m \\times n) \\times \\hat{E}_{mul} \\] 16位transformer对比 使用各种规模的 BitNet 训练一系列自回归语言模型,范围从 125M 到 30B。这些模型在英语语料库上进行训练,该语料库由 Pile 数据集、Common Crawl 快照、RealNews 和 CC-Stories 数据集组成。具体的训练超参可以阅读原文。同时训练作为对比的transformer。 从测试可以发现,模型的loss与transformer相比有类似的scaling low。 能耗也有类似的规律。 当增大学习率的时候,BitNet可以更好的保持性能。 与PTQ对比 使用与前文相同的设置训练BitNet和transformer,在训练好的transformer上尝试PTQ量化。 结果如下所示: 在PPL和Acc上结果明显优于目前最优的PTQ方法。 消融实验 作者还尝试了消融实验: BitNet使用的是 absmax+SubLn。Elastic代表弹性的量化函数,通过可学习的参数动态调整尺度。Pre-LN 是 GPT 相关的默认架构,而 BMT 已被证明可以提高二值化模型的稳定性。 代码 核心代码文件为:BitNet/bitnet/bitlinear.py at main · kyegomez/BitNet 参考资料 BitNet: Scaling 1-bit Transformers for Large Language Models kyegomez/BitNet: Implementation of \"BitNet: Scaling 1-bit Transformers for Large Language Models\" in pytorch","categories":[{"name":"科研","slug":"科研","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/"},{"name":"论文阅读","slug":"科研/论文阅读","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/%E8%AE%BA%E6%96%87%E9%98%85%E8%AF%BB/"}],"tags":[{"name":"量化","slug":"量化","permalink":"https://bg51717.github.io/tags/%E9%87%8F%E5%8C%96/"}]},{"title":"Hexo+Github搭建个人Wiki风格博客","slug":"SmallProjects/博客搭建/Hexo+Github搭建个人Wiki风格博客","date":"2023-10-18T08:12:44.000Z","updated":"2024-11-02T05:27:08.469Z","comments":true,"path":"/13640/","link":"","permalink":"https://bg51717.github.io/13640/","excerpt":"","text":"介绍 以前一直不理解更新博客的用处,后来发现记录一些学到的东西和处理问题的经验对个人还是互联网都是有促进作用的(希望不是在生产互联网垃圾)。 所以本文会介绍一下个人搭建这个博客的过程。 后面博客有添加双主题的教程。 博客风格 简洁(本来想弄点二次元风格的,但是博客还是减少点无用东西吧)(后来还是加了二次元主题来使用双主题) 多级分类:多级分类可以帮助来快速浏览学过的知识点 标签:标签还是比较重要的,可以实习对相关博客的快速定位 主题选择 基于上述的需求,最终选择了Wikitten作为自己的博客主题 顺便放一下这个大佬的相关链接: Wiki地址:http://wiki.zthxxx.me/ Wikitten主题地址:https://github.com/zthxxx/hexo-theme-Wikitten/ 环境搭建 参考地址:使用 Hexo+GitHub 搭建个人免费博客教程(小白向) - 知乎 (zhihu.com) Github创建项目并取名为 用户名.github.io 使用 npm 一键安装 Hexo 博客程序 npm install -g hexo-cli 准备一个空文件,进行hexo初始化: hexo init # 初始化 使用npm安装package.json里面的依赖 npm install 安装用于部署的包hexo-deployer-git npm install hexo-deployer-git --save 修改_config.yml文件末尾的Deployment部分 deploy: type: git repository: [email protected]:用户名/用户名.github.io.git branch: master 公式 公式引擎有 mathjax和 katex两种,前者的功能多一点,后者则更加轻量化。可以根据自己的需要进行选择或者尝试后再做选择。 Mathjax 参考文章:Hexo显示Latex公式最新解决方案_hexo latex-CSDN博客 卸载部分插件 npm un hexo-math npm un hexo-renderer-marked 安装hexo-renderer-pandoc渲染器 npm i hexo-renderer-pandoc 配置主题配置下的mathjax设置(文件位置在 \\themes\\{主题名}\\ _config.yml # MathJax mathjax: enable: true per_page: true 安装Pandoc 去Pandoc官网下载最新版本pandoc:Pandoc - About pandoc (参考文章说 Anaconda自带的 pandoc可能会引起问题并且提供了解决方案,但是笔者使用的是 miniconda,没有遇到任何问题 Katex 参考文章:如何在 Butterfly 主题中使用 KaTeX 数学公式 | Proton's Blog 卸载部分插件(如果还装了什么其他的旧渲染器,也要一并卸载 sudo npm un hexo-renderer-marked --save 安装 hexo-renderer-markdown-it-katex npm i hexo-renderer-markdown-it-katex 然后在hexo的博客文件 _config.yml里添加 markdown: render: html: true xhtmlOut: false breaks: true linkify: true typographer: true plugins: anchors: level: 1 collisionSuffix: '' 图片 参考文章:Hexo 引用本地图片以及引用本地任意位置图片的一点思路 | 养恐龙 (leay.net) 对于图片显示,hexo的原生设置较为冷门,hexo-asset-image等插件也提供了一定的解决方法,在博客文件同目录下设置个同名文件夹存放图片。但是笔者习惯使用 vscode编辑 markdown文章,而vscode默认在同目录下的 /image/{文件名}下存放图片文件。 笔者最后没有找到已有的较好的解决方案,于是写了个脚本文件进行处理(仅供参考)。 首先安装 hexo-asset-image插件,然后在 _config.yml里设置: post_asset_folder: true # 注意这个不能简单设置为false,否则插件hexo-asset-image会直接跳过(可以看看源码 marked: prependRoot: true postAsset: true 然后构建一个脚本文件在每次更新完后进行预处理,其中重要函数为 import os import shutil import re from functools import partial def move_directory(src, dst): \"\"\"把src下所有内容复制到dst下,并删除src\"\"\" # 确保目标目录存在 if not os.path.exists(dst): os.makedirs(dst) # 遍历源目录中的所有文件和子目录 for item in os.listdir(src): s = os.path.join(src, item) d = os.path.join(dst, item) # 如果是目录,递归移动子目录 if os.path.isdir(s): move_directory(s, d) else: # 移动文件,如果目标文件已存在,则跳过 if not os.path.exists(d): shutil.copy(s, d) # 最后,删除空的源目录 os.rmdir(src) def img_url_replace(match, file_name): pattern = r\"!\\[(.*?)\\]\\((.*?)\\)\" name, old_link = re.findall(pattern, match.group(0))[0] if old_link.startswith(\"./{file_name}\"): # 如果格式正确 return match.group(0) if old_link.startswith(\"./\"): old_link = old_link[2:] if old_link.startswith(\"image/\"): old_link = old_link[6:] return f\"![{name}](./{old_link})\" def remedy_image_path(md_file_path): \"\"\"修改md文件中的图片路径\"\"\" with open(md_file_path, \"r\", encoding=\"utf-8\") as f: text = f.read() img_patten = r\"!\\[.*?\\]\\((.*?)\\)\" file_name = os.path.basename(md_file_path).split(\".\")[0] # img_patten = r'!\\[.*?\\]\\((.*?)\\)|<img.*?src=[\\'\\\"](.*?)[\\'\\\"].*?>' updated_text, changed_num = re.subn( img_patten, partial(img_url_replace, file_name=file_name), text ) if changed_num>0: with open(md_file_path, 'w', encoding='utf-8') as f: f.write(updated_text) def process_md_file(file_path): \"\"\"处理md文件\"\"\" # 如果不是md文件,返回 if not file_path.endswith(\".md\"): return file_dir, file_name = os.path.split(file_path) # 如果没有需要处理的文件 if not os.path.exists(os.path.join(file_dir, \"image\", file_name.split(\".\")[0])): return # 移动图片文件 move_directory( src=os.path.join(file_dir,'image',file_name.split('.')[0]), dst=os.path.join(file_dir,file_name.split('.')[0]) ) # 修改连接 remedy_image_path(file_path) def dfs(dir): \"\"\"dfs处理所有文件\"\"\" for root, dirs, files in os.walk(dir): for file in files: file = os.path.join(root, file) process_md_file(file) for sub_dir in dirs: sub_dir = os.path.join(root, sub_dir) dfs(sub_dir) Hexo常用命令 hexo clean #清除生成的网页文件 hexo g #生成静态网页 hexo s #本地运行博客 hexo d #将网页文件推送至远程服务器 Hexo常用文件夹 文件夹 备注 node_modules 依赖文件 .deploy_git 推送到远程的文件 public 生成的网页文件 themes 主题文件夹 scaffolds 博客模板文件夹 source 博客源文件 主题安装 Wikitten主题地址:https://github.com/zthxxx/hexo-theme-Wikitten/ 里面提供了双语详细的安装步骤,笔者使用的版本是 5b0d493 注意:里面有个可选步骤 配置mathjax渲染,笔者发现进行这个步骤后会出现生成网页文件失败的情况,跳过即可,并且暂时没发现有什么问题 评论系统giscus 由于不知名的原因,Gitment和Gitalk无法使用,选择了giscus来替代,参考Hexo静态博客使用giscus评论系统教程 1.打开自己的GitHub,选择博客所在仓库或可新建一个仓库 确保仓库是公开的(新建的时候选择 private或者在仓库的 Settings - General,选择 change visibility - change to public) 确保仓库开启了 Discussions(在仓库的 Settings - General,勾选 Discussions) 确保安装了 giscus app 2.打开官方配置页面giscus,生成代码 语言 仓库:填写格式:你的用户名/你的仓库名 页面 与 discussion 映射关系:由于本博客会分级,所以选择路径pathname Discussion分类:General 特性:按个人喜好即可,本博客加载了:启用主贴上的反应,将评论框放在评论上方,懒加载评论 主题:选择与博客风格一致的主题 3.修改主题 _config.yml 在文件末尾添加 giscus: enable: true 方便以后直接从配置文件开关评论区 4.粘贴代码 网页会自动生成代码,复制粘贴到网页模板文件即可 不同主题的模板文件位置可能不同,wiki主题在 themes/Wikitten/layout/comment/giscus.ejs下添加代码 (然后检查同目录的引入giscus的文件:counter.ejs,index.ejs,scripts.ejs,仿照之前代码添加引入) <% if (theme.comment.giscus.enable) { %> <div id=\"giscus-container\"></div> <script src=\"https://giscus.app/client.js\" data-repo=\"<%= theme.comment.giscus.repo %>\" data-repo-id=\"<%= theme.comment.giscus.repo_id %>\" data-category=\"<%= theme.comment.giscus.category %>\" data-category-id=\"<%= theme.comment.giscus.category_id %>\" data-mapping=\"title\" data-strict=\"0\" data-reactions-enabled=\"1\" data-emit-metadata=\"0\" data-input-position=\"top\" data-theme=\"<%= theme.comment.giscus.theme || 'light' %>\" data-lang=\"<%= theme.comment.giscus.lang || 'zh-CN' %>\" data-loading=\"lazy\" crossorigin=\"anonymous\" async> </script> <noscript>请启用 JavaScript 以查看评论。</noscript> <% } %> 然后修改 themes/Wikitten/layout/common/article.ejs文件,把 <% if (!index) { %> <%- partial('comment/index') %> <% } %> 移动到 <footer class=\"article-footer\"> </footer> 前面,两个代码块在同一个 div内。 然后在 themes/Wikitten/_config.yml内修改: comment: disqus: # enter disqus shortname here duoshuo: # enter duoshuo shortname here youyan: # enter youyan uid here giscus: enable: true repo: '' # 添加 repo_id: '' # 添加 category: 'General' category_id: '' # 添加 theme: 'light' lang: 'zh-CN' 对于不同的主题添加的方式可能不同 常见QA 修改配置文件应该修改站点的 _config.yml 还是主题的 _config.yml? 具体使用哪个,要看主题的源代码,如果是 config.xxx那就是用的根目录配置文件,如果是 theme.xxx那就用的是主题目录的配置文件。 怎么让自己的博客可以被常用搜索引擎搜索到? 很多搜索引擎可以在搜索框里用 site:网址来判断是否被收录 Google 打开Google搜索,搜索“Google Search Console” 根据提示登录你的Google账号 选择资源类型 选择验证方式(本博客用的是HTML标记,把对应代码添加到主题文件里面,本主题是 themes/Wikitten/layout/common/head.ejs) 在Google Search Console后台添加站点地图 参考资料 使用 Hexo+GitHub 搭建个人免费博客教程(小白向) Hexo 的个人 Wiki 主题 - Wikitten 超详细Hexo+Github博客搭建小白教程 Hexo显示Latex公式最新解决方案_hexo latex-CSDN博客 如何在 Butterfly 主题中使用 KaTeX 数学公式 | Proton's Blog Hexo 引用本地图片以及引用本地任意位置图片的一点思路 | 养恐龙 (leay.net) Hexo静态博客使用giscus评论系统教程 Hexo官方文档","categories":[{"name":"SmallProjects","slug":"SmallProjects","permalink":"https://bg51717.github.io/categories/SmallProjects/"},{"name":"博客搭建","slug":"SmallProjects/博客搭建","permalink":"https://bg51717.github.io/categories/SmallProjects/%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA/"}],"tags":[{"name":"博客","slug":"博客","permalink":"https://bg51717.github.io/tags/%E5%8D%9A%E5%AE%A2/"},{"name":"Hexo","slug":"Hexo","permalink":"https://bg51717.github.io/tags/Hexo/"},{"name":"Wiki","slug":"Wiki","permalink":"https://bg51717.github.io/tags/Wiki/"}]},{"title":"快速启动工具——utools","slug":"工具/utools","date":"2023-04-05T02:12:21.000Z","updated":"2024-11-02T02:55:49.874Z","comments":true,"path":"/44392/","link":"","permalink":"https://bg51717.github.io/44392/","excerpt":"","text":"介绍 这里接受一个快速启动的工具utools和常用的插件。 utools utools已经被很多人开始使用了,无论是 alt+space 还是鼠标中键等快速启动方式,都可以使用户不要来回在鼠标和键盘之间切换从而提升效率。内置的一些默认插件已经可以极大的提升效率了。 utools优势: 完善的生态市场:里面有很多人在开发各种插件,并且插件的体积都非常小,也支持插件数据迁移 ,基本你想到的功能都可以在插件市场找到 多平台:utools在多个平台都可以使用,也可以多平台进行数据迁移 插件推荐 音速启动 utools搜索框可以使用且快速启动的大概为系统安装的软件搜素、文件搜索、网络搜索、插件搜索等,但是假如你有复杂的命令以及一个别名使用多个命令等,这是原生的utools无法使用的,而下面这款插件就可以使用。 音速启动插件是utools官方开发的插件,可以让utools快速启动的能力再次得到提高。在音速启动的配置界面,你可以配置一些命令,并且给这些常用的命令起别名,这样在utools搜索框内就可以通过别名快速运行一些命令。","categories":[{"name":"工具","slug":"工具","permalink":"https://bg51717.github.io/categories/%E5%B7%A5%E5%85%B7/"}],"tags":[{"name":"工具","slug":"工具","permalink":"https://bg51717.github.io/tags/%E5%B7%A5%E5%85%B7/"},{"name":"效率","slug":"效率","permalink":"https://bg51717.github.io/tags/%E6%95%88%E7%8E%87/"}]}],"categories":[{"name":"数学","slug":"数学","permalink":"https://bg51717.github.io/categories/%E6%95%B0%E5%AD%A6/"},{"name":"代码","slug":"数学/代码","permalink":"https://bg51717.github.io/categories/%E6%95%B0%E5%AD%A6/%E4%BB%A3%E7%A0%81/"},{"name":"科研","slug":"科研","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/"},{"name":"论文阅读","slug":"科研/论文阅读","permalink":"https://bg51717.github.io/categories/%E7%A7%91%E7%A0%94/%E8%AE%BA%E6%96%87%E9%98%85%E8%AF%BB/"},{"name":"线性代数","slug":"数学/线性代数","permalink":"https://bg51717.github.io/categories/%E6%95%B0%E5%AD%A6/%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0/"},{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"经典模块","slug":"深度学习/经典模块","permalink":"https://bg51717.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/%E7%BB%8F%E5%85%B8%E6%A8%A1%E5%9D%97/"},{"name":"微积分","slug":"数学/微积分","permalink":"https://bg51717.github.io/categories/%E6%95%B0%E5%AD%A6/%E5%BE%AE%E7%A7%AF%E5%88%86/"},{"name":"模板","slug":"模板","permalink":"https://bg51717.github.io/categories/%E6%A8%A1%E6%9D%BF/"},{"name":"SmallProjects","slug":"SmallProjects","permalink":"https://bg51717.github.io/categories/SmallProjects/"},{"name":"博客搭建","slug":"SmallProjects/博客搭建","permalink":"https://bg51717.github.io/categories/SmallProjects/%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA/"},{"name":"工具","slug":"工具","permalink":"https://bg51717.github.io/categories/%E5%B7%A5%E5%85%B7/"},{"name":"工程细节","slug":"深度学习/工程细节","permalink":"https://bg51717.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/%E5%B7%A5%E7%A8%8B%E7%BB%86%E8%8A%82/"},{"name":"杂项","slug":"杂项","permalink":"https://bg51717.github.io/categories/%E6%9D%82%E9%A1%B9/"},{"name":"自然语言处理","slug":"自然语言处理","permalink":"https://bg51717.github.io/categories/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/"},{"name":"huggingface","slug":"huggingface","permalink":"https://bg51717.github.io/categories/huggingface/"},{"name":"概率论","slug":"数学/概率论","permalink":"https://bg51717.github.io/categories/%E6%95%B0%E5%AD%A6/%E6%A6%82%E7%8E%87%E8%AE%BA/"},{"name":"自然语言处理","slug":"深度学习/自然语言处理","permalink":"https://bg51717.github.io/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/"},{"name":"课程资源","slug":"课程资源","permalink":"https://bg51717.github.io/categories/%E8%AF%BE%E7%A8%8B%E8%B5%84%E6%BA%90/"}],"tags":[{"name":"数学","slug":"数学","permalink":"https://bg51717.github.io/tags/%E6%95%B0%E5%AD%A6/"},{"name":"SymPy","slug":"SymPy","permalink":"https://bg51717.github.io/tags/SymPy/"},{"name":"深度学习","slug":"深度学习","permalink":"https://bg51717.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"},{"name":"量化","slug":"量化","permalink":"https://bg51717.github.io/tags/%E9%87%8F%E5%8C%96/"},{"name":"矩阵","slug":"矩阵","permalink":"https://bg51717.github.io/tags/%E7%9F%A9%E9%98%B5/"},{"name":"SVD","slug":"SVD","permalink":"https://bg51717.github.io/tags/SVD/"},{"name":"梯度估计","slug":"梯度估计","permalink":"https://bg51717.github.io/tags/%E6%A2%AF%E5%BA%A6%E4%BC%B0%E8%AE%A1/"},{"name":"模板","slug":"模板","permalink":"https://bg51717.github.io/tags/%E6%A8%A1%E6%9D%BF/"},{"name":"PyTorch","slug":"PyTorch","permalink":"https://bg51717.github.io/tags/PyTorch/"},{"name":"分布式","slug":"分布式","permalink":"https://bg51717.github.io/tags/%E5%88%86%E5%B8%83%E5%BC%8F/"},{"name":"ddq","slug":"ddq","permalink":"https://bg51717.github.io/tags/ddq/"},{"name":"数据并行","slug":"数据并行","permalink":"https://bg51717.github.io/tags/%E6%95%B0%E6%8D%AE%E5%B9%B6%E8%A1%8C/"},{"name":"Data Parallel","slug":"Data-Parallel","permalink":"https://bg51717.github.io/tags/Data-Parallel/"},{"name":"博客","slug":"博客","permalink":"https://bg51717.github.io/tags/%E5%8D%9A%E5%AE%A2/"},{"name":"Hexo","slug":"Hexo","permalink":"https://bg51717.github.io/tags/Hexo/"},{"name":"Wiki","slug":"Wiki","permalink":"https://bg51717.github.io/tags/Wiki/"},{"name":"dotfiles","slug":"dotfiles","permalink":"https://bg51717.github.io/tags/dotfiles/"},{"name":"dotbot","slug":"dotbot","permalink":"https://bg51717.github.io/tags/dotbot/"},{"name":"Linux","slug":"Linux","permalink":"https://bg51717.github.io/tags/Linux/"},{"name":"终端","slug":"终端","permalink":"https://bg51717.github.io/tags/%E7%BB%88%E7%AB%AF/"},{"name":"oh-my-zsh","slug":"oh-my-zsh","permalink":"https://bg51717.github.io/tags/oh-my-zsh/"},{"name":"powerlevel10k","slug":"powerlevel10k","permalink":"https://bg51717.github.io/tags/powerlevel10k/"},{"name":"安卓","slug":"安卓","permalink":"https://bg51717.github.io/tags/%E5%AE%89%E5%8D%93/"},{"name":"Google","slug":"Google","permalink":"https://bg51717.github.io/tags/Google/"},{"name":"HuggingFace","slug":"HuggingFace","permalink":"https://bg51717.github.io/tags/HuggingFace/"},{"name":"Trainer","slug":"Trainer","permalink":"https://bg51717.github.io/tags/Trainer/"},{"name":"config","slug":"config","permalink":"https://bg51717.github.io/tags/config/"},{"name":"model","slug":"model","permalink":"https://bg51717.github.io/tags/model/"},{"name":"dataset","slug":"dataset","permalink":"https://bg51717.github.io/tags/dataset/"},{"name":"随机数","slug":"随机数","permalink":"https://bg51717.github.io/tags/%E9%9A%8F%E6%9C%BA%E6%95%B0/"},{"name":"vscode","slug":"vscode","permalink":"https://bg51717.github.io/tags/vscode/"},{"name":"python","slug":"python","permalink":"https://bg51717.github.io/tags/python/"},{"name":"调试","slug":"调试","permalink":"https://bg51717.github.io/tags/%E8%B0%83%E8%AF%95/"},{"name":"debug","slug":"debug","permalink":"https://bg51717.github.io/tags/debug/"},{"name":"transformers_tutorials","slug":"transformers-tutorials","permalink":"https://bg51717.github.io/tags/transformers-tutorials/"},{"name":"信息学","slug":"信息学","permalink":"https://bg51717.github.io/tags/%E4%BF%A1%E6%81%AF%E5%AD%A6/"},{"name":"pytroch_tutorials","slug":"pytroch-tutorials","permalink":"https://bg51717.github.io/tags/pytroch-tutorials/"},{"name":"优化算法","slug":"优化算法","permalink":"https://bg51717.github.io/tags/%E4%BC%98%E5%8C%96%E7%AE%97%E6%B3%95/"},{"name":"自然语言处理","slug":"自然语言处理","permalink":"https://bg51717.github.io/tags/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/"},{"name":"向量","slug":"向量","permalink":"https://bg51717.github.io/tags/%E5%90%91%E9%87%8F/"},{"name":"工具","slug":"工具","permalink":"https://bg51717.github.io/tags/%E5%B7%A5%E5%85%B7/"},{"name":"文献管理","slug":"文献管理","permalink":"https://bg51717.github.io/tags/%E6%96%87%E7%8C%AE%E7%AE%A1%E7%90%86/"},{"name":"效率","slug":"效率","permalink":"https://bg51717.github.io/tags/%E6%95%88%E7%8E%87/"}]}