当前位置: 首页 > news >正文

标量化rknn的输入输出向量转换处理

这是一篇技术探索。yolo11模型生成后,我发现它无法在rknn环境正确识别出目标对象。而在宿主机上,或者直接调用.pt转换过的.onnx模型是可以得到正确结果的。这篇文章对应近乎一天的工作。最终的结论就是。这是一个模型量化的问题,与yolo的版本关系不大。

我现在已经能够让float32格式,未量化的.rknn模型在rk3588上正确执行,并得到正确的结果。量化后,首先是识别效果下降,然后是识别框的位置也不对。然后,整篇文章有几点结论:

1.在调用yolo模型转换的rknn模型时,一般的rknn.config的mean_value应该是(0,0,0),std_value应该是(1,1,1)。

2.在rknn提供的标准.onnx2rknn中,输入输出接口即使经过标准量化,也仍然是float32格式。但是,rknn-toolkit输出rknn模型时,建议在实际工程部署时进行量化处理。

3.yolo11相对yolo5,在我查询到的.pt->.onnx->.rknn链路处理完毕后,输出参数会变为6个。我看到的例程里,后续的画框处理与yolo5一致,只用到了3个(1,x,n,n)的矩阵。所以似乎可以进一步简化输入输出参数。

4.yolo11还有一个改进是,输出矩阵的维度由原有的254 class,改为与用户的模型的class个数一致,缩减了输出矩阵的规模。

5.我现在遭遇的问题是,模型的原始参数表离散度太大,无法有效进行标量化处理。rknn提供的自定义标量处理流程中,.cfg文件无法找到常量参数表对应的标量化参数配置。

6.然后注意在.pt->.onnx时,可能需要指定opset。这些细节的参数可能是不可省略的,时间有限,我无法做针对性的测试了。

我下一步需要解决的就是问题5。他应该有明确的解决方案。我会对照比如yolo10的rknn移植代码来观察一下之前的同志是如何做适配处理的。因为这个问题,不是到yolo11才出现的。

依据我已经掌握的信息,感觉它可能会有如下的处理策略:

  1. 修改模型,加入额外的参数。
  2. 调整模型的标量化策略,比如由线性的离散修改为其他的方式,
  3. 可能涉及对.rknn的转换后的标量模型,进行二次训练。
  4. 调整特定的标量参数,比如由uint8,改为int16
  5. 先将.onnx转为一个已经进行量化处理的模型,比如onnx.runtime
  6. 修改yolo的训练模型,考虑在训练时即使用标量化的模型。
  7. 查找rknn-toolkit用户手册,因为这是一个通用的问题,昨天随手找到的two stage 转换之类的策略,rknn那里又该会有比较稳妥的解决方案。

这个部分今天可能会深入处理。

文中一些无效的信息:

1.1.无需考量,这是量化模型本身的内部处理逻辑。只用来了解量化时的一些配置参数。

1.问题

当onnx模型尚未标量化(quantize)之前,自训练数据集能够有效识别目标对象,但是,rknn模型,通常是标量化转换过的,就是输入输出参数已经从float32->u8。那么如何处理丢给rknn模型的输入输出参数,然后进行反标量化(dequantize)?

一般的处理,是需要拿到0点和缩放scale。

1.1 通用的反标量化过程  - 例程

import numpy as np# 假设这是从模型量化过程中获取到的缩放因子和零点
scales = [0.1, 0.2, 0.15, 0.25, 0.12, 0.18]
zero_points = [128, 127, 129, 126, 128, 127]# 假设这是模型输出的 uint8 数据
outputs_u8 = {'reg1': np.random.randint(0, 256, size=(1, 10), dtype=np.uint8),'cls1': np.random.randint(0, 256, size=(1, 5), dtype=np.uint8),'reg2': np.random.randint(0, 256, size=(1, 10), dtype=np.uint8),'cls2': np.random.randint(0, 256, size=(1, 5), dtype=np.uint8),'reg3': np.random.randint(0, 256, size=(1, 10), dtype=np.uint8),'cls3': np.random.randint(0, 256, size=(1, 5), dtype=np.uint8)
}# 反量化函数
def dequantize(u8_data, scale, zero_point):return (u8_data.astype(np.float32) - zero_point) * scale# 对每个输出进行反量化
outputs_f32 = {}
output_names = ['reg1', 'cls1', 'reg2', 'cls2', 'reg3', 'cls3']
for i, name in enumerate(output_names):outputs_f32[name] = dequantize(outputs_u8[name], scales[i], zero_points[i])# 打印反量化后的结果
for name, data in outputs_f32.items():print(f"{name}: {data.dtype}, shape={data.shape}")

1.1.1 为了确认模型的传入传出参数,我们对模型传入传出前做了log日志:

你可能会疑惑,这些关于输入输出响亮的类型信息是如何得到的,相关完整代码参见附录A,相关的调用模型,打印输入输出信息的代码片段如下:

        pred_results = ubuntu_detect(ONNX_MODEL, image, CLASSES)print(f'onnx model{ONNX_MODEL}')print(f'<<inputs array, shape{image.shape}, datatype={image.dtype}')for i in range(len(pred_results)):print(f'>>output array[i], shape{pred_results[i].shape}, datatype={pred_results[i].dtype}')

调用模型前的输入输出参数如下: 

<<inputs array, shape(1, 3, 640, 640), datatype=float32
>>output array[i], shape(1, 64, 80, 80), datatype=float32
>>output array[i], shape(1, 80, 80, 80), datatype=float32
>>output array[i], shape(1, 64, 40, 40), datatype=float32
>>output array[i], shape(1, 80, 40, 40), datatype=float32
>>output array[i], shape(1, 64, 20, 20), datatype=float32
>>output array[i], shape(1, 80, 20, 20), datatype=float32

注意:这组模型及调用是能够对目标对象进行识别的:

detectResult: 16
obj num is : 2

2.rknn-toolkit2.3文档查找 - 找到标量化的参数

2.1 尝试先跑通非量化版本

完整代码参见附录B,相关输出信息:

我们先尝试把这个输入输出的default dtype修改为float32,看看有没有可能行。有的,那就是onnx模型转换时不量化,把onnx2rknn.py中的:

rknn.build(do_quantization=False, dataset=DATASET)

此时的onnx2rknn输出信息:

然后将 detet_by_onnx.py中原本对onnx模型的调用改为对rknn模型的调用:

RKNN_MODEL = r'/home/firefly/app/git/yolo11_rknn_test/models/yolov11s-640-640_rk3588-float-20250219.rknn'
IMG_PATH = '/home/firefly/app/git/yolo11_rknn_test/images/cake26.jpg'
QUANTIZE_ON = Falsedef rk3588_detect(model, pic, classes):rknn = RKNN(verbose=True)rknn.config(mean_values=[[0,0,0]],std_values=[[255,255,255]],quant_img_RGB2BGR=False,target_platform='rk3588')#image.net's #another [0,0,0] [255,255,255]#mean_values = [0.485, 0.456, 0.406] 和 std_values = [0.229, 0.224, 0.225]rknn.load_rknn(path=model)rknn.init_runtime(target="rk3588", core_mask=RKNN.NPU_CORE_AUTO)outputs = rknn.inference(inputs=[pic], data_format=['nchw'])return outputs

注意:传入的pic实际是:nchw格式(1-3-640-640)即:number- channel - height - width格式,但是结果不对:

firefly@firefly:~/app/git/yolo11_rknn_test/detect$ python3 ./detect_rk3588.py
This is main ....
<class 'numpy.ndarray'>
I rknn-toolkit2 version: 2.3.0
I target set by user is: rk3588
onnx model/home/firefly/app/git/yolo11_rknn_test/models/yolov11s-640-640_rk3588-float-20250219.rknn
<<inputs array, shape(1, 3, 640, 640), datatype=float32
>>output array[i], shape(1, 64, 80, 80), datatype=float32
>>output array[i], shape(1, 80, 80, 80), datatype=float32
>>output array[i], shape(1, 64, 40, 40), datatype=float32
>>output array[i], shape(1, 80, 40, 40), datatype=float32
>>output array[i], shape(1, 64, 20, 20), datatype=float32
>>output array[i], shape(1, 80, 20, 20), datatype=float32
detectResult: 0
obj num is : 0 

2.1.1 bufix

问题出在meanvalue, stdvalue, 对float32而言,这两个数值建议设置为:

rknn.config(mean_values=[[0,0,0]],std_values=[[1,1,1]],

2.2 量化

量化过程中注意这个提示,依照《03_Rockchip_RKNPU_API_Reference_RKNN_Toolkit2_V2.3.0_EN.pdf》2:12:1应该被这样处理:

def rk3588_onnx2rknn(model, pic, classes):rknn = RKNN(verbose=True)rknn.config(mean_values=[[0,0,0]],std_values=[[1,1,1]],quant_img_RGB2BGR=False,#force_float32_nodes=['onnx::conv_1016', 'onnx::conv_1217'],  # 指定某层使用 float32 模式target_platform='rk3588')#image.net's #another [0,0,0] [255,255,255]#mean_values = [0.485, 0.456, 0.406] 和 std_values = [0.229, 0.224, 0.225]rknn.load_onnx(model=model)if QUANTIZE_ON:rknn.hybrid_quantization_step1(dataset='./dataset.txt')ret = rknn.hybrid_quantization_step2(model_input='./moonpie_80_0_11s_Feb19.model',data_input='./moonpie_80_0_11s_Feb19.data',model_quantization_cfg='./moonpie_80_0_11s_Feb19.quantization.cfg')else:rknn.build(do_quantization=False, dataset='dataset.txt', rknn_batch_size=1)rknn.export_rknn("./onnx2rknn.rknn");#rknn.load_rknn("./onnx2rknn.rknn")rknn.init_runtime(target="rk3588", core_mask=RKNN.NPU_CORE_AUTO)outputs = rknn.inference(inputs=[pic], data_format=['nchw'])return outputs

理论上上面这段代码需要执行两次。用户需要修改:.cfg文件中越界的那部分layer.将其类型人为改为float32。比如对输入输出参数的修改:

import torchtry:dummy_input = torch.randn(1,3,640,640)input_names=['data']output_names=['reg1', 'cls1','reg2', 'cls2','reg3', 'cls3']torch.onnx.export(self.model, dummy_input, '/app/rk3588_build/rknn_models/moonpie_80_0_11s_Feb19.onnx', verbose=False, input_names=input_names, output_names=output_names, opset_version=11)except RuntimeError:print('error occur when onnx export.')#do nothing#passfinally:print("==================onnx self gened==========")

比如,依据onnx2rknn的提示:

你至少应该对输入输出参数进行量化处理,相应的位置在:

的:

我们试着将其修改为int16,看看效果:

嗯....传入传出参数的类型不是在这里改的。

附录A detect_by_onnx.py

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import os
import sys
from math import exp
import cv2
import numpy as np
import onnxruntime as ortROOT = os.getcwd()
if str(ROOT) not in sys.path:sys.path.append(str(ROOT))ONNX_MODEL = r'/home/firefly/app/git/yolo11_rknn_test/models/moonpie_80_0_11s_Feb19.onnx'
IMG_PATH = '/home/firefly/app/git/yolo11_rknn_test/images/cake26.jpg'
QUANTIZE_ON = FalseCLASSES = ['moonpie', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light','fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow','elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee','skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard','tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple','sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch','potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone','microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear','hair drier', 'toothbrush']meshgrid = []class_num = len(CLASSES)
headNum = 3
strides = [8, 16, 32]
mapSize = [[80, 80], [40, 40], [20, 20]]
nmsThresh = 0.45
objectThresh = 0.5input_imgH = 640
input_imgW = 640def ubuntu_detect(model, pic, classes):# 明确指定执行提供者providers = ['AzureExecutionProvider', 'CPUExecutionProvider']ort_session = ort.InferenceSession(model, providers=providers)pred_results = (ort_session.run(None, {'data': pic}))return pred_resultsclass DetectBox:def __init__(self, classId, score, xmin, ymin, xmax, ymax):self.classId = classIdself.score = scoreself.xmin = xminself.ymin = yminself.xmax = xmaxself.ymax = ymaxclass YOLOV11DetectObj:def __init__(self):passdef GenerateMeshgrid(self):for index in range(headNum):for i in range(mapSize[index][0]):for j in range(mapSize[index][1]):meshgrid.append(j + 0.5)meshgrid.append(i + 0.5)def IOU(self, xmin1, ymin1, xmax1, ymax1, xmin2, ymin2, xmax2, ymax2):xmin = max(xmin1, xmin2)ymin = max(ymin1, ymin2)xmax = min(xmax1, xmax2)ymax = min(ymax1, ymax2)innerWidth = xmax - xmininnerHeight = ymax - ymininnerWidth = innerWidth if innerWidth > 0 else 0innerHeight = innerHeight if innerHeight > 0 else 0innerArea = innerWidth * innerHeightarea1 = (xmax1 - xmin1) * (ymax1 - ymin1)area2 = (xmax2 - xmin2) * (ymax2 - ymin2)total = area1 + area2 - innerAreareturn innerArea / totaldef NMS(self, detectResult):predBoxs = []sort_detectboxs = sorted(detectResult, key=lambda x: x.score, reverse=True)for i in range(len(sort_detectboxs)):xmin1 = sort_detectboxs[i].xminymin1 = sort_detectboxs[i].yminxmax1 = sort_detectboxs[i].xmaxymax1 = sort_detectboxs[i].ymaxclassId = sort_detectboxs[i].classIdif sort_detectboxs[i].classId != -1:predBoxs.append(sort_detectboxs[i])for j in range(i + 1, len(sort_detectboxs), 1):if classId == sort_detectboxs[j].classId:xmin2 = sort_detectboxs[j].xminymin2 = sort_detectboxs[j].yminxmax2 = sort_detectboxs[j].xmaxymax2 = sort_detectboxs[j].ymaxiou = self.IOU(xmin1, ymin1, xmax1, ymax1, xmin2, ymin2, xmax2, ymax2)if iou > nmsThresh:sort_detectboxs[j].classId = -1return predBoxsdef sigmoid(self, x):return 1 / (1 + exp(-x))def postprocess(self, out, img_h, img_w):print('postprocess ... ')detectResult = []output = []for i in range(len(out)):print(out[i].shape)output.append(out[i].reshape((-1)))scale_h = img_h / input_imgHscale_w = img_w / input_imgWgridIndex = -2cls_index = 0cls_max = 0for index in range(headNum):reg = output[index * 2 + 0]cls = output[index * 2 + 1]for h in range(mapSize[index][0]):for w in range(mapSize[index][1]):gridIndex += 2if 1 == class_num:cls_max = sigmoid(cls[0 * mapSize[index][0] * mapSize[index][1] + h * mapSize[index][1] + w])cls_index = 0else:for cl in range(class_num):cls_val = cls[cl * mapSize[index][0] * mapSize[index][1] + h * mapSize[index][1] + w]if 0 == cl:cls_max = cls_valcls_index = clelse:if cls_val > cls_max:cls_max = cls_valcls_index = clcls_max = self.sigmoid(cls_max)if cls_max > objectThresh:regdfl = []for lc in range(4):sfsum = 0locval = 0for df in range(16):temp = exp(reg[((lc * 16) + df) * mapSize[index][0] * mapSize[index][1] + h * mapSize[index][1] + w])reg[((lc * 16) + df) * mapSize[index][0] * mapSize[index][1] + h * mapSize[index][1] + w] = tempsfsum += tempfor df in range(16):sfval = reg[((lc * 16) + df) * mapSize[index][0] * mapSize[index][1] + h * mapSize[index][1] + w] / sfsumlocval += sfval * dfregdfl.append(locval)x1 = (meshgrid[gridIndex + 0] - regdfl[0]) * strides[index]y1 = (meshgrid[gridIndex + 1] - regdfl[1]) * strides[index]x2 = (meshgrid[gridIndex + 0] + regdfl[2]) * strides[index]y2 = (meshgrid[gridIndex + 1] + regdfl[3]) * strides[index]xmin = x1 * scale_wymin = y1 * scale_hxmax = x2 * scale_wymax = y2 * scale_hxmin = xmin if xmin > 0 else 0ymin = ymin if ymin > 0 else 0xmax = xmax if xmax < img_w else img_wymax = ymax if ymax < img_h else img_hbox = DetectBox(cls_index, cls_max, xmin, ymin, xmax, ymax)detectResult.append(box)# NMSprint('detectResult:', len(detectResult))predBox = self.NMS(detectResult)return predBoxdef precess_image(self, img_src, resize_w, resize_h):print(f'{type(img_src)}')image = cv2.resize(img_src, (resize_w, resize_h), interpolation=cv2.INTER_LINEAR)image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)image = image.astype(np.float32)image /= 255.0return imagedef detect(self, img_path):self.GenerateMeshgrid()orig = cv2.imread(img_path)if orig is None:print(f"无法读取图像: {img_path}")returnimg_h, img_w = orig.shape[:2]image = self.precess_image(orig, input_imgW, input_imgH)image = image.transpose((2, 0, 1))image = np.expand_dims(image, axis=0)#image = np.ones((1, 3, 640, 640), dtype=np.uint8)# print(image.shape)#ort_session = ort.InferenceSession(ONNX_MODEL)#pred_results = (ort_session.run(None, {'data': image}))pred_results = ubuntu_detect(ONNX_MODEL, image, CLASSES)print(f'onnx model{ONNX_MODEL}')print(f'<<inputs array, shape{image.shape}, datatype={image.dtype}')for i in range(len(pred_results)):print(f'>>output array[i], shape{pred_results[i].shape}, datatype={pred_results[i].dtype}')out = []for i in range(len(pred_results)):out.append(pred_results[i])predbox = self.postprocess(out, img_h, img_w)np.save('onnx_out1.npy', out[1])np.save('onnx_out3.npy', out[3])np.save('onnx_out5.npy', out[5])print('obj num is :', len(predbox))for i in range(len(predbox)):xmin = int(predbox[i].xmin)ymin = int(predbox[i].ymin)xmax = int(predbox[i].xmax)ymax = int(predbox[i].ymax)classId = predbox[i].classIdscore = predbox[i].scorecv2.rectangle(orig, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2)ptext = (xmin, ymin)title = CLASSES[classId] + "%.2f" % scorecv2.putText(orig, title, ptext, cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2, cv2.LINE_AA)cv2.imwrite('./test_onnx_result.jpg', orig)if __name__ == '__main__':print('This is main ....')img_path = IMG_PATHobj = YOLOV11DetectObj()obj.detect(img_path)

附录B onnx2rknn.py


import cv2
import numpy as npfrom rknn.api import RKNN
import osonnx_model = './rknn_models/moonpie_80_0_11s_Feb19.onnx' #onnx路径
save_rknn_dir = './rknn_models/moonpie_80_0_11s_Feb19.rknn'#rknn保存路径
IMG_PATH = '/app/rk3588_build/cake26.jpg'
DATASET = './dataset_cake.txt'if __name__ == '__main__':platform = 'rk3588'exp = 'yolov11'Width = 640Height = 640# Model from https://github.com/airockchip/rknn_model_zooMODEL_PATH = onnx_model NEED_BUILD_MODEL = True# NEED_BUILD_MODEL = Falseim_file = IMG_PATH# Create RKNN objectrknn = RKNN()OUT_DIR = "rknn_models"RKNN_MODEL_PATH = './{}/{}_{}.rknn'.format(OUT_DIR, exp+'-'+str(Width)+'-'+str(Height), platform)if NEED_BUILD_MODEL:rknn.config(mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], target_platform=platform)# Load modelprint('--> Loading model')ret = rknn.load_onnx(MODEL_PATH)if ret != 0:print('load model failed!')exit(ret)print('done')# Build modelprint('--> Building model')ret = rknn.build(do_quantization=True, dataset=DATASET)if ret != 0:print('build model failed.')exit(ret)print('done')# Export rknn modelif not os.path.exists(OUT_DIR):os.mkdir(OUT_DIR)print('--> Export RKNN model: {}'.format(RKNN_MODEL_PATH))ret = rknn.export_rknn(RKNN_MODEL_PATH)if ret != 0:print('Export rknn model failed.')exit(ret)print('done')else:ret = rknn.load_rknn(RKNN_MODEL_PATH)rknn.release()

相关文章:

标量化rknn的输入输出向量转换处理

这是一篇技术探索。yolo11模型生成后&#xff0c;我发现它无法在rknn环境正确识别出目标对象。而在宿主机上&#xff0c;或者直接调用.pt转换过的.onnx模型是可以得到正确结果的。这篇文章对应近乎一天的工作。最终的结论就是。这是一个模型量化的问题&#xff0c;与yolo的版本…...

认知重构 | 自我分化 | 苏格拉底式提问

注&#xff1a;本文为 “认知重构 | 自我分化” 相关文章合辑。 心理学上有一个词叫&#xff1a;认知重构&#xff08;改变 “非黑即白&#xff0c;一分为二” 的思维方式&#xff09; 原创 心理师威叔 心理自救 2024 年 10 月 26 日 19:08 广东 你有没有过这样的时候&#x…...

Java集合之ArrayList(含源码解析 超详细)

1.ArrayList简介 ArrayList的底层是数组队列&#xff0c;相当于动态数组。与Java中的数组相比&#xff0c;它的容量能动态增长。在添加大量元素前&#xff0c;应用程序可以使用ensureCapacity操作来增加ArrayList实例的容量。这可以减少递增式再分配的数量。 ArrayList继承于Ab…...

Java笔记18

2-10-3Cookie&Session 1.会话跟踪技术概述 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次…...

LangChain大模型应用开发:构建Agent智能体

介绍 大家好&#xff0c;博主又来给大家分享知识了。今天要给大家分享的内容是使用LangChain进行大模型应用开发中的构建Agent智能体。 在LangChain中&#xff0c;Agent智能体是一种能够根据输入的任务或问题&#xff0c;动态地决定使用哪些工具(如搜索引擎、数据库查询等)来…...

巧用GitHub的CICD功能免费打包部署前端项目

近年来&#xff0c;随着前端技术的发展&#xff0c;前端项目的构建和打包过程变得越来越复杂&#xff0c;占用的资源也越来越多。我有一台云服务器&#xff0c;原本打算使用Docker进行部署&#xff0c;以简化操作流程。然而&#xff0c;只要执行sudo docker-compose -f deploy/…...

【2】常用cmd命令大全、使用cmd运行和编译Java程序

文章目录 一、常用cmd命令大全文件和目录操作系统信息查看磁盘管理网络操作其他常用命令 二、使用cmd命令运行和编译Java程序 一、常用cmd命令大全 cmd的常用命令较多&#xff0c;java初学者只需了解这几个即可 dir&#xff1a;查看当前路径下的所有文件夹 cd&#xff1a;进入指…...

UniApp SelectorQuery 讲解

一、SelectorQuery简介 在UniApp中&#xff0c;SelectorQuery是一个非常强大的工具&#xff0c;它允许开发者查询节点信息。通过这个API&#xff0c;我们可以获取到页面元素的尺寸、位置、滚动条位置等信息。这在处理动态布局、动画效果或是用户交互时尤为重要。 二、基本使用…...

【行业解决方案篇十一】【DeepSeek零售分析:客流热力图生成系统】

开篇:当商店开始"思考" 你可能不知道,现在北京三里屯的优衣库旗舰店,每天要处理超过3000个顾客的移动轨迹数据。这些数据不是用来监控,而是让店铺自己"学会"把畅销款T恤摆在哪里最能促进销量。今天要讲的DeepSeek零售分析系统,就是这样一个能把"…...

车载DoIP协议 --- TCP详细解析

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 简单&#xff0c;单纯&#xff0c;喜欢独处&#xff0c;独来独往&#xff0c;不易合同频过着接地气的生活…...

C++关键字之mutable

1.介绍 在C中&#xff0c;mutable是一个关键字&#xff0c;用于修饰类的成员变量。它的主要作用是允许在常量成员函数或常量对象中修改被标记为mutable的成员变量。通常情况下&#xff0c;常量成员函数不能修改类的成员变量&#xff0c;但有些情况下&#xff0c;某些成员变量的…...

设计模式| 观察者模式 Observer Pattern详解

目录 一、概述1.1 动机1.2 核心思想1.3 别名 二、角色与实现原理2.1 角色2.2 实现原理2.3 类图 三、经典接口实现3.1 示例3.1.1 观察者接口3.1.2 目标接口3.1.3 具体被观察者3.1.4 具体观察者3.1.5 Client3.1.6 UML时序图 3.2 特点 四、其他实现方式4.1 委托与事件&#xff08;…...

Git-速查

Git 安装 Git 之后&#xff0c;你可以… 配置全局用户信息&#xff08;推荐&#xff09; 全局设置&#xff0c;创建本地仓库时默认分支名称为 main&#xff08;你需要什么名称就该什么名称&#xff09;【推荐配置为 main 】 git config --global init.defaultBranch main全…...

Spring Boot嵌入式服务器深度解析:从配置到调优的全方位指南

文章目录 引言一、嵌入式服务器核心原理1.1 架构设计特点1.2 主流服务器对比 二、嵌入式服务器配置实战2.1 基础配置模板2.2 HTTPS安全配置 三、高级调优策略3.1 线程池优化&#xff08;Tomcat示例&#xff09;3.2 响应压缩配置3.3 访问日志配置 四、服务器切换实战4.1 切换至U…...

深入解析浏览器渲染全流程:从URL输入到页面渲染的底层原理与性能优化(附实战代码)

本文以https://example.com为例&#xff0c;逐层剖析浏览器从输入URL到页面渲染的完整链路&#xff0c;涵盖DNS解析、TCP/TLS握手、HTTP请求、DOM/CSSOM构建等核心阶段&#xff0c;结合代码示例与性能调优技巧&#xff0c;助你掌握浏览器底层运行机制。 一、导航阶段&#xff1…...

【网络安全】常见的web攻击

1、SQL注入攻击 定义&#xff1a; 攻击者在HTTP请求中注入恶意的SQL代码&#xff0c;当服务器利用参数构建SQL语句的时候&#xff0c;恶意的SQL代码被一起构建,并在数据库中执行。 示例&#xff1a; 用户登录&#xff1a; 输入用户名xx&#xff0c; 密码 or 1 …...

MySQL面试学习

MySQL 1.事务 事务的4大特性 事务4大特性&#xff1a;原子性、一致性、隔离性、持久性 原⼦性&#xff1a; 事务是最⼩的执⾏单位&#xff0c;不允许分割。事务的原⼦性确保动作要么全部完成&#xff0c;要么全不执行一致性&#xff1a; 执⾏事务前后&#xff0c;数据保持⼀…...

一文读懂Docker之Docker Compose

目录 一、Docker Compose简介 二、Docker Compose的安装和基本使用 1、Docker Compose的安装 步骤一、下载docker-compose 步骤二、新增可执行权限 步骤三、查看是否安装成功 2、Docker Compose的基本使用 (1)、docker-compose up (2)、docker-compose ps (3)、docke…...

escape SQL中用法

select * from tablename where username like %#%% escape # 这个的意思就是&#xff0c;escape指定字符#&#xff0c;#字符后面的第一个字符被认为是普通字符 查询示例2 查询username字段中包含[的数据也是一样&#xff0c;即&#xff1a; select * from tablename where us…...

Cherno C++ P57 Standard array处理静态数组

这篇文章当中我们讲一下如何使用C自带的standard array来处理静态数组。 首先什么是静态数组&#xff0c;静态数组通常指的是不会增长的数据&#xff0c;长度是已经确定了的。我们在定义数组的时候就必须确定好长度与类型。 其次C当中也确实给我们提供了一些可以用来处理静态…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中&#xff0c;手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力&#xff0c;既支持点击、长按、拖拽等基础单一手势的精细控制&#xff0c;也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合

在汽车智能化的汹涌浪潮中&#xff0c;车辆不再仅仅是传统的交通工具&#xff0c;而是逐步演变为高度智能的移动终端。这一转变的核心支撑&#xff0c;来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒&#xff08;T-Box&#xff09;方案&#xff1a;NXP S32K146 与…...

QT3D学习笔记——圆台、圆锥

类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体&#xff08;对象或容器&#xff09;QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质&#xff08;定义颜色、反光等&#xff09;QFirstPersonC…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看

文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...