關於 Softmax 迴歸的反向傳播求導數過程
阿新 • • 發佈:2020-12-26
對於 $Softmax$ 迴歸的正向傳播非常簡單,就是對於一個輸入 $X$ 對每一個輸入標量 $x_i$ 進行加權求和得到 $Z$ 然後對其做概率歸一化。
## Softmax 示意圖
下面看一個簡單的示意圖:
![image](https://tva4.sinaimg.cn/large/006VTcCxgy1gm05gv92alj317s0b7t9d.jpg)
其中 $X\in\mathbb{R}^{n\times m}$ 是一個向量或矩陣,這取決於傳入的是一個訓練樣本還是一組訓練樣本,其中 $n$ 是輸入特徵的數量,$m$ 是傳入的訓練樣本數量;此圖只是示意的一個簡單的 $Softmax$ 的傳播單元,可以把它理解為一個神經單元,所以 $W\in\mathbb{R}^{n\times k}$ 可以看成每一個 $X$ 的特徵的權重,$W$ 是向量還是矩陣同樣取決於第二層(圖中第一個方框)有多少個神經單元,用 $k$ 表示第二層的數量,$b\in\mathbb{R}$ 為偏置(bias)單元。
## 全連線神經網路
上圖只是一個廣泛的 $Softmax$ 的示意圖,下面用一個神經網路表示。
![image](https://tva1.sinaimg.cn/large/006VTcCxly1gm0hr5c0mqj31e80q80we.jpg)
上圖是更廣義的 $L$ 層全連線神經網路,其中,$l_1$ 表示第一層的神經元數量,$l_L$ 表示最後一層,即第 $L$ 層的神經元數量,根據 $Softmax$ 模型,假設此神經網路用作 $C$ 分類的網路,$l_L$ 的數量也是 $C$ 的數量;$\hat{y}\in\mathbb{R}^{C\times m}$ 可以是向量也可以是矩陣,同樣取決於是否對一組輸入進行了 **向量化(vectorization)** ,表示每一個樣本的預測概率;最後將 $\hat{y}$ 傳入損失函式 $\ell(y,\hat{y})$ 對其計算損失,公式如下:
$$
\ell(y,\hat{y})=-\sum_{i=1}^Cy_i\ln{\hat{y}_i}
$$
最後,定義該網路一組訓練樣本的 $cost$ 損失函式:
$$
J(W,b)=\frac{1}{m}\sum_{i=1}^m\ell(y^{(i)},\hat{y}^{(i)})
$$
## 反向傳播求導推理
下面對神經網路進行反向傳播的求導推導。
首先,神經網路前向傳播的最後一個操作是計算單個樣本上的損失度 $\ell(y,\hat{y})$ 所以首先計算 $\frac{\partial\ell}{\partial\hat{y}}$ 由於神經網路的最後一層 $a^{[L]}$ 就是 $\hat{y}$ 的預測輸出,所以就是對 $a^{[L]}$ 求導:
$$
\begin{split}
\frac{\partial\ell}{\partial a^{[L]}}&=\frac{\partial}{\partial a^{[L]}}\left(-\sum_{i=1}^Cy_i\ln{\hat{y}_i}\right)\\
&=\frac{\partial}{\partial a^{[L]}}\left(-(y_1\ln{\hat{y}_1}+y_2\ln{\hat{y}_2}+\dots+y_C\ln{\hat{y}_C})\right)\\
&=\frac{\partial}{\partial a^{[L]}}\left(-(y_1\ln{a^{[L]}_1}+y_2\ln{a^{[L]}_2}+\dots+y_C\ln{a^{[L]}_C})\right)
\end{split}
$$
可以從公式中看到,$a^{[L]}\in\mathbb{R}^{C\times 1}$ 是一個向量,而 $\ell\in\mathbb{R}$ 則為一個標量,根據 **標量對向量** 求導的法則,可以得到:
$$
\begin{split}
\frac{\partial\ell}{\partial a^{[L]}}&=\frac{\partial}{\partial a^{[L]}}\left(-(y_1\ln{a^{[L]}_1}+y_2\ln{a^{[L]}_2}+\dots+y_C\ln{a^{[L]}_C})\right)\\
&=\begin{bmatrix}
\frac{\partial}{\partial a^{[L]}_1}\left(-(y_1\ln{a^{[L]}_1}+y_2\ln{a^{[L]}_2}+\dots+y_C\ln{a^{[L]}_C})\right)&
\frac{\partial}{\partial a^{[L]}_2}\left(-(y_1\ln{a^{[L]}_1}+y_2\ln{a^{[L]}_2}+\dots+y_C\ln{a^{[L]}_C})\right)&
\dots&
\frac{\partial}{\partial a^{[L]}_C}\left(-(y_1\ln{a^{[L]}_1}+y_2\ln{a^{[L]}_2}+\dots+y_C\ln{a^{[L]}_C})\right)
\end{bmatrix}\\
&=\begin{bmatrix}
-\frac{y_1}{a^{[L]}_1}&
-\frac{y_2}{a^{[L]}_2}&
\dots&
-\frac{y_C}{a^{[L]}_C}&
\end{bmatrix}\\
&=-\frac{y}{a^{[L]}}\\
&=-\frac{y}{\hat{y}}
\end{split}
$$
得到 $\frac{\partial\ell}{\partial a^{[L]}}$ 後,下面就繼續對 $\frac{\partial\ell}{\partial z^{[L]}}$ 求偏導,因為 $a$ 是關於 $z$ 的函式,所以使用鏈式求導法則 $\frac{\partial\ell}{\partial z^{[L]}}=\frac{\partial\ell}{\partial a^{[L]}}\frac{\partial a^{[L]}}{\partial z^{[L]}}$ 下面計算 $\frac{\partial a^{[L]}}{\partial z^{[L]}}$ 又因為 $a^{[L]},z^{[L]}\in\mathbb{R}^{C\times 1}$ 都是相同維度的向量,所以根據 **向量對向量** 求導的法則,可以得到:
$$
\begin{split}
\frac{\partial a^{[L]}}{\partial z^{[L]}}&=\begin{bmatrix}
\frac{\partial a^{[L]}}{\partial z^{[L]}_1}&
\frac{\partial a^{[L]}}{\partial z^{[L]}_2}&
\dots&
\frac{\partial a^{[L]}}{\partial z^{[L]}_C}
\end{bmatrix}
\end{split}
$$
可以觀察上式子中,$z^{[L]}_i\in\mathbb{R}$ 是一個標量,$a^{[L]}$ 為向量,所以使用 **向量對向量** 的求導法則:
$$
\begin{split}
\frac{\partial a^{[L]}}{\partial z^{[L]}}&=\begin{bmatrix}
\frac{\partial a^{[L]}}{\partial z^{[L]}_1}&
\frac{\partial a^{[L]}}{\partial z^{[L]}_2}&
\dots&
\frac{\partial a^{[L]}}{\partial z^{[L]}_C}
\end{bmatrix}\\
&=\begin{bmatrix}
\frac{\partial}{\partial z^{[L]}_1}\left(\frac{e^{z^{[L]}}}{\sum_{i=1}^Ce^{z^{[L]}_i}} \right)&
\frac{\partial}{\partial z^{[L]}_2}\left(\frac{e^{z^{[L]}}}{\sum_{i=1}^Ce^{z^{[L]}_i}} \right)&
\dots&
\frac{\partial}{\partial z^{[L]}_C}\left(\frac{e^{z^{[L]}}}{\sum_{i=1}^Ce^{z^{[L]}_i}} \right)
\end{bmatrix}
\end{split}
$$
我們拿出來第一個元素 $\frac{\partial}{\partial z_1^{[L]}}\left(\frac{e_z^{[L]}}{\sum_{i=1}^Ce^{z^{[L]}_i}}\right)$ 對其研究,發現是 **向量對標量** 求導,我們將其展開:
$$
\begin{split}
\frac{\partial}{\partial z_1^{[L]}}\left(\frac{e^{z^{[L]}}}{\sum_{i=1}^Ce^{z^{[L]}_i}}\right)&=\begin{bmatrix}
\frac{\partial}{\partial z^{[L]}_1}\left(\frac{e^{z_1^{[L]}}}{\sum_{i=1}^Ce^{z^{[L]}_i}}\right)&
\frac{\partial}{\partial z^{[L]}_1}\left(\frac{e^{z_2^{[L]}}}{\sum_{i=1}^Ce^{z^{[L]}_i}}\right)&
\dots&
\frac{\partial}{\partial z^{[L]}_1}\left(\frac{e^{z_C^{[L]}}}{\sum_{i=1}^Ce^{z^{[L]}_i}}\right)&
\end{bmatrix}
\end{split}
$$
我們可以從這個式子中發現一個規律,在對 $\frac{\partial a^{[L]}}{\partial z^{[L]}}$ 求導的展開式中,每一項都會有一個分母項 $z_i^{[L]}$ 和分子的向量中的一個元素 $e^{z_i^{[L]}}$ 相對應,分子中的其它項 $e^{z_j^{[L]}}$ 就與之不對應;
比如在第一個元素 $\frac{\partial a^{[L]}_1}{\partial z^{[L]}_1}$ 中,$a$ 和 $z$ 的下標都相同,所以可以得到:
$$
\begin{split}
\frac{\partial a^{[L]}_1}{\partial z_1^{[L]}}
&=\frac{\partial}{\partial z_1^{[L]}}\left(\frac{e^{z_1^{[L]}}}{\sum_{i=1}^Ce^{z_i^{[L]}}}\right)\\
&=\frac{(e^{z_1^{[L]}})'\sum_{i=1}^Ce^{z_i^{[L]}}-e^{z_1^{[L]}}(e^{z_1^{[L]}}+e^{z_2^{[L]}}+\dots+e^{z_C^{[L]}})'}{\left(\sum_{i=1}^Ce^{z_i^{[L]}}\right)^2}\\
&=\frac{e^{z_1^{[L]}}\sum_{i=1}^Ce^{z_i^{[L]}}-e^{z_1^{[L]}}e^{z_1^{[L]}}}{\left(\sum_{i=1}^Ce^{z_i^{[L]}}\right)^2}\\
&=\frac{e^{z_1^{[L]}}\sum_{i=1}^Ce^{z_i^{[L]}}}{(\sum_{i=1}^Ce^{z_i^{[L]}})^2}-\frac{e^{z_1^{[L]}}e^{z_1^{[L]}}}{(\sum_{i=1}^Ce^{z_i^{[L]}})^2}\\
&=\frac{e^{z_1^{[L]}}}{\sum_{i=1}^Ce^{z_i^{[L]}}}-\frac{e^{z_1^{[L]}}}{\sum_{i=1}^Ce^{z_i^{[L]}}}\frac{e^{z_1^{[L]}}}{\sum_{i=1}^Ce^{z_i^{[L]}}}\\
&=a^{[L]}_1(1-a^{[L]}_1)
\end{split}
$$
我們可以將其對推廣到其它的求導式子中,即當 $i=j$ 時,我們可以得到:
$$
\frac{\partial e^{z^{[L]}_i}}{\partial z^{[L]}_j}=a^{[L]}_i(1-a^{[L]}_i)
$$
如果,當 $i\neq j$ 時,我們得到(由於使用的 $i,j$ 作為下標,故將分母的 $\Sigma$ 累加和的下標使用 $k$ 替換):
$$
\begin{split}
\frac{\partial a^{[L]}_i}{\partial z_j^{[L]}}
&=\frac{\partial}{\partial z_j^{[L]}}\left(\frac{e^{z_i^{[L]}}}{\sum_{k=1}^Ce^{z_k^{[L]}}}\right)\\
&=\frac{(e^{z_i^{[L]}})'\sum_{k=1}^Ce^{z_k^{[L]}}-e^{z_i^{[L]}}(e^{z_1^{[L]}}+e^{z_2^{[L]}}+\dots+e^{z_C^{[L]}})'}{\left(\sum_{k=1}^Ce^{z_k^{[L]}}\right)^2}\\
&=\frac{0\sum_{i=1}^Ce^{z_i^{[L]}}-e^{z_i^{[L]}}e^{z_j^{[L]}}}{\left(\sum_{k=1}^Ce^{z_k^{[L]}}\right)^2}\\
&=\frac{-e^{[L]}_ie^{[L]}_j}{(\sum_{k=1}^Ce^{[L]}_k)^2}\\
&=-\frac{e^{[L]}_i}{\sum_{k=1}^Ce^{[L]}_k}\frac{e^{[L]}_j}{\sum_{k=1}^Ce^{[L]}_k}\\
&=-a_ia_j
\end{split}
$$
然後我們寫出 $\frac{\partial a^{[L]}}{\partial z^{[L]}}$ 的 **雅可比矩陣(jacobian matrix)** :
$$
\begin{split}
\frac{\partial a^{[L]}}{\partial z^{[L]}}&=\begin{bmatrix}
\frac{\partial a^{[L]}_1}{\partial z^{[L]}_1}&\frac{\partial a^{[L]}_1}{\partial z^{[L]}_2}&\dots&\frac{\partial a^{[L]}_1}{\partial z^{[L]}_C}\\
\frac{\partial a^{[L]}_2}{\partial z^{[L]}_1}&\frac{\partial a^{[L]}_2}{\partial z^{[L]}_2}&\dots&\frac{\partial a^{[L]}_2}{\partial z^{[L]}_C}\\
\vdots&\vdots&\ddots&\vdots\\
\frac{\partial a^{[L]}_C}{\partial z^{[L]}_1}&\frac{\partial a^{[L]}_C}{\partial z^{[L]}_2}&\dots&\frac{\partial a^{[L]}_C}{\partial z^{[L]}C}
\end{bmatrix}\\
\end{split}
$$
我們發現除了對角線上即 $i=j$ 時的求導為 $a_i(1-a_j)$ 而矩陣的其它元素即 $i\neq j$ 時求導為 $-a_ia_j$ 。
至此,我們求出來了 $\frac{\partial\ell}{\partial a^{[L]}}$ 和 $\frac{\partial a^{[L]}}{\partial z^{[L]}}$ 所以下面我們就開始計算 $\frac{\partial\ell}{\partial z^{[L]}}$ 的導數:
$$
\frac{\partial\ell}{\partial z^{[L]}}=\frac{\partial\ell}{\partial a^{[L]}}\frac{\partial a^{[L]}}{\partial z^{[L]}}
$$
首先我們先看第一項,在上面我們已經求出了 $\frac{\partial\ell}{\partial a^{[L]}}$ 的導數即 $-\frac{y}{a^{[L]}}$ 首先我們從分子和分母中可以看到 $y,a^{[L]}\in\mathbb{R}^{1\times C}$ 兩個都是一個 $1\times C$ 的向量,所以可以得到 $\frac{\partial\ell}{\partial a^{[L]}}$ 也是一個 $1\times C$ 向量;而式子的第二項我們剛剛求出了其 **雅可比矩陣** $\frac{\partial a^{[L]}}{\partial z^{[L]}}\in\mathbb{R}^{C\times C}$ 是一個 $C\times C$ 的矩陣,而在對 $\frac{\partial\ell}{\partial z^{[L]}}$ 將向量和矩陣相乘,所以我們得到了一個 $1\times C$ 的向量:
$$
\begin{split}
\frac{\partial\ell}{\partial a^{[L]}}\frac{\partial a^{[L]}}{\partial z^{[L]}}&=
\begin{bmatrix}
-\frac{y_1}{a^{[L]}_1}&-\frac{y_2}{a^{[L]}_2}&\dots&-\frac{y_C}{a^{[L]}_C}
\end{bmatrix}
\begin{bmatrix}
\frac{\partial a^{[L]}_1}{\partial z^{[L]}_1}&\frac{\partial a^{[L]}_1}{\partial z^{[L]}_2}&\dots&\frac{\partial a^{[L]}_1}{\partial z^{[L]}_C}\\
\frac{\partial a^{[L]}_2}{\partial z^{[L]}_1}&\frac{\partial a^{[L]}_2}{\partial z^{[L]}_2}&\dots&\frac{\partial a^{[L]}_2}{\partial z^{[L]}_C}\\
\vdots&\vdots&\ddots&\vdots\\
\frac{\partial a^{[L]}_C}{\partial z^{[L]}_1}&\frac{\partial a^{[L]}_C}{\partial z^{[L]}_2}&\dots&\frac{\partial a^{[L]}_C}{\partial z^{[L]}C}
\end{bmatrix}\\
&=\begin{bmatrix}
-\frac{y_1}{a^{[L]}_1}\frac{\partial a^{[L]}_1}{\partial z^{[L]}_1}-\frac{y_2}{a^{[L]}_2}\frac{\partial a^{[L]}_2}{\partial z^{[L]}_1}-\dots-\frac{y_C}{a^{[L]}_C}\frac{\partial a^{[L]}_C}{\partial z^{[L]}_1}\\
-\frac{y_1}{a^{[L]}_1}\frac{\partial a^{[L]}_1}{\partial z^{[L]}_2}-\frac{y_2}{a^{[L]}_2}\frac{\partial a^{[L]}_2}{\partial z^{[L]}_2}-\dots-\frac{y_C}{a^{[L]}_C}\frac{\partial a^{[L]}_C}{\partial z^{[L]}_2}\\
\vdots\\
-\frac{y_1}{a^{[L]}_1}\frac{\partial a^{[L]}_1}{\partial z^{[L]}_C}-\frac{y_2}{a^{[L]}_2}\frac{\partial a^{[L]}_2}{\partial z^{[L]}_C}-\dots-\frac{y_C}{a^{[L]}_C}\frac{\partial a^{[L]}_C}{\partial z^{[L]}_C}\\
\end{bmatrix}^T\\
&=\begin{bmatrix}
-\sum_{i=1}^C\frac{y_i}{a^{[L]}_i}\frac{\partial a^{[L]}_i}{\partial z^{[L]}_1}&
-\sum_{i=1}^C\frac{y_i}{a^{[L]}_i}\frac{\partial a^{[L]}_i}{\partial z^{[L]}_2}&
\dots&
-\sum_{i=1}^C\frac{y_i}{a^{[L]}_i}\frac{\partial a^{[L]}_i}{\partial z^{[L]}_C}
\end{bmatrix}
\end{split}
$$
注意第二行得到的結果應該是一個 $1\times C$ 的向量,由於排版所以將其轉置成 $C\times 1$ 的向量,不過這毫不影響推導。
所以,根據式子的最後一步,我們可以得到,對於 $a^{[L]}$ 上的所有元素 $a^{[L]}_j$ 我們可以得到一個更統一化的式子:
$$
\begin{split}
\frac{\partial\ell}{\partial a^{[L]}}\frac{\partial a^{[L]}}{\partial z^{[L]}}&=\begin{bmatrix}
-\sum_{i=1}^C\frac{y_i}{a^{[L]}_i}\frac{\partial a^{[L]}_i}{\partial a^{[L]}_1}&
-\sum_{i=1}^C\frac{y_i}{a^{[L]}_i}\frac{\partial a^{[L]}_i}{\partial a^{[L]}_2}&
\dots&
-\sum_{i=1}^C\frac{y_i}{a^{[L]}_i}\frac{\partial a^{[L]}_i}{\partial a^{[L]}_C}
\end{bmatrix}\\
&=\begin{bmatrix}
-\sum_{i=1}^C\frac{y_i}{a^{[L]}_i}\frac{\partial a^{[L]}_i}{\partial a^{[L]}_j}
\end{bmatrix},j=[1,2,\dots,C]\\
\end{split}
$$
我們觀察式子的最後一項關於 $\frac{\partial a^{[L]}_i}{\partial a^{[L]}_j}$ 我們在上面求出了其導數有兩種情況:
$$
\begin{split}
\frac{\partial a^{[L]}_i}{\partial a^{[L]}_j}=\left\{\begin{matrix}
a_i(1-a_j)&&i=j\\
-a_ia_j&&i\neq j
\end{matrix}\right.
\end{split}
$$
我們將這個結果帶回到上面的式子中:
$$
\begin{split}
-\sum_{i=1}^C\frac{y_i}{a^{[L]}_i}\frac{\partial a^{[L]}_i}{\partial z^{[L]}_j}&=
\left\{\begin{matrix}
-\sum_{i=1}^C\frac{y_i}{a^{[L]}_i}a^{[L]}_i(1-a^{[L]}_j)&&i=j\\
\sum_{i=1}^C\frac{y_i}{a^{[L]}_i}a^{[L]}_ia^{[L]}_j&&i\neq j
\end{matrix}\right.\\
&=
\left\{\begin{matrix}
-\sum_{i=1}^Cy_i(1-a^{[L]}_j)&&i=j\\
\sum_{i=1}^Cy_ia^{[L]}_j&&i\neq j
\end{matrix}\right.\\
&=
\left\{\begin{matrix}
\sum_{i=1}^Cy_ia^{[L]}_j-y_i&&i=j\\
\sum_{i=1}^Cy_ia^{[L]}_j&&i\neq j
\end{matrix}\right.
\end{split}
$$
注意雖然最後把式子推導成為兩個部分,但是最原始的式子就是要把每一項 $i$ 與 $j$ 全部加起來,不過就是因為要區別對待 $i,j$ 相不相等的情況,這裡當 $i=j$ 時只有一項,所以可以將前面的 $\sum$ 符號去掉,然後把後面的 $i\neq j$ 的項加起來,我們可以得到:
$$
\begin{split}
-\sum_{i=1}^C\frac{y_i}{a^{[L]}_i}\frac{\partial a^{[L]}_i}{\partial z^{[L]}_j}&=
{\color{red}{-y_j+y_ja^{[L]}_j}}+{\color{green}{\sum_{i\in{\{i|i\neq j\}}}y_ia^{[L]}_j}}\\
&=-y_j+{\color{blue}{y_ja^{[L]}_j+\sum_{i\in{\{i|i\neq j\}}}y_ia^{[L]}_j}}\\
&=-y_j+{\color{orange}{\sum_{i=1}^Cy_ia^{[L]}_j}}\\
&=-y_j+a^{[L]}_j\sum_{i=1}^Cy_i\\
&=a^{[L]}_i-y_j\\
&=a^{[L]}-y
\end{split}
$$
首先說明一下式子的第一行,紅色的部分是 $i=j$ 的情況,只有一項,所以不需要用 $\sum$ 符號表示,綠色部分是當 $i\neq j$ 的情況;
在第二行中,我們可以看到,在整個藍色的式子中,第一項是 $i=j$ 的情況,而後面是 $i\neq j$ 的所有項加起來,我們可以發現第一項的 $y_j$ 正好補充了後面的 $\sum$ 求和的部分,所以將這兩項合併就到了 $\sum_{i=1}^C$ 的項;
因為下標是 $i$ 索引的,所以將常數項 $a^{[L]}_j$ 提到前面來;此時觀察後面的 $\sum_{i=1}^Cy_i$ 項,根據 $Softmax$ 多分類的情況,$y$ 是由一個 $1$ 和其它全 $0$ 組成的,所以對 $y$ 進行累加和,我們得到的是 $1$
最後,整理下式子,我們就能得到 $\frac{\partial\ell}{\partial z^{[L]}}$ 的導數為 $a^{[L]}-y$
## 總結
$Softmax$ 迴歸的啟用部分,和使用 $Sigmoid/ReLu$ 作為啟用函式是有所不同的,因為在 $Sigmoid/ReLu$ 中,每一個神經元計算得到 $z$ 後不需要將其它神經元的 $z$ 全部累加起來做概率的 **歸一化** ;也就是說以往的 $Sigmoid/ReLu$ 作為啟用函式,每一個神經元由 $z$ 計算 $a$ 時是獨立於其它的神經元的;所以在反向傳播求導數的時候,我們就能發現當計算 $\frac{\partial a}{\partial z}$ 的時候,不再是單獨的一一對應的關係,而是像正向傳播那樣,將上一層的結果全部整合到每一個神經元上,下面的圖中,紅色箭頭表示了 $Softmax$ 和 $Sigmoid/ReLu$ 的反向傳播的路徑的有所不同。
![image](https://tva4.sinaimg.cn/large/006VTcCxly1gm15ngqdafj31e50ucwj6.jpg)
在上圖中, $Softmax$ 層的啟用的反向傳播,可以看到每一個 $a^{[L]}_i$ 都回饋到了不同的 $z^{[L]}_j$ 的神經元上;其中紅色的線表示了 $i=j$ 的情況,其它藍色的線表明了 $i\neq j$ 的情況,這也說明了為什麼在 $Softmax$ 裡的求導中會出現兩種情況;反觀第一層中的 $Sigmoid/ReLu$ 啟用中,每一個對 $z^{[1]}_i$ 的啟用都是在本地的神經元中得到的,沒有其它神經單元傳入的情況,所以也沒有複雜的分下標 $i,j$ 討論求導的情況。
## 參考部落格
1. [https://www.cnblogs.com/zhaopAC/p/9539118.html](https://www.cnblogs.com/zhaopAC/p/9539118.html)
2. [https://blog.csdn.net/xxuffei/article/details/90022008](https://blog.csdn.net/xxuffei/article/details/9