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

【无标题】yoloV8目标检测与实例分割--目标检测onnx模型部署

1. 模型转换

ONNX Runtime 是一个开源的高性能推理引擎,用于部署和运行机器学习模型,其设计的目标是优化执行open neural network exchange (onnx)格式定义各模型,onnx是一种用于表示机器学习模型的开放标准。ONNX Runtime提供了几个关键功能和优势:

a. 跨平台兼容性:ONNX Runtime 旨在与各种硬件与操作系统平台兼容,主要包括Windows、Linux及各种加速器,如CPU、GPU和FPGA,使得能够轻松在不同环境中部署和运行机器学习模型。

b. 高性能:ONNX Runtime 经过性能优化,能够提供高效的模型计算,而且针对不同的平台提供了对应的优化模式。

c. 多框架支持:ONNX Runtime 可以与使用不同的机器学习框架创建的模型一起使用,包括Pytorch、Tensorflow等。

d. 模型转换:ONNX Runtime 可以将所支持的框架模型转换为onnx格式,从而更容易在各种场景中部署。

e. 多语言支持:ONNX Runtime 可用多种编程语言,包括C++、C#、Python等,使其能够适用于不同语言的开发场景。

f. 自定义运算符:ONNX Runtime 支持自定义运算符,允许开发人员扩展其功能以支持特定操作或硬件加速。

ONNX Runtime广泛用于各种机器学习应用的生产部署,包括计算机视觉、自然语言处理等。它由ONNX社区积极维护,并持续接受更新和改进。

2. pt模型与onnx模型区别

pt模型和onnx模型均为常用的表示机器学习模型的文件格式,主要区别体现在:

a. 文件格式:

pt模型:Pytorch框架的权重文件格式,通常保存为.pt或.pth扩展名保存,包含了模型的权重参数及模型结构的定义。

onnx模型:ONNX格式的模型文件,通常以.onnx扩展名保存,onnx文件是一种中性表示格式,独立于任何特定的深度学习框架,用于跨不同框架之间的模型转换和部署。

b. 框架依赖:

pt模型:依赖于Pytorch框架,在加载和运行时需要使用Pytorch库,限制了此类模型在不同框架中的直接使用。

onnx模型:ONNX模型独立于深度学习框架,可以在支持ONNX的不同框架中加载和运行,如Tensorflow、Caffe2及ONNX Runtime等。

c. 跨平台兼容性:

pt模型:需要在不同平台上进行Pytorch的兼容性配置,需要额外的工作和依赖处理。

onnx模型:ONNX模型的独立性使其更容易在不同平台和硬件上部署,无需担心框架依赖性问题。

3. yolov8 pt模型转换为onnx

要在不同框架或平台中部署训练的pt模型,需要利用ONNX转换工具将pt模型转换为ONNX格式。

from ultralytics import YOLO% load model
model = YOLO('yolov8m.pt')% expert model
success = model.expert(format="onnx")

4. 构建推理模型

a. 环境配置

onnx模型推理只依赖于onnxruntime库,图像处理依赖opencv,需要安装此两个库。

pip3 install onnxruntime
pip3 install opencv-python
pip3 install numpy
pip3 install gradio

b. 部署代码

utils.py

import numpy as np
import cv2class_names = ['person', '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']# Create a list of colors for each class where each color is a tuple of 3 integer values
rng = np.random.default_rng(3)
colors = rng.uniform(0, 255, size=(len(class_names), 3))def nms(boxes, scores, iou_threshold):# Sort by scoresorted_indices = np.argsort(scores)[::-1]keep_boxes = []while sorted_indices.size > 0:# Pick the last boxbox_id = sorted_indices[0]keep_boxes.append(box_id)# Compute IoU of the picked box with the restious = compute_iou(boxes[box_id, :], boxes[sorted_indices[1:], :])# Remove boxes with IoU over the thresholdkeep_indices = np.where(ious < iou_threshold)[0]# print(keep_indices.shape, sorted_indices.shape)sorted_indices = sorted_indices[keep_indices + 1]return keep_boxesdef multiclass_nms(boxes, scores, class_ids, iou_threshold):unique_class_ids = np.unique(class_ids)keep_boxes = []for class_id in unique_class_ids:class_indices = np.where(class_ids == class_id)[0]class_boxes = boxes[class_indices,:]class_scores = scores[class_indices]class_keep_boxes = nms(class_boxes, class_scores, iou_threshold)keep_boxes.extend(class_indices[class_keep_boxes])return keep_boxesdef compute_iou(box, boxes):# Compute xmin, ymin, xmax, ymax for both boxesxmin = np.maximum(box[0], boxes[:, 0])ymin = np.maximum(box[1], boxes[:, 1])xmax = np.minimum(box[2], boxes[:, 2])ymax = np.minimum(box[3], boxes[:, 3])# Compute intersection areaintersection_area = np.maximum(0, xmax - xmin) * np.maximum(0, ymax - ymin)# Compute union areabox_area = (box[2] - box[0]) * (box[3] - box[1])boxes_area = (boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1])union_area = box_area + boxes_area - intersection_area# Compute IoUiou = intersection_area / union_areareturn ioudef xywh2xyxy(x):# Convert bounding box (x, y, w, h) to bounding box (x1, y1, x2, y2)y = np.copy(x)y[..., 0] = x[..., 0] - x[..., 2] / 2y[..., 1] = x[..., 1] - x[..., 3] / 2y[..., 2] = x[..., 0] + x[..., 2] / 2y[..., 3] = x[..., 1] + x[..., 3] / 2return ydef draw_detections(image, boxes, scores, class_ids, mask_alpha=0.3):det_img = image.copy()img_height, img_width = image.shape[:2]font_size = min([img_height, img_width]) * 0.0006text_thickness = int(min([img_height, img_width]) * 0.001)det_img = draw_masks(det_img, boxes, class_ids, mask_alpha)# Draw bounding boxes and labels of detectionsfor class_id, box, score in zip(class_ids, boxes, scores):color = colors[class_id]draw_box(det_img, box, color)label = class_names[class_id]caption = f'{label} {int(score * 100)}%'draw_text(det_img, caption, box, color, font_size, text_thickness)return det_imgdef detections_dog(image, boxes, scores, class_ids, mask_alpha=0.3):det_img = image.copy()img_height, img_width = image.shape[:2]font_size = min([img_height, img_width]) * 0.0006text_thickness = int(min([img_height, img_width]) * 0.001)# det_img = draw_masks(det_img, boxes, class_ids, mask_alpha)# Draw bounding boxes and labels of detectionsfor class_id, box, score in zip(class_ids, boxes, scores):color = colors[class_id]draw_box(det_img, box, color)label = class_names[class_id]caption = f'{label} {int(score * 100)}%'draw_text(det_img, caption, box, color, font_size, text_thickness)return det_imgdef draw_box( image: np.ndarray, box: np.ndarray, color: tuple[int, int, int] = (0, 0, 255),thickness: int = 2) -> np.ndarray:x1, y1, x2, y2 = box.astype(int)return cv2.rectangle(image, (x1, y1), (x2, y2), color, thickness)def draw_text(image: np.ndarray, text: str, box: np.ndarray, color: tuple[int, int, int] = (0, 0, 255),font_size: float = 0.001, text_thickness: int = 2) -> np.ndarray:x1, y1, x2, y2 = box.astype(int)(tw, th), _ = cv2.getTextSize(text=text, fontFace=cv2.FONT_HERSHEY_SIMPLEX,fontScale=font_size, thickness=text_thickness)th = int(th * 1.2)cv2.rectangle(image, (x1, y1),(x1 + tw, y1 - th), color, -1)return cv2.putText(image, text, (x1, y1), cv2.FONT_HERSHEY_SIMPLEX, font_size, (255, 255, 255), text_thickness, cv2.LINE_AA)def draw_masks(image: np.ndarray, boxes: np.ndarray, classes: np.ndarray, mask_alpha: float = 0.3) -> np.ndarray:mask_img = image.copy()# Draw bounding boxes and labels of detectionsfor box, class_id in zip(boxes, classes):color = colors[class_id]x1, y1, x2, y2 = box.astype(int)# Draw fill rectangle in mask imagecv2.rectangle(mask_img, (x1, y1), (x2, y2), color, -1)return cv2.addWeighted(mask_img, mask_alpha, image, 1 - mask_alpha, 0)

YOLODet.py

import time
import cv2
import numpy as np
import onnxruntimefrom detection.utils import xyw2xyxy, draw_detections, multiclass_nms, detections_dogclass YOLODet:def __init__(self, path, conf_thresh=0.7, iou_thresh=0.5):self.conf_threshold = conf_threshself.iou_threshold = iou_thresh# Initialize modelself.initialize_model(path)def __call__(self, image):return self.detect_objects(image)def initialize_model(self, path):self.session = onnxruntime.InferenceSession(path, providers=onnxruntime.get_available_providers())# Get model infoself.get_input_details()self.get_output_details()def detect_objects(self, image):input_tensor = self.prepare_input(image)# perform inference on the imageoutputs = self.inference(input_tensor)self.boxes, self.scores, self.class_ids = self.process_output(outputs)return self.boxes. self.scores, self.class_idsdef prepare_input(self, image):self.img_height, self.img_width = img.shape[:2]input_img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)# resize input imageinput_img = cv2.resize(input_img, (self.input_width, self.input_height))# scale input pixel values to 0 to 1input_img = input_img / 255.0input_img = input_img.transpose(2, 0, 1)input_tensor = input_img[np.newaxis, :, :, :].astype(np.float32)return input_tensordef inference(self, input_tensor):start = time.perf_counter()outputs = self.session.run(self.output_names, {self.input_names[0]: input_tensor})# printf(f"inference time: {(time.perf_counter() - start)*1000:.2f} ms")return outputsdef process_output(self, output):predictions = np.squeeze(output[0]).T# filter out object confidence scores below thresholdscores = np.max(predictions[:,4:], axis=1)predictions = predictions[scores > self.conf_threshold, :]scores = scores[scores > self.conf_threshold]if len(scores) == 0:return [], [], []# get the class with the highest confidenceclass_ids = np.argmax(predictions[:,4:], axis=1)# get bounding boxes for each objectboxes = self.extract_boxes(predictions)# apply non-maxima suppression to suppress weak, overlapping bounding boxes# indices = nms(boxes, scores, class_ids, self.iou_threshold)return boxes[indices], scores[indices], class_ids[indices]def extract_boxes(self, predictions):# extract boxes from predictionsboxes = predictions[:,:4]# scale boxes to original image dimensionsboxes = self.rescale_boxes(boxes)# convert boxes to xyxy fromatboxes = xyw2xyxy(boxes)return boxesdef rescale_boxes(self, boxes):# rescale boxes to original image dimensionsinput_shape = np.array([self.input_width, self.input_height, self.input_width, self.input_height])boxes = np.divide(boxes, input_shape, dtype=np.float32)boxes *= np.array([self.img_width, self.img_height, self.img_width, self.img_height])return boxesdef draw_detection(self, image, draw_scores=True, mask_alpha=0.4):return detection_dog(image, self.boxes, self.scores, self.class_ids, mask_alpha)def get_input_details(self):model_inputs = self.session.get_inputs()self.input_names = [model_inputs[i].name for i in range(len(model_inputs))]self.input_shape = model_inputs[0].shapeself.input_height = self.input_shape[2]self.input_width = self.input_shape[3]def get_output_details(self):model_outputs = self.session.get_outputs()self.output_names = [model_output[i].name for i in range(len(model_outputs))]

5. 测试模型

图像测试

import cv2
import numpy as np
from detection import YOLODet
import gradio as grmodel = 'yolov8m.onnx'
yolo_det = YOLODet(model, conf_thresh=0.5, iou_thresh=0.3)def det_img(cv_src):yolo_det(cv_src)cv_dst = yolo_det.draw_detections(cv_src)return cv_dstif __name__ == '__main__':input = gr.Image()output = gr.Image()demo = gr.Interface(fn=det_img, inputs=input, outputs=output)demo.launch()

视频推理

def detectio_video(input_path, model_path, output_path):cap = cv2.VideoCapture(input_path)fps = int(cap.get(5))t = int(1000 / fps)videoWriter = Nonedet = YOLODet(model_path, conf_thresh=0.3, iou_thresh=0.5)while True:# try_, img = cap.read()if img is None:breakdet(img)cv_dst = det.draw_detections(img)if videoWriter is None:fourcc = cv2.VideoWriter_fourcc('m','p','4','v')videoWriter = cv2.VideoWriter(output_path, fourcc, fps, (cv_dst.shape[1], cv_dst.shape[0]))cv2.imshow("detection", cv_dst)cv2.waitKey(t)if cv2.getWindowProperty("detection", cv2.WND_PROP_AUTOSIZE) < 1:breakcap.release()videoWriter.release()cv2.destroyAllWindows()

相关文章:

【无标题】yoloV8目标检测与实例分割--目标检测onnx模型部署

1. 模型转换 ONNX Runtime 是一个开源的高性能推理引擎&#xff0c;用于部署和运行机器学习模型&#xff0c;其设计的目标是优化执行open neural network exchange &#xff08;onnx&#xff09;格式定义各模型&#xff0c;onnx是一种用于表示机器学习模型的开放标准。ONNX Ru…...

深入理解与防御跨站脚本攻击(XSS):从搭建实验环境到实战演练的全面教程

跨站脚本攻击&#xff08;XSS&#xff09;是一种常见的网络攻击手段&#xff0c;它允许攻击者在受害者的浏览器中执行恶意脚本。以下是一个XSS攻击的实操教程&#xff0c;包括搭建实验环境、编写测试程序代码、挖掘和攻击XSS漏洞的步骤。 搭建实验环境 1. 安装DVWA&#xff…...

初步认识栈和队列

Hello&#xff0c;everyone&#xff0c;今天小编讲解栈和队列的知识&#xff01;&#xff01;&#xff01; 1.栈 1.1栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。 进行数据插入和删除操作的一端 称为栈顶&…...

插件:NGUI

一、版本 安装完毕后重启一下即可&#xff0c;否则可能创建的UI元素不生效 二、使用 Label文字 1、创建Canvs 2、只有根节点的这些脚本全部展开才能鼠标右键创建UI元素 3、选择字体 Label添加打字效果 Sprite图片 1、选择图集 2、选择图集中的精灵 InvisibleWidget容器 用来…...

网络爬虫原理及其应用

你是否想知道Google 和 Bing 等搜索引擎如何收集搜索结果中显示的所有数据。这是因为搜索引擎对其档案中的所有页面建立索引&#xff0c;以便它们可以根据查询返回最相关的结果。网络爬虫使搜索引擎能够处理这个过程。 本文重点介绍了网络爬虫的重要方面、网络爬虫为何重要、其…...

串口中断原理及实现

一、串口的原理 SM0、SM1——串行口工作模式 SM0SM1模式特点00模式0移位寄存器方式&#xff0c;用于I/O口扩展01模式18位UART,波特率可变10模式29位UART,波特率为时钟频率/32或/6411模式39位UART,波特率可变 TI、RI——发送、接收中断标志位 TITI0 允许发送>TI1 发送完成后…...

课时136:变量进阶_变量实践_高级赋值

2 变量进阶 2.1 变量实践 2.1.1 高级赋值 学习目标 这一节&#xff0c;我们从 基础知识、简单实践、小结 三个方面来学习 基础知识 简介 所谓的高级赋值&#xff0c;是另外的一种变量值获取方法&#xff0c;这里涉及到更多我们学习之外的一些shell内置变量格式,其实这部分…...

牛客网刷题 | BC99 正方形图案

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 KiKi学习了循环&am…...

启动小程序F12窗口管理器

如何使用小程序F12任务窗口管理器教学流程 一、引言 小程序的开发者们&#xff0c;是否希望有一款工具能帮助你们更好地管理任务窗口&#xff1f; 二、前置准备 观看视频教程 访问B站视频链接&#xff1a;https://www.bilibili.com/video/BV1aa4y197UU/?spm_id_from333.9…...

完全背包之零钱兑换I

上次分享完完全背包问题的解决思路后&#xff0c;这次分享一道和完全背包有关的leetcode题。 零钱兑换 给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。计算并返回可以凑成总金额所需的最少的硬币个数。如果…...

Flutter 中的 FittedBox 小部件:全面指南

Flutter 中的 FittedBox 小部件&#xff1a;全面指南 在Flutter的丰富布局小部件中&#xff0c;FittedBox扮演着一个独特而重要的角色。它是一个灵活的组件&#xff0c;用于将子组件的大小和位置适应到给定的约束条件中。本文将提供FittedBox的全面指南&#xff0c;帮助你了解…...

Java的线程的使用

一.两种创建线程的方式 1.继承Thread类&#xff08;匿名内部类&#xff09; 创建方式&#xff1a; 1.定义一个子类继承Thread&#xff0c;重写run方法 2.创建子类对象&#xff0c; 3.调用子类对象的start方法&#xff08;启动还是执行的run方法&#xff09; 优缺点&#x…...

行为型模式 (Python版)

模板方法模式 """案例&#xff1a;写简历内容&#xff1a;最近有个招聘会&#xff0c;可以带上简历去应聘了。但是&#xff0c;其中有一家公司不接受简历&#xff0c;而是给应聘者发了两张公司自己定制的简历表&#xff0c;分别是A类型的简历表和B类型的简历表…...

vscode:如何解决”检测到include错误,请更新includePath“

vscode:如何解决”检测到include错误&#xff0c;请更新includePath“ 前言解决办法1 获取includePath路径2 将includePath路径添加到指定文件3 保存 前言 配置vscode是出现如下错误&#xff1a; 解决办法 1 获取includePath路径 通过cmd打开终端&#xff0c;输入如下指令&a…...

区块链会议投稿资讯CCF A--USENIX Security 2025 截止9.4、1.22 附录用率

会议名称&#xff1a;34th USENIX Security Symposium CCF等级&#xff1a;CCF A类学术会议 类别&#xff1a;网络与信息安全 录用率&#xff1a;2023年接收率29%&#xff0c;2024录用的区块链相关文章请查看 Symposium Topics System security Operating systems security …...

vue实现可拖拽移动悬浮球

封装悬浮球组件&#xff0c;文件名s-icons.vue <template><div ref"icons" class"icons-container" :style"{ left: left px, top: top px }"><slot></slot></div> </template> <script> export …...

立体库堆垛机的精密构造与功能(收藏版)

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 新书《智能物流系统构成与技术实践》 在现代物流仓储体系中&#xff0c;堆垛机以其高效、精准的操作能力&#xff0c;成为了自动化存储与检索系统的关键所在。 其复杂的构造和多样化的…...

算法提高之你能回答这些问题吗

算法提高之你能回答这些问题吗 核心思想&#xff1a;线段树 用sum,lmax,rmax,tmax分别存线段长度,最大前缀,最大后缀,最大子段和 #include <iostream>#include <cstring>#include <algorithm>using namespace std;const int N 500010;int n,m;int w[N];s…...

C++-指针

在C中&#xff0c;指针是至关重要的组成部分。它是C语言最强大的功能之一&#xff0c;也是最棘手的功能之一。 指针具有强大的能力&#xff0c;其本质是协助程序员完成内存的直接操纵。 指针&#xff1a;特定类型数据在内存中的存储地址&#xff0c;即内存地址。 指针变量的定…...

Three.js 研究:2、如何让动画线性运动

1、默认的动画含有加速度并非线性的 制作好的动画很明显是非线性的&#xff0c;这是一个运动环&#xff0c;为了让环运行线性进行如下设置。 2、设置动画成为线性动画...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...