PyTorch框架学习十九——模型加载与保存

PyTorch框架学习十九——模型加载与保存

  • 一、序列化与反序列化
  • 二、PyTorch中的序列化与反序列化
    • 1.torch.save
    • 2.torch.load
  • 三、模型的保存
    • 1.方法一:保存整个Module
    • 2.方法二:仅保存模型参数
  • 四、模型的加载
    • 1.加载整个模型
    • 2.仅加载模型参数
  • 五、断点续训练
    • 1.断点续训练的模型保存
    • 2.断点续训练的模型加载

距离上次的学习笔记时隔了正好一个月。。。下面继续!

其实深度学习的五大步骤——数据、模型、损失函数、优化器和迭代训练,这些用PyTorch的基本实现前面的笔记都已经涉及,最后的几次笔记是一些实用的技巧,PyTorch框架的基础学习就将告一段落,后面开始Paper的学习,有精力的话也一定会更新。

一、序列化与反序列化

这里先介绍一下内存与硬盘的区别,这两种存储设备最重要的区别是内存的内容不会永久保存,在程序运行结束或是断电等情况下,数据会全部丢失,而在硬盘里的数据则不受上述的影响。所以我们有必要将一些值得长久保存的东西,比如训练好的模型、关键的数据等从内存中转存到硬盘中,下次使用的时候再从硬盘中读取到内存。

  • 序列化:将数据从内存转存到硬盘。
  • 反序列化:将数据从硬盘转存到内存。

具体见下图所示:
在这里插入图片描述
序列化与反序列化的主要目的是:可以使得数据/模型可以长久地保存。

二、PyTorch中的序列化与反序列化

1.torch.save

torch.save(obj, f, pickle_module=<module 'pickle' from '/opt/conda/lib/python3.6/pickle.py'>, pickle_protocol=2, _use_new_zipfile_serialization=True)

参数如下所示:
在这里插入图片描述
主要需要关注两个:

  • obj:需要保存的对象。
  • f:保存的路径。

2.torch.load

torch.load(f, map_location=None, pickle_module=<module 'pickle' from '/opt/conda/lib/python3.6/pickle.py'>, **pickle_load_args)

参数如下所示:
在这里插入图片描述
主要需要关注前两个:

  • f:加载文件的路径。
  • map_location:指定存放的位置,cpu或gpu。

三、模型的保存

1.方法一:保存整个Module

torch.save(net, path)

下面构建一个简单的网络模型作为例子:

import torch
import numpy as np
import torch.nn as nn# 构建一个LeNet网络模型,并实例化为net
class LeNet2(nn.Module):def __init__(self, classes):super(LeNet2, self).__init__()self.features = nn.Sequential(nn.Conv2d(3, 6, 5),nn.ReLU(),nn.MaxPool2d(2, 2),nn.Conv2d(6, 16, 5),nn.ReLU(),nn.MaxPool2d(2, 2))self.classifier = nn.Sequential(nn.Linear(16*5*5, 120),nn.ReLU(),nn.Linear(120, 84),nn.ReLU(),nn.Linear(84, classes))def forward(self, x):x = self.features(x)x = x.view(x.size()[0], -1)x = self.classifier(x)return xdef initialize(self):for p in self.parameters():p.data.fill_(20191104)net = LeNet2(classes=2019)

忽略训练过程,真实的训练不应该这样。。。,这里为了简单,只是人为地改变了一下参数权重,并不是训练得到的结果。

# "训练"
print("训练前: ", net.features[0].weight[0, ...])
net.initialize()
print("训练后: ", net.features[0].weight[0, ...])

(虚假)结果:

训练前:  tensor([[[-0.0065, -0.0388,  0.0194, -0.0944, -0.0539],[-0.0981,  0.0611, -0.0066, -0.0140,  0.0849],[ 0.0495, -0.0793, -0.0424,  0.0282,  0.0338],[ 0.0523, -0.0409, -0.1071, -0.0623,  0.0956],[-0.0385,  0.0554,  0.0160,  0.0671,  0.0913]],[[ 0.0409,  0.0811, -0.0677,  0.1040, -0.0236],[-0.0717, -0.0418,  0.0007,  0.0950, -0.0309],[-0.0089,  0.0140, -0.0855,  0.0818, -0.0270],[ 0.0316, -0.0637,  0.0093,  0.0669, -0.1031],[-0.0337,  0.1068, -0.0927, -0.1069,  0.0267]],[[-0.0034,  0.0862,  0.0743, -0.0082,  0.0929],[-0.0738,  0.0781, -0.0701, -0.0327, -0.0553],[-0.0951, -0.0604, -0.0906,  0.0169,  0.0084],[ 0.0589,  0.0391,  0.0493,  0.1018,  0.0563],[ 0.0407, -0.0053,  0.0307,  0.0077,  0.0262]]],grad_fn=<SelectBackward>)
训练后:  tensor([[[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.]],[[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.]],[[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.]]],grad_fn=<SelectBackward>)

好,通过上面的偷懒操作,视为我们训练好了模型,现在需要保存这个模型,方法一是保存整个模型,如下图所示:

path_model = "./model.pkl"
torch.save(net, path_model)

此时查看当前目录,多了一个model.pkl的文件,这个就是保存的模型文件:
在这里插入图片描述
这是一个简单的模型,如果模型比较庞大的话,这种方法会有一个问题,就是保存耗时较长,占用内存也较大,所以一般不这样保存。

2.方法二:仅保存模型参数

我们回顾一下模型的内容,一个模型它是有很多部分组成的,就像刚刚构建的LeNet,其模型所包含的内容就如下所示:
在这里插入图片描述
而我们可以观察一下,在模型的训练过程中,只有参数_parameters是不断被更新的,那么我们可以在保存的时候仅保存模型参数,其他的部分在用的时候可以重新构建,然后将参数加载到新构建的模型上就等同于是保存了整个模型,这样不仅可以节省保存的时间也能减小内存加载的消耗。

这种方法的保存代码为:

net_state_dict = net.state_dict()
torch.save(net_state_dict, path)

这里使用了state_dict()函数,返回了一个字典,包含了这个模型的所有参数。

还是以刚刚的LeNet为例,只需将保存的代码改为:

path_state_dict = "./model_state_dict.pkl"
# 保存模型参数
net_state_dict = net.state_dict()
torch.save(net_state_dict, path_state_dict)

即可。

可以通过单步调试查看net_state_dict 的内容:
在这里插入图片描述
它的确包含了每一层的权重和偏置,而在当前目录下多了一个名为model_state_dict.pkl的保存文件。
在这里插入图片描述

四、模型的加载

这个都是和三中的两种方法对应,保存的时候是整个模型就加载整个模型,保存的时候是仅保存模型参数就先构建模型然后加载模型参数。

1.加载整个模型

import torch
import numpy as np
import torch.nn as nnclass LeNet2(nn.Module):def __init__(self, classes):super(LeNet2, self).__init__()self.features = nn.Sequential(nn.Conv2d(3, 6, 5),nn.ReLU(),nn.MaxPool2d(2, 2),nn.Conv2d(6, 16, 5),nn.ReLU(),nn.MaxPool2d(2, 2))self.classifier = nn.Sequential(nn.Linear(16*5*5, 120),nn.ReLU(),nn.Linear(120, 84),nn.ReLU(),nn.Linear(84, classes))def forward(self, x):x = self.features(x)x = x.view(x.size()[0], -1)x = self.classifier(x)return xdef initialize(self):for p in self.parameters():p.data.fill_(20191104)# ================================== load net ===========================
flag = 1
# flag = 0
if flag:path_model = "./model.pkl"net_load = torch.load(path_model)print(net_load)

输出:

LeNet2((features): Sequential((0): Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1))(1): ReLU()(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(3): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))(4): ReLU()(5): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False))(classifier): Sequential((0): Linear(in_features=400, out_features=120, bias=True)(1): ReLU()(2): Linear(in_features=120, out_features=84, bias=True)(3): ReLU()(4): Linear(in_features=84, out_features=2019, bias=True))
)

注意:在加载之前一定要先定义模型LeNet,定义必须和保存时的一致,不然会对应不上。

2.仅加载模型参数

这种方法需要比前一种方法多一步:实例化模型。即先得有一个模型才能对它加载参数。

# ================================== load state_dict ===========================flag = 1
# flag = 0
if flag:# 加载参数到内存,并查看keyspath_state_dict = "./model_state_dict.pkl"state_dict_load = torch.load(path_state_dict)print(state_dict_load.keys())# 实例化模型net_new = LeNet2(classes=2019)# 更新参数到模型,等同于加载整个模型print("加载前: ", net_new.features[0].weight[0, ...])net_new.load_state_dict(state_dict_load)print("加载后: ", net_new.features[0].weight[0, ...])

输出:

odict_keys(['features.0.weight', 'features.0.bias', 'features.3.weight', 'features.3.bias', 'classifier.0.weight', 'classifier.0.bias', 'classifier.2.weight', 'classifier.2.bias', 'classifier.4.weight', 'classifier.4.bias'])
加载前:  tensor([[[ 0.1004, -0.0658, -0.0483, -0.0398,  0.0197],[ 0.0157,  0.0474, -0.0996, -0.0778, -0.0177],[-0.0889, -0.0732, -0.0625,  0.1042,  0.1082],[ 0.0360, -0.0571,  0.0523, -0.0154, -0.0241],[ 0.0427,  0.0968,  0.0166,  0.0405, -0.0782]],[[ 0.0610,  0.0467,  0.0285,  0.0061,  0.0146],[-0.0410,  0.0822, -0.0535,  0.0842, -0.0443],[ 0.1010,  0.0972, -0.0625, -0.1114,  0.0899],[ 0.0462, -0.0121,  0.0476,  0.0673,  0.0900],[ 0.1030, -0.0278,  0.0374, -0.0098, -0.0929]],[[ 0.0703,  0.0600, -0.0538,  0.0179, -0.0008],[ 0.1119,  0.0187,  0.0359, -0.0721,  0.0033],[-0.0225,  0.0788, -0.1081,  0.0700,  0.0269],[ 0.0519, -0.0959, -0.0329, -0.0520, -0.0741],[ 0.1020,  0.0321,  0.0549, -0.0522, -0.0783]]],grad_fn=<SelectBackward>)
加载后:  tensor([[[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.]],[[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.]],[[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.],[20191104., 20191104., 20191104., 20191104., 20191104.]]],grad_fn=<SelectBackward>)

五、断点续训练

这是一个非常实用的技巧,设想你的一个模型训练了五天,然后因为断电等不可控原因突然中止,是不是非常抓狂。

断点续训练的思想就是,在训练的过程中,每隔一定数量的epoch就保存一次模型(参数),若程序中断,可以从离中断最近位置保存的文件中加载模型(参数)继续训练

1.断点续训练的模型保存

在训练过程中,只有模型与优化器中会有与训练相关的数据,比如模型的参数权重、优化器中的学习率等等,以及还需要记录一下保存时的epoch,所以我们可以构建这样一个字典,存放刚刚所说的三类数据:

checkpoint = {"model_state_dict": net.state_dict(),"optimizer_state_dict": optimizer.state_dict(),"epoch": epoch}

将这个checkpoint保存为pkl文件即可。

if (epoch+1) % checkpoint_interval == 0:checkpoint = {"model_state_dict": net.state_dict(),"optimizer_state_dict": optimizer.state_dict(),"epoch": epoch}path_checkpoint = "./checkpoint_{}_epoch.pkl".format(epoch)torch.save(checkpoint, path_checkpoint)

其中checkpoint_interval 就是每隔多少个epoch保存一次上述信息。

下面我们人为构建一个中断,模型、损失函数、优化器、迭代训练部分的代码省略:

if epoch > 5:print("训练意外中断...")break

结果如下所示:

Training:Epoch[000/010] Iteration[010/012] Loss: 0.6753 Acc:53.75%
Valid:	 Epoch[000/010] Iteration[003/003] Loss: 0.4504 Acc:57.37%
Training:Epoch[001/010] Iteration[010/012] Loss: 0.3928 Acc:83.75%
Valid:	 Epoch[001/010] Iteration[003/003] Loss: 0.0130 Acc:85.79%
Training:Epoch[002/010] Iteration[010/012] Loss: 0.1717 Acc:93.12%
Valid:	 Epoch[002/010] Iteration[003/003] Loss: 0.0027 Acc:92.11%
Training:Epoch[003/010] Iteration[010/012] Loss: 0.1100 Acc:95.62%
Valid:	 Epoch[003/010] Iteration[003/003] Loss: 0.0003 Acc:96.32%
Training:Epoch[004/010] Iteration[010/012] Loss: 0.0321 Acc:98.12%
Valid:	 Epoch[004/010] Iteration[003/003] Loss: 0.0000 Acc:98.42%
Training:Epoch[005/010] Iteration[010/012] Loss: 0.0064 Acc:100.00%
Valid:	 Epoch[005/010] Iteration[003/003] Loss: 0.0000 Acc:100.00%
Training:Epoch[006/010] Iteration[010/012] Loss: 0.0109 Acc:99.38%
训练意外中断...

在这里插入图片描述

当前目录下多了一个pkl文件,其中的4是指保存的第五轮训练后的结果:
在这里插入图片描述

2.断点续训练的模型加载

与正常训练差不多,只是需要在迭代训练之前把上述保存的三种信息加载给模型,使之恢复到断点的状态:

path_checkpoint = "./checkpoint_4_epoch.pkl"
checkpoint = torch.load(path_checkpoint)
# 模型参数更新
net.load_state_dict(checkpoint['model_state_dict'])
# 优化器参数更新
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
# epoch数恢复到断点
start_epoch = checkpoint['epoch']
scheduler.last_epoch = start_epoch

结果如下所示:

Training:Epoch[005/010] Iteration[010/012] Loss: 0.0146 Acc:99.38%
Valid:	 Epoch[005/010] Iteration[003/003] Loss: 0.0000 Acc:99.47%
Training:Epoch[006/010] Iteration[010/012] Loss: 0.0039 Acc:100.00%
Valid:	 Epoch[006/010] Iteration[003/003] Loss: 0.0000 Acc:100.00%
Training:Epoch[007/010] Iteration[010/012] Loss: 0.0013 Acc:100.00%
Valid:	 Epoch[007/010] Iteration[003/003] Loss: 0.0000 Acc:100.00%
Training:Epoch[008/010] Iteration[010/012] Loss: 0.0020 Acc:100.00%
Valid:	 Epoch[008/010] Iteration[003/003] Loss: 0.0000 Acc:100.00%
Training:Epoch[009/010] Iteration[010/012] Loss: 0.0010 Acc:100.00%
Valid:	 Epoch[009/010] Iteration[003/003] Loss: 0.0000 Acc:100.00%

可见,epoch继续从5开始,准确率一开始就很高,是紧接着前5次的结果的,再观察损失函数的变化,初始值就很小了:
在这里插入图片描述
综上,这样的方法,可以使得因特殊原因中途中断的训练从中断位置继续训练而不用从头开始,非常有用。

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

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

相关文章

新计算推动信息技术产业新发展?

来源&#xff1a;工信头条未来智能实验室是人工智能学家与科学院相关机构联合成立的人工智能&#xff0c;互联网和脑科学交叉研究机构。未来智能实验室的主要工作包括&#xff1a;建立AI智能系统智商评测体系&#xff0c;开展世界人工智能智商评测&#xff1b;开展互联网&#…

保存tensorboard的损失曲线为图片

损失loss一般是标量&#xff0c;损失曲线一般显示在TensorBoard的SCALARS下&#xff0c;如图所示&#xff1a; 如果想将损失曲线保存下来&#xff0c;选中左边“Show data download links”按钮&#xff0c;曲线下面就会有一个下载按钮&#xff0c;但是只能保存为SVG文件&#…

美国服务机器人技术路线图

来源&#xff1a;美国国家科学基金会服务机器人正在以高速的增长速度加速步入我们的日常生活。正是基于广阔的市场前景&#xff0c;美国国家科学基金会颁布了《美国机器人技术路线图》&#xff0c;其中服务机器人是其中的重点一章。服务机器人的主要应用领域服务机器人是一类用…

OpenCV与图像处理学习一——图像基础知识、读入、显示、保存图像、灰度转化、通道分离与合并

OpenCV与图像处理学习一——图像基础知识、读入、显示、保存图像、灰度转化、通道分离与合并一、图像基础知识1.1 数字图像的概念1.2 数字图像的应用1.3 OpenCV介绍二、图像属性2.1 图像格式2.2 图像尺寸2.2.1 读入图像2.2.2 显示图像2.2.3 保存图像2.3 图像分辨率和通道数2.3.…

【Docker】Docker学习笔记:安装部署

Docker 是实现轻量级的操作系统虚拟化解决方案。 Docker目前已经支持非常多的Linux平台&#xff0c;Ubuntu、Red Hat企业版Linux、Debian、CentOs、Fedora、Oracle Linux等。如果使用虚拟环境&#xff0c;甚至可以在OS X 和 windows中运行。 安装的条件 Docker目前只能在64位CP…

定了!5G商用牌照近期发放​​​​,透露两大信息(附:2019年5G行业关键材料及市场研究报告)...

来源&#xff1a;世界科技创新论坛据工信微报和新华社消息&#xff1a;日前&#xff0c;全球5G正在进入商用部署的关键期。坚持自主创新与开放合作相结合&#xff0c;我国5G产业已建立竞争优势。5G标准是全球产业界共同参与制定的统一国际标准&#xff0c;我国声明的标准必要专…

OpenCV与图像处理学习二——图像直方图与色彩空间

OpenCV与图像处理学习二——图像直方图与色彩空间2.4 图像直方图&#xff08;Image Histogram&#xff09;2.4.1 直方图的绘制2.4.2 三通道直方图绘制2.5 颜色空间2.5.1 RGB颜色空间2.5.2 HSV颜色空间&#xff08;Hue、Saturation、Value&#xff09;2.5.3 HSI2.5.4 CMYK&#…

JavaScript基础5——关于ECMAscript的函数

ECMAScript的函数概述(一般定义到<head>标签之间)(1)定义函数&#xff0c;JavaScript一般有三种定义函数方法&#xff1a; *第一种是使用function语句定义函数&#xff08;静态方法&#xff09; 1 function 函数名(var1,var2,...,varX) 2 { 3 4 代码&…

中国科学家首次观察到量子世界的宇称时间对称

来源&#xff1a;中国新闻网 中国科学家调控量子跳双人舞 首次观察到量子世界的宇称时间对称中新社合肥6月3日电 (吴兰 范琼)记者3日从中国科学技术大学获悉&#xff0c;该校杜江峰院士的研究团队通过调控量子跳出双人舞&#xff0c;在国际上首次观察到量子世界的宇称时间对称。…

OpenCV与图像处理学习三——线段、矩形、圆、椭圆、多边形的绘制以及文字的添加

OpenCV与图像处理学习三——线段、矩形、圆、椭圆、多边形的绘制以及文字的添加一、OpenCV中的绘图函数1.1 线段绘制1.2 矩形绘制1.3 圆绘制1.4 椭圆的绘制1.5 多边形绘制1.6 添加文字上两次笔记主要知识点回顾&#xff1a; 数字图像基本概念图像的读取、显示与保存图像直方图…

AI英雄 | 论人工智能与自由意志,请看尤瓦尔与李飞飞的这场“激辩”

来源&#xff1a;Towards Data Science尤瓦尔赫拉利和李飞飞在斯坦福大学展开了一场别开生面的对话&#xff0c;他们所提出的问题已经远远超出了我们可以解答的范围。《连线》杂志主编尼古拉斯•汤普森在座无虚席的纪念礼堂主持了这场90分钟的谈话。赫拉利&#xff08;Harari&a…

OpenCV与图像处理学习四——图像几何变换:平移、缩放、旋转、仿射变换与透视变换

OpenCV与图像处理学习四——图像几何变换&#xff1a;平移、缩放、旋转、仿射变换与透视变换二、图像的几何变换2.1 图像平移2.2 图像缩放&#xff08;上采样与下采样&#xff09;2.3 图像旋转2.4 仿射变换2.5 透视变化2.6 几何变化小结续上次的笔记&#xff1a;OpenCV与图像处…

课后作业和动手动脑

一&#xff0c;运行TestInherits.java 通过super调用基类构造方法&#xff0c;必是子类构造方法中的第一个语句。 二.为什么子类的构造方法在运行之前&#xff0c;必须调用父类的构造方法&#xff1f;能不能反过来&#xff1f;为什么不能反过来&#xff1f; 构造函数的主要作用…

OpenCV与图像处理学习五——图像滤波与增强:线性、非线性滤波、直方图均衡化与Gamma变换

OpenCV与图像处理学习五——图像滤波与增强&#xff1a;线性、非线性滤波、直方图均衡化与Gamma变换三、图像滤波与增强3.1 线性滤波3.1.1 方框滤波3.1.2 均值滤波3.1.3 高斯滤波3.1.4 一般卷积滤波3.2 非线性滤波3.2.1 中值滤波3.2.2 双边滤波3.3 图像直方图均衡化3.3.1 单通道…

张钹院士:人工智能技术已进入第三代

来源&#xff1a;经济观察报近日&#xff0c;中科院院士、清华大学人工智能研究院院长张钹教授接受记者采访时认为&#xff0c;目前基于深度学习的人工智能在技术上已经触及天花板。从长远来看&#xff0c;必须得走人类智能这条路&#xff0c;最终要发展人机协同&#xff0c;人…

软件工程作业

典型用户1 名字老陈性别&#xff0c;年龄男&#xff0c;40岁职业教师收入两万/年知识层次和能力本科&#xff0c;熟练计算机操作生活、工作情况教书&#xff0c;辅导孩子完成作业动机&#xff0c;目的&#xff0c;困难希望节省辅导孩子的时间&#xff0c;用于自己的业务工作用户…

numpy.ndarray索引/切片方式

注意&#xff1a;获得多维数组的前三个子数组不能用array[0,1,2]&#xff0c;应该用 array[0:3]&#xff0c;如下例子&#xff1a; a np.random.random([85, 7794, 64]) b a[0:3] print(np.shape(b)) # (3, 7794, 64)

OpenCV与图像处理学习六——图像形态学操作:腐蚀、膨胀、开、闭运算、形态学梯度、顶帽和黑帽

OpenCV与图像处理学习六——图像形态学操作&#xff1a;腐蚀、膨胀、开、闭运算、形态学梯度、顶帽和黑帽四、图像形态学操作4.1 腐蚀和膨胀4.1.1 图像腐蚀4.1.2 图像膨胀4.2 开运算与闭运算4.2.1 开运算4.2.2 闭运算4.3 形态学梯度&#xff08;Gradient&#xff09;4.4顶帽和黑…

解密硅谷大骗局

来源&#xff1a;硅谷封面企鹅号、腾讯科技在许多为人称道的科技创业故事中&#xff0c;总不乏硅谷的名字。从英特尔、IBM到微软、苹果&#xff0c;从雅虎、谷歌到Twitter、Facebook&#xff0c;这里诞生了很多知名科技企业。对于全球的创业者来说&#xff0c;硅谷就是梦想中的…

OpenCV与图像处理学习七——传统图像分割之阈值法(固定阈值、自适应阈值、大津阈值)

OpenCV与图像处理学习七——传统图像分割之阈值法&#xff08;固定阈值、自适应阈值、大津阈值&#xff09;一、固定阈值图像分割1.1 直方图双峰法1.2 OpenCV中的固定阈值分割二、自动阈值图像分割2.1 自适应阈值法2.2 迭代法阈值分割2.3 Otsu大津阈值法前面的笔记介绍了一些Op…