【学习笔记】深度学习入门:基于Python的理论与实现-神经网络

news2024/12/23 23:11:34

CONTENTS

    • 三、神经网络
      • 3.1 从感知机到神经网络
      • 3.2 Activation function
      • 3.3 多维数组的运算
      • 3.4 三层神经网络的实现
      • 3.5 输出层的设计
      • 3.6 手写数字识别

三、神经网络

3.1 从感知机到神经网络

用图来表示神经网络的话,如下图所示,我们把最左边的一列称为输入层,最右边的一列称为输出层,中间的一列称为中间层(隐藏层)

在这里插入图片描述

在上图的网络中,偏置 b b b并没有被画出来。如果要明确地表示出 b b b,可以像下图那样做。下图中添加了权重为 b b b的输入信号 1 1 1。这个感知机将 x 1 , x 2 , 1 x_1,x_2,1 x1,x2,1三个信号作为神经元的输入,将其和各自的权重相乘后,传送至下一个神经元。在下一个神经元中,计算这些加权信号的总和。如果这个总和超过 0 0 0,则输出 1 1 1,否则输出 0 0 0。另外,由于偏置的输入信号一直是 1 1 1,所以为了区别于其他神经元,我们在图中把这个神经元整个涂成灰色。

在这里插入图片描述

我们用一个函数来表示这种分情况的动作(超过 0 0 0则输出 1 1 1,否则输出 0 0 0),即 y = h ( b + w 1 x 1 + w 2 x 2 ) y=h(b+w_1x_1+w_2x_2) y=h(b+w1x1+w2x2),其中函数 h ( x ) h(x) h(x)如下式所示:

在这里插入图片描述

h ( x ) h(x) h(x)函数会将输入信号的总和转换为输出信号,这种函数一般称为激活函数。激活函数的作用在于决定如何来激活输入信号的总和。

可将以上式子细化为如下两个式子:

在这里插入图片描述

激活函数的计算过程如下图所示:

在这里插入图片描述

3.2 Activation function

神经网络中经常使用的一个激活函数就是下式表示的 s i g m o i d sigmoid sigmoid函数:

在这里插入图片描述

现在,我们先尝试画出阶跃函数的图像,当输入超过 0 0 0时,输出 1 1 1,否则输出 0 0 0。可以像下面这样简单地实现阶跃函数:

# 参数只能为实数
def step_function(x):
	if x > 0:
		return 1
	else:
		return 0

# 参数可以为NumPy数组
def step_function(x):
	y = x > 0
	return y.astype(np.int)

接着进行函数图像的绘制:

import numpy as np
import matplotlib.pylab as plt

def step_function(x):
	return np.array(x > 0, dtype=np.int)
x = np.arange(-5.0, 5.0, 0.1)
y = step_function(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1)  # 指定y轴的范围
plt.show()

结果如下图所示:

在这里插入图片描述

接下来我们实现 s i g m o i d sigmoid sigmoid函数:

def sigmoid(x):
	return 1 / (1 + np.exp(-x))

并绘制函数图像:

x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1)  # 指定y轴的范围
plt.show()

结果如下图所示:

在这里插入图片描述

s i g m o i d sigmoid sigmoid函数是一条平滑的曲线,输出随着输入发生连续性的变化。而阶跃函数以 0 0 0为界,输出发生急剧性的变化。 s i g m o i d sigmoid sigmoid函数的平滑性对神经网络的学习具有重要意义。

相对于阶跃函数只能返回 0 0 0 1 1 1 s i g m o i d sigmoid sigmoid函数可以返回$0.731\dots,0.880\dots $等实数(这一点和刚才的平滑性有关)。也就是说,感知机中神经元之间流动的是 0 0 0 1 1 1的二元信号,而神经网络中流动的是连续的实数值信号。

阶跃函数和 s i g m o i d sigmoid sigmoid函数虽然在平滑性上有差异,但是它们具有相似的形状。两者的结构均是“输入小时,输出接近 0 0 0(为 0 0 0);随着输入增大,输出向 1 1 1靠近(变成 1 1 1)”。也就是说,当输入信号为重要信息时,阶跃函数和 s i g m o i d sigmoid sigmoid函数都会输出较大的值;当输入信号为不重要的信息时,两者都输出较小的值。

阶跃函数和 s i g m o i d sigmoid sigmoid函数还有其他共同点,就是两者均为非线性函数。神经网络的激活函数必须使用非线性函数。换句话说,激活函数不能使用线性函数。为什么不能使用线性函数呢?因为使用线性函数的话,加深神经网络的层数就没有意义了。

接下来介绍另一个十分重要的激活函数: R e L U ReLU ReLU函数。 R e L U ReLU ReLU函数在输入大于 0 0 0时,直接输出该值;在输入小于等于 0 0 0时,输出 0 0 0,可以用下式表示:

在这里插入图片描述

其代码实现以及函数图像如下:

def relu(x):
	return np.maximum(0, x)

在这里插入图片描述

3.3 多维数组的运算

多维数组就是“数字的集合”,数字排成一列的集合、排成长方形的集合、排成三维状或者(更加一般化的) N N N维状的集合都称为多维数组。

A = np.array([1, 2, 3, 4])
np.ndim(A)  # 1,获得数组的维数
A.shape  # (4,)
A.shape[0]  # 4

B = np.array([[1, 2], [3, 4], [5, 6]])
np.ndim(B)  # 2
B.shape  # (3, 2)

下面,我们来介绍矩阵(二维数组)的乘积。比如 2 × 2 2\times 2 2×2的矩阵,其乘积可以像下图这样进行计算:

在这里插入图片描述

矩阵的乘积是通过左边矩阵的行(横向)和右边矩阵的列(纵向)以对应元素的方式相乘后再求和而得到的。这个运算在Python中可以用如下代码实现:

A = np.array([[1, 2], [3, 4]])
A.shape  # (2, 2)
B = np.array([[5, 6], [7, 8]])
B.shape  # (2, 2)
np.dot(A, B)  # array([[19, 22], [43, 50]]),dot()称为点积运算

需要注意的是,在多维数组的乘积运算中,必须使两个矩阵中的对应维度的元素个数一致,如下图所示:

在这里插入图片描述

下面我们使用NumPy矩阵来实现神经网络。这里我们以下图中的简单神经网络为对象。这个神经网络省略了偏置和激活函数,只有权重:

在这里插入图片描述

3.4 三层神经网络的实现

在介绍神经网络中的处理之前,我们先导入 w 12 ( 1 ) w_{12}^{(1)} w12(1) a 1 ( 1 ) a_{1}^{(1)} a1(1)等符号。请看下图,下图中只突出显示了从输入层神经元 x 2 x_2 x2到后一层的神经元 a 1 ( 1 ) a_{1}^{(1)} a1(1)的权重。权重和隐藏层的神经元的右上角有一个 “ ( 1 ) ” “(1)” (1),它表示权重和神经元的层号(即第 1 1 1层的权重、第 1 1 1层的神经元)。此外,权重的右下角有两个数字,它们是后一层的神经元和前一层的神经元的索引号。比如, w 12 ( 1 ) w_{12}^{(1)} w12(1)表示前一层的第 2 2 2个神经元 x 2 x_2 x2到后一层的第 1 1 1个神经元 a 1 ( 1 ) a_{1}^{(1)} a1(1)的权重。

在这里插入图片描述

现在看一下从输入层到第 1 1 1层的第 1 1 1个神经元的信号传递过程,如下图所示:

在这里插入图片描述

现在用数学式表示 a 1 ( 1 ) a_{1}^{(1)} a1(1) a 1 ( 1 ) a_{1}^{(1)} a1(1)通过加权信号和偏置的和按该公式进行计算: a 1 ( 1 ) = w 11 ( 1 ) x 1 + w 12 ( 1 ) x 2 + b 1 ( 1 ) a_{1}^{(1)}=w_{11}^{(1)}x_1+w_{12}^{(1)}x_2+b_{1}^{(1)} a1(1)=w11(1)x1+w12(1)x2+b1(1)

使用矩阵的乘法运算进行计算的过程如下:

在这里插入图片描述

下面我们用NumPy多维数组来实现上式,这里将输入信号、权重、偏置设置成任意值:

X = np.array([1.0, 0.5])
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
B1 = np.array([0.1, 0.2, 0.3])
print(W1.shape)  # (2, 3)
print(X.shape)  # (2,)
print(B1.shape) # (3,)
A1 = np.dot(X, W1) + B1

接下来,我们观察第 1 1 1层中激活函数的计算过程。如果把这个计算过程用图来表示的话,则如下图所示:

在这里插入图片描述

隐藏层的加权和(加权信号和偏置的总和)用 a a a表示,被激活函数转换后的信号用 z z z表示。此外,图中 h ( ) 4 表 示 激 活 函 数 , 这 里 我 们 使 用 的 是 h()4表示激活函数,这里我们使用的是 h()4使sigmoid$函数。用Python来实现,代码如下所示:

Z1 = sigmoid(A1)
print(A1)  # [0.3, 0.7, 1.1]
print(Z1)  # [0.57444252, 0.66818777, 0.75026011]

下面,我们来实现第 1 1 1层到第 2 2 2层的信号传递:

W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
B2 = np.array([0.1, 0.2])
print(Z1.shape) # (3,)
print(W2.shape) # (3, 2)
print(B2.shape) # (2,)
A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)

在这里插入图片描述

最后是第 2 2 2层到输出层的信号传递。输出层的实现也和之前的实现基本相同。不过,最后的激活函数和之前的隐藏层有所不同。这里我们定义了 i d e n t i t y _ f u n c t i o n ( ) identity\_function() identity_function()函数(也称为“恒等函数”),并将其作为输出层的激活函数。恒等函数会将输入按原样输出,因此,这个例子中没有必要特意定义 i d e n t i t y _ f u n c t i o n ( ) identity\_function() identity_function()。这里这样实现只是为了和之前的流程保持统一。

def identity_function(x):
	return x
W3 = np.array([[0.1, 0.3], [0.2, 0.4]])
B3 = np.array([0.1, 0.2])
A3 = np.dot(Z2, W3) + B3
Y = identity_function(A3)  # 或者Y = A3

在这里插入图片描述

至此,我们已经介绍完了 3 3 3层神经网络的实现。现在我们把之前的代码实现全部整理一下。这里,我们按照神经网络的实现惯例,只把权重记为大写字母 W 1 W1 W1,其他的(偏置或中间结果等)都用小写字母表示。

def init_network():
	network = {}
	network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
	network['b1'] = np.array([0.1, 0.2, 0.3])
	network['W2'] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
	network['b2'] = np.array([0.1, 0.2])
	network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])
	network['b3'] = np.array([0.1, 0.2])
	return network

def forward(network, x):
	W1, W2, W3 = network['W1'], network['W2'], network['W3']
	b1, b2, b3 = network['b1'], network['b2'], network['b3']
	a1 = np.dot(x, W1) + b1
	z1 = sigmoid(a1)
	a2 = np.dot(z1, W2) + b2
	z2 = sigmoid(a2)
	a3 = np.dot(z2, W3) + b3
	y = identity_function(a3)
	return y

network = init_network()
x = np.array([1.0, 0.5])
y = forward(network, x)
print(y)  # [ 0.31682708 0.69627909]

3.5 输出层的设计

神经网络可以用在分类问题和回归问题上,不过需要根据情况改变输出层的激活函数。一般而言,回归问题用恒等函数,分类问题用 s o f t m a x softmax softmax函数。

恒等函数会将输入按原样输出,对于输入的信息,不加以任何改动地直接输出。分类问题中使用的 s o f t m a x softmax softmax函数可以用下面的式子表示:

在这里插入图片描述

用Python实现:

def softmax(a):
	exp_a = np.exp(a)
	sum_exp_a = np.sum(exp_a)
	y = exp_a / sum_exp_a
	return y

观察代码,我们发现由于指数函数存在溢出问题,如果在这些超大值之间进行除法运算,结果会出现“不确定”的情况。因此我们需要对公式进行改进:

在这里插入图片描述

因此在进行 s o f t m a x softmax softmax的指数函数的运算时,加上(或者减去)某个常数并不会改变运算的结果。这里的 C ′ C' C可以使用任何值,但是为了防止溢出,一般会使用输入信号中的最大值。例如:

a = np.array([1010, 1000, 990])
np.exp(a) / np.sum(np.exp(a))  # softmax函数的运算
# 返回结果为array([nan, nan, nan]),没有被正确计算

c = np.max(a)  # 1010
a - c  # array([0, -10, -20])
np.exp(a - c) / np.sum(np.exp(a - c))
# 返回结果为array([9.99954600e-01, 4.53978686e-05, 2.06106005e-09])

综上,我们可以像下面这样实现 s o f t m a x softmax softmax函数:

def softmax(a):
	c = np.max(a)
	exp_a = np.exp(a - c)  # 溢出对策
	sum_exp_a = np.sum(exp_a)
	y = exp_a / sum_exp_a
	return y

s o f t m a x softmax softmax函数的输出是 0.0 0.0 0.0 1.0 1.0 1.0之间的实数。并且, s o f t m a x softmax softmax函数的输出值的总和是 1 1 1。输出总和为 1 1 1 s o f t m a x softmax softmax函数的一个重要性质。正因为有了这个性质,我们才可以把 s o f t m a x softmax softmax函数的输出解释为“概率”。

输出层的神经元数量需要根据待解决的问题来决定。对于分类问题,输出层的神经元数量一般设定为类别的数量。比如,对于某个输入图像,预测是图中的数字 0 0 0 9 9 9中的哪一个的问题( 10 10 10类别分类问题),可以像下图这样,将输出层的神经元设定为 10 10 10个(假定 y 2 y_2 y2的输出值最大)。

在这里插入图片描述

3.6 手写数字识别

假设神经网络的学习已经全部结束,我们使用学习到的参数,先实现神经网络的“推理处理”。这个推理处理也称为神经网络的前向传播

这里使用的数据集是MNIST手写数字图像集。MNIST是机器学习领域最有名的数据集之一,被应用于从简单的实验到发表的论文研究等各种场合。

MNIST的图像数据是 28 × 28 28\times 28 28×28像素的灰度图像( 1 1 1通道),各个像素的取值在 0 0 0 255 255 255之间。每个图像数据都相应地标有 “ 7 ” , “ 2 ” , “ 1 ” “7”,“2”,“1” 7,2,1等标签。

假设已经提供了便利的Python脚本mnist.py,该脚本支持从下载MNIST数据集到将这些数据转换成NumPy数组等处理,使用mnist.py中的 l o a d _ m n i s t ( ) load\_mnist() load_mnist()函数,就可以按下述方式轻松读入MNIST数据。

import sys, os
sys.path.append('D:\VS Code Project\Deep Learning')  # 为了导入父目录中的文件而进行的设定
from dataset.mnist import load_mnist

# 第一次调用会花费几分钟 ……
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)

# 输出各个数据的形状
print(x_train.shape)  # (60000, 784)
print(t_train.shape)  # (60000,)
print(x_test.shape)  # (10000, 784)
print(t_test.shape)  # (10000,)

load_mnist函数以(训练图像, 训练标签), (测试图像, 测试标签)的形式返回读入的MNIST数据。此外,还可以像load_mnist(normalize=True, flatten=True, one_hot_label=False)这样,设置 3 3 3个参数。第 1 1 1个参数normalize设置是否将输入图像正规化为 0.0 ∼ 1.0 0.0\sim 1.0 0.01.0的值。如果将该参数设置为False,则输入图像的像素会保持原来的 0 ∼ 255 0\sim 255 0255。第 2 2 2个参数flatten设置是否展开输入图像(变成一维数组)。如果将该参数设置为False,则输入图像为 1 × 28 × 28 1\times 28\times 28 1×28×28的三维数组;若设置为True,则输入图像会保存为由 784 784 784个元素构成的一维数组。第 3 3 3个参数one_hot_label设置是否将标签保存为 o n e − h o t one-hot onehot表示( o n e − h o t   r e p r e s e n t a t i o n one-hot\ representation onehot representation)。 o n e − h o t one-hot onehot表示是仅正确解标签为 1 1 1,其余皆为 0 0 0的数组,就像[0,0,1,0,0,0,0,0,0,0]这样。当one_hot_labelFalse时,只是像 7 , 2 7,2 7,2这样简单保存正确解标签;当one_hot_labelTrue时,标签则保存为 o n e − h o t one-hot onehot表示。

接下来使用PIL模块显示训练图像的第一张图像:

import sys, os
sys.path.append('D:\VS Code Project\Deep Learning')
import numpy as np
from dataset.mnist import load_mnist
from PIL import Image

def img_show(img):
	pil_img = Image.fromarray(np.uint8(img))
	pil_img.show()

(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)
img = x_train[0]
label = t_train[0]
print(label)  # 5
print(img.shape)  # (784,)
img = img.reshape(28, 28)  # 把图像的形状变成原来的尺寸
print(img.shape)  # (28, 28)
img_show(img)

显示结果如下图所示:

在这里插入图片描述

神经网络的输入层有 784 784 784个神经元,输出层有 10 10 10个神经元。输入层的 784 784 784这个数字来源于图像大小的 28 × 28 = 784 28\times 28=784 28×28=784,输出层的 10 10 10这个数字来源于 10 10 10类别分类(数字 0 ∼ 9 0\sim 9 09,共 10 10 10类别)。此外,这个神经网络有 2 2 2个隐藏层,第 1 1 1个隐藏层有 50 50 50个神经元,第 2 2 2个隐藏层有 100 100 100个神经元。这个 50 50 50 100 100 100可以设置为任何值。

下面我们先定义 3 3 3个函数:

def get_data():
	(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, flatten=True, one_hot_label=False)
	return x_test, t_test

def init_network():
	with open("sample_weight.pkl", 'rb') as f:
		network = pickle.load(f)
	return network

def predict(network, x):
	W1, W2, W3 = network['W1'], network['W2'], network['W3']
	b1, b2, b3 = network['b1'], network['b2'], network['b3']
	a1 = np.dot(x, W1) + b1
	z1 = sigmoid(a1)
	a2 = np.dot(z1, W2) + b2
	z2 = sigmoid(a2)
	a3 = np.dot(z2, W3) + b3
	y = softmax(a3)
	return y

init_network()会读入保存在pickle文件sample_weight.pkl中的学习到的权重参数 A A A。这个文件中以字典变量的形式保存了权重和偏置参数。剩余的 2 2 2个函数和前面介绍的代码实现基本相同,无需再解释。现在,我们用这 3 3 3个函数来实现神经网络的推理处理,并评价它的识别精度:

x, t = get_data()
network = init_network()
accuracy_cnt = 0
for i in range(len(x)):  # 逐一取出保存在x中的图像数据
	y = predict(network, x[i])
	p = np.argmax(y)  # 获取概率最高的元素的索引
	if p == t[i]:
		accuracy_cnt += 1
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))
# Accuracy:0.9352

下面我们使用Python解释器,输出刚才的神经网络的各层的权重的形状:

x, _ = get_data()
network = init_network()
W1, W2, W3 = network['W1'], network['W2'], network['W3']
x.shape  # (10000, 784)
x[0].shape  # (784,)
W1.shape  # (784, 50)
W2.shape  # (50, 100)
W3.shape  # (100, 10)

确认矩阵的形状:

在这里插入图片描述

现在我们来考虑打包输入多张图像的情形。比如,我们想用predict()函数一次性打包处理 100 100 100张图像。为此,可以把 x x x的形状改为 100 × 784 100\times 784 100×784,将 100 100 100张图像打包作为输入数据,这种打包式的输入数据称为 b a t c h batch batch)。批有“捆”的意思,图像就如同纸币一样扎成一捆,如下图所示:

在这里插入图片描述

下面我们进行基于批处理的代码实现:

x, t = get_data()
network = init_network()
batch_size = 100  # 批数量
accuracy_cnt = 0
for i in range(0, len(x), batch_size):
	x_batch = x[i:i + batch_size]
	y_batch = predict(network, x_batch)
	p = np.argmax(y_batch, axis=1)
	accuracy_cnt += np.sum(p == t[i:i + batch_size])
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))

下一节:【学习笔记】深度学习入门:基于Python的理论与实现-神经网络的学习。

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

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

相关文章

Open WebRTC Toolkit Native SDK Windows环境编译

1、首先按照编译webrtc原生代码环境,配置系统环境 https://chromium.googlesource.com/chromium/src//main/docs/windows_build_instructions.mdhttps://chromium.googlesource.com/chromium/src//main/docs/windows_build_instructions.md 安装openssl软件/s…

MODBUS协议下,能否实现MCGS触摸屏与FX5U之间无线通讯?

在工厂里,触摸屏往往位于程控室内,作为控制多个不同位置PLC的主站设备。因为触摸屏和plc所处位置距离较为分散,重新铺设电缆线工期长,成本高,故采用无线方式解决触摸屏与PLC之间的通讯问题。 一、方案概述 本方案是M…

年底了,准备跳槽的可以看看

前两天跟朋友感慨,今年的铜九铁十、裁员、疫情导致好多人都没拿到offer!现在已经12月了,具体明年的金三银四只剩下两个月。 对于想跳槽的职场人来说,绝对要从现在开始做准备了。这时候,很多高薪技术岗、管理岗的缺口和市场需求也…

【Linux03-基本工具之VIM】Linux下的强大编辑器(附软件生态与yum)

前言 本期分享6个Linux中常用的基本工具,以确保后续的学习能够进行。 零、软件生态与yum 抛出一个问题:软件的下载? 具体拆分 软件从哪里下?软件由谁提供?怎么下载? 软件,肯定不在本地&am…

RabbitMQ死信队列、延时队列

介绍: 消息被消费⽅否定确认,使⽤ channel.basicNack 或 channel.basicReject ,并且此时 requeue 属性被设置为 false 。消息在队列的存活时间超过设置的TTL时间。消息队列的消息数量已经超过最⼤队列⻓度。那么该消息将成为“死信”。“死信…

Espresso Sequencer:去中心化Rollups

1. 引言 前序博客有: HyperPlonk——实现zkEVM的一种zk-proof system Espresso Systems团队致力于为Web3世界开发工具和基础设施。 Espresso Sequencer:为在不牺牲扩展性和速度的情况下,实现的去中心化rollups系统,兼具Web2的性…

[附源码]计算机毕业设计springboot软考刷题小程序

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

MAC层协议总结

一、现存问题 广播网络存在问题 当信道的使用存在竞争时,如何分配信道的使用权----->如一个人停止讲话,另外可能两个或多个人同时讲话,当只有单个信道时,怎么决定下一个讲话的人?------>为了解决这个问题&#…

JavaCV音视频开发宝典:rtsp转推到rtp(非TS流方式),及使用TS流发送解决sdp缺失问题

《JavaCV音视频开发宝典》专栏目录导航 《JavaCV音视频开发宝典》专栏介绍和目录 前言 在之前的文章中,由于忘记介绍使用的rtp推流方式都是TS流方式,RTP方式推流没讲,本章作为之前文章(JavaCV音视频开发宝典:rtsp拉流并使用转码方式转推到rtp)的补充。 注意:本文不需要…

Spring MVC Formatter(数据格式化)详解

Spring MVC 框架的 Formatter 与 Converter 一样,也是一个可以将一种数据类型转换成另一种数据类型的接口。不同的是,Formatter 的源数据类型必须是 String 类型,而 Converter 的源数据类型是任意数据类型。 在 Web 应用中由 HTTP 发送的请求…

keychron矮轴无线机械键盘简直就是yyds

一、背景 日常生活中,我们都离不开键盘,游戏党打游戏要键盘, 工作人员无论使用台式电脑还是笔记本操作都离不开键盘,尤其程序员这一族,键盘简直就是敲代码的灵魂。随着经济和科技飞速发展,我们早已不在像从…

[附源码]Python计算机毕业设计Django电子相册管理系统

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,…

公共建筑节能大数据应用进展

3月26日|清华大学建筑节能学术周——公共建筑节能—工程实践助力实现双碳目标 【3月26日公开论坛】公共建筑节能 – 工程实践助力实现双碳目标 公共建筑节能大数据应用进展 建筑用能负荷受到气象条件、建筑围护结构、设备系统、人行为等多重因素耦合影响&#xff…

C语言 结构体

C语言 结构体一、结构体的声明和初始化1. 结构体声明2. 结构体初始化二、typedef 重定义结构体三、结构体成员的类型四、结构体成员的访问五、结构体传参六、结构体的自引用七、结构体的内存对齐对齐规则程序清单1程序清单2程序清单3程序清单4修改默认对齐数一、结构体的声明和…

nvcc编译器之设备和主机独立编译(chapter 6)

目录 6. CUDA中的独立编译 6.1 单独编译时的代码改动 6.2 nvcc独立编译选项 6.3 库 6.4 示例 6.5 分布编译优化 6.6 独立编译的潜在问题 6. CUDA中的独立编译 在5.0版本之前,CUDA不支持分开编译,因此CUDA代码不能访问跨文件(编译单元&…

挂耳式耳机品牌排行榜,五款目前排行靠前的耳机分享

耳机传声的方式无非就是空气传播以及骨骼传播,而骨传导耳机就属后者,通过骨骼震动来完成声波的传递,在传递的过程无需经过外耳道和鼓膜,在一定程度上缓解了对耳道造成的损伤,减少对于耳道的负担,看到这里如…

OSI参考模型个人总结

附:参考模型 OSI参考模型 基于国际标准化组织ISO的建议,作为各种层上使用的协议国际标准化的第一步发展起来的,被称作ISO开放系统互联参考模型(open system interconnection refertence model),简称为OSI模…

[附源码]Python计算机毕业设计Django高校后勤保障系统

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,…

【解决问题】413错误 413 Request Entity Too Large 接口返回413 报413nginx

文章目录问题排查解决方案1、修改nginx配置文件nginx.conf2、更新完成后需要重启nginx3、其他可能性博客背景:JAVA项目,前端想弄个便捷富文本,直接很多图片转base64编码直接存库了。字段为longtext类型。这种问题通常是在使用http请求对象太大…

python实现中缀表达式转后缀表达式

前缀、中缀、后缀表达式(逆波兰表达式) 前缀表达式称为波兰表达式,前缀表达式的运算符位于操作符之前 举例说明:(34)x 5 – 6 对应的前缀表达式就是- X 3 4 5 6 中缀表达式转为后缀表达式: …