news 2026/10/5 7:03:31

用TensorFlow从零搭建CNN:数据量与卷积核谁更影响精度?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用TensorFlow从零搭建CNN:数据量与卷积核谁更影响精度?

简介:面向深度学习初学者与TensorFlow入门者,这份PDF以MNIST手写数字识别为例,完整演示了用Python实现CNN的代码过程:网络包含两个卷积层和一个全连接层,卷积层采用ReLU激活并配合2x2最大池化,全连接层使用Softmax输出十类概率,同时加入Dropout防止过拟合,损失函数选用交叉熵,并用Adam优化器完成训练。包内为1个PDF文件,大小仅56KB,内容紧凑,代码段与运行结果均做了转写标注,便于随时对照阅读。作者还专门做了两组实验:训练数据从1000增至100000时准确率明显提升,而仅把卷积核从16/32增至32/64对性能提升有限,这一结论对初学调参、理解数据量与模型容量关系很有参考价值。资源已有3760人学习下载,适合快速掌握CNN基础流程并希望少走弯路的读者。

1. 用 TensorFlow 从零搭一个 CNN:这套代码为什么值得照着敲一遍

不少刚接触深度学习的读者,把 CNN 的代码来回抄了十几遍,却始终没搞明白每一层张量到底变成了什么样、为什么卷积核数量往上加效果却不明显。这份 Python CNN 卷积神经网络代码实现,正好把这两件事说透了。它基于 TensorFlow 1.x 的经典接口,在 MNIST 上构造了一个两层卷积加一层全连接的小型网络,并用「固定模型增大训练集」和「固定训练集增大卷积核」两组对照实验,直接回答了一个很多教程回避的问题:精度上不去,到底是数据不够,还是模型不够大?适合正在入门 CNN、想亲手改参数看效果,以及准备在课程设计或实验报告中复现一组可对比数据的读者。

代码本身不复杂,十几分钟就能跑通,但里面藏着的网络设计逻辑、参数联动关系和过拟合处理方式,才是这份资源真正的价值所在。下面按我自己的拆解习惯,从网络结构逐步讲到踩坑点,最后再分享几个改代码做扩展的小技巧。

2. 先看懂网络骨架:两层卷积加池化,为什么是 16→32→512 这个组合

这份代码的网络结构属于 LeNet 风格的小型 CNN,针对 MNIST 这种 28×28 灰度手写数字图片设计。MNIST 每张图是单通道,分辨率不高,类别只有 10 个,所以不需要 ResNet 那种几十层的深度,两层卷积加一层全连接已经足够跑出不错的精度。理解这个网络,关键不是背代码,而是跟着张量的维度变化走一遍。

2.1 输入张量的 reshape:从 784 到 28×28×1

MNIST 数据集的原始输入是 784 维的向量,也就是把 28×28 的图片按行展开。CNN 要求输入保持空间结构,所以第一步必须 reshape 成四维张量,对应 TensorFlow 的 NHWC 格式:batch、height、width、channels。

x = tf.placeholder(tf.float32, [None, 784]) y = tf.placeholder(tf.float32, [None, 10]) x_image = tf.reshape(x, [-1, 28, 28, 1])

这里-1表示 batch 维度由 TensorFlow 自动推断,你喂多少张图它就变成多少。784是 28×28 的展平长度,1是通道数,因为 MNIST 是灰度图。如果换成 RGB 彩色图,这个位置要改成 3,后面的卷积核输入通道也要跟着变。placeholder 的第一个维度设为None,是为了让训练和测试阶段使用不同的 batch 大小,训练时一次喂 50 张,测试时可以一次性喂全部 10000 张。

2.2 卷积核尺寸和通道数:5×5 滤波器的作用

代码里第一个卷积层是weight_variable([5, 5, 1, 16]),第二个是[5, 5, 16, 32]。这四个数字分别表示卷积核的高度、宽度、输入通道数、输出通道数。5×5 是经典的卷积核尺寸,在 MNIST 这种小分辨率图像上,它比 3×3 有更大的感受野,能更早地捕捉到笔画级别的局部特征。第一层输入通道是 1(灰度图),输出 16 个特征图;第二层输入通道变成 16,因为要承接第一层的输出,输出 32 个特征图。

通道数从 16 翻倍到 32,是一个很常见的 CNN 设计惯例。每经过一次池化,特征图的空间尺寸减半,信息被压缩,通道数随之翻倍,以保留足够多的特征表达。全连接层的输入维度7*7*32就是第二层池化后的结果:原始 28×28 经过两次 2×2 最大池化,尺寸变成 7×7,通道数 32,展平后就是 1568。如果改动卷积层的输出通道数,这个 7×7 的数字本身不变,但最后的7*7*32必须同步修改,这是改代码时最容易翻车的地方。

2.3 参数初始化为什么用截断正态分布加常数偏置

def weight_variable(shape): initial = tf.truncated_normal(shape, stddev=0.1) return tf.Variable(initial) def bias_variable(shape): initial = tf.constant(0.1, shape=shape) return tf.Variable(initial)

权重的初始化方式用的是truncated_normal,标准差 0.1。截断正态分布和普通正态分布的区别在于,它会丢弃距离均值超过两个标准差的样本,避免初始化时出现极端大的权重值。神经网络训练初期,过大的权重很容易让激活函数饱和,梯度消失或爆炸的问题会提前出现。偏置全部初始化为 0.1 的常数,是为了防止 ReLU 神经元在初始化阶段就大量死于负区间,保证前向传播时有一定比例的神经元处于激活状态。

实际使用中,如果发现训练初期 loss 不下降,可以检查一下偏置初始化和权重标准差。如果发现收敛速度明显偏慢,把标准差调到 0.05 或 0.15 试试,但幅度别太大。

2.4 卷积与池化的参数语义:SAME padding 和 2×2 窗口

def conv2d(x, W): return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME') def max_pool_2x2(x): return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

卷积的 strides 四元组对应 NHWC 格式,[1, 1, 1, 1]表示 batch 和通道维度步长为 1,高和宽方向也以 1 像素滑动。padding 设为 SAME,意思是输出尺寸与输入尺寸保持相同,边缘不足的部分自动补零。池化层的 ksize 是[1, 2, 2, 1],步长同样是[1, 2, 2, 1],即每个 2×2 窗口取最大值,然后窗口向右和向下移动 2 个像素,不重叠。

配合 SAME padding,两次池化后 28×28 → 14×14 → 7×7,每一次空间尺寸减半。这里要注意一个很容易踩的坑:如果后面改用了 padding='VALID',7×7 的输出尺寸会变成 6×6 或者需要重新计算,全连接层的输入维度就得重新推。SAME 只是保证卷积输出尺寸不变,池化仍然会把尺寸减半,不要看到 SAME 就以为池化也不降尺寸。

3. 训练过程拆开看:损失函数、Dropout 和优化器是如何协同工作的

网络结构只是骨架,真正决定模型能不能收敛的是训练环节的四个选择:交叉熵损失、Adam 优化器、Dropout 比例和 batch 大小。这段代码把每个环节都压缩到了极简形式,但每一行的作用都需要展开理解。

3.1 交叉熵损失:为什么分类任务不用均方误差

cross_entropy = tf.reduce_mean(-tf.reduce_sum(y * tf.log(y_conv), reduction_indices=[1]))

这一行是手工展开的交叉熵公式。y是 one-hot 编码的真实标签,y_conv是 softmax 输出的预测概率。逐元素相乘后求和,得到每个样本的交叉熵,再用reduce_mean取平均。分类任务不用均方误差,是因为 softmax 输出的概率分布和 one-hot 标签之间的均方误差梯度在饱和区域会变得非常小,收敛速度明显变慢;交叉熵配合 softmax 的梯度形式更干净,这是深度学习中「默认组合」级别的共识。

这里把reduction_indices=[1]放在行内,等价于现在更常见的axis=1。如果要在新版 TensorFlow 或 PyTorch 里复刻这行,语义完全一样的写法是F.cross_entropy(logits, target),但手工展开的版本对理解原理更有帮助。

3.2 Adam 优化器与学习率:1e-4 这个量级怎么选

train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

Adam 是自适应矩估计优化器,它会为每个参数单独维护学习率,对梯度稀疏或尺度差异大的问题有较好的适应性。相比原始的 SGD,Adam 对学习率的敏感度低很多,但这不意味着可以乱设。1e-4 是 MNIST 这类小数据集上比较稳妥的起点,如果学习率调到 1e-2 或 1e-3,前几步 loss 可能会急剧抖动,甚至出现 NaN。

判断学习率是否合适的做法是:先跑几十个 batch 观察 loss 变化曲线,如果 loss 震荡不降,优先调低学习率;如果 loss 降得很慢,再考虑调高。代码里只训练 20 轮,每轮 50 张图,总共只看了 1000 个样本,这个量级配合 1e-4 是够用的。如果复制这套代码去训练更大的数据集,比如 CIFAR-10,1e-4 会显得偏保守,可以把迭代轮数提高或者改用学习率衰减策略。

3.3 Dropout 的 keep_prob 两个取值:训练 0.5,测试 1.0

keep_prob = tf.placeholder(tf.float32) h_fc_drop = tf.nn.dropout(h_fc, keep_prob)

Dropout 在训练时随机把一部分神经元的输出置零,迫使网络不依赖单一神经元,从而减轻过拟合。这里keep_prob设计成 placeholder,就是为了训练和测试阶段使用不同的值。训练时设为 0.5,即随机丢弃一半的神经元;测试时设为 1.0,即保留全部神经元。

需要注意,测试时设置 keep_prob 为 1.0 不只是「关闭随机性」这么简单,它还隐含了权重缩放逻辑。TensorFlow 的 dropout 实现会在训练时把保留的神经元输出除以 keep_prob,保证期望值不变,所以测试时不缩放也不会影响输出尺度。如果自己手写 dropout,记得在训练时做 scaled 处理,否则测试阶段的输出会比预期偏小。

3.4 训练循环与准确率评估:为什么要分开喂 feed_dict

tf.global_variables_initializer().run() for i in range(20): batch = mnist.train.next_batch(50) train_step.run(feed_dict={x:batch[0], y:batch[1], keep_prob:0.5}) print("test accuracy %g" % accuracy.eval(feed_dict={x:mnist.test.images, y:mnist.test.labels, keep_prob:1.0}))

训练循环只跑了 20 个 step,每个 step 取 50 张图,累计 1000 条训练样本。这个设定在资源里是有意为之的,目的是控制变量去对比不同条件下的精度差异,而不是追求最高分。注意train_step.run()和accuracy.eval()都需要通过 feed_dict 提供数据,区别在于训练时 keep_prob 是 0.5,测试时是 1.0。如果把 keep_prob 忘了喂,TensorFlow 会直接报错,因为 placeholder 没有被赋值。

还有一个是初学者容易踩错的点:mnist.train.next_batch(50)每次会从训练集里随机抽取 50 张图,并且是有放回的抽取,所以 20 个 step 看到的不是 20×50 条独立样本,而是重复抽样。这在小训练量实验里是正常的,不影响控制变量对比的结论,但如果想复现一个稳定的最终精度,建议把训练轮数提到 500 以上。

4. 这份资源真正的核心:两组对照实验揭示精度瓶颈在哪

我在拆很多课程设计资源时,最怕碰到那种只丢一份代码、不给实验结论的。这份资源好就好在,代码后面直接附了两组控制变量实验结果,而且结论非常反直觉:训练集只有 1000 条时,把卷积核从 16→32 加到 32→64,精度提升极其有限;反过来保持模型不变,把训练集从 1000 加到 10000,精度提升非常明显。这个结论如果自己不去做实验,很难从直觉上判断对错。

4.1 第一组实验:模型固定,训练集从 1000 加到 100000

资源里的实验设置很清晰:算法模型结构保持不变,第一层 16 个卷积核,第二层 32 个卷积核,全连接层 512 维,10 分类。训练集分别取 1000、10000、100000 条。代码里只写了 20 个 step,如果要复现 10000 和 100000 组的实验,需要把训练轮数和 batch 对应地调大。

我建议按这样的方式复现:保持 batch=50 不变,1000 条训练集对应 20 轮,10000 条对应 200 轮,100000 条时要注意,MNIST 训练集本身只有 55000 条,next_batch是有放回抽样,所以 100000 条实际上是多个 epoch 的组合,设置 2000 轮即可。

# 复现时建议改成变量控制,而不是硬编码 20 train_steps = 200 for i in range(train_steps): batch = mnist.train.next_batch(50) train_step.run(feed_dict={x: batch[0], y: batch[1], keep_prob: 0.5})

在固定模型的条件下,增大训练集带来的收益是单调且显著的。原因在于模型容量没有变,欠拟合是主要矛盾,更多数据让网络看到了更多笔画变体,对数字形态的覆盖更完整。MNIST 本身类别均衡、标注质量高,不存在数据噪声问题,所以数据量越大,梯度估计越稳定,收敛到的局部最优也越好。

4.2 第二组实验:训练集固定,卷积核从 16→32 加到 32→64

第二组实验把训练集分别固定为 1000、10000、100000,模型改为第一层 32 个卷积核,第二层 64 个卷积核,全连接层保持 512。对应的改动位置在代码里很清楚。

W_conv = weight_variable([5, 5, 1, 32]) b_conv = bias_variable([32]) W_conv2 = weight_variable([5, 5, 32, 64]) b_conv2 = bias_variable([64]) W_fc = weight_variable([7 * 7 * 64, 512]) b_fc = bias_variable([512])

第二层卷积核的输出通道从 32 改成 64 之后,全连接层的输入维度从7*7*32必须同步改成7*7*64,否则矩阵乘法维度不匹配,直接报错。这一步是改动网络结构时最容易出的问题。我可以直接说结论:在训练集只有 1000 条时,加大卷积核数量几乎不涨点,因为模型已经进入过拟合区间,参数多了反而更容易记住训练集里的个别样本,泛化能力没有实质提升;当训练集达到 10000 甚至 100000 条时,增大的模型容量才被数据「喂饱」,精度提升才开始显现。

4.3 两组实验合起来读:先加数据还是先加参数

资源结尾的总结非常到位:训练集较小时,一味增加卷积核数量对预测性能的提升十分有限;相同模型下,适当增加训练集数据对模型提升非常明显;想达到更高性能,两者可以兼得。这句话其实揭示了深度学习调参的一个底层逻辑,模型容量和数据量必须匹配。

签出一个实际判断标准:如果增大模型后,训练集精度显著提升但测试集精度不动,说明模型已经过拟合,此时优先加数据或加正则化;如果增大模型后训练集和测试集精度一起提升,说明模型欠拟合,此时加大参数才有意义。资源里 1000 条训练集的场景就属于前者,100000 条的场景接近后者。

提示:复现这两组实验时,不要只改一个参数就急着看结果,至少跑三次取平均。小训练量下精度波动范围可能有 1~2 个百分点,一次实验结果很容易误导判断。

5. 照着跑会遇到的问题:TensorFlow 版本兼容与数据路径的四个高频坑

这份代码是基于 TensorFlow 1.x 的经典接口写的,用现在的 TensorFlow 2.x 直接跑会报一堆错。我在复现时踩过几个典型的坑,逐个写出来,按「现象 → 原因 → 解决」的方式整理,省得你来回翻文档。

5.1 tf.InteractiveSession 报错或不存在

现象:在 TensorFlow 2.x 环境中运行sess = tf.InteractiveSession(),直接报 AttributeError,或者提示Session只能在 eager mode 关闭时使用。

原因:TensorFlow 2.0 开始默认开启 eager execution,并且把tf.Session等图执行接口移到了tf.compat.v1命名空间。原始代码基于 2018 年的 TensorFlow 1.x 编写,当时InteractiveSession是最常见的写法。

解决:在代码开头强制关闭 eager mode,并调用兼容接口。

import tensorflow.compat.v1 as tf tf.disable_v2_behavior()

加上这两行之后,tf.InteractiveSession、tf.placeholder、tf.nn.dropout这些旧接口都能在 TensorFlow 2.x 下正常工作。建议把原来的import tensorflow as tf改成import tensorflow.compat.v1 as tf,这样后面所有 API 都走兼容层,不容易出现一半新版一半旧版的混乱局面。

5.2 MNIST 数据集下载卡死或路径识别不了

现象:运行到input_data.read_data_sets时长时间卡住,或者提示EOFError、文件不存在。原因:read_data_sets在本地找不到数据集时会自动尝试从网络下载,但国内访问原始下载地址经常超时或连接中断。原代码里给的是本地路径C:/Users/zhen/MNIST_data_bak/,别人机器上当然没有这个目录。

解决:提前手动下载 MNIST 四个文件,放到本地目录,然后把路径改成自己的目录。需要的是 train-images-idx3-ubyte.gz、train-labels-idx1-ubyte.gz、t10k-images-idx3-ubyte.gz、t10k-labels-idx1-ubyte.gz 这四个文件,read_data_sets会自动识别 gz 压缩格式,不需要手动解压。路径中不要出现中文,Windows 下写成D:/data/mnist/这种正斜杠形式。

5.3 全连接层维度不匹配,报矩阵乘法错误

现象:修改卷积核数量后,运行到tf.matmul(h_pool_flat, W_fc)报维度错误,提示 shape 对不上。

原因:h_pool_flat的维度由7*7*32硬编码在 reshape 里,而这一步和W_conv2的输出通道数强相关。只改了卷积层的输出通道数,忘记了同步修改全连接层输入维度,两者不匹配。

解决:要么把张量形状打印出来做精确计算,要么把这个数字提炼成变量集中管理。

conv2_output_channels = 64 h_pool2_shape = 7 * 7 * conv2_output_channels W_fc = weight_variable([h_pool2_shape, 512]) h_pool_flat = tf.reshape(h_pool2, [-1, h_pool2_shape])

我一般会建议用第二种方式,把7*7*32和7*7*64这类魔法数字统一定义,改模型结构时只动一处。如果你用的是不同尺寸的输入图片,7×7 这个数字也要跟着重新推算,不能照抄。

5.4 训练不收敛或 loss 为 NaN

现象:loss 在前几步就跳到 NaN,或者一直不下降,准确率维持在 10% 左右(等于瞎猜)。原因:常见原因有三个,学习率过大、权重初始化标准差过大、输入数据没有归一化。MNIST 的数据已经做了归一化处理,所以这个数据集上后两个问题不太常见,更多是学习率设置问题。

解决:确认你用的是 1e-4 而不是 1e-2;把初始化标准差从 0.1 降到 0.05;打印第一个 batch 的 loss 值,如果初始 loss 就是 NaN,优先检查标签是否为 one-hot 编码、tf.log(y_conv)是否出现了 log(0) 的情况。softmax 输出理论上在 (0,1) 区间,但由于浮点精度,极小概率会被截断为 0,可以给tf.log(y_conv)加上一个极小 epsilon 做保护。

6. 改代码做扩展的实用技巧:从 MNIST 迁移到自己的数据,只需要动四个位置

很多人拿到这份代码以后,第一反应是想让它跑自己的数据集。这个迁移过程比想象中简单,关键位置只有四个:输入尺寸、通道数、分类数量、数据加载方式。只要按顺序改,基本不会出问题。

6.1 替换数据加载:从 MNIST 到自己的图片文件夹

原始代码使用mnist.train.next_batch直接取数据,换成自己的数据后,最小改动方式是保留 placeholder 结构,用批量读取代替。

import cv2 import os import numpy as np def load_images_and_labels(data_dir): images, labels = [], [] for label_name in os.listdir(data_dir): label = int(label_name) label_dir = os.path.join(data_dir, label_name) for file_name in os.listdir(label_dir): img_path = os.path.join(label_dir, file_name) img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) img = cv2.resize(img, (28, 28)) img = img.astype(np.float32) / 255.0 images.append(img.reshape(-1)) labels.append(label) return np.array(images), np.array(labels) data_dir = './my_dataset' X_data, y_data = load_images_and_labels(data_dir) y_data_onehot = np.eye(10)[y_data]

这段代码的核心是把每张图片统一缩放到 28×28、归一化到 0~1、展平成 784 维向量,然后做 one-hot 编码。np.eye(10)[y_data]这行的意思是,生成一个 10 行 10 列的单位矩阵,按标签值索引取出对应的 one-hot 行向量。如果你的类别不是 10 个,把 10 改成实际类别数。

6.2 动态调整网络输入和输出维度

图片如果不再是 28×28,比如 64×64,第一层卷积的输入尺寸、池化后的中间维度都要改。原代码里全连接层的输入是硬编码的7*7*32,换成 64×64 输入,经过两次 2×2 池化后变成 16×16,全连接输入就应该是16*16*32。

input_h, input_w = 64, 64 conv1_output = 32 conv2_output = 64 pooled_h = input_h // 4 pooled_w = input_w // 4 x_image = tf.reshape(x, [-1, input_h, input_w, 1]) W_conv2 = weight_variable([5, 5, conv1_output, conv2_output]) fc_input_dim = pooled_h * pooled_w * conv2_output W_fc = weight_variable([fc_input_dim, 512]) h_pool_flat = tf.reshape(h_pool2, [-1, fc_input_dim])

// 4是因为两次 2×2 池化,尺寸各缩一半,合计缩到四分之一。这里假设输入尺寸是偶数且能被 4 整除,否则需要配合 padding 策略重新计算。

6.3 预测结果输出与保存模型

原代码只打印了测试准确率,实际使用中往往需要把预测结果以图像形式可视化,或者保存模型权重供后续推理。

# 保存模型 saver = tf.train.Saver() saver.save(sess, './mnist_cnn_model/model.ckpt') # 加载模型 saver.restore(sess, './mnist_cnn_model/model.ckpt') # 对单张图片预测 prediction = tf.argmax(y_conv, 1) result = prediction.eval(feed_dict={x: img.reshape(1, -1), keep_prob: 1.0})

tf.argmax(y_conv, 1)的作用是在预测概率向量中取最大值所在的下标,就是最终的类别编号。keep_prob在预测时设为 1.0,这一点和测试阶段保持一致,不要在这里设成 0.5。

6.4 学习率衰减与训练轮数调整

想在新数据集上得到更高精度,可以直接在同一份代码上增加学习率衰减,不需要改网络结构。常见的做法是指数衰减:每训练一定轮数,学习率乘以一个衰减系数。

global_step = tf.Variable(0, trainable=False) learning_rate = tf.train.exponential_decay( 1e-4, global_step, decay_steps=500, decay_rate=0.96, staircase=True) train_step = tf.train.AdamOptimizer(learning_rate).minimize( cross_entropy, global_step=global_step)

decay_steps=500表示每 500 步衰减一次,decay_rate=0.96表示每次乘以 0.96,staircase=True表示阶梯式衰减而不是连续衰减。这套参数是很多小型 CNN 实验的常见起点,跑出来的精度通常比固定学习率更稳定。

我自己的习惯是拿到任何一份 CNN 代码,第一件事就是先打印每一层的输出张量形状,确认维度链路完整后再开始训练。这份代码我在复现时踩过最多的坑就是全连接层维度没同步修改,以及 TensorFlow 2.x 环境下 Session 兼容问题。从那以后我每次改动卷积核数量或输入尺寸,都会强制走一遍「改通道数 → 同步改 fc 输入维度 → 打印形状确认」的流程,十几秒的事,能省掉大量排错时间。希望这份拆解能帮你在复现时少走几步弯路。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 7:02:11

城市大脑数字底座一网统管云平台建设:从数据到事件闭环的实战路径

简介:一份面向城市治理数字化与智慧城市建设的完整解决方案文档,适用于政府信息化部门、智慧城市项目规划人员及解决方案架构师。文档围绕城市大脑一体化数字底座,系统梳理数据中台、AI中台、技术中台、业务中台和云平台基础设施的需求&#…

作者头像 李华
网站建设 2026/10/5 7:02:01

IEEE 802.1Qcc与TSN流预留:从SRP分布式协商到集中式配置落地

简介:IEEE 802.1Qcc-2018是时间敏感网络(TSN)协议族中的关键标准,作为IEEE 802.1Q-2018的第31号修正案,定义了流预留协议(SRP)的增强与性能改进,用于提升局域网和城域网中时间敏感流…

作者头像 李华
网站建设 2026/10/5 6:59:03

子域名基础05_Web与网络层

子域名挖掘前置基础 05:Web 与网络层本篇定位:前面四篇讲的都是 DNS 层——域名怎么解析、记录是什么、委派怎么运作。但子域名解析到 IP 之后,背后跑的是 Web 和网络服务。本篇讲子域名背后的 Web 与网络层——CDN、负载均衡、网络架构图、H…

作者头像 李华
网站建设 2026/10/5 6:58:34

Java 项目实战: 外卖平台优化-YApi接口管理平台与文档导入导出

前后端分离: YApi 接口管理平台的定义、导出与批量导入 纲要 完整使用链路: YApi 是什么:高效、易用、功能强大的 API 管理平台,需自行部署定义接口:创建项目 → 添加分类 → 添加接口 → 配置请求参数与响应数据接口状态流转&…

作者头像 李华
网站建设 2026/10/5 6:58:10

【嵌入式学习】嵌入式原理知识-RTC,PWR,Flash及低功耗(十)

1. Unix时间戳 1.1 Unix时间戳简介Unix时间戳(Unix Timestamp)定义为从UTC/GMT的1970年1月1日0时0分0秒开始,共计经历的秒数,不考虑闰秒。 只使用秒计时,不进位。时间戳存在于一个秒计数器,为32位/64位的整…

作者头像 李华