1. 最优化(Optimization)

  • 定义:最优化是寻找能使得损失函数值最小化的参数W的过程。

注:给的是损失优化问题的一个简单定义,并不是完整的最优化数学定义。

  • 给定函数\(f(x)\),其中\(x\)是输入数据的向量,需要计算函数\(f(x)\)相对于\(x\)的梯度,也就是\(\Delta f(x)\)

1.1. 梯度下降算法

模型的训练即是寻找合适的w和b以最小化代价函数值。

  • 参数\(w\)和\(b\)的更新公式为:
• \[w:=w-\alpha \frac{dJ(w,b)}{dw}, b:=b-\alpha\frac{dJ(w,b)}{db}
\]

其中 \(\alpha\) 表示学习速率,即每次更新的 \(w\) 的步伐长度。当 \(w\) 大于最优解 \({w}'\) 时,导数大于0,那么 \(w\) 就会向更小的方向更新。反之当 \(w\) 小于最优解 \({w}'\) 时,导数小于0,那么 \(w\)

1.2. 链式法则计算复合表达式

  • 假设\(J(a,b,c)=3(a+bc)\),\(a=7,b=4,c=2,v=15,J=45\)流程图表示如下:

graph LR
b(b)-->u(u=bc)
c(c)-->u(u=bc)
u-->v(v=a+u)
a--->v
v-->j(J=3v)

反向计算不难得出:

\(\frac{dJ}{dv}=3\),\(\frac{dv}{da}=\frac{dv}{du}=1\),\(\frac{du}{db}=c=2\),\(\frac{du}{dc}=b=4\)
则
• \(\frac{dJ}{da}=\frac{dJ}{dv}\frac{dv}{da}=3*1=3\)
• \(\frac{dJ}{db}=\frac{dJ}{dv}\frac{dv}{du}\frac{du}{db}=3*1*2=6\)
• \(\frac{dJ}{dc}=\frac{dJ}{dv}\frac{dv}{du}\frac{du}{dc}=3*1*4=12\)
1.2.1. 逻辑回归的链式法则推导过程

逻辑回归的梯度下降过程计算,首先从前往后的计算图得出如下:

• \(z=w^Tx+b\)
• \(\hat{y}=a=\sigma(z)=\frac{1}{1+e^{-z}}\)
• \(L(\hat{y},y)=-(y\log{a})-(1-y)\log(1-a)\)

那么计算图从前向后过程为(假设样本有两个特征):

graph LR
w1-->z(z=w1x1+w2x2+b)
x1-->z
w2-->z
x2-->z
b(b)-->z
z-->hat_y("^y=a=σ(z)")
hat_y-->J("J=(a,y)")

计算出\(J\)关于\(z\)的导数的计算公式:

• \(\frac{dJ}{da}=-\frac{y}{a}+\frac{1-y}{1-a}\)
• \(\frac{da}{dz}=\frac{d\sigma}{dz}=a(1-a)\)
\[\frac{dJ}{dz}=\frac{dJ}{da}\frac{da}{dz}=(-\frac{y}{a}+\frac{1-y}{1-a})(a(1-a))=(-y+ay)+(a-ay)=a-y
\]

所以这样可以求出总损失相对于\(w_1,w_2,b\)参数的某一点导数,从而可以更新参数

• \(\frac{dJ}{dw_1}=\frac{dJ}{dz}\frac{dz}{dw_1}=dz·x_1=(a-y)x_1\)
• \(\frac{dJ}{dw_2}=\frac{dJ}{dz}\frac{dz}{dw_2}=dz·x_2=(a-y)x_2\)
• \(\frac{dJ}{db}=dz·1=a-y\)
1.2.2. 案例:逻辑回归前向与反向传播简单计算

假设简单的模型为\(y=sigmod(w_1x_1+w_2x_2+b)\),其中点\((x_1,x_2)=(-1,-2)\),目标值为\(1\),假设给一个初始化\(w_1=2,w_2=-3,b=-3\),下面用代码呈现上面的过程。

# 假设一些随机数据和权重,以及目标结果1
w = [2, -3, -3]  # 权重
x = [-1, -2]  # 数据
y = 1  # 目标值

# 前向传播
z = w[0]*x[0] + w[1]*x[1] + w[2]
# 预测该样本的概率
a = 1.0 / (1 + np.exp(-z))
# 将概率与目标值进行损失计算
cost = np.sum(y * np.log(a) + (1 - y) * np.log(1 - a))

# 对神经元反向传播
# 点积变量的梯度,使用sigmoid函数求导
dz = a - y
# 回传计算x梯度
dx = [w[0] * dz, w[1] * dz]
# 回传计算w梯度(在点(-1,-2)位置)
dw = [x[0] * dz, x[1] * dz, 1.0 * dz]

可以看出\(w_1,w_2,b\)是在这次更新时收到\(x\)的输入影响梯度的计算的。

1.3. 反向传播的向量化编程实现

训练期间会有m个样本,每个样本都要进行梯度下降计算,所以要在所有样本上计算结果及梯度等操作。

\[J(w,b)=\frac{1}{m}\sum_{i=1}^{m}{L(a^{(i)},y^{(i)})}
\]
1.3.1 向量化反向传播实现

思路

\(z_1=w^Tx_1+b\)

\(z_2=w^Tx_2+b\)

\(z_3=w^Tx_3+b\)

\(a_1=\sigma(z_1)\)

\(a_2=\sigma(z_2)\)

\(a_3=\sigma(z_3)\)

  • 可以写成
• \[\bar{w}=
\begin{pmatrix}
w_1\\
\vdots\\
w_n
\end{pmatrix},
\bar{x}=
\begin{pmatrix}
\vdots & \vdots & \vdots & \vdots & \vdots \\
x_1 & x_2 & x_3 & \vdots & x_m \\
\vdots & \vdots & \vdots & \vdots & \vdots
\end{pmatrix}
\]
注:\(\bar{w}\) 的形状为(n,1),\(\bar{x}\)
  • 让\(Z=W^TX+b=(z_1,z_2,z_3,...,z_m)+b=np.dot(W^T,X)+b\),得出的结果为\([1,n]·[n,m]=[1,m]\)大小的矩阵(大写的\(W,X\)为多个样本表示)。
    伪代码实现
初始化,假设n个特征,m个样本
\(J=0,W=np.zeros([n,1]),b=0\)
\(Z=np.dot(W^T,X)+b\)
\(A=\sigma(Z)\)
\(L=(1/m)np.sum(Y*np.log(A)+(1-Y)*np.log(1-A))\)
每个样本梯度计算过程为:
\(dZ=A-Y\)
\(dW=\frac{1}{m}XdZ^T\)
\(db=\frac{1}{m}np.sum(dZ)\)
更新:
\(W:=W-\alpha dW\)
\(b:=b-\alpha db\)
  • 这相当于一次使用了M个样本的所有特征值与目标值,如果想多次迭代,就使得这M个样本重复计算若干次。
1.3.2. 代码实现
# 随机初始化权重
W = np.random.random([2, 1])
X = np.random.random([2, 10])
b = 0.0
Y = np.array([0, 1, 1, 0, 1, 1, 0, 1, 0, 0])

Z = np.dot(W.T, X) + b
A = 1.0 / (1 + np.exp(-Z))
cost = -1 / 10 * np.sum(Y * np.log(A) + (1 - Y) * np.log(1 - A))

# 形状:A:[1,10]  Y:[1,10]
dZ = A - Y
# [2,10] * ([1,10].T) = [2,1]
dW = (1.0 / 10) * np.dot(X, dZ.T)
db = (1.0 / 10) * np.sum(dZ)