CV - 目标检测
物体检测
目标检测和图片分类的区别:
图像分类(Image Classification)
目的:图像分类的目的是识别出图像中主要物体的类别。它试图回答“图像是什么?”的问题。
输出:通常输出是一个标签或一组概率值,表示图像属于各个预定义类别的可能性。例如,对于一张包含猫的图片,分类器可能会输出“猫”这个标签。
应用场景:适用于只需要了解图像整体内容的场景,如识别照片中的动物种类、区分不同的风景类型等。
目标检测(Object Detection)
目的:目标检测不仅需要识别图像中所有感兴趣物体的类别,还需要确定每个物体在图像中的具体位置。它试图回答“图像中有什么?它们在哪里?”的问题。
输出:除了给出物体的类别外,还会输出物体所在的边界框(bounding box),即用矩形框标记出每个物体的位置。例如,在自动驾驶场景下,系统不仅要能识别出行人、车辆等物体,还要精确地定位它们的位置以便做出安全决策。
应用场景:适合于需要知道图像内特定对象位置的情况,比如视频监控、自动驾驶汽车、医学影像分析等领域。
边缘框
- 一个边缘框可以通过 4 个数字定义
- (左上 x,左上 y,右下 x,右下 y)
- (左上 x,左上 y,宽,高)

目标检测数据集
- 每行表示一个物体
- 图片文件名,物体类别,边缘框
- COCO (cocodataset.org)
- 80 物体,330K 图片,1.5M 物体
总结
- 物体检测识别图片里的多个物体的类别和位置
- 位置通常用边缘框表示。
边缘框实现
%matplotlib inline
import torch
from d2l import torch as d2ld2l.set_figsize()
img = d2l.plt.imread('../img/catdog.jpg')
d2l.plt.imshow(img);

定义在这两种表示法之间进行转换的函数
#@save
def box_corner_to_center(boxes):"""从(左上,右下)转换到(中间,宽度,高度)"""x1, y1, x2, y2 = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]cx = (x1 + x2) / 2cy = (y1 + y2) / 2w = x2 - x1h = y2 - y1boxes = torch.stack((cx, cy, w, h), axis=-1)return boxes#@save
def box_center_to_corner(boxes):"""从(中间,宽度,高度)转换到(左上,右下)"""cx, cy, w, h = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]x1 = cx - 0.5 * wy1 = cy - 0.5 * hx2 = cx + 0.5 * wy2 = cy + 0.5 * hboxes = torch.stack((x1, y1, x2, y2), axis=-1)return boxes
定义图像中狗和猫的边界框
# bbox是边界框的英文缩写
dog_bbox, cat_bbox = [60.0, 45.0, 378.0, 516.0], [400.0, 112.0, 655.0, 493.0]boxes = torch.tensor((dog_bbox, cat_bbox))
box_center_to_corner(box_corner_to_center(boxes)) == boxes

将边界框在图中画出
#@save
def bbox_to_rect(bbox, color):# 将边界框(左上x,左上y,右下x,右下y)格式转换成matplotlib格式:# ((左上x,左上y),宽,高)return d2l.plt.Rectangle(xy=(bbox[0], bbox[1]), width=bbox[2]-bbox[0], height=bbox[3]-bbox[1],fill=False, edgecolor=color, linewidth=2)fig = d2l.plt.imshow(img)
fig.axes.add_patch(bbox_to_rect(dog_bbox, 'blue'))
fig.axes.add_patch(bbox_to_rect(cat_bbox, 'red'));

数据集
李沐老师收集并标记了一个小型数据集,下面首先是下载该数据集:
%matplotlib inline
import os
import pandas as pd
import torch
import torchvision
from d2l import torch as d2l#@save
d2l.DATA_HUB['banana-detection'] = (d2l.DATA_URL + 'banana-detection.zip','5de26c8fce5ccdea9f91267273464dc968d20d72')
读取香蕉检测数据集
#@save
def read_data_bananas(is_train=True):"""读取香蕉检测数据集中的图像和标签"""data_dir = d2l.download_extract('banana-detection')csv_fname = os.path.join(data_dir, 'bananas_train' if is_trainelse 'bananas_val', 'label.csv')csv_data = pd.read_csv(csv_fname)csv_data = csv_data.set_index('img_name')images, targets = [], []for img_name, target in csv_data.iterrows():images.append(torchvision.io.read_image(os.path.join(data_dir, 'bananas_train' if is_train else'bananas_val', 'images', f'{img_name}')))# 这里的target包含(类别,左上角x,左上角y,右下角x,右下角y),# 其中所有图像都具有相同的香蕉类(索引为0)targets.append(list(target))return images, torch.tensor(targets).unsqueeze(1) / 256
创建一个自定义 Dataseet 实例:
#@save
class BananasDataset(torch.utils.data.Dataset):"""一个用于加载香蕉检测数据集的自定义数据集"""def __init__(self, is_train):self.features, self.labels = read_data_bananas(is_train)print('read ' + str(len(self.features)) + (f' training examples' ifis_train else f' validation examples'))def __getitem__(self, idx):return (self.features[idx].float(), self.labels[idx])def __len__(self):return len(self.features)
为训练集和测试集返回两个数据加载器实例
#@save
def load_data_bananas(batch_size):"""加载香蕉检测数据集"""train_iter = torch.utils.data.DataLoader(BananasDataset(is_train=True),batch_size, shuffle=True)val_iter = torch.utils.data.DataLoader(BananasDataset(is_train=False),batch_size)return train_iter, val_iter
读取一个小批量,并打印其中的图像和标签的形状
batch_size, edge_size = 32, 256
train_iter, _ = load_data_bananas(batch_size)
batch = next(iter(train_iter))
batch[0].shape, batch[1].shape

演示:
imgs = (batch[0][0:10].permute(0, 2, 3, 1)) / 255
axes = d2l.show_images(imgs, 2, 5, scale=2)
for ax, label in zip(axes, batch[1][0:10]):d2l.show_bboxes(ax, [label[0][1:5] * edge_size], colors=['w'])

QA 思考
Q1:如果在工业检测中数据集非常小(近百张),除了进行数据增强外,还有什么更好的方法吗?
A1:迁移学习:找一个非常好的,在一个比较大的目标检测数据集上训练的比较好的模型,然后拿过来进行微调。近百张其实也不算小的了,只是说模型训练出来不是很好。数据增强的话,对图像进行处理,对应的框也必须要相应的变化一下,这是比较麻烦的点。
后记
自己实现了一遍,然后也是使用的李沐老师在课件中提供的狗猫图片:
import torch
from matplotlib import pyplot as plt
from PIL import Imagedef set_image_display_size():plt.figure(figsize=(8, 6))def load_image(img_path):return Image.open(img_path)def show_single_image(img):plt.imshow(img)plt.axis('on') # 显示坐标轴plt.show()def box_corner_to_center(boxes):x1, y1, x2, y2 = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]cx = (x1 + x2) / 2cy = (y1 + y2) / 2w = x2 - x1h = y2 - y1boxes = torch.stack((cx, cy, w, h), axis=-1)return boxesdef box_center_to_corner(boxes):# 这里向左和向上都是减小的cx, cy, w, h = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]x1 = cx - 0.5 * wy1 = cy - 0.5 * hx2 = cx + 0.5 * wy2 = cy + 0.5 * hboxes = torch.stack((x1, y1, x2, y2), axis=-1)return boxesdef bbox_to_rect(bbox, color):# 将边界框(左上x, 左上y, 右下x, 右下y)格式转换成matplotlib格式:# ((左上x, 左上y), 宽, 高)return plt.Rectangle(xy=(bbox[0], bbox[1]), width=bbox[2] - bbox[0], height=bbox[3] - bbox[1],fill=False, edgecolor=color, linewidth=2)if __name__ == "__main__":# 设置图像显示大小set_image_display_size()# 加载图像img_path = '../image/catdog.jpg' # 确保路径正确img = load_image(img_path)show_single_image(img)# dog, cat 边界框dog_bbox, cat_bbox = [60.0, 45.0, 378.0, 516.0], [400.0, 112.0, 655.0, 493.0]# 转换为张量并测试转换函数是否正确boxes = torch.tensor((dog_bbox, cat_bbox))converted_boxes = box_center_to_corner(box_corner_to_center(boxes))# torch.allclose,判断两个张量是否在一定容差范围内相等。print("转换是否一致:", torch.allclose(converted_boxes, boxes))# 显示图像并绘制边界框,imshow 将 img 显示在画布上fig = plt.imshow(img)# add_patch 是 matplotlib 中用于在坐标轴上添加图形元素(如矩形、圆形等)的函数fig.axes.add_patch(bbox_to_rect(dog_bbox, 'blue')) # 狗的边界框fig.axes.add_patch(bbox_to_rect(cat_bbox, 'red')) # 猫的边界框plt.show()
我在colab上跑了一下下面的代码,发现可以,下载不了的可能需要一点 “魔法”
import hashlib
import os
import tarfile
import zipfile
import pandas as pd
import requests
import torch
import torchvisionfrom matplotlib import pyplot as plt"""这段代码本身并没有包含训练过程。它只是加载了已经标注好的香蕉检测数据集,并可视化了图像及其对应的边界框。这些边界框信息是预先标注好的(存储在 CSV 文件中),而不是通过模型训练得到的。read_data_bananas 函数读取数据集中的图像和标签:图像存储在文件夹中(如 bananas_train/images/)。标签存储在 CSV 文件中(如 bananas_train/label.csv)。每个标签包括图像名称、目标类别(香蕉类别的索引为 0)、以及边界框坐标。zip 结构类似如下:banana-detection/├── bananas_train/│ ├── images/│ │ ├── image1.jpg│ │ ├── image2.jpg│ │ └── ...│ └── label.csv # 包含每张图像的标注信息(即边界框和类别)└── bananas_val/├── images/│ ├── image1.jpg│ ├── image2.jpg│ └── ...└── label.csv
"""
DATA_HUB = dict()
DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/'DATA_HUB['banana-detection'] = (DATA_URL + 'banana-detection.zip','5de26c8fce5ccdea9f91267273464dc968d20d72')def download(name, cache_dir=os.path.join('..', 'data')):"""下载一个DATA_HUB中的文件,返回本地文件名"""# assert 条件, 错误信息assert name in DATA_HUB, f"{name} 不存在于 {DATA_HUB}""""url:文件的下载地址。 http://d2l-data.s3-accelerate.amazonaws.com/banana-detection.zipsha1_hash:文件内容的 SHA-1 校验值,用于验证文件完整性。5de26c8fce5ccdea9f91267273464dc968d20d72"""url, sha1_hash = DATA_HUB[name]"""递归创建目录:包括所有必要的父目录。避免重复创建导致的错误:如果目录已经存在,不会抛出异常。"""os.makedirs(cache_dir, exist_ok=True)"""url.split('/')[-1]:提取 URL 路径的最后一部分(通常是文件名)。os.path.join(cache_dir, ...):将缓存目录和文件名组合成完整的本地文件路径。"""fname = os.path.join(cache_dir, url.split('/')[-1]) # fname = ../data/banana-detection.zipif os.path.exists(fname): # 文件存在,进入校验流程# 创建一个 SHA-1 哈希对象,用于计算文件的哈希值。sha1 = hashlib.sha1()# 以二进制模式读取文件with open(fname, 'rb') as f:while True:# 每次读取 1MB 数据(1048576 字节),更新哈希对象。data = f.read(1048576)# 为空,退出循环if not data:break# 将读取的数据块逐步添加到哈希计算中sha1.update(data)"""计算哈希值:调用 sha1.hexdigest() 获取最终的 SHA-1 哈希值(40 个字符的十六进制字符串)。校验哈希值:将计算得到的哈希值与预期的哈希值 sha1_hash 进行比较。如果两者相等,说明文件完整且未被篡改,直接返回文件路径(命中缓存)。否则,继续执行下载逻辑。"""if sha1.hexdigest() == sha1_hash:return fname # 命中缓存print(f'正在从{url}下载{fname}...')"""使用 requests.get 方法发送 HTTP 请求,从远程服务器下载文件。参数说明:stream=True:以流式方式下载文件,避免一次性加载整个文件到内存中。verify=True:启用 SSL 证书验证,确保安全连接。"""r = requests.get(url, stream=True, verify=True)"""打开本地文件 fname,以二进制写入模式('wb')创建或覆盖文件。将下载的内容 r.content 写入文件。下载完成后,关闭文件。"""with open(fname, 'wb') as f:f.write(r.content)return fnamedef download_extract(name, folder=None):"""下载并解压zip/tar文件"""fname = download(name) # 下载文件的完整路径"""os.path.dirname(fname):提取文件所在的目录路径(即父目录)。例如,如果 fname 是 '../data/example.zip',则 base_dir 是 '../data'。os.path.splitext(fname):将文件路径拆分为文件名和扩展名。例如,如果 fname 是 '../data/example.zip',则 data_dir 是 '../data/example',ext 是 '.zip'。"""base_dir = os.path.dirname(fname)data_dir, ext = os.path.splitext(fname)if ext == '.zip':fp = zipfile.ZipFile(fname, 'r')elif ext in ('.tar', '.gz'):fp = tarfile.open(fname, 'r')else:assert False, '只有zip/tar/gz 文件可以被解压缩'# 解压文件到指定的目录 base_dirfp.extractall(base_dir)"""如果 folder 参数存在:返回 os.path.join(base_dir, folder),即解压后目录下的指定子目录。如果 folder 参数不存在:返回 data_dir,即解压后的默认目录。"""return os.path.join(base_dir, folder) if folder else data_dirdef read_data_bananas(is_train=True):"""读取香蕉检测数据集中的图像和标签"""data_dir = download_extract('banana-detection')"""根据 is_train 参数,确定加载训练集还是验证集的标注文件:如果 is_train=True,加载 'bananas_train/label.csv'。如果 is_train=False,加载 'bananas_val/label.csv'。使用 pandas 库的 read_csv 方法读取 CSV 文件内容。将 img_name 列设置为索引列(方便后续按图像名称访问数据)。"""csv_fname = os.path.join(data_dir, 'bananas_train' if is_trainelse 'bananas_val', 'label.csv') # for example: ../data/banana-detection/banana_train/label.csvcsv_data = pd.read_csv(csv_fname) # 读取 csv 文件# 将 img_name 列设置为索引列csv_data = csv_data.set_index('img_name')"""images:用于存储读取的图像数据。targets:用于存储每张图像对应的标注信息。"""images, targets = [], []"""img_name:当前行的索引值(即图像名称)。target:当前行的数据(即标注信息)。"""for img_name, target in csv_data.iterrows():images.append(torchvision.io.read_image(os.path.join(data_dir, 'bananas_train' if is_train else'bananas_val', 'images', f'{img_name}')))# 这里的target包含(类别,左上角x,左上角y,右下角x,右下角y),# 其中所有图像都具有相同的香蕉类(索引为0)targets.append(list(target))"""images:图像数据列表。torch.tensor(targets).unsqueeze(1) / 256:将 targets 转换为 PyTorch 张量。使用 unsqueeze(1) 在维度 1 上增加一个维度,使其形状变为 (N, 1, 5),其中 N 是样本数量,5 是每个标注信息的长度。所有边界框坐标除以 256,进行归一化处理(假设图像大小为 256x256)。"""return images, torch.tensor(targets).unsqueeze(1) / 256class BananasDataset(torch.utils.data.Dataset):"""一个用于加载香蕉检测数据集的自定义数据集"""def __init__(self, is_train):self.features, self.labels = read_data_bananas(is_train)print('read ' + str(len(self.features)) + (f' training examples' ifis_train else f' validation examples'))# such as : read 1000 training examplesdef __getitem__(self, idx):return self.features[idx].float(), self.labels[idx]def __len__(self):return len(self.features)def load_data_bananas(batch_size):"""加载香蕉检测数据集"""train_iter = torch.utils.data.DataLoader(BananasDataset(is_train=True),batch_size, shuffle=True)val_iter = torch.utils.data.DataLoader(BananasDataset(is_train=False),batch_size)return train_iter, val_iterdef show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):figsize = (num_cols * scale, num_rows * scale)"""_:表示整个图形对象(这里用下划线忽略)axes:表示所有子图的数组"""_, axes = plt.subplots(num_rows, num_cols, figsize=figsize)axes = axes.flatten()for i, (ax, img) in enumerate(zip(axes, imgs)):if torch.is_tensor(img):ax.imshow(img.numpy())else:ax.imshow(img)ax.axes.get_xaxis().set_visible(False)ax.axes.get_yaxis().set_visible(False)if titles:ax.set_title(titles[i])return axesdef bbox_to_rect(bbox, color):"""将边界框(左上x,左上y,右下x,右下y)格式转换成matplotlib格式:((左上x,左上y),宽,高)"""return plt.Rectangle(xy=(bbox[0], bbox[1]), width=bbox[2] - bbox[0], height=bbox[3] - bbox[1],fill=False, edgecolor=color, linewidth=2)# 定义一个名为 numpy 的函数,它能够将 PyTorch 张量转换为 NumPy 数组,并且自动处理张量的梯度信息
numpy = lambda x, *args, **kwargs: x.detach().numpy(*args, **kwargs)def show_bboxes(axes, bboxes, labels=None, colors=None):"""显示所有边界框""""""功能:将输入对象转换为列表形式。如果 obj 是 None,返回默认值 default_values。如果 obj 不是列表或元组,则将其包装成单元素列表。否则,直接返回 obj。作用:确保 labels 和 colors 参数始终是列表形式,以便后续迭代操作。"""def _make_list(obj, default_values=None):if obj is None:obj = default_valueselif not isinstance(obj, (list, tuple)):obj = [obj]return obj"""将 labels 转换为列表形式。如果未提供标签,则返回空列表。将 colors 转换为列表形式。如果未提供颜色,则使用默认颜色列表 ['b', 'g', 'r', 'm', 'c'](蓝色、绿色、红色、洋红色、青色)。"""labels = _make_list(labels)colors = _make_list(colors, ['b', 'g', 'r', 'm', 'c'])"""遍历 bboxes 列表中的每个边界框 bbox。根据索引 i,从 colors 列表中循环选择颜色(防止颜色不足时重复使用)。调用 bbox_to_rect 函数,将边界框坐标转换为 Matplotlib 的矩形对象。假设 bbox_to_rect 是一个已定义的函数,用于将边界框坐标转换为 matplotlib.patches.Rectangle 对象。numpy(bbox):将边界框数据转换为 NumPy 数组(假设 bbox 是 PyTorch 张量)。使用 axes.add_patch(rect) 将矩形添加到绘图区域中。"""for i, bbox in enumerate(bboxes):color = colors[i % len(colors)]rect = bbox_to_rect(numpy(bbox), color)axes.add_patch(rect)"""检查是否提供了标签,并且当前索引 i 在标签范围内。根据边界框颜色选择标签文字的颜色:如果边界框颜色为白色('w'),标签文字颜色为黑色('k')。否则,标签文字颜色为白色('w')。使用 axes.text 方法,在边界框的左上角位置添加标签文本。rect.xy[0] 和 rect.xy[1]:矩形左上角的 x 和 y 坐标。va='center' 和 ha='center':设置文本垂直和水平居中对齐。fontsize=9:设置字体大小为 9。color=text_color:设置文本颜色。bbox=dict(facecolor=color, lw=0):为文本添加背景框,背景颜色与边界框一致,边框宽度为 0。"""if labels and len(labels) > i:text_color = 'k' if color == 'w' else 'w'axes.text(rect.xy[0], rect.xy[1], labels[i],va='center', ha='center', fontsize=9, color=text_color,bbox=dict(facecolor=color, lw=0))if __name__ == "__main__":batch_size, edge_size = 32, 256train_iter, _ = load_data_bananas(batch_size)"""使用 iter(train_iter) 创建一个迭代器对象。调用 next() 获取迭代器的第一个批次的数据。"""batch = next(iter(train_iter))"""torch.Size([32, 3, 256, 256]):表示 32 张 RGB 图像,每张图像大小为 256x256。torch.Size([32, num_boxes, 5]):表示每张图像有 num_boxes 个边界框,每个边界框包含 5 个值(类别 + 坐标)。 """print(batch[0].shape, batch[1].shape)"""功能:batch[0][0:10]:从 batch[0] 中提取前 10 张图像。.permute(0, 2, 3, 1):调整张量的维度顺序,将 (N, C, H, W) 转换为 (N, H, W, C),即从 PyTorch 的默认格式转换为适合显示的格式。/ 255:将像素值归一化到 [0, 1] 范围(假设原始像素值范围是 [0, 255])。结果:imgs 是一个形状为 (10, 256, 256, 3) 的张量,表示 10 张归一化的 RGB 图像。"""imgs = (batch[0][0:10].permute(0, 2, 3, 1)) / 255axes = show_images(imgs, 2, 5, scale=2)for ax, label in zip(axes, batch[1][0:10]):"""label[0][1:5]:提取第一个边界框的坐标信息([x_min, y_min, x_max, y_max])。* edge_size:将归一化的坐标值还原为原始像素坐标。show_bboxes(ax, ...):在子图 ax 上绘制边界框,颜色为白色 ('w')。"""show_bboxes(ax, [label[0][1:5] * edge_size], colors=['w'])plt.show()
"""
归一化是为了模型训练:确保输入数据在合理的范围内,加速收敛并提高稳定性。
还原是为了可视化:将数据转换回原始范围,以便人类可以直观地理解图像和标注信息。
"""

相关文章:
CV - 目标检测
物体检测 目标检测和图片分类的区别: 图像分类(Image Classification) 目的:图像分类的目的是识别出图像中主要物体的类别。它试图回答“图像是什么?”的问题。 输出:通常输出是一个标签或一组概率值&am…...
node-modules-inspector 可视化node_modules
1、node_modules 每个vue的项目都有很多的依赖,有的是dev的,有的是生产的。 2、使用命令pnpx node-modules-inspector pnpx node-modules-inspector 3、node_modules可视化 4、在线体验 Node Modules Inspector 5、github地址 https://github.com/a…...
远程服务器下载llama模型
适用于有防火墙不能直接从HF上下载的情况 然后,你可以克隆 Llama-3.1-8B-Instruct 模型: git clone https://你的用户名:你的访问令牌hf-mirror.com/meta-llama/Llama-3.1-8B-Instruct用户名,令牌来自huggingface官网 注意:要提…...
Spring 中的 @Autowired 和 @Resource
🧩 一、Autowired 和 Resource 的基本作用 注解来源作用AutowiredSpring 提供(org.springframework.beans.factory.annotation.Autowired)按类型 自动注入ResourceJDK 提供(javax.annotation.Resource)默认按名称 注入…...
自定义汇编语言(Custom Assembly Language) 和 Unix Git
1. 什么是自定义汇编语言(Custom Assembly Language)? 汇编语言(Assembly Language)是一种低级编程语言,它直接与 CPU 指令集(Instruction Set Architecture, ISA)对应,…...
Python菜鸟教程(小程序)
目录 一.简易计算器 二.学生成绩分级 三.密码设置 四.作业选择 点赞收藏,评论支持 一.简易计算器 print(-------使用的运算符-------\n) print(1.加号) print(2.减号) print(3.乘号) print(4.除号) Aint(input(请输入第一个数: )) Bint(input(请输入第二个数: )) Fi…...
2011-2019年各省地方财政金融监管支出数据
2011-2019年各省地方财政金融监管支出数据 1、时间:2007-2019年 2、来源:国家统计局、统计年鉴 3、指标:行政区划代码、地区、年份、地方财政金融监管支出 4、范围:31省 5、指标说明:地方财政在金融监管方面的支出…...
Java大厂面试题 -- JVM 优化进阶之路:从原理到实战的深度剖析(2)
最近佳作推荐: Java大厂面试题 – 深度揭秘 JVM 优化:六道面试题与行业巨头实战解析(1)(New) 开源架构与人工智能的融合:开启技术新纪元(New) 开源架构的自动化测试策略优…...
SQL LIKE 语句详解
SQL LIKE 语句详解 引言 SQL(Structured Query Language)是用于管理关系数据库的标准语言。在SQL中,LIKE 语句是一种常用的字符串匹配操作符,用于在查询中搜索包含特定模式的字符串。本文将详细介绍 LIKE 语句的用法、规则以及注意事项。 一、什么是 LIKE 语句? LIKE …...
现代Web应用中的时光机器:深入解析撤销/重做功能的艺术与科学
引言:数字世界的安全网 在现实世界中,我们拥有橡皮擦、撤销键和后悔药(比喻意义上)。数字世界同样需要这样的安全保障。研究表明: **85%**的用户会在完成复杂表单时犯至少一个错误 提供撤销功能的界面可将用户满意度…...
存储引擎 / 事务 / 索引
1. 存储引擎 MySQL 中特有的术语。 (Oracle 有,但不叫这个名字) 是一种表存储 / 组织数据的方式 不同的存储引擎,表存储数据的方式不同 1.1 查看存储引擎 命令: show engines \g(或大写:G…...
Go语言-初学者日记(八):构建、部署与 Docker 化
🧱 一、go build:最基础的构建方式 Go 的构建工具链是出了名的轻量、简洁,直接用 go build 就能把项目编译成二进制文件。 ✅ 构建当前项目 go build -o myapp-o myapp 指定输出文件名默认会构建当前目录下的 main.go 或 package main &a…...
【ESP32】ESP32物联网应用:MQTT控制与状态监测
ESP32物联网应用:MQTT控制与状态监测 引言 在物联网时代,远程监测和控制设备已经成为现实生活中常见的需求。本文将介绍如何使用ESP32微控制器配合MQTT协议,实现一个简单而强大的物联网应用:远程状态监测和设备控制。我们将以巴…...
RabbitMQ运维
RabbitMQ运维 一.集群1.简单介绍2.集群的作用 二.搭建集群1.多机多节点搭建步骤 2.单机单节点搭建步骤 3.宕机演示 三.仲裁队列1.简单介绍2.Raft协议Raft基本概念主节点选举选举过程 3.仲裁队列的使用 四.HAProxy负载均衡1.安装HAProxy2.HAProxy的使用 一.集群 1.简单介绍 Ra…...
Ansible 实战:Roles,运维的 “魔法函数”
一、介绍 你现在已经学过tasks和handlers,那么,最好的playbook组织方式是什么呢?答案很简单:使用roles!roles基于一种已知的文件结构,能够自动加载特定的vars_files、tasks以及handlers。通过roles对内容进…...
CVAT安装和使用(Windows)
必要组件安装 WSL2 Docker Git Chrome Install WSL2 (Windows subsystem for Linux) refer to this official guide. WSL2 requires Windows 10, version 2004 or higher. After installing WSL2, install a Linux Distribution of your choice. 安装 WSL2(适用…...
Spring 中的 bean 生命周期
🌱 一、什么是 Bean 生命周期? 在 Spring 容器中,一个 Bean 从“创建 → 初始化 → 使用 → 销毁”,经历了完整的生命周期。 Spring 提供了 多个扩展点 让你可以在这些阶段做事情,比如注入资源、日志记录、连接资源、清…...
关于JVM和OS中的指令重排以及JIT优化
关于JVM和OS中的指令重排以及JIT优化 前言: 这东西应该很重要才对,可是大多数博客都是以讹传讹,全是错误,尤其是JVM会对字节码进行重排都出来了,明明自己测一测就出来的东西,写出来误人子弟… 研究了两天&…...
微软推出首款量子计算芯片Majorana 1
全球首款拓扑架构量子芯片问世,2025年2月20日,经过近20年研究,微软推出了首款量子计算芯片Majorana 1,其宣传视频如本文末尾所示。 微软表示,开发Majorana 1需要创造一种全新的物质状态,即所谓的“拓扑体”…...
数组练习题总结
一、求出数组中的最大值let arr [1, 2, 5, 7];let max 0;for (i 0; i < arr.length; i) {// console.log(max);if (max < arr[i]) {max arr[i];}}console.log(max); 首先生成一个数组,设一个变量为保存最大值,写一个循环,在循环里写…...
Kotlin 中的 `reified` 关键字全解析:保留类型信息 + 优化高阶函数的双重魔法
在使用 Kotlin 编写泛型函数时,你是否遇到过这样的尴尬:你想判断某个对象是不是泛型类型 T,结果却发现代码根本编译不过?这是因为 Kotlin 和 Java 一样,泛型在运行时会被类型擦除。 为了解决这个问题,Kotl…...
在CPU服务器上部署Ollama和Dify的过程记录
在本指南中,我将详细介绍如何在CPU服务器上安装和配置Ollama模型服务和Dify平台,以及如何利用Docker实现这些服务的高效部署和迁移。本文分为三大部分:Ollama部署、Dify环境配置和Docker环境管理,适合需要在本地或私有环境中运行A…...
【计网】TCP 协议详解 与 常见面试题
三次握手、四次挥手的常见面试题 不用死记,只需要清楚三次握手,四次挥手的流程,回答的时候心里要记住,假设网络是不可靠的 问题(1):为什么关闭连接时需要四次挥手,而建立连接却只要三次握手? 关…...
Java 基础-31-枚举-认识枚举
在Java编程语言中,枚举(Enum)是一种特殊的类,它允许一组固定的常量。它们非常适合用来表示一组固定的值,比如星期几、季节、颜色等。枚举自Java 5开始引入,为定义常量提供了一种更强大和方便的方式。本文将…...
7.4 SVD 的几何背景
一、SVD 的几何解释 SVD 将矩阵分解成三个矩阵的乘积: ( 正交矩阵 ) ( 对角矩阵 ) ( 正交矩阵 ) (\pmb{正交矩阵})\times(\pmb{对角矩阵})(\pmb{正交矩阵}) (正交矩阵)(对角矩阵)(正交矩阵),用几何语言表述其几何背景: ( 旋转 ) ( 伸缩 )…...
低延迟云网络的核心技术
低延迟云网络通过架构优化、协议创新、硬件加速等多维度技术手段,将数据传输延迟降低至毫秒级甚至微秒级。 1. 网络架构优化 1.1 扁平化网络Leaf-Spine 架构 减少网络层级,缩短数据转发路径(如数据中心内部一跳可达)。 扁平化网络Leaf-Spine(叶子-脊椎)架构是一种现代…...
C++的多态-上
目录 多态的概念 多态的定义及实现 1.虚函数 2. 多态的实现 2.1.多态构成条件 2.2.虚函数重写的两个例外 (1)协变(基类与派生类虚函数返回值类型不同) (2)析构函数的重写(基类与派生类析构函数的名字不同) 2.3.多态的实现 2.4.多态在析构函数中的应用 2.5.多态构成条…...
内存与显存:从同根生到殊途异路的科技演进
在现代计算机的世界里,内存和显存是两个不可或缺的硬件组件。它们看似功能相近,却在发展历程中逐渐分道扬镳,各自服务于不同的计算需求。今天,我们将从一根内存条和一块显卡入手,深入探讨内存与显存的异同,…...
手搓多模态-04 归一化介绍
在机器学习中,归一化是一个非常重要的工具,它能帮助我们加速训练的速度。在我们前面的SiglipVisionTransformer 中,也有用到归一化层,如下代码所示: class SiglipVisionTransformer(nn.Module): ##视觉模型的第二层&am…...
【C++】第八节—string类(上)——详解+代码示例
hello,又见面了! 云边有个稻草人-CSDN博客 C_云边有个稻草人的博客-CSDN博客——C专栏(质量分高达97!) 菜鸟进化中。。。 目录 一、为什么要学习string类? 1.1 C语言中的字符串 1.2 面试题(暂不做讲解) …...
