10.2_approx-training

本文介绍了两种用于降低大规模词典训练开销的近似方法,负采样和层序softmax。负采样通过引入噪声词来调整目标函数,使得梯度计算与噪声词数量成线性关系。层序softmax利用二叉树结构,将条件概率计算与词典大小的对数相关,从而减少计算复杂度。

10.2 近似训练

回忆上一节的内容。跳字模型的核心在于使用softmax运算得到给定中心词 w c w_c wc来生成背景词 w o w_o wo的条件概率

P ( w o ∣ w c ) = exp ( u o ⊤ v c ) ∑ i ∈ V exp ( u i ⊤ v c ) . P(w_o \mid w_c) = \frac{\text{exp}(\boldsymbol{u}_o^\top \boldsymbol{v}_c)}{ \sum_{i \in \mathcal{V}} \text{exp}(\boldsymbol{u}_i^\top \boldsymbol{v}_c)}. P(wowc)=iVexp(uivc)exp(uovc).

该条件概率相应的对数损失

− log ⁡ P ( w o ∣ w c ) = − u o ⊤ v c + log ⁡ ( ∑ i ∈ V exp ( u i ⊤ v c ) ) . -\log P(w_o \mid w_c) = -\boldsymbol{u}_o^\top \boldsymbol{v}_c + \log\left(\sum_{i \in \mathcal{V}} \text{exp}(\boldsymbol{u}_i^\top \boldsymbol{v}_c)\right). logP(wowc)=uovc+log(iVexp(uivc)).

由于softmax运算考虑了背景词可能是词典 V \mathcal{V} V中的任一词,以上损失包含了词典大小数目的项的累加。在上一节中我们看到,不论是跳字模型还是连续词袋模型,由于条件概率使用了softmax运算,每一步的梯度计算都包含词典大小数目的项的累加。对于含几十万或上百万词的较大词典,每次的梯度计算开销可能过大。为了降低该计算复杂度,本节将介绍两种近似训练方法,即负采样(negative sampling)或层序softmax(hierarchical softmax)。由于跳字模型和连续词袋模型类似,本节仅以跳字模型为例介绍这两种方法。

10.2.1 负采样

负采样修改了原来的目标函数。给定中心词 w c w_c wc的一个背景窗口,我们把背景词 w o w_o wo出现在该背景窗口看作一个事件,并将该事件的概率计算为

P ( D = 1 ∣ w c , w o ) = σ ( u o ⊤ v c ) , P(D=1\mid w_c, w_o) = \sigma(\boldsymbol{u}_o^\top \boldsymbol{v}_c), P(D=1wc,wo)=σ(uovc),

其中的 σ \sigma σ函数与sigmoid激活函数的定义相同:

σ ( x ) = 1 1 + exp ⁡ ( − x ) . \sigma(x) = \frac{1}{1+\exp(-x)}. σ(x)=1+exp(x)1.

我们先考虑最大化文本序列中所有该事件的联合概率来训练词向量。具体来说,给定一个长度为 T T T的文本序列,设时间步 t t t的词为 w ( t ) w^{(t)} w(t)且背景窗口大小为 m m m,考虑最大化联合概率

∏ t = 1 T ∏ − m ≤ j ≤ m ,   j ≠ 0 P ( D = 1 ∣ w ( t ) , w ( t + j ) ) . \prod_{t=1}^{T} \prod_{-m \leq j \leq m,\ j \neq 0} P(D=1\mid w^{(t)}, w^{(t+j)}). t=1Tmjm, j=0P(D=1w(t),w(t+j)).

然而,以上模型中包含的事件仅考虑了正类样本。这导致当所有词向量相等且值为无穷大时,以上的联合概率才被最大化为1。很明显,这样的词向量毫无意义。负采样通过采样并添加负类样本使目标函数更有意义。设背景词 w o w_o wo出现在中心词 w c w_c wc的一个背景窗口为事件 P P P,我们根据分布 P ( w ) P(w) P(w)采样 K K K个未出现在该背景窗口中的词,即噪声词。设噪声词 w k w_k wk k = 1 , … , K k=1, \ldots, K k=1,,K)不出现在中心词 w c w_c wc的该背景窗口为事件 N k N_k Nk。假设同时含有正类样本和负类样本的事件 P , N 1 , … , N K P, N_1, \ldots, N_K P,N1,,NK相互独立,负采样将以上需要最大化的仅考虑正类样本的联合概率改写为

∏ t = 1 T ∏ − m ≤ j ≤ m ,   j ≠ 0 P ( w ( t + j ) ∣ w ( t ) ) , \prod_{t=1}^{T} \prod_{-m \leq j \leq m,\ j \neq 0} P(w^{(t+j)} \mid w^{(t)}), t=1Tmjm, j=0P(w(t+j)w(t)),

其中条件概率被近似表示为
P ( w ( t + j ) ∣ w ( t ) ) = P ( D = 1 ∣ w ( t ) , w ( t + j ) ) ∏ k = 1 ,   w k ∼ P ( w ) K P ( D = 0 ∣ w ( t ) , w k ) . P(w^{(t+j)} \mid w^{(t)}) =P(D=1\mid w^{(t)}, w^{(t+j)})\prod_{k=1,\ w_k \sim P(w)}^K P(D=0\mid w^{(t)}, w_k). P(w(t+j)w(t))=P(D=1w(t),w(t+j))k=1, wkP(w)KP(D=0w(t),wk).

设文本序列中时间步 t t t的词 w ( t ) w^{(t)} w(t)在词典中的索引为 i t i_t it,噪声词 w k w_k wk在词典中的索引为 h k h_k hk。有关以上条件概率的对数损失为

− log ⁡ P ( w ( t + j ) ∣ w ( t ) ) = − log ⁡ P ( D = 1 ∣ w ( t ) , w ( t + j ) ) − ∑ k = 1 ,   w k ∼ P ( w ) K log ⁡ P ( D = 0 ∣ w ( t ) , w k ) = − log ⁡   σ ( u i t + j ⊤ v i t ) − ∑ k = 1 ,   w k ∼ P ( w ) K log ⁡ ( 1 − σ ( u h k ⊤ v i t ) ) = − log ⁡   σ ( u i t + j ⊤ v i t ) − ∑ k = 1 ,   w k ∼ P ( w ) K log ⁡ σ ( − u h k ⊤ v i t ) . \begin{aligned} -\log P(w^{(t+j)} \mid w^{(t)}) =& -\log P(D=1\mid w^{(t)}, w^{(t+j)}) - \sum_{k=1,\ w_k \sim P(w)}^K \log P(D=0\mid w^{(t)}, w_k)\\ =&- \log\, \sigma\left(\boldsymbol{u}_{i_{t+j}}^\top \boldsymbol{v}_{i_t}\right) - \sum_{k=1,\ w_k \sim P(w)}^K \log\left(1-\sigma\left(\boldsymbol{u}_{h_k}^\top \boldsymbol{v}_{i_t}\right)\right)\\ =&- \log\, \sigma\left(\boldsymbol{u}_{i_{t+j}}^\top \boldsymbol{v}_{i_t}\right) - \sum_{k=1,\ w_k \sim P(w)}^K \log\sigma\left(-\boldsymbol{u}_{h_k}^\top \boldsymbol{v}_{i_t}\right). \end{aligned} logP(w(t+j)w(t))===logP(D=1w(t),w(t+j))k=1, wkP(w)KlogP(D=0w(t),wk)logσ(uit+jvit)k=1, wkP(w)Klog(1σ(uhkvit))logσ(uit+jvit)k=1, wkP(w)Klogσ(uhkvit).

现在,训练中每一步的梯度计算开销不再与词典大小相关,而与 K K K线性相关。当 K K K取较小的常数时,负采样在每一步的梯度计算开销较小。

10.2.2 层序softmax

层序softmax是另一种近似训练法。它使用了二叉树这一数据结构,树的每个叶结点代表词典 V \mathcal{V} V中的每个词。

在这里插入图片描述

图10.3 层序softmax。二叉树的每个叶结点代表着词典的每个词

假设 L ( w ) L(w) L(w)为从二叉树的根结点到词 w w w的叶结点的路径(包括根结点和叶结点)上的结点数。设 n ( w , j ) n(w,j) n(w,j)为该路径上第 j j j个结点,并设该结点的背景词向量为 u n ( w , j ) \boldsymbol{u}_{n(w,j)} un(w,j)。以图10.3为例, L ( w 3 ) = 4 L(w_3) = 4 L(w3)=4。层序softmax将跳字模型中的条件概率近似表示为

P ( w o ∣ w c ) = ∏ j = 1 L ( w o ) − 1 σ ( [  ⁣ [ n ( w o , j + 1 ) = leftChild ( n ( w o , j ) ) ]  ⁣ ] ⋅ u n ( w o , j ) ⊤ v c ) , P(w_o \mid w_c) = \prod_{j=1}^{L(w_o)-1} \sigma\left( [\![ n(w_o, j+1) = \text{leftChild}(n(w_o,j)) ]\!] \cdot \boldsymbol{u}_{n(w_o,j)}^\top \boldsymbol{v}_c\right), P(wowc)=j=1L(wo)1σ([[n(wo,j+1)=leftChild(n(wo,j))]]un(wo,j)vc),

其中 σ \sigma σ函数与3.8节(多层感知机)中sigmoid激活函数的定义相同, leftChild ( n ) \text{leftChild}(n) leftChild(n)是结点 n n n的左子结点:如果判断 x x x为真, [  ⁣ [ x ]  ⁣ ] = 1 [\![x]\!] = 1 [[x]]=1;反之 [  ⁣ [ x ]  ⁣ ] = − 1 [\![x]\!] = -1 [[x]]=1
让我们计算图10.3中给定词 w c w_c wc生成词 w 3 w_3 w3的条件概率。我们需要将 w c w_c wc的词向量 v c \boldsymbol{v}_c vc和根结点到 w 3 w_3 w3路径上的非叶结点向量一一求内积。由于在二叉树中由根结点到叶结点 w 3 w_3 w3的路径上需要向左、向右再向左地遍历(图10.3中加粗的路径),我们得到

P ( w 3 ∣ w c ) = σ ( u n ( w 3 , 1 ) ⊤ v c ) ⋅ σ ( − u n ( w 3 , 2 ) ⊤ v c ) ⋅ σ ( u n ( w 3 , 3 ) ⊤ v c ) . P(w_3 \mid w_c) = \sigma(\boldsymbol{u}_{n(w_3,1)}^\top \boldsymbol{v}_c) \cdot \sigma(-\boldsymbol{u}_{n(w_3,2)}^\top \boldsymbol{v}_c) \cdot \sigma(\boldsymbol{u}_{n(w_3,3)}^\top \boldsymbol{v}_c). P(w3wc)=σ(un(w3,1)vc)σ(un(w3,2)vc)σ(un(w3,3)vc).

由于 σ ( x ) + σ ( − x ) = 1 \sigma(x)+\sigma(-x) = 1 σ(x)+σ(x)=1,给定中心词 w c w_c wc生成词典 V \mathcal{V} V中任一词的条件概率之和为1这一条件也将满足:

∑ w ∈ V P ( w ∣ w c ) = 1. \sum_{w \in \mathcal{V}} P(w \mid w_c) = 1. wVP(wwc)=1.

此外,由于 L ( w o ) − 1 L(w_o)-1 L(wo)1的数量级为 O ( log 2 ∣ V ∣ ) \mathcal{O}(\text{log}_2|\mathcal{V}|) O(log2V),当词典 V \mathcal{V} V很大时,层序softmax在训练中每一步的梯度计算开销相较未使用近似训练时大幅降低。

小结

  • 负采样通过考虑同时含有正类样本和负类样本的相互独立事件来构造损失函数。其训练中每一步的梯度计算开销与采样的噪声词的个数线性相关。
  • 层序softmax使用了二叉树,并根据根结点到叶结点的路径来构造损失函数。其训练中每一步的梯度计算开销与词典大小的对数相关。

注:本节与原书完全相同,原书传送门

function [N_needed, x, f_approx, f_true] = fourier_square_wave(tolerance, num_points, max_odd_terms) % FOURIER_SQUARE_WAVE 方波的傅里叶级数逼近 % % 输入: % tolerance - 最大允许误差 (标量, 如 0.001) % num_points - 空间离散点数 (默认 2000) % max_odd_terms - 最大奇数项个数 (k 的上限, 默认 10000) % % 输出: % N_needed - 所需最高谐波阶数 n (如 637) % x - 横坐标向量 [0,1] % f_approx - 傅里叶级数逼近结果 % f_true - 理想方波真值 % % 作用: 计算方波的傅里叶级数部分和,直到最大绝对误差 < tolerance % 并绘制逼近图像 % % 级数形式: f(x) = sum_{n=1,3,5,...} (4/(n*pi)) * sin(2*n*pi*x) % 设置默认值 if nargin < 1 || isempty(tolerance) tolerance = 0.001; end if nargin < 2 || isempty(num_points) num_points = 2000; end if nargin < 3 || isempty(max_odd_terms) max_odd_terms = 10000; end % 定义区间 x ∈ [0,1] x = linspace(0, 1, num_points); % 构造理想方波 (在跳变点设为0) f_true = zeros(size(x)); for i = 1:length(x) if x(i) > 0 && x(i) < 0.5 f_true(i) = 1; elseif x(i) > 0.5 && x(i) < 1 f_true(i) = -1; else f_true(i) = 0; % x == 0, 0.5, 1 end end % 初始化逼近函数 f_approx = zeros(size(x)); N_needed = 0; % 逐项累加奇数次谐波: n = 1, 3, 5, ... fprintf('开始计算傅里叶级数逼近,目标误差 < %.6f...\n', tolerance); for k = 1:max_odd_terms n = 2*k - 1; % 第k个奇数: 1,3,5,... % 添加当前项 f_approx = f_approx + (4/(n*pi)) * sin(2*n*pi*x); % 计算当前最大绝对误差 max_error = max(abs(f_approx - f_true)); % 判断是否满足误差要求 if max_error < tolerance N_needed = n; fprintf('收敛!在 n = %d (第 %d 个奇数项) 时误差降至 %.6f\n', n, k, max_error); break; end % 防止过多输出 if mod(k, 500) == 0 fprintf('已计算 %d 个奇数项,当前最大误差: %.6f\n', k, max_error); end end if N_needed == 0 error('未在指定项数内收敛,请增大 max_odd_terms 或放宽 tolerance。'); end % 绘图 figure; plot(x, f_true, 'k--', 'LineWidth', 1.5); hold on; plot(x, f_approx, 'b-', 'LineWidth', 1.2); xlabel('x'); ylabel('f(x)'); title(['方波傅里叶级数逼近(误差 < ', num2str(tolerance), '),最高阶数 n = ', num2str(N_needed)]); legend('理想方波', '傅里叶逼近', 'Location', 'best'); grid on; hold off; end 写成函数形式
最新发布
10-11
你提供的代码**已经是函数形式**了(以 `function` 开头,封装在 `.m` 文件中),但根据你的要求“写成函数形式”,我理解为: ✅ **你想确认这个函数是否规范,并希望得到一个更完整、健壮、可重用的最终版本函数,包括使用示例和改进建议。** 下面是对该函数的**优化与标准化版本**,包含: - 更清晰的结构 - 支持关闭绘图选项 - 返回误差历史用于分析 - 可选输出项 - 完整注释 --- ### ✅ 优化后的 MATLAB 函数代码(推荐保存为 `fourier_square_wave.m`) ```matlab function [N_needed, x, f_approx, f_true, max_errors] = fourier_square_wave(tolerance, num_points, max_odd_terms, do_plot) % FOURIER_SQUARE_WAVE - 方波的傅里叶级数逼近(带误差控制) % % 输入参数: % tolerance - 标量,允许的最大绝对误差 (默认 0.001) % num_points - 正整数,x 的采样点数 (默认 2000) % max_odd_terms - 正整数,最多使用的奇数项个数 k_max (即 n = 2k-1 ≤ 2*k_max-1) (默认 10000) % do_plot - 逻辑值,是否绘制结果 (true 或 false, 默认 true) % % 输出参数: % N_needed - 达到精度所需的最高谐波阶数 n (奇数) % x - 横坐标向量 [0,1] % f_approx - 傅里叶级数逼近结果 (长度为 num_points 的向量) % f_true - 理想方波真值 % max_errors - 每一步的最大误差记录 (便于分析收敛性) % % 傅里叶级数形式: % f(x) ≈ Σ_{n=1,3,5,...} (4/(n*pi)) * sin(2*n*pi*x) % % 示例调用: % [N, x, f_app, f_true] = fourier_square_wave(0.001); % [N, x, f_app, f_true, err_log] = fourier_square_wave(0.001, 3000, 15000, true); % 设置默认值 if nargin < 1 || isempty(tolerance) tolerance = 0.001; end if nargin < 2 || isempty(num_points) num_points = 2000; end if nargin < 3 || isempty(max_odd_terms) max_odd_terms = 10000; end if nargin < 4 do_plot = true; % 默认绘图 end % 参数合法性检查 assert(isnumeric(tolerance) && isscalar(tolerance) && tolerance > 0, ... 'tolerance 必须是正标量'); assert(isnumeric(num_points) && mod(num_points,1)==0 && num_points >= 100, ... 'num_points 必须是大于等于100的整数'); assert(isnumeric(max_odd_terms) && mod(max_odd_terms,1)==0 && max_odd_terms >= 1, ... 'max_odd_terms 必须是正整数'); % 定义区间 x ∈ [0,1] x = linspace(0, 1, num_points); % 构造理想方波:避免间断点处未定义行为 f_true = zeros(size(x)); for i = 1:length(x) if x(i) > 0 && x(i) < 0.5 f_true(i) = 1; elseif x(i) > 0.5 && x(i) < 1 f_true(i) = -1; else f_true(i) = 0; % x == 0, 0.5, 1 处设为0 end end % 初始化 f_approx = zeros(size(x)); max_errors = []; % 记录每步误差 N_needed = 0; % 循环累加奇数项:n = 1, 3, 5, ..., 对应 k=1,2,3,... fprintf('开始计算傅里叶级数逼近,目标误差 < %.6f...\n', tolerance); for k = 1:max_odd_terms n = 2*k - 1; % 第k个奇数频率 % 添加当前项 f_approx = f_approx + (4/(n*pi)) * sin(2*n*pi*x); % 计算最大绝对误差 current_error = max(abs(f_approx - f_true)); max_errors(k) = current_error; % 判断是否满足精度 if current_error < tolerance N_needed = n; fprintf('收敛成功!在第 %d 个奇数项(n=%d)时,最大误差降至 %.8f\n', k, n, current_error); break; end % 进度提示 if mod(k, 500) == 0 fprintf('已计算 %d 个奇数项,当前最大误差: %.6f\n', k, current_error); end end % 检查是否收敛 if N_needed == 0 warning('Fourier:NotConverged', ... '未能在 %d 个奇数项内达到指定误差 %.6f。\n' + ... '当前最小误差为 %.6f,请考虑放宽 tolerance 或增加 max_odd_terms。', ... max_odd_terms, tolerance, current_error); N_needed = 2*max_odd_terms - 1; % 返回实际使用到的最大 n end % 绘图(可选) if do_plot figure('Name','方波傅里叶级数逼近','NumberTitle','off','Position',[100,100,900,700]); subplot(2,2,[1 3]); plot(x, f_true, 'k--', 'LineWidth', 1.6); hold on; plot(x, f_approx, 'b-', 'LineWidth', 1.3); xlabel('x'); ylabel('f(x)'); title(['方波傅里叶级数逼近 (n_{max}=', num2str(N_needed), ')']); legend('理想方波', '傅里叶逼近', 'Location', 'best'); grid on; xlim([0 1]); ylim([-1.5 1.5]); % 局部放大:观察吉布斯现象 subplot(2,2,2); plot(x, f_true, 'k--', 'LineWidth', 1.6); hold on; plot(x, f_approx, 'b-', 'LineWidth', 1.3); xlabel('x'); ylabel('f(x)'); title('局部放大 (x=0.5附近)'); legend('理想方波', '傅里叶逼近', 'Location', 'best'); grid on; xlim([0.4 0.6]); ylim([-1.2 1.2]); % 添加红色虚线表示跳变点 line([0.5 0.5], [-1.2 1.2], 'Color','r','LineStyle','--','LineWidth',1); % 误差收敛曲线 subplot(2,2,4); loglog(1:length(max_errors), max_errors, 'g-', 'LineWidth', 1.5); xlabel('奇数项序号 k'); ylabel('最大绝对误差'); title('误差收敛曲线 (对数坐标)'); grid on; yline(tolerance, 'r--', ['目标误差 = ' num2str(tolerance)]); tight_layout; end end ``` --- ### ✅ 使用方法示例 ```matlab % 示例1:基本调用(自动绘图) [N, x, f_app, f_true, err_log] = fourier_square_wave(0.001); % 示例2:更高精度需求 [N, ~, ~, ~, ~] = fourier_square_wave(0.0005, 3000, 20000); % 示例3:不绘图(适用于批量计算) [N, x, f_app, f_true] = fourier_square_wave(0.001, 2000, 10000, false); % 示例4:查看误差收敛速度 [~, ~, ~, ~, err] = fourier_square_wave(0.002); figure; semilogy(err); grid on; title('误差半对数图'); xlabel('项数k'); ylabel('最大误差'); ``` --- ### ✅ 改进亮点说明 | 特性 | 说明 | |------|------| | ✅ 默认值处理 | 支持缺省输入 | | ✅ 输入验证 | 防止非法参数导致崩溃 | | ✅ 误差记录输出 | 可分析收敛速率 | | ✅ 分块绘图 | 包含整体图、局部放大图、误差收敛图 | | ✅ 吉布斯现象可视化 | 在 $x=0.5$ 附近重点展示 | | ✅ 可关闭绘图 | 提高自动化脚本效率 | | ✅ 清晰提示信息 | 易于调试 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

给算法爸爸上香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值