GoogLeNet网络模型

article/2025/6/23 9:43:07

GoogLeNet网络模型

诞生背景

在2014年的ImageNet图像识别挑战赛中,一个GoogLeNet的网络架构大放异彩,与VGG不同的是,VGG用的是3*3的卷积,而GoogLeNet从1*1到7*7的卷积核都用,也就是使用不同大小的卷积核组合。

网络模型架构

在GoogLeNet中,基本的卷积块被称为Inception块。

Inception模块

输入
1x1卷积
1x1卷积
3x3卷积
1x1卷积
5x5卷积
3x3最大池化
1x1卷积
通道合并

Inception块参数详解

输入为224×224×3三通道的图像。

路径1:

​ (1)输入为224×224×3,卷积核数量为64个;卷 积核的尺寸大小为1×1×3;步幅为1(stride=1), 填充为0(padding=0);卷积后得到shape为 224×224×64的特征图输出。

路径2:

​ (1)输入为224×224×3,卷积核数量为96个;卷 积核的尺寸大小为1×1×3;步幅为1(stride = 1), 填充为0(padding=0);卷积后得到shape为 224×224×64的特征图输出。

​ (2)输入为224×224×64,卷积核数量为128个; 卷积核的尺寸大小为3×3×64;步幅为1(stride = 1),填充为1(padding=1);卷积后得到shape 为224×224×128的特征图输出。

路径3:

(1)输入为224×224×3,卷积核数量为16个;卷积核 的尺寸大小为1×1×3;步幅为1(stride = 1),填充为 0(padding=0);卷积后得到shape为224×224×16的 特征图输出。 

​ (2)输入为224×224×16,卷积核数量为32个;卷积 核的尺寸大小为5×5×16;步幅为1(stride = 1),填 充 为 2 ( padding=2 ) ; 卷 积 后 得 到 shape 为 224×224×32的特征图输出。

路径4:

​ (1)输入为224×224×3,池化核的尺寸大小为3×3; 步幅为1(stride = 1),填充为1(padding=1);池 化后得到shape为224×224×3的特征图输出。

​ (2)输入为224×224×3,卷积核数量为32个;卷积核 的尺寸大小为1×1×3;步幅为1(stride = 1),填充为 0(padding=0);卷积后得到shape为224×224×32的 特征图输出。

通道合并:

路径1的到输出为:224×224×64

路径2的到输出为:224×224×128

路径3的到输出为:224×224×32

路径4的到输出为:224×224×32

最终通道合并为64+128+32+32=256,最终的输出为: 224×224×256

整个网络模型架构

阶段层号操作参数 / 配置输出尺寸(H×W×C)对应论文描述
输入层-图像输入224×224 RGB,均值预处理224×224×3摘要 / 引言部分
初始卷积Conv17×7 卷积,步长 2,填充 364 个滤波器112×112×64引言中 “7×7 卷积层”
初始池化Pool13×3 最大池化,步长 2,填充 1-56×56×64引言中 “最大池化层”
降维卷积Conv2并行分支: ① 1×1 卷积 ② 1×1 卷积→3×3 卷积分支①:64 通道 分支②:64→192 通道56×56×256引言中 “3×3 卷积(含 1×1 降维)”
Inception 组 1Inception 2a4 分支并行: ① 1×1 卷积 ② 1×1→3×3 卷积 ③ 1×1→5×5 卷积 ④ 池化→1×1①64 ②96→128 ③16→32 ④32 通道56×56×256论文第 4 节 “第一组 Inception 模块”
Inception 2b4 分支并行(通道数调整)①128 ②128→192 ③32→96 ④64 通道56×56×480同上
降采样Pool23×3 最大池化,步长 2,填充 1-28×28×480组间池化,降低尺寸
Inception 组 2Inception 3a4 分支并行(通道数调整)①192 ②96→208 ③16→48 ④64 通道28×28×512论文第 4 节 “第二组 Inception 模块”
Inception 3b4 分支并行(通道数调整)①160 ②112→224 ③24→64 ④64 通道28×28×512同上
降采样Pool33×3 最大池化,步长 2,填充 1-14×14×512组间池化,降低尺寸
Inception 组 3Inception 4a4 分支并行(通道数调整) 后接辅助分类器 1①128 ②128→256 ③24→64 ④64 通道 辅助分类器:平均池化 + 1×1 卷积 + FC14×14×512论文第 4 节 “第三组 Inception 模块”
Inception 4b4 分支并行(通道数调整) 后接辅助分类器 2①112 ②144→288 ③32→64 ④64 通道 辅助分类器:同上14×14×528同上,辅助分类器缓解梯度消失
Inception 4c4 分支并行(通道数调整)①256 ②256→320 ③32→128 ④128 通道14×14×832同上
降采样Pool43×3 最大池化,步长 2,填充 1-7×7×832组间池化,降低尺寸
Inception 组 4Inception 5a4 分支并行(通道数调整)①256 ②320→384 ③32→128 ④128 通道7×7×896论文第 4 节 “第四组 Inception 模块”
Inception 5b4 分支并行(通道数调整)①384 ②384→448 ③48→128 ④128 通道7×7×1088同上
全局池化GlobalPool全局平均池化-1×1×1024引言中 “全局平均池化替代全连接层”
分类器Dropout随机丢弃 70% 神经元-1×1×1024论文第 5 节 “Dropout 层”
FC全连接层 + Softmax1024→1000 通道(ImageNet 类别数)1×1×1000论文第 5 节 “线性层 + Softmax”

环境准备

首先在本地中的某个盘符新建一个文件夹,就叫GoogLeNet吧,作为项目的根目录。

然后新建一个python文件,就叫plot.py吧,往里面写入以下代码,用于下载数据集:

# FashionMNIST里面包含了许多数据集
from click.core import batch
from spacy.cli.train import train
from torchvision.datasets import FashionMNIST
from torchvision import transforms # 处理数据集,归一化
import torch.utils.data as Data
import numpy as np
import matplotlib.pyplot as plt# 下载FashionMMIST数据集
train_data = FashionMNIST(root="./data", # 指定数据集要下载的路径train=True,# 要训练集# 将数据进行归一化操作transform=transforms.Compose([transforms.Resize(size=224), # 调整数据的大小transforms.ToTensor() # 将数据转换为tensor]),download=True # 开启下载
)# 加载数据集集
train_loader = Data.DataLoader(dataset=train_data,# 要加载的数据集batch_size=64 ,# 批量数据大小shuffle=True, # 打乱数据顺序num_workers=0, # 加载数据线程数量
)# 绘制出训练集
for step,(b_x,b_y) in enumerate(train_loader):if step > 0:breakbatch_x = b_x.squeeze().numpy() # 将四维张量移除第一维,将数据转换为numpy格式batch_y = b_y.numpy() # 将张量数据转成numpy格式class_label = train_data.classes # 训练集标签
print("class_label,",class_label)# 绘图
plt.figure(figsize=(12,5))
for ii in np.arange(len(batch_y)):plt.subplot(4,16,ii+1)plt.imshow(batch_x[ii, : , :],cmap=plt.cm.gray)plt.title(class_label[batch_y[ii]],size=10)plt.axis('off')plt.subplots_adjust(wspace=0.05)plt.show()

执行上述代码后,就会开始下载所需要的数据集文件,只不过下载的速度比较慢,然后下载完成,项目的根目录会多出data文件夹,以下是data的目录结构:

--data--FashionMNIST--raw # 该文件夹下就存放数据集文件

模型搭建

创建model.py文件,用于构建模型代码。

import torch
from torch import nn
from torchsummary import summary# 定义一个通用的Inception模块
class Inception(nn.Module):"""in_channels:输入通道数c1:路线1的卷积c2:元组,路线2中有两层卷积c3:元组,路线3中有两层卷积c4:路线4的卷积"""def __init__(self,in_channels,c1,c2,c3,c4):super(Inception,self).__init__()self.ReLU = nn.ReLU() # 定义激活函数 ReLU# 路线1 单1*1卷积层self.p1_1 = nn.Conv2d(in_channels=in_channels,out_channels=c1,kernel_size=1)# 路线2 单1*1卷积层,3*3的卷积self.p2_1 = nn.Conv2d(in_channels=in_channels,out_channels=c2[0],kernel_size=1)self.p2_2 = nn.Conv2d(in_channels=c2[0],out_channels=c2[1],kernel_size=3,padding=1)# 路线3 单1*1卷积层,5*5的卷积self.p3_1 = nn.Conv2d(in_channels=in_channels,out_channels=c3[0],kernel_size=1)self.p3_2 = nn.Conv2d(in_channels=c3[0],out_channels=c3[1],kernel_size=5,padding=2)# 路线4 3*3最大池化,单1*1卷积# stride的默认参数与kernel_size的值一致,而kernel_size的默认值是1self.p4_1 = nn.MaxPool2d(kernel_size=3,padding=1,stride=1)self.p4_2 = nn.Conv2d(in_channels=in_channels,out_channels=c4,kernel_size=1)def forward(self,x):# x为输入大小p1 = self.ReLU(self.p1_1(x)) # 路线1p2 = self.ReLU(self.p2_2(self.ReLU(self.p2_1(x)))) # 路线2p3 = self.ReLU(self.p3_2(self.ReLU(self.p3_1(x)))) # 路线3p4 = self.ReLU(self.p4_2(self.p4_1(x))) # 路线4# 最后进行通道融合 dim=1表示在通道的基础上进行融合return torch.cat((p1,p2,p3,p4),dim=1)# 定义GoogLeNet网络模型
class GoogLeNet(nn.Module):def __init__(self,Inception):super(GoogLeNet,self).__init__()# 定义第一块网络层,相当于封装了部分网络层self.b1 = nn.Sequential(nn.Conv2d(in_channels=3,out_channels=64,kernel_size=7,stride=2,padding=3),nn.ReLU(),nn.MaxPool2d(kernel_size=3,stride=2,padding=1))# 定义第二块网络层self.b2 = nn.Sequential(nn.Conv2d(in_channels=64,out_channels=64,kernel_size=1),nn.ReLU(),nn.Conv2d(in_channels=64,out_channels=192,kernel_size=3,padding=1),nn.ReLU(),nn.MaxPool2d(kernel_size=3,stride=2,padding=1))# 定义第三块网络层self.b3 = nn.Sequential(Inception(192,64,(96,128),(16,32),32),Inception(256,128,(128,192),(32,96),64),nn.MaxPool2d(kernel_size=3,stride=2,padding=1))# 定义第四块网络层self.b3 = nn.Sequential(Inception(480,192,(96,208),(16,48),64),Inception(512,160,(112,224),(24,64),64),Inception(512,128,(128,256),(24,64),64),Inception(512,112,(128,288),(32,64),64),Inception(528,256,(160,320),(32,128),128),nn.MaxPool2d(kernel_size=3,stride=2,padding=1))# 定义第五块网络层self.b5 = nn.Sequential(Inception(832,256,(160,320),(32,128),128),Inception(832,384,(192,384),(48,128),128),nn.AdaptiveAvgPool2d((1,1)), # 全局平均池化nn.Flatten(), # 平展层nn.Linear(1024,10) # 全连接层)# 参数初始化 避免模型不收敛for m in self.modules(): # 遍历模型的每一层if isinstance(m,nn.Conv2d): # 如果当前层是卷积层nn.init.kaiming_normal_(m.weight,mode="fan_out",nonlinearity='relu') # 使用凯明初始化方式if m.bias is not None: # 如果偏置b存在nn.init.constant_(m.bias,0) # 将b置为0elif isinstance(m,nn.Linear): # 如果当前层是全连接层nn.init.normal_(m.weight,0,0.01) # 使用正太分布初始化,将权重w置为0,标准差为0.01if m.bias is not None: # 如果参数b存在nn.init.constant_(m.bias,0) # 将b置为0# 定义前向传播def forward(self,x):x = self.b1(x)x = self.b2(x)x = self.b3(x)x = self.b4(x)x = self.b5(x)return x# 定义主函数,进行模型测试(仅测试)
if __name__ == "__main__":device = torch.device("cuda" if torch.cuda.is_available() else "cpu")model = GoogLeNet(Inception).to(device)print(summary(model,(1,224,224)))
  • 打个简单的比方,训练网络模型,就好比解方程,为了得到这个方程的极值点,训练的过程就好比是找准一个方向,不断的朝这个方向靠近,使得方程的值不断减小,最终达到极值点,而不收敛,就是,不论你怎么跑,方程的解都不减小。即达不到最后的极值点.在loss上就表现为稳定性的比较大。跟迭代不收敛或者系统不稳定差不多,上下波动不能趋近一个定值。

  • 收敛的意思是指某个值一直在往我们所期望的阈值靠,就拿深度学习中loss损失来做示例,如下一张图是loss在每轮训练时的一个曲线图,可以看到loss一直从一开始的1.8在往1.0降,1.0就是我们期望的阈值,而1.8是最开始loss最大损失值。

模型训练

创建一个model_train.py 文件

import copy
import timeimport torch
from torchvision.datasets import FashionMNIST
from torchvision import transforms
import torch.nn as nn
import torch.utils.data as Data
import numpy as np
import pandas as pd
import matplotlib.pyplot as pltfrom model import GoogLeNet# 处理训练集核数据集
def train_val_data_process():# 加载数据集train_data = FashionMNIST(root="./data",# 数据集所在路径train=True, # 要训练集# 将数据进行归一化操作transform=transforms.Compose([transforms.Resize(size=224), # 修改数据的大小transforms.ToTensor() # 将数据转成Tensor格式]),download=True # 开启加载)# 随机 划分训练集 和 验证集train_data,val_data = Data.random_split(train_data, # 要划分的数据集[round(0.8*len(train_data)), # 划分80%给训练集round(0.2*len(train_data)) # 划分20%给验证集])# 加载训练集数据train_dataloader = Data.DataLoader(dataset=train_data,# 要加载的训练集batch_size=32,# 每轮的训练批次数shuffle=True,# 打乱数据顺序num_workers=2,# 加载数据线程数量)# 加载验证集数据val_dataloader = Data.DataLoader(dataset=val_data,# 要加载的验证集batch_size=32,# 每轮的训练批数shuffle=True,# 打乱数据顺序num_workers=2,# 加载数据集的线程数量)return train_dataloader,val_dataloader# 模型训练
def train_model_process(model,train_dataloader,val_dataloader,num_epochs):# model:需要训练的模型,train_dataloader:训练集数据,val_dataloader:验证集数据,num_epochs:训练轮数# 指定设备device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 定义优化器optimizer = torch.optim.Adam(model.parameters(),lr=0.001)# 定义交叉熵损失函数criterion = nn.CrossEntropyLoss()# 将模型放入到设备中进行训练model.to(device)# 复制当前模型的参数best_model_wts = copy.deepcopy(model.state_dict())# 初始化参数,记录模型的的精确度和损失值best_acc = 0.0 # 最高精确度train_loss_all = [] # 训练集的总损失train_acc_all = [] # 训练集的总精度val_loss_all = [] # 验证集的总损失val_acc_all = [] # 验证集的总精度since = time.time() # 记录开始训练的时间# 开始训练模型 每轮参数for epoch in range(num_epochs):print("Epoch {}/{}".format(epoch,num_epochs-1))print("-"*10)# 初始化参数,记录本轮的模型的损失之和精度train_loss = 0.0 # 训练的损失train_corrects = 0  # 训练的准确度val_loss = 0.0 # 验证集的损失val_corrents = 0 # 验证集的准确度train_num = 0 # 本轮训练集的数量val_num = 0 # 本轮验证集的数量# 取出每轮中的数据集进行训练for step,(b_x,b_y) in enumerate(train_dataloader):b_x = b_x.to(device) # 将训练集数据放入到设备当中b_y = b_y.to(device) # 将标签数据放入到设备当中model.train() # 开启模型训练模式# 将每批次中的标签数据放入到模型中,进行前向传播output = model(b_x)# 查找每一行中最大值对应的行标,即预测值pre_lab = torch.argmax(output,dim=1)# 计算当前批次的损失值(模型的输出,标签)loss = criterion(output,b_y)# 每批次训练完后,将梯度初始化成0optimizer.zero_grad()# 反向传播计算loss.backward()# 更新参数optimizer.step()# 本批次损失值的累加train_loss += loss.item() * b_x.size(0)# 如果模型预测的结果正确,本批次的准确度+1train_corrects += torch.sum(pre_lab == b_y.data)# 本此次的训练数据累加train_num += b_y.size(0)# 取出每轮中的数据进行验证for step,(b_x,b_y) in enumerate(val_dataloader):# 将数据和标签分别放入到设备中b_x = b_x.to(device)b_y = b_y.to(device)model.eval() # 设置模型为评估模式# 前向传播,输入一个批次,输出该批次的对应的预测值output = model(b_x)# 查找每一行中最大值对应的行标,即预测值pre_lab = torch.argmax(output,dim=1)# 计算本此次的损失函数loss = criterion(output,b_y)# 本批次的损失函数累加val_loss += loss.item() * b_x.size(0)# 如果预测正确,那就本批次的精度度累加val_corrents += torch.sum(pre_lab==b_y.data)# 当前用于验证的样本数累加val_num += b_x.size(0)# 计算每轮次的损失值和准确率train_loss_all.append(train_loss / train_num) # 本轮训练集的loss值train_acc_all.append(train_corrects.double().item() / train_num) # 本轮训练集的准确率val_loss_all.append(val_loss / val_num) # 本轮验证集的loss值val_acc_all.append(val_corrents.double().item() / val_num) # 本轮验证集的准确率print("{} train loss:{:.4f} train acc: {:.4f}".format(epoch, train_loss_all[-1], train_acc_all[-1]))print("{} val loss:{:.4f} val acc: {:.4f}".format(epoch, val_loss_all[-1], val_acc_all[-1]))# 寻找最高准确度 和 模型的权重参数if val_acc_all[-1] > best_acc:best_acc = val_acc_all[-1] # 最高准确度best_model_wts = copy.deepcopy(model.state_dict()) # 最佳模型参数# 计算训练耗时time_use = time.time() - sinceprint("训练耗费的时间:{:.0f}m{:.0f}s".format(time_use//60,time_use%60))# 将最佳的模型参数保存torch.save(best_model_wts,"best_model.pth") # .pth是权重文件的后缀,如果相对路径不行,就改为绝对路径# 保存训练好的模型参数train_process = pd.DataFrame(data={"epoch":range(num_epochs),"train_loss_all":train_loss_all,"train_acc_all":train_acc_all,"val_loss_all":val_loss_all,"val_acc_all":val_acc_all})return train_process# 定义绘图的函数,绘制loss和准确度
def matplot_acc_loss(train_process):plt.figure(figsize=(12,4))# 绘制训练集和验证集的损失值图像plt.subplot(1,2,1) # 一行两列,第一列plt.plot(train_process['epoch'],train_process.train_loss_all,"ro-",label="train loss")plt.plot(train_process['epoch'],train_process.val_acc_all,"bs-",label="val loss")plt.legend() # 图例plt.xlabel("epoch") # x轴标签plt.ylabel("loss") # y轴标签# 绘制训练集和验证集的准确度图像plt.subplot(1,2,2) # 一行两列,第二列plt.plot(train_process['epoch'],train_process.val_loss_all,"ro-",label="train acc")plt.plot(train_process['epoch'],train_process.val_acc_all,"bs-",label="va acc")if __name__ == '__main__':# 实例化自定义模型类model = GoogLeNet()# 加载数据集train_dataloader,val_dataloader = train_val_data_process()# 训练模型train_process = train_model_process(model,train_dataloader,val_dataloader,20)# 绘制图像matplot_acc_loss(train_process)

模型训练完毕后,会生成最佳模型参数文件:best_model.pth

模型测试

创建一个model_test.py文件,用于模型的测试

import torch
import torch.utils.data as Data
from numpy.random import shuffle
from torchvision import transforms
from torchvision.datasets import FashionMNISTfrom model import GoogLeNet,Inception# 加载要训练的数据
def test_data_process():# 加载测试集数据test_data = FashionMNIST(root="./data",# 指定数据集要下载的路径train=False,# 不要训练集数据# 数据归一化操作transform=transforms.Compose([transforms.Resize(size=224), # 将数据转成224*224大小transforms.ToTensor(),# 将数据转成Tensor格式]),download=True # 加载数据)# 通过DataLoader加载器 来加载数据test_dataloader = Data.DataLoader(dataset=test_data,# 要加载的数据batch_size=1, # 每轮训练的批次数shuffle=True, # 打乱数据集num_workers=0, # 加载数据集的线程数量)return test_dataloader# 测试模型
def test_model_process(model,test_dataloader):# 指定设备device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 将模型放入设备中model.to(device)# 初始化模型训练的每轮参数test_correct = 0.0 # 准确度test_num = 0 # 测试样本数量# 只进行前向传播with torch.no_grad(): # 将梯度设置为0for test_data_x,test_data_y in enumerate(test_dataloader): # 遍历每轮次# 由于上面设置批次为1,所以这里就不需要循环批次了test_data_x = test_data_x.to(device) # 将测试数据放入到设备中test_data_y = test_data_y.to(device) # 将标签数据放入到设备中# 模型切换成评估模式model.eval()# 前向传播 将测试数据放入到模型中output = model(test_data_x)# 查找每一行中最大值的行标pre_lab = torch.argmax(output,dim=1)# 模型预测的结果 将pre_lab 与 标签数据 进行比较# 如果预测正确,则加1test_correct += torch.sum(pre_lab==test_data_y.data)# 测试样本数量累加test_num += test_data_y.size(0)# 计算最终测试的准确率 每轮的准确度 / 总样本数量test_acc = test_correct.double().item() / test_numprint("测试模型的准确率为:",test_acc)if __name__ == '__main__':# 加载模型model = GoogLeNet(Inception)# 模型具体的训练过程device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 加载训练好的模型最佳参数model.load_state_dict(torch.load('best_model.pth',map_location=device))# 加载测试的数据集test_dataloader = test_data_process()# 开始测试# test_model_process(model, test_dataloader) # 简略测试# 模型具体的训练过程# device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 将模型放入到设备当中model = model.to(device)# 数据的类别classes = ('T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat','Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot')# 梯度设置为0with torch.no_grad():# 遍历测试集中的 测试数据 和 标签for b_x,b_y in test_dataloader:# 将数据和标签移动到与模型相同的设备b_x = b_x.to(device)b_y = b_y.to(device)# 将模型设置为评估模式model.eval()# 将数据放入到模型中,得出预测结果output = model(b_x)# 获取最大值的行标pre_lab = torch.argmax(output,dim=1)# 取出张量中的下标result = pre_lab.item()label = b_y.item()print("预测结果为:",classes[result],"标签为:",classes[label])

猫狗分类

利用自己的训练集来进行模型训练,实现猫狗分类

环境搭建

将上面刚刚搭建好的GoogLeNet项目,复制一份,就叫GoogLeNet-1吧。

数据集准备和划分

本地已经准备好了data_cat_dog的自定义数据集文件夹,里面均为图片。执行以下脚本,可以实现训练集的数据划分。

data_cat_dog的自定义数据集获取地址

禁止私自搬运和商用,后果自负,仅限学习使用

新建一个data_partitioning.py文件,并写入以下代码:

import os
from shutil import copy
import randomdef mkfile(file):if not os.path.exists(file):os.makedirs(file)# 获取data文件夹下所有文件夹名(即需要分类的类名)
file_path = 'data_cat_dog'
flower_class = [cla for cla in os.listdir(file_path)]# 创建 训练集train 文件夹,并由类名在其目录下创建5个子目录
mkfile('data/train')
for cla in flower_class:mkfile('data/train/' + cla)# 创建 验证集val 文件夹,并由类名在其目录下创建子目录
mkfile('data/test')
for cla in flower_class:mkfile('data/test/' + cla)# 划分比例,训练集 : 测试集 = 9 : 1
split_rate = 0.1# 遍历所有类别的全部图像并按比例分成训练集和验证集
for cla in flower_class:cla_path = file_path + '/' + cla + '/'  # 某一类别的子目录images = os.listdir(cla_path)  # iamges 列表存储了该目录下所有图像的名称num = len(images)eval_index = random.sample(images, k=int(num * split_rate))  # 从images列表中随机抽取 k 个图像名称for index, image in enumerate(images):# eval_index 中保存验证集val的图像名称if image in eval_index:image_path = cla_path + imagenew_path = 'data/test/' + clacopy(image_path, new_path)  # 将选中的图像复制到新路径# 其余的图像保存在训练集train中else:image_path = cla_path + imagenew_path = 'data/train/' + clacopy(image_path, new_path)print("\r[{}] processing [{}/{}]".format(cla, index + 1, num), end="")  # processing barprint()print("processing done!")

执行以上代码,就可以划分猫和狗的训练集和测试集,然后本地项目中会多个以下文件:

--data--test--cat--dog--train--cat--dog

数据集加载

重写model_train.py文件中的部分代码

import copy
import timeimport torch
from torchvision.datasets import ImageFolder # 加载自己的数据集
from torchvision import transforms
import torch.nn as nn
import torch.utils.data as Data
import numpy as np
import pandas as pd
import matplotlib.pyplot as pltfrom model import GoogLeNet# 处理训练集核数据集
def train_val_data_process():# ======(修改的部分开始)=============================ROOT_TRAIN = r'data\train' # 数据集的路径# 定义归一化方法 计算均值和方差normalize = transforms.Normalize([0.162,0.151,0.138],[0.058,0.052,0.048])# 定义数据集处理方法变量train_transform = transforms.Compose( # 对数据集进行操作[transforms.Resize((224,224)),# 修改数据集的大小为 224*224transforms.ToTensor(), # 将数据修改为tensor格式normalize # 数据归一化]  )# 加载数据集 ImageFolder()为第三方库导入的包train_data = ImageFolder(ROOT_TRAIN, # 读取数据集的路径transform = train_transform # 处理数据的方法)# ======(修改的部分结束)=============================# 随机 划分训练集 和 验证集train_data,val_data = Data.random_split(train_data, # 要划分的数据集[round(0.8*len(train_data)), # 划分80%给训练集round(0.2*len(train_data)) # 划分20%给验证集])# 加载训练集数据train_dataloader = Data.DataLoader(dataset=train_data,# 要加载的训练集batch_size=32,# 每轮的训练批次数shuffle=True,# 打乱数据顺序num_workers=2,# 加载数据线程数量)# 加载验证集数据val_dataloader = Data.DataLoader(dataset=val_data,# 要加载的验证集batch_size=32,# 每轮的训练批数shuffle=True,# 打乱数据顺序num_workers=2,# 加载数据集的线程数量)return train_dataloader,val_dataloader# 模型训练
def train_model_process(model,train_dataloader,val_dataloader,num_epochs):# model:需要训练的模型,train_dataloader:训练集数据,val_dataloader:验证集数据,num_epochs:训练轮数# 指定设备device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 定义优化器optimizer = torch.optim.Adam(model.parameters(),lr=0.001)# 定义交叉熵损失函数criterion = nn.CrossEntropyLoss()# 将模型放入到设备中进行训练model.to(device)# 复制当前模型的参数best_model_wts = copy.deepcopy(model.state_dict())# 初始化参数,记录模型的的精确度和损失值best_acc = 0.0 # 最高精确度train_loss_all = [] # 训练集的总损失train_acc_all = [] # 训练集的总精度val_loss_all = [] # 验证集的总损失val_acc_all = [] # 验证集的总精度since = time.time() # 记录开始训练的时间# 开始训练模型 每轮参数for epoch in range(num_epochs):print("Epoch {}/{}".format(epoch,num_epochs-1))print("-"*10)# 初始化参数,记录本轮的模型的损失之和精度train_loss = 0.0 # 训练的损失train_corrects = 0  # 训练的准确度val_loss = 0.0 # 验证集的损失val_corrents = 0 # 验证集的准确度train_num = 0 # 本轮训练集的数量val_num = 0 # 本轮验证集的数量# 取出每轮中的数据集进行训练for step,(b_x,b_y) in enumerate(train_dataloader):b_x = b_x.to(device) # 将训练集数据放入到设备当中b_y = b_y.to(device) # 将标签数据放入到设备当中model.train() # 开启模型训练模式# 将每批次中的标签数据放入到模型中,进行前向传播output = model(b_x)# 查找每一行中最大值对应的行标,即预测值pre_lab = torch.argmax(output,dim=1)# 计算当前批次的损失值(模型的输出,标签)loss = criterion(output,b_y)# 每批次训练完后,将梯度初始化成0optimizer.zero_grad()# 反向传播计算loss.backward()# 更新参数optimizer.step()# 本批次损失值的累加train_loss += loss.item() * b_x.size(0)# 如果模型预测的结果正确,本批次的准确度+1train_corrects += torch.sum(pre_lab == b_y.data)# 本此次的训练数据累加train_num += b_y.size(0)# 取出每轮中的数据进行验证for step,(b_x,b_y) in enumerate(val_dataloader):# 将数据和标签分别放入到设备中b_x = b_x.to(device)b_y = b_y.to(device)model.eval() # 设置模型为评估模式# 前向传播,输入一个批次,输出该批次的对应的预测值output = model(b_x)# 查找每一行中最大值对应的行标,即预测值pre_lab = torch.argmax(output,dim=1)# 计算本此次的损失函数loss = criterion(output,b_y)# 本批次的损失函数累加val_loss += loss.item() * b_x.size(0)# 如果预测正确,那就本批次的精度度累加val_corrents += torch.sum(pre_lab==b_y.data)# 当前用于验证的样本数累加val_num += b_x.size(0)# 计算每轮次的损失值和准确率train_loss_all.append(train_loss / train_num) # 本轮训练集的loss值train_acc_all.append(train_corrects.double().item() / train_num) # 本轮训练集的准确率val_loss_all.append(val_loss / val_num) # 本轮验证集的loss值val_acc_all.append(val_corrents.double().item() / val_num) # 本轮验证集的准确率print("{} train loss:{:.4f} train acc: {:.4f}".format(epoch, train_loss_all[-1], train_acc_all[-1]))print("{} val loss:{:.4f} val acc: {:.4f}".format(epoch, val_loss_all[-1], val_acc_all[-1]))# 寻找最高准确度 和 模型的权重参数if val_acc_all[-1] > best_acc:best_acc = val_acc_all[-1] # 最高准确度best_model_wts = copy.deepcopy(model.state_dict()) # 最佳模型参数# 计算训练耗时time_use = time.time() - sinceprint("训练耗费的时间:{:.0f}m{:.0f}s".format(time_use//60,time_use%60))# 将最佳的模型参数保存torch.save(best_model_wts,"best_model.pth") # .pth是权重文件的后缀,如果相对路径不行,就改为绝对路径# 保存训练好的模型参数train_process = pd.DataFrame(data={"epoch":range(num_epochs),"train_loss_all":train_loss_all,"train_acc_all":train_acc_all,"val_loss_all":val_loss_all,"val_acc_all":val_acc_all})return train_process# 定义绘图的函数,绘制loss和准确度
def matplot_acc_loss(train_process):plt.figure(figsize=(12,4))# 绘制训练集和验证集的损失值图像plt.subplot(1,2,1) # 一行两列,第一列plt.plot(train_process['epoch'],train_process.train_loss_all,"ro-",label="train loss")plt.plot(train_process['epoch'],train_process.val_acc_all,"bs-",label="val loss")plt.legend() # 图例plt.xlabel("epoch") # x轴标签plt.ylabel("loss") # y轴标签# 绘制训练集和验证集的准确度图像plt.subplot(1,2,2) # 一行两列,第二列plt.plot(train_process['epoch'],train_process.val_loss_all,"ro-",label="train acc")plt.plot(train_process['epoch'],train_process.val_acc_all,"bs-",label="va acc")if __name__ == '__main__':# 实例化自定义模型类model = GoogLeNet()# 加载数据集train_dataloader,val_dataloader = train_val_data_process()# 训练模型train_process = train_model_process(model,train_dataloader,val_dataloader,20)# 绘制图像matplot_acc_loss(train_process)

数据集归一化

在项目的根目录下创建一个py文件,就叫mean_std.py吧,用于计算均值和方差,使数据归一化,符合正太分布

# mean_std.py文件
from PIL import Image
import os
import numpy as np# 文件夹路径,包含所有图片文件
folder_path = 'data_cat_dog'# 初始化累积变量
total_pixels = 0
sum_normalized_pixel_values = np.zeros(3)  # 如果是RGB图像,需要三个通道的均值和方差# 遍历文件夹中的图片文件
for root, dirs, files in os.walk(folder_path):for filename in files:if filename.endswith(('.jpg', '.jpeg', '.png', '.bmp')):  # 可根据实际情况添加其他格式image_path = os.path.join(root, filename)image = Image.open(image_path)image_array = np.array(image)# 归一化像素值到0-1之间normalized_image_array = image_array / 255.0# print(image_path)# print(normalized_image_array.shape)# 累积归一化后的像素值和像素数量total_pixels += normalized_image_array.sizesum_normalized_pixel_values += np.sum(normalized_image_array, axis=(0, 1))# 计算均值和方差
mean = sum_normalized_pixel_values / total_pixelssum_squared_diff = np.zeros(3)
for root, dirs, files in os.walk(folder_path):for filename in files:if filename.endswith(('.jpg', '.jpeg', '.png', '.bmp')):image_path = os.path.join(root, filename)image = Image.open(image_path)image_array = np.array(image)# 归一化像素值到0-1之间normalized_image_array = image_array / 255.0# print(normalized_image_array.shape)# print(mean.shape)# print(image_path)try:diff = (normalized_image_array - mean) ** 2sum_squared_diff += np.sum(diff, axis=(0, 1))except:print(f"捕获到自定义异常")# diff = (normalized_image_array - mean) ** 2# sum_squared_diff += np.sum(diff, axis=(0, 1))variance = sum_squared_diff / total_pixelsprint("Mean:", mean)
print("Variance:", variance)

运行起来也是需要一定的时间。

模型测试

修改model_test.py文件中的部分代码

import torch
import torch.utils.data as Data
from numpy.random import shuffle
from torchvision import transforms
from torchvision.datasets import FashionMNIST
from torchvision.datasets import ImageFolderfrom model import GoogLeNet,Inception# 加载要训练的数据
def test_data_process():#======================修改的部分开始======================ROOT_TRAIN = r'data\train' # 数据集的路径# 定义归一化方法 计算均值和方差normalize = transforms.Normalize([0.162,0.151,0.138],[0.058,0.052,0.048])# 定义数据集处理方法变量test_transform = transforms.Compose( # 对数据集进行操作[transforms.Resize((224,224)),# 修改数据集的大小为 224*224transforms.ToTensor(), # 将数据修改为tensor格式normalize # 数据归一化]  )# 加载数据集 ImageFolder()为第三方库导入的包test_data = ImageFolder(ROOT_TRAIN, # 读取数据集的路径transform = test_transform # 处理数据的方法)#======================修改的部分结束======================# 通过DataLoader加载器 来加载数据test_dataloader = Data.DataLoader(dataset=test_data,# 要加载的数据batch_size=1, # 每轮训练的批次数shuffle=True, # 打乱数据集num_workers=0, # 加载数据集的线程数量)return test_dataloader# 测试模型
def test_model_process(model,test_dataloader):# 指定设备device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 将模型放入设备中model.to(device)# 初始化模型训练的每轮参数test_correct = 0.0 # 准确度test_num = 0 # 测试样本数量# 只进行前向传播with torch.no_grad(): # 将梯度设置为0for test_data_x,test_data_y in enumerate(test_dataloader): # 遍历每轮次# 由于上面设置批次为1,所以这里就不需要循环批次了test_data_x = test_data_x.to(device) # 将测试数据放入到设备中test_data_y = test_data_y.to(device) # 将标签数据放入到设备中# 模型切换成评估模式model.eval()# 前向传播 将测试数据放入到模型中output = model(test_data_x)# 查找每一行中最大值的行标pre_lab = torch.argmax(output,dim=1)# 模型预测的结果 将pre_lab 与 标签数据 进行比较# 如果预测正确,则加1test_correct += torch.sum(pre_lab==test_data_y.data)# 测试样本数量累加test_num += test_data_y.size(0)# 计算最终测试的准确率 每轮的准确度 / 总样本数量test_acc = test_correct.double().item() / test_numprint("测试模型的准确率为:",test_acc)if __name__ == '__main__':# 加载模型model = GoogLeNet(Inception)# 模型具体的训练过程device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 加载训练好的模型最佳参数model.load_state_dict(torch.load('best_model.pth',map_location=device))# 加载测试的数据集test_dataloader = test_data_process()# 开始测试# test_model_process(model, test_dataloader) # 简略测试# 模型具体的训练过程# device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 将模型放入到设备当中model = model.to(device)# 数据的类别classes = ['猫','狗']# 梯度设置为0with torch.no_grad():# 遍历测试集中的 测试数据 和 标签for b_x,b_y in test_dataloader:# 将数据和标签移动到与模型相同的设备b_x = b_x.to(device)b_y = b_y.to(device)# 将模型设置为评估模式model.eval()# 将数据放入到模型中,得出预测结果output = model(b_x)# 获取最大值的行标pre_lab = torch.argmax(output,dim=1)# 取出张量中的下标result = pre_lab.item()label = b_y.item()print("预测结果为:",classes[result],"标签为:",classes[label])

模型的推理

随便从网上下载猫或者狗的图像到项目的根目录下,这里就下载猫的图片吧:

图片名就设置为: 7dd98d1001e939010b1f0c1e537c22e836d19614.jpeg

然后增加和修改model_test.py文件中的代码:

import torch
import torch.utils.data as Data
from numpy.random import shuffle
from torchvision import transforms
from torchvision.datasets import FashionMNIST
from torchvision.datasets import ImageFolder
from PIL import Image # 导入第三方库 增加代码from model import GoogLeNet,Inception# 加载要训练的数据
def test_data_process():#======================修改的部分开始======================ROOT_TRAIN = r'data\train' # 数据集的路径# 定义归一化方法 计算均值和方差normalize = transforms.Normalize([0.162,0.151,0.138],[0.058,0.052,0.048])# 定义数据集处理方法变量test_transform = transforms.Compose( # 对数据集进行操作[transforms.Resize((224,224)),# 修改数据集的大小为 224*224transforms.ToTensor(), # 将数据修改为tensor格式normalize # 数据归一化]  )# 加载数据集 ImageFolder()为第三方库导入的包test_data = ImageFolder(ROOT_TRAIN, # 读取数据集的路径transform = test_transform # 处理数据的方法)#======================修改的部分结束======================# 通过DataLoader加载器 来加载数据test_dataloader = Data.DataLoader(dataset=test_data,# 要加载的数据batch_size=1, # 每轮训练的批次数shuffle=True, # 打乱数据集num_workers=0, # 加载数据集的线程数量)return test_dataloader# 测试模型
def test_model_process(model,test_dataloader):# 指定设备device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 将模型放入设备中model.to(device)# 初始化模型训练的每轮参数test_correct = 0.0 # 准确度test_num = 0 # 测试样本数量# 只进行前向传播with torch.no_grad(): # 将梯度设置为0for test_data_x,test_data_y in enumerate(test_dataloader): # 遍历每轮次# 由于上面设置批次为1,所以这里就不需要循环批次了test_data_x = test_data_x.to(device) # 将测试数据放入到设备中test_data_y = test_data_y.to(device) # 将标签数据放入到设备中# 模型切换成评估模式model.eval()# 前向传播 将测试数据放入到模型中output = model(test_data_x)# 查找每一行中最大值的行标pre_lab = torch.argmax(output,dim=1)# 模型预测的结果 将pre_lab 与 标签数据 进行比较# 如果预测正确,则加1test_correct += torch.sum(pre_lab==test_data_y.data)# 测试样本数量累加test_num += test_data_y.size(0)# 计算最终测试的准确率 每轮的准确度 / 总样本数量test_acc = test_correct.double().item() / test_numprint("测试模型的准确率为:",test_acc)if __name__ == '__main__':# 加载模型model = GoogLeNet(Inception)# 指定设备device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 加载训练好的模型最佳参数model.load_state_dict(torch.load('best_model.pth',map_location=device))# 加载测试的数据集# test_dataloader = test_data_process()# 开始测试# test_model_process(model, test_dataloader) # 简略测试# 指定设备device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 将模型放入到设备当中model = model.to(device)# 数据的类别classes = ['猫','狗']# =================新增代码部分开始,到底直接结束=================# 读取刚刚下载到本地猫的图片cat_url = "7dd98d1001e939010b1f0c1e537c22e836d19614.jpeg"image = Image.open(url) # 要预测图片的路径# 定义归一化方法 计算均值和方差normalize = transforms.Normalize([0.162,0.151,0.138],[0.058,0.052,0.048])# 定义数据集处理方法变量test_transform = transforms.Compose( # 对数据集进行操作[transforms.Resize((224,224)),# 修改数据集的大小为 224*224transforms.ToTensor(), # 将数据修改为tensor格式normalize # 数据归一化]  )# 对下载的图片进行格式处理 转成torch类型数据image = test_transform(image)# print(image.shape) # torch.Size([3,224,224]) 打印处理后的图片的维度# 添加批次维度 1image = unsqueeze(0)# print(image.shape) # torch.Size([1,3,224,224]) 打印处理后的图片的维度,其中1表示批次,是由unsqueeze(0)得来的# 去除梯度下降with torch.no_grad():model.eval() # 开启验证模式image = image.to(device) # 将图片放入到设备当中output = model(image) # 将图片放入到模型当中pre_lab = torch.argmax(out_put,dim=1) # 标签result = pre_lab.item()# print(pre_lab) # tensor([0],device='cude:0')# print(result) # 0 表示猫print("预测值: ",classes[result])

device = torch.device(“cuda” if torch.cuda.is_available() else “cpu”)

# 将模型放入到设备当中
model = model.to(device)# 数据的类别
classes = ['猫','狗']# =================新增代码部分开始,到底直接结束=================
# 读取刚刚下载到本地猫的图片
cat_url = "7dd98d1001e939010b1f0c1e537c22e836d19614.jpeg"
image = Image.open(url) # 要预测图片的路径# 定义归一化方法 计算均值和方差
normalize = transforms.Normalize([0.162,0.151,0.138],[0.058,0.052,0.048])# 定义数据集处理方法变量
test_transform = transforms.Compose( # 对数据集进行操作[transforms.Resize((224,224)),# 修改数据集的大小为 224*224transforms.ToTensor(), # 将数据修改为tensor格式normalize # 数据归一化]  
)# 对下载的图片进行格式处理 转成torch类型数据
image = test_transform(image)
# print(image.shape) # torch.Size([3,224,224]) 打印处理后的图片的维度# 添加批次维度 1
image = unsqueeze(0)
# print(image.shape) # torch.Size([1,3,224,224]) 打印处理后的图片的维度,其中1表示批次,是由unsqueeze(0)得来的# 去除梯度下降
with torch.no_grad():model.eval() # 开启验证模式image = image.to(device) # 将图片放入到设备当中output = model(image) # 将图片放入到模型当中pre_lab = torch.argmax(out_put,dim=1) # 标签result = pre_lab.item()
# print(pre_lab) # tensor([0],device='cude:0')
# print(result) # 0 表示猫
print("预测值: ",classes[result])


http://www.hkcw.cn/article/pcpLcMFyWk.shtml

相关文章

Linux:动静态库

一:什么是库 库是写好的,现有的,成熟的可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人都从零开始写,因此库的存在一样非同寻常 本质上库是一种可执行代码的二进制形式,可以被操作…

【图像处理入门】2. Python中OpenCV与Matplotlib的图像操作指南

一、环境准备 import cv2 import numpy as np import matplotlib.pyplot as plt# 配置中文字体显示(可选) plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False二、图像的基本操作 1. 图像读取、显示与保存 使用OpenCV…

设计模式——装饰器设计模式(结构型)

摘要 文中主要介绍了装饰器设计模式,它是一种结构型设计模式,可在不改变原有类代码的情况下,动态为对象添加额外功能。文中详细阐述了装饰器模式的角色、结构、实现方式、适合场景以及实战示例等内容,还探讨了其与其他设计模式的…

生活小记啊

最近生活上的事情还是蛮多的,想到哪写到哪。 工作 三月的某个周六,正在加班写技术方案,大晚上写完了听到调动通知,要去新的团队了。 还是蛮不舍的,看着产品从无到有,一路走过来,倾注了不少感…

【android bluetooth 案例分析 04】【Carplay 详解 2】【Carplay 连接之手机主动连车机】

1. 背景 在【android bluetooth 案例分析 04】【Carplay 详解 1】【CarPlay 在车机侧的蓝牙通信原理与角色划分详解】中我们从整理上介绍了车机中 carplay 相关基础概念。 本节 将详细分析 iphone手机主动 连接 车机carplay 这一过程。 先回顾一下 上一节, carpla…

【Kotlin】数字字符串数组集合

【Kotlin】简介&变量&类&接口 【Kotlin】数字&字符串&数组&集合 文章目录 Kotlin_数字&字符串&数组&集合数字字面常量显式转换数值类型转换背后发生了什么 运算字符串字符串模板字符串判等修饰符数组集合通过序列提高效率惰性求值序列的操…

FreeCAD源码分析: 串行化工具

本文分析FreeCAD中的串行化工具。 注1:限于研究水平,分析难免不当,欢迎批评指正。 注2:文章内容会不定期更新。 零、预修 0.1 QDataStream 0.2 Boost.Iostreams 0.3 Zipios 0.4 Xerces-C 一、核心组件 1.1 Base::Writer 1.2 Ba…

【R语言编程绘图-plotly】

安装与加载 在R中使用plotly库前需要安装并加载。安装可以通过CRAN进行,使用install.packages()函数。加载库使用library()函数。 install.packages("plotly") library(plotly)测试库文件安装情况 # 安装并加载必要的包 if (!requireNamespace("p…

设计模式——系统数据建模设计

摘要 本文主要介绍了UML在软件系统分析和设计中的应用,详细阐述了六大类关系(泛化、实现、依赖、关联、聚合、组合)及其在UML类图中的表示方法,并通过具体例子说明了这些关系在实际编程中的应用。同时,文章还概述了UM…

37. Sudoku Solver

题目描述 37. Sudoku Solver 回溯 class Solution {vector<vector<bool>> row_used;vector<vector<bool>> col_used;vector<vector<bool>> box_used;public:void solveSudoku(vector<vector<char>>& board) {row_used.r…

【Java开发日记】基于 Spring Cloud 的微服务架构分析

目录 1、Spring Cloud 2、Spring Cloud 的核心组件 1. Eureka&#xff08;注册中心&#xff09; 2. Zuul&#xff08;服务网关&#xff09; 3. Ribbon&#xff08;负载均衡&#xff09; 4. Hystrix&#xff08;熔断保护器&#xff09; 5. Feign&#xff08;REST转换器&a…

进程间通信IV System V 系列(linux)

目录 消息队列 原理 操作 补充概念 信号量 (原子性计数器) 原理 操作 (和共享内存相似) 总结 小知识 消息队列 原理 在内核中建立一个队列&#xff0c;进程可以相互进行通信&#xff0c;通过队列进行IPC&#xff0c;就是进程之间发送带类型的数据块。 操作 接口和共享…

【MySQL】索引(B+树详解)

MySQL(五)索引 一、索引的减I/O设计 1.读取量 2.搜索树 2.1方向 2.2有序 3.分多叉 3.1B树 弊端: 3.2B树 3.2.1非叶子-搜索字段 3.2.1.1海量分叉 3.2.1.1.1最大式 3.2.1.1.2最快式 3.2.1.2缓存内存 3.2.1.2.1字段总量小 3.2.1.2.2时间复杂度 3.2.1.3区间搜索向…

2025年全国青少年信息素养大赛复赛C++算法创意实践挑战赛真题模拟强化训练(试卷4:共计6题带解析)

2025年全国青少年信息素养大赛复赛C++算法创意实践挑战赛真题模拟强化训练(试卷4:共计6题带解析) 第1题:最佳情侣身高差(题目及解析) 题目描述 专家通过多组情侣研究数据发现,最佳的情侣身高差遵循着一个公式:(女方的身高) 1.09 =(男方的身高)。如果符合,你俩的身…

5.31 day33

知识点回顾&#xff1a; PyTorch和cuda的安装 查看显卡信息的命令行命令&#xff08;cmd中使用&#xff09; cuda的检查 简单神经网络的流程 数据预处理&#xff08;归一化、转换成张量&#xff09; 模型的定义 继承nn.Module类 定义每一个层 定义前向传播流程 定义损失函数和优…

【C++】模板

目录 1、函数模板 基本用法 函数模板的实现原理 函数模板的实例化 模板参数的匹配原则 2、类模板 类模板的定义格式 类模板的实例化 1、函数模板 基本用法 template < typename T >返回值类型 函数名(参数列表){} template 是模板的意思&#xff0c;typename是…

第六十二节:深度学习-加载 TensorFlow/PyTorch/Caffe 模型

在计算机视觉领域,OpenCV的DNN(深度神经网络)模块正逐渐成为轻量级模型部署的利器。本文将深入探讨如何利用OpenCV加载和运行三大主流框架(TensorFlow、PyTorch、Caffe)训练的模型,并提供完整的代码实现和优化技巧。 一、OpenCV DNN模块的核心优势 OpenCV的DNN模块自3.3…

Linux系统下安装配置 Nginx

Windows Nginx https://nginx.org/en/download.htmlLinux Nginx https://nginx.org/download/nginx-1.24.0.tar.gz解压 tar -zxvf tar -zxvf nginx-1.18.0.tar.gz #解压安装依赖&#xff08;如未安装&#xff09; yum groupinstall "Development Tools" -y yum…

qwen3解读

1. 模型架构 重点&#xff1a; 思维模式和非思维模式这两种不同的操作模式集成到一个模型中。这样可以让用户在这些模式间切换&#xff0c;而不是在不同模型间切换。多阶段的后培训方法&#xff1a;增强推理和非推理模式。将基础模型和人的偏好结合。 预训练阶段&#xff1a…

用127.0.0.1访问VMware虚拟机中间件

文章目录 前言一、虚拟机安装、插件安装二、改虚拟机配置1.更改虚拟机使用的是 NAT 模式2.编辑 VMware 的 NAT 设置并添加端口转发 总结至此大功告成了&#xff01;&#xff01;&#xff01; 前言 docker 安装中间件很方便,平时使用windows作开发,如果想快速安装中间件,那么首…