ML.Net - 开源的跨平台机器学习框架
- 支持CPU/GPU训练
- 轻松简洁的预测代码
- 可扩展其他的机器学习平台
- 跨平台
Visual Studio默认安装了Model Builder插件,可以很快地进行一些通用模型类型的训练和部署,提高接入机器学习的开发效率
通过非常简单地 右键项目-添加-机器学习模型
ModelBuilder中提供了集中常用的模型类型以供开发者使用,开发者可以通过这些类别的模型快速接入,并且训练自己的数据,本节内容将会使用计算机视觉中的”图像分类“进行演示
接下来要选择训练的环境,提供了CPU/GPU/Azure云三种方式训练,这里为了简单演示,我使用了CPU训练,如果数据量大且复杂的请选择GPU,并且提前安装CUDA、cuDNN
我从搜索引擎中,搜集到了一系列”奥特曼“的图片(我相信不是所有人都可以认出各个时代的各个奥特曼 哈哈哈)
然后将这些图片进行了文件夹分类,导入到ModelBuilder中,如下:
本次演示训练157张图片,耗时50秒
此环节,为了检验训练成果和准确率,ModelBuilder中提供了图形化的方式进行预测检测,我在另外的搜索引擎中,找到了一张没有经过训练的图片,它准确地判断出了”迪迦奥特曼“的概率为63%
这一环节中,ModelBuilder给出了示例代码,直接复制粘贴就可以用到自己的实际项目中
同时还提供了,一键生成控制台或者WebAPI项目的入口。给力!?
我新建了一个WPF项目,添加了一个Button,进行简单测试:
<Grid>
<Button Click="Button_Click" Content="预测一个奥特曼" />
</Grid>
private void Button_Click(object sender, RoutedEventArgs e) {
OpenFileDialog dialog= new OpenFileDialog();
if (dialog.ShowDialog().Value) {
//Load sample data
var imageBytes = File.ReadAllBytes(dialog.FileName);
UltraMan.ModelInput sampleData = new UltraMan.ModelInput() {
ImageSource = imageBytes,
};
//Load model and predict output
var result = UltraMan.Predict(sampleData);
if (result.Score.Any(s=>s>=0.6)) {
MessageBox.Show(result.PredictedLabel);
} else {
MessageBox.Show("没有识别到奥特曼");
}
}
}
选择一张图片,导入之后,即可弹出预测结果
ModelBuilder的操作非常简单,基本不需要了解机器学习的原理或者python,对一些有这些内置模型需求的.Net开发者很有帮助!~?
如果团队中有其他专业的AI人员进行模型训练和机器学习代码编写,如何将pytorch、tensenflow等框架训练的模型用在.Net中呢?
ML.Net在支持使用内置的ModelBuilder模型外,还支持使用onnx模型进行预测
- 开放式神经网络交换 (ONNX) 是一种用于表示机器学习模型的开放标准格式。ONNX 由合作伙伴社区提供支持,这些合作伙伴已在许多框架和工具中实现了它
- 开源的onnx模型系列下载(有很多第三方的优质onnx现成模型可供下载使用):onnx/models:ONNX 格式的预训练、最先进的模型集合 (github.com)
- ML模型仪表盘(可以查看模型详细推导流程和输入输出列) :Release WinML Dashboard v0.7.0 · microsoft/Windows-Machine-Learning (github.com)
这里需要提前介绍一下ML模型仪表盘
右侧的Inputs和Outputs在后续步骤中比较关键
进入github根据需求下载模型文件,本篇文章使用了【Emotion FERPlus】模型进行情绪预测
下载地址:github
根据github中的接入说明,理解输入、输出、预处理、预测、后处理等流程后,开始接入
输入:N*1*64*64 的float数组
表示可以预测多张(N)图片,且输入图片要是单色通道图(1),尺寸为64*64(需要缩放)
预处理:导入图片路径进行预测
python代码中,将图片导入,并进行了缩放处理,然后使用np.array把图片数据转为了float数组形式,最后把数组进行[1,1,64,64]的形状缩放,将rgb提取了单色数据
输出:1*8 的float数组
输出了一个8长度的一维数组,分别代表了8种表情的分数值,可能性最高的值为最终结果
使用ML.Net接入onnx前,需要安装几个nuget包:
- Microsoft.ML
- Microsoft.ML.ImageAnalytics
- Microsoft.ML.OnnxTransformer
?输入:
public class EmotionInput {
[ImageType(64,64)]
public MLImage Image { get; set; }
}
定义了一个输入类EmotionInput,标记图像为64*64,且类型为MLImage
?输出:
public class EmotionOutput {
[ColumnName("Plus692_Output_0")]
public float[] Result { get; set; }
}
根据ML Dashboard可以看到输出列名为Plus692_Output_0,类型为一维浮点数组
?开始预测:
public class EmotionPrediction {
private readonly string modelFile = "emotion-ferplus-8.onnx";
private string[] emotions = new string[] { "一般", "快乐", "惊讶", "伤心", "生气", "疑惑", "害怕", "蔑视" };
private PredictionEngine<EmotionInput, EmotionOutput> predictionEngine;
public EmotionPrediction()
{
MLContext context = new MLContext();
var emptyData = new List<EmotionInput>();
var data = context.Data.LoadFromEnumerable(emptyData);
var pipeline = context.Transforms.ResizeImages("resize", 64, 64, inputColumnName: nameof(EmotionInput.Image), Microsoft.ML.Transforms.Image.ImageResizingEstimator.ResizingKind.Fill).
Append(context.Transforms.ExtractPixels("Input3", "resize", Microsoft.ML.Transforms.Image.ImagePixelExtractingEstimator.ColorBits.Blue)).
Append(context.Transforms.ApplyOnnxModel(modelFile));
var model = pipeline.Fit(data);
predictionEngine = context.Model.CreatePredictionEngine<EmotionInput, EmotionOutput>(model);
}
public string Predict(string path) {
using (var stream = new FileStream(path, FileMode.Open)) {
using(var bitmap = MLImage.CreateFromStream(stream)) {
var result = predictionEngine.Predict(new EmotionInput() { Image = bitmap });
var max = result.Result.Max();
var index = result.Result.ToList().IndexOf(max);
return emotions[index];
}
}
}
}
其中预测部分,比较关键的地方是预测管道部分,从Input中拿到图片数据-->Resize-->提取图片的蓝色数据-->作为Input3输入列传入模型
这里使用蓝色作为提取色,是因为蓝色在色彩表示中较为明亮,计算机更容易识别这些像素和区域
?使用WPF接入试试看:
<Grid>
<Button Click="Button_Click" Content="预测表情" />
</Grid>
private EmotionPrediction prediction = new EmotionPrediction();
private void Button_Click(object sender, RoutedEventArgs e) {
OpenFileDialog dialog = new OpenFileDialog();
if (dialog.ShowDialog().Value) {
var result = prediction.Predict(dialog.FileName);
MessageBox.Show(result);
}
}
导入一张普通图片试试:
导入一张甜心美少女试试:
onnx模型的接入,使得ML.Net的可扩展性更高,不仅仅是内置模型,还可以更多~
接下来是一个稍微复杂一点的模型的接入方法
卷积神经网络中,人脸识别、车牌识别、物体识别很火热(比如有名的开源模型YOLO)
当然ModelBuilder已经内置物体识别模型,可以识别的物体在图中位置和矩形区域
进入github根据需求下载模型文件,本篇文章使用了【UltraFace】模型进行人脸识别
下载地址:github
通过描述,可以看出此模型会复杂一些。需要将数据进行BGR2RGB、偏移、归一化等预处理,也需要对预测结果进行非极大值抑制、矩形框变化处理
?输入:1*3*320*240
一张图片,进行缩放处理到320*240,且不要透明度
?预处理:BRG-RGB + Resize + 偏移 + 归一化 + 数据转chw
这里是opencv中的一系列图片处理的方法,为了匹配模型的输入且加快训练预测的速度
在深度学习中,神经网络模型的输入数据一般都需要经过一些预处理才能被正确地输入到模型中进行训练或者预测。下面是对各个预处理步骤的解释:
- BGR-RGB转换:在OpenCV中读取图像时,图像的通道顺序是BGR,而在深度学习中通常使用的是RGB格式。因此,需要对输入图像进行BGR-RGB通道转换。
- Resize:由于神经网络模型对输入图像的大小有一定的要求,因此在输入图像大小不符合要求时,需要进行图像的缩放操作。缩放操作有助于保留输入图像中的重要特征,并且可以减少训练和预测的时间和计算资源消耗。
- 偏移:在进行归一化操作前,先将图像每个像素点的值减去一个常数,这个常数一般是对训练数据集像素值取平均值。通过这个操作,可以将输入图像的像素值整体向左偏移一定的偏移量,使得整个像素值的范围更加平衡,便于模型的训练和优化。
- 归一化:在神经网络模型中,通过对输入数据进行归一化的操作,可以使得数据更加平滑,减少噪声和异常情况的影响。一般地,归一化会将数据的数值范围缩放到0到1之间或者-1到1之间(或其他固定范围内),这样有助于加快训练和提高模型的稳定性。
- 数据转置和重排:在深度学习框架中,输入数据的格式通常是(batch_size, channel, height, width),所以需要将预处理后的图像从(height, width, channel)的格式转化为(channel, height, width)的格式,并加上一维batch_size,以便于输入到网络中进行训练或者预测。
综上所述,这些预处理步骤是为了将图像处理成与模型输入相对应的格式,并且预处理后的图像可以减少噪声、保留重要特征、加快训练和提高模型的稳定性。
?输出:1*4420*2 和 1*4420*4的两个数组
分别代表了分数和矩形框的数据
?后处理:矩形框的转换+非极大值抑制
需要对输出的两个矩形进行处理,根据分数排列、根据非极大值抑制筛选出最确定的矩形框结果
在目标检测中,经常会出现多个检测框(bounding box)重叠覆盖同一目标的情况,而我们通常只需要保留一个最佳的检测结果。非极大值抑制(Non-Maximum Suppression,NMS)就是一种常见的目标检测算法,用于在冗余的检测框中筛选出最佳的一个。
NMS 原理是在对检测结果进行处理前,按照检测得分进行排序(一般检测得分越高,表明检测框越可能包含目标),然后选择得分最高的检测框加入结果中。接下来,遍历排序后的其余检测框,如果检测框之间的IoU(Intersection over Union,交并比)大于一定阈值,那么就将该检测框删除,因为被保留的那一个框已经足够表明目标的存在。
该过程不断迭代,直到所有框都被遍历完毕为止,从未删除的框中即为最终结果。由于 NMS 算法可以过滤掉重叠检测框中的冗余结果,因此在很多基于深度学习的目标检测算法(如 YOLO、SSD 等)中都被广泛使用。
== 以上作为了解,具体看下面代码 ==
public class RTFInput {
[ImageType(640, 480)]
public MLImage Image { get; set; }
}
public class RTFOutput {
[ColumnName("scores")]
[VectorType(1, 17640, 2)]
public float[] Scores { get; set; }
[ColumnName("boxes")]
[VectorType(1, 17640, 4)]
public float[] Boxes { get; set; }
}
public class RTFPrediction {
private readonly string modelFile = "version-RFB-640.onnx";
private PredictionEngine<RTFInput, RTFOutput> predictionEngine = null;
public RTFPrediction() {
MLContext context = new MLContext();
var emptyData = new List<RTFInput>();
var data = context.Data.LoadFromEnumerable(emptyData);
var pipeline =
context.Transforms.ResizeImages(
resizing: Microsoft.ML.Transforms.Image.ImageResizingEstimator.ResizingKind.Fill,//填充Resize
outputColumnName: "resize",//Resize的结果放置到 data列
imageWidth: 640,
imageHeight: 480,
inputColumnName: nameof(RTFInput.Image)//从Image属性来源,
)
.Append(
context.Transforms.ExtractPixels(
offsetImage: 127f,
scaleImage: 1 / 128f,
inputColumnName: "resize",
outputColumnName: "input")
).Append(
context.Transforms.ApplyOnnxModel(
modelFile: modelFile,
inputColumnNames: new string[] { "input" },
outputColumnNames: new string[] { "scores", "boxes" }));
var model = pipeline.Fit(data);
predictionEngine = context.Model.CreatePredictionEngine<RTFInput, RTFOutput>(model);//生成预测引擎
}
public ImageSource Predict(string path) {
using (var stream = new FileStream(path, FileMode.Open)) {
using (var bitmap = MLImage.CreateFromStream(stream)) {
var prediction = predictionEngine.Predict(new RTFInput() { Image = bitmap });
var boxes = ParseBox(prediction);
boxes = boxes.Where(b => b.Score > 0.9).OrderByDescending(s => s.Score).ToList();
boxes = HardNMS(boxes, 0.4);
var bitmapimage = new BitmapImage(new Uri(path));
var rtb = new RenderTargetBitmap(bitmap.Width, bitmap.Height, 96, 96, PixelFormats.Pbgra32);
int t = 0;
var dv = new DrawingVisual();
using (DrawingContext dc = dv.RenderOpen()) {
dc.DrawImage(bitmapimage, new Rect(0, 0, bitmap.Width, bitmap.Height));
foreach (var item in boxes) {
dc.DrawRectangle(null, new Pen(Brushes.Red, 2), new Rect(item.Rect.X * bitmap.Width, item.Rect.Y * bitmap.Height, item.Rect.Width * bitmap.Width, item.Rect.Height * bitmap.Height));
}
}
rtb.Render(dv);
return rtb;
}
}
}
private List<Box> ParseBox(RTFOutput prediction) {
var length = prediction.Boxes.Length / 4;
var boxes = Enumerable.Range(0, length).Select(i => new Box() {
X1 = prediction.Boxes[i * 4],
Y1 = prediction.Boxes[i * 4 + 1],
X2 = prediction.Boxes[i * 4 + 2],
Y2 = prediction.Boxes[i * 4 + 3],
Score = prediction.Scores[i * 2 + 1]
}
);
boxes = boxes.OrderByDescending(b => b.Score);
return boxes.ToList();
}
public List<Box> HardNMS(List<Box> boxes, double overlapThreshold) {
var selectedBoxes = new List<Box>();
while (boxes.Count > 0) {
// 取出置信度最高的bbox
var currentBox = boxes[0];
selectedBoxes.Add(currentBox);
// 计算当前bbox和其余bbox之间的IOU
boxes.RemoveAt(0);
for (int i = boxes.Count - 1; i >= 0; i--) {
var iou = CalculateIOU(currentBox, boxes[i]);
if (iou >= overlapThreshold) {
boxes.RemoveAt(i);
}
}
}
return selectedBoxes;
}
public double CalculateIOU(Box boxA, Box boxB) {
// 计算相交部分的坐标信息
float xOverlap = Math.Max(0, Math.Min(boxA.X2, boxB.X2) - Math.Max(boxA.X1, boxB.X1) + 1);
float yOverlap = Math.Max(0, Math.Min(boxA.Y2, boxB.Y2) - Math.Max(boxA.Y1, boxB.Y1) + 1);
// 计算相交部分的面积和并集部分的面积
float intersectionArea = xOverlap * yOverlap;
float unionArea = boxA.Area + boxB.Area - intersectionArea;
// 计算IoU
double iou = (double)intersectionArea / unionArea;
return iou;
}
}
Box的定义:
public class Box {
public float X1 { get; set; }
public float Y1 { get; set; }
public float X2 { get; set; }
public float Y2 { get; set; }
public float Score { get; set; }
// 计算面积
public float Area => (X2 - X1 + 1) * (Y2 - Y1 + 1);
private Rect GetRect() {
var hei = Y2 - Y1;
var wid = X2 - X1;
if (wid < 0) {
wid = 0;
}
if (hei < 0) {
hei = 0;
}
return new Rect(X1, Y1, wid, hei);
}
private Rect rect = Rect.Empty;
public Rect Rect {
get {
if (rect.IsEmpty) {
rect = GetRect();
}
return rect;
}
}
}
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition />
</Grid.RowDefinitions>
<Button Click="Button_Click" Content="识别人脸框" />
<Image x:Name="image" Grid.Row="1" />
</Grid>
private RTFPrediction prediction = new RTFPrediction();
private void Button_Click(object sender, RoutedEventArgs e) {
OpenFileDialog dialog = new OpenFileDialog();
if (dialog.ShowDialog().Value) {
var result = prediction.Predict(dialog.FileName);
if (result!=null) {
image.Source = result;
}
}
}
还是导入一张快乐美少女~
识别到了!!并且我在WPF中用DrawingContext为它绘制了红色矩形框。哎呀!应该用粉色!
再导入一张多人脸图试试?
完美~~
本文只描述了三种训练或者接入机器学习的方式,应该可以实现一部分机器学习的需求,本文只是作为一个.NET平台机器学习预测的例子,并没有将ML.Net和传统Pytorch等成熟框架进行比较,只是给出另外一种选择。
作为绝大多数.Net开发者,如果有现成的、不需要跨语言、上手成本低的机器学习框架可以用在现有业务中,当然没有必要再学习专业的人工智能技能了。
需要注意的是,ML.Net是符合.Net Standard2.0标准的,如果在.Net Framework中使用,需要注意版本>=4.6.1
本文只代表作者本人理解,如有出入,欢迎在评论区指出,不拉不踩,交流为主
本文中出现的代码已经上传至github : http://github.com/BigHeadDev/ML.Net.Demo
大家好,又见面了,我是全栈君。packageorg.rui.thread.newc; importjava.text.DateFormat; importjava.text.SimpleDateFormat; importjava.util.ArrayList; importjava.util.Calendar; importjava.util.Collections; importjava.util.List; importjava.util.Random; importjava.util.concurrent.ScheduledThreadPoolExecutor; importjava.util.concurrent.TimeUnit; /** *温室控制器 *@authorlenovo * */ publicclassGreenhouseScheduler { privatevolatilebooleanlight=false;//光 privatevolatilebooleanwater=false;//水 privateStringthermostat="
Ceph集群文档集群架构: 环境: 10.200.51.4admin、osd、mon作为管理和监控节点 10.200.51.9osd、mds 10.200.51.10osd、mds 10.200.51.113~client节点ceph1作为管理,osd.mon节点。前三台新增硬盘[root@ceph1~]#mkfs.xfs/dev/sdb meta-data=/dev/sdbisize=512agcount=4,agsize=1310720blks =sectsz=512attr=2,projid32bit=1 =crc=1finobt=0,sparse=0 data=bsize=4096blocks=5242880,imaxpct=25 =sunit=0swidth=0blks naming=version2bsize=4096ascii-ci=0ftype=1 log=internallogbsize=4096blocks=2560,version=2 =sectsz=512sunit=0blks,lazy-count=1 realtime=noneextsz=4096blocks
padmev0.0.1-Gameboy模拟器引擎padmev0.0.1-AGameboyemulatorenginePadme(PixelAsDot-MatrixEmulator,像素点阵模拟器),padme-core是一个Gameboy模拟器引擎。它本身不依赖libstd或动态内存,这使得它更容易在任何嵌入式平台或web组件中使用。 特性no_std计时器DMACPU反汇编程序带fifo的像素处理器单元外部屏幕外部串行端口操纵台Rom,MBC1,MBC3集成测试音频处理器单元TODO支持MBC2、MBC4、MBC5、MBC6、MBC7为每个模块添加单元测试项目地址:https://github.com/alexlren/padme-core在线模拟器:https://padme.ccpinging.net-一个通过用Rust编写的后端监控你的互联网连接的开源网站Pinging.net-Anopen-sourcewebsiteformonitoringyouinternetconnectionwithabackendwritteninRustpinging.net通过运行多个测试快速确
Python基本语法在介绍Python的语法之前,我们需要了解下计算机中的两种模式:1.交互模式在macos与ubuntu等系统中,我们通过使用terminal输入python进入python的交互模式。在windows下面,我们是通过cmd进入到交互模式,长得很像下面这个这样。Python3.7.0(default,Jun282018,08:04:48)[MSCv.191264bit(AMD64)]::Anaconda,Inc.onwin32 Type"help","copyright","credits"or"license"formoreinformation. >>> 复制在进入到Python环境之后,我们输入**exit()**并按回车,推出Python的交互环境,返回到命令行。Python3.7.0(default,Jun282018,08:04:48)[MSCv.191264bit(AMD64)]::Anaconda,Inc.onwin32 Type"help&qu
1、把小米路由器插上电源,台式电脑,用网线连接到小米路由器LAN1、LAN2中任意一个接口。如果是用手机、笔记本电脑来设置,可以搜索连接到小米路由器的WiFi信号,默认WiFi名称是:Xiaomi_XXXX_XXXX,如下图所示。2、在浏览器中输入:miwifi.com或者192.168.31.1并按下回车——在打开的界面中,点击“同意、继续”。3、在跳转的页面中,点击“中继工作模式(扩展现有的无线网络)”4、选择被中继的无线网络的名称——然后输入被中继无线网络的密码——点击“下一步”。5、设置“Wi-Fi名称”、“Wi-Fi密码”——点击“下一步”。温馨提示:请把这里的“Wi-Fi名称”、“Wi-Fi密码”,设置成与主路由器的WiFi名称、WiFi密码一致。6、“管理密码”就是小米路由器的登录密码,以后打开miwifi.com时,需要输入这里设置的管理密码,才能登录到设置界面的。可以直接设置一个“管理密码”。也可以勾选“与Wi-Fi密码相同”,也就是把WiFi密码作为管理密码,建议大家按照这样设置。7、如果你的小米路由器之前已经进行了相关的配置,现在想要用来中继某个WiFi信号,可以
关于用xshell远程连接系统自动断开问题的解决办法:1、服务器端的配置我们都知道,作为服务器,默认一般都是被动的等待客户端的连接到来。但对基于ssh协议的xshell的运用,总是出现自动断开的情况。vi命令打开/etc/ssh/sshd_config文件,可以看到:ClientAliveInterval用来指定服务器向客户端发送消息的时间间隔。默认是0,即不发送。ClientAliveCountMax用来指定服务器向客户端发送消息的次数。若到达指定的次数,客户端一次也没有回复,那么连接就要断开。因此可以对此进行相应的改变:ClientAliveInterval60//每隔60秒,服务器就要向客户端发送一次消息,客户端响应后,连接才会保持,否则,断开。ClientAliveCountMax3//可以使用默认值3注意:前面的“#”要去掉。重启sshd服务:servicesshdrestart或者/etc/init.d/sshdrestart2、客户端的配置KeepAlive修改。我的xshell的KeepAlive的默认Interval是60秒,即每隔60秒,客户端就要向服务器发送一次包
主从复制是为了加强系统数据库的可用性,当主库挂掉时,从数据库保存数据,数据不会丢失,将从库切换为主库,等主库弄好之后再替换回来,提高了项目的可用性。当然我们也可以读写分离等操作,提高系统的并发性。本博文只记录了如何进行主从复制的配置和过程中的一些问题的解决方法。原理:MySQL使用3个线程来执行复制功能(其中1个在【主服务器】上,另两个在【从服务器】上) 当【从服务器】发出STARTSLAVE时,【从服务器】创建一个I/O线程,以连接【主服务器】并让它发送记录在其二进制日志中的语句。 【主服务器】创建一个线程将二进制日志中的内容发送到【从服务器】。该线程可以识别为【主服务器】上SHOWPROCESSLIST的输出中的BinlogDump线程。 【从服务器】I/O线程读取主服务器BinlogDump线程发送的内容并将该数据拷贝到【从服务器】数据目录中的本地文件中,即中继日志。 第3个线程是SQL线程,是【从服务器】创建用于读取中继日志并执行日志中包含的更新一:安装环境操作系统:CentOS 数据库版本:MySQL5.7 主机A:192.168.1.1
作者:Derek简介Github地址:https://github.com/Bytom/bytomGitee地址:https://gitee.com/BytomBlockchain/bytom本章介绍bytom代码孤块管理作者使用MacOS操作系统,其他平台也大同小异GolangVersion:1.8孤块介绍什么是孤块当节点收到了一个有效的区块,而在现有的主链中却未找到它的父区块,那么这个区块被认为是“孤块”。父区块是指当前区块的PreviousBlockHash字段指向上一区块的hash值。接收到的孤块会被存储在孤块池中,直到它们的父区块被节点收到。一旦收到了父区块,节点就会将孤块从孤块池中取出,并且连接到它的父区块,让它作为区块链的一部分。孤块出现的原因当两个或多个区块在很短的时间间隔内被挖出来,节点有可能会以不同的顺序接收到它们,这个时候孤块现象就会出现。我们假设有三个高度分别为100、101、102的块,分别以102、101、100的颠倒顺序被节点接收。此时节点将102、101放入到孤块管理缓存池中,等待彼此的父块。当高度为100的区块被同步进来时,会被验证区块和交易,然后存储
在《中篇》中,我们对管道的构成以及它对请求的处理流程进行了详细介绍,接下来我们需要了解的是这样一个管道是如何被构建起来的。总的来说,管道由一个服务器和一个HttpApplication构成,前者负责监听请求并将接收的请求传递给给HttpApplication对象处理,后者则将请求处理任务委托给注册的中间件来完成。中间件的注册是通过ApplicationBuilder对象来完成的,所以我们先来了解一下这究竟是个怎样的对象。[源代码从这里下载]目录 一、ApplicationBuilder——用于注册中间件并创建管道 二、Startup——利用ApplicationBuilder注册中间件 三、作为宿主的WebHost和它的构建者一、ApplicationBuilder——用于注册中间件并创建管道我们所说的ApplicationBuilder是对所有实现了IApplicationBuilder接口的所有类型及其对象的统称。用于创建WebHost的WebHostBuilder具有一个用于管道定值的Configure方法,它利用作为参数的ApplicationBuilder对象进行中间件的注册
大家好,又见面了,我是你们的朋友全栈君。 首先第一步是安装该插件如图:File->setting–>plugins进入该页面,点击如图所示按钮.然后搜索Translation如图:我们需要的结果一般都不会排在前面,需要往下拉再找找,名字应当也是Translation,我这边已经安装了所以没有这个图标,找到Translation插件以后,就可以点击Install就可以安装这个插件了,安装完以后就可以重启IDEA,之后我们就可以配置快捷键重启完以后就会发现在在toolbar这边会多一个图标接下来我们绑定快捷键同样我们File->setting->keymap进入热键设置界面,在右侧输入框搜索Translation如图:可以通过右键选项来选择添加快捷还是移除快捷方式,我这边习惯用ALT+字母来触发标识1的界面标识2的界面此外还可以选择谷歌翻译还是有道翻译,我这里推荐使用谷歌翻译,有道翻译需要一个appid,虽然有公用id但是会有使用次数限制appid的获取方式这边就不列举了。可以在这两个地方来修改方式,当然也可以在增加一个快捷键来修改翻译方式,但是我觉着没有什么必要生
从指定位置截取字符串 “:3”表示从变量test的第4个字符开始,截取到变量值的末尾 “:-3”表示截取变量值的最后三个字符从指定位置截取,截取指定长度 “:2:3”表示从第三个字符开始,截取三个字符 “:-3:2”表示从倒数第三个字符开始,截取二个字符获取变量的长度 变量名前加一个#号,即可输出变量的长度。删除某个字符串左侧的所有字符 “#*3”表示删除字符串左边第一个3及左侧的所有字符,也可以把3替换成其他字符 “##*3”删除字符串从左往右最后一个3及左侧的所有字符删除某个字符串右侧的所有字符 “%7*”删除字符串中从右往左第一个7及右侧的所有字符,7也可以替换成其他字符 “%%4*”删除字符串中从右往左最一个4及右侧的所有字符替换变量中的字符串 把变量中的123替换为abc 替换变量中所有123为abc 分别替换行首的123,行尾的123
Eclipse中安装MemoryAnalyzer插件 一、简介 Eclipse作为JAVA非常好用的一款IDE,其自带的可扩展插件非常有利于JAVA程序员的工作效率提升。 MemoryAnalyzerTool(也叫MAT)是一款JAVA虚拟机内存映像分析工具,可以在JAVA程序运行的时候有程序抛出的异常加上已经设置好的参数(-XX:+HeapDumpOnOutOfMemory)调试出内存泄漏或者异常的位置以及原因跟踪,MemeoryAnalyzer可以对Dump出来的堆转储快照进行分析,重点是确认内存中的对象是否是有必要的,也就是要先确认到底是出现了内存泄漏(MemoryLeak)还是内存溢出(MemoryOverFlow)。 二、准备工作 1.EclipseIDE:我的是安装的EclipseNeon3,大部分版本Eclipse都支持插件安装。 名称:EclipseNeon 版本:3 下载地址:https://www.eclipse.org/downloads/packages/release/neon/3(我的是Windows64位的JavaEE版本) 2
NOIP2020游记 郑重声明 出于对他人名誉权和知识产权的尊重,本人在此郑重声明: 本文中提到的所有\(\operatorname{NOIP}\)都为「国家独立野餐奥林匹克竞赛(NationalOlympaidofIndependentPicnic)」的缩写,与独立社团法人\(\operatorname{CCF}^\circledR\)及其注册商标\(\operatorname{NOIP}^\circledR\)无任何关系! Day-1 全机房陷入ACSaber狂潮。 Day0 早晨睡到十点。 起来听了几遍退役的你,感觉贼差。 吃完早饭(?)又颓了一会,就去学校准备走了 开周子凯的飞机失败。周子凯又和周孟哲贴贴。 z(zk+mz)贴贴! 高速上看到一个牌子,说前面修路堵车,让下高速绕行,于是司机从天长(安徽的)下了高速,绕了一小时又从天长上去了,一小时无了 晚上到宾馆,出去绕了一圈没找到吃的,又回去点外卖。 晚上把算竞进指的图论又翻了一遍,最后准备看下字符串哈希(伏笔),发现十一点半了,就睡觉了。 听说周子凯晚上做了一晚上CF构造,第二天早晨起来狂翻字符串。 猜不对考点,说明我水平
Lab1Exercise10 为了能够更好的了解在x86上的C程序调用过程的细节,我们首先找到在obj/kern/kern.asm中test_backtrace子程序的地址,设置断点,并且探讨一下在内核启动后,这个程序被调用时发生了什么。对于这个循环嵌套调用的程序test_backtrace,它一共压入了多少信息到堆栈之中。并且它们都代表什么含义? 答: 先找到这个子程序的地址,打开obj/kern/kern.asm。在这个文件中我们查到调用test_backtrace子程序指令的地址为0xf0100040. 所以我们在这里设置断点,并且开始调试。 首先看一下test_backtrace的C语言形式: void test_backtrace(intx) { cprintf("enteringtest_backtrace%d\n",x); if(x>0) test_backtrace(x-1); else mon_backtrace(0,0,0); cprintf("leavingtest_backtrace%d\n",x); }复制  
一、Zabbix简介zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。zabbix能监视各种网络参数,保证服务器系统的安全运营;并提供灵活的通知机制以让系统管理员快速定位/解决存在的各种问题。二、Zabbix安装本文档在同一台机器上安装了zabbix-server和zabbix-agent自己监控自己。1、配置LAMPCentos7.0配置LAMPInstallLAMPServer(Apache,MariaDB,PHP)OnCentOS/RHEL/ScientificLinux72、下载zabbixWgethttp://netix.dl.sourceforge.net/project/zabbix/ZABBIX%20Latest%20Stable/2.4.7/zabbix-2.4.7.tar.gz复制 3、安装所需要的基础组件 yuminstall-ycurlcurl-develmydql-develnet-snmpsnmpnet-snmp-develperl-DBIphp-gdphp-xmlphp-bcmathphp-mbstring
核心思想:套娃 啥意思呢?这玩意就像套娃一样,从上往下扒,拿走一个还有一个,再拿走一个,诶还有一个,如果你愿意,可以一直扒到最底下没有了为止。 基本用法 1.创建一个被包装的error 方式一:fmt.Errorf 使用 %w 参数返回一个被包装的error err1:=errors.New("newerror") err2:=fmt.Errorf("err2:[%w]",err1) err3:=fmt.Errorf("err3:[%w]",err2) fmt.Println(err3)复制 //output err3:[err2:[newerror]]复制 err2 就是一个合法的被包装的error,同样地,err3 也是一个被包装的error,如此可以一直套下去。 方式二:自定义struct typeWarpErrorstruct{ msgstring errerror } func(e*WarpError)Error()string{ returne.msg } func(e*WrapError)Unwrap()error{ retur
最近要用到通过post上传文件,网上盛传的有curl的post提交和fsockopen,其中curl最简单,于是从最简单的说起。 这是简单的将一个变量post到另外一个页面 $url='';$data=array('a'=>'b');$ch=curl_init();curl_setopt($ch,CURLOPT_URL,$url);curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);curl_setopt($ch,CURLOPT_POST,true);curl_setopt($ch,CURLOPT_POSTFIELDS,$data);$ret=curl_exec($ch);curl_close($ch);主要说下这个选项CURLOPT_RETURNTRANSFER:如果设置为true/1,则curl_exec的时候不会自动将请求网页的内容输出到屏幕,$ret为请求网页的内容,如果设置为false/0,则curl_exec的时候会自动将请求网页的内容输出到屏幕,此时如果请求成功的话$ret的内容是1或者true。 下面是上传本地文件的代码,如果需要上
2021.11.280:21 深夜了,突然想写点什么。 刚刚在知乎上无意刷到某个高中联考题。 以看回答的人好像还挺厉害,可以说出“没上145”是失误,只能拿到“刚上140的中档分数”,感觉挺厉害的。 提到了和石门联考,虽然不知道是哪个学校的,有点在意,点进去主页看了一下。 是个退役OIer?,还有blog,点进去看看。 翻了翻他的CSP2020游记和WC2021游记,感觉水平和我差不多吧,点进他的高考计划看了一下。 每科都是级前十,好厉害。最后又去看了看他有没有其他游记,发现没有了。注意到原来是高一就退役的,现在才高二。 一时不知道说什么。 感觉他的自律和生活状态都挺厉害的。 但我也希望仅仅我也只是觉得厉害。 承认别人比自己强并不是一件很难的事情,我常常对自己这样说。 而事实也确实是这样,但是对自己摆烂的学习状态,是不是有点反思呢。 我从来不认为卷是件好事,所以也从来不和别人卷。 忙里偷闲看动画也确实是我的爱好,但我还是希望我时时刻刻做自己想做的事情。 不因为老师的几句话而狂卷文化课,但是也不因为就要跟别人对着干这样没什么脑子的想法就违心。 希望自己活的更加自在吧。 换了同桌。真的,突
比赛链接:[第66场周赛](竞赛-AcWing) 先放代码,题解慢慢补 AAcWing4606.奇偶判断 #include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<ctime> #include<bitset> #include<vector> #include<cstdio> #include<complex> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> usingnamespacestd; typedeflonglongLL; typedeflongdoubleLD; typedefunsignedlonglongULL; intmain() { stringstr; cin>>str;