当前位置: 首页 > 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都可以完成二分类的任务。在神经网络的二分类中, σ \sigma σ的默认取值一般都是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;从右…...

一文读懂 HTTP:Web 数据交换的基石

HTTP 概述 HTTP 是一种用作获取诸如 HTML 文档这类资源的协议。它是 Web 上进行任何数据交换的基础&#xff0c;同时&#xff0c;也是一种客户端—服务器&#xff08;client-server&#xff09;协议&#xff0c;也就是说&#xff0c;请求是由接受方——通常是 Web 浏览器——发…...

算法知识补充2

一部分&#xff1a;Tire树&#xff1a;高效地存储和查找字符串集合的数据结构acwing835 #include<iostream> #include<cstring> using namespace std; const int N100010; int son[N][26],cnt[N],idx; char str[N]; void insert(char str[]){int p0;for(int i0;st…...

Vue.js组件开发-实现对视频预览

在 Vue 中实现视频文件预览 实现步骤 创建 Vue 组件&#xff1a;构建一个 Vue 组件用于处理视频文件的选择和预览。文件选择&#xff1a;添加一个文件输入框&#xff0c;允许用户选择视频文件。读取文件&#xff1a;监听文件选择事件&#xff0c;使用 FileReader API 读取所选…...

SSM开发(三) spring与mybatis整合(含完整运行demo源码)

目录 本文主要内容 一、Spring整合MyBatis的三个关键点 二、整合步骤 1、创建一个Maven项目 2、在pom.xml文件中添加jar包的依赖 3、配置MyBatis 注解实现方式 XML配置文件实现 4、配置Spring 5、测试运行 本文主要内容 1. Spring + Mybatis整合; 2. MyBatis两种SQL…...

.NET MAUI进行UDP通信(二)

上篇文章有写过一个简单的demo&#xff0c;本次对项目进行进一步的扩展&#xff0c;添加tabbar功能。 1.修改AppShell.xaml文件&#xff0c;如下所示&#xff1a; <?xml version"1.0" encoding"UTF-8" ?> <Shellx:Class"mauiDemo.AppShel…...

14-6-3C++STL的list

&#xff08;一&#xff09;list的插入 1.list.insert(pos,elem);//在pos位置插入一个elem元素的拷贝&#xff0c;返回新数据的位置 #include <iostream> #include <list> using namespace std; int main() { list<int> lst; lst.push_back(10); l…...

AAAI2024论文解读|HGPROMPT Bridging Homogeneous and Heterogeneous Graphs

论文标题 HGPROMPT: Bridging Homogeneous and Heterogeneous Graphs for Few-shot Prompt Learning 跨同构异构图的小样本提示学习 论文链接 HGPROMPT: Bridging Homogeneous and Heterogeneous Graphs for Few-shot Prompt Learning论文下载 论文作者 Xingtong Yu, Yuan…...

WPA_cli P2P命令详解及使用

目录 通用命令 status scan scan_results add_network set_network enable_network reconfigure save_config quit P2P 相关命令 p2p_find p2p_peers p2p_connect [method] p2p_group_add [ssid=] [freq=] [ht40] [persistent] p2p_remove_client p2p_di…...

【竞技宝】LPL:IG3-1击败RNG

北京时间1月26日&#xff0c;英雄联盟LPL2025正在如火如荼的进行之中&#xff0c;昨日共进行两场比赛。第二场比赛由RNG对阵IG。本场比赛&#xff0c;RNG在首局前期打出完美节奏后一直压制着IG拿下比赛&#xff0c;但此后的三局&#xff0c;IG发挥出自己擅长大乱斗的能力在团战…...

sqlite3 学习笔记

文章目录 前言SQL的概念与表格相关的操作i.创建表格&#xff08;增&#xff09;ii 删除表格&#xff08;删&#xff09;iii 更改表格&#xff08;改&#xff09;iv 查询表格&#xff08;查&#xff09; 与记录相关的操作i 插入记录ii 删除记录iii 查询记录iv 修改记录 Linux中使…...

Visual Studio Community 2022(VS2022)安装方法

废话不多说直接上图&#xff1a; 直接上步骤&#xff1a; 1&#xff0c;首先可以下载安装一个Visual Studio安装器&#xff0c;叫做Visual Studio installer。这个安装文件很小&#xff0c;很快就安装完成了。 2&#xff0c;打开Visual Studio installer 小软件 3&#xff0c…...

项目集成RabbitMQ

文章目录 1.common-rabbitmq-starter1.创建common-rabbitmq-starter2.pom.xml3.自动配置1.RabbitMQAutoConfiguration.java2.spring.factories 2.测试使用1.创建common-rabbitmq-starter-demo2.目录结构3.pom.xml4.application.yml5.TestConfig.java 配置交换机和队列6.TestCon…...

3097. 或值至少为 K 的最短子数组 II

3097. 或值至少为 K 的最短子数组 II 题目链接&#xff1a;3097. 或值至少为 K 的最短子数组 II 代码如下&#xff1a; class Solution { public:int minimumSubarrayLength(vector<int>& nums, int k) {int res INT_MAX;for (int i 0;i < nums.size();i) {in…...

Linux 35.6 + JetPack v5.1.4之编译器升级

Linux 35.6 JetPack v5.1.4之编译器升级 1. 源由2. 步骤步骤一&#xff1a;添加编译器源步骤二&#xff1a;安装gcc/g 11/13步骤三&#xff1a;确认安装版本步骤四&#xff1a;配置gcc/g版本步骤五&#xff1a;使能gcc/g版本步骤六&#xff1a;查看使能链接关系步骤七&#xf…...

[MoeCTF 2022]ezhtml

题目 查看页面源代码 有个/evil.js文件打开查看 看到了flag NSSCTF{e15f7f51-d1a0-4d1b-a96d-c987a4fe69a0} 到这里也就可以直接结束了 // 获取元素节点 var sx document.querySelector(#sx); // 获取 id 为 sx 的元素节点 var yw document.querySelector(#yw); // 获取…...

活动回顾和预告|微软开发者社区 Code Without Barriers 上海站首场活动成功举办!

Code Without Barriers 上海活动回顾 Code Without Barriers&#xff1a;AI & DATA 深入探索人工智能与数据如何变革行业 2025年1月16日&#xff0c;微软开发者社区 Code Without Barriers &#xff08;CWB&#xff09;携手 She Rewires 她原力在大中华区的首场活动“AI &…...

使用 Redis List 和 Pub/Sub 实现简单的消息队列

使用 Redis List 和 Pub/Sub 实现简单的消息队列 Redis 本身不是专门的消息队列系统&#xff0c;但它提供了多种数据结构&#xff08;如 List、Pub/Sub、Stream&#xff09;来实现消息队列功能。根据不同的业务需求&#xff0c;可以选择不同的方式&#xff1a; 在 Redis 中&a…...

本地项目上传到码云

本地项目上传到码云 写在前面1. 系统安装git环境2. 创建仓库3. 开始上传3.1 创建新的远程仓库3.2 在项目的文件夹用git打开3.3 删除本地的 .git 目录3.4 初始化新的 Git 仓库3.5 添加远程仓库3.6 添加项目文件3.7 提交更改3.8 推送到远程仓库3.9 验证 4. 完整的步骤总结写在最后…...

Ansible入门学习之基础元素介绍

一、Ansible目录结构介绍 1.通过rpm -ql ansible获取ansible所有文件存放的目录 有配置文件目录 /etc/ansible/ 执行文件目录 /usr/bin/ 其中 /etc/ansible/ 该文件目录的主要功能是 inventory主机信息配置&#xff0c;ansible工具功能配置。 ansible自身的配置文件…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

多模态图像修复系统:基于深度学习的图片修复实现

多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...