PID神经网络原理与MATLAB实现(SISO)

2024-03-03 10:20

本文主要是介绍PID神经网络原理与MATLAB实现(SISO),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


最近想实现一下PID神经网络,但是书籍和博客都令人头疼,主要是卡在误差反向传播的计算过程中。找一篇通俗易懂的文章实在不易,最终,只能自己静下心,仔细琢磨。只要每一步在逻辑上都是合理的,我们有理由相信能够得到正确的结果。抱着这样的心态,由浅入深,来实现一下。


一、网络结构定义

简单起见,假设一个受控系统单输入 u u u 单输出 x x x ,使用一个PID神经网络来作为控制器,使得系统输出达到目标值 z z z

网络结构定义

其中, W 3 × 2 W_{3\times 2} W3×2 V 1 × 3 V_{1\times 3} V1×3分别为输入层—隐含层权重矩阵和隐含层—输出层权重矩阵。 y i y_i yi y o y_o yo分别是PID计算前后向量。 z , x , u z, x, u z,x,u分别为期望值、实际值和网络输出,都是数。


1.1 前向传播

根据以上结构,容易得出前向传播如下。注意简介起见,第 ( k ) (k) (k)次的标注省略。

y i 1 = w 11 z + w 12 x y i 2 = w 21 z + w 22 x y i 3 = w 31 z + w 32 x (1) \begin{aligned} & y_{i1} = w_{11} z + w_{12} x \\ & y_{i2} = w_{21} z + w_{22} x \\ & y_{i3} = w_{31} z + w_{32} x \tag{1} \end{aligned} yi1=w11z+w12xyi2=w21z+w22xyi3=w31z+w32x(1)

y o 1 = y i 1 y o 2 = y o 2 ( k − 1 ) + y i 2 y o 3 = y i 3 ( k ) − y i 3 ( k − 1 ) (2) \begin{aligned} & y_{o1} = y_{i1} \\ & y_{o2} = y_{o2}(k-1) + y_{i2} \\ & y_{o3} = y_{i3}(k) - y_{i3}(k-1) \tag{2} \end{aligned} yo1=yi1yo2=yo2(k1)+yi2yo3=yi3(k)yi3(k1)(2)

u = v 11 y o 1 + v 12 y o 2 + v 13 y o 3 (3) u = v_{11} y_{o1} + v_{12} y_{o2} + v_{13} y_{o3} \tag{3} u=v11yo1+v12yo2+v13yo3(3)

不妨设受控对象为一个类似二次函数的时变系统(用于仿真),则输出及损失函数为

x = u 2 ( k ) + 0.1 x ( k − 1 ) (4) x = u^2(k) + 0.1x(k-1) \tag{4} x=u2(k)+0.1x(k1)(4)

J = 1 2 ( z − x ) 2 (5) J = \frac{1}{2} (z-x)^2 \tag{5} J=21(zx)2(5)


1.2 反向传播

前向传播计算完成,以下开始反向传播。首先考虑权重 V V V 对误差 J J J 的影响,也即计算 ∂ J ∂ V \frac{\partial J}{\partial V} VJ,根据前向传递函数关系,易得

∂ J ∂ v 11 = ∂ J ∂ x ∂ x ∂ u ∂ u ∂ v 11 = − ( z − x ) ⋅ ∂ x ∂ u ⋅ y o 1 (6) \begin{aligned} \frac{\partial J}{\partial v_{11}} &= \frac{\partial J}{\partial x} \frac{\partial x}{\partial u} \frac{\partial u}{\partial v_{11}} \\ &= -(z-x) \cdot \frac{\partial x}{\partial u}\cdot y_{o1} \end{aligned} \tag{6} v11J=xJuxv11u=(zx)uxyo1(6)

由于受控对象的数学模型往往不知道或者偏导难以计算,中间的 ∂ x ∂ u \frac{\partial x}{\partial u} ux 常用下式子代替

∂ x ∂ u = s i g n x ( k ) − x ( k − 1 ) u ( k ) − u ( k − 1 ) (7) \frac{\partial x}{\partial u} ={\rm sign} \frac{x(k) - x(k-1)}{u(k) - u(k-1)} \tag{7} ux=signu(k)u(k1)x(k)x(k1)(7)

以下 ∂ x ∂ u \frac{\partial x}{\partial u} ux皆由式(7)得出,不在赘述。

其中 s i g n ( ⋅ ) {\rm sign}(\cdot) sign()是符号函数,输入正数输出 1 1 1,输入负数输出 − 1 -1 1,输入 0 0 0输出 0 0 0。使用符号函数的好处是归一化到 [ − 1 , 1 ] [-1,1] [1,1],避免分母很接近0导致输出很大的问题。

同理易得

∂ J ∂ v 12 = − ( z − x ) ⋅ ∂ x ∂ u ⋅ y o 2 (8) \frac{\partial J}{\partial v_{12}} = -(z-x) \cdot \frac{\partial x}{\partial u} \cdot y_{o2} \tag{8} v12J=(zx)uxyo2(8)

∂ J ∂ v 13 = − ( z − x ) ⋅ ∂ x ∂ u ⋅ y o 3 (9) \frac{\partial J}{\partial v_{13}} = -(z-x) \cdot \frac{\partial x}{\partial u} \cdot y_{o3} \tag{9} v13J=(zx)uxyo3(9)


接下来,计算真正的难点 ∂ J ∂ W \frac{\partial J}{\partial W} WJ

∂ J ∂ w 11 = ∂ J ∂ x ∂ x ∂ u ∂ u ∂ y o 1 ∂ y o 1 ∂ y i 1 ∂ y i 1 ∂ w 11 = − ( z − x ) ⋅ ∂ x ∂ u ⋅ v 11 ⋅ 1 ⋅ z (10) \begin{aligned} \frac{\partial J}{\partial w_{11}} &= \frac{\partial J}{\partial x} \frac{\partial x}{\partial u} \frac{\partial u}{\partial y_{o1}} \frac{\partial y_{o1}}{\partial y_{i1}} \frac{\partial y_{i1}}{\partial w_{11}} \\ &=-(z-x) \cdot\frac{\partial x}{\partial u} \cdot v_{11} \cdot 1 \cdot z \end{aligned} \tag{10} w11J=xJuxyo1uyi1yo1w11yi1=(zx)uxv111z(10)

同理易得

∂ J ∂ w 12 = ∂ J ∂ x ∂ x ∂ u ∂ u ∂ y o 1 ∂ y o 1 ∂ y i 1 ∂ y i 1 ∂ w 12 = − ( z − x ) ⋅ ∂ x ∂ u ⋅ v 11 ⋅ 1 ⋅ x (11) \begin{aligned} \frac{\partial J}{\partial w_{12}} &= \frac{\partial J}{\partial x} \frac{\partial x}{\partial u} \frac{\partial u}{\partial y_{o1}} \frac{\partial y_{o1}}{\partial y_{i1}} \frac{\partial y_{i1}}{\partial w_{12}} \\ &=-(z-x) \cdot\frac{\partial x}{\partial u} \cdot v_{11} \cdot 1 \cdot x \end{aligned} \tag{11} w12J=xJuxyo1uyi1yo1w12yi1=(zx)uxv111x(11)


∂ J ∂ w 21 = ∂ J ∂ x ∂ x ∂ u ∂ u ∂ y o 2 ∂ y o 2 ∂ y i 2 ∂ y i 2 ∂ w 21 = − ( z − x ) ⋅ ∂ x ∂ u ⋅ v 12 ⋅ ∂ y o 2 ∂ y i 2 ⋅ z (12) \begin{aligned} \frac{\partial J}{\partial w_{21}} &= \frac{\partial J}{\partial x} \frac{\partial x}{\partial u} \frac{\partial u}{\partial y_{o2}} \frac{\partial y_{o2}}{\partial y_{i2}} \frac{\partial y_{i2}}{\partial w_{21}} \\ &=-(z-x) \cdot \frac{\partial x}{\partial u} \cdot v_{12} \cdot \frac{\partial y_{o2}}{\partial y_{i2}} \cdot z \end{aligned} \tag{12} w21J=xJuxyo2uyi2yo2w21yi2=(zx)uxv12yi2yo2z(12)

其中

∂ y o 2 ∂ y i 2 = Δ y o 2 Δ y i 2 = y o 2 ( k ) − y o 2 ( k − 1 ) y i 2 ( k ) − y i 2 ( k − 1 ) = y i 2 ( k ) y i 2 ( k ) − y i 2 ( k − 1 ) \frac{\partial y_{o2}}{\partial y_{i2}} = \frac{\Delta y_{o2}}{\Delta y_{i2}} = \frac{y_{o2}(k) - y_{o2}(k-1)}{y_{i2}(k) - y_{i2}(k-1)} = \frac{y_{i2}(k) }{y_{i2}(k) - y_{i2}(k-1)} yi2yo2=Δyi2Δyo2=yi2(k)yi2(k1)yo2(k)yo2(k1)=yi2(k)yi2(k1)yi2(k)

和之前一样,使用符号函数计算可得

∂ y o 2 ∂ y i 2 = s i g n y i 2 ( k ) y i 2 ( k ) − y i 2 ( k − 1 ) (13) \frac{\partial y_{o2}}{\partial y_{i2}} = {\rm sign}\frac{y_{i2}(k) }{y_{i2}(k) - y_{i2}(k-1)} \tag{13} yi2yo2=signyi2(k)yi2(k1)yi2(k)(13)

同理

∂ J ∂ w 22 = ∂ J ∂ x ∂ x ∂ u ∂ u ∂ y o 2 ∂ y o 2 ∂ y i 2 ∂ y i 2 ∂ w 22 = − ( z − x ) ⋅ ∂ x ∂ u ⋅ v 12 ⋅ ∂ y o 2 ∂ y i 2 ⋅ x (14) \begin{aligned} \frac{\partial J}{\partial w_{22}} &= \frac{\partial J}{\partial x} \frac{\partial x}{\partial u} \frac{\partial u}{\partial y_{o2}} \frac{\partial y_{o2}}{\partial y_{i2}} \frac{\partial y_{i2}}{\partial w_{22}} \\ &=-(z-x) \cdot \frac{\partial x}{\partial u} \cdot v_{12} \cdot \frac{\partial y_{o2}}{\partial y_{i2}} \cdot x \end{aligned} \tag{14} w22J=xJuxyo2uyi2yo2w22yi2=(zx)uxv12yi2yo2x(14)


∂ J ∂ w 31 = ∂ J ∂ x ∂ x ∂ u ∂ u ∂ y o 3 ∂ y o 3 ∂ y i 3 ∂ y i 3 ∂ w 31 = − ( z − x ) ⋅ ∂ x ∂ u ⋅ v 13 ⋅ ∂ y o 3 ∂ y i 3 ⋅ z (15) \begin{aligned} \frac{\partial J}{\partial w_{31}} &= \frac{\partial J}{\partial x} \frac{\partial x}{\partial u} \frac{\partial u}{\partial y_{o3}} \frac{\partial y_{o3}}{\partial y_{i3}} \frac{\partial y_{i3}}{\partial w_{31}} \\ &=-(z-x) \cdot \frac{\partial x}{\partial u} \cdot v_{13} \cdot \frac{\partial y_{o3}}{\partial y_{i3}} \cdot z \end{aligned} \tag{15} w31J=xJuxyo3uyi3yo3w31yi3=(zx)uxv13yi3yo3z(15)

其中

∂ y o 3 ∂ y i 3 = Δ y o 3 Δ y i 3 = y o 3 ( k ) − y o 3 ( k − 1 ) y i 3 ( k ) − y i 3 ( k − 1 ) = y o 3 ( k ) − y o 3 ( k − 1 ) y o 3 ( k ) \frac{\partial y_{o3}}{\partial y_{i3}} = \frac{\Delta y_{o3}}{\Delta y_{i3}} = \frac{y_{o3}(k) - y_{o3}(k-1)}{y_{i3}(k) - y_{i3}(k-1)} = \frac{y_{o3}(k) - y_{o3}(k-1)}{y_{o3}(k)} yi3yo3=Δyi3Δyo3=yi3(k)yi3(k1)yo3(k)yo3(k1)=yo3(k)yo3(k)yo3(k1)

依旧使用符号函数归一化

∂ y o 3 ∂ y i 3 = s i g n y o 3 ( k ) − y o 3 ( k − 1 ) y o 3 ( k ) \frac{\partial y_{o3}}{\partial y_{i3}} ={\rm sign} \frac{y_{o3}(k) - y_{o3}(k-1)}{y_{o3}(k)} yi3yo3=signyo3(k)yo3(k)yo3(k1)

同理

∂ J ∂ w 32 = ∂ J ∂ x ∂ x ∂ u ∂ u ∂ y o 3 ∂ y o 3 ∂ y i 3 ∂ y i 3 ∂ w 32 = − ( z − x ) ⋅ ∂ x ∂ u ⋅ v 13 ⋅ ∂ y o 3 ∂ y i 3 ⋅ x (16) \begin{aligned} \frac{\partial J}{\partial w_{32}} &= \frac{\partial J}{\partial x} \frac{\partial x}{\partial u} \frac{\partial u}{\partial y_{o3}} \frac{\partial y_{o3}}{\partial y_{i3}} \frac{\partial y_{i3}}{\partial w_{32}} \\ &=-(z-x) \cdot \frac{\partial x}{\partial u} \cdot v_{13} \cdot \frac{\partial y_{o3}}{\partial y_{i3}} \cdot x \end{aligned} \tag{16} w32J=xJuxyo3uyi3yo3w32yi3=(zx)uxv13yi3yo3x(16)


1.3 权值更新

如果使用传统的梯度下降法,则更新公式为

W = W − α 1 ∂ J ∂ W (17) W = W - \alpha_1 \frac{\partial J}{\partial W} \tag{17} W=Wα1WJ(17)

V = V − α 2 ∂ J ∂ V (18) V = V - \alpha_2 \frac{\partial J}{\partial V} \tag{18} V=Vα2VJ(18)

其中, α 1 , α 2 \alpha_1, \alpha_2 α1,α2为学习率。

有时,也增加动量项,则更新公式为

W d ( k ) = β 1 W d ( k − 1 ) + ( 1 − β 1 ) ∂ J ∂ W W = W − α 1 W d (19) \begin{aligned} & W_d(k) = \beta_1 W_d(k-1) + (1-\beta_1)\frac{\partial J}{\partial W}\\ & W = W - \alpha_1 W_d \tag{19} \end{aligned} Wd(k)=β1Wd(k1)+(1β1)WJW=Wα1Wd(19)

V d ( k ) = β 2 V d ( k − 1 ) + ( 1 − β 2 ) ∂ J ∂ V V = V − α 2 V d (20) \begin{aligned} & V_d(k) = \beta_2 V_d(k-1) + (1-\beta_2)\frac{\partial J}{\partial V}\\ & V = V - \alpha_2 V_d \tag{20} \end{aligned} Vd(k)=β2Vd(k1)+(1β2)VJV=Vα2Vd(20)

其中, β 1 , β 2 \beta_1, \beta_2 β1,β2为动量因子,一般取 0.9; W d , V d W_d, V_d Wd,Vd 可取初始值为0进行迭代。

1.4 实际问题

以上算法纯属理论,在仿真中可能都会遇到问题。一个典型的问题是除数为0的情况,在这里很容易发生,例如 u ( k ) = u ( k − 1 ) u(k) = u(k-1) u(k)=u(k1) 就是如此。另一个问题是数值过大,比如除数很小就很容易导致这种现象。因此有必要规避除数为0,也要做好限幅工作。写程序时需要注意这些细节。


二、仿真

现假设受控系统是一个式(4) 描述的系统,设期望值为一个阶梯信号,整个程序如下

%% 参数初始化
% 初始化
rng(1);                         % 设置随机数种子
z = 1;                          % 目标值
x = 0;                          % 实际值初始化
W = 0.3*(rand(3,2) - 0.5);      % 隐含层—输入层权重矩阵
V = 0.3*(rand(3,1) - 0.5);      % 输出层—隐含层权重矩阵%学习率
alpha1 = 0.06;                  % W权重相关学习率
alpha2 = 0.02;                  % V权重相关学习率% 动量因子
beta1 = 0.9;
beta2 = 0.9;% 限幅值
xmax = 1;
ymax = 1;
umax = 1;% 变量初始化,_1 结尾为上一次的值,_2 结尾表示上两个时刻值
yi3_1 = 0;                      
u_1 = 0;
u_2 = 0;
x_1 = 0;
yi2_1 = 0;
yo3_1 = 0;
dW = 0;                         % W变化量,初始化为0
dV = 0;
yo = zeros(3,1);     xdata = [];                     % 记录实际值
zdata = [];                     % 记录期望值N = 2000;                       % 迭代次数
for k=1:N%% 设置目标值if k <500z = 0.5;elseif k < 1000z = 0.2;elseif k < 1500 z = 0.8;elsez = 0;end%% 正向传播% 隐含层输入yi = W * [z; x];                % 隐含层输出yo(1) = yi(1);                  % P  yo(2) = yo(2) + yi(2);          % Iyo(3) = yi(3) - yi3_1;          % Dyo(find(yo>ymax)) = ymax;       % 限幅yo(find(yo<-ymax)) = -ymax;% 输出层输出u = V' * yo;                    u(find(u>umax)) = umax;u(find(u<-umax)) = -umax;% 受控对象输出x = u^2 + 0.1*x_1;                  x(find(x>xmax)) = xmax;         x(find(x<-xmax)) = -xmax;% 损失函数J = 1/2 * (z-x)^2;              %% 反向传播% 计算J对V的偏导数 pJ_pVpJ_px = -(z-x);temp = u - u_1;                     % u(k)-u(k-1)if temp >= 0 && temp < 1e-20        % 除数限幅在(-,-1e-20][1e-20,+)temp = 1e-20;                   % 防止除数为0elseif temp < 0 && temp > -1e-20temp = -1e-20;endpx_pu = sign((x - x_1) / temp);     % 使用sign,限幅在{-1,0,1}pJ_pV = pJ_px * px_pu * yo;         % 误差对V的偏导,yo为3x1向量% 计算J对W的偏导数 pJ_pWpJ_pw11 = pJ_px * px_pu * V(1) * 1 * z;pJ_pw12 = pJ_px * px_pu * V(1) * 1 * x;temp = yi(2) - yi2_1;               % yi(k) - yi(k-1)if temp >= 0 && temp < 1e-20        % 除数限幅在(-,-1e-20][1e-20,+)temp = 1e-20;                   % 防止除数为0elseif temp < 0 && temp > -1e-20temp = -1e-20;endpJ_pw21 = pJ_px * px_pu * V(2) * (sign(yi(2)/temp)) * z;pJ_pw22 = pJ_px * px_pu * V(2) * (sign(yi(2)/temp)) * x;temp = yo(3) ;                      % yo(k)if temp >= 0 && temp < 1e-20        % 除数限幅在(-,-1e-20][1e-20,+)temp = 1e-20;                   % 防止除数为0elseif temp < 0 && temp > -1e-20temp = -1e-20;endpJ_pw31 = pJ_px * px_pu * V(3) * (sign((yo(3) - yo3_1)/temp)) * z;pJ_pw32 = pJ_px * px_pu * V(3) * (sign((yo(3) - yo3_1)/temp)) * x;pJ_pW = [pJ_pw11, pJ_pw12; pJ_pw21, pJ_pw22; pJ_pw31, pJ_pw32]; % 误差对W的偏导%% 动量法权重更新dW = beta1 * dW + (1-beta1) * pJ_pW;        % 动量法计算增量dV = beta2 * dV + (1-beta2) * pJ_pV;W = W - alpha1 * dW;                        % 权重更新V = V - alpha2 * dV;%% 其他数据更新u_2 = u_1;u_1 = u;                                    % u(k-1)x_1 = x;                                    % x(k-1)yi2_1 = yi(2);                              % yi2_1 为上一次 yi(2)yo3_1 = yo(3);                              % yo3_1 为上一次 yo(3)xdata = [xdata; x];                         % 记录数据方便绘图zdata = [zdata; z];
end%% 可视化
plot(1:N, zdata, 'LineWidth',1.5);hold on;      % 目标值
plot(1:N, xdata, '.'); hold off                 % 实际值
legend('目标值','实际值')                        % 标注
title('PID神经网络');                           % 标题

运行结果如下

在这里插入图片描述

可见,跟踪效果很不错。不过,和所有神经网络一样,参数要小心谨慎,如果设置不合理,就达不到期望的效果。


四、总结与展望

和PID和神经网络相比,PID神经网络有什么美妙之处呢?其实,它结合了两者的优势,PID使用了反馈控制,好的PID参数使得实际值快速稳定地接近目标值,但是有时候很难调节出一组好的PID参数。神经网络的优势是,通过误差反馈传播调整权值,这个过程是自动完成的。PID参数已经通过权值的方式隐藏在网络结构中,使用反向传播时该参数自动得到优化。

其实,PID神经网络大显身手的地方是多输入多输出的耦合系统的控制,下一次,就用来做一个无人机姿态控制器。

— 完 —

这篇关于PID神经网络原理与MATLAB实现(SISO)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/769325

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import