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

介绍两款生成神经网络架构示意图的工具:NN-SVG和PlotNeuralNet

对于神经网络架构的可视化是很有意义的,可以在很大程度上帮助到我们清晰直观地了解到整个架构,我们在前面的 PyTorch的ONNX结合MNIST手写数字数据集的应用(.pth和.onnx的转换与onnx运行时)
有介绍,可以将模型架构文件(常见的格式都可以)在线上传到 https://netron.app/,将会生成架构示意图,比如将yolov5s.pt这个预训练模型,上传之后,将出现下面这样的图片(局部):

这种属于非常简单的层的连接展示,也能够直观知道整个架构是由哪些层组成,虽然每层可以查看一些属性,不过对于每层的具体细节并没有那么直观展现在图片当中。
接下来介绍的这两款都会生成漂亮的可视化神经网络图,可以用来绘制报告和演示使用,效果非常棒。 

1、NN-SVG

NN-SVG生成神经网络架构的地址:http://alexlenail.me/NN-SVG/AlexNet.html
显示可能很慢,最好科学上网,进去之后,我们可以看到,有三种神经网络架构可以进行设置:FCNN、LeNet、AlexNet 我们分别来看下:

1.1、FCNN 

第一种就是最基础的全连接神经网络FCNN输入层-->隐藏层(若干)-->输出层,截图如下:

左侧边栏可以进行一些颜色、形状、透明度等设置,也可以很方便的增加和减少层。右边就会实时的显示出操作的效果。

1.2、LeNet

LeNet是一种经典的卷积神经网络,最初用来识别手写数字,我们来看下其结构:

可以看到架构主要是由卷积层组成,输入层-->卷积层-->最大池化层-->...-->全连接层-->输出层
左边同样的都是可以设置颜色,透明度等,可以增减层数,在每层里可以设置数量、高宽以及卷积核大小,还可以指定是否显示层的名称,这样就更加清楚的知道架构是由哪些具体的层组成了。

1.3、AlexNet

AlexNet是辛顿和他的学生Alex Krizhevsky设计的CNN,在2012年ImageNet的竞赛中获得冠军,它是在LeNet的基础上应用了ReLU激活函数(取代Sigmoid)、Dropout层(避免过拟合)、LRN层(增强泛化能力)等的一种神经网络,截图如下:

同样的可以直观看到,每个层的数量、宽高、卷积核的大小,这些直观的神经网络示意图,尤其对于初学者来说可以很好的理解某个神经网络的整个计算过程。
最后的这些都是可以点击"Download SVG"将其下载成svg格式(一种XML格式)的文件。

2、PlotNeuralNet

2.1、安装

首先确认自己的操作系统,然后对应着进行安装,后面出现的示例是本人的Ubuntu 18.04版本上做的。

Ubuntu 16.04

sudo apt-get install texlive-latex-extra

Ubuntu 18.04.2
基于本网站,请安装以下软件包,包含一些字体包:

sudo apt-get install texlive-latex-base
sudo apt-get install texlive-fonts-recommended
sudo apt-get install texlive-fonts-extra
sudo apt-get install texlive-latex-extra

Windows或其他系统

下载安装MiKTeX:https://miktex.org/download

下载安装Git bash:https://git-scm.com/download/win
或者Cygwin:https://www.cygwin.com/
准备就绪之后运行即可:

cd pyexamples/
bash ../tikzmake.sh test_simple

2.2、克隆运行

上面的Latex安装好了之后,就克隆PlotNeuralNet: 

git clone https://github.com/HarisIqbal88/PlotNeuralNet.git

 我们先来执行自带的一个测试文件

cd pyexamples/
bash ../tikzmake.sh test_simple

将生成test_simple.pdf,截图如下:

2.3、test_simple.py

我们来看下自带的test_simple.py内容:

import sys
sys.path.append('../')
from pycore.tikzeng import *# defined your arch
arch = [to_head( '..' ),to_cor(),to_begin(),to_Conv("conv1", 512, 64, offset="(0,0,0)", to="(0,0,0)", height=64, depth=64, width=2 ),to_Pool("pool1", offset="(0,0,0)", to="(conv1-east)"),to_Conv("conv2", 128, 64, offset="(1,0,0)", to="(pool1-east)", height=32, depth=32, width=2 ),to_connection( "pool1", "conv2"), to_Pool("pool2", offset="(0,0,0)", to="(conv2-east)", height=28, depth=28, width=1),to_SoftMax("soft1", 10 ,"(3,0,0)", "(pool1-east)", caption="SOFT"  ),to_connection("pool2", "soft1"),    to_Sum("sum1", offset="(1.5,0,0)", to="(soft1-east)", radius=2.5, opacity=0.6),to_connection("soft1", "sum1"),to_end()]def main():namefile = str(sys.argv[0]).split('.')[0]to_generate(arch, namefile + '.tex' )if __name__ == '__main__':main()

代码比较简单,导入库之后就是定义架构,然后就自定义的每一层都写在arch这个列表中的 to_begin() 和 to_end() 之间,然后就通过函数 to_generate() arch列表生成.tex文件,最后就是通过bash自动转换成pdf文件,我们查看下bash文件内容:cat tikzmake.sh

#!/bin/bashpython $1.py 
pdflatex $1.texrm *.aux *.log *.vscodeLog
rm *.texif [[ "$OSTYPE" == "darwin"* ]]; thenopen $1.pdf
elsexdg-open $1.pdf
fi

2.4、自定义网络架构

接下来我们自定义一个网络架构测试下,tony.py

import sys
sys.path.append('../')
from pycore.tikzeng import *# defined your arch
arch = [to_head('..'),to_cor(),to_begin(),to_input('dog.png', width=18, height=14),to_Conv("conv1", 512, 64, offset="(1,0,0)", to="(0,0,0)", height=64, depth=64, width=10,caption="Conv1 Layer"),to_Pool("pool1", offset="(0,0,0)", to="(conv1-east)",caption="Pool1 Layer"),to_Conv("conv2", 128, 64, offset="(4,0,0)", to="(pool1-east)", height=32, depth=32, width=5,caption="Conv2 Layer"),to_connection("pool1", "conv2"),to_Pool("pool2", offset="(0,0,0)", to="(conv2-east)", height=28, depth=28, width=1,caption="Pool2 Layer"),to_SoftMax("soft1", 10 ,"(8,0,0)", "(pool1-east)", caption="Softmax Layer"),to_connection("pool2", "soft1"),to_skip(of="pool1",to="pool2",pos=1.25),to_end()]def main():namefile = str(sys.argv[0]).split('.')[0]to_generate(arch, namefile + '.tex' )if __name__ == '__main__':main()

其中一些代码的解释:

to_input:可以指定输入图片
to="(conv1-east)":表示当前层在conv1的东边(右边)
to_connection( "pool1", "conv2"):在两者之间画连接线
caption:标题
to_skip:做跳线,其中pos大于1表示向上进行画线,小于1就是向下,这个可以自己进行调试
如果对一些方法不明确其有哪些参数,可以使用帮助:help

to_input(pathfile, to='(-3,0,0)', width=8, height=8, name='temp')
to_SoftMax(name, s_filer=10, offset='(0,0,0)', to='(0,0,0)', width=1.5, height=3, depth=25, opacity=0.8, caption=' ')


当然这里的需要命令行进入到PlotNeuralNet目录,因为需要加载:from pycore.tikzeng import *
其他层需要加入,依葫芦画瓢即可,很简单,比如:
to_UnPool('Unpool', offset="(5,0,0)", to="(0,0,0)",height=64, width=2, depth=64, caption='Unpool'),
to_ConvRes("ConvRes",  s_filer=512, n_filer=64, offset="(10,0,0)", to="(0,0,0)", height=64, width=2, depth=64, caption='ConvRes'),
to_ConvSoftMax("ConvSoftMax",  s_filer=512,  offset="(15,0,0)", to="(0,0,0)", height=64, width=2, depth=64, caption='ConvSoftMax'),
to_Sum("sum", offset="(5,0,0)", to="(ConvSoftMax-east)", radius=2.5, opacity=0.6),...

2.5、tikzeng.py

我们来查看下tikzeng.py代码:

import osdef to_head( projectpath ):pathlayers = os.path.join( projectpath, 'layers/' ).replace('\\', '/')return r"""
\documentclass[border=8pt, multi, tikz]{standalone} 
\usepackage{import}
\subimport{"""+ pathlayers + r"""}{init}
\usetikzlibrary{positioning}
\usetikzlibrary{3d} %for including external image 
"""def to_cor():return r"""
\def\ConvColor{rgb:yellow,5;red,2.5;white,5}
\def\ConvReluColor{rgb:yellow,5;red,5;white,5}
\def\PoolColor{rgb:red,1;black,0.3}
\def\UnpoolColor{rgb:blue,2;green,1;black,0.3}
\def\FcColor{rgb:blue,5;red,2.5;white,5}
\def\FcReluColor{rgb:blue,5;red,5;white,4}
\def\SoftmaxColor{rgb:magenta,5;black,7}   
\def\SumColor{rgb:blue,5;green,15}
"""def to_begin():return r"""
\newcommand{\copymidarrow}{\tikz \draw[-Stealth,line width=0.8mm,draw={rgb:blue,4;red,1;green,1;black,3}] (-0.3,0) -- ++(0.3,0);}\begin{document}
\begin{tikzpicture}
\tikzstyle{connection}=[ultra thick,every node/.style={sloped,allow upside down},draw=\edgecolor,opacity=0.7]
\tikzstyle{copyconnection}=[ultra thick,every node/.style={sloped,allow upside down},draw={rgb:blue,4;red,1;green,1;black,3},opacity=0.7]
"""# layers definitiondef to_input( pathfile, to='(-3,0,0)', width=8, height=8, name="temp" ):return r"""
\node[canvas is zy plane at x=0] (""" + name + """) at """+ to +""" {\includegraphics[width="""+ str(width)+"cm"+""",height="""+ str(height)+"cm"+"""]{"""+ pathfile +"""}};
"""# Conv
def to_Conv( name, s_filer=256, n_filer=64, offset="(0,0,0)", to="(0,0,0)", width=1, height=40, depth=40, caption=" " ):return r"""
\pic[shift={"""+ offset +"""}] at """+ to +""" {Box={name=""" + name +""",caption="""+ caption +r""",xlabel={{"""+ str(n_filer) +""", }},zlabel="""+ str(s_filer) +""",fill=\ConvColor,height="""+ str(height) +""",width="""+ str(width) +""",depth="""+ str(depth) +"""}};
"""# Conv,Conv,relu
# Bottleneck
def to_ConvConvRelu( name, s_filer=256, n_filer=(64,64), offset="(0,0,0)", to="(0,0,0)", width=(2,2), height=40, depth=40, caption=" " ):return r"""
\pic[shift={ """+ offset +""" }] at """+ to +""" {RightBandedBox={name="""+ name +""",caption="""+ caption +""",xlabel={{ """+ str(n_filer[0]) +""", """+ str(n_filer[1]) +""" }},zlabel="""+ str(s_filer) +""",fill=\ConvColor,bandfill=\ConvReluColor,height="""+ str(height) +""",width={ """+ str(width[0]) +""" , """+ str(width[1]) +""" },depth="""+ str(depth) +"""}};
"""# Pool
def to_Pool(name, offset="(0,0,0)", to="(0,0,0)", width=1, height=32, depth=32, opacity=0.5, caption=" "):return r"""
\pic[shift={ """+ offset +""" }] at """+ to +""" {Box={name="""+name+""",caption="""+ caption +r""",fill=\PoolColor,opacity="""+ str(opacity) +""",height="""+ str(height) +""",width="""+ str(width) +""",depth="""+ str(depth) +"""}};
"""# unpool4, 
def to_UnPool(name, offset="(0,0,0)", to="(0,0,0)", width=1, height=32, depth=32, opacity=0.5, caption=" "):return r"""
\pic[shift={ """+ offset +""" }] at """+ to +""" {Box={name="""+ name +r""",caption="""+ caption +r""",fill=\UnpoolColor,opacity="""+ str(opacity) +""",height="""+ str(height) +""",width="""+ str(width) +""",depth="""+ str(depth) +"""}};
"""def to_ConvRes( name, s_filer=256, n_filer=64, offset="(0,0,0)", to="(0,0,0)", width=6, height=40, depth=40, opacity=0.2, caption=" " ):return r"""
\pic[shift={ """+ offset +""" }] at """+ to +""" {RightBandedBox={name="""+ name + """,caption="""+ caption + """,xlabel={{ """+ str(n_filer) + """, }},zlabel="""+ str(s_filer) +r""",fill={rgb:white,1;black,3},bandfill={rgb:white,1;black,2},opacity="""+ str(opacity) +""",height="""+ str(height) +""",width="""+ str(width) +""",depth="""+ str(depth) +"""}};
"""# ConvSoftMax
def to_ConvSoftMax( name, s_filer=40, offset="(0,0,0)", to="(0,0,0)", width=1, height=40, depth=40, caption=" " ):return r"""
\pic[shift={"""+ offset +"""}] at """+ to +""" {Box={name=""" + name +""",caption="""+ caption +""",zlabel="""+ str(s_filer) +""",fill=\SoftmaxColor,height="""+ str(height) +""",width="""+ str(width) +""",depth="""+ str(depth) +"""}};
"""# SoftMax
def to_SoftMax( name, s_filer=10, offset="(0,0,0)", to="(0,0,0)", width=1.5, height=3, depth=25, opacity=0.8, caption=" " ):return r"""
\pic[shift={"""+ offset +"""}] at """+ to +""" {Box={name=""" + name +""",caption="""+ caption +""",xlabel={{" ","dummy"}},zlabel="""+ str(s_filer) +""",fill=\SoftmaxColor,opacity="""+ str(opacity) +""",height="""+ str(height) +""",width="""+ str(width) +""",depth="""+ str(depth) +"""}};
"""def to_Sum( name, offset="(0,0,0)", to="(0,0,0)", radius=2.5, opacity=0.6):return r"""
\pic[shift={"""+ offset +"""}] at """+ to +""" {Ball={name=""" + name +""",fill=\SumColor,opacity="""+ str(opacity) +""",radius="""+ str(radius) +""",logo=$+$}};
"""def to_connection( of, to):return r"""
\draw [connection]  ("""+of+"""-east)    -- node {\midarrow} ("""+to+"""-west);
"""def to_skip( of, to, pos=1.25):return r"""
\path ("""+ of +"""-southeast) -- ("""+ of +"""-northeast) coordinate[pos="""+ str(pos) +"""] ("""+ of +"""-top) ;
\path ("""+ to +"""-south)  -- ("""+ to +"""-north)  coordinate[pos="""+ str(pos) +"""] ("""+ to +"""-top) ;
\draw [copyconnection]  ("""+of+"""-northeast)  
-- node {\copymidarrow}("""+of+"""-top)
-- node {\copymidarrow}("""+to+"""-top)
-- node {\copymidarrow} ("""+to+"""-north);
"""def to_end():return r"""
\end{tikzpicture}
\end{document}
"""def to_generate( arch, pathname="file.tex" ):with open(pathname, "w") as f: for c in arch:print(c)f.write( c )

从这些代码也可以看出,通过这些方法,返回的是Latex代码来进行绘制的。

 运行命令:bash ../tikzmake.sh tony   生成如图:

可以看到生成的可视化架构图,相比较于以前手工做图来说,真的大大提高了效率。更多详情可以去看具体源码。

github:PlotNeuralNet

相关文章:

介绍两款生成神经网络架构示意图的工具:NN-SVG和PlotNeuralNet

对于神经网络架构的可视化是很有意义的,可以在很大程度上帮助到我们清晰直观地了解到整个架构,我们在前面的 PyTorch的ONNX结合MNIST手写数字数据集的应用(.pth和.onnx的转换与onnx运行时) 有介绍,可以将模型架构文件(常见的格式都可以)在线上…...

iOS IdiotAVplayer实现视频分片缓存

文章目录 IdiotAVplayer 实现视频切片缓存一 iOS视频边下边播原理一 分片下载的实现1 分片下载的思路2 IdiotAVplayer 实现架构 三 IdiotAVplayer 代码解析IdiotPlayerIdiotResourceLoaderIdiotDownLoader IdiotAVplayer 实现视频切片缓存 一 iOS视频边下边播原理 初始化AVUR…...

SpringBootWeb请求-响应

HTTP请求 前后端分离 在这种模式下,前端技术人员基于"接口文档",开发前端程序;后端技术人员也基于"接口文档",开发后端程序。 由于前后端分离,对我们后端技术人员来讲,在开发过程中&a…...

List集合详解

目录 1、集合是什么? 1.1、集合与集合之间的关系 2、List集合的特点 3、遍历集合的三种方式 3.1、foreach(增强佛如循环遍历) 3.2、for循环遍历 3.3、迭代器遍历 4、LinkedList和ArrayList的区别 4.1、为什么ArrayList查询会快一些? 4.2、为什么LinkedLi…...

投稿指南【NO.12_8】【极易投中】核心期刊投稿(组合机床与自动化加工技术)

近期有不少同学咨询投稿期刊的问题,大部分院校的研究生都有发学术论文的要求,少部分要求高的甚至需要SCI或者多篇核心期刊论文才可以毕业,但是核心期刊要求论文质量高且审稿周期长,所以本博客梳理一些计算机特别是人工智能相关的期…...

解决git无法上传大文件(50MB)

解决方法 使用LFS解决GitHub无法上传大于50MB的文件 LFS简介 Git LFS(Large File Storage)是 Git 的一个扩展,用于管理大型文件,如二进制文件、图像、音频和视频文件等。它的主要目的是解决 Git 对大型二进制文件的版本控制和存…...

用递归实现字符串逆序(不使用库函数)

文章目录 前言一、题目要求二、解题步骤1.大概框架2.如何反向排列?3.模拟实现strlen4.实现反向排列5.递归实现反向排列 总结 前言 嗨,亲爱的读者们!我是艾老虎尤,今天,我们将探索一个题目,这个题目对新手非…...

初学python(一)

一、python的背景和前景 二、 python的一些小事项 1、在Java、C中,2 / 3 0,也就是整数 / 整数 整数,会把小数部分舍掉。而在python中2 / 3 0.66666.... 不会舍掉小数部分。 在编程语言中,浮点数遵循IEEE754标准,不…...

Excel VSTO开发8 -相关控件

版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。 8 相关控件 在VSTO开发中,Ribbon(或称为Ribbon UI)是指Office应用程序中的那个位于顶部的带有选…...

华为数据管理——《华为数据之道》

数据分析与开发 元数据是描述数据的数据,用于打破业务和IT之间的语言障碍,帮助业务更好地理解数据。 元数据是数据中台的重要的基础设施,元数据治理贯彻数据产生、加工、消费的全过程,沉淀了数据资产,搭建了技术和业务…...

Flink CDC 菜鸟教程 -环境篇

本教程将介绍如何使用 Flink CDC 来实现这个需求, 在 Flink SQL CLI 中进行,只涉及 SQL,无需一行 Java/Scala 代码,也无需安装 IDE。 系统的整体架构如下图所示: 环境篇 1、 准备一台Linux 2、准备教程所需要的组件 下载 flink-1.13.2 并将其解压至目录 flink-1.13.2 …...

【线上问题】linux部署docker应用docker-compose启动报端口占用问题(感觉上没有被占用)

目录 一、问题说明二、排查过程 一、问题说明 1.linux服务器使用的不是root用户权限 2.docker应用服务没有关闭的情况下,做了些重装docker,重启docker等操作 3.docker-compose up -d然后docker logs查看日志报端口被占用 4.netstat -ntpl | grep 端口 也…...

解决虚拟机克隆后IP和命名冲突问题

目录 解决IP冲突问题 解决命名冲突 解决IP冲突问题 克隆后的虚拟机和硬件地址和ip和我们原虚拟机的相同,我们需要重新生成硬件地址和定义ip,步骤如下: (1)进入 /etc/sysconfig/network-scripts/ifcfg-ens33 配置文件…...

分享一个python基于数据可视化的智慧社区服务平台源码

💕💕作者:计算机源码社 💕💕个人简介:本人七年开发经验,擅长Java、Python、PHP、.NET、Node.js、微信小程序、爬虫、大数据等,大家有这一块的问题可以一起交流! &#x1…...

[密码学入门]凯撒密码

单表代换 单表:英文26字母的顺序 代换:替换为别的字母并保证解密的唯一性 假如我们让加密方式为所有字母顺序移动3位 import stringstring.ascii_lowercase abcdefghijklmnopqrstuvwxyz b3 加密算法y(xb)mod26 解密算法为x(y-b)mod26 密钥空间26 …...

博客之QQ登录功能(一)

流程图 上图spring social 封装了1-8步需要的工作 1、新建包和书写配置文件 public class QQProperties {//App唯一标 识private String appId "100550231";private String appSecret "69b6ab57b22f3c2fe6a6149274e3295e";//QQ供应商private String…...

Redis多机数据库实现

Redis多机数据库实现 为《Redis设计与实现》笔记 复制 客户端可以使用SLAVEOF命令将指定服务器设置为该服务器的主服务器 127.0.0.1:12345> SLAVEOF 127.0.0.1 6379127.0.0.1:6379将被设置为127.0.0.1:123456的主服务器 旧版复制功能的实现 Redis的复制功能分为同步&a…...

Leangoo领歌 -敏捷任务管理软件,任务管理更轻松更透明

​任务管理,简单易懂,就是对任务进行管理。那怎么可以更好进行任务管理呢?怎么样样可以让任务进度可视化,一目了然呢?有效的管理可以让我们事半功倍。 接下来我们看一下如何借助任务管理软件高效的做任务管理。 首先…...

go的iris框架进行本地资源映射到服务端

我这里使用的是HandleDirapi,有其他的请补充 package mainimport ("github.com/kataras/iris/v12" )type Hello struct{Status int json:"status"Message string json:"message" }func main(){app : iris.New()//第一个api:相当于首页app.Get(&q…...

代码随想录day46|139. 单词拆分

139. 单词拆分 class Solution:def wordBreak(self, s: str, wordDict: List[str]) -> bool:dp [False]*(len(s)1)dp[0]Truefor i in range(len(s)1):for j in wordDict:if i>len(j) and (s[i-len(j):i] in wordDict) and dp[i-len(j)]:dp[i] Truereturn dp[len(s)]多…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

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

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

站群服务器的应用场景都有哪些?

站群服务器主要是为了多个网站的托管和管理所设计的&#xff0c;可以通过集中管理和高效资源的分配&#xff0c;来支持多个独立的网站同时运行&#xff0c;让每一个网站都可以分配到独立的IP地址&#xff0c;避免出现IP关联的风险&#xff0c;用户还可以通过控制面板进行管理功…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...