Jiale's Blog - Jiale
type: channel
link: https://jiale.domcer.com/index.php/author/1/
description: Jiale
items: 「转载」北京大学计算机基础能力手册
items: 分布式数据库原理及技术
items: 深度学习（DL）-经典卷积神经网络-测试(model_test)模板代码
items: 深度学习（DL）-经典卷积神经网络-训练(model_train)模板代码
items: 深度学习（DL）-经典卷积神经网络-输入（输出）通道数与卷积核的关系
items: 深度学习（DL）-经典卷积神经网络-以VGG16理解合理设置通道数的原理
items: 深度学习（DL）-经典卷积神经网络-概念理解-通道数到底是什么
items: Anaconda Prompt 中创建虚拟环境
items: 深度学习（DL）-代码探究-张量怎么变成二维矩阵
items: 深度学习（DL）-原理探究-从ReLu函数理解何为反向传播

「转载」北京大学计算机基础能力手册
type: item
date: 2026-06-29T19:22:00+08:00
description: 本网站仅做转载，最终解释权仍在原作者。点击下载main.pdf北京大学计算机基础能力手册

分布式数据库原理及技术
type: item
date: 2026-06-23T19:49:00+08:00
description: 本文章基于安徽理工大学数据科学与大数据技术2024级《分布式数据库原理与技术》课程授课内容整合重构，搭建条理清晰、体系完整的知识框架，可供同学们复习查阅。Chiale2026.6.23一、大数据领域科普概念（Lesson 1，仅作了解）数据中台数据湖仓数据脱敏分布式数据库雏形（ICBC Bank）区块链TCP 三次握手、四次挥手联邦学习 / 深度学习二、分布式数据库理论基础（Lesson 2）三级模式/两级映像在分布式数据库中仍然保留，并增加了新内容：全局模式和分片模式。性能对比指标与传统 RDBMS 对比的指标：存储空间大小、检索效率。HDFS（GFS）的共享存储通过何种机制实现共享服务（问题提出，未在笔记中展开答案）。数据分类结构化数据：MySQL、Oracle 等半结构化数据：JSON、XML非结构化数据：视频等（处理路径：视频 → YOLO → JSON）三、Hive 基础环境与工作模式（Lesson 2 - Lesson 3）Hive 的三种部署方式（Lesson 2 Q4）嵌入式 Derby、远程 Metastore、本地 MetastoreHive 的三种工作模式（Lesson 3 Q1）Derby 模式、远程模式、本地模式环境操作要点启动顺序：关闭防火墙 → 启动 Zookeeper → 检查状态 → 在 master 上 sbin/start-all.sh 启动 HadoopMySQL 安装在 slave2，Hive 启用本地模式关闭顺序：Hive 中 exit → master sbin/stop-all.sh → 所有机器 zkServer.sh stop → 全部关机统一数据库名：bigdata2024，本地数据目录：/dataMetastore 存储位置存储在 HDFS 上的 /warehousedir/home（除 Derby 模式外）四、Hive 表与数据管理（Lesson 3 - Lesson 9）4.1 基本操作查看表结构：desc 表名 （展示字段名）查看详细参数：desc formatted 表名显示建表语句：show create table 表名永久配置：修改 hive-site.xml；临时配置：Hive 控制台直接使用 set命令，例如：set hive.cli.print.current.db=true; set hive.cli.print.header=true;4.2 表的默认存储与数据导入方式默认存储位置：/warehousedir/home/xxxx.db/表名（即 Directory）数据导入的三种方式：insert 语句：insert into table 表名 values(...); 或从其他表查询插入，会在表目录下生成文件 000000_0hdfs 上传：hdfs dfs -put 本地文件 表对应的HDFS目录，文件会直接成为表数据的一部分load data 命令：load data local inpath '本地文件路径' into table 表名;会将本地文件复制到表目录下，若文件与表目录中已有文件重名，会自动生成带序号的副本（如 xxx_copy_1）。若使用 overwrite 关键字则会先清空表目录下的所有文件再装载。4.3 自定义存储位置（LOCATION）可以使用 location 子句指定表在 HDFS 上的存储路径，此时不会自动创建 数据库.db/表名 的层级目录，而是直接使用给定路径。要求：location 路径最好与默认管理体系一致，且精确到表名级别；建表时若有其他约束，location 必须放在最后一行。示例：create table stu_test(sid int, sname string, sage int, addr string) location '/testdb';此时 HDFS 上只有 /testdb 目录，不会出现 xxx.db/stu_test。通常推荐的做法是让 location 的末级目录与表名一致，并放在数据库目录下，以便管理。4.4 复杂数据类型（Array、Map、Struct）详细过程1. Array 类型建表语句：create table student1(sid int, sname string, grade array<float>) row format delimited fields terminated by ',' collection items terminated by '#' lines terminated by '\n';分隔符说明：字段之间用逗号分隔；数组内部元素用 # 分隔；行之间用换行符。数据文件student1.txt 的内容格式如下：1,zhangsan,8…

深度学习（DL）-经典卷积神经网络-测试(model_test)模板代码
type: item
date: 2026-01-31T19:45:00+08:00
description: 一、导入依赖模块import torch import torch.utils.data as Data from torchvision import transforms from torchvision.datasets import FashionMNIST from model import LeNet代码行逻辑与作用关键补充import torch导入PyTorch核心库提供张量计算、设备管理（CPU/GPU）、模型前向传播等基础能力import torch.utils.data as Data导入PyTorch数据加载工具封装数据集为可迭代的DataLoader，简化批次读取、打乱、多进程加载等逻辑from torchvision import transforms导入图像预处理工具用于对FashionMNIST的图像做标准化、尺寸调整、格式转换（如PIL→Tensor）from torchvision.datasets import FashionMNIST导入FashionMNIST数据集服装分类数据集（10类，如T恤、裤子），替代MNIST的手写数字分类，更贴近实际场景from model import LeNet导入自定义的LeNet模型model.py中定义的LeNet卷积神经网络，是本次要测试的模型二、测试数据处理函数 test_data_process作用：加载并预处理测试集，返回可迭代的DataLoader（数据加载器）def test_data_process(): test_data = FashionMNIST(root='./data', train=False, transform=transforms.Compose([transforms.Resize(size=28), transforms.ToTensor()]), download=True) test_dataloader = Data.DataLoader(dataset=test_data, batch_size=1, shuffle=True, num_workers=0) return test_dataloader代码行逻辑与作用关键补充def test_data_process():定义测试数据处理函数（无入参）封装数据加载逻辑，提高代码复用性test_data = FashionMNIST(...)加载FashionMNIST测试集+ root='./data'：数据集保存到当前目录的data文件夹 + train=False：指定加载测试集（train=True为训练集） + transform=...：图像预处理管道（组合多个变换）： - transforms.Resize(size=28)：强制将图像缩放到28×28（确保输入模型的尺寸统一） - transforms.ToTensor()：将PIL图像转为PyTorch张量，同时把像素值从0-255归一化到0-1 + download=True：如果data文件夹无数据集，自动从官网下载test_dataloader = Data.DataLoader(...)封装测试集为DataLoader+ dataset=test_data：指定要加载的数据集 + batch_size=1：批次大小为1（测试时批次不影响准确率，设1方便逐样本验证） + shuffle=True：打乱测试集顺序（测试时shuffle不影响最终准确率，仅改变遍历顺序） + num_workers=0：数据加载的子进程数（Windows系统推荐设0，避免多进程冲突）return test_dataloader返回构建好的测试数据加载器供后续模型测试函数调用三、模型测试函数 test_model_process作用：用测试集评估模型的预测准确率，核心是仅前向传播、不计算梯度def test_model_process(model, test_dataloader): device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") model = model.to(device) #初始化参数 test_correct = 0.0 test_num = 0 #只进行前向传播计算，不计算梯度，从而节省内存，加快运行速度 with torch.no_grad(): for test_data_x, test_data_y in test_dataloader: # 将特征放入到测试设备中 test_data_x = test_data_x.to(device) # 将标签放入到测试设备中 test_data_y =…

深度学习（DL）-经典卷积神经网络-训练(model_train)模板代码
type: item
date: 2026-01-31T19:36:00+08:00
description: 一、核心逻辑先明确整体训练框架：数据准备→模型 / 优化器 / 损失函数初始化→多轮次训练（训练 + 验证）→保存最优模型→可视化，以下按函数拆分讲解。（一）数据处理函数 train_val_data_processdef train_val_data_process(): train_data = FashionMNIST(root='./data', train=True, transform=transforms.Compose([transforms.Resize(size=28), transforms.ToTensor()]), download=True) # 划分训练/验证集（8:2） train_data, val_data = Data.random_split(train_data, [round(0.8*len(train_data)), round(0.2*len(train_data))]) # 构建训练集DataLoader train_dataloader = Data.DataLoader(dataset=train_data, batch_size=32, shuffle=True, num_workers=2) # 构建验证集DataLoader val_dataloader = Data.DataLoader(dataset=val_data, batch_size=32, shuffle=True, num_workers=2) return train_dataloader, val_dataloader逻辑：加载 FashionMNIST 训练集：transform将图片转为模型可接受的 Tensor 格式，download=True自动下载数据集；拆分训练 / 验证集：避免用测试集验证，保证评估客观性；封装 DataLoader：核心是batch_size=32（批次加载）、shuffle=True（打乱数据）、num_workers=2（多线程加速）。（二）核心训练函数 train_model_process1. 初始化核心组件def train_model_process(model, train_dataloader, val_dataloader, num_epochs): # 自动选择GPU/CPU device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # Adam优化器（自适应学习率，更新模型参数） optimizer = torch.optim.Adam(model.parameters(), lr=0.001) # 分类损失函数：交叉熵 criterion = nn.CrossEntropyLoss() # 模型迁移到指定设备（GPU/CPU） model = model.to(device) # 保存最优模型参数（避免过拟合覆盖） best_model_wts = copy.deepcopy(model.state_dict())关键：所有计算（模型 / 数据 / 损失）必须在同一设备，否则报错。2. 训练过程参数初始化best_acc = 0.0 # 最优验证准确率 train_loss_all = [] # 训练损失记录 val_loss_all = [] # 验证损失记录 train_acc_all = [] # 训练准确率记录 val_acc_all = [] # 验证准确率记录 since = time.time() # 训练开始时间（统计耗时）逻辑：每轮 epoch 重置累加变量，避免跨轮污染；记录指标用于后续可视化。3. 多轮次（epoch）训练循环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_corrects = 0 train_num = 0 val_num = 0epoch：每轮完整遍历一次训练集 + 验证集，是训练的基本单位。4. 训练阶段（核心：前向→反向→参数更新）for step, (b_x, b_y) in enumerate(train_dataloader): # 数据迁移到设备（和模型同设备） b_x = b_x.to(device) b_y = b_y.to(device) # 模型设为训练模式（启用Dropout/BatchNorm训练行为） model.train(…

深度学习（DL）-经典卷积神经网络-输入（输出）通道数与卷积核的关系
type: item
date: 2026-01-31T12:57:09+08:00
description: 输入通道数 → 决定「每个卷积核的通道数」（硬性匹配，没得选）人为设定的输出通道数 → 决定「卷积核的总个数」（超参数，自己定）简单说，卷积核有两个关键维度，输入通道数只管其中一个（每个卷积核的深度），不管另一个（卷积核的总数）先明确：卷积核不是 “单个小方块”，而是 “多层小方块叠成的小积木”你可以把一个卷积核想象成「叠起来的多层滤镜」，层数就是这个卷积核的通道数—— 这个层数必须和输入特征图的通道数完全一样，这是卷积运算的硬性规则（维度对不上，根本没法计算）。而卷积层的卷积核总个数，是另一回事，它和输入通道数无关，完全由你想得到的输出通道数决定（就是我们之前说的人为设定的超参数）。举最直观的例子（VGG16 首层，输入 3 通道 RGB 图）输入特征图：224×224×3（3 是输入通道数）我们要的输出通道数：64（人为设定）对应的卷积核设计：卷积核总个数：64 个（和输出通道数一致，人为定的）；每个卷积核的规格：3×3×3（最后一个 3 是卷积核的通道数，和输入通道数 3 硬性匹配，没得选）。卷积的运算过程（为什么必须匹配）：每个 3×3×3 的卷积核，会把自己的 3 层滤镜，分别和输入特征图的 3 个通道做卷积（逐元素相乘求和），然后把 3 个通道的卷积结果加起来，得到1 张单通道的特征图；64 个这样的卷积核，就会得到 64 张单通道特征图，叠在一起就是输出的 224×224×64 特征图（64 个输出通道）。如果输入通道数是 64（比如 VGG16 第一层池化后），要的输出通道数是 128（人为定），那卷积核就是：128 个 3×3×64 的卷积核（64 是卷积核通道数，匹配输入；128 是个数，匹配输出）。核心关系总结维度由什么决定？性质例子（输入 64 通道→输出 128 通道）单个卷积核的通道数输入通道数硬性匹配64（和输入通道数一致）卷积层的卷积核总个数输出通道数（人为定）可调整超参数128（人为设定的输出通道数）再补一个反例，彻底打消疑问如果输入是 64 通道，你想输出 256 通道，那卷积核就是256 个 3×3×64—— 输入通道数还是 64，但卷积核个数直接跳到 256，和输入通道数完全无关，只看你要的输出通道数。反之，如果输入通道数变了（比如从 3 变成 1，黑白图），那卷积核的个数可以不变（还是 64），但每个卷积核的通道数必须从 3 变成 1（3×3×3→3×3×1）。总结输入通道数管 “单个卷积核的深度”（必须匹配），输出通道数管 “卷积核的总数”（人为设定），二者毫无决定关系，只是卷积核的两个不同属性

深度学习（DL）-经典卷积神经网络-以VGG16理解合理设置通道数的原理
type: item
date: 2026-01-31T12:42:31+08:00
description: VGG16 设计通道数（64→128→256→512，后两段保持 512）的核心初衷，是围绕「视觉特征提取的天然规律」，适配自身「小卷积核 + 深度堆叠」的核心结构，同时在「特征表达能力、计算量、训练稳定性、泛化能力」之间做最优的工程平衡—— 这个通道数序列不是单纯的 “拍脑袋实验值”，而是把视觉认知逻辑、网络结构特点、硬件工程限制三者结合后的精准设计，每一个通道数的梯度和上限，都对应着明确的设计目标。初衷 1贴合视觉特征「从简到繁、从少到多」的天然提取规律这是最根本的初衷，所有卷积网络的通道数设计都基于此，VGG16 把这个规律做到了极致简洁的「翻倍式适配」。图片的视觉特征提取是层层组合的过程：「浅层」只能提取低级基础特征（竖线、横线、斜纹、颜色块、明暗交界），这些特征的种类极少、模式固定，根本不需要多通道 ——64 个通道足以把所有基础特征 “抓全”，再多就是计算量浪费（比如用 128 个通道抓竖线，大部分通道会学到重复的特征）；「深层」会把浅层的低级特征拼接、融合成高级语义特征（比如 “竖线 + 横线” 拼成角、“多个角” 拼成眼睛、“眼睛 + 鼻子” 拼成人脸，还有车轮、树叶、桌腿等无数物体部件），这些高级特征的种类呈指数级暴增，必须用更多通道来「分门别类编码」——256→512 的通道数，就是为了给每一种复杂的高级特征分配一个 “专属编码通道”，保证网络能精准区分不同的语义特征，不会把 “眼睛” 和 “车轮” 的特征混在一起。简单说：VGG16 的通道数翻倍，就是跟着特征的 “复杂程度 / 种类数” 翻倍走，让通道数刚好匹配特征的实际需求，不多余、不不足。初衷 2支撑 VGG16「小卷积核 + 深度堆叠」的核心结构（专属设计初衷）VGG16 的标志性设计是放弃大卷积核（如 7×7、5×5），用多个 3×3 小卷积核堆叠（比如 2 层 3×3 卷积代替 1 层 5×5，3 层 3×3 代替 1 层 7×7），这个设计的目的是用更少的参数，获得更多的非线性激活（ReLU），提升特征表达的灵活性。但这个结构有个「硬需求」：必须有足够的通道数，才能避免 “信息瓶颈”——如果深层通道数和浅层一样（比如全程 64），那么多层 3×3 卷积堆叠后，浅层的低级特征会在传递中不断 “挤压”，根本无法充分融合成高级特征，就像用一根细管子传大量水，水会不断溢出，深度堆叠的优势会完全消失。而 64→128→256→512 的通道数递增，就是为深度堆叠的小卷积核 “铺好信息通道”：每一层卷积堆叠前，先增加通道数，让特征融合有足够的 “空间”，保证 VGG16 的深度堆叠能真正发挥作用（更多非线性、更好的特征提取）。初衷 3精准平衡「模型容量」和「计算量 / 硬件限制」深度学习中，通道数是提升模型容量（学习能力）的核心手段（比单纯加深网络更高效），但通道数越多，计算量、显存占用就越大 —— 而 VGG16 诞生时（2014 年），硬件算力和显存还远不如现在（当时主流显卡显存只有 2-4G），通道数设计必须兼顾 “能训、能跑”。VGG16 用 「通道数翻倍 + 池化层尺寸减半」的组合 ，实现了计算量的「动态平衡」，这是最精妙的工程设计：池化层把特征图的长、宽各减半，特征图面积变为原来的 1/4，计算量会大幅下降；紧接着的卷积层把通道数翻倍，计算量会小幅上升；一降一升后，整体计算量基本保持稳定，不会呈指数级爆炸。比如从 64 通道→池化→128 通道：池化让面积减 1/4，卷积核翻倍让通道数 ×2，最终计算量只有原来的2/4=1/2，显存占用也可控；而把深层通道数定在 512，不是不想升到 1024，而是512 是当时硬件能支撑的 “性价比上限”——1024 通道会让显存溢出、训练无法进行，而 512 通道已经能让网络学到足够的高级特征，模型精度和工程实现达到最优平衡。初衷 4保证网络「训练稳定性」，避免梯度消失 / 特征重复如果通道数设计混乱（比如浅层 128、深层 64，或突然从 64 跳到 512），会出现两个问题：梯度消失：深层通道数过少，特征信息在反向传播时会不断丢失，梯度无法有效传回到浅层，网络学不到东西；特征重复：浅层通道数过多，有限的低级特征会让大量通道学到重复的内容，网络出现 “冗余学习”，不仅浪费计算量，还会导致过拟合。而 VGG16缓慢、均匀的翻倍式通道数递增（64→128→256→512），让特征信息在正向传播时能「平稳融合」，反向传播时梯度能「平稳传递」，既避免了梯度消失，又减少了特征重复，保证了网络在深度堆叠的情况下，依然能稳定训练、收敛。同时，深层保持 512 通道不变（最后两段卷积都是 512），也是为了让高级特征的提取更 “聚焦”—— 到了网络最深层，特征已经是最复杂的全局语义特征，不需要再增加通道数，保持 512…

深度学习（DL）-经典卷积神经网络-概念理解-通道数到底是什么
type: item
date: 2026-01-31T12:36:36+08:00
description: 通道数就是特征图的「特征种类数」，也可以理解成网络看图片的「专属视角数」—— 每个通道只盯着图片里的一种特定特征，通道数越多，网络能同时捕捉的特征种类就越全。再往简单说，把卷积网络想象成一群 “特征侦探”，通道数就是「侦探的人数」，每个侦探只负责找一种特定线索，所有侦探的结果合在一起，就是网络对图片的完整特征理解。以「输入图片通道」为例平时看的 RGB 彩色图，就是3 个通道：红色通道（R）：只保留图片里红色的明暗信息（不管绿、蓝）；绿色通道（G）：只保留绿色的明暗信息；蓝色通道（B）：只保留蓝色的明暗信息。这 3 个通道叠在一起，就是你看到的彩色画面。如果是黑白图，就是1 个通道，只有明暗信息，没有颜色种类。通道的本质就是「把信息按种类分开存」，方便后续针对性处理。以VGG16为例，卷积的「网络通道数」是什么卷积层的输出通道数 = 卷积核个数，其实就是给网络新增了对应数量的 “专属侦探”：比如 VGG16 首层 64 个卷积核→64 个通道，就是 64 个侦探，每个侦探只找一种简单特征（有的专找竖线、有的专找横线、有的专找斜纹、有的专找颜色块，都是最基础的视觉线索）；到了深层 512 个卷积核→512 个通道，就是 512 个侦探，每个侦探只找一种复杂特征（有的专找眼睛、有的专找车轮、有的专找树叶轮廓、有的专找桌面边缘，都是浅层简单特征拼出来的 “高级线索”）。每个通道最终会输出一张「特征图」，这张图里越亮的地方，代表图片对应位置越符合这个通道的特征（比如 “竖线通道” 的特征图，图片里竖线的位置会特别亮，其他地方暗）。通道数的「核心作用」：1. 「分门别类存特征」，不混不乱，方便后续融合如果把所有特征都揉在 1 个通道里，网络根本分不清 “这里是竖线”“那里是纹理”，就像把所有侦探的线索堆在一个盒子里，越往后越乱。而通道就是给每个特征配一个 “专属文件夹”，竖线放竖线的文件夹，眼睛放眼睛的文件夹，后续网络可以轻松把这些文件夹里的信息拼起来，还原出图片里的物体（比如 “眼睛 + 鼻子 + 嘴巴” 拼出人脸）。2. 「通道越多，能抓的特征越全」，网络看得越 “明白”浅层用少通道（64/128）：因为图片里的简单特征就那么几种（线、纹、色块），多了也是浪费，相当于没必要找 100 个侦探都去查竖线；深层用多通道（256/512）：因为深层要抓的复杂特征特别多（不同的物体部件、不同的组合方式），需要更多侦探各司其职，才能把图片里的细节都查清楚，相当于查一个复杂案件，需要不同领域的侦探（法医、痕迹、刑侦）一起上。VGG16 里池化层不改变通道数，就是因为池化只是 “把侦探的线索图缩小（去掉无用细节）”，但不会删掉任何侦探（还是原来的 64/128/512 个），线索的种类数没变，只是线索图更简洁了；而卷积层增加通道数，就是新增一批更专业的侦探，从查简单线索升级为查复杂线索。终极大白话总结通道数 = 网络的特征种类数 / 侦探人数；每个通道管一种特征，通道越多，网络能捕捉的特征越全，对图片的理解就越深刻；VGG16 逐层加通道，就是让网络从 “看简单线条” 慢慢升级为 “看复杂物体”。

Anaconda Prompt 中创建虚拟环境
type: item
date: 2026-01-22T08:26:40+08:00
description: 一、基础准备（Anaconda Prompt 中）# 1. 清屏，让界面更整洁 cls二、环境管理（base 环境下执行）# 2. 查看当前系统中所有已创建的conda环境（确认环境列表） conda env list # 3. 方式1：创建指定名称的虚拟环境（推荐，简单易记） # 替换【你的环境名】为自定义名称，比如data_analysis conda create -n 你的环境名 python=3.9 # 3. 方式2：创建指定安装路径的虚拟环境（按需选择） # 替换【你的安装路径】和【你的环境名】，比如D:\conda_env\data_analysis conda create --prefix=你的安装路径\你的环境名 python=3.9 # 4. 激活创建好的虚拟环境（方式1创建的环境） conda activate 你的环境名 # （可选）如果是方式2创建的路径型环境，激活命令如下 # conda activate 你的安装路径\你的环境名三、虚拟环境内的库安装与验证# 5. 查看当前环境已安装的库（激活环境后执行） conda list # 6. 安装指定版本的NumPy（使用清华源加速） pip install numpy==1.21.5 -i https://pypi.tuna.tsinghua.edu.cn/simple # 7. 安装指定版本的Pandas pip install Pandas==1.2.4 -i https://pypi.tuna.tsinghua.edu.cn/simple # 8. 安装指定版本的Matplotlib pip install Matplotlib==3.5.1 -i https://pypi.tuna.tsinghua.edu.cn/simple # 9. 验证NumPy版本（确认安装成功） pip show numpy # （可选）验证其他库版本，替换numpy即可 # pip show pandas # pip show matplotlib四、环境退出与删除（按需执行）# 10. 退出当前虚拟环境，回到base环境 conda deactivate # 11. 删除指定名称的虚拟环境（需先退出该环境） conda remove -n 你的环境名 --all # （可选）删除路径型环境 # conda remove --prefix=你的安装路径\你的环境名 --all

深度学习（DL）-代码探究-张量怎么变成二维矩阵
type: item
date: 2026-01-08T16:21:00+08:00
description: 在深度学习代码中出现了张量如何转化为二维矩阵的例子，用AI整理过后形成了本文。一、先理解：为什么要把张量展平成二维矩阵？全连接层（仿射层）的核心运算是矩阵乘法：y = X · W + b，而矩阵乘法有严格的维度要求：输入X必须是二维矩阵（形状：[样本数, 输入特征数]）权重W是二维矩阵（形状：[输入特征数, 输出特征数]）但实际场景中，输入往往不是二维的：比如批量图片：形状是 [批量数, 通道数, 高, 宽]（例：(100, 3, 28, 28) → 100 张 3 通道 28×28 的图片）比如批量序列数据：形状是 [批量数, 序列长度, 特征维度]（例：(50, 10, 128)）这些高维张量无法直接和权重矩阵做乘法，必须先展平成二维——固定 “样本数” 为第一维，把剩下的所有维度合并成 “特征数” 一维。二、核心操作：X.reshape(X.shape[0], -1) 详解reshape是 NumPy/PyTorch 中重塑数组 / 张量形状的函数，这里的关键是X.shape[0]和-1的组合：部分作用X.shape[0]固定第一维为样本数（批量大小），保证每个样本的特征独立，不混淆。-1NumPy 的 “自动计算维度” 标记：让程序自动计算这一维的大小，等于「总元素数 ÷ 样本数」。例子假设输入是 100 张 3 通道 28×28 的图片，张量形状为 X.shape = (100, 3, 28, 28)：import numpy as np # 模拟输入：100张3通道28×28的图片 X = np.random.randn(100, 3, 28, 28) print("原始形状：", X.shape) # 输出：(100, 3, 28, 28) # 展平成二维矩阵 X_flat = X.reshape(X.shape[0], -1) print("展平后形状：", X_flat.shape) # 输出：(100, 2352) # 2352 = 3 × 28 × 28，刚好是单张图片的所有像素数（特征数）再比如输入是 50 条序列数据（形状(50, 10, 128)）：X = np.random.randn(50, 10, 128) X_flat = X.reshape(X.shape[0], -1) print(X_flat.shape) # 输出：(50, 1280) → 1280 = 10 × 128三、original_x_shape 的关键作用：恢复梯度形状反向传播时，我们计算出的输入梯度dX一开始是展平后的二维形状（比如(100, 2352)），但前一层的输出是高维的（比如卷积层输出是(100, 3, 28, 28)），如果直接把二维的梯度传给前一层，维度会不匹配，导致计算错误。所以original_x_shape的作用就是保存输入的原始形状，反向传播时用它把梯度恢复成原始维度：python运行# 反向传播时恢复梯度形状 dX = np.dot(dy, self.W.T) # 计算出的梯度是二维：(100, 2352) dX = dX.reshape(*self.original_x_shape) # 恢复成(100, 3, 28, 28)这里的*self.original_x_shape是 Python 的 “解包” 操作，把保存的形状元组(100, 3, 28, 28)拆解成参数传给reshape，等价于dX.reshape(100, 3, 28, 28)。总结X.reshape(X.shape[0], -1) 是高维张量适配全连接层的核心：固定样本数维度，自动展平剩余维度为一维，得到 “样本数 × 总特征数” 的二维矩阵。original_x_shape 是反向传播的 “维度桥梁”：保存原始形状，确保梯度能恢复成前一层需要的维度，避免维度不匹配错误。-1 在 reshape 中是 “懒人神器”：无需手动计算展平后的特征数，由程序自动推导，适配任意高维输入。

深度学习（DL）-原理探究-从ReLu函数理解何为反向传播
type: item
date: 2026-01-07T21:48:00+08:00
description: 前排提醒：阅读本文时记得要动脑子和动笔，不要想当然。一、反向传播是什么“反向传播”（Backpropagation） 是深度学习中训练神经网络的核心算法神经网络训练时，从损失函数（最终误差）出发，沿着神经网络的层反向计算每个参数的梯度，并利用梯度下降法更新参数，最终让损失函数变小（模型预测更准）的过程。 前向传播：“输入数据→经过各层计算→输出预测值→计算损失（误差）”（从输入到输出）； 反向传播：“从损失出发→反向算出每一层参数该怎么调整→更新参数”（从输出回到输入）；一般而言，需要做一次前向传播才能做一次反向传播。核心目的：求参数的梯度神经网络的参数（权重 W、偏置 b）需要不断调整，而调整的依据是梯度（参数变化对损失的影响程度）。 反向传播的本质就是用链式法则（微积分里的复合函数求导），把 “损失对最终输出的梯度” 一步步传递回每一层，算出 “损失对每一个参数的梯度”。二、结合ReLu函数假设我们有一个极简网络：输入x → 全连接层（W,b） → ReLU层 → 输出y → 损失L步骤 1：前向传播（正向算结果） 全连接层：z = x*W + bReLU 层：y = max(z, 0)（就是我们写的 ReLU.forward）损失：L = (y - 真实值)²（比如均方误差损失）步骤 2：反向传播（反向算梯度） 我们的目标是算dL/dW（损失对权重 W 的梯度）和dL/db（损失对偏置 b 的梯度），用链式法则拆解： 最终，反向传播就是把这些梯度 “从后往前” 乘起来，得到每个参数的梯度，再用W = W - 学习率*dL/dW更新参数。 逐个拆解（结合 ReLU 类）：dL/dy：损失对 ReLU 输出 y 的梯度（比如L=(y-t)²，则dL/dy=2*(y-t)）；dy/dz：ReLU 的导数（z>0 时 = 1，z≤0 时 = 0）—— 这就是后续 ReLU.backward 里用mask清零梯度的逻辑；dz/dW：全连接层对 W 的导数（=x），dz/db=1；最终，反向传播就是把这些梯度 “从后往前” 乘起来，得到每个参数的梯度，再用W = W - 学习率*dL/dW更新参数。 结合我们写的 ReLU.backward 理解我们的 ReLU 类的backward函数，就是反向传播中 “ReLU 层” 的梯度计算步骤：输入dout：其实就是dL/dy（损失对 ReLU 输出的梯度）；dout[self.mask] = 0：就是计算dy/dz * dout（因为 z≤0 时dy/dz=0，所以梯度清零；z>0 时dy/dz=1，梯度不变）；返回的dx：就是dL/dz（损失对 ReLU 输入 z 的梯度），会传递给前一层（全连接层）继续反向计算。反向传播的关键特点链式法则是核心：把复杂的复合函数求导拆解成多个简单导数的乘积，从后往前逐层计算；效率高：一次反向传播就能算出所有参数的梯度，不用重复计算；和前向传播配套：前向传播时会记录中间结果（比如 ReLU 的mask），反向传播时复用这些结果快速算梯度。三、具体简单案例指定的数值（x=2、W=3、b=1、真实值 = 5），搭建一个极简神经网络（输入→全连接层→ReLU 层→损失）第一步：先明确网络结构我们的极简网络只有 3 层：输入x → 全连接层（W=3, b=1）→ ReLU层 → 输出y → 损失L（均方误差）全连接层公式：z=x∗W+bReLU 层公式：y=max(z,0)损失函数（均方误差）：L=1/2(y−t)2（加 1/2 是为了求导时消去系数，不影响梯度方向）第二步：前向传播（正向算结果）我们从输入 x 开始，一步步算到损失 L：步骤公式数值计算结果1. 全连接层计算z=x∗W+bz=2∗3+1=7z=72. ReLU 层计算y=max(z,0)y=max(7,0)=7y=73. 损失计算L=1/2(y−t)2L=1/2(7−5)2=1/2∗4=2L=2前向传播结论：输入 x=2 经过网络后输出 y=7，和真实值 5 的损失是 2。第三步：反向传播（反向算梯度）反向传播的核心是链式法则，从损失 L 出发，反向算每一层的梯度，最终得到损失对 W、b、x 的梯度（我们的目标是更新 W 和 b）。先明确反向传播的目标我们需要算 3 个梯度：dL/dy：损失对 ReLU 输出 y 的梯度dL/dz：损失对全连接层输出 z 的梯度（ReLU 的输入）dL/dW、dL/db：损失对全连接层参数 W、b 的梯度（最终要用来更新参数）逐步骤计算梯度（纯数值）步骤 1：计算dL/dy（损失对 ReLU 输出 y 的梯度）公式：dL/dy=dyd[1/2(y−t)2]=y−t数值计算：dL/dy=7−5=2结论：dL/dy=2步骤 2：计算dL/dz（损失对全连接层 z …