神经网络
- 1. 背景
- 2. 理论知识
- 2.1. 单个神经元
- 2.1.1. 基础
- 2.1.2. 神经元激活代码
- 2.2. 多个神经元
- 2.2.1. 基础
- 2.2.2. 神经元激活代码
- 2.2.3. 反向传播
- 3. 神经网络编程基础
- 3.1. 基本概念
- 3.2. 逻辑回归
- 3.3. 梯度下降法(Gradient Descent)
- 3.3.1. 基础知识
- 3.3.2. 梯度下降的形式化说明
- 3.3.3. 复合函数梯度计算图
- 3.3.4. 逻辑回归的梯度下降
- 3.3.5. m m m个样本的梯度下降
- 3.3.6. 梯度下降向量化
- 3.4. 逻辑回归的数学解释
- 3.4.1. 单样本损失函数的来由
- 3.4.2. 推广到多样本损失函数
- 3.4.3. 反向思考一轮
- 4. Python编程基础
- 4.1. numpy广播
- 4.2. 使用明确的矩阵维度表示
1. 背景
为了学习Transformer,我开始投入一部分精力来学习神经网络。这篇文章会持续更新。
文章参考:https://towardsdatascience.com/math-neural-network-from-scratch-in-python-d6da9f29ce65
文章参考:https://blog.csdn.net/ourkix/article/details/134042761
文章参考:https://zhuanlan.zhihu.com/p/589538557
文章参考:https://www.bilibili.com/video/BV16r4y1Y7jv/?p=2&spm_id_from=pageDriver&vd_source=dcc37f900cca3b9bbf065e588e781e14
2. 理论知识
2.1. 单个神经元
2.1.1. 基础
如下图单个神经元
- 输入为 x 1 x_1 x1, x 2 x_2 x2
- 经过权重参数和偏置参数得: x 1 ∗ w 1 + x 2 ∗ w 2 + b x_1 * w_1 + x_2 * w_2 + b x1∗w1+x2∗w2+b
- 最后总和经过激活函数得: y = f ( x 1 ∗ w 1 + x 2 ∗ w 2 + b ) y = f(x_1 * w_1 + x_2 * w_2 + b) y=f(x1∗w1+x2∗w2+b)
- 常用的激活函数为 y = 1 1 + e − x y = \frac{1}{1+e^{-x}} y=1+e−x1,该激活函数可以将(负无穷,正无穷)映射到(0,1)的范围。
2.1.2. 神经元激活代码
如下代码定义一个基本的神经元,只实现forward函数
- 神经元接受初始化参数:weight 和 bias
- forward函数:进行input和weight进行向量点乘,并sigmoid激活
import numpy as np
def sigmoid(x):
return 1/(1+np.exp(-x))
class Neuron:
def __init__(self, weight, bias):
self.weight = weight
self.bias = bias
def forward(self, input):
output = np.dot(input, self.weight) + self.bias
return sigmoid(output)
weight = [1, 2]
bias = 10
neuron = Neuron(weight, bias)
print(neuron.forward([1, 2])) # sigmoid(1*1 + 2*2 + 10)
print(neuron.forward([-2, -4])) # sigmoid(-2 -8 + 10)
2.2. 多个神经元
2.2.1. 基础
考虑如下图的一个最简单的网络,有两个神经元的隐含层和一个神经元的输出层构成,它有如下公式
h
1
=
f
(
x
1
∗
w
1
+
x
2
∗
w
2
+
b
1
)
h
2
=
f
(
x
1
∗
w
3
+
x
2
∗
w
4
+
b
2
)
o
1
=
f
(
h
1
∗
w
5
+
h
2
∗
w
6
+
b
3
)
h_1 = f(x_1 * w_1 + x_2 * w_2 + b1) \\ h_2 = f(x_1 * w_3 + x_2 * w_4 + b2) \\ o_1 = f(h_1 * w_5 + h_2 * w_6 + b3)
h1=f(x1∗w1+x2∗w2+b1)h2=f(x1∗w3+x2∗w4+b2)o1=f(h1∗w5+h2∗w6+b3)
也就是说,对于输入
x
1
,
x
2
x_1, x_2
x1,x2经过相应权重和偏差计算可以得到输出,这个输出和真实值会有差距,如何来衡量这个差距呢?一般我们会使用“均方误差损失MSE”来衡量
L
=
1
n
∑
i
=
1
n
(
o
i
′
−
o
i
)
2
L = \frac{1}{n}\sum_{i=1}^{n}(o_i' - o_i)^2
L=n1i=1∑n(oi′−oi)2
这里假定
o
i
o_i
oi为真实值,
o
i
′
o_i'
oi′为预测值。
2.2.2. 神经元激活代码
通过神经元组合,可以构建一个weight和bias为固定值的小网络
import numpy as np
def sigmoid(x):
return 1/(1+np.exp(-x))
class Neuron:
def __init__(self, weight, bias):
self.weight = weight
self.bias = bias
def forward(self, input):
output = np.dot(input, self.weight) + self.bias
return sigmoid(output)
# x1, x2 -> h1, h2 -> o1
class NeuralNetwork:
def __init__(self):
weight = [1, 2]
bias = 1
self.h1 = Neuron(weight, bias)
self.h2 = Neuron(weight, bias)
self.o1 = Neuron(weight, bias)
def forward(self, input):
a = self.h1.forward(input)
b = self.h2.forward(input)
return self.o1.forward([a, b])
net = NeuralNetwork()
print(net.forward(np.array([-2, -4]))) # 0.731131354942724
print(sigmoid(-9)) # 0.00012339457598623172
print(sigmoid(0.000123394 * 1 + 0.000123394 * 2 + 1)) # 0.7311313546030448
# a = sigmoid(-9) 0.000123394
# b = sigmoid(-9) 0.000123394
# o = sigmoid(0.000123394 * 1 + 0.000123394 * 2 + 1) # 0.7311313546030448
2.2.3. 反向传播
简单总结下上述的几个公式
h
1
=
f
(
x
1
∗
w
1
+
x
2
∗
w
2
+
b
1
)
h
2
=
f
(
x
1
∗
w
3
+
x
2
∗
w
4
+
b
2
)
o
1
=
f
(
h
1
∗
w
5
+
h
2
∗
w
6
+
b
3
)
L
=
1
n
∑
i
=
1
n
(
o
i
′
−
o
i
)
2
h_1 = f(x_1 * w_1 + x_2 * w_2 + b1) \\ h_2 = f(x_1 * w_3 + x_2 * w_4 + b2) \\ o_1 = f(h_1 * w_5 + h_2 * w_6 + b3) \\ L = \frac{1}{n}\sum_{i=1}^{n}(o_i' - o_i)^2
h1=f(x1∗w1+x2∗w2+b1)h2=f(x1∗w3+x2∗w4+b2)o1=f(h1∗w5+h2∗w6+b3)L=n1i=1∑n(oi′−oi)2
为了找到最合适的权重和偏置,以最小化
L
L
L,我们可以采用梯度下降的方案来解决,通过
L
L
L分别对
w
1
w_1
w1或者
w
2
w_2
w2求偏导,然后使得
w
1
w_1
w1或者
w
2
w_2
w2都向着
L
L
L变小的方向移动一个小的step。
To be continued
3. 神经网络编程基础
本章节主要来讲解整个逻辑回归从0到1。
3.1. 基本概念
x
x
x: 表示一个
n
x
n_x
nx维数据,为输入数据,维度为
(
n
x
,
1
)
(n_x, 1)
(nx,1);
y
y
y:表示输出结果,取值为(0,1);
(
x
(
i
)
,
y
(
i
)
)
(x^{(i)}, y^{(i)})
(x(i),y(i)):表示第
i
i
i组数据,可能是训练数据,也可能是测试数据,此处默认为训练数据;
X
=
[
x
(
1
)
,
x
(
2
)
,
.
.
.
,
x
(
m
)
]
X = [x^{(1)}, x^{(2)}, . . . , x^{(m)}]
X=[x(1),x(2),...,x(m)]:表示所有的训练数据集的输入值,放在一个
n
x
∗
m
n_x * m
nx∗m的矩阵中,其中
m
m
m表示样本数目;
Y
=
[
y
(
1
)
,
y
(
2
)
,
.
.
.
,
y
(
m
)
]
Y = [y^{(1)}, y^{(2)}, . . . , y^{(m)}]
Y=[y(1),y(2),...,y(m)]:对应表示所有训练数据集的输出值,维度为
1
∗
m
1 * m
1∗m
上述的每个样本都是一个列向量,如
x
(
i
)
x^{(i)}
x(i)的形式,多个样本按照列顺序排布,最终形成矩阵
X
X
X, 其中
X
X
X.shape =
(
n
x
,
m
)
(n_x, m)
(nx,m)
3.2. 逻辑回归
y y y的预测值 y ^ \hat{y} y^,是 y y y的一个估计。准确来说,我们使用 y ^ \hat{y} y^来表达 y y y等于 1 1 1的概率,它是一个0到1的值。举例来说,如果 x x x是一个图片,那么 y ^ \hat{y} y^可以用来表示这张图片是一只猫的概率有多大。我们用 w w w来表示逻辑回归的参数, b b b表示偏移量,构造函数 y ^ = w T ∗ x + b \hat{y} = w^T* x + b y^=wT∗x+b来进行预测y值。这是线性回归中使用的函数,但是对于二元分类来说,它有个问题。要用 y ^ \hat{y} y^表示 y y y等于 1 1 1的概率值,它在0和1之间,因此我们需要再输出之前再加上一个sigmoid函数,来控制输出范围。
经过修改,逻辑回归的输出函数为
y
^
=
σ
(
w
T
x
+
b
)
,其中
σ
(
z
)
=
1
1
+
e
−
z
\hat{y} = \sigma(w^Tx + b),其中\sigma(z) = \frac{1}{1 + e^{-z}}
y^=σ(wTx+b),其中σ(z)=1+e−z1
对于给定的数据 { ( x ( 1 ) , y ( 1 ) ) , ( x ( 2 ) , y ( 2 ) ) , . . . , ( x ( m ) , y ( m ) ) } \{(x^{(1)},y^{(1)}) , (x^{(2)}, y^{(2)}), . . . , (x^{(m)},y^{(m)})\} {(x(1),y(1)),(x(2),y(2)),...,(x(m),y(m))},期望得到 y ^ ≈ y \hat{y} \approx y y^≈y。为了让模型学习到 w , b w, b w,b参数,需要定义一个损失函数(代价函数)。我们用 L ( y ^ , y ) L(\hat{y}, y) L(y^,y)来表示预测输出值 y ^ \hat{y} y^和真实值 y y y到底有多接近。
一般情况下我们会使用平方误差(预测值和真实值的平差差)来作为损失函数,但在逻辑回归中一般不这样做,这是因为我们的目标不是凸优化,它可能有多个局部最优值,梯度下降可能找不到全局最优值。在逻辑回归任务重,我们一般会使用另外一个损失函数:
L
(
y
^
,
y
)
=
−
y
l
o
g
(
y
^
)
−
(
1
−
y
)
l
o
g
(
1
−
y
^
)
L(\hat{y}, y) = -ylog(\hat{y}) - (1-y)log(1 - \hat{y})
L(y^,y)=−ylog(y^)−(1−y)log(1−y^)
当
y
=
1
y=1
y=1的时候,损失函数简化为
l
o
g
(
y
^
)
log(\hat{y})
log(y^),如果要最小化损失函数
L
L
L,则需要将
y
^
\hat{y}
y^尽可能的大,它的取值范围为0-1,所以
y
^
\hat{y}
y^会无限接近于1
当
y
=
0
y=0
y=0的时候,损失函数简化为
−
l
o
g
(
1
−
y
^
)
-log(1-\hat{y})
−log(1−y^),如果要
L
L
L尽可能的小,需要
y
^
\hat{y}
y^尽可能的小,所以
y
^
\hat{y}
y^会无限接近于0(注意log函数是增函数,两个负号之后,
−
l
o
g
(
1
−
y
^
)
-log(1-\hat{y})
−log(1−y^)还是增函数)
损失函数是基于单个样本来定义的,为了衡量其在全部样本的表现,使用所有样本的损失值均值来衡量,即
J
(
w
,
b
)
=
1
m
∑
i
=
1
m
L
(
y
^
(
i
)
,
y
(
i
)
)
=
1
m
∑
i
=
1
m
(
−
y
(
i
)
l
o
g
(
y
^
(
i
)
)
−
(
1
−
y
(
i
)
)
l
o
g
(
1
−
y
^
(
i
)
)
)
J(w, b) = \frac{1}{m} \sum_{i=1}^{m} {L(\hat{y}^{(i)}, y^{(i)}) } \\ = {\frac{1}{m} \sum_{i=1}^{m} (-y^{(i)}log(\hat{y}^{(i)}) - (1-y^{(i)})log(1 - \hat{y}^{(i)})) }
J(w,b)=m1i=1∑mL(y^(i),y(i))=m1i=1∑m(−y(i)log(y^(i))−(1−y(i))log(1−y^(i)))
在训练模型的时候,通过调整 w , b w,b w,b的值,以使得 J ( w , b ) J(w,b) J(w,b)最小。
3.3. 梯度下降法(Gradient Descent)
3.3.1. 基础知识
我们以一个单参数的w和b来举例,它的二维图示如下图所示:
这是一个凸函数,它像一个大碗。我们需要寻找合适
(
w
,
b
)
(w,b)
(w,b),以最小化
J
(
w
,
b
)
J(w,b)
J(w,b)。算法随机初始化一个点,然后随着梯度下降的方向,直到全局最优。因为它是凸函数,不管从哪里初始化,都可以到达全局最优点。
如果上述函数是非凸函数,则有多个最小值,梯度下降就很容易落入局部最优值。
3.3.2. 梯度下降的形式化说明
梯度下降的更新逻辑是
w
:
=
w
−
α
d
J
(
w
)
d
w
w := w - \alpha \frac{dJ(w)}{d w}
w:=w−αdwdJ(w)
这里的
α
\alpha
α是学习率,
d
J
(
w
)
d
w
\frac{dJ(w)}{d w}
dwdJ(w)为代价函数在
w
w
w处的梯度值
我们以单
w
w
w参数的函数来举例,如下图它是一个一元曲线图。下图的右半边是增函数,它的导数大于0;左半边是减函数,它的导数小于0。每次梯度更新是使用
w
w
w减去一个梯度值,从左边来看,因为导数小于0,则每次减法相当于增加
w
w
w,从右边来看,因为导数大于0,则每次减法相当于减少
w
w
w。也就是说,不管
w
w
w的初始值是在最小值左侧,还是右侧,
w
w
w都始终向着全局最小的位置行进。
当有多个参数的时候,这里的导数就变成的求偏导,梯度更新是每个参数都沿着梯度下降的方向,即
w
:
=
w
−
α
d
J
(
w
,
b
)
d
w
,
b
:
=
b
−
α
d
J
(
w
,
b
)
d
b
w := w - \alpha \frac{dJ(w,b)}{d w} , b := b - \alpha \frac{dJ(w,b)}{d b}
w:=w−αdwdJ(w,b),b:=b−αdbdJ(w,b)
3.3.3. 复合函数梯度计算图
对于如下的流程,如果我们学过复合函数的导数公式,我们有
d
J
d
b
=
d
J
d
v
d
v
d
u
d
u
d
b
=
3
∗
1
∗
c
=
3
∗
1
∗
2
=
6
\frac{dJ}{db} = \frac{dJ}{dv}\frac{dv}{du}\frac{du}{db} = 3 * 1 * c = 3*1*2 = 6
dbdJ=dvdJdudvdbdu=3∗1∗c=3∗1∗2=6
这如何从微分意义上来大致解答呢?我们采用类似于微分增量的概念,让
b
b
b增加一个微小量
0.001
0.001
0.001,即从
3
3
3增加到
3.001
3.001
3.001, 我们观察
J
J
J的增量,即可用来近似表达
d
J
d
b
\frac{dJ}{db}
dbdJ的值。
3.3.4. 逻辑回归的梯度下降
回顾前述的公式
y
^
=
a
=
σ
(
w
T
x
+
b
)
,其中
σ
(
z
)
=
1
1
+
e
−
z
L
(
y
^
,
y
)
=
−
y
l
o
g
(
y
^
)
−
(
1
−
y
)
l
o
g
(
1
−
y
^
)
J
(
w
,
b
)
=
1
m
∑
i
=
1
m
L
(
y
^
(
i
)
,
y
(
i
)
)
\hat{y} = a = \sigma(w^Tx + b),其中\sigma(z) = \frac{1}{1 + e^{-z}} \\ L(\hat{y}, y) = -ylog(\hat{y}) - (1-y)log(1 - \hat{y}) \\ J(w, b) = \frac{1}{m} \sum_{i=1}^{m} {L(\hat{y}^{(i)}, y^{(i)}) }
y^=a=σ(wTx+b),其中σ(z)=1+e−z1L(y^,y)=−ylog(y^)−(1−y)log(1−y^)J(w,b)=m1i=1∑mL(y^(i),y(i))
我们用
a
a
a代替
y
^
\hat{y}
y^来计算单个样本的代价函数,则
L
L
L对
a
a
a的导数
d
L
d
a
\frac{dL}{da}
dadL为
d
L
d
a
=
−
(
y
∗
1
a
+
(
1
−
y
)
∗
1
1
−
a
∗
(
−
1
)
)
=
−
(
y
a
+
y
−
1
1
−
a
)
=
−
y
−
a
y
+
a
y
−
a
a
(
1
−
a
)
=
a
−
y
a
(
1
−
a
)
\begin{align*} \frac{dL}{da} &= -(y * \frac{1}{a} + (1-y) * \frac{1}{1-a} * (-1)) \\ &= -(\frac{y}{a} + \frac{y-1}{1-a}) = -\frac{y-ay + ay - a}{a(1-a)} \\ &= \frac{a-y}{a(1-a)} \\ \end{align*}
dadL=−(y∗a1+(1−y)∗1−a1∗(−1))=−(ay+1−ay−1)=−a(1−a)y−ay+ay−a=a(1−a)a−y
a
a
a对
z
z
z的导数
d
a
d
z
\frac{da}{dz}
dzda为
d
a
d
z
=
−
1
(
1
+
e
−
z
)
2
∗
e
−
z
∗
(
−
1
)
=
e
−
z
∗
1
(
1
+
e
−
z
)
2
=
1
−
a
a
∗
a
2
=
a
(
1
−
a
)
\begin{align*} \frac{da}{dz} &= - \frac{1}{(1+e^{-z})^2} * e^{-z} * (-1) \\ &= e^{-z} * \frac{1}{(1+e^{-z})^2} \\ &= \frac{1-a}{a} * a^2 = a(1-a) \end{align*}
dzda=−(1+e−z)21∗e−z∗(−1)=e−z∗(1+e−z)21=a1−a∗a2=a(1−a)
则连续求导法
d
L
d
z
\frac{dL}{dz}
dzdL为
d
L
d
z
=
d
L
d
a
∗
d
a
d
z
=
a
−
y
a
(
1
−
a
)
∗
a
(
1
−
a
)
=
a
−
y
\begin{align*} \frac{dL}{dz} &= \frac{dL}{da} * \frac{da}{dz} \\ &= \frac{a-y}{a(1-a)} * a(1-a) \\ &= a-y \end{align*}
dzdL=dadL∗dzda=a(1−a)a−y∗a(1−a)=a−y
假设
w
w
w是个二元函数,满足
z
=
w
1
x
1
+
w
2
x
2
+
b
z = w_1x_1 + w_2x_2 + b
z=w1x1+w2x2+b。我们继续推导,求
L
L
L对
w
w
w和
b
b
b的导数,即计算
d
L
d
w
\frac{dL}{dw}
dwdL和
d
L
d
b
\frac{dL}{db}
dbdL的值:
d
L
d
w
1
=
d
L
d
z
d
z
d
w
1
=
(
a
−
y
)
∗
x
1
d
L
d
w
2
=
d
L
d
z
d
z
d
w
2
=
(
a
−
y
)
∗
x
2
d
L
d
b
=
d
L
d
z
d
z
d
b
=
(
a
−
y
)
\begin{align*} \frac{dL}{dw_1} &= \frac{dL}{dz}\frac{dz}{dw_1} = (a-y) * x_1 \\ \frac{dL}{dw_2} &= \frac{dL}{dz}\frac{dz}{dw_2} = (a-y) * x_2 \\ \frac{dL}{db} &= \frac{dL}{dz}\frac{dz}{db} = (a-y) \end{align*}
dw1dLdw2dLdbdL=dzdLdw1dz=(a−y)∗x1=dzdLdw2dz=(a−y)∗x2=dzdLdbdz=(a−y)
3.3.5. m m m个样本的梯度下降
上述是单个样本的梯度下降,我们现在计算
m
m
m个样本的梯度下降,再次回顾
m
m
m个样本的损失函数
J
(
w
,
b
)
=
1
m
∑
i
=
1
m
L
(
a
(
i
)
,
y
(
i
)
)
J(w,b) = \frac{1}{m} \sum_{i=1}^{m} L(a^{(i)}, y^{(i)})
J(w,b)=m1i=1∑mL(a(i),y(i))
则求对
w
1
w_1
w1的偏导为
d
J
(
w
,
b
)
d
w
1
=
1
m
∑
i
=
1
m
d
L
(
a
(
i
)
,
y
(
i
)
)
d
w
1
1
m
∑
i
=
1
m
(
a
(
i
)
−
y
(
i
)
)
∗
x
1
(
i
)
\frac{dJ(w,b)}{dw_1} = \frac{1}{m} \sum_{i=1}^{m} \frac{dL(a^{(i)}, y^{(i)})}{dw_1} \\ \frac{1}{m} \sum_{i=1}^{m}(a^{(i)}-y^{(i)})*x_1^{(i)}
dw1dJ(w,b)=m1i=1∑mdw1dL(a(i),y(i))m1i=1∑m(a(i)−y(i))∗x1(i)
后续为了简化起见,在某些地方会用
d
w
1
dw_1
dw1来表示
d
J
(
w
,
b
)
d
w
1
\frac{dJ(w,b)}{dw_1}
dw1dJ(w,b)
梯度下降算法
偷懒一把,直接从视频中截图出梯度下降算法的实现。其实通过前述的推导,我们已经明确知道了
d
J
(
w
,
b
)
d
w
1
\frac{dJ(w,b)}{dw_1}
dw1dJ(w,b)是如何计算的,也就不难得到如下的算法迭代公式。
它的思想很简单
- 初始化损失和梯度都为0(为后续的sum求和作准备)
- 根据前述 J ( w , b ) J(w,b) J(w,b)的计算公式,计算出 J ( w , b ) J(w,b) J(w,b)
- 然后应用前述的梯度计算方法,计算出 d J ( w , b ) d w 1 \frac{dJ(w,b)}{dw_1} dw1dJ(w,b)的值,这里的损失求和和梯度求和是在遍历样本的过程中进行累加的
- 最后遍历完所有样本求均值
- 在得到前述的梯度和之后,应用梯度下降的更新公式,代入学习率,即可得到新的
w
w
w和
b
b
b
3.3.6. 梯度下降向量化
上述梯度计算的是通过两层for循环迭代,另外一种思路是采用矩阵向量运算,这样可以更好利用并行化优化。
向量化和非向量化的代码耗时
import numpy as np
import time
a = np.random.rand(1000000)
b = np.random.rand(1000000)
tic = time.time()
c = np.dot(a, b)
toc = time.time()
print(c)
print("Vectorized version:"+ str(1000*(toc-tic))+"ms")
c = 0
tic = time.time()
for i in range(1000000):
c += a[i]*b[i]
toc = time.time()
print(c)
print("For loop:"+ str(1000*(toc-tic))+ "ms")
输出结果
250121.78770186222
Vectorized version:23.240327835083008ms
250121.7877018585
For loop:487.37192153930664ms
可以看到两者有20多倍的耗时差,这得益于向量化的能力,CPU一般可以很容易使用SIMD指令来加速矩阵运算。想写循环时候,检查 numpy 是否存在类似的内置函数,从而避免使用循环(loop)方式。
我们首先使用向量化来优化前向传播的一些计算
已知
z
(
1
)
=
(
w
T
x
(
1
)
+
b
)
,
z
(
2
)
=
(
w
T
x
(
2
)
+
b
)
,
z
(
3
)
=
(
w
T
x
(
3
)
+
b
)
z^{(1)} = (w^Tx^{(1)} + b), z^{(2)} = (w^Tx^{(2)} + b), z^{(3)} = (w^Tx^{(3)} + b)
z(1)=(wTx(1)+b),z(2)=(wTx(2)+b),z(3)=(wTx(3)+b)
从而,我们有
Z
=
[
z
(
1
)
,
z
(
2
)
,
.
.
.
,
z
(
m
)
]
=
[
(
w
T
x
(
1
)
+
b
)
,
(
w
T
x
(
2
)
+
b
)
,
.
.
.
,
(
w
T
x
(
m
)
+
b
)
]
=
[
w
T
x
(
1
)
,
w
T
x
(
2
)
,
.
.
.
,
w
T
x
(
m
)
]
+
[
b
,
b
,
.
.
.
.
,
b
]
=
w
T
[
x
(
1
)
,
x
(
2
)
,
.
.
.
,
x
(
m
)
]
+
[
b
,
b
,
.
.
.
.
,
b
]
\begin{align*} Z &= [z^{(1)}, z^{(2)}, ..., z^{(m)}] \\ &= [(w^Tx^{(1)} + b), (w^Tx^{(2)} + b), ..., (w^Tx^{(m)} + b)] \\ &= [w^Tx^{(1)}, w^Tx^{(2)}, ..., w^Tx^{(m)}] + [b,b,....,b]\\ &= w^T[x^{(1)}, x^{(2)}, ..., x^{(m)}]+[b,b,....,b] \end{align*}
Z=[z(1),z(2),...,z(m)]=[(wTx(1)+b),(wTx(2)+b),...,(wTx(m)+b)]=[wTx(1),wTx(2),...,wTx(m)]+[b,b,....,b]=wT[x(1),x(2),...,x(m)]+[b,b,....,b]
显而易见,我们可以定义一个输入矩阵
X
=
[
x
(
1
)
,
x
(
2
)
,
.
.
.
,
x
(
m
)
]
X = [x^{(1)}, x^{(2)}, ..., x^{(m)}]
X=[x(1),x(2),...,x(m)],它是一个
n
∗
m
n*m
n∗m的矩阵,其中
n
n
n为单个样本的维度,
m
m
m为样本个数,它的每列是一个样本,则
Z
=
w
T
X
+
[
b
,
b
,
.
.
.
.
,
b
]
Z = w^TX+[b,b,....,b]
Z=wTX+[b,b,....,b]
在代码实现中,我们可以写
Z
=
n
p
.
d
o
t
(
w
T
,
X
)
+
b
Z = np.dot(w^T, X) + b
Z=np.dot(wT,X)+b,用向量加上实数
b
b
b,python会自动将
b
b
b进行展开成向量,那么关于激活值
a
a
a呢?
我们已知
a
(
1
)
=
σ
(
z
(
1
)
)
,
a
(
2
)
=
σ
(
z
(
2
)
)
,
a
(
3
)
=
σ
(
z
(
3
)
)
a^{(1)} = \sigma(z^{(1)}), a^{(2)} = \sigma(z^{(2)}), a^{(3)} = \sigma(z^{(3)})
a(1)=σ(z(1)),a(2)=σ(z(2)),a(3)=σ(z(3))
则激活值向量为
A
=
[
a
(
1
)
,
a
(
2
)
,
.
.
.
,
a
(
m
)
]
=
[
σ
(
z
(
1
)
)
,
σ
(
z
(
2
)
)
,
.
.
.
,
σ
(
z
(
m
)
)
]
=
σ
(
Z
)
\begin{align*} A &= [a^{(1)}, a^{(2)}, ..., a^{(m)}] \\ &= [\sigma(z^{(1)}), \sigma(z^{(2)}), ..., \sigma(z^{(m)})] \\ &=\sigma(Z) \end{align*}
A=[a(1),a(2),...,a(m)]=[σ(z(1)),σ(z(2)),...,σ(z(m))]=σ(Z)
再次回忆代价函数
J
(
w
,
b
)
J(w,b)
J(w,b),将它使用激活值来表示,则
J
(
w
,
b
)
=
1
m
∑
i
=
1
m
−
y
(
i
)
l
o
g
a
(
i
)
−
(
1
−
y
(
i
)
)
l
o
g
(
1
−
a
(
i
)
)
J(w, b) = \frac{1}{m} \sum_{i=1}^{m} {-y^{(i)}loga^{(i)} -(1-y^{(i)})log(1-a^{(i)})}
J(w,b)=m1i=1∑m−y(i)loga(i)−(1−y(i))log(1−a(i))
若令
Y
=
[
y
(
1
)
,
y
(
2
)
,
.
.
.
,
y
(
m
)
]
Y=[y^{(1)}, y^{(2)}, ..., y^{(m)}]
Y=[y(1),y(2),...,y(m)],则
J
(
w
,
b
)
=
1
m
∑
i
=
1
m
−
y
(
i
)
l
o
g
a
(
i
)
−
(
1
−
y
(
i
)
)
l
o
g
(
1
−
a
(
i
)
)
=
−
Y
T
l
o
g
(
A
)
−
(
1
−
Y
)
T
l
o
g
(
1
−
A
)
\begin{align*} J(w, b) &= \frac{1}{m} \sum_{i=1}^{m} {-y^{(i)}loga^{(i)} -(1-y^{(i)})log(1-a^{(i)})} \\ &=-Y^Tlog(A)-(1-Y)^Tlog(1-A) \end{align*}
J(w,b)=m1i=1∑m−y(i)loga(i)−(1−y(i))log(1−a(i))=−YTlog(A)−(1−Y)Tlog(1−A)
其次,我们使用向量化来优化两层for循环的梯度下降算法
回顾一下梯度计算算法的流程:
我们用
d
Z
=
[
d
z
(
1
)
,
d
z
(
2
)
,
.
.
.
,
d
z
(
m
)
]
dZ = [dz^{(1)}, dz^{(2)}, ..., dz^{(m)} ]
dZ=[dz(1),dz(2),...,dz(m)],则我们有
d
Z
=
[
d
z
(
1
)
,
d
z
(
2
)
,
.
.
.
,
d
z
(
m
)
]
=
[
a
(
1
)
−
y
(
1
)
,
a
(
2
)
−
y
(
2
)
,
.
.
.
,
a
(
m
)
−
y
(
m
)
]
=
[
a
(
1
)
,
a
(
2
)
,
.
.
.
,
a
(
m
)
]
−
[
y
(
1
)
,
y
(
2
)
,
.
.
.
,
y
(
m
)
]
=
A
−
Y
\begin{align*} dZ &= [dz^{(1)}, dz^{(2)}, ..., dz^{(m)} ] \\ & = [a^{(1)} - y^{(1)}, a^{(2)} - y^{(2)}, ..., a^{(m)} - y^{(m)} ] \\ &= [a^{(1)}, a^{(2)}, ..., a^{(m)}] - [y^{(1)}, y^{(2)}, ..., y^{(m)}] \\ & = A - Y \end{align*}
dZ=[dz(1),dz(2),...,dz(m)]=[a(1)−y(1),a(2)−y(2),...,a(m)−y(m)]=[a(1),a(2),...,a(m)]−[y(1),y(2),...,y(m)]=A−Y
梯度更新的计算公式使用
d
z
(
i
)
dz^{(i)}
dz(i)来表示的话,则为
d
w
1
=
1
m
∑
i
=
1
m
d
L
(
a
(
i
)
,
y
(
i
)
)
d
w
1
=
1
m
∑
i
=
1
m
x
1
(
i
)
∗
d
z
(
i
)
d
w
2
=
1
m
∑
i
=
1
m
d
L
(
a
(
i
)
,
y
(
i
)
)
d
w
2
=
1
m
∑
i
=
1
m
x
2
(
i
)
∗
d
z
(
i
)
d
b
=
1
m
∑
i
=
1
m
d
L
(
a
(
i
)
,
y
(
i
)
)
d
b
=
1
m
∑
i
=
1
m
d
z
(
i
)
dw_1 = \frac{1}{m} \sum_{i=1}^{m} \frac{dL(a^{(i)}, y^{(i)})}{dw_1} = \frac{1}{m} \sum_{i=1}^{m} {x_1^{(i)} * dz^{(i)}} \\ dw_2 = \frac{1}{m} \sum_{i=1}^{m} \frac{dL(a^{(i)}, y^{(i)})}{dw_2} = \frac{1}{m} \sum_{i=1}^{m} {x_2^{(i)} * dz^{(i)} } \\ db = \frac{1}{m} \sum_{i=1}^{m} \frac{dL(a^{(i)}, y^{(i)})}{db} = \frac{1}{m} \sum_{i=1}^{m} { dz^{(i)}}
dw1=m1i=1∑mdw1dL(a(i),y(i))=m1i=1∑mx1(i)∗dz(i)dw2=m1i=1∑mdw2dL(a(i),y(i))=m1i=1∑mx2(i)∗dz(i)db=m1i=1∑mdbdL(a(i),y(i))=m1i=1∑mdz(i)
则
d
b
=
1
m
∗
n
p
.
s
u
m
(
d
Z
)
db = \frac{1}{m} * np.sum(dZ)
db=m1∗np.sum(dZ)。
接下来,我们来求
d
w
dw
dw,由于
w
w
w是列向量,为了便于梯度更新时候的矩阵运算,我们令
d
w
=
[
d
w
1
,
d
w
2
,
.
.
.
,
d
w
n
]
T
dw = [dw_1, dw_2, ..., dw_n]^T
dw=[dw1,dw2,...,dwn]T,也是一个列向量,则
d
w
dw
dw可以表示为:
d
w
=
[
d
w
1
d
w
2
⋮
d
w
n
]
=
1
m
∗
[
x
1
(
1
)
x
1
(
2
)
⋯
x
1
(
m
)
x
2
(
1
)
x
2
(
2
)
⋯
x
2
(
m
)
⋮
⋮
⋮
⋮
x
n
(
1
)
x
n
(
2
)
⋯
x
n
(
m
)
]
∗
[
d
z
(
1
)
d
z
(
2
)
⋮
d
z
(
m
)
]
=
1
m
∗
[
x
(
1
)
,
x
(
2
)
,
.
.
.
,
x
(
m
)
]
∗
[
d
z
(
1
)
d
z
(
2
)
⋮
d
z
(
m
)
]
\begin{align*} dw &= \left[ \begin{matrix} dw_1 \\ dw_2 \\ \vdots \\ dw_n \\ \end{matrix}\right] \\ &= \frac{1}{m} * \left[ \begin{matrix} x_1^{(1)} & x_1^{(2)} & \cdots & x_1^{(m)} \\ x_2^{(1)} & x_2^{(2)} & \cdots & x_2^{(m)} \\ \vdots & \vdots & \vdots & \vdots \\ x_n^{(1)} & x_n^{(2)} & \cdots & x_n^{(m)} \\ \end{matrix} \right] * \left[ \begin{matrix} dz^{(1)} \\ dz^{(2)} \\ \vdots\\ dz^{(m)} \\ \end{matrix} \right] \\ &= \frac{1}{m} * [x^{(1)}, x^{(2)}, ..., x^{(m)}]*\left[ \begin{matrix} dz^{(1)} \\ dz^{(2)} \\ \vdots\\ dz^{(m)} \\ \end{matrix} \right] \end{align*}
dw=
dw1dw2⋮dwn
=m1∗
x1(1)x2(1)⋮xn(1)x1(2)x2(2)⋮xn(2)⋯⋯⋮⋯x1(m)x2(m)⋮xn(m)
∗
dz(1)dz(2)⋮dz(m)
=m1∗[x(1),x(2),...,x(m)]∗
dz(1)dz(2)⋮dz(m)
回忆一下前述输入矩阵
X
X
X和
Z
Z
Z的表示形式,
d
w
dw
dw可以简化为
d
w
=
1
m
∗
X
∗
d
Z
T
dw = \frac{1}{m} * X * dZ^T
dw=m1∗X∗dZT
这可以使用代码
d
w
=
1
m
∗
n
p
.
d
o
t
(
X
,
d
Z
T
)
dw = \frac{1}{m} * np.dot(X, dZ^T)
dw=m1∗np.dot(X,dZT)。我们也可以将其写成样本展开的形式为
d
w
=
1
m
(
x
(
1
)
d
z
(
1
)
+
x
(
2
)
d
z
(
m
)
+
.
.
.
+
x
(
m
)
d
z
(
m
)
)
dw = \frac{1}{m} (x^{(1)}dz^{(1)} + x^{(2)}dz^{(m)} + ... + x^{(m)}dz^{(m)})
dw=m1(x(1)dz(1)+x(2)dz(m)+...+x(m)dz(m))
经过上述的矩阵简化之后,我们的前向和反向传播的公式可以简化为
Z
=
w
T
X
+
[
b
,
b
,
.
.
.
.
,
b
]
=
n
p
.
d
o
t
(
w
T
,
X
)
+
b
A
=
[
a
(
1
)
,
a
(
2
)
,
.
.
.
,
a
(
m
)
]
=
σ
(
Z
)
J
(
w
,
b
)
=
−
Y
T
l
o
g
(
A
)
−
(
1
−
Y
)
T
l
o
g
(
1
−
A
)
d
Z
=
[
d
z
(
1
)
,
d
z
(
2
)
,
.
.
.
,
d
z
(
m
)
]
=
A
−
Y
d
w
=
1
m
∗
X
∗
d
Z
T
=
1
m
∗
n
p
.
d
o
t
(
X
,
d
Z
T
)
d
b
=
1
m
∗
n
p
.
s
u
m
(
d
Z
)
w
:
=
w
−
α
∗
d
w
b
:
=
b
−
α
∗
d
b
\begin{align*} Z &= w^TX+[b,b,....,b] = np.dot(w^T, X) + b \\ A&= [a^{(1)}, a^{(2)}, ..., a^{(m)}] =\sigma(Z) \\ J(w, b) &=-Y^Tlog(A)-(1-Y)^Tlog(1-A) \\ dZ &= [dz^{(1)}, dz^{(2)}, ..., dz^{(m)} ] = A - Y \\ dw &= \frac{1}{m} * X * dZ^T = \frac{1}{m} * np.dot(X, dZ^T) \\ db &= \frac{1}{m} * np.sum(dZ) \\ w :&= w - \alpha * dw \\ b :&= b - \alpha * db \end{align*}
ZAJ(w,b)dZdwdbw:b:=wTX+[b,b,....,b]=np.dot(wT,X)+b=[a(1),a(2),...,a(m)]=σ(Z)=−YTlog(A)−(1−Y)Tlog(1−A)=[dz(1),dz(2),...,dz(m)]=A−Y=m1∗X∗dZT=m1∗np.dot(X,dZT)=m1∗np.sum(dZ)=w−α∗dw=b−α∗db
利用前几个公式实现前向和反向传播,利用最后两个公式完成梯度更新,上述的公式没有任何for循环,全是向量运算,足够高效。
对应python代码
def sigmoid(z):
return 1/(1 + np.exp(-z))
def compute gradient(X,y,w, b):
m=X.shape[1]
z=np.dot(w.T,X)+b
a= sigmoid(z)
dw=(1/m)*np.dot(X, (a-y).T)
db =(1/m)*np.sum(a-y)
return dw, db
3.4. 逻辑回归的数学解释
3.4.1. 单样本损失函数的来由
对于给定的一个样本
(
x
,
y
)
(x, y)
(x,y),对于
x
x
x,逻辑回归中的预测结果
y
^
\hat{y}
y^表示如下
y
^
=
σ
(
w
T
x
+
b
)
,其中
σ
(
z
)
=
1
1
+
e
−
z
\hat{y} = \sigma(w^Tx + b),其中\sigma(z) = \frac{1}{1 + e^{-z}}
y^=σ(wTx+b),其中σ(z)=1+e−z1
这里
y
^
\hat{y}
y^表示
y
=
1
y=1
y=1的概率,它的输出值在
[
0
,
1
]
[0,1]
[0,1]之间。由于这是一个二分类问题,真实值
y
y
y只能取值
0
0
0或者
1
1
1,所以我们有:
p
(
y
=
1
∣
x
)
=
y
^
p
(
y
=
0
∣
x
)
=
1
−
y
^
\begin{align*} p(y=1|x) &= \hat{y} \\ p(y=0|x) &= 1 - \hat{y} \end{align*}
p(y=1∣x)p(y=0∣x)=y^=1−y^
上述公式是两个式子,为了简便计算,我们将其合并为一个式子来表达,即
p
(
y
∣
x
)
=
y
^
y
∗
(
1
−
y
^
)
(
1
−
y
)
p(y|x) = \hat{y}^y * (1-\hat{y})^{(1-y)}
p(y∣x)=y^y∗(1−y^)(1−y)
我们可以代入
y
=
1
y=1
y=1,满足
p
(
y
=
1
∣
x
)
=
y
^
p(y=1|x) = \hat{y}
p(y=1∣x)=y^;代入
y
=
0
y=0
y=0,满足
p
(
y
=
0
∣
x
)
=
1
−
y
^
p(y=0|x) = 1 - \hat{y}
p(y=0∣x)=1−y^。
对于给定的一个样本
(
x
,
y
)
(x,y)
(x,y),我们的目标是最大化
p
(
y
∣
x
)
p(y|x)
p(y∣x),它等价于最大化
l
o
g
(
p
(
y
∣
x
)
)
log(p(y|x))
log(p(y∣x)),这是因为
l
o
g
log
log函数是严格递增的函数,故而我们目标是最大化
l
o
g
(
(
p
(
y
∣
x
)
)
=
l
o
g
(
y
^
y
∗
(
1
−
y
^
)
(
1
−
y
)
)
=
y
l
o
g
y
^
+
(
1
−
y
)
l
o
g
(
1
−
y
^
)
log((p(y|x)) = log(\hat{y}^y * (1-\hat{y})^{(1-y)}) = ylog\hat{y} + (1-y)log(1-\hat{y})
log((p(y∣x))=log(y^y∗(1−y^)(1−y))=ylogy^+(1−y)log(1−y^)
回想前述的损失函数的定义:
L
(
y
^
,
y
)
=
−
y
l
o
g
(
y
^
)
−
(
1
−
y
)
l
o
g
(
1
−
y
^
)
L(\hat{y}, y) = -ylog(\hat{y}) - (1-y)log(1 - \hat{y})
L(y^,y)=−ylog(y^)−(1−y)log(1−y^)
所以我们有
l
o
g
(
(
p
(
y
∣
x
)
)
=
−
L
(
y
^
,
y
)
log((p(y|x)) = -L(\hat{y}, y)
log((p(y∣x))=−L(y^,y)
考虑到损失函数前面有个负号,因此最大化输出概率值
p
(
y
∣
x
)
p(y|x)
p(y∣x)等价于最小化损失函数
L
(
y
^
,
y
)
L(\hat{y},y)
L(y^,y),这是单个样本的损失函数表达式。
3.4.2. 推广到多样本损失函数
对于有
m
m
m个样本,我们的目标是寻找参数
(
w
,
b
)
(w,b)
(w,b),以最大化联合概率函数
P
(
y
(
1
)
,
y
(
2
)
,
.
.
.
,
y
(
m
)
∣
x
(
1
)
,
x
(
2
)
,
.
.
.
,
x
(
m
)
)
P({y}^{(1)},{y}^{(2)},...,{y}^{(m)}|x^{(1)}, x^{(2)}, ..., x^{(m)})
P(y(1),y(2),...,y(m)∣x(1),x(2),...,x(m))
假设所有的样本满足独立同分布的特性,则联合概率密度函数等于所有样本概率密度函数的乘积,即
P
(
y
(
1
)
,
y
(
2
)
,
.
.
.
,
y
(
m
)
∣
x
(
1
)
,
x
(
2
)
,
.
.
.
,
x
(
m
)
)
=
p
(
y
(
1
)
∣
x
(
1
)
)
∗
p
(
y
(
2
)
∣
x
(
2
)
)
∗
.
.
.
∗
p
(
y
(
m
)
∣
x
(
m
)
)
=
∏
i
=
1
m
p
(
y
(
i
)
∣
x
(
i
)
)
\begin{align*} P({y}^{(1)},{y}^{(2)},...,{y}^{(m)}|x^{(1)}, x^{(2)}, ..., x^{(m)}) &= p({y}^{(1)} | x^{(1)}) * p({y}^{(2)} | x^{(2)}) * ... * p({y}^{(m)} | x^{(m)}) \\ & = \prod_{i=1}^m p({y}^{(i)} | x^{(i)}) \end{align*}
P(y(1),y(2),...,y(m)∣x(1),x(2),...,x(m))=p(y(1)∣x(1))∗p(y(2)∣x(2))∗...∗p(y(m)∣x(m))=i=1∏mp(y(i)∣x(i))
同样利用
l
o
g
log
log的特性,两边求对数,以将乘积变成加法,则等同于最大化:
l
o
g
(
∏
i
=
1
m
p
(
y
(
i
)
∣
x
(
i
)
)
)
=
∑
i
=
1
m
l
o
g
(
p
(
y
(
i
)
∣
x
(
i
)
)
)
=
−
∑
i
=
1
m
L
(
y
^
,
y
)
log(\prod_{i=1}^m p({y}^{(i)} | x^{(i)})) = \sum_{i=1}^{m}log(p({y}^{(i)} | x^{(i)})) = - \sum_{i=1}^{m} L(\hat{y}, y)
log(i=1∏mp(y(i)∣x(i)))=i=1∑mlog(p(y(i)∣x(i)))=−i=1∑mL(y^,y)
由于上面的式子最右边有个负号,因此最大化联合概率密度函数等同于最小化
∑
i
=
1
m
L
(
y
^
,
y
)
\sum_{i=1}^{m} L(\hat{y}, y)
i=1∑mL(y^,y)
前面乘以系数
1
m
\frac{1}{m}
m1,这就等同于我们的前述所述的多样本损失函数的定义
J
(
w
,
b
)
=
1
m
∑
i
=
1
m
L
(
a
(
i
)
,
y
(
i
)
)
J(w,b) = \frac{1}{m} \sum_{i=1}^{m} L(a^{(i)}, y^{(i)})
J(w,b)=m1i=1∑mL(a(i),y(i))
3.4.3. 反向思考一轮
我们期望找到参数
(
w
,
b
)
(w,b)
(w,b),它能使得
m
m
m个样本出现的概率最大,即最大化这些样本的联合概率密度函数
P
(
y
(
1
)
,
y
(
2
)
,
.
.
.
,
y
(
m
)
∣
x
(
1
)
,
x
(
2
)
,
.
.
.
,
x
(
m
)
;
w
,
b
)
P({y}^{(1)},{y}^{(2)},...,{y}^{(m)}|x^{(1)}, x^{(2)}, ..., x^{(m)}; w,b)
P(y(1),y(2),...,y(m)∣x(1),x(2),...,x(m);w,b)
这种是我们概率论中的常考题型,参考最大似然估计一章,它详细解释了如何依赖样本值来计算出 w w w和 b b b。
联合概率的公式不容易计算,我们一般认为样本之间满足独立同分布的特性,这就可以将联合概率密度写成单样本概率密度函数的乘积。
p
(
y
(
1
)
∣
x
(
1
)
)
∗
p
(
y
(
2
)
∣
x
(
2
)
)
∗
.
.
.
∗
p
(
y
(
m
)
∣
x
(
m
)
)
=
∏
i
=
1
m
p
(
y
(
i
)
∣
x
(
i
)
)
p({y}^{(1)} | x^{(1)}) * p({y}^{(2)} | x^{(2)}) * ... * p({y}^{(m)} | x^{(m)})= \prod_{i=1}^m p({y}^{(i)} | x^{(i)})
p(y(1)∣x(1))∗p(y(2)∣x(2))∗...∗p(y(m)∣x(m))=i=1∏mp(y(i)∣x(i))
概率密度的乘积同样很难计算,我们进一步通过
l
o
g
log
log对数的形式,将其转换为加法运算,即最大化
∑
i
=
1
m
l
o
g
(
p
(
y
(
i
)
∣
x
(
i
)
)
)
\sum_{i=1}^{m}log(p({y}^{(i)} | x^{(i)}))
i=1∑mlog(p(y(i)∣x(i)))
在概率论书籍中,到达这一步之后,我们会根据题目中给出的样本概率分布,将概率密度函数代入到
p
(
y
(
i
)
∣
x
(
i
)
)
p({y}^{(i)} | x^{(i)})
p(y(i)∣x(i))中,进行化简。对于本文的二元预测来说,我们定义了
y
^
\hat{y}
y^为
p
(
y
=
1
∣
x
)
p(y=1|x)
p(y=1∣x),进而可以得到二元样本
(
x
,
y
)
(x,y)
(x,y)的概率分布函数(对于离散型随机变量
y
y
y,我们通常称之为概率分布函数)为
p
(
y
∣
x
)
=
y
^
y
∗
(
1
−
y
^
)
(
1
−
y
)
p(y|x) =\hat{y}^y * (1-\hat{y})^{(1-y)}
p(y∣x)=y^y∗(1−y^)(1−y),代入到上面的式子,即最大化
∑
i
=
1
m
(
y
(
i
)
l
o
g
y
^
(
i
)
+
(
1
−
y
(
i
)
)
l
o
g
(
1
−
y
^
(
i
)
)
)
\sum_{i=1}^{m}( y^{(i)}log\hat{y}^{(i)} + (1-y^{(i)})log(1-\hat{y}^{(i)}) )
i=1∑m(y(i)logy^(i)+(1−y(i))log(1−y^(i)))
又因为我们的
y
^
=
σ
(
w
T
x
+
b
)
\hat{y} = \sigma(w^Tx+b)
y^=σ(wTx+b),则可以进一步代入上式,即最大化
∑
i
=
1
m
(
y
(
i
)
l
o
g
σ
(
w
T
x
(
i
)
+
b
)
+
(
1
−
y
(
i
)
)
l
o
g
(
1
−
σ
(
w
T
x
(
i
)
+
b
)
)
)
\sum_{i=1}^{m}( y^{(i)}log \sigma(w^Tx^{(i)}+b) + (1-y^{(i)})log(1- \sigma(w^Tx^{(i)}+b)) )
i=1∑m(y(i)logσ(wTx(i)+b)+(1−y(i))log(1−σ(wTx(i)+b)))
再重复一遍,我们的目标是求出参数
(
w
,
b
)
(w,b)
(w,b),以使得上述公式的值最大。对于概率论的书籍中,这个时候,我们会将上述公式对
w
w
w和
b
b
b分别求偏导,使得所有的偏导值为0,解这个多元方程组,就可以使用样本值来表达
w
w
w和
b
b
b。令
P
P
P代表上述的公式,我们有
∂
P
∂
w
1
=
0
∂
P
∂
w
2
=
0
∂
P
∂
b
=
0
\begin{align*} \frac{\partial{P}}{\partial w_1} &= 0 \\ \frac{\partial{P}}{\partial w_2} &= 0 \\ \frac{\partial{P}}{\partial b} &= 0 \end{align*}
∂w1∂P∂w2∂P∂b∂P=0=0=0
想一想我们梯度下降在做的事情,我们初始化 ( w , b ) (w,b) (w,b)之后通过不断梯度下降的迭代,使得代价函数 J ( w , b ) J(w,b) J(w,b)最小。当其最小的时候,其实就是上述的各个偏导数为0。
4. Python编程基础
4.1. numpy广播
- 矩阵按列求和
import numpy as np
A = np.array([[56.0,0.0,4.4,68.0],
[1.2,104.0,52.0,8.0],
[1.8,135.0,99.0,0.9]])
print(A)
## 按列求和
sumA = A.sum(axis=0)
print(sumA) # 输出 [ 59. 239. 155.4 76.9]
- 矩阵除法广播
## 求各个列中不同元素的占比
print(A/sumA)
输出
[[0.94915254 0. 0.02831403 0.88426528]
[0.02033898 0.43514644 0.33462033 0.10403121]
[0.03050847 0.56485356 0.63706564 0.01170351]]
一个 m ∗ n m*n m∗n的矩阵除以 1 ∗ m 1*m 1∗m的矩阵,相当于 m ∗ n m*n m∗n的矩阵的每一行都和该行向量对应元素相除。与之所类比,我们同样可以进行加、减、乘运算。
- 矩阵加上一个实数
## 4.2. 每个元素都加上100
print(A+100)
输出
[[156. 100. 104.4 168. ]
[101.2 204. 152. 108. ]
[101.8 235. 199. 100.9]]
- 维度延展
广播机制首先需要判断参与计算的两个数组能否被广播机制处理?即判断是否广播兼容,规则是,比较两个数组的shape,从shape的尾部开始一一比对- (1) 如果两个数组的维度相同,从后向前逐个维进行比对,若轴长度相同或者其中一方的长度为1,则广播兼容:例如 m ∗ n m*n m∗n和 1 ∗ n 1*n 1∗n,它们都是2维的,从后向前比对,先是 n n n,两个轴长度相同;其次是 m m m和 1 1 1,其中一方为 1 1 1,故而满足广播兼容。
- (2) 如果两个数组的维度不同,那么给低维度的数组前扩展提升一维,且扩展维的轴长度为1,然后继续形同(1)进行处理:例如
k
∗
m
∗
n
k*m*n
k∗m∗n和
1
∗
n
1*n
1∗n,它们的维度不同,需要先给
1
∗
n
1*n
1∗n扩展一个维度变成
1
∗
1
∗
n
1*1*n
1∗1∗n,然后按照方法(1)进行处理。
4.2. 使用明确的矩阵维度表示
使用明确的维度表示,不要使用一维数组,转向使用 n ∗ 1 n*1 n∗1维矩阵(基本上是列向量),或者 1 ∗ n 1 * n 1∗n 维矩阵(基本上是行向量)。