16,8和4位浮点数是如何工作的

50年前Kernighan、Ritchie和他们的C语言书的第一版开始,人们就知道单精度“float”类型有32位大小,双精度类型有64位大小。还有一种具有扩展精度的80位“长双精度”类型,这些类型几乎涵盖了浮点数据处理的所有需求。但是在最近几年,尤其是今年LLM的兴起,为了减小模型的存储和内存占用,开发人员开始尽可能地缩小浮点类型。

在本文中,我们将介绍最流行的浮点格式,创建一个简单的神经网络,并了解它是如何工作的。

“标准”32位浮点数

我们先回顾一下标准格式。IEEE 754浮点运算标准由IEEE于1985年制定。32浮点型的典型数字是这样的:

第一个比特(bit)是一个符号,接下来的8个比特代表一个指数,最后一个比特代表尾数。最终值的计算公式为:

我们创建一个辅助函数以二进制形式打印浮点值:

 import structdef print_float32(val: float):""" Print Float32 in a binary form """m = struct.unpack('I', struct.pack('f', val))[0]return format(m, 'b').zfill(32)print_float32(0.15625)# > 00111110001000000000000000000000 

再创建一个逆向转换函数,这将在后面有用:

 def ieee_754_conversion(sign, exponent_raw, mantissa, exp_len=8, mant_len=23):""" Convert binary data into the floating point value """sign_mult = -1 if sign == 1 else 1exponent = exponent_raw - (2 ** (exp_len - 1) - 1)mant_mult = 1for b in range(mant_len - 1, -1, -1):if mantissa & (2 ** b):mant_mult += 1 / (2 ** (mant_len - b))return sign_mult * (2 ** exponent) * mant_multieee_754_conversion(0b0, 0b01111100, 0b01000000000000000000000)#> 0.15625

作为开发,你肯定知道浮点类型的准确性是有限的,比如这个:

 val = 3.14print(f"{val:.20f}")# > 3.14000000000000012434

在一般情况下,这不是一个大问题,但是我们拥有的比特位越少,得到的精度就越低。

16位浮点数

早期对这种格式的需求并不大,直到2008年才将16位浮点类型添加到IEEE 754标准中。它有一个符号位,5个指数位和10位尾数(分数):

他的转换逻辑与32位浮点数相同,但精度较低。以二进制形式打印一个16位浮点数:

 import numpy as npdef print_float16(val: float):""" Print Float16 in a binary form """m = struct.unpack('H', struct.pack('e', np.float16(val)))[0]return format(m, 'b').zfill(16)print_float16(3.14)# > 0100001001001000

使用之前使用的方法,我们可以进行反向转换:

 ieee_754_conversion(0, 0b10000, 0b1001001000, exp_len=5, mant_len=10)# > 3.140625

我们还可以找到Float16中可以表示的最大值:

 ieee_754_conversion(0, 0b11110, 0b1111111111, exp_len=5, mant_len=10)#> 65504.0

这里使用0b11110,是因为在IEEE 754标准中,0b11111是为“无穷大”保留的。同理还可以找到可能的最小值:

 ieee_754_conversion(0, 0b00001, 0b0000000000, exp_len=5, mant_len=10)#> 0.00006104

对于大多数开发人员来说,像这样的类型是一种“未知的领域”,因为c++中也没有标准的16位浮点类型。

16位" bfloat " (BFP16)

这种浮点格式是由谷歌团队开发的,它是专门为机器学习设计的(名字中的“B”也代表“大脑”)。该类型是对“标准”16位浮点数的修改:指数被扩大到8位,因此“bfloat16”的动态范围实际上与float-32相同。但尾数的大小被减少到7位:

让我们做一个和之前类似的计算:

 ieee_754_conversion(0, 0b10000000, 0b1001001, exp_len=8, mant_len=7)#> 3.140625

可以看到由于指数较大,bfloat16格式具有更宽的范围:

 ieee_754_conversion(0, 0b11111110, 0b1111111, exp_len=8, mant_len=7)#> 3.3895313892515355e+38

这比前面示例中的65504.0要好得多,但是正如前面提到的:因为尾数中的位数更少,所以bfloat16的精度更低,可以在Tensorflow中测试这两种类型:

 import tensorflow as tfprint(f"{tf.constant(1.2, dtype=tf.float16).numpy().item():.12f}")# > 1.200195312500print(f"{tf.constant(1.2, dtype=tf.bfloat16).numpy().item():.12f}")# > 1.203125000000

8位浮点(FP8)

这种(相对较新的)格式是在2022年提出的,它也是为机器学习而创建的——因为模型变得更大,将它们放入GPU内存是一个挑战。FP8格式有两种变体:E4M3(4位指数和3位尾数)和E5M2(5位指数和2位尾数)。

让我们来获取两种格式的最大可能值:

 ieee_754_conversion(0, 0b1111, 0b110, exp_len=4, mant_len=3)# > 448.0ieee_754_conversion(0, 0b11110, 0b11, exp_len=5, mant_len=2)# > 57344.0

也可以在Tensorflow中使用FP8:

 import tensorflow as tffrom tensorflow.python.framework import dtypesa_fp8 = tf.constant(3.14, dtype=dtypes.float8_e4m3fn)print(a_fp8)# > 3.25a_fp8 = tf.constant(3.14, dtype=dtypes.float8_e5m2)print(a_fp8)# > 3.0

让我们在这两种类型中画一个正弦波:

 import numpy as npimport tensorflow as tffrom tensorflow.python.framework import dtypesimport matplotlib.pyplot as pltlength = np.pi * 4resolution = 200xvals = np.arange(0, length, length / resolution)wave = np.sin(xvals)wave_fp8_1 = tf.cast(wave, dtypes.float8_e4m3fn)wave_fp8_2 = tf.cast(wave, dtypes.float8_e5m2)plt.rcParams["figure.figsize"] = (14, 5)plt.plot(xvals, wave_fp8_1.numpy())plt.plot(xvals, wave_fp8_2.numpy())plt.show()

可以看到,有一些差别,但是都还不错。

可以明显看到一些精度的损失,但这个图像看起来仍然像正弦波!

4位浮点类型

现在让我们来看看最“疯狂”的东西——4位浮点值!4位浮点数(FP4)是遵循IEEE标准的最小可能值,具有1位符号,2位指数和1位尾数:

第二种可能的4位实现是所谓的NormalFloat (NF4)数据类型。NF4值针对保存正态分布变量进行了优化。其他数据类型很难做到这一点,但所有可能的NF4值都可以很容易地打印在一个列表中:

 [-1.0, -0.6961928009986877, -0.5250730514526367, -0.39491748809814453, -0.28444138169288635, -0.18477343022823334, -0.09105003625154495, 0.0,0.07958029955625534, 0.16093020141124725, 0.24611230194568634, 0.33791524171829224, 0.44070982933044434, 0.5626170039176941, 0.7229568362236023, 1.0]

FP4和NF4类型都在bitsandbytes库中有相应的实现。作为一个例子,让我们将[1.0,2.0,3.0,4.0]数组转换为FP4:

 from bitsandbytes import functional as bfdef print_uint(val: int, n_digits=8) -> str:""" Convert 42 => '00101010' """return format(val, 'b').zfill(n_digits)device = torch.device("cuda")x = torch.tensor([1.0, 2.0, 3.0, 4.0], device=device)x_4bit, qstate = bf.quantize_fp4(x, blocksize=64)print(x_4bit)# > tensor([[117], [35]], dtype=torch.uint8)print_uint(x_4bit[0].item())# > 01110101print_uint(x_4bit[1].item())# > 00100011print(qstate)# > (tensor([4.]), # >  'fp4', # >  tensor([ 0.0000,  0.0052,  0.6667,  1.0000,  0.3333,  0.5000,  0.1667,  0.2500,# >           0.0000, -0.0052, -0.6667, -1.0000, -0.3333, -0.5000, -0.1667, -0.2500])])

作为输出,我们得到两个对象:一个16位数组[117,35],实际上包含我们的4个数字,和一个“状态”对象,包含缩放因子4.0和所有16个FP4数字的张量。

例如,第一个4位数字为“0111”(=7),在状态对象中我们可以看到对应的浮点值为0.25;0.25 4 = 1.0。第二个数字是“0101”(=5),结果是0.54 = 2.0。对于第三个数字,“0010”是2,0.666*4 = 2.666,接近但不等于3.0。对于4位值显然有了一些精度损失。对于最后一个值,“0011”是3,1000 *4 = 4.0。

逆向转换不需要手动操作bitsandbytes可以帮我们自动完成

 x = bf.dequantize_fp4(x_4bit, qstate)print(x)# > tensor([1.000, 2.000, 2.666, 4.000])

4位格式也有一个有限的动态范围。例如,数组[1.0,2.0,3.0,64.0]将被转换为[0.333,0.333,0.333,64.0]。但对于规范化的数据,还是可以接受的。作为一个例子,让我们画一个FP4格式的正弦波:

 import matplotlib.pyplot as pltimport numpy as npfrom bitsandbytes import functional as bflength = np.pi * 4resolution = 256xvals = np.arange(0, length, length / resolution)wave = np.sin(xvals)x_4bit, qstate = bf.quantize_fp4(torch.tensor(wave, dtype=torch.float32, device=device), blocksize=64)dq = bf.dequantize_fp4(x_4bit, qstate)plt.rcParams["figure.figsize"] = (14, 5)plt.title('FP8 Sine Wave')plt.plot(xvals, wave)plt.plot(xvals, dq.cpu().numpy())plt.show()

可以看到精度的损失:

特别说明,在写这篇文章的时候,4位类型NF4只适用于CUDA;目前还不支持CPU计算。

测试

作为本文的最后一步,我们创建一个神经网络模型并对其进行测试。使用transformers库,通过将load_in_4-bit参数设置为True,就可以以4位加载预训练模型。但这并不能让我们理解它是如何工作的。所以我们将创建一个小型神经网络,训练它并以4位精度使用它。

首先,让我们创建一个神经网络模型:

 import torchimport torch.nn as nnimport torch.optim as optimfrom typing import Anyclass NetNormal(nn.Module):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.model = nn.Sequential(nn.Linear(784, 128),nn.ReLU(),nn.Linear(128, 64),nn.ReLU(),nn.Linear(64, 10))def forward(self, x):x = self.flatten(x)x = self.model(x)return F.log_softmax(x, dim=1)

我们使用MNIST数据集,数据集分为6万张训练图像和1万张测试图像;可以使用参数train=True|False在DataLoader中指定选择。

 from torchvision import datasets, transformstrain_loader = torch.utils.data.DataLoader(datasets.MNIST("data", train=True, download=True,transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.1307,), (0.3081,))])),batch_size=batch_size, shuffle=True)test_loader = torch.utils.data.DataLoader(datasets.MNIST("data", train=False, transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.1307,), (0.3081,))])),batch_size=batch_size, shuffle=True)

训练过程以“正常”方式进行,使用默认精度:

 device = torch.device("cuda")batch_size = 64epochs = 4log_interval = 500def train(model: nn.Module, train_loader: torch.utils.data.DataLoader,optimizer: Any, epoch: int):""" Train the model """model.train()for batch_idx, (data, target) in enumerate(train_loader):data, target = data.to(device), target.to(device)optimizer.zero_grad()output = model(data)loss = F.nll_loss(output, target)loss.backward()optimizer.step()if batch_idx % log_interval == 0:print(f'Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)}]\tLoss: {loss.item():.5f}')def test(model: nn.Module, test_loader: torch.utils.data.DataLoader):""" Test the model """model.eval()test_loss = 0correct = 0with torch.no_grad():for data, target in test_loader:data, target = data.to(device), target.to(device)t_start = time.monotonic()output = model(data)test_loss += F.nll_loss(output, target, reduction='sum').item()pred = output.argmax(dim=1, keepdim=True)correct += pred.eq(target.view_as(pred)).sum().item()test_loss /= len(test_loader.dataset)t_diff = time.monotonic() - t_startprint(f"Test set: Average loss: {test_loss:.4f}, Accuracy: {correct}/{len(test_loader.dataset)} ({100. * correct / len(test_loader.dataset)}%)\n")def get_size_kb(model: nn.Module):""" Get model size in kilobytes """size_model = 0for param in model.parameters():if param.data.is_floating_point():size_model += param.numel() * torch.finfo(param.data.dtype).bitselse:size_model += param.numel() * torch.iinfo(param.data.dtype).bitsprint(f"Model size: {size_model / (8*1024)} KB")# Trainmodel = NetNormal().to(device)optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)for epoch in range(1, epochs + 1):train(model, train_loader, optimizer, epoch)test(model, test_loader)get_size(model)# Savetorch.save(model.state_dict(), "mnist_model.pt")

这里还有一个“get_size_kb”方法来获取以kb为单位的模型大小。

训练过程是这样的:

 Train Epoch: 1 [0/60000] Loss: 2.31558Train Epoch: 1 [32000/60000] Loss: 0.53704Test set: Average loss: 0.2684, Accuracy: 9225/10000 (92.25%)Train Epoch: 2 [0/60000] Loss: 0.19791Train Epoch: 2 [32000/60000] Loss: 0.17268Test set: Average loss: 0.1998, Accuracy: 9401/10000 (94.01%)Train Epoch: 3 [0/60000] Loss: 0.30570Train Epoch: 3 [32000/60000] Loss: 0.33042Test set: Average loss: 0.1614, Accuracy: 9530/10000 (95.3%)Train Epoch: 4 [0/60000] Loss: 0.20046Train Epoch: 4 [32000/60000] Loss: 0.19178Test set: Average loss: 0.1376, Accuracy: 9601/10000 (96.01%)Model size: 427.2890625 KB

我们的简单模型准确率达到96%,神经网络大小为427 KB。

下面我们将“Linear”层替换为“Linear8bitLt”。

 from bitsandbytes.nn import Linear8bitLtclass Net8Bit(nn.Module):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.model = nn.Sequential(Linear8bitLt(784, 128, has_fp16_weights=False),nn.ReLU(),Linear8bitLt(128, 64, has_fp16_weights=False),nn.ReLU(),Linear8bitLt(64, 10, has_fp16_weights=False))def forward(self, x):x = self.flatten(x)x = self.model(x)return F.log_softmax(x, dim=1)device = torch.device("cuda")# Loadmodel = Net8Bit()model.load_state_dict(torch.load("mnist_model.pt"))get_size_kb(model)print(model.model[0].weight)# Convertmodel = model.to(device)get_size_kb(model)print(model.model[0].weight)# Runtest(model, test_loader)

结果如下:

 Model size: 427.2890625 KBParameter(Int8Params([[ 0.0071,  0.0059,  0.0146,  ...,  0.0111, -0.0041,  0.0025],...,[-0.0131, -0.0093, -0.0016,  ..., -0.0156,  0.0042,  0.0296]]))Model size: 107.4140625 KBParameter(Int8Params([[  9,   7,  19,  ...,  14,  -5,   3],...,[-21, -15,  -3,  ..., -25,   7,  47]], device='cuda:0',dtype=torch.int8))Test set: Average loss: 0.1347, Accuracy: 9600/10000 (96.0%)

原始模型以标准浮点格式加载;它的大小是一样的,权重看起来像[0.0071,0.0059,…]。模型尺寸缩小了4倍。正如我们所看到的,权重值在相同的范围内,因此转换很容易-在测试运行期间,根本没有准确性损失!

再继续4bit的版本:

 from bitsandbytes.nn import LinearFP4, LinearNF4class Net4Bit(nn.Module):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.model = nn.Sequential(LinearFP4(784, 128),nn.ReLU(),LinearFP4(128, 64),nn.ReLU(),LinearFP4(64, 10))def forward(self, x):x = self.flatten(x)x = self.model(x)return F.log_softmax(x, dim=1)# Loadmodel = Net4Bit()model.load_state_dict(torch.load("mnist_model.pt"))get_model_size(model)print(model.model[2].weight)# Convertmodel = model.to(device)get_model_size(model)print(model.model[2].weight)# Runtest(model, test_loader)

输出如下所示:

 Model size: 427.2890625 KBParameter(Params4bit([[ 0.0916, -0.0453,  0.0891,  ...,  0.0430, -0.1094, -0.0751],...,[-0.0079, -0.1021, -0.0094,  ..., -0.0124,  0.0889,  0.0048]]))Model size: 54.1015625 KBParameter(Params4bit([[ 95], [ 81], [109],...,[ 34], [ 46], [ 33]], device='cuda:0', dtype=torch.uint8))Test set: Average loss: 0.1414, Accuracy: 9579/10000 (95.79%)

模型大小减少了8倍,从427 KB减少到54 KB,但准确率下降1%。这怎么可能?至少对这个模型来说,答案很简单:

  • 权重或多或少是均匀分布的,并且精度损失不是太大。
  • 神经网络使用Softmax作为输出,最大值的索引决定了实际结果。所以对于寻找最大索引,值本身并不重要。例如,当其他值为0.1或0.2时,该值为0.8或0.9没有任何区别!

我们从测试数据集加载数字并检查模型输出。

 dataset = datasets.MNIST('data', train=False, transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.1307,), (0.3081,))]))np.set_printoptions(precision=3, suppress=True)  # No scientific notationdata_in = dataset[4][0]for x in range(28):for y in range(28):print(f"{data_in[0][x][y]: .1f}", end=" ")print()

打印输出显示了我们想要预测的数字:

让我们看看“标准”模型将返回什么:

 # Suppress scientific notationnp.set_printoptions(precision=2, suppress=True)  # Predictwith torch.no_grad():output = model(data_in.to(device))print(output[0].cpu().numpy())ind = output.argmax(dim=1, keepdim=True)[0].cpu().item()print("Result:", ind)# > [ -8.27 -13.89  -6.89 -11.13  -0.03  -8.09  -7.46  -7.6   -6.43  -3.77]# > Result: 4

最大元素位于第5个位置(numpy数组中的元素从0开始编号),对应于数字“4”。

这是 8-bit:模型的输出:

 # > [ -9.09 -12.66  -8.42 -12.2   -0.01  -9.25  -8.29  -7.26  -8.36  -4.45]# > Result: 4

4-bit的如下:

 # > [ -8.56 -12.12  -7.52 -12.1   -0.01  -8.94  -7.84  -7.41  -7.31  -4.45]# > Result: 4

可以看到实际输出值略有不同,但最大索引是保持不变的。

总结

在本文中,我们测试了16位、8位和4位浮点数的不同方案,创建了一个神经网络,并能够以8位和4位精度运行它。通过将精度从标准浮点数降低到4位浮点数,内存占用减少了8倍,但精度损失最小。

就像我们昨天的文章中提到的,即使是4位也已经不是极限了;在GPTQ论文中,提到了将权重量化为2甚至3元(1.5比特!)。还有可以对不同层应用不同量化的ExLlamaV2。

https://avoid.overfit.cn/post/51c2993a2f824910b241199a52d2c994

作者:Dmitrii Eliuseev

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

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

相关文章

认识柔性数组

在C99中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员 限制条件是: 结构体中最后一个成员未知大小的数组 1.柔性数组的形式 那么我们怎样写一个柔性数组呢 typedef struct st_type {int i;int a[0];//柔性数组成员 }ty…

【Kafka专题】Kafka收发消息核心参数详解

目录 前置知识课程内容一、从基础的客户端说起(Java代码集成使用)1.1 消息发送者源码示例1.2 消息消费者源码示例1.3 客户端使用小总结 *二、从客户端属性来梳理客户端工作机制*2.1 消费者分组消费机制2.2 生产者拦截器机制2.3 消息序列化机制2.4 消息分…

开源白板工具 Excalidraw 架构解读

本文讲解开源白板工具 Excalidraw 的架构设计。 版本 0.16.1 技术栈 Vite React TypeScript Yarn Husky。 脚手架原来是用的是 Create React App,但这个脚手架已经不维护了,一年多没发布新版本了。 目前市面上比较流行的 React 脚手架是 Vite&…

协议栈——收发数据(拼接网络包,自动重发,滑动窗口机制)

目录 协议栈何时发送数据~ 数据长度 IP模块的分片功能 发送频率 网络包序号~利用syn拼接网络包ack确认网络包完整 确定偏移量 服务器ack确定收到数据总长度 序号作用 双端告知各自序号 协议栈自动重发机制 大致流程 ack等待时间如何调整 是…

色彩一致性自动处理方法在遥感图像中的应用

前言 在获取卫星遥感影像时,由于受不均匀的光照、不同的大气条件和不同的传感器设备等因素的影响,遥感影像中会存在局部亮度和色彩分布不均匀的现象,下面是在BigMap地图下载器中收集的几幅谷歌卫星影像,像下面这种都是拼接好的影像…

python对RabbitMQ的简单使用

原文链接:https://blog.csdn.net/weixin_43810267/article/details/123914324 RabbitMq 是实现了高级消息队列协议(AMQP)的开源消息代理中间件。消息队列是一种应用程序对应用程序的通行方式,应用程序通过写消息,将消…

图像处理与计算机视觉--第五章-图像分割-霍夫变换

文章目录 1.霍夫变换(Hough Transform)原理介绍2.霍夫变换(Hough Transform)算法流程3.霍夫变换(Hough Transform)算法代码4.霍夫变换(Hough Transform)算法效果 1.霍夫变换(Hough Transform)原理介绍 Hough Transform是一种常用的计算机视觉图形检验方法,霍夫变换一…

【vue3】wacth监听,监听ref定义的数据,监听reactive定义的数据,详解踩坑点

假期第二篇,对于基础的知识点,我感觉自己还是很薄弱的。 趁着假期,再去复习一遍 之前已经记录了一篇【vue3基础知识点-computed和watch】 今天在学习的过程中发现,之前记录的这一篇果然是很基础的,很多东西都讲的不够…

【Kafka专题】Kafka集群架构设计原理详解

目录 前言前置知识课程内容一、Kafka的Zookeeper元数据梳理1.1 zookeeper整体数据1.2 Controller Broker选举机制1.3 Leader Partition选举机制1.4 Leader Partition自动平衡机制*1.5 Partition故障恢复机制1.6 HW一致性保障-Epoch更新机制1.7 总结 学习总结感谢 前言 Kafka的…

数学建模Matlab之数据预处理方法

本文综合代码来自文章http://t.csdnimg.cn/P5zOD 异常值与缺失值处理 %% 数据修复 % 判断缺失值和异常值并修复,顺便光滑噪音,渡边笔记 clc,clear;close all; x 0:0.06:10; y sin(x)0.2*rand(size(x)); y(22:34) NaN; % 模拟缺失值 y(89:95) 50;% 模…

竞赛选题 机器视觉 opencv 深度学习 驾驶人脸疲劳检测系统 -python

文章目录 0 前言1 课题背景2 Dlib人脸识别2.1 简介2.2 Dlib优点2.3 相关代码2.4 人脸数据库2.5 人脸录入加识别效果 3 疲劳检测算法3.1 眼睛检测算法3.2 打哈欠检测算法3.3 点头检测算法 4 PyQt54.1 简介4.2相关界面代码 5 最后 0 前言 🔥 优质竞赛项目系列&#x…

【Java 进阶篇】JDBC PreparedStatement 详解

在Java中,与关系型数据库进行交互是非常常见的任务之一。JDBC(Java Database Connectivity)是Java平台的一个标准API,用于连接和操作各种关系型数据库。其中,PreparedStatement 是 JDBC 中一个重要的接口,用…

RAID知识点总结

目录 RAID类型 RAID的数据组织及存取方式 RAID热备与重构 RAID逻辑卷 常见的RAID RAID0 RAID 1 RAID3 RAID 5 RAID 6 RAID组合 RAID 10 RAID 50 总结 RAID技术对比 RAID的应用场景 RAID2.0 使用RAID2.0的原因 RAID2.0的发展 RAID2.0技术:两层虚拟…

K8s架构简述

以部署一个nginx服务说明kubernetes系统各个组件调用关系: 一旦kubernetes环境启动之后,master和node都会将自身的信息存储到etcd数据库中 一个nginx服务的安装请求会首先被发送到master节点的apiServer组件 apiServer组件会调用scheduler组件来决定到底…

【强化学习】05 —— 基于无模型的强化学习(Prediction)

文章目录 简介蒙特卡洛算法时序差分方法Example1 MC和TD的对比偏差(Bias)/方差(Variance)的权衡Example2 Random WalkExample3 AB 反向传播(backup)Monte-Carlo BackupTemporal-Difference BackupDynamic Programming Backup Boot…

请求转发与请求作用域

创建input.jsp页面,通过表单输入学号、姓名后,单击登录按钮,控制转发到FirstServlet对其进行处理,然后通过请求对象的getRequestDispartcher()获得RequestDispartcher对象,将请求转发至SecondServlet,在Sec…

SpringBoot 可以同时处理多少请求

一、前言 首先,在Spring Boot应用中,我们可以使用 Tomcat、Jetty、Undertow 等嵌入式 Web 服务器作为应用程序的运行容器。这些服务器都支持并发请求处理的能力。另外,Spring Boot 还提供了一些配置参数,可以对 Web 服务器进行调…

北大硕士7年嵌入式学习经验分享

阶段 1 大一到大三这个阶段我与大多数学生相同: 学习本专业知识(EE专业),学习嵌入式软件开发需要的计算机课程(汇编原理,计算机组成原理,操作系统,C语言等)&#xff0c…

常见web信息泄露

一、源码(备份文件)泄露 1、git泄露 Git是一个开源的分布式版本控制系统,在执行git init初始化目录的时候,会在当前目录下自动创建一个.git目录,用来记录代码的变更记录等。发布代码的时候,如果没有把.git这个目录删除&#xff…

SpringBoot 中使用JPA

最近忙里偷闲,想写一点关于JPA的东西,另外也加深下对JPA的理解,才有了此篇博文。 一、JPA JPA (Java Persistence API)Java持久化API,是一套Sun公司Java官方制定的ORM 规范(sun公司并没有实现…