当前位置: 首页 > news >正文

张量链式法则(下篇):揭秘Transpose、Summation等复杂算子反向传播,彻底掌握深度学习求导精髓!

本文首发于本人的微信公众号,链接:https://mp.weixin.qq.com/s/eEDo6WF0oJtRvLYTeUYxYg

摘要

本文紧接系列的上篇,介绍了 transpose,summation,broadcast_to 等更为复杂的深度学习算子的反向传播公式推导。

写在前面

本系列文章的上篇介绍了张量函数链式法则公式,并以几个简单的算子为例子展示了公式的使用方法。本文将继续以更复杂的算子为例子演示公式的使用方法,求解这些算子的反向传播公式也是我研究张量函数链式法则的目的:因为对于 transpose,broadcast_to 这类会根据传入的参数改变输出张量维度数量的算子,常规的矩阵链式法则公式已无法解决。

常见算子的反向传播推导(下半部分)

复习一下

张量函数链式法则的公式为:

\[\nabla_{\lambda_1 \lambda_2 \cdots \lambda_n} = \sum_{\substack{\mu_1 \in [1, e_1] \\ \mu_2 \in [1, e_2] \\ \vdots \\ \mu_m \in [1, e_m]}} g_{\mu_1 \mu_2 \cdots \mu_m} \frac{\partial}{\partial x_{\lambda_1 \lambda_2 \cdots \lambda_n}} f_{\mu_1 \mu_2 \cdots \mu_m} \]

求解步骤为:我们首先需要确定各个张量的形状,然后把注意力集中到自变量里的某个元素,写出这个元素的导数表达式,然后再推广到整个导数张量。

接下来我们继续常见算子的反向传播公式推导。

Summation

这个算子是对输入张量沿着某些轴求和,这个算子有一个参数 axes,表示求和的规约轴,例如,对于一个四维张量 \(X \in \mathbb{R}^{d_1 \times d_2 \times d_3 \times d_4}\),如果 axes=(2, 3)\(F(X) \in \mathbb{R}^{d_1 \times d_4}\),是一个二维张量,且 \(f_{ij} = \sum_{k=1}^{d_2} \sum_{l=1}^{d_3} X_{iklj}\)

由此可见,对于多个轴的 summation 操作其实可以拆分为多次的对于一个轴的 summation,所以我们仅讨论 axes 只有一个轴的公式,对于有多个轴的场景可以将其视为复合函数,通过反复使用该公式来进行扩展。

单轴 Summation 问题描述

所以我们要解决的问题就变成了:函数 \(F\) 会对张量 \(X\) 的第 \(a\) 个维度进行求和,求该函数的反向传播公式。

(注:本文统一以 1 为起始下标,实际编程时 axes 是以 0 为起始下标,这个差异需要注意)

首先确定各个张量的形状,如果自变量 \(X \in \mathbb{R}^{d_1 \times d_2 \times \cdots \times d_n}\) 是一个 \(n\) 维张量,那么 \(F(X) \in \mathbb{R}^{d_1 \times d_2 \times \cdots \times d_{a-1} \times d_{a+1} \times \cdots \times d_n}\)\(n-1\) 维张量。

单轴 Summation 问题求解

接下来可以写出每个自变量的导数的表达式:

\[\nabla_{\lambda_1 \lambda_2 \cdots \lambda_n} = \sum_{\substack{\mu_1 \in [1, e_1] \\ \mu_2 \in [1, e_2] \\ \vdots \\ \mu_{a-1} \in [1, e_{a-1}] \\ \mu_{a+1} \in [1, e_{a+1}] \\ \vdots \\ \mu_m \in [1, e_m]}} g_{\mu_1 \mu_2 \cdots \mu_m} \cdot \frac{\partial f_{\mu_1 \mu_2 \cdots \mu_m}}{\partial x_{\lambda_1 \lambda_2 \cdots \lambda_n}} \]

\[= \sum_{\substack{\mu_1 \in [1, e_1] \\ \mu_2 \in [1, e_2] \\ \vdots \\ \mu_{a-1} \in [1, e_{a-1}] \\ \mu_{a+1} \in [1, e_{a+1}] \\ \vdots \\ \mu_m \in [1, e_m]}} g_{\mu_1 \mu_2 \cdots \mu_n} \cdot \frac{\partial}{\partial x_{\lambda_1 \lambda_2 \cdots \lambda_n}} \sum_{i=1}^{e_a} x_{\mu_1 \mu_2 \cdots \mu_{a-1} i \mu_{a+1} \cdots \mu_n} \]

注意到,当且仅当 \(\mu_1 = \lambda_1, \mu_2 = \lambda_2, \ldots, \mu_n = \lambda_n\) 时,这个表达式值不为 0,且满足上述条件时,只有当 \(i = \lambda_a\) 时,求和表达式值为 1,\(i\) 为其他值时都为 0,所以这一项的最终结果是 \(g_{\lambda_1 \lambda_2 \cdots \lambda_n}\)

所以最终的 \(\nabla = \text{broadcast}(G, a)\),即把张量 \(G\) 在第 \(a\) 个轴做 broadcast_tobroadcast_to 操作的定义见下文)。

当然,这里实际操作时首先要对 \(G\)reshape,把因为求和丢掉的轴 unsqueeze 回来,然后再通过 broadcast_to 操作广播到 \(X\) 的形状,具体可以参考下面的具体代码实现:

a = node.inputs[0]
target_dim_num = len(a.shape)
grad_new_shape = []
for i in range(target_dim_num):if i in self.axes:grad_new_shape.append(1)else:grad_new_shape.append(a.shape[i])
return broadcast_to(reshape(out_grad, grad_new_shape), a.shape)

多轴 Summation 问题求解

接下来讨论 axes 有多个的情形,通过上面的讨论,容易想到:只需要把求和规约掉的多个轴通过 reshape 进行 unsqueeze,然后再进行 broadcast 就行了。

实际情况正是如此,以两个轴为例,这种情况可以认为是两个单轴 summation 操作的复合,在实际进行反向传播时,会先传播到第一个单轴 summation,此时会进行一次 broadcast_to,然后这个结果会作为 \(G\) 继续传播到第二个单轴 summation,此时又会进行一次 broadcast_to,最终结果等价于把这两次 broadcast_to 放到一起完成。

严格的数学推导这里就不展开了,留作习题自证不难。

所以对于 Summation,最终的导数结果为:

\[\nabla = \text{broadcast\_to}(G, X.\text{shape}) \]

BroadcastTo

这个算子是对一个张量进行广播操作,也就是把张量的元素在若干个轴上进行“复制”的操作,形成一个更“充实”的张量。numpy,pytorch 等框架在处理形状不同的张量时会自动进行广播操作。例如,\(A\) 的形状是 \((6, 6, 5, 4)\)\(B\) 的形状是 \((6, 5, 4)\),在执行 \(A \odot B\) 时,框架会自动在 \(B\) 的左边补上维度 1,变成 \((1, 6, 5, 4)\),然后再执行广播变成 \((6, 6, 5, 4)\),然后再做哈达马积。

这里我们同样先讨论只针对一个轴进行 broadcast_to 的情形,多轴的情形同样可以视为多个单轴 broadcast_to 的嵌套。

(注:以下讨论涉及到的参数和实际编程中的参数有差异,实际编程中是直接传入 broadcast_to 之后的形状作为参数)

单轴 BroadcastTo 算子定义

单轴 broadcast_to 算子有两个参数:

  • 参数 a,表示在哪一个轴进行广播,该算子要求自变量在这一维度的大小为 1
  • 参数 b,表示要将这一维度广播到多大

这一算子的形式化的定义为:

  • \(X \in \mathbb{R}^{d_1 \times d_2 \times \cdots \times d_n}\),是 \(n\) 维张量,其中 \(d_a = 1\)\(F(X) = \text{broadcast\_to}(X, a)\)
  • \(F(X) \in \mathbb{R}^{d_1 \times d_2 \times \cdots \times d_{a-1} \times b \times d_{a+1} \times \cdots \times d_n}\),其中 \(f_{\lambda_1 \lambda_2 \cdots \lambda_n} = x_{\lambda_1 \lambda_2 \cdots \lambda_{a-1} 1 \lambda_{a+1} \cdots \lambda_n}\)

直观上来看就是把 \(X\) 在第 \(a\) 维的元素复制了 \(b\) 份。

单轴 BroadcastTo 问题求解

首先可以确认,\(X\)\(\nabla\) 形状相同,为 \(\mathbb{R}^{d_1 \times d_2 \times \cdots \times d_{a-1} \times 1 \times d_{a+1} \times \cdots \times d_n}\)\(G\)\(F(X)\) 的形状相同,为 \(\mathbb{R}^{d_1 \times d_2 \times \cdots \times d_{a-1} \times b \times d_{a+1} \times \cdots \times d_n}\)

写出 \(\nabla\) 的表达式可得:

\[\nabla_{\lambda_1 \lambda_2 \cdots \lambda_n} = \sum_{\substack{ \mu_1 \in [1, e_1] \\ \mu_2 \in [1, e_2] \\ \vdots \\ \mu_a \in [1, b] \\ \vdots \\ \mu_n \in [1, e_n]}} g_{\mu_1 \mu_2 \cdots \mu_n} \cdot \frac{\partial f_{\mu_1 \mu_2 \cdots \mu_n}}{\partial x_{\lambda_1 \lambda_2 \cdots \lambda_n}} \]

\(F\) 的定义式代入,原式子可写作:

\[\sum_{\substack{ \mu_1 \in [1, e_1] \\ \mu_2 \in [1, e_2] \\ \vdots \\ \mu_a \in [1, b] \\ \vdots \\ \mu_n \in [1, e_n]}} g_{\mu_1 \mu_2 \cdots \mu_n} \cdot \frac{\partial x_{\mu_1 \mu_2 \cdots \mu_{a-1} 1 \mu_{a+1} \cdots \mu_n}}{\partial x_{\lambda_1 \lambda_2 \cdots \lambda_n}} \]

注意到,只有当 \(\mu_1 = \lambda_1, \mu_2 = \lambda_2, \ldots, \mu_{a-1} = \lambda_{a-1}, \mu_{a+1} = \lambda_{a+1}, \ldots, \mu_n = \lambda_n\) 时,求和式不为 0,所以这个式子可以进一步化简为:

\[\sum_{\mu_a \in [1, b]} g_{\lambda_1 \lambda_2 \cdots \lambda_{a-1} \mu_a \lambda_{a+1} \cdots \lambda_n} \]

这个表达式的值恰好就等于张量 \(G\)\(a\) 轴做 Summation,所以有:

\[\nabla = \text{Summation}(G, a) \]

多轴 BroadcastTo 问题求解

和 Summation 类似,多轴情形下只需要对所有广播过的轴做 Summation 即可,由此可得,多轴情形下:

\[\nabla = \text{Summation}(G, (a_1, a_2, \ldots, a_m)) \]

其中 \(a_1, a_2, \ldots, a_m\) 是所有经过广播的轴的编号,具体可以参考以下代码实现:

old_shape = node.inputs[0].shape
new_shape = self.shape
sum_axes = []
for i in range(len(new_shape)):if i >= len(old_shape) or (old_shape[i] == 1 and new_shape[i] != 1):sum_axes.append(i)return reshape(summation(out_grad, tuple(sum_axes)), old_shape)

Reshape

顾名思义,这个算子的作用就是改变张量的形状。numpy 对于这个操作的描述是:在不改变数组内容的情况下为数组赋予新的形状。可以认为 numpy 存储的多维张量本质上是一个连续的一维数组,形状只是我们看这个数组的一个视角,以二维张量为例,假设这个一维数组是 \([1,2,3,4,5,6]\),如果以 \(2 \times 3\) 矩阵的视角去看,那就会是:

\[\begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix} \]

如果以 \(6 \times 1\) 的矩阵视角去看,那就会是:

\[\begin{bmatrix} 1 \\ 2 \\ 3 \\ 4 \\ 5 \\ 6 \end{bmatrix} \]

Reshape 问题求解

这里我们可以猜一下,以三维张量为例,\(\nabla, X \in \mathbb{R}^{e_1 \times e_2 \times e_3}\)\(G, F(X) \in \mathbb{R}^{d_1 \times d_2 \times d_3}\),其中 \(e_1 \times e_2 \times e_3 = d_1 \times d_2 \times d_3\)

注意到 \(\nabla\)\(G\) 的元素数量相同,只是形状不同,那只需要进行一次 reshape 即可。

事实正是如此,对于 \(F(X) = \text{reshape}(X, \text{new\_shape})\),其反向传播导数:

\[\nabla = \text{reshape}(G, X.\text{shape}) \]

这里具体的数学推导就不再赘述了,留作习题供读者练习。

(提示:可以考虑定义一个辅助函数,将原来轴的参数映射到新的轴上的参数)

Transpose

这一算子的定义是做转置,二维矩阵的转置很显然,就是行列互换。推广到 \(n\) 维张量,就是选择两个轴,然后在这两个轴上做互换。

(注:这里的 transpose 是 CMU Homework1 里面定义的,而非 numpy 里的定义,这里只会转置两个轴,但是这里推导得到的结果可以轻易推广到多轴的情形)

Transpose 形式化定义

  • 这一算子有 2 个参数 ab,表示需要转置的两个轴
  • \(X \in \mathbb{R}^{d_1 \times d_2 \times \cdots \times d_n}\),是 \(n\) 维张量
  • \(F(X) \in \mathbb{R}^{d_1 \times d_2 \times \cdots \times d_{a-1} \times d_b \times d_{a+1} \times \cdots \times d_{b-1} \times d_a \times d_{b+1} \times \cdots \times d_n}\) 也是 \(n\) 维张量,只是第 \(a\) 维和第 \(b\) 维的大小互换了
  • 且其中:

\[f_{\lambda_1 \lambda_2 \cdots \lambda_{a-1} \lambda_b \lambda_{a+1} \cdots \lambda_{b-1} \lambda_a \lambda_{b+1} \cdots \lambda_n} = f_{\lambda_1 \lambda_2 \cdots \lambda_{a-1} \lambda_a \lambda_{a+1} \cdots \lambda_{b-1} \lambda_b \lambda_{b+1} \cdots \lambda_n} \]

Transpose 问题求解

这里也很容易才到,对 \(G\) 做同样的转置即可得到,这里同样不展开赘述了,留作习题供读者练习。

(提示:同样可以考虑定义映射轴的辅助函数来解决)

MatMul

这一算子是矩阵乘法,二维矩阵的公式已经在上一篇文章里给出,这里主要补充一下 batch 模式下的矩阵乘法。根据 numpy 里的定义,进行 MatMul 的两个张量 \(X\)\(Y\) 可以是两个高维的张量,例如,当 \(X\) 的形状为 \((6, 6, 5, 3)\)\(Y\) 的形状为 \((6, 6, 3, 4)\) 时,会把 \(X\) 视为是 36 个 \(5 \times 3\) 矩阵按照 \(6 \times 6\) 的格式排列,然后把 \(Y\) 视为 36 个 \(3 \times 4\) 的矩阵按照 \(6 \times 6\) 排列,最后将两个大矩阵中对应位置的两个小矩阵做矩阵乘法,最终会得到 36 个 \(5 \times 4\) 的小矩阵,组成一个形状为 \((6, 6, 5, 4)\) 的张量。

这一操作同样支持广播,即:如果 \(X\) 形状为 \((6, 6, 5, 3)\)\(Y\) 的形状为 \((3,4)\),那么最终结果会是形状为 \((6, 6, 5, 4)\) 的张量,即 \(X\) 的 36 个小矩阵每一个都和 \(Y\) 做矩阵乘法。

这种情形下,如果记单个矩阵乘法的函数为 MatMul,批量矩阵乘法函数为 MatMul_Batch,那么此时 MatMul_Batch 实际上是 MatMul(X, broadcast_to(Y, X.shape)),所以在处理 MatMul_Batch\(Y\) 求导时,需要考虑到这里实际上是嵌套了一层广播的,而广播的反向传播是做 Summation,所以在套用单矩阵 MatMul 的反向传播公式之后还需要做一个 Summation 将形状变回和 \(Y\) 相同的形状,具体过程可以参考如下的代码实现:

(注:理论上是需要先做 Summation 再做 Matmul 的反向传播,但是先做 Summation 和后做是等价的,为了代码实现方便就统一放到后面来做了)

a, b = node.inputs
a_grad, b_grad = matmul(out_grad, transpose(b)), matmul(transpose(a), out_grad)if len(a_grad.shape) > len(a.shape):sum_axes = tuple((i for i in range(len(a_grad.shape) - len(a.shape))))a_grad = summation(a_grad, sum_axes)if len(b_grad.shape) > len(b.shape):sum_axes = tuple((i for i in range(len(b_grad.shape) - len(b.shape))))b_grad = summation(b_grad, sum_axes)return a_grad, b_grad

一些剩下的简单算子

接下来放一些简单算子的反向传播公式,这里就只给出结果而省略推导过程了。

Negate

这个算子是把张量中所有元素取相反数,很显然:

\[\nabla = -G \]

Log

这个算子是对张量中所有元素取自然对数,很显然:

\[\nabla = \frac{G}{X} \]

Exp

这个算子是对张量中所有元素过一次指数函数 \(y = e^x\),很显然:

\[\nabla = G \odot \exp(X) \]

EWisePow

这个算子接收 2 个相同形状的自变量 \(X\)\(Y\)(如果形状不同会进行广播到相同形状),对于 \(X\) 里的每一个 \(x\),取 \(Y\) 对应位置上的元素 \(y\),做 \(x^y\)

很显然:

\[\nabla^X = G \odot Y \odot \text{EWisePow}(X, Y - 1) \]

\[\nabla^Y = G \odot \text{EWisePow}(X, Y) \odot \log(X) \]

相关文章:

张量链式法则(下篇):揭秘Transpose、Summation等复杂算子反向传播,彻底掌握深度学习求导精髓!

本文紧接系列的上篇,介绍了 transpose,summation,broadcast_to 等更为复杂的深度学习算子的反向传播公式推导。本文首发于本人的微信公众号,链接:https://mp.weixin.qq.com/s/eEDo6WF0oJtRvLYTeUYxYg 摘要 本文紧接系列的上篇,介绍了 transpose,summation,broadcast_to…...

详细介绍:QT初探TCP(四)

详细介绍:QT初探TCP(四)pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important; font-size:…...

近期理工类学术会议推荐 | 人工智能、工业设计、电气工程、传感器技术、环境工程等EI会议合集

第五届人工智能与智能制造国际研讨会(AIIM 2025) The 5th International Symposium on Artificial Intelligence and Intelligent Manufacturing 2025年9月19-21日 中国-四川成都 检索类型:EI Compendex, Scopus 2025年智能装备与工业设计国际学术会议(IEID 2025) 2025 In…...

AI访销大脑之“创建及查询数据”新玩法

业代日常使用软件要做很多操作,从做拜访计划、录新门店信息,到执行拜访八步,再到查销量,到查门店信息等等,这样的创建数据、查数据的工作,总是让业代感觉费时间。如果有更多时间,本可以花在更好地谈客情、产品销售上面! 期待总会变为现实,业代的这个愿望,在 AI 的加持…...

史上最薄iPhone 17 Air登场!极致轻薄背后藏有哪些妥协?

北京时间9月10日凌晨,苹果在加州库比蒂诺总部举行了名为“AweDropping”的秋季新品发布会,正式推出了iPhone 17系列。其中最引人注目的当属全新机型——iPhone 17 Air,它以5.6毫米的极致厚度成为苹果史上最薄的手机。 这款新产品被苹果CEO库克称为“将彻底改变智能手机体验”…...

一毛钱好友商城系统介绍

1. 概述总结 一毛钱好友商城系统是一款基于微擎系统交付的多功能程序,微擎系统是一款基于 PHP 开发的开源应用生态系统,主要用于快速搭建微信公众号、小程序等应用,同时支持 Web 系统开发与部署。该商城系统支持多端(公众号、小程序、字节端等),采用 PHP7.4、8.0 开发,代…...

网页转小程序封装机系统介绍

1. 概述总结 本文介绍的网页转小程序封装机是基于微擎系统交付的一款工具,微擎系统是基于 PHP 开发的开源应用生态系统,可用于快速搭建微信公众号、小程序等应用,同时支持 Web 系统开发与部署。该封装机能够将微信公众号开发的网页模块封装成微信小程序,其源码已加密,且为…...

美客分销商城小程序系统介绍

1. 概述总结 美客分销商城小程序系统是一款适用于微信小程序的应用系统,通过微擎系统交付。其交付方式为在线交付,源码已加密,且保障为官方正品。需要注意的是,该系统有全新升级版本,即美客社交电商,可提供更丰富的社交电商模式支持。 微擎系统是一款基于 PHP 开发的开源…...

P12021 面包题

将 \(i\) 向 \(ki\) 连边,发现会变成若干条链,答案即为每条链的答案乘积。 不难发现链的独立集大小就是非伯纳切数列,可以直接做。 现在就变成了求长度为某个值的链的个数,考虑弱化限制可以求出其后缀和,然后差分一下可以得出答案。...

C++ - STL - 静态数组array

array 静态数组 array是固定大小的序列容器,array中包含特定个数并且严格按照线性序列排序的元素。因此array允许对元素进行随机访问,指向某一元素的指针可以通过偏移访问其他元素。在array内部,它只保存自己包含的元素,其他任何信息都不保存,包括自身的大小。 说白了其实…...

C++ - STL - 键值对 map

键值对 map map用于存储键值对(key-value)数据,其内部基于红黑树实现自动排序功能 ‌有序存储‌: 元素默认按键的升序排列,可通过自定义比较函数修改排序规则 键唯一性‌: 每个键在容器中只能出现一次,重复插入会覆盖原有值 平衡二叉树‌: 采用红黑树结构保证O(log n)时间…...

C++ - STL - 集合set(元素具有排他性)

set 集合(用来存储唯一性元素) C++中的set是标准模板库(STL)中的关联容器,用于存储唯一元素并按特定顺序自动排序。 ‌唯一性‌: set中的元素不可重复,插入重复元素会被自动忽略 自动排序‌: 默认按升序排列(可通过自定义比较函数修改排序规则) 底层实现‌: 基于红黑树…...

大三上 大模型系统与工程 第二次课笔记 20250912

一、介绍大模型(如 GPT、LLaMA、Gemini 等)的推理流程。 可以把这个过程想象成让一个博学但“慢思考”的巨人完成一项任务。它的知识已经全部学好了(存储在模型的权重参数中),推理就是它运用这些知识进行“思考”和“输出”的过程。 整个推理流程可以清晰地分为三个核心阶…...

批量删除所有 LXC 容器以及用户名

第 1 步:停止所有正在运行的 LXC 容器 通过 PVE 网页shell或服务器终端,执行以下命令。这将安全地停止所有状态为 running 的容器。bash# 获取所有正在运行的容器列表(ID 和名称) pct list# 停止所有正在运行的容器 for id in $(pct list | awk NR>1 {if ($2 == "r…...

C++ - STL - 动态数组vector(矢量)

vector 动态数组 定义和初始化 vector<int> a; //创建一个空数组vector<int> b(100); //创建100个元素大小的数组vector<char> c(10,a);//创建一个10个元素的数组,元素都是avector<int> d{1,2};//创建一个2个元素的数组,元素是1和2注意上面的小括号…...

彻底解决docker:docker: Get https://registry-1.docker.io/v2/: net/http: request canceled 报错

彻底解决docker:docker: Get https://registry-1.docker.io/v2/: net/http: request canceled 报错给docker服务配置一个代理mkdir -p /etc/systemd/system/docker.service.dtouch /etc/systemd/system/docker.service.d/http-proxy.confvim /etc/systemd/system/docker.servi…...

Transformer-和扩散模型的生成式-AI-实用指南-预览版--全-

Transformer 和扩散模型的生成式 AI 实用指南(预览版)(全)原文:Hands-On Generative AI with Transformers and Diffusion Models 译者:飞龙 协议:CC BY-NC-SA 4.0第一章:扩散模型 在 2020 年末,一个名为扩散模型的鲜为人知的模型类别开始在机器学习领域引起轰动。研究…...

7. Job与CronJob

Job 与 CronJob 控制器 ​ 接下来给大家介绍另外一类资源对象:Job,我们在日常的工作中经常都会遇到一些需要进行批量数据处理和分析的需求,当然也会有按时间来进行调度的工作,在我们的 Kubernetes 集群中为我们提供了Job 和 CronJob 两种资源对象来应对我们的这种需求。 ​…...

nginx反向代理正则匹配示例及nginx内置变量详解

https://www.cnblogs.com/chenjw-note/p/14388257.html1.匹配url:https://bby.ios.xxx.com:8081/zd?sid=15&key=repx_2530 匹配到sid的值根据sid值进行代理分发 2.达到效果:请求https://bby.ios.xxx.com:8081/xxxx?sid=xxx&xxxxx 根据sid的值转发到对应sid的…...

mt_12

...

完整教程:【QT】-怎么实现瀑布图

完整教程:【QT】-怎么实现瀑布图pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important; fo…...

【初赛】二叉树性质和遍历 - Slayer

二叉树的性质与遍历 一、二叉树的基本性质 1. 定义 二叉树是每个节点最多有两个子树的树结构,子树分为左子树和右子树,具有顺序性 2. 关键性质性质1:在非空二叉树中,第 \(i\) 层最多有 \(2^{i-1}个\)节点 性质2:深度为k的二叉树最多有 \(2^k - 1\) 个节点 性质3:对任何一…...

详细解析苹果iOS应用上架到App Store的完整步骤与指南

本指南全面讲解了将iOS应用提交到苹果App Store的整个流程,从注册开发者账号开始,到创建App ID和证书、配置应用元数据、打包IPA文件、上传至App Store Connect、进行TestFlight测试、提交审核以及最终发布。每个步骤都提供了详细说明,旨在帮助开发者避免常见错误,高效完成…...

drawio

目录常见问题如何实现一个矩形里添加一个子矩形,拖动时作为整体 常见问题 如何实现一个矩形里添加一个子矩形,拖动时作为整体选中父矩形,快捷键:ctrl + g;...

bootstrap-select插件在webpack中点击无响应

​ 使用插件:https://github.com/snapappointments/bootstrap-select问题:bootstrap-select插件在webpack中点击无响应解决方案: 1、在入口文件中按顺序引入css和js import bootstrap/dist/css/bootstrap.min.css import "bootstrap-select/dist/css/bootstrap-select.…...

Kali 字体大小设置

Kali 字体大小设置 终端字体颜色调整 调整前调整后在session -> 参数配置终端颜色这里可以改自己喜欢的终端颜色在字体这里可以修改字体大小和字体可以选择图片改终端的背景显示和分辨率 在左上角这里选择全部应用程序 选择显示可以修改kali的分辨率和刷新率桌面图标样式和终…...

如何使用 OCR 提取扫描件 PDF 的文本(Python 实现) - E

从 PDF 中提取文本一直是很多人的需求。市面上的工具虽然能处理大部分数字 PDF,但遇到扫描件 PDF 时往往无能为力,想要直接复制或获取其中的文字并不容易。其实这个问题并不是没有解法 —— 本文将带你了解如何借助 Python + OCR 技术,从扫描 PDF 中提取可编辑文本。 为什么…...

重复从网页复制文字到编辑器的Autohotkey自动化代码

为了下某本小说,用feiyuetools录了一段ahk v1的代码,经过阅读与删改之后,得到了以下代码,备忘一下。 原来记录的脚本有很多垃圾代码,必须手工整理与清洁。好在弄好之后,还是很好用的,稳定性不错。 操作:用chrome打开某网站页面,打开notepad4.exe, 开始记录脚本,修改…...

WeakMap 应用场景与示例

WeakMap 是 JavaScript 中一种非常有用的数据结构,它通过弱引用机制来帮助管理内存,防止内存泄漏。简单来说,当你用一个对象作为 WeakMap 的键时,WeakMap 不会阻止这个对象被垃圾回收器回收。一旦这个对象在其他地方没有被引用了,它以及它在 WeakMap 中对应的值就会被自动…...

node,nvm,nrm,npm扫盲

Node相关Node >> 指 Node.js ,Node.js发布于2009年5月,由Ryan Dahl开发,是一个基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动、非阻塞式I/O模型, 让JavaScript 运行在服务端的开发平台,它让JavaScript成为与PHP、Python、Perl、Ruby等服务端语言平起…...

使用 conda 懒加载的方式减少 PowerShell 的启动时间

使用 scoop 安装的 miniconda3,在 PowerShell 中进行了 conda init 初始化(注意:不是 Windows PowerShell,见下图,PowerShell 是在 Microsoft Store 中安装的,link)问题:启动速度太慢,大概需要 4 秒 于是进行优化,原理:不要在 profile 里直接运行 conda 的 heavy ho…...

深入 Spring MVC 底层:从 DispatcherServlet 到自定义组件的全链路解析 - 实践

深入 Spring MVC 底层:从 DispatcherServlet 到自定义组件的全链路解析 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "…...

podman 替代docker

podman machine init podman machine start podman machine stop podman machine set --rootless podman machine start--- 她说, 她是仙,她不是神...

202404_古剑山杯_数独

拼图,gapsTags:拼图,gaps 0x00. 题目 附件路径:https://pan.baidu.com/s/1GyH7kitkMYywGC9YJeQLJA?pwd=Zmxh#list/path=/CTF附件 附件名称:202404_古剑山杯_数独.zip 0x01. WP 01.解压缩后发现一个image.png类似拼图02.使用工具gaps进行自动拼图 gaps run image.png output.…...

m1芯片装windows系统使用感受

m1芯片装Windows系统使用感受 随着技术的不断进步,苹果公司在其Mac系列电脑中引入了自家设计的M1芯片。这款芯片不仅在性能上有着显著的提升,同时也带来了能效比上的巨大飞跃。然而,对于一些用户来说,macOS可能无法满足他们的所有需求,特别是那些需要运行特定Windows应用程…...

mac book怎么切换windows系统

如何在MacBook上安装并切换Windows系统 一、引言 随着科技的发展和个人需求的多样化,许多人可能会遇到需要在苹果公司的MacBook上运行Windows操作系统的情况。这可能是因为某些特定的应用程序或游戏只能在Windows环境下运行,或者用户希望体验不同操作系统的特色。本篇文章将详…...

硬件内在函数

AVX-512支持:SIMD的终极形态AVX-512支持:SIMD的终极形态// 优化的数值计算// 优化前的代码 public double[] ProcessData(double[] input) {var result = new double[input.Length];for (int i = 0; i < input.Length; i++){result[i] = Math.Sin(input[i]) * Math.Cos(in…...

202205_宁波市赛_DocDocDoc

DOCX,PNG高度隐写,凯撒密码Tags:DOCX,PNG高度隐写,凯撒密码 0x00. 题目 附件路径:https://pan.baidu.com/s/1GyH7kitkMYywGC9YJeQLJA?pwd=Zmxh#list/path=/CTF附件 附件名称:202205_第五届市赛_DocDocDoc.zip 0x01. WP解压文件后的到一docx文件,查看内容无明显信息将扩展名…...

DP题

1.区间dp--精妙状态设计与转移 https://codeforces.com/contest/2129/problem/D dp[l][r][a][b]: 表示只考虑放置[l,r]区间内部的数,并且满足所有的s[l]~s[r]的条件,对l-1的贡献是a,对r+1的贡献是b的方案数 转移: 对于dp[l][r][a][b]来说,枚举第一个放置的数,比如说是k,如果k对…...

LGP7115 [NOIP 2020] 移球游戏 学习笔记

LGP7115 [NOIP 2020] 移球游戏 学习笔记 Luogu Link 前言\(\texttt{NOIP2020}\) 笑传之 \(\texttt{Change Content of Balls.in}\)。致敬传奇修改文件选手我也不知道是谁。 题意简述 你面前有 \(n+1\) 根柱子。对于前 \(n\) 个柱子,每根上有 \(m\) 个球,而第 \(n+1\) 根初始是…...

阿里为何建议MVC+Manager层混合架构?

MVC 架构的弊端 Manager 层的特征 Manager 层使用案例传统三层架构代码示 引入 Manager 层后的代码示例初入编程世界时,前辈们总会教导我们,系统设计应遵循 MVC(Model - View - Controller) 架构。MVC 架构就像一个精巧的齿轮组,将整个系统清晰地划分为 Model(模型)、Vi…...

Android(Kotlin)+ ML Kit:移动端英文数字验证码识别实战

1 概述与适用场景 在移动端直接对截图或拍照的英文数字验证码做识别,可以用于自动化测试、无障碍辅助或内部工具。使用 Google ML Kit 的 Text Recognition(可离线运行)可以避免服务端延迟。为了提升识别率,我们在前端加入图像预处理(灰度、二值化、去噪和放大)再送给 OC…...

用Android(Kotlin)+ ML Kit:移动端英文数字验证码识别实战

1 概述与适用场景 在移动端直接对截图或拍照的英文数字验证码做识别,可以用于自动化测试、无障碍辅助或内部工具。使用 Google ML Kit 的 Text Recognition(可离线运行)可以避免服务端延迟。为了提升识别率,我们在前端加入图像预处理(灰度、二值化、去噪和放大)再送给 OC…...

“人工智能+”的坚硬内核,边缘地带的“数字火种”:大模型如何烧出一片新天地

本文由大模型根据作者提问生成,仅修改标题。一场由“失业工程师+过气博主+快烂了的瓜”引发的变革,正悄然重塑中国AI的商业逻辑。如果你只关注科技头条,你会觉得AI的故事属于巨头:万亿参数、军备竞赛、AGI威胁论。但在主流视野之外,真正的革命正在边缘地带发生。这里没有光…...

详细介绍:10:00开始面试,10:06就出来了,问的问题有点变态。。。

详细介绍:10:00开始面试,10:06就出来了,问的问题有点变态。。。pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier Ne…...

PHP启动报错:liboing.so.5:cannot op如何处理?

在 PHP 启动时报错 liboing.so.5: cannot open shared object file: No such file or directory 是因为系统无法找到或加载 liboing.so.5 共享库。这通常是由于以下原因之一导致的:库文件缺失:系统中没有安装 liboing.so.5 文件。 路径未配置:系统未正确配置动态链接库路径。…...

时空倒流 Time - 题解

设 \(x\) 轮为负操作,\(y\) 轮为正操作,\(d\) 为需要修改的差值(正负同理,取正即可),那么可更改成的范围为 \(-x \times R +y*L \sim -x*L+y*R\),令上式为 \(M \sim N\),易得 M 为最小的可能值,N 为最大的可能值,可通过不断给 M 加 1,使 M 变成 N。 综上,枚举 \(x \…...

202508_QQ_XORPNG

Tags:XOR,PNGLSB 0x00. 题目 附件路径:https://pan.baidu.com/s/1GyH7kitkMYywGC9YJeQLJA?pwd=Zmxh#list/path=/CTF附件 附件名称:202508_QQ_XOR&PNG 0x01. WP 01.十六进制编辑器核对 打开发现文件内容并非PNG常见文件头,尝试与常见PNG文件头XOR发现循环的KEY为kelaibe…...

Voice Agent 全球开发者比赛,TEN Dev Challenge 2025 等你来战!

TEN Dev Challenge 2025 全球开发者大赛现已启动,本次赛事聚焦实时交互与对话式AI领域,面向全球开发者开放参与通道。无论您是独立开发者,还是 3 人以内的小型开发团队,均可通过线上形式参与,并有机会角逐总计 1.1 万美元奖金,同时获得行业级展示与合作资源。💰 USD 11…...

第02周 预习:Java基础语法2、面向对象入门 - hohohoho--

第02周 预习:Java基础语法2、面向对象入门项目名称 内容课程名称 java班级 网安2413学生姓名 王璐学号 202421336068预习 1.1 学习目标掌握引用类型及常见类:数组、数组列表(ArrayList)、方法及引用类型作为方法参数 掌握类、对象、方法、属性相关基本概念,对象的初始化。 能…...