大模型系列6--神经网络(WIP)

news2024/12/23 23:54:11

神经网络

  • 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 x1w1+x2w2+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(x1w1+x2w2+b)
  • 常用的激活函数为 y = 1 1 + e − x y = \frac{1}{1+e^{-x}} y=1+ex1,该激活函数可以将(负无穷,正无穷)映射到(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(x1w1+x2w2+b1)h2=f(x1w3+x2w4+b2)o1=f(h1w5+h2w6+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=1n(oioi)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(x1w1+x2w2+b1)h2=f(x1w3+x2w4+b2)o1=f(h1w5+h2w6+b3)L=n1i=1n(oioi)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 nxm的矩阵中,其中 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 1m

上述的每个样本都是一个列向量,如 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^=wTx+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+ez1

对于给定的数据 { ( 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^)(1y)log(1y^)
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(1y^),如果要 L L L尽可能的小,需要 y ^ \hat{y} y^尽可能的小,所以 y ^ \hat{y} y^会无限接近于0(注意log函数是增函数,两个负号之后, − l o g ( 1 − y ^ ) -log(1-\hat{y}) log(1y^)还是增函数)

损失函数是基于单个样本来定义的,为了衡量其在全部样本的表现,使用所有样本的损失值均值来衡量,即
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=1mL(y^(i),y(i))=m1i=1m(y(i)log(y^(i))(1y(i))log(1y^(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=31c=312=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+ez1L(y^,y)=ylog(y^)(1y)log(1y^)J(w,b)=m1i=1mL(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=(ya1+(1y)1a1(1))=(ay+1ay1)=a(1a)yay+aya=a(1a)ay
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+ez)21ez(1)=ez(1+ez)21=a1aa2=a(1a)

则连续求导法 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=dadLdzda=a(1a)aya(1a)=ay

假设 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=(ay)x1=dzdLdw2dz=(ay)x2=dzdLdbdz=(ay)

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=1mL(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=1mdw1dL(a(i),y(i))m1i=1m(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 nm的矩阵,其中 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=1my(i)loga(i)(1y(i))log(1a(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=1my(i)loga(i)(1y(i))log(1a(i))=YTlog(A)(1Y)Tlog(1A)

其次,我们使用向量化来优化两层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)]=AY
梯度更新的计算公式使用 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=1mdw1dL(a(i),y(i))=m1i=1mx1(i)dz(i)dw2=m1i=1mdw2dL(a(i),y(i))=m1i=1mx2(i)dz(i)db=m1i=1mdbdL(a(i),y(i))=m1i=1mdz(i)
d b = 1 m ∗ n p . s u m ( d Z ) db = \frac{1}{m} * np.sum(dZ) db=m1np.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= dw1dw2dwn =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=m1XdZT
这可以使用代码 d w = 1 m ∗ n p . d o t ( X , d Z T ) dw = \frac{1}{m} * np.dot(X, dZ^T) dw=m1np.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)(1Y)Tlog(1A)=[dz(1),dz(2),...,dz(m)]=AY=m1XdZT=m1np.dot(X,dZT)=m1np.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+ez1
这里 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^=1y^

上述公式是两个式子,为了简便计算,我们将其合并为一个式子来表达,即
p ( y ∣ x ) = y ^ y ∗ ( 1 − y ^ ) ( 1 − y ) p(y|x) = \hat{y}^y * (1-\hat{y})^{(1-y)} p(yx)=y^y(1y^)(1y)
我们可以代入 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)=1y^

对于给定的一个样本 ( x , y ) (x,y) (x,y),我们的目标是最大化 p ( y ∣ x ) p(y|x) p(yx),它等价于最大化 l o g ( p ( y ∣ x ) ) log(p(y|x)) log(p(yx)),这是因为 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(yx))=log(y^y(1y^)(1y))=ylogy^+(1y)log(1y^)

回想前述的损失函数的定义:
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^)(1y)log(1y^)
所以我们有
l o g ( ( p ( y ∣ x ) ) = − L ( y ^ , y ) log((p(y|x)) = -L(\hat{y}, y) log((p(yx))=L(y^,y)
考虑到损失函数前面有个负号,因此最大化输出概率值 p ( y ∣ x ) p(y|x) p(yx)等价于最小化损失函数 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=1mp(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=1mp(y(i)x(i)))=i=1mlog(p(y(i)x(i)))=i=1mL(y^,y)
由于上面的式子最右边有个负号,因此最大化联合概率密度函数等同于最小化
∑ i = 1 m L ( y ^ , y ) \sum_{i=1}^{m} L(\hat{y}, y) i=1mL(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=1mL(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=1mp(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=1mlog(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(yx)=y^y(1y^)(1y),代入到上面的式子,即最大化
∑ 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=1m(y(i)logy^(i)+(1y(i))log(1y^(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=1m(y(i)logσ(wTx(i)+b)+(1y(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*} w1Pw2PbP=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 mn的矩阵除以 1 ∗ m 1*m 1m的矩阵,相当于 m ∗ n m*n mn的矩阵的每一行都和该行向量对应元素相除。与之所类比,我们同样可以进行加、减、乘运算。

  • 矩阵加上一个实数
## 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 mn 1 ∗ n 1*n 1n,它们都是2维的,从后向前比对,先是 n n n,两个轴长度相同;其次是 m m m 1 1 1,其中一方为 1 1 1,故而满足广播兼容。
    • (2) 如果两个数组的维度不同,那么给低维度的数组前扩展提升一维,且扩展维的轴长度为1,然后继续形同(1)进行处理:例如 k ∗ m ∗ n k*m*n kmn 1 ∗ n 1*n 1n,它们的维度不同,需要先给 1 ∗ n 1*n 1n扩展一个维度变成 1 ∗ 1 ∗ n 1*1*n 11n,然后按照方法(1)进行处理。
      在这里插入图片描述

4.2. 使用明确的矩阵维度表示

使用明确的维度表示,不要使用一维数组,转向使用 n ∗ 1 n*1 n1维矩阵(基本上是列向量),或者 1 ∗ n 1 * n 1n 维矩阵(基本上是行向量)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2036506.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Selenium + Python 自动化测试09(多窗口切换)

我们的目标是:按照这一套资料学习下来,大家可以独立完成自动化测试的任务。 上一篇我们讨论了截图的操作方法,本篇文章我们讲述一下多窗口切换的操作方法。 在实际的测试项目组中我们可能会遇到多窗口的情况,有时候需要在不同窗口…

MyBatis 配置与测试方式

目录 一,什么是MyBatis 二,准备工作 创建项目 配置数据库连接 持久层代码 单元测试 一,什么是MyBatis 简单来说,MyBatis 是一款优秀的持久层框架,用于简化JDBC的开发,能更简单完成程序与数据库之间…

MySQL 复制建表、操作补充、pymysql操作—/—<10>

一、复制建表 1、复制其他表的结构 只复制结构,不复制值 CREATE TABLE aaa LIKE student student表: 复制完的a表: 2、复制查询结果并建表: create table a (select id,sum(score) as sum_sco from score group by id) score表结构如图所…

【存储学习笔记】3:备份(Backup)技术分类

1 定义 备份:出于数据恢复的目的而创建一份额外的数据副本。 2 技术指标 2.1 备份窗口(Backup Window) 从定指标的角度:可以安全地实施备份的时间 从实现指标的角度:备份操作所需的时间 2.2 恢复时间目标&#xf…

C 408—《数据结构》算法题基础篇—数组(通俗易懂)

目录 Δ前言 一、数组的合并 0.题目: 1.算法设计思想: 2.C语言描述: 3.算法的时间和空间复杂度 : 二、数组元素的倒置 0.题目 : 1.算法设计思想 : 2.C语言描述 : 3.算法的时间和空间复杂度 : 三、数组中特定值元素的删除 0.题目 : …

贷齐乐案例

源码分析&#xff1a; <?php // 设置 HTTP 头部&#xff0c;指定内容类型为 text/html&#xff0c;字符集为 utf-8 header("Content-type: text/html; charsetutf-8"); // 引入数据库配置文件 require db.inc.php; // 定义函数 dhtmlspecialchars&#xff0c;用…

基于WOA鲸鱼优化的GroupCNN分组卷积网络时间序列预测算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 分组卷积神经网络&#xff08;GroupCNN&#xff09; 4.2 WOA优化算法 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 matlab2022a 3.部分核…

获奖方案|趋动科技:资源池化释放AI算力价值

“据统计&#xff0c;GPU的平均利用率不超过30%&#xff0c;会产生巨大的算力资源浪费。我们用软件定义的方式通常可以把用户GPU的利用率提升3-8倍&#xff0c;甚至可以到10倍。” 这是算力池化软件公司趋动科技援引行业报告数据并结合自身企业最佳实践经验给出的最新数据。通…

分布式版本控制概述

目录 1. 版本控制软件的基础功能 2. 集中式版本控制 3. 分布式版本控制 git 是分布式管理控制工具&#xff0c;用来管理开发项目中的资源: 这样的软件在项目管理开发中称为 SCM&#xff08;Software Configuration Management&#xff09; 软件; 下面是版本控制到分布式版本…

java快速导出word文档

点关注不迷路&#xff0c;欢迎再访&#xff01; 精简博客内容&#xff0c;尽量已行业术语来分享。 努力做到对每一位认可自己的读者负责。 帮助别人的同时更是丰富自己的良机。 文章目录 前言一.添加 Apache POI 依赖二.填充文档内容三.导出文档效果测试 前言 在 Java 应用程序…

Apache CloudStack Official Document 翻译节选(一)

关于 Apache CloudStack 的 概念和专用术语 &#xff08;一&#xff09; 甲一 Apache CloudStack Apache CloudStack 是一个开源的IAAS平台&#xff0c;管理调度着用于构建公有云或私有云的计算、网络、存储资源池。 借助Apache CloudStack&#xff0c;你可以创建一个按需使用的…

解锁4款高效的视频转文字助手!

虽然视频已经成为我们获取和记录信息的重要方式&#xff0c;可是有时候我们仍需要将视频中的内容转换为文字&#xff0c;才能够有效地整理、分析和利用这些信息。所以就有了视频转文字工具&#xff0c;今天我就推荐&#xff14;款好用的转换工具给大家 1、福昕视频在线转换 直…

【Java数据结构】---List(Stack)

乐观学习&#xff0c;乐观生活&#xff0c;才能不断前进啊&#xff01;&#xff01;&#xff01; 我的主页&#xff1a;optimistic_chen 我的专栏&#xff1a;c语言 &#xff0c;Java 欢迎大家访问~ 创作不易&#xff0c;大佬们点赞鼓励下吧~ 文章目录 前言栈Stack栈的模拟实现…

PyTorch 基础学习(5)- 神经网络

系列文章&#xff1a; PyTorch 基础学习&#xff08;1&#xff09; - 快速入门 PyTorch 基础学习&#xff08;2&#xff09;- 张量 Tensors PyTorch 基础学习&#xff08;3&#xff09; - 张量的数学操作 PyTorch 基础学习&#xff08;4&#xff09;- 张量的类型 PyTorch 基础学…

<数据集>路面坑洼识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;681张 标注数量(xml文件个数)&#xff1a;681 标注数量(txt文件个数)&#xff1a;681 标注类别数&#xff1a;1 标注类别名称&#xff1a;[pothole] 使用标注工具&#xff1a;labelImg 标注规则&#xff1a;对类…

15.3 模型评估与调优

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; 工&#x1f497;重&#x1f497;hao&#x1f497;&#xff1a;野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题.…

给SQL server数据库表字段添加注释SQL,附修改、删除注释SQL及演示

目录 一. 前提小知识(数据库连接&#xff0c;数据库&#xff0c;SCHEMA&#xff0c;Table的关系) 二. 添加备注 2.1 添加备注基本语法(sys.sp_addextendedproperty) 2.2 SQL演示 2.3 fn_listextendedproperty函数查询备注个数 2.4 开发常用添加注释语法 三. 修改备注 3…

深入理解 PHP 高性能框架 Workerman 守护进程原理

大家好&#xff0c;我是码农先森。 守护进程顾名思义就是能够在后台一直运行的进程&#xff0c;不会霸占用户的会话终端&#xff0c;脱离了终端的控制。相信朋友们对这东西都不陌生了吧&#xff1f;如果连这个概念都还不能理解的话&#xff0c;建议回炉重造多看看 Linux 进程管…

C++:vector类(default关键字,迭代器失效)

目录 前言 成员变量结构 iterator定义 size capacity empty clear swap []运算符重载 push_back pop_back reserve resize 构造函数 默认构造函数 default 迭代器构造 拷贝构造函数 赋值重载函数 析构函数 insert erase 迭代器失效问题 insert失效 er…

Linux使用学习笔记3 系统运维监控基础

系统运维监控类命令 查询每个进程的线程数 for pid in $(ps -ef | grep -v grep|grep "systemd" |awk {print $2});do echo ${pid} > /tmp/a.txt;cat /proc/${pid}/status|grep Threads > /tmp/b.txt;paste /tmp/a.txt /tmp/b.txt;done|sort -k3 -rn for pid…