当前位置: 首页 > news >正文

深度学习 Pytorch 单层神经网络

神经网络是模仿人类大脑结构所构建的算法,在人脑里,我们有轴突连接神经元,在算法中,我们用圆表示神经元,用线表示神经元之间的连接,数据从神经网络的左侧输入,让神经元处理之后,从右侧输出结果。

在这里插入图片描述

下图是一个最简单的神经元的结构。从这里开始,我们正式开始认识神经网络。

在这里插入图片描述


28 单层回归网络:线性回归

28.1 单层回归网络的理论基础

深度学习中的计算是“简单大量”,而不是“复杂的单一问题”。神经网络的原理很多时候都比经典机器学习算法简单。了解神经网络,可以从 线性回归 算法开始。

线性回归算法是机器学习中最简单的回归类算法,多元线性回归指的就是一个样本对应多个特征的线性回归问题。假设我们的数据现在就是二维表,对于一个 n n n个特征的样本而言,它的预测结果可以写作一个几乎人人熟悉的方程:
z ^ i = b + w 1 x i 1 + w 2 x i 2 + … + w n x i n \hat{z}_i = b + w_1 x_{i1} + w_2 x_{i2} + \ldots + w_n x_{in} z^i=b+w1xi1+w2xi2++wnxin
w w w b b b被统称为模型的权重,其中 b b b被称为截距(intercept),也叫做偏差(bias), w 1 w_1 w1~ w n w_n wn被称为回归系数(regression coefficient),也叫作权重(weights), x i 1 x_{i1} xi1~ x i n x_{in} xin是样本 i i i上的不同特征。这个表达式,其实就和我们小学时就无比熟悉的 y = a x + b y = ax + b y=ax+b 是同样的性质。其中 y y y被我们称为因变量,在线性回归中表示为 z z z,在机器学习中也就表现为我们的标签。如果写作 z z z,则代表真实标签。如果写作 z ^ \hat{z} z^(读作z帽或者zhat),则代表预测出的标签。模型得出的结果,一定是预测的标签

符号规范
在我们学习autograd的时候,我们说线性回归的方程是 y ^ i = b + w 1 x i 1 + w 2 x i 2 + … + w n x i n \hat{y}_i = b + w_1 x_{i1} + w_2 x_{i2} + \ldots + w_n x_{in} y^i=b+w1xi1+w2xi2++wnxin。但在这里,为什么写做 z z z呢?首先,无论是回归问题还是分类问题,y永远表示标签(labels)。在回归问题中,y是连续型数字,在分类问题中,y是离散型的整数。对于线性回归来说,线性方程的输出结果就是最终的标签。但对于整个深度学习体系而言,复杂神经网络的输出才是最后的标签。在我们单独对线性回归进行说明的时候,行业惯例就是使用 z z z来表示线性回归的结果。

如果考虑我们有m个样本,则回归结果可以被写作:
z ^ i = b + w 1 x i 1 + w 2 x i 2 + … + w n x i n \hat{z}_i = b + w_1 x_{i1} + w_2 x_{i2} + \ldots + w_n x_{in} z^i=b+w1xi1+w2xi2++wnxin
其中 z ^ i \hat{z}_i z^i是包含了m个全部的样本的预测结果的列向量。注意,我们通常使用粗体的小写字母来表示列向量,粗体的大写字母表示矩阵或者行列式。 并且在机器学习中,我们默认所有的一维向量都是列向量。

我们可以使用矩阵来表示上面多个样本的回归结果的方程,其中 w w w可以被看做是一个结构为(n+1,1)的列矩阵(这里的n加上的1是我们的截距b), 是一个结构为(m,n+1)的特征矩阵(这里的n加上的1是为了与截距b相乘而留下的一列1,这列1有时也被称作 x 0 x_0 x0,则有:

[ z ^ 1 z ^ 2 z ^ 3 … z ^ m ] = [ 1 x 11 x 12 x 13 … x 1 n 1 x 21 x 22 x 23 … x 2 n 1 x 31 x 32 x 33 … x 3 n … … … … … 1 x m 1 x m 2 x m 3 … x m n ] ∗ [ b w 1 w 2 … w n ] \begin{bmatrix}\hat{z}_1 \\\hat{z}_2 \\\hat{z}_3 \\\ldots \\\hat{z}_m\end{bmatrix}= \begin{bmatrix} 1 & x_{11} & x_{12} & x_{13} & \ldots & x_{1n} \\ 1 & x_{21} & x_{22} & x_{23} & \ldots & x_{2n} \\ 1 & x_{31} & x_{32} & x_{33} & \ldots & x_{3n} \\ \ldots & \ldots & \ldots & \ldots & & \ldots \\ 1 & x_{m1} & x_{m2} & x_{m3} & \ldots & x_{mn} \end{bmatrix} * \begin{bmatrix} b \\ w_1 \\ w_2 \\ \ldots \\ w_n \end{bmatrix} z^1z^2z^3z^m = 1111x11x21x31xm1x12x22x32xm2x13x23x33xm3x1nx2nx3nxmn bw1w2wn

z ^ = X w \hat{z} = Xw z^=Xw

如果在我们的方程里没有常量b,我们则可以不写X中的第一列以及w中的第一行。

线性回归的任务,就是构造一个预测函数来映射输入的特征矩阵 和标签值 的线性关系。这个预测函数的图像是一条直线,所以线性回归的求解就是对直线的拟合过程。

预测函数的本质就是我们需要构建的模型,而构造预测函数的核心就是找出模型的权重向量 ,也就是求解线性方程组的参数(相当于求解 y = a x + b y=ax+b y=ax+b里的 a a a b b b)。

现在假设,我们的数据只有2个特征,则线性回归方程可以写作如下结构:
z ^ = b + x 1 w 1 + x 2 w 2 \hat{z}=b+x_1w_1+x_2w_2 z^=b+x1w1+x2w2
此时,我们只要对模型输入特征 x 1 x_1 x1 x 2 x_2 x2的取值,就可以得出对应的预测值 z ^ \hat{z} z^。神经网络的预测过程是从神经元左侧输入特征,让神经元处理数据,并从右侧输出预测结果。这个过程和我们刚才说到的线性回归输出预测值的过程是一致的。如果我们使用一个神经网络来表达线性回归上的过程,则可以有:

在这里插入图片描述

这就是一个最简单的单层回归神经网络的表示图。

在神经网络中,竖着排列在一起的一组神经元叫做“一层网络”,所以线性回归的网络直观看起来有两层,两层神经网络通过写有参数的线条相连。我们从左侧输入常数1和特征取值 x 1 x_1 x1 x 2 x_2 x2,再让它们与相对应的参数相乘,就可以得到 b b b x 1 w 1 x_1w_1 x1w1 x 2 w 2 x_2w_2 x2w2三个结果。这三个结果通过连接到下一层神经元的直线,被输入下一层神经元。我们在第二层的神经元中将三个乘积进行加和(使用符号 ∑ \sum 表示),就可以得到加和结果 z ^ \hat{z} z^,即 b + x 1 w 1 + x 2 w 2 b+x_1w_1+x_2w_2 b+x1w1+x2w2,这个值正是我们的预测值。可见,线性回归方程与上面的神经网络图达到的效果是一模一样的

在上述过程中,左侧的是神经网络的输入层input layer)。输入层由众多承载数据用的神经元组成,数据从这里输入,并流入处理数据的神经元中。在所有神经网络中,输入层永远只有一层,且每个神经元上只能承载一个特征(一个 x x x)或一个常量(通常都是1)。现在的二元线性回归只有两个特征,所以输入层上只需要三个神经元,包括两个特征和一个常量,其中这里的常量仅仅是被用来乘以偏差 b b b用的。对于没有偏差的线性回归来说,我们可以不设置常量1

右侧的是输出层output layer)。输出层由大于等于一个神经元组成,我们总是从这一层来获取预测结果。输出层的每个神经元上都承载着单个或多个功能,可以处理被输入神经元的数据。在线性回归中,这个功能就是“加和”,当我们把加和替换成其他的功能,就能够形成各种不同的神经网络。

在神经元之间相互连接的线表示了数据流动的方向,就像人脑神经细胞之间相互联系的“轴突”。在人脑神经细胞中,轴突控制电子信号流过的强度,在人工神经网络中,神经元之间的连接线上的权重也代表了信息可通过的强度。最简单的例子是,当 w w w0.5时,在特征 x 1 x_1 x1上的信息就只有0.5倍能够传递到下一层神经元中,因为被输入到下层神经元中去进行计算的实际值是 0.5 x 1 0.5x_1 0.5x1。相对的,如果 w 1 w_1 w12.5,则会传递2.5倍的 上的信息。因此,有的深度学习课程会将权重 w w w比喻成是电路中的”电压“,电压越大,则电信号越强烈,电压越小,信号也越弱,这都是在描述权重 w w w会如何影响传入下一层神经元的信息/数据量的大小。

到此,我们已经了解了线性回归的网络是怎么一回事,它是最简单的回归神经网络,同时也是最简单的神经网络。类似于线性回归这样的神经网络,被称为单层神经网络

单层神经网络
从直观来看,线性回归的网络结构明明有两层,为什么线性回归被叫做“单层神经网络”呢?业内通识是,在描述神经网络的层数的时候,我们不考虑输入层。输入层是每个神经网络都必须存在的一层,当使用相同的输入数据时,任意两个神经网络之间的不同之处就在输入层之后的所有层。所以,我们把输入层之后只有一层的神经网络称为单层神经网络。在非常非常少见的情况下,有的深度学习课程或教材中也会直接将所有层都算入其中,将上述网络称为“两层神经网络”,这种做法虽然不太规范,但也不能称之为“错误的”。因此,当出现“N层神经网络”的描述时,一定要注意原作者是否将输入层考虑进去了。

28.2 tensor实现单层神经网络的正向传播

让我们使用一组非常简单的代码来实现一下回归神经网络求解 z ^ \hat{z} z^的过程,在神经网络中,这个过程是从左向右进行的,被称为神经网络的正向传播(forward spread)。来看下面这组数据:

x0x1x2z
100-0.2
110-0.05
101-0.05
1110.1

我们将构造能够拟合出以上数据的单层回归神经网络

import torch
X = torch.tensor([[1,0,0],[1,1,0],[1,0,1],[1,1,1]], dtype = torch.float32)
z = torch.tensor([[-0.2],[-0.05], [-0.05],[0.1]])
w = torch.tensor([-0.2,0.15,0.15])def LinearR(X,w):zhat = torch.mv(X,w)return zhatzhat = LinearR(X,w)

28.3 tensor计算中的新手陷阱

接下来,我们对这段代码进行详细的说明

# 导入库
import torch# 首先生成特征张量
X = torch.tensor([[1, 0, 0], [1, 1, 0], [1, 0, 1], [1, 1, 1]])
# 我们输入的是整数,默认生成的是int64的类型# 生成标签
z = torch.tensor([-0.2, -0.05, -0.05, 0.1])
# 我们输入的是浮点数,默认生成的是float32的类型# 定义常量b和权重w
w = torch.tensor([-0.2, 0.15, 0.15])
# 注意,常量b所在的位置必须与特征张量中X中全为1的那一列所在位置相对应

tensor计算中的第一大坑:PyTorch的静态性

在前几节有提到过:静态性指的是对输入的张量类型有明确的要求,例如部分函数只能输入浮点型张量,而不能输入整型张量。

# 定义线性回归计算的函数
def LinearR(X, w):# 矩阵与向量相乘时,向量必须作为mv的第二个参数zhat = torch.mv(X, w)return zhat
LnearR(X,w)
# output : 
RuntimeError : expected scalar type Long but found Float

ps:Long = int64

PyTorch中的许多函数都不接受浮点型的分类标签,但也有许多函数要求真实标签的类型必须与预测值的类型一致,因此标签的类型定义总是一个容易踩坑的地方。通常来说,我们还是回将标签定义为float32,如果在函数运行时报错,要求整形,我们再使用.long()方法将其转换为整型。

另一个非常容易踩坑的地方是,PyTorch中许多函数不接受一维张量但同时也有许多函数不接受二维标签( ̄_ ̄|||)。因此我们在生成标签时,可以默认生成二维标签,若函数报错说不能接受二维标签,我们再使用view()函数将其调整为一维。

# 因此之后需要改成:
def LinearR(X, w):zhat = torch.mv(X.float(), w)return zhat# 还可以使用大写的Tensor来解决这个问题,但这个方法并不推荐
X = torch.Tensor([[1, 0, 0], [1, 1, 0], [1, 0, 1], [1, 1, 1]])# 或者直接养成好习惯
X = torch.Tensor([[1, 0, 0], [1, 1, 0], [1, 0, 1], [1, 1, 1]], dtype = torch.float32)
LinearR(X,w)
# output :
tensor([-0.2000, -0.0500, -0.0500,  0.1000])

torch.tensor——判断你的输入类型是什么类型,然后根据你输入的数据类型来确定结果的数据类型

torch.Tensor——无论你输入什么数据,都无脑使用float32


tensor计算中的第二大坑:精度问题

# 预测值
zhat = LinearR(X, w)	
# 真实值
z = torch.tensor([[-0.2],[-0.05], [-0.05],[0.1]], dtype = torch.float32)
zhat == z
# output :
tensor([ True, False, False, False])

False一定不是因为数据类型发生错误得出的,因为我们已经把z的数据类型改为了浮点型。

在多元线性回归中,我们使用SSE(误差平方和)来衡量回归的结果优劣:
S S E = ∑ i = 1 m ( z i − z ^ i ) 2 SSE = \sum_{i=1}^{m}(z_i - \hat{z}_i)^2 SSE=i=1m(ziz^i)2
如果预测值和真实值完全相等,那SSE的结果应该为0。在这里,SSE虽然非常接近0,但的确是不为0的。

SSE = sum((zhat - z) ** 2)
SSE
# output :
tensor(8.3267e-17)
#设置显示精度,再来看yhat与y_reg
torch.set_printoptions(precision=30) #看小数点后面30位的情况
zhat
# output :
tensor([-0.200000002980232238769531250000, -0.049999997019767761230468750000, -0.049999997019767761230468750000, 0.100000008940696716308593750000])
z
# output :
tensor([-0.200000002980232238769531250000, -0.050000000745058059692382812500, -0.050000000745058059692382812500,  0.100000001490116119384765625000])

zhatz的差异有两个原因:

  • float32由于只保留32位,所以精确性会有一些问题。
  • torch.mv这个函数在进行计算时,内部计算时会出现一些很微小的精度问题。

精度问题在tensor维度很高,数字很大时,也会变得更大

preds = torch.ones(300,68,64,64) * 0.1
preds.sum() * 10
# output :
tensor(83558352.)
preds = torch.ones(300,68,64,64)
preds.sum()
# output :
tensor(83558400.)

怎么解决这个问题呢?

python中存在decimal库不同,pytorch设置了64位浮点数来 尽量 减轻精度问题

preds = torch.ones(300,68,64,64,dtype = torch.float64) *0.1
preds.sum() * 10
# output :
tensor(83558400.000000059604644775390625000000, dtype=torch.float64)

但即便如此,也不能完全消除精度问题带来的区别

如果你希望能够无视掉非常小的区别,而让两个张量的比较结果展示为True,可以使用下面的代码

torch.allclose(zhat, z)

28.4 torch.nn.Linear实现单层回归神经网络的正向传播

在这里插入图片描述

上面为pytorch的架构图,从图中我们可以看到,torch.nn是包含了构筑神经网络结构基本元素的包,在这个包中可以找到任意的神经网络层。这些神经网络层都是nn.Module这个大类的子类。

我们的torch.nn.Linear就是神经网络中的“线性层”,它可以实现形如 z ^ = X w \hat{z}=Xw z^=Xw的加和功能。

在单层回归神经网络结构图中,torch.nn.Linear类表示了我们的输出层。现在我们就来看看它是如何使用的。

在这里插入图片描述

回顾一下我们的数据:

x0x1x2z
100-0.2
110-0.05
101-0.05
1110.1

接下来,使用nn.Linear来实现单层回归神经网络:

import torch
X = torch.tensor([[0,0],[1,0],[0,1],[1,1]],dtype = torch.float32)
output = torch.nn.Linear(2, 1)
zhat = output(X)
  • nn.Linear是一个类,在这里代表了输出层,所以使用了output作为变量名,output = 这一行相当于是类的实例化过程。
  • 实例化的时候,nn.Linear需要输入两个参数,分别是(上一层的神经元个数,这一层的神经元个数)。上一层是输出层,因此神经元个数由特征的个数决定(2个)。这一层是输出层,作为回归神经网络,输出层只有一个神经元。因此nn.Linear中输入的是(2,1)。
  • 上面只定义了X,没有定义wb。所有nn.Module的子类,形如nn.XXX的层,都会在实例化的同时随机生成wb的初始值。所以实例化之后,我们就可以调用以下属性来查看生成的 w w w b b b
# 查看生成的w
output.weight
# output :
Parameter containing:tensor([[ 0.683788955211639404296875000000, -0.588803172111511230468750000000]],requires_grad=True)# 查看生成的b
output.bias
# output :
Parameter containing:tensor([0.426940977573394775390625000000], requires_grad=True)
  • 其中,w是必然会生成的,b是我们可以控制是否要生成的。
output = torch.nn.Linear(2, 1, bias = False)
  • 由于wb是随机生成的,所以同样的代码运行多次后的结果是不一致的。如果我们希望控制随机性,则可以使用torch中的random类。如下所示:
torch.random.manual_seed(420)	# 人为设置随机数种子
  • 由于不需要定义常量b,因此在特征张量中,也不需要留出与常数项相乘的x0那一列,在输入数据时,我们只输入了两个特征x1和x2
  • 输入层只有一层,且输入层的结构(神经元的个数)由输入的特征张量 X 决定,因此在pytorch中构筑神经网络时,不需要定义输入层。
  • 实例化之后,将特征张量输入到实例化后的类中,即可得到输出层的输出结果。

由于我们没有自己定义wb,所以无法让nn.Linear输出的zhat与我们真实的z接近——让真实值与预测值差异更小的部分,我们会在之后进行讲解。


29 二分类神经网络:逻辑回归

29.1 二分类神经网络的理论基础

线性回归是统计学经典算法,它能够拟合出一条直线来描述变量之间的 线性关系 。但 在实际中,变量之间的关系通常都不是一条直线,而是呈现出某种曲线关系 。在统计学的历史中,为了让统计学模型能够更好地拟合曲线,统计学家们在线性回归的方程两边引入了联系函数(link function),对线性回归的方程做出了各种各样的变化,并将这些变化后的方程称为“广义线性回归”。其中比较著名的有等式两边同时取对数的对数函数回归、同时取指数的S形函数回归等。

y = a x + b → ln ⁡ y = ln ⁡ ( a x + b ) y = a x + b → e y = e a x + b \begin{align*} y &= ax + b \quad \rightarrow \quad \ln y = \ln(ax + b) \\ y &= ax + b \quad \rightarrow \quad e^y = e^{ax + b} \end{align*} yy=ax+blny=ln(ax+b)=ax+bey=eax+b

在探索的过程中,一种奇特的变化吸引了统计学家们的注意,这个变化就是sigmoid函数带来的变化。

Sigmoid函数的公式如下:
σ = S i g m o i d ( z ) = 1 1 + e − z \sigma = Sigmoid(z) = \frac{1}{1 + e^{-z}} σ=Sigmoid(z)=1+ez1
其中 e e e为自然常数(约为2.71828),其中 z z z是它的自变量, σ \sigma σ是因变量, z z z的值常常是线性模型的取值(比如,线性回归的结果 z z z)。Sigmoid函数是一个S型的函数,它的图像如下:

从图像上就可以看出,这个函数的性质相当特别。当自变量 z z z趋近正无穷时,因变量 σ \sigma σ趋近于1,而当 z z z趋近负无穷时, σ \sigma σ趋近于0,这使得sigmoid函数能够将任何实数映射到(0,1)区间。同时,Sigmoid的导数在$ z=0 点时最大(这一点的斜率最大),所以它可以快速将数据从 点时最大(这一点的斜率最大),所以它可以快速将数据从 点时最大(这一点的斜率最大),所以它可以快速将数据从z=0$的附近排开,让数据点到远离自变量取0的地方去。这样的性质,让sigmoid函数拥有将连续性变量 转化为离散型变量 的力量,这也就是化回归算法为分类算法的力量

具体怎么操作呢?只要将线性回归方程的结果作为自变量带入sigmoid函数,得出的数据就一定是(0,1)之间的值。此时,只要我们设定一个阈值(比如0.5),规定 大于0.5时,预测结果为1类, 小于0.5时,预测结果为0类,则可以顺利将回归算法转化为分类算法。此时,我们的标签就是类别0和1了。这个阈值可以自己调整,在没有调整之前,一般默认0.5

σ = 1 1 + e − z = 1 1 + e − X w \sigma = \frac{1}{1 + e^{-z}} = \frac{1}{1 + e^{-Xw}} σ=1+ez1=1+eXw1
更神奇的是,当我们对线性回归的结果取sigmoid函数之后,只要再进行以下操作:

1)将结果 σ \sigma σ以几率 ( σ 1 − σ ) \left(\frac{\sigma}{1-\sigma}\right) (1σσ)的形式展现

2)在几率上求以e为底的对数

就很容易得到:
ln ⁡ σ 1 − σ = ln ⁡ ( 1 1 + e − X w 1 − 1 1 + e − X w ) = ln ⁡ ( 1 1 + e − X w e − X w 1 + e − X w ) = ln ⁡ ( 1 e − X w ) = ln ⁡ ( e X w ) = X w \begin{align*} \ln \frac{\sigma}{1-\sigma} &= \ln \left( \frac{\frac{1}{1+e^{-Xw}}}{1 - \frac{1}{1+e^{-Xw}}} \right) \\ &= \ln \left( \frac{\frac{1}{1+e^{-Xw}}}{\frac{e^{-Xw}}{1+e^{-Xw}}} \right) \\ &= \ln \left( \frac{1}{e^{-Xw}} \right) \\ &= \ln (e^{Xw}) \\ &= Xw \end{align*} ln1σσ=ln(11+eXw11+eXw1)=ln(1+eXweXw1+eXw1)=ln(eXw1)=ln(eXw)=Xw
不难发现,让 σ \sigma σ取对数几率后所得到的值就是我们线性回归的 z z z!因为这个性质,在等号两边加sigmoid的算法被称为“对数几率回归”,在英文中就是Logistic Regression,就是逻辑回归。逻辑回归可能是广义线性回归中最广为人知的算法,它是一个叫做“回归“实际上却总是被用来做分类的算法,对机器学习和深度学习都有重大的意义。在面试中,如果我们希望了解一个人对机器学习的理解程度,第一个问题可能就会从sigmoid函数以及逻辑回归是如何来的开始。

σ \sigma σ值代表了样本为某一类标签的概率
ln ⁡ σ 1 − σ \ln \frac{\sigma}{1 - \sigma} ln1σσ是形似对数几率的一种变化。而几率odds的本质其实是 p 1 − p \frac{p}{1-p} 1pp,其中p是事件A发生的概率,而1-p是事件A不会发生的概率,并且p+(1-p)=1。因此,很多人在理解逻辑回归时,都对 σ \sigma σ做出如下的解释:我们让线性回归结果逼近01,此时 σ \sigma σ 1 − σ 1-\sigma 1σ之和为1,因此它们可以被我们看作是一对正反例发生的概率,即 σ \sigma σ是某样本i的标签被预测为1的概率,而 1 − σ 1-\sigma 1σi的标签被预测为0的概率, σ 1 − σ \frac{\sigma}{1-\sigma} 1σσ就是样本i的标签被预测为1的相对概率。基于这种理解,逻辑回归、即单层二分类神经网络返回的结果被当成是概率来看待和使用(如果直接说它就是概率,或许不太严谨)。每当我们希望求解“样本i的标签是1或是0的概率”时,我们就使用逻辑回归。因此,当一个样本对应的 σ i \sigma_i σi越接近10,我们就认为逻辑回归对这个样本的预测结果越肯定,样本被分类正确的可能性也越高。如果 σ i \sigma_i σi非常接近阈值(比如0.5),就说明逻辑回归其实对这个样本究竟应该是哪一类别,不是非常肯定。

29.2 tensor实现二分类神经网络的正向传播

我们可以在PyTorch中非常简单地实现逻辑回归的预测过程,让我们来看下面这一组数据。很容易注意到,这组数据和上面的回归数据的特征( x 1 , x 2 x_1,x_2 x1,x2)是完全一致的,只不过标签y由连续型结果转变为了分类型的01。这一组分类的规律是这样的:当两个特征都为1的时候标签就为1,否则标签就为0。这一组特殊的数据被我们称之为 “与门”(AND GATE) ,这里的“与”正是表示“特征一与特征二都是1”的含义。

x0x1x2andgate
1000
1100
1010
1111

拟合这组数据,只需要在刚才我们写好的代码后面加上sigmoid函数以及阈值处理后的变化。

import torch
X = torch.tensor([[1, 0, 0], [1, 1, 0], [1, 0, 1], [1, 1, 1]], dtype = torch.float32)
andgate = torch.tensor([-0.2, 0.15, 0.15], dtype = torch.float32)
# 保险起见,生成二维的、float32类型的标签
w = torch.tensor([-0.2,0.15,0.15], dtype = torch.float32)def LogisticR(X,w):zhat = torch.mv(X,w)sigma = 1/(1+torch.exp(-zhat))#sigma = torch.sigmoid(zhat)andhat = torch.tensor([int(x) for x in sigma >= 0.5], dtype = torch.float32)return sigma, andhat

接下来,我们对这段代码进行详细的说明:

# 导入torch库
import torch
# 特征张量,养成良好习惯,上来就定义数据类型
X = torch.tensor([[1,0,0],[1,1,0],[1,0,1],[1,1,1]], dtype = torch.float32)
#标签,分类问题的标签是整型
andgate = torch.tensor([0,0,0,1], dtype = torch.float32)
#定义w,注意这一组w与之前在回归中使用的完全一样
w = torch.tensor([-0.2,0.15,0.15], dtype = torch.float32)
def LogisticR(X,w):#首先执行线性回归的过程,依然是mv函数,让矩阵与向量相乘得到zzhat = torch.mv(X,w) #执行sigmoid函数,你可以调用torch中的sigmoid函数,也可以自己用torch.exp来写sigma = torch.sigmoid(zhat) #sigma = 1/(1+torch.exp(-zhat))#设置阈值为0.5, 使用列表推导式将值转化为0和1andhat = torch.tensor([int(x) for x in sigma >= 0.5], dtype = torch.float32) return sigma, andhat
sigma, andhat = LogisticR(X,w)
sigma
# output :
tensor([0.450166016817092895507812500000, 0.487502634525299072265625000000,0.487502634525299072265625000000, 0.524979174137115478515625000000])
andhat
# output :
tensor([0., 0., 0., 1.])
andgate == andhat
#最后得到的都是0和1,虽然andhat数据格式是float32,但本质上数还是整数,不存在精度问题

可见,这里得到了与我们期待的结果一致的结果,这就将回归算法转变为了二分类。这个过程在神经网络中的表示图如下:

在这里插入图片描述

可以看出,这个结构与线性回归的神经网络唯一不同的就是输出层中多出了一个Sigmoid(z) 。当有了Sigmoid函数的结果 σ \sigma σ之后,只要了解阈值是0.5(或者任意我们自己设定的数值),就可以轻松地判断任意样本的预测标签 y ^ \hat{y} y^。在二分类神经网络中,Sigmoid实现了将连续型数值转换为分类型数值的作用,在现代神经网络架构中,除了Sigmoid函数之外,还有许多其他的函数可以被用来将连续型数据分割为离散型数据,接下来,我们就介绍一下这些函数。


29.3 符号函数sign,ReLU,Tanh

符号函数sign

我们可以使用以下表达式来表示它:
y = { 1 if  z > 0 0 if  z = 0 − 1 if  z < 0 y = \begin{cases} 1 & \text{if } z > 0 \\ 0 & \text{if } z = 0 \\ -1 & \text{if } z < 0 \end{cases} y= 101if z>0if z=0if z<0
由于函数的取值是间断的,符号函数也被称为“阶跃函数”,表示在0的两端,函数的结果y是从-1直接阶跃到了1。在这里,我们使用y而不是 σ \sigma σ来表示输出的结果,是因为输出结果直接是01-1这样的类别,就相当于标签了。对于sigmoid函数而言, 返回的是0~1之间的概率值,如果我们希望获取最终预测出的类别,还需要将概率转变成01这样的数字才可以。但符号函数可以直接返回类别,因此我们可以认为符号函数输出的结果就是最终的预测结果y。在二分类中,符号函数也可以忽略中间的时候,直接分为01两类,用如下式子表示:
y = { 1 if  z > 0 0 if  z ≤ 0 y = \begin{cases} 1 & \text{if } z > 0 \\ 0 & \text{if } z \leq 0 \end{cases} y={10if z>0if z0
等号被并在上方或下方都可以。这个式子可以很容易被转化为下面的式子:
∵ z = w 1 x 1 + w 2 x 2 + b ∴ y = { 1 if  w 1 x 1 + w 2 x 2 + b > 0 0 if  w 1 x 1 + w 2 x 2 + b ≤ 0 ∴ y = { 1 if  w 1 x 1 + w 2 x 2 > − b 0 if  w 1 x 1 + w 2 x 2 ≤ − b \because z = w_1 x_1 + w_2 x_2 + b \\ \therefore y = \begin{cases} 1 & \text{if } w_1 x_1 + w_2 x_2 + b > 0 \\ 0 & \text{if } w_1 x_1 + w_2 x_2 + b \leq 0 \end{cases} \\ \therefore y = \begin{cases} 1 & \text{if } w_1 x_1 + w_2 x_2 > -b \\ 0 & \text{if } w_1 x_1 + w_2 x_2 \leq -b \end{cases} z=w1x1+w2x2+by={10if w1x1+w2x2+b>0if w1x1+w2x2+b0y={10if w1x1+w2x2>bif w1x1+w2x2b
此时, − b -b b就是一个阈值,我们可以使用任意字母来替代它,比较常见的是字母 θ \theta θ 。当然,不把它当做阈值,依然保留 w 1 x 1 + w 2 x 2 + b w_1x_1+w_2x_2+b w1x1+w2x2+b0进行比较的关系也没有任何问题。和sigmoid一样,我们也可以使用阶跃函数来处理”与门“的数据:

import torch
X = torch.tensor([[0,0],[1,0],[0,1],[1,1]],dtype=torch.float32)
andgate = torch.tensor([[0],[0],[0],[1]], dtype = torch.float32)
w = torch.tensor([-0.2,0.15, 0.15], dtype = torch.float32)def LinearRwithsign(X,w):zhat = torch.mv(X,w)andhat = torch.tensor([int(x) for x in zhat >= 0], dtype = torch.float32)return zhat, andhat

阶跃函数和sigmoid都可以完成二分类的任务。在神经网络的二分类中, 的默认取值一般都是sigmoid函数,少用阶跃函数,这是由神经网络的解法决定的。


ReLU

ReLU(Rectified Linear Unit)函数又名整流线型单元函数,应用甚至比sigmoid更广泛。ReLU提供了一个很简单的非线性变换:当输入的自变量大于0时,直接输出该值,当输入的自变量小于等于0时,输出0。这个过程可以用以下公式表示出来:
R e L U : σ = { z ( z > 0 ) 0 ( z ≤ 0 ) ReLU: \sigma = \begin{cases} z & (z > 0) \\ 0 & (z \leq 0) \end{cases} ReLU:σ={z0(z>0)(z0)
ReLU函数是一个非常简单的函数,本质就是max(0,z)max函数会从输入的数值中选择较大的那个值进行输出,以达到保留正数元素,将负元素清零的作用。ReLU的图像如下所示:

在这里插入图片描述

相对的,ReLU函数导数的图像如下:

在这里插入图片描述

当输入 z z z为正数时,ReLU函数的导数为1,当 z z z为负数时,ReLU函数的导数为0,当输入为0时,ReLU函数不可导。因此,ReLU函数的导数图像看起来就是阶跃函数,这是一个美好的巧合。


tanh

tanh(hyperbolic tangent)是双曲正切函数,双曲正切函数的性质与sigmoid相似,它能够将数值压缩到(-1,1)区间内。
t a n h : σ = e 2 z − 1 e 2 z + 1 tanh: \sigma = \frac{e^{2z} - 1}{e^{2z} + 1} tanh:σ=e2z+1e2z1
而双曲正切函数的图像如下:

在这里插入图片描述

可以看出,tanh的图像和sigmoid函数很像,不过sigmoid函数的范围是在(0,1)之间,tanh却是在坐标系的原点(0,0)点上中心对称。

tanh求导后可以得到如下公式和导数图像:
tanh ⁡ ′ ( z ) = 1 − tanh ⁡ 2 ( z ) \tanh'(z) = 1 - \tanh^2(z) tanh(z)=1tanh2(z)
在这里插入图片描述

可以看出,当输入的 约接近于0,tanh函数导数也越接近最大值1,当输入越偏离0时,tanh函数的导数越接近于0。**这些函数是最常见的二分类转化函数,他们在神经网络的结构中有着不可替代的作用。**在单层神经网络中,这种作用是无法被体现的,因此关于这一点,我们可以之后再进行说明。到这里,我们只需要知道这些函数都可以将连续型数据转化为二分类就足够了。


29.4 torch.functional实现二分类神经网络的正向传播

之前我们使用torch.nn.Linear类实现了单层回归神经网络,现在我们试着来实现单层二分类神经网络,也就是逻辑回归。逻辑回归与线性回归的唯一区别,就是在线性回归的结果之后套上了sigmoid函数。

不难想象,只要让nn.Linear的输出结果再经过sigmoid函数,就可以实现逻辑回归的正向传播了。

PyTorch中,我们几乎总是从nn.functional中调用相关函数。

在这里插入图片描述

回顾一下我们的数据和网络架构:

x0x1x2andgate
1000
1100
1010
1111

接下来,我们在之前线性回归代码的基础上,加上nn.functional来实现单层二分类神经网络:

import torch
from torch.nn import functional as F
X = torch.tensor([[0,0],[1,0],[0,1],[1,1]], dtype = torch.float32)
torch.random.manual_seed(420) #人为设置随机数种子
dense = torch.nn.Linear(2,1)
zhat = dense(X)
sigma = F.sigmoid(zhat)
y = [int(x) for x in sigma > 0.5]

在这里,nn.Linear虽然依然是输出层,但却没有担任最终输出值的角色,因此这里我们使用dense作为变量名。dense表示紧密链接的层,即上一层的大部分神经元都与这一层的大部分神经元相连,在许多神经网络中我们都会用到密集链接的层,因此dense是我们经常会用到的一个变量名。我们将数据从nn.Linear传入,得到zhat,然后再将zhat的结果传入sigmoid函数,得到sigma,之后再设置阈值为0.5,得到最后的y

PyTorch中,我们可以从functional模块里找出大部分之前我们提到的函数

#符号函数sign
torch.sign(zhat)
#ReLU
F.relu(zhat)
#tanh
torch.tanh(zhat)

PyTorch的安排中,符号函数sign与双曲正切函数tanh更多时候只是被用作数学计算工具,而ReLUSigmoid却作为神经网络的组成部分被放在库functional中,这其实反映出实际使用时大部分人的选择。

ReLUSigmoid还是主流的、位于nn.Linear后的函数。


30 多分类神经网络:Softmax回归

30.1 认识softmax函数

之前介绍分类神经网络时,我们只说明了二分类问题,即标签只有两种类别的问题(0和1,猫和狗)。虽然在实际应用中,许多分类问题都可以用二分类的思维解决,但依然存在很多多分类的情况,最典型的就是手写数字的识别问题。计算机在识别手写数字时,需要对每一位数字进行判断,而个位数字总共有10个(0~9),所以手写数字的分类是十分类问题,一般分别用0~9表示。

在这里插入图片描述

Softmax函数是深度学习基础中的基础,它是神经网络进行多分类时,默认放在输出层中处理数据的函数。假设现在神经网络是用于三分类数据,且三个分类分别是苹果,柠檬和百香果,序号则分别是分类1、分类2和分类3。则使用softmax函数的神经网络的模型会如下所示:

在这里插入图片描述

与二分类一样,我们从网络左侧输入特征,从右侧输出概率,且概率是通过线性回归的结果 z z z外嵌套softmax函数来进行计算。在二分类时,输出层只有一个神经元,只输出样本对于正类别的概率(通常是标签为1的概率),而softmax的输出层有三个神经元,分别输出该样本的真实标签是苹果、柠檬或百香果的概率 σ 1 , σ 2 , σ 3 \sigma_1,\sigma_2,\sigma_3 σ1,σ2,σ3在多分类中,神经元的个数与标签类别的个数是一致的,如果是十分类,在输出层上就会存在十个神经元,分别输出十个不同的概率。此时,样本的预测标签就是所有输出的概率 σ 1 , σ 2 , σ 3 \sigma_1,\sigma_2,\sigma_3 σ1,σ2,σ3中最大的概率对应的标签类别

那每个概率是如何计算出来的呢?来看Softmax函数的公式:
σ k = Softmax ( z k ) = e z k ∑ K e z \sigma_k = \text{Softmax}(z_k) = \frac{e^{z_k}}{\sum^{K} e^z} σk=Softmax(zk)=Kezezk
其中 e e e为自然常数(约为2.71828), 与sigmoid函数中的 z z z一样,表示回归类算法(如线性回归)的结果。 表示该数据的标签中总共有 K K K个标签类别,如三分类时 K = 3 K=3 K=3,四分类时 K = 4 K=4 K=4 k k k表示标签类别 k k k类。很容易可以看出,Softmax函数的分子是多分类状况下某一个标签类别的回归结果的指数函数,分母是多分类状况下所有标签类别的回归结果的指数函数之和,因此Softmax****函数的结果代表了样本的结果为类别 k k k的概率**。


30.2 Pytorch中的softmax函数

我们曾经提到过,神经网络是模型效果很好,但运算速度非常缓慢的算法。softmax函数也存在相同的问题——它可以将多分类的结果转变为概率(这是一个极大的优势),但它需要的计算量非常巨大。由于softmax的分子和分母中都带有 e e e为底的指数函数,所以在计算中非常容易出现极大的数值。

在这里插入图片描述

如上图所示, e10就已经等于20000了,而回归结果 z z z完全可能是成千上万的数字。事实上e100会变成一个后面有40多个0的超大值,e1000则会直接返回无限大inf,这意味着这些数字已经超出了计算机处理数时要求的有限数据宽度,超大数值无法被计算机运算和表示。这种现象叫做“溢出“,当计算机返回”内存不足”或Python服务器直接断开连接的时候,可能就是发生了这个问题。来看看这个问题实际发生时的状况:

#对于单一样本,假定一组巨大的z
z = torch.tensor([1010,1000,990], dtype=torch.float32)
torch.exp(z) / torch.sum(torch.exp(z)) # softmax函数的运算
# output :
tensor([nan, nan, nan])

因此,我们一般不会亲自使用tensor来手写softmax函数。在PyTorch中,我们往往使用内置好的softmax函数来计算softmax的结果,我们可以使用torch.softmax来轻松的调用它,具体代码如下:

z = torch.tensor([1010,1000,990], dtype=torch.float32)
torch.softmax(z,0)
#你也可以使用F.softmax, 它返回的结果与torch.softmax是完全一致的

#假设三个输出层神经元得到的z分别是10,9,5
z = torch.tensor([10,9,5], dtype=torch.float32)
torch.exp(z) / torch.sum(torch.exp(z)) # softmax函数的运算z = torch.tensor([10,9,5], dtype=torch.float32)
torch.softmax(z,0)	# 第二个参数表示计算的维度索引
# output :
tensor([0.7275, 0.2676, 0.0049])

从上面的结果可以看出,softmax函数输出的是从01.0之间的实数,而且多个输出值的总和是1。因为有了这个性质,我们可以把softmax函数的输出解释为“概率”,这和我们使用sigmoid函数之后认为函数返回的结果是概率异曲同工。从结果来看,我们可以认为返回了我们设定的 ([10,9,5])的这个样本的结果应该是第一个类别(也就是z=10的类别),因为类别1的概率是最大的

需要注意的是,使用了softmax函数之后,各个 之间的大小关系并不会随之改变,这是因为指数函数ez是单调递增函数,也就是说,使用softmax之前的 如果比较大,那使用softmax之后返回的概率也依然比较大。这是说,无论是否使用softmax,我们都可以判断出样本被预测为哪一类,我们只需要看最大的那一类就可以了。所以,在神经网络进行分类的时候,如果不需要了解具体分类问题每一类的概率是多少,而只需要知道最终的分类结果,我们可以省略输出层上的softmax函数。


30.3 使用nn.Linear与functional实现多分类神经网络的正向传播

import torch
from torch.nn import functional as F
X = torch.tensor([[0,0],[1,0],[0,1],[1,1]], dtype = torch.float32)
torch.random.manual_seed(420) 
dense = torch.nn.Linear(2,3) #此时,输出层上的神经元个数是3个,因此应该是(2,3)
zhat = dense(X)
sigma = F.softmax(zhat,dim=1) #此时需要进行加和的维度是1

31 回归vs二分类vs多分类

到这里,我们已经见过了三个不同的神经网络:

在这里插入图片描述

注意到有什么相似和不同了吗?

首先可能会注意到的是,这三个神经网络都是单层神经网络,除了输入层,他们都有且只有一层网络。实际上,现实中使用的神经网络几乎99%都是多层的,但我们的网络也能够顺利进行预测,这说明单层神经网络其实已经能够实现基本的预测功能。同时,这也说明了一个问题,无论处理的是回归还是分类,神经网络的处理原理是一致的。实际上,就连算法的限制、优化方法和求解方法也都是一致的。回归和分类神经网络唯一的不同只有输出层上的 σ \sigma σ

虽然线性回归看起来并没有 σ \sigma σ的存在,但实际上我们可以认为线性回归中的 σ \sigma σ是一个恒等函数(identityfunction),即是说 σ ( z ) = z \sigma(z)=z σ(z)=z(相当于 y = x y=x y=x,或 f ( x ) = x f(x)=x f(x)=x)。而多分类的时候也可以不采用任何函数,只观察 z z z的大小,所以多分类也可以被认为是利用了恒等函数作为 σ \sigma σ。总结来说,回归和分类对应的 σ \sigma σ分别如下:

输出类型 σ \sigma σ
回归恒等函数
二分类sigmoid或任意可以实现二分类的函数(通常都是sigmoid
多分类softmax或恒等函数

第二个很容易发现的现象是,只有多分类的情况在输出层出现了超过一个神经元。实际上,当处理单标签问题时(即只有一个y的问题),回归神经网络和二分类神经网络的输出层永远只有一个神经元,而只有多分类的情况才会让输出层上超过一个神经元。

相关文章:

深度学习 Pytorch 单层神经网络

神经网络是模仿人类大脑结构所构建的算法&#xff0c;在人脑里&#xff0c;我们有轴突连接神经元&#xff0c;在算法中&#xff0c;我们用圆表示神经元&#xff0c;用线表示神经元之间的连接&#xff0c;数据从神经网络的左侧输入&#xff0c;让神经元处理之后&#xff0c;从右…...

常用集合-数据结构-MySql

目录 java核心&#xff1a; 常用集合与数据结构: 单例集合: 双列集合: 线程安全的集合: ConcurrentHashMap集合: HashTable集合: CopyOnWriteArrayList集合: CopyOnWriteArraySet集合: ConcurrentLinkedQueue队列: ConcurrentSkipListMap和ConcurrentSkipListSet&…...

策略模式 - 策略模式的使用

引言 在软件开发中&#xff0c;设计模式是解决常见问题的经典解决方案。策略模式&#xff08;Strategy Pattern&#xff09;是行为型设计模式之一&#xff0c;它允许在运行时选择算法的行为。通过将算法封装在独立的类中&#xff0c;策略模式使得算法可以独立于使用它的客户端…...

【贪心算法】在有盾牌的情况下能通过每轮伤害的最小值(亚马逊笔试题)

思路&#xff1a; 采用贪心算法&#xff0c;先计算出来所有的伤害值&#xff0c;然后再计算每轮在使用盾牌的情况下能减少伤害的最大值&#xff0c;最后用总的伤害值减去能减少的最大值就是最少的总伤害值 public static long getMinimumValue(List<Integer> power, int…...

零基础Vue学习1——Vue学习前环境准备

目录 环境准备 创建Vue项目 项目目录说明 后续开发过程中常用命令 环境准备 安装开发工具&#xff1a;vscode、webstorm、idea都可以安装node:V22以上版本即可安装pnpm 不知道怎么安装的可以私信我教你方法 创建Vue项目 本地新建一个文件夹&#xff0c;之后在文件夹下打开…...

小游戏源码开发搭建技术栈和服务器配置流程

近些年各种场景小游戏开发搭建版本层出不穷,山东布谷科技拥有多年海内外小游戏源码开发经验&#xff0c;现为从事小游戏源码开发或游戏运营的朋友们详细介绍小游戏开发及服务器配置流程。 一、可以对接到app的小游戏是如何开发的 1、小游戏源码开发的需求分析&#xff1a; 明…...

【Rust自学】15.3. Deref trait Pt.2:隐式解引用转化与可变性

喜欢的话别忘了点赞、收藏加关注哦&#xff08;加关注即可阅读全文&#xff09;&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 15.3.1. 函数和方法的隐式解引用转化(Deref Coercion) 隐式解引用转化(Deref Coercion)是为…...

SQL-leetcode—1174. 即时食物配送 II

1174. 即时食物配送 II 配送表: Delivery ------------------------------------ | Column Name | Type | ------------------------------------ | delivery_id | int | | customer_id | int | | order_date | date | | customer_pref_delivery_date | date | -------------…...

css3 svg制作404页面动画效果HTML源码

源码介绍 css3 svg制作404页面动画效果HTML源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果 效果预览 源码如下 <!doctype html> <html> <head> <meta charse…...

MATLAB提供的颜色映射表colormap——伪彩色

图像处理领域的一个习惯&#xff1a;不是真实的颜色&#xff0c;一般用伪彩色。一是说明不是物体本身的颜色&#xff0c;二是彩色更容易分辨。 MATLAB陆续提供了16种颜色映射表colormap。 之前的都很丑&#xff0c;近5年新增的4种还可以。总的说来还是丑。 这是一种鸟的名字。…...

2013年蓝桥杯第四届CC++大学B组真题及代码

目录 1A&#xff1a;高斯日记&#xff08;日期计算&#xff09; 2B&#xff1a;马虎的算式&#xff08;暴力模拟&#xff09; 3C&#xff1a;第39级台阶&#xff08;dfs或dp&#xff09; 4D&#xff1a;黄金连分数&#xff08;递推大数运算&#xff09; 5E&#xff1a;前缀…...

我的创作纪念日——1/23

机缘 想起写博客&#xff0c;其实是当时看鹏哥C语言时&#xff0c;他说通过写博客的方式来记录自己学习过程&#xff0c;有利于提升自己。尽管我只看了几集就没怎么看&#xff0c;但是写博客的习惯保留下来。 至于为什么&#xff0c;一方面单纯当作单个代码库&#xff0c;把自…...

C# Interlocked 类使用详解

总目录 前言 在多线程编程中&#xff0c;确保多个线程对共享资源的安全访问是一个关键挑战。C# 提供了多种同步机制来处理并发问题&#xff0c;其中 System.Threading.Interlocked 类提供了一种轻量级的方法来进行原子操作。它允许您执行一些常见的增量、减量、交换等操作&…...

SYN Flooding的攻击原理

SYN Flooding是一种常见的网络攻击方式&#xff0c;属于拒绝服务攻击&#xff08;DoS&#xff09;的一种&#xff0c;其攻击原理主要是利用了TCP协议的三次握手过程&#xff0c;以下是具体介绍&#xff1a; TCP三次握手正常流程 第一次握手&#xff1a;客户端向服务器发送一个…...

Mono里运行C#脚本35—加载C#语言基类的过程

前面大体地分析了整个Mono运行过程,主要从文件的加载,再到EXE文件的入口点, 然后到方法的编译,机器代码的生成,再到函数调用的跳板转换,进而解析递归地实现JIT。 但是还有很多功能没有解析的,就是C#语言相关最多的,就是类的加载,以及类语言设计的实现属性, 比如类的…...

类包含类 三角分形 面向对象

Cad c# Sj类的构造函数&#xff0c;直接包含电线和三个分形三角形。...

Flutter:搜索页,搜索bar封装

view 使用内置的Chip简化布局 import package:chenyanzhenxuan/common/index.dart; import package:ducafe_ui_core/ducafe_ui_core.dart; import package:flutter/material.dart; import package:get/get.dart; import package:tdesign_flutter/tdesign_flutter.dart;import i…...

chrome插件:网页图片高清下载

前置条件&#xff1a; 安装有chrome谷歌浏览器的电脑 使用步骤&#xff1a; 1.打开chrome扩展插件 2.点击管理扩展程序 3.加载已解压的扩展程序 4.选择对应文件夹 5.成功后会出现一个扩展小程序 6.点击对应小程序 7.输入需要访问的网址&#xff0c;点击扩展插件即可进行图片…...

docker 简要笔记

文章目录 一、前提内容1、docker 环境准备2、docker-compose 环境准备3、流程说明 二、打包 docker 镜像1、基础镜像2、国内镜像源3、基础的dockerfile4、打包镜像 四、构建运行1、docker 部分2、docker-compose 部分2.1、构建docker-compose.yml2.1.1、同目录构建2.1.2、利用镜…...

Java I/O 流介绍

Java学习资料 Java学习资料 Java学习资料 一、引言 在 Java 编程中&#xff0c;I/O&#xff08;Input/Output&#xff09;流是处理输入和输出操作的核心机制。它允许程序与外部设备&#xff08;如文件、网络连接、键盘、显示器等&#xff09;进行数据交互。通过使用 I/O 流&…...

C# OpenCV机器视觉:利用CNN实现快速模板匹配

在一个阳光灿烂的周末&#xff0c;阿强正瘫在沙发上&#xff0c;百无聊赖地换着电视频道。突然&#xff0c;一则新闻吸引了他的注意&#xff1a;某博物馆里一幅珍贵的古画离奇失踪&#xff0c;警方怀疑是被一伙狡猾的盗贼偷走了&#xff0c;现场只留下一些模糊不清的监控画面&a…...

数据结构——实验七·排序

欢迎各位大佬们来到Tubishu的博客&#x1f31f; Tubishu是一名计算机本科生&#xff0c;不定期发送一些在学校的成果供佬们消遣~希望能为佬的编程之路添砖加瓦⭐&#x1f525; 求各位大佬们垂怜&#x1f525;点赞评论一下呗&#x1f525;&#x1f525; 本文专栏 ➡️ 数据结构 …...

Flutter_学习记录_Tab的简单Demo~真的很简单

1. Tab的简单使用了解 要实现tab(选项卡或者标签视图)需要用到三个组件&#xff1a; TabBarTabBarViewTabController 这一块&#xff0c;我也不知道怎么整理了&#xff0c;直接提供代码吧&#xff1a; import package:flutter/material.dart;void main() {runApp(MyApp());…...

const的用法

文章目录 一、C和C中const修饰变量的区别二、const和一级指针的结合const修饰的量常出现的错误是:const和一级指针的结合总结&#xff1a;const和指针的类型转换公式 三、const和二级指针的结合 一、C和C中const修饰变量的区别 C中&#xff1a;const必须初始化&#xff0c;叫常…...

分布式微服务系统简述

distributed microservice 分布式与微服务的定义及关系&#xff1b;分布式微服务架构里的各组件&#xff0c;如&#xff1a;配置中心、服务注册/发现、服务网关、负载均衡器、限流降级、断路器、服务调用、分布式事务等&#xff1b;spring cloud 介绍及实现案例&#xff0c;如…...

Uniapp开发总结

一、tabBar 如果应用是一个多 tab 应用&#xff0c;可以通过 tabBar 配置项指定一级导航栏&#xff0c;以及 tab 切换时显示的对应页。 pages.json 页面路由 | uni-app官网 pages.json基本配置&#xff1a; "tabBar": {"color": "#000",&quo…...

uniapp APP端页面触发调用webview(页面为uniapp开发的H5)里的方法

原理&#xff1a; 使用 getCurrentInstance() 获取当前组件的 Vue 实例&#xff0c;通过 instance.proxy.$scope.$getAppWebview() 获取 Uniapp 的原生 WebView 对象。 使用 WebView 提供的 evalJS 方法&#xff0c;执行嵌入 H5 页面内的 JavaScript 代码 <template>&l…...

Qt 5.14.2 学习记录 —— 이십 QFile和多线程

文章目录 1、QFile1、打开2、读写3、关闭4、程序5、其它功能 2、多线程1、演示2、锁 3、条件变量和信号量 1、QFile Qt有自己的一套文件体系&#xff0c;不过Qt也可以使用C&#xff0c;C&#xff0c;Linux的文件操作。使用Qt的文件体系和Qt自己的一些类型更好配合。 管理写入读…...

未初始化数据恢复全攻略

没有初始化概述 在日常使用电脑、硬盘、U盘等存储设备时&#xff0c;我们可能会遇到“没有初始化”的提示。这一情况通常发生在存储设备突然无法被系统正常识别或访问时&#xff0c;系统往往要求我们先进行初始化操作。然而&#xff0c;初始化操作意味着对存储设备进行格式化&…...

自动驾驶中的多传感器时间同步

目录 前言 1.多传感器时间特点 2.统一时钟源 2.1 时钟源 2.2 PPSGPRMC 2.3 PTP 2.4 全域架构时间同步方案 3.时间戳误差 3.1 硬件同步 3.2 软件同步 3.2.3 其他方式 ① ROS 中的 message_filters 包 ② 双端队列 std::deque 参考&#xff1a; 前言 对多传感器数据…...

三元组抽取在实际应用中如何处理语义模糊性?

在实际应用中&#xff0c;三元组抽取面临语义模糊性的问题&#xff0c;这主要体现在输入文本的非规范描述、复杂句式以及多义性等方面。为了有效处理这种模糊性&#xff0c;研究者们提出了多种方法和技术&#xff0c;以下是一些关键策略&#xff1a; 基于深度学习的方法 深度学…...

代码随想录刷题day16|(哈希表篇)349.两个数组的交集

目录 一、哈希表理论基础 二、集合set在哈希法中的应用 三、相关算法题目 四、相关知识点 1.set集合特点和常用方法 1.1 set集合概述 1.2 set集合特点 1.3 常用方法 2.set集合转换成数组 法1&#xff1a;另新建一个数组 法2&#xff1a;将结果集合转为数组 ▲ 3.数组…...

浅谈Redis

2007 年&#xff0c;一位程序员和朋友一起创建了一个网站。为了解决这个网站的负载问题&#xff0c;他自己定制了一个数据库。于2009 年开发&#xff0c;称之为Redis。这位意大利程序员是萨尔瓦托勒桑菲利波(Salvatore Sanfilippo)&#xff0c;他被称为Redis之父&#xff0c;更…...

整数的个数(信息学奥赛一本通-1067)

【题目描述】 给定k(1<k<100)个正整数&#xff0c;其中每个数都是大于等于1&#xff0c;小于等于10的数。写程序计算给定的k个正整数中&#xff0c;1&#xff0c;5和10出现的次数。 【输入】 输入有两行&#xff1a;第一行包含一个正整数k&#xff0c;第二行包含k个正整数…...

macos的图标过大,这是因为有自己的设计规范

苹果官方链接&#xff1a;App 图标 | Apple Developer Documentation 这个在官方文档里有说明&#xff0c;并且提供了sketch 和 ps 的模板。 figma还提供了模板&#xff1a; Figma...

C++17 命名空间的新特性:简化与优化的典范

文章目录 1. 简化的嵌套命名空间1.1 背景与问题1.2 C17的解决方案1.3 实际应用场景1.4 注意事项 2. 声明多个名称的using声明2.1 背景与问题2.2 C17的解决方案2.3 实际应用场景2.4 注意事项 3. 属性命名空间的简化3.1 背景与问题3.2 C17的解决方案3.3 实际应用场景3.4 注意事项…...

使用python-docx包进行多文件word文字、字符批量替换

1、首先下载pycharm。 2、改为中文。 3、安装python-docx包。 搜索包名字&#xff0c;安装。 4、新建py文件&#xff0c;写程序。 from docx import Documentdef replace1(array1):# 替换词典&#xff08;标签值按实际情况修改&#xff09;dic {替换词1: array1[0], 替换…...

模块初阶学习

当我们在过去想要实现一个功能时&#xff0c;例如Swap交换函数时&#xff0c;我们需要不断考虑参数的正确与否。如果是在c语言&#xff0c;我们还需要不断更改函数名字&#xff0c;以防止函数名重复。在c我们可以通过函数名重载解决这个问题&#xff0c;但还是有一些小问题&…...

华为 Ascend 平台 YOLOv5 目标检测推理教程

1. 背景介绍 随着人工智能技术的快速发展&#xff0c;目标检测在智能安防、自动驾驶、工业检测等领域中扮演了重要角色。YOLOv5 是一种高效的目标检测模型&#xff0c;凭借其速度和精度的平衡广受欢迎。 华为 Ascend 推理框架&#xff08;ACL&#xff09;是 Ascend CANN 软件…...

16.好数python解法——2024年省赛蓝桥杯真题

问题描述 一个整数如果按从低位到高位的顺序,奇数位(个位、百位、万位…)上的数字是奇数,偶数位(十位、千位、十万位…)上的数字是偶数,我们就称之为“好数”。 给定一个正整数N,请计算从1到N一共有多少个好数。 输入格式 一个整数N。 输出格式 一个整数代表答案。 样例输入 1 …...

在WSL使用gnome终端

默认在windows11环境下使用WSL会打开windows终端&#xff0c;如果想要使用gnome终端可以进行如下操作 确保 WSLg 已启用&#xff1a; WSLg 默认在 Windows 11 和最新版本的 WSL 2 中启用。 检查 WSL 版本&#xff1a; wsl --list --verbose 如果未启用 WSLg&#xff0c;请更…...

评估篇| 大模型评测综述

在传统的自然语言任务下,如分类等,经常会用精确率、F1等指标,来评测模型的好坏。随着大模型技术研究的快速发展,以往的指标,对于大模型评估显得过于单薄。如何准确地评估大语言模型在不同维度的能力水平,已经成为当前研究的热点问题。为了全面考察大语言模型的有效性,研…...

Ubuntu下载zenodo文件Ubuntu download zenodo

一般数据集文件会比较大&#xff0c;直接下载单个压缩包很慢。可以使用代码多线程下载小文件。 环境 Ubuntu22.04 示例代码 pip3 install zenodo_get zenodo_get https://zenodo.org/records/13715870参考 https://github.com/dvolgyes/zenodo_get...

OpenHarmony 5.0.2 Release来了!

版本概述 OpenHarmony 5.0.2 Release版本对标准系统的能力进行持续完善&#xff0c;以快速迭代的方式推出API 14&#xff0c;相比5.0.1 Release版本&#xff0c;重点做出了如下特性新增或增强&#xff1a; 进一步增强ArkUI、图形图像的能力&#xff0c;提供更多组件的高级属性…...

蓝桥杯3519 填充 | 分类讨论

题目传送门 很简单&#xff0c;遍历一次字符串&#xff0c;将‘?’作为0或1处理&#xff0c;发现00和11统计次数即可。 s str(input()) cnt 0 arr [00, 11, 0?, ?0, 1?, ?1, ??] i0 while i < len(s)-1:if s[i:(i2)] in arr:i 2cnt 1else:i 1 print(cnt)END✨...

均值(信息学奥赛一本通-1060)

【题目描述】 给出一组样本数据&#xff0c;包含n个浮点数&#xff0c;计算其均值&#xff0c;精确到小数点后4位。 【输入】 输入有两行&#xff0c;第一行包含一个整数n&#xff08;n小于100&#xff09;&#xff0c;代表样本容量&#xff1b;第二行包含n个绝对值不超过1000的…...

Windows Docker Desktop安装及使用 Docker 运行 MySQL

Docker Desktop是Docker的官方桌面版&#xff0c;专为Mac和Windows用户设计&#xff0c;提供了一个简单易用的界面来管理和运行Docker容器。它集成了Docker引擎&#xff0c;为开发人员提供了一个快速、可靠、可扩展的方式来构建、运行和管理应用。DockerDesktop的优势在于&…...

关于使用微服务的注意要点总结

一、防止过度设计 微服务的拆分一定要结合团队人员规模来考虑&#xff0c;笔者就曾遇到过一个公司的项目&#xff0c;是从外部采购回来的&#xff0c;微服务划分为十几个应用&#xff0c;我们在此项目基础上进行自行维护和扩展。由于公司业务规模不大&#xff0c;而且二次开发的…...

对于RocksDB和LSM Tree的一些理解

LSM Tree的读写过程 HBase、LevelDB&#xff0c;rocksDB&#xff08;是一个引擎&#xff09;底层的数据结构是LSM Tree适合写多读少的场景&#xff0c;都是追加写入内存中的MemTable&#xff0c;写入一条删除&#xff08;或修改&#xff09;标记&#xff0c;而不用去访问实际的…...

Pyecharts之特殊图表的独特展示

在数据可视化的世界里&#xff0c;除了常见的柱状图、折线图、饼图等&#xff0c;还有一些特殊的图表可以为我们带来独特的展示效果&#xff0c;帮助我们以更有趣、更直观的方式呈现数据。Pyecharts 为我们提供了多种特殊图表的绘制功能&#xff0c;本文将介绍象形图、水球图和…...