yolov5核查数据标注漏报和误报

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、误报
  • 二、漏报
  • 三、源码
  • 总结


前言

本文主要用于记录数据标注和模型预测之间的漏报和误报思想及其源码


提示:以下是本篇文章正文内容,下面案例可供参考

一、误报

我自己定义的误报是模型的预测结果框比人为标注的目标框多,也就是当标注人员标注图片的时候标注不仔细未能标注全的情况,逻辑是将在原始标注的xml文件当中添加误报-类别名称的框。
在这里插入图片描述

二、漏报

我自己定义的漏报是人为标注的框模型没有全部预测出来,也就是当标注人员标注图片的时候标注错误或者标注的框质量不合格的情况(跟模型性能也有关系),逻辑是将在原始标注的xml文件当中添加漏报-类别名称的框。
在这里插入图片描述

三、源码

import argparse
import os
import time
import shutil
import cv2
import numpy as np
import torch
from pathlib import Path
from pascal_voc_writer import Writer
import torchvision
from xml.etree import ElementTree
from xml.etree.ElementTree import Elementimport warnings
warnings.simplefilter(action='ignore', category=FutureWarning)FILE = Path(__file__).resolve()
ROOT = FILE.parents[0]def xywh2xyxy(x):# Convert nx4 boxes from [x, y, w, h] to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-righty = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)y[:, 0] = x[:, 0] - x[:, 2] / 2  # top left xy[:, 1] = x[:, 1] - x[:, 3] / 2  # top left yy[:, 2] = x[:, 0] + x[:, 2] / 2  # bottom right xy[:, 3] = x[:, 1] + x[:, 3] / 2  # bottom right yreturn ydef box_iou(box1, box2):def box_area(box):# box = 4xnreturn (box[2] - box[0]) * (box[3] - box[1])area1 = box_area(box1.T)area2 = box_area(box2.T)# inter(N,M) = (rb(N,M,2) - lt(N,M,2)).clamp(0).prod(2)inter = (torch.min(box1[:, None, 2:], box2[:, 2:]) - torch.max(box1[:, None, :2], box2[:, :2])).clamp(0).prod(2)return inter / (area1[:, None] + area2 - inter)  # iou = inter / (area1 + area2 - inter)def cv_imread(file_path):cv_img = cv2.imdecode(np.fromfile(file_path, dtype=np.uint8), cv2.IMREAD_UNCHANGED) #读取的为bgr图像return cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True, stride=32):# Resize and pad image while meeting stride-multiple constraintsshape = im.shape[:2]  # current shape [height, width]if isinstance(new_shape, int):new_shape = (new_shape, new_shape)# Scale ratio (new / old)r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])if not scaleup:  # only scale down, do not scale up (for better val mAP)r = min(r, 1.0)# Compute paddingratio = r, r  # width, height ratiosnew_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]  # wh paddingif auto:  # minimum rectangledw, dh = np.mod(dw, stride), np.mod(dh, stride)  # wh paddingelif scaleFill:  # stretchdw, dh = 0.0, 0.0new_unpad = (new_shape[1], new_shape[0])ratio = new_shape[1] / shape[1], new_shape[0] / shape[0]  # width, height ratiosdw /= 2  # divide padding into 2 sidesdh /= 2if shape[::-1] != new_unpad:  # resizeim = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR)top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))left, right = int(round(dw - 0.1)), int(round(dw + 0.1))im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)  # add borderreturn im, ratio, (dw, dh)def preprocess_file(path, img_size, stride, auto):img_rgb_ = cv_imread(path)  # RGBassert img_rgb_ is not None, f'Image Not Found {path}'# Padded resizeimg_rgb = letterbox(img_rgb_, img_size, stride=stride, auto=auto)[0]# Convertimg_rgb = img_rgb.transpose((2, 0, 1))  # HWC to CHWimg_rgb = np.ascontiguousarray(img_rgb)# 将一个内存不连续存储的数组转换为内存连续存储的数组,使得运行速度更快return img_rgb, img_rgb_def preprocess_mat(mat, img_size, stride, auto):img_bgr = mat  # BGR# Padded resizeimg_rgb = letterbox(img_bgr, img_size, stride=stride, auto=auto)[0]# Convertimg_rgb = img_rgb.transpose((2, 0, 1))[::-1]  # HWC to CHW, BGR to RGBimg_rgb = np.ascontiguousarray(img_rgb)return img_rgb, img_bgrdef clip_coords(boxes, shape):# Clip bounding xyxy bounding boxes to image shape (height, width)if isinstance(boxes, torch.Tensor):  # faster individuallyboxes[:, 0].clamp_(0, shape[1])  # x1boxes[:, 1].clamp_(0, shape[0])  # y1boxes[:, 2].clamp_(0, shape[1])  # x2boxes[:, 3].clamp_(0, shape[0])  # y2else:  # np.array (faster grouped)boxes[:, [0, 2]] = boxes[:, [0, 2]].clip(0, shape[1])  # x1, x2boxes[:, [1, 3]] = boxes[:, [1, 3]].clip(0, shape[0])  # y1, y2def scale_coords(img1_shape, coords, img0_shape, ratio_pad=None):# Rescale coords (xyxy) from img1_shape to img0_shapeif ratio_pad is None:  # calculate from img0_shapegain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1])  # gain  = old / newpad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2  # wh paddingelse:gain = ratio_pad[0][0]pad = ratio_pad[1]coords[:, [0, 2]] -= pad[0]  # x paddingcoords[:, [1, 3]] -= pad[1]  # y paddingcoords[:, :4] /= gainclip_coords(coords, img0_shape)return coordsdef remove_name_elements(element):name_element = element.find('name')if name_element is not None and name_element.text and name_element.text.startswith('\ufeff'):name_element.text = name_element.text.lstrip('\ufeff')for child in element:remove_name_elements(child)def read_xml(xml_file: str, names):if os.path.getsize(xml_file) == 0:return []with open(xml_file, encoding='utf-8-sig') as in_file:# if not in_file.readline():#     return []tree = ElementTree.parse(in_file)root = tree.getroot()remove_name_elements(root)results = []obj: Elementfor obj in tree.findall("object"):xml_box = obj.find("bndbox")x_min = float(xml_box.find("xmin").text)y_min = float(xml_box.find("ymin").text)x_max = float(xml_box.find("xmax").text)y_max = float(xml_box.find("ymax").text)b = [x_min, y_min, x_max, y_max]cls_id = names.index(obj.find("name").text)results.append([cls_id, b])return resultsdef non_max_suppression(prediction, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False, multi_label=False,labels=(), max_det=300):"""Runs Non-Maximum Suppression (NMS) on inference resultsReturns:list of detections, on (n,6) tensor per image [xyxy, conf, cls]"""nc = prediction.shape[2] - 5  # number of classesxc = prediction[..., 4] > conf_thres  # candidates# Checksassert 0 <= conf_thres <= 1, f'Invalid Confidence threshold {conf_thres}, valid values are between 0.0 and 1.0'assert 0 <= iou_thres <= 1, f'Invalid IoU {iou_thres}, valid values are between 0.0 and 1.0'# Settingsmin_wh, max_wh = 2, 7680  # (pixels) minimum and maximum box width and heightmax_nms = 30000  # maximum number of boxes into torchvision.ops.nms()time_limit = 10.0  # seconds to quit afterredundant = True  # require redundant detectionsmulti_label &= nc > 1  # multiple labels per box (adds 0.5ms/img)merge = False  # use merge-NMSt = time.time()output = [torch.zeros((0, 6), device=prediction.device)] * prediction.shape[0]for xi, x in enumerate(prediction):  # image index, image inference# Apply constraintsx[((x[..., 2:4] < min_wh) | (x[..., 2:4] > max_wh)).any(1), 4] = 0  # width-heightx = x[xc[xi]]  # confidence# Cat apriori labels if autolabellingif labels and len(labels[xi]):lb = labels[xi]v = torch.zeros((len(lb), nc + 5), device=x.device)v[:, :4] = lb[:, 1:5]  # boxv[:, 4] = 1.0  # confv[range(len(lb)), lb[:, 0].long() + 5] = 1.0  # clsx = torch.cat((x, v), 0)# If none remain process next imageif not x.shape[0]:continue# Compute confx[:, 5:] *= x[:, 4:5]  # conf = obj_conf * cls_conf# Box (center x, center y, width, height) to (x1, y1, x2, y2)box = xywh2xyxy(x[:, :4])# Detections matrix nx6 (xyxy, conf, cls)if multi_label:i, j = (x[:, 5:] > conf_thres).nonzero(as_tuple=False).Tx = torch.cat((box[i], x[i, j + 5, None], j[:, None].float()), 1)else:  # conf是置信度 j是类别conf, j = x[:, 5:].max(1, keepdim=True)x = torch.cat((box, conf, j.float()), 1)[conf.view(-1) > conf_thres]# Filter by classif classes is not None:x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)]# Apply finite constraint# if not torch.isfinite(x).all():#     x = x[torch.isfinite(x).all(1)]# Check shapen = x.shape[0]  # number of boxesif not n:  # no boxescontinueelif n > max_nms:  # excess boxesx = x[x[:, 4].argsort(descending=True)[:max_nms]]  # sort by confidence# Batched NMSc = x[:, 5:6] * (0 if agnostic else max_wh)  # classesboxes, scores = x[:, :4] + c, x[:, 4]  # boxes (offset by class), scoresi = torchvision.ops.nms(boxes, scores, iou_thres)  # NMSif i.shape[0] > max_det:  # limit detectionsi = i[:max_det]if merge and (1 < n < 3E3):  # Merge NMS (boxes merged using weighted mean)# update boxes as boxes(i,4) = weights(i,n) * boxes(n,4)iou = box_iou(boxes[i], boxes) > iou_thres  # iou matrixweights = iou * scores[None]  # box weightsx[i, :4] = torch.mm(weights, x[:, :4]).float() / weights.sum(1, keepdim=True)  # merged boxesif redundant:i = i[iou.sum(1) > 1]  # require redundancyoutput[xi] = x[i]if (time.time() - t) > time_limit:break  # time limit exceededend = time.time()# print(time.time() - t,'seconds')return outputclass Detect():def __init__(self, weights, imgsz, conf_thres, iou_thres):self.device = 'cpu'self.weights = weightsself.model = Noneself.imgsz = imgszself.conf_thres = conf_thresself.iou_thres = iou_thresif torch.cuda.is_available() and torch.cuda.device_count() > 1:self.device = torch.device('cuda:0')self.init_model()self.stride = max(int(self.model.stride.max()), 32)def init_model(self):ckpt = torch.load(self.weights, map_location=self.device)  # loadckpt = (ckpt.get('ema', None) or ckpt['model']).float()  # FP32 modelfuse = Trueself.model = ckpt.fuse().eval() if fuse else ckpt.eval()  # fused or un-fused model in eval mode fuse()将Conv和bn层进行合并,提高模型的推理速度self.model.float()def infer_image(self, image_path):im, im0 = preprocess_file(image_path, img_size=self.imgsz, stride=self.stride, auto=True)im = torch.from_numpy(im).to(self.device).float() / 255if len(im.shape) == 3:im = im[None]  # expand for batch dim# Inferencepred = self.model(im, augment=False, visualize=False)[0]# NMSpred = non_max_suppression(pred, self.conf_thres, self.iou_thres, None, False, max_det=1000)det = pred[0]results = []if len(det):# Rescale boxes from img_size to im0 sizedet[:, :4] = scale_coords(im.shape[2:], det[:, :4], im0.shape).round()# resultsfor *xyxy, conf, cls in reversed(det):xyxy = (torch.tensor(xyxy).view(1, 4)).view(-1).tolist()  # normalized xywhresults.append([cls.item(), xyxy, conf.item()])return resultsdef infer_mat(self, mat):im, im0 = preprocess_mat(mat, img_size=self.imgsz, stride=self.stride, auto=True)im = torch.from_numpy(im).to(self.device).float() / 255if len(im.shape) == 3:im = im[None]  # expand for batch dim# Inferencepred = self.model(im, augment=False, visualize=False)[0]# NMSpred = non_max_suppression(pred, self.conf_thres, self.iou_thres, None, False, max_det=1000)det = pred[0]results = []if len(det):# Rescale boxes from img_size to im0 sizedet[:, :4] = scale_coords(im.shape[2:], det[:, :4], im0.shape).round()# resultsfor *xyxy, conf, cls in reversed(det):xyxy = (torch.tensor(xyxy).view(1, 4)).view(-1).tolist()  # normalized xywhresults.append([cls.item(), xyxy, conf.item()])return resultsdef box_iou_np(box1, box2):x11, y11, x12, y12 = box1x21, y21, x22, y22 = box2width1 = np.maximum(0, x12 - x11)height1 = np.maximum(0, y12 - y11)width2 = np.maximum(0, x22 - x21)height2 = np.maximum(0, y22 - y21)area1 = width1 * height1area2 = width2 * height2# 计算交集,需要计算交集部分的左、上、右、下坐标xi1 = np.maximum(x11, x21)yi1 = np.maximum(y11, y21)xi2 = np.minimum(x12, x22)yi2 = np.minimum(y12, y22)# 计算交集部分面积w = np.maximum(0, xi2 - xi1)h = np.maximum(0, yi2 - yi1)intersection = w * h# 计算并集union = area1 + area2 - intersection# 计算iouiou = intersection / unionreturn ioudef main(opt):if not os.path.exists(opt.output_path):os.makedirs(opt.output_path, exist_ok=True)#oxist_ok表示如果目录存在,不要抛出异常,正常结束detect = Detect(opt.weights, opt.imgsz, opt.conf_thres, opt.iou_thres)imgs = []for root,dirs,files in os.walk(opt.input_path):for file in files:if os.path.splitext(file)[1] in opt.extensions:imgs.append(root+'/'+file)total = len(imgs)for i,img in enumerate(imgs):print(f"{i + 1 : >05d}/{total : >05d} {img}")mat = cv_imread(img)xml = os.path.splitext(img)[0]+'.xml'h,w,_ = mat.shaperesults = detect.infer_image(img)# 标注anns = []if os.path.exists(xml):anns = read_xml(xml, opt.names)else:anns = []# 核查误报fps = []if opt.fp:for result in results:result_cls, result_box, _ = resultif result_cls in opt.verifynames:finded = Falsefor ann in anns:ann_cls, ann_box = annif ann_cls == result_cls and box_iou_np(ann_box, result_box) > 0:finded = Truebreakif not finded:fps.append([result_cls, result_box])# 核查漏报fns = []if opt.fn:for ann in anns:ann_cls, ann_box = annif ann_cls in opt.verifynames:finded = Falsefor result in results:result_cls, result_box, _ = resultif ann_cls == result_cls and box_iou_np(ann_box, result_box) > 0:finded = Truebreakif not finded:fns.append([ann_cls, ann_box])if len(fps) == 0 and len(fns) == 0:continue# 写文件writer = Writer(img, w, h)# 写原始标注for ann in anns:ann_cls, ann_box = annx_min = ann_box[0]y_min = ann_box[1]x_max = ann_box[2]y_max = ann_box[3]writer.addObject(opt.names[int(ann_cls)], x_min, y_min, x_max, y_max)# 写误报if opt.fp:for ann in fps:ann_cls, ann_box = annx_min = ann_box[0]y_min = ann_box[1]x_max = ann_box[2]y_max = ann_box[3]writer.addObject("误报-" + opt.names[int(ann_cls)], x_min, y_min, x_max, y_max)# 写漏报if opt.fn:for ann in fns:ann_cls, ann_box = annx_min = ann_box[0]y_min = ann_box[1]x_max = ann_box[2]y_max = ann_box[3]writer.addObject("漏报-" + opt.names[int(ann_cls)], x_min, y_min, x_max, y_max)# 写文件writer.save(os.path.join(opt.output_path, os.path.basename(xml)))shutil.copy2(img, os.path.join(opt.output_path, os.path.basename(img)))def parse_opt(known):parser = argparse.ArgumentParser()parser.add_argument('--weights',type=str, default=ROOT / 'weights/best.pt', help='模型权重pt文件')parser.add_argument('--imgsz', type=tuple, default=(1280,1280), help='输入模型大小')parser.add_argument("--conf_thres", type=float, default=0.25, help="模型conf阈值")parser.add_argument('--iou_thres', type=float, default=0.5, help='标注与模型输出框的IOU阈值,用于判断误报和漏报')parser.add_argument('--names', type=list, default=["键盘", "显示器", "鼠标", "桌子", "椅子", "人"],help='核查的所有类别标注名称')parser.add_argument('--verifynames', type=list, default=[0,1], help='需要核查的类别')parser.add_argument('--input_path', type=str, default=r'', help='输入image和xml路径')parser.add_argument('--output_path', type=str, default=r''+'核查', help='输出image和xml路径')parser.add_argument('--extensions', type=list, default=['.jpg', '.JPG', '.jpeg', '.png', '.bmp', '.tiff', '.tif', '.svg', '.pfg'])parser.add_argument("--fp", type=bool, default=True, help="是否核查误报")parser.add_argument("--fn", type=bool, default=True, help="是否核查漏报")return parser.parse_known_args()[0] if known else parser.parse_args() #True 标志可以处理任何位置参数,不会因为位置参数崩溃,Fakse任何未知参数导致程序显示错误消息并退出if __name__ == '__main__':opt = parse_opt(True)main(opt)

总结

安装对应的库,修改命令行参数weights、names、verifynames、input_path和output_path即可使用。(注:将源码放置到yolov5对应的文件夹下方即可。)

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Word如何插入图片并移动到某个位置

Word如何插入图片并移动到某一个位置 新建word→插入→图片 选择合适的位置→选择图片→打开 点击图片→布局选项→选择文字环绕下的任意一个→固定在页面上 点击图片就可以将图片移动到任意位置

【prometheus】【blackbox_exporter】grafna导入blackbox_exporter看板配置

1、进入到grafana看板&#xff0c;选择合适的看板模版 地址&#xff1a;https://grafana.com/grafana/dashboards/ 在搜索框中输入 blackbox_exporter,找到合适的模版&#xff0c;如下图所示&#xff1a; 2、点击并下载对应看板JSON数据 3、在grafana的页面进行导入操作 3.1…

微服务面试题:分布式事务和服务监控

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

wx015基于springboot+vue+uniapp的经济新闻资讯的设计与实现

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…

获取用户详细信息-ThreadLocal优化

Thread全局接口可用&#xff0c;不用再重复编写。所以为了代码的复用&#xff0c;使用Thread。把之前的内容&#xff08;函数的参数和map与username&#xff09;注释掉&#xff0c;换为Thread传过来的内容&#xff08;map与username&#xff09;。 因为Thread需要在拦截器里面…

【论文阅读笔记】IceNet算法与代码 | 低照度图像增强 | IEEE | 2021.12.25

目录 1 导言 2 相关工作 A 传统方法 B 基于CNN的方法 C 交互方式 3 算法 A 交互对比度增强 1)Gamma estimation 2)颜色恢复 3)个性化初始η B 损失函数 1)交互式亮度控制损失 2)熵损失 3)平滑损失 4)总损失 C 实现细节 4 实验 5 IceNet环境配置和运行 1 下载…

git环境配置用户与秘钥

git环境配置用户与秘钥 git环境配置git配置用户名与邮箱git配置秘钥 git环境配置 已经安装git后环境配置 git配置用户名与邮箱 查看git版本 git -v查看git配置环境 git config --global --list第一次未配置时会报无法找到配置文件 全局配置git用户名 git config --glob…

logback日志框架源码分析

目录 (一)入口:slf4j选择日志框架 (二)日志框架初始化 (1)logback的3种配置方式 a、BasicConfigurator默认配置 b、SPI方式配置的Configurator实现类 c、通过配置文件初始化 (2)xml配置文件初始化 (三)Logger的创建 (四)打印日志 本文源码基于:logback版…

SpringBoot整合篇 05、Springboot整合Redission

文章目录 前言Redission详细配置步骤pom依赖application.yaml配置类CacheConfigEnvironmentContext RedissionController单测 前言 本篇博客是SpringBoot整合Redission&#xff0c;若文章中出现相关问题&#xff0c;请指出&#xff01; 所有博客文件目录索引&#xff1a;博客…

从0入门自主空中机器人-4-【PX4与Gazebo入门】

前言: 从上一篇的文章 从0入门自主空中机器人-3-【环境与常用软件安装】 | MGodmonkeyの世界 中我们的机载电脑已经安装了系统和常用的软件&#xff0c;这一篇文章中我们入门一下无人机常用的开源飞控PX4&#xff0c;以及ROS中无人机的仿真 1. PX4的安装 1.1 PX4固件代码的下载…

支持最新 mysql9的workbench8.0.39 中文汉化教程来了

之前在 B 站上发布了 mysql8 workbench 汉化教程&#xff0c;一年多来帮助很多初学者解决了不熟悉英文的烦恼。 汉化视频可以访问&#xff1a; 2024最新版mysql8.0.39中文版mysql workbench汉化 中文升级 旧版汉化报错解决_哔哩哔哩_bilibili MySql Workbench汉化_哔哩哔哩_…

RC充电电路仿真与分析

RC充电原理 下图是一个常见的RC充电电路&#xff1a;&#xff08;假设R10K&#xff0c;C100nF&#xff09; SW断开时&#xff0c;这个电路处于断路状态&#xff0c;C既没有充电也没有放电&#xff1b;SW闭合时&#xff0c;直流电源5V为电容C充电&#xff1b; 充电时电容两端…

(二)编译原生SDK以及配置交叉编译链

文章目录 编译原生SDKLinuxSDK的安装第一步解压LinuxSDK第二步安装依赖软件第三步解压Buildroot的dl文件 Linux系统镜像编译、生成第一步 配置编译环境第二步 编译 LinuxSDK编译上面配置好的 环境配置编译 LinuxSDK配置内核选项配置 Buildroot编译 Qt 库 编译生成 Linux 系统镜…

分布式算法(五):初识ZAB协议

文章目录 一、什么是Zookeeper二、ZAB与Zookeeper的关系为什么Zookeeper不直接使用Paxos 三、ZAB简介1.名词解释提案&#xff08;Proposal&#xff09;事务&#xff08;Transaction&#xff09;原子广播&#xff08;Atomic Broadcast&#xff09; 2.集群角色领导者&#xff08;…

RabbitMQ基础篇之Java客户端 Topic交换机

文章目录 Topic 交换机概述 Routing Key 与 Binding Key优缺点及场景优点缺点应用场景 案例演示创建队列和交换机&#xff1a;消费者代码&#xff1a;消息发送代码&#xff1a;测试&#xff1a; 总结 Topic 交换机概述 路由机制&#xff1a; Topic交换机与Direct交换机类似&am…

开源模型应用落地-LangChain实用小技巧-使用各种Loader高效解析不同数据源(七)

一、前言 在 LangChain框架中&#xff0c;提供了Loader机制&#xff0c;以统一的方式来从各种数据源获取数据&#xff0c;使得开发人员可以方便地集成不同类型的数据源&#xff0c;而无需为每种数据源编写特定的加载代码。它可以将不同格式的数据转换为 LangChain 可以处理的统…

SSRF服务端请求Gopher伪协议白盒测试

前言 是什么SSRF&#xff1f; 这个简单点说就是 服务端的请求伪造 就是这个如果是个 请求图片的网站 他的目的是请求外部其他网站的 图片 但是 SSRF指的是让他请求本地的图片 再展示出来 请求的是他的服务器上的图片 SSRF(Server-Side Request Forgery:服务器端请求伪造) …

数据可视化-16. 日历图

目录 1. 日历图的概念 2. 日历图的适用场景 2.1 事件或活动的频率分析 2.2 数据的时间周期性分析 2.3 异常值检测 2.4 绩效监控 3. 日历图的缺陷 3.1 粒度受限于天数 3.2 数据密度过高时候难以解读 3.3 难以比较多个数据集 3.4 周期性较长的数据不易展示 4. 日历图…

Flink源码解析之:如何根据JobGraph生成ExecutionGraph

Flink源码解析之&#xff1a;如何根据JobGraph生成ExecutionGraph 在上一篇Flink源码解析中&#xff0c;我们介绍了Flink如何根据StreamGraph生成JobGraph的流程&#xff0c;并着重分析了其算子链的合并过程和JobGraph的构造流程。 对于StreamGraph和JobGraph的生成来说&…

LeetCode算法题——有序数组的平方

题目描述 给你一个按非递减顺序排序的整数数组nums&#xff0c;返回每个数字的平方组成的新数组&#xff0c;要求也按非递减顺序排序。 题解 解法一&#xff1a;暴力解法 思路&#xff1a; 该题目可通过暴力解法解决&#xff0c;即利用for循环遍历数组&#xff0c;对数组每…