Python训练打卡Day35

模型可视化与推理

知识点回顾:

  1. 三种不同的模型可视化方法:推荐torchinfo打印summary+权重分布可视化
  2. 进度条功能:手动和自动写法,让打印结果更加美观
  3. 推理的写法:评估模式

模型结构可视化

理解一个深度学习网络最重要的2点:

1. 了解损失如何定义的,知道损失从何而来----把抽象的任务通过损失函数量化出来

2. 了解参数总量,即知道每一层的设计---层设计决定参数总量

为了了解参数总量,我们需要知道层设计,以及每一层参数的数量。下面介绍1几个层可视化工具:

1. nn.model自带的方法
#  nn.Module 的内置功能,直接输出模型结构
print(model)

这是最基础、最简单的方法,会直接打印模型对象,它会输出模型的结构,显示模型中各个层的名称和参数信息

# nn.Module 的内置功能,返回模型的可训练参数迭代器
for name, param in model.named_parameters():print(f"Parameter name: {name}, Shape: {param.shape}")

可以将模型中带有weight的参数(即权重)提取出来,并转为 numpy 数组形式,对其计算统计分布,并且绘制可视化图表

# 提取权重数据
import numpy as np
weight_data = {}
for name, param in model.named_parameters():if 'weight' in name:weight_data[name] = param.detach().cpu().numpy()# 可视化权重分布
fig, axes = plt.subplots(1, len(weight_data), figsize=(15, 5))
fig.suptitle('Weight Distribution of Layers')for i, (name, weights) in enumerate(weight_data.items()):# 展平权重张量为一维数组weights_flat = weights.flatten()# 绘制直方图axes[i].hist(weights_flat, bins=50, alpha=0.7)axes[i].set_title(name)axes[i].set_xlabel('Weight Value')axes[i].set_ylabel('Frequency')axes[i].grid(True, linestyle='--', alpha=0.7)plt.tight_layout()
plt.subplots_adjust(top=0.85)
plt.show()# 计算并打印每层权重的统计信息
print("\n=== 权重统计信息 ===")
for name, weights in weight_data.items():mean = np.mean(weights)std = np.std(weights)min_val = np.min(weights)max_val = np.max(weights)print(f"{name}:")print(f"  均值: {mean:.6f}")print(f"  标准差: {std:.6f}")print(f"  最小值: {min_val:.6f}")print(f"  最大值: {max_val:.6f}")print("-" * 30)

对比 fc1.weight 和 fc2.weight 的统计信息 ,可以发现它们的均值、标准差、最值等存在差异。这反映了不同层在模型中的作用不同。权重统计信息可以为超参数调整提供参考。

2.torchsummary库的summary方法
# pip install torchsummary -i https://pypi.tuna.tsinghua.edu.cn/simple
from torchsummary import summary
# 打印模型摘要,可以放置在模型定义后面
summary(model, input_size=(4,))

        该方法不显示输入层的尺寸,因为输入的神经网是自己设置的,所以不需要显示输入层的尺寸。但是在使用该方法时,input_size=(4,) 参数是必需的,因为 PyTorch 需要知道输入数据的形状才能推断模型各层的输出形状和参数数量。

        这是因为PyTorch 的模型在定义时是动态的,它不会预先知道输入数据的具体形状。nn.Linear(4, 10) 只定义了 “输入维度是 4,输出维度是 10”,但不知道输入的批量大小和其他维度,比如卷积层需要知道输入的通道数、高度、宽度等信息。----并非所有输入数据都是结构化数据

        因此,要生成模型摘要(如每层的输出形状、参数数量),必须提供一个示例输入形状,让 PyTorch “运行” 一次模型,从而推断出各层的信息。

summary 函数的核心逻辑是:

1. 创建一个与 input_size 形状匹配的虚拟输入张量(通常填充零)

2. 将虚拟输入传递给模型,执行一次前向传播(但不计算梯度)

3. 记录每一层的输入和输出形状,以及参数数量

4. 生成可读的摘要报告

构建神经网络的时候

1. 输入层不需要写:x多少个特征 输入层就有多少神经元

2. 隐藏层需要写,从第一个隐藏层可以看出特征的个数

3. 输出层的神经元和任务有关,比如分类任务,输出层有3个神经元,一个对应每个类别

可学习参数计算

1. Linear-1对应self.fc1 = nn.Linear(4, 10),表明前一层有4个神经元,这一层有10个神经元,每2个神经元之间靠着线相连,所有有4*10个权重参数+10个偏置参数=50个参数

2. relu层不涉及可学习参数,可以把它和前一个线性层看成一层,图上也是这个含义

3. Linear-3层对应代码 self.fc2 = nn.Linear(10,3),10*3个权重参数+3个偏置=33个参数

总参数83个,占用内存几乎为0

1.3 torchinfo库的summary方法

 torchinfo 是提供比 torchsummary 更详细的模型摘要信息,包括每层的输入输出形状、参数数量、计算量等。

# pip install torchinfo -i https://pypi.tuna.tsinghua.edu.cn/simple
from torchinfo import summary
summary(model, input_size=(4, ))

进度条功能

tqdm这个库非常适合用在循环中观察进度。尤其在深度学习这种训练是循环的场景中。他最核心的逻辑如下

1. 创建一个进度条对象,并传入总迭代次数。一般用with语句创建对象,这样对象会在with语句结束后自动销毁,保证资源释放。with是常见的上下文管理器,这样的使用方式还有用with打开文件,结束后会自动关闭文件。

2. 更新进度条,通过pbar.update(n)指定每次前进的步数n(适用于非固定步长的循环)。

1.手动更新
from tqdm import tqdm  # 先导入tqdm库
import time  # 用于模拟耗时操作# 创建一个总步数为10的进度条
with tqdm(total=10) as pbar:  # pbar是进度条对象的变量名# pbar 是 progress bar(进度条)的缩写,约定俗成的命名习惯。for i in range(10):  # 循环10次(对应进度条的10步)time.sleep(0.5)  # 模拟每次循环耗时0.5秒pbar.update(1)  # 每次循环后,进度条前进1步
from tqdm import tqdm
import time# 创建进度条时添加描述(desc)和单位(unit)
with tqdm(total=5, desc="下载文件", unit="个") as pbar:# 进度条这个对象,可以设置描述和单位# desc是描述,在左侧显示# unit是单位,在进度条右侧显示for i in range(5):time.sleep(1)pbar.update(1)  # 每次循环进度+1

unit 参数的核心作用是明确进度条中每个进度单位的含义,使可视化信息更具可读性。在深度学习训练中,常用的单位包括:

  • epoch:训练轮次(遍历整个数据集一次)。
  • batch:批次(每次梯度更新处理的样本组)。
  • sample:样本(单个数据点)
2.自动更新
from tqdm import tqdm
import time# 直接将range(3)传给tqdm,自动生成进度条
# 这个写法我觉得是有点神奇的,直接可以给这个对象内部传入一个可迭代对象,然后自动生成进度条
for i in tqdm(range(3), desc="处理任务", unit="epoch"):time.sleep(1)

for i in tqdm(range(3), desc="处理任务", unit="个")这个写法则不需要在循环中调用update()方法,更加简洁。实际上这2种写法都随意选取,这里都介绍下

 

# 用tqdm的set_postfix方法在进度条右侧显示实时数据(如当前循环的数值、计算结果等):
from tqdm import tqdm
import timetotal = 0  # 初始化总和
with tqdm(total=10, desc="累加进度") as pbar:for i in range(1, 11):time.sleep(0.3)total += i  # 累加1+2+3+...+10pbar.update(1)  # 进度+1pbar.set_postfix({"当前总和": total})  # 显示实时总和

完整代码:

import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
import time
import matplotlib.pyplot as plt
from tqdm import tqdm  # 导入tqdm库用于进度条显示# 设置GPU设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(f"使用设备: {device}")# 加载鸢尾花数据集
iris = load_iris()
X = iris.data  # 特征数据
y = iris.target  # 标签数据# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# 归一化数据
scaler = MinMaxScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)# 将数据转换为PyTorch张量并移至GPU
X_train = torch.FloatTensor(X_train).to(device)
y_train = torch.LongTensor(y_train).to(device)
X_test = torch.FloatTensor(X_test).to(device)
y_test = torch.LongTensor(y_test).to(device)class MLP(nn.Module):def __init__(self):super(MLP, self).__init__()self.fc1 = nn.Linear(4, 10)  # 输入层到隐藏层self.relu = nn.ReLU()self.fc2 = nn.Linear(10, 3)  # 隐藏层到输出层def forward(self, x):out = self.fc1(x)out = self.relu(out)out = self.fc2(out)return out# 实例化模型并移至GPU
model = MLP().to(device)# 分类问题使用交叉熵损失函数
criterion = nn.CrossEntropyLoss()# 使用随机梯度下降优化器
optimizer = optim.SGD(model.parameters(), lr=0.01)# 训练模型
num_epochs = 20000  # 训练的轮数# 用于存储每100个epoch的损失值和对应的epoch数
losses = []
epochs = []start_time = time.time()  # 记录开始时间# 创建tqdm进度条
with tqdm(total=num_epochs, desc="训练进度", unit="epoch") as pbar:# 训练模型for epoch in range(num_epochs):# 前向传播outputs = model(X_train)  # 隐式调用forward函数loss = criterion(outputs, y_train)# 反向传播和优化optimizer.zero_grad()loss.backward()optimizer.step()# 记录损失值并更新进度条if (epoch + 1) % 200 == 0:losses.append(loss.item())epochs.append(epoch + 1)# 更新进度条的描述信息pbar.set_postfix({'Loss': f'{loss.item():.4f}'})# 每1000个epoch更新一次进度条if (epoch + 1) % 1000 == 0:pbar.update(1000)  # 更新进度条# 确保进度条达到100%if pbar.n < num_epochs:pbar.update(num_epochs - pbar.n)  # 计算剩余的进度并更新time_all = time.time() - start_time  # 计算训练时间
print(f'Training time: {time_all:.2f} seconds')# # 可视化损失曲线
# plt.figure(figsize=(10, 6))
# plt.plot(epochs, losses)
# plt.xlabel('Epoch')
# plt.ylabel('Loss')
# plt.title('Training Loss over Epochs')
# plt.grid(True)
# plt.show()

模型的推理

测试这个词在大模型领域叫做推理(inference),意味着把数据输入到训练好的模型的过程。

 

# 在测试集上评估模型,此时model内部已经是训练好的参数了
# 评估模型
model.eval() # 设置模型为评估模式
with torch.no_grad(): # torch.no_grad()的作用是禁用梯度计算,可以提高模型推理速度outputs = model(X_test)  # 对测试数据进行前向传播,获得预测结果_, predicted = torch.max(outputs, 1) # torch.max(outputs, 1)返回每行的最大值和对应的索引#这个函数返回2个值,分别是最大值和对应索引,参数1是在第1维度(行)上找最大值,_ 是Python的约定,表示忽略这个返回值,所以这个写法是找到每一行最大值的下标# 此时outputs是一个tensor,p每一行是一个样本,每一行有3个值,分别是属于3个类别的概率,取最大值的下标就是预测的类别# predicted == y_test判断预测值和真实值是否相等,返回一个tensor,1表示相等,0表示不等,然后求和,再除以y_test.size(0)得到准确率# 因为这个时候数据是tensor,所以需要用item()方法将tensor转化为Python的标量# 之所以不用sklearn的accuracy_score函数,是因为这个函数是在CPU上运行的,需要将数据转移到CPU上,这样会慢一些# size(0)获取第0维的长度,即样本数量correct = (predicted == y_test).sum().item() # 计算预测正确的样本数accuracy = correct / y_test.size(0)print(f'测试集准确率: {accuracy * 100:.2f}%')

模型的评估模式简单来说就是评估阶段会关闭一些训练相关的操作和策略 ,比如更新参数 正则化等操作,确保模型输出结果的稳定性和一致性。

@浙大疏锦行

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

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

相关文章

四、生活常识

一、效应定律 效应 1、沉没成本效应 投入的越多&#xff0c;退出的难度就越大&#xff0c;因为不甘心自己之前的所有付出都付之东流。 2、破窗效应 干净的环境下&#xff0c;没有人会第一个丢垃圾&#xff0c;但是当环境变得糟糕&#xff0c;人们就开始无所妒忌的丢垃圾。…

机器学习圣经PRML作者Bishop20年后新作中文版出版!

机器学习圣经PRML作者Bishop20年后新书《深度学习&#xff1a;基础与概念》出版。作者克里斯托弗M. 毕晓普&#xff08;Christopher M. Bishop&#xff09;微软公司技术研究员、微软研究 院 科学智 能 中 心&#xff08;Microsoft Research AI4Science&#xff09;负责人。剑桥…

Python应用嵌套猜数字小游戏

大家好!今天向大家分享的是有关“嵌套”的猜数字小游戏。希望能够帮助大家理解嵌套。 代码呈现: # 1. 构建一个随机的数字变量 import random num random.randint(1, 10)guess_num int(input("输入你要猜测的数字&#xff1a; "))# 2. 通过if判断语句进行数字的猜…

黑马k8s(十四)

1.Service-概述 service&#xff1a;用于四层路由的负载&#xff0c;Ingress七层路由的负载&#xff1b;&#xff0c;先学习service 开启ipvs 2.Service-资源清单文件介绍 修改每个显示的内容 ClusterIP类型的Service Endpoints&#xff1a;建立service与pod关联 亲和性测试…

Kotlin 中 Lambda 表达式的语法结构及简化推导

在 Kotlin 编程中&#xff0c;Lambda 表达式是一项非常实用且强大的功能。今天&#xff0c;我们就来深入探讨一下 Lambda 表达式的语法结构&#xff0c;以及它那些令人 “又爱又恨” 的简化写法。 一、Lambda 表达式完整语法结构 Lambda 表达式最完整的语法结构定义为{参数名…

Kafka Streams 和 Apache Flink 的无状态流处理与有状态流处理

Kafka Streams 和 Apache Flink 与数据库和数据湖相比的无状态和有状态流处理的概念和优势。 在数据驱动的应用中&#xff0c;流处理的兴起改变了我们处理和操作数据的方式。虽然传统数据库、数据湖和数据仓库对于许多基于批处理的用例来说非常有效&#xff0c;但在要求低延迟…

【后端高阶面经:缓存篇】34、高并发下缓存穿透、击穿、雪崩怎么解决

一、缓存三大核心问题:穿透、击穿、雪崩的本质区别 (一)概念对比表 问题类型核心特征典型场景危害等级缓存穿透数据在缓存和数据库中均不存在,请求直接穿透到数据库恶意攻击(伪造不存在的ID)、业务逻辑漏洞★★★★★缓存击穿热点数据在缓存中过期,大量并发请求同时击穿…

使用Rancher在CentOS 环境上部署和管理多Kubernetes集群

引言 随着容器技术的迅猛发展&#xff0c;Kubernetes已成为容器编排领域的事实标准。然而&#xff0c;随着企业应用规模的扩大&#xff0c;多集群管理逐渐成为企业IT架构中的重要需求。 Rancher作为一个开源的企业级多集群Kubernetes管理平台&#xff0c;以其友好的用户界面和…

【Mini-F5265-OB开发板试用测评】按键控制测试

本文介绍了如何使用按键控制 MCU 引脚的输出电平。 原理 由原理图可知 板载用户按键 K1 和 K2 分别与主控的 PB0 和 PB1 相连。 代码 #define _MAIN_C_#include "platform.h" #include "gpio_key_input.h" #include "main.h"int main(void) …

用C#最小二乘法拟合圆形,计算圆心和半径

用C#最小二乘法拟合圆形&#xff0c;计算圆心和半径 using System; using System.Collections.Generic;namespace ConsoleApp2 {internal class Program{static void Main(string[] args){List<Tuple<double, double>> points new List<Tuple<double, doubl…

四、web安全-行业术语

1. 肉鸡 所谓“肉鸡”是一种很形象的比喻&#xff0c;比喻那些可以随意被我们控制的电脑&#xff0c;对方可以是WINDOWS系统&#xff0c;也可以是UNIX/LINUX系统&#xff0c;可以是普通的个人电脑&#xff0c;也可以是大型的服务器&#xff0c;我们可以象操作自己的电脑那样来…

MYSQL丢失pid处理方式

1、停止服务器 systemctl stop mysqld 2、修改 /data/mysql/etc/my.cnf pid-file /tmp/mysql/mysql.pid 改为 pid-file /data/mysql/mysql.pid 3、创建 touch /data/mysql/mysql.pid ch…

《计算机组成原理》第 2 章 - 计算机的发展及应用​

计算机从诞生至今&#xff0c;经历了翻天覆地的变化&#xff0c;应用领域也在不断拓展。本文将结合 Java 代码实例&#xff0c;带你深入了解计算机的发展历程、应用场景及未来展望&#xff0c;让你在学习理论的同时&#xff0c;还能通过实践加深理解。​ 2.1 计算机的发展史​ …

Github 2025-05-26 开源项目周报Top15

根据Github Trendings的统计,本周(2025-05-26统计)共有15个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目5TypeScript项目3JavaScript项目3C++项目2Roff项目1Go项目1C#项目1Jupyter Notebook项目1Rust项目1CSS项目1Shell项目1Dockerfile项目…

详解MYSQL索引失效问题排查

目录 一、快速定位索引失效的步骤 1. 使用 EXPLAIN 分析执行计划详解Mysql的Explain语句 2. 确认索引是否存在 3. 检查查询条件是否符合索引规则 二、常见索引失效场景及解决方法 1. 索引列参与计算或函数 2. 隐式类型转换 3. 使用 LIKE 以通配符开头 4. 使用 OR 连接…

在 springboot3.x 使用 knife4j 以及常见报错汇总

目录 引言&#xff1a; 引入依赖&#xff1a; 配置文件&#xff1a; 过滤静态资源&#xff1a; 增强模式&#xff1a; 便捷地址访问&#xff1a; 常见问题&#xff1a; 注解使用实例&#xff1a; &#x1f4c4; ​文档参考地址​&#xff1a; SpringBoot 3.x 结合 …

【C/C++】环形缓冲区:高效数据流转核心

文章目录 1 核心结构与原理1.1 组成1.2 内存布局1.3 关键操作 2 实现细节与优化2.1 满/空状态的判断2.2 多线程安全&#xff08;无锁实现&#xff09;2.3 性能优化 3 典型应用场景4 代码示例5 优缺点6 对比7 进阶 环形缓冲区&#xff08;Ring Buffer&#xff09;&#xff0c;又…

功耗仅4W!迷你服务器黑豹X2(Panther X2)卡刷、线刷刷入Armbian(ubuntu)系统教程

功耗仅4W&#xff01;迷你服务器黑豹X2&#xff08;Panther X2&#xff09;卡刷、线刷刷入Armbian&#xff08;ubuntu&#xff09;系统教程 前言 前段时间逛海鲜市场的时候留意到一个矿渣盒子&#xff0c;黑豹x2&#xff0c;又是一个类似迅雷赚钱宝这样的挖矿项目已经gg的定制…

【Elasticsearch】更新操作原理

Elasticsearch 的更新操作&#xff08;如 _update 和 _update_by_query&#xff09;在底层实现上有一些复杂的原理&#xff0c;这些原理涉及到 Elasticsearch 的数据存储机制、索引机制以及事务日志&#xff08;Translog&#xff09;的使用。以下是 Elasticsearch 更新操作的主…

【C++】红黑树的实现

目录 前言 一、红黑树的概念 二、红黑树的实现 三、红黑树的查找 四、红黑树的验证 五、红黑树的删除 总结 前言 本文讲解红黑树&#xff0c;主要讲解插入部分的实现&#xff0c;建议在理解了AVL树的旋转后再来学习红黑树&#xff0c;因为红黑树也涉及旋转&#xff0c;并…