第 4 章:第一个神经网络实战——使用 PyTorch

在这里插入图片描述

经过前三章的学习,我们已经对神经网络的理论基础有了扎实的理解。我们知道数据如何前向传播,如何用损失函数评估预测,以及如何通过梯度下降和反向传播来更新网络参数。

理论是根基,但真正的乐趣在于实践。从本章开始,我们将走出理论的殿堂,亲手用代码构建、训练并评估一个真正的神经网络。

我们将使用的工具是 PyTorch,一个由 Facebook 人工智能研究院(FAIR)开发和维护的、当今最流行、最强大的深度学习框架之一。

在本章中,我们将一起完成以下任务:

  1. 环境搭建:安装 PyTorch 并配置好我们的开发环境。
  2. 数据加载:加载并处理经典的 MNIST 手写数字数据集。
  3. 模型构建:使用 PyTorch 的 nn 模块定义我们的神经网络结构。
  4. 模型训练:编写训练循环,实现我们学过的"前向传播 -> 计算损失 -> 反向传播 -> 更新参数"的完整流程。
  5. 模型评估与预测:在测试数据上检验我们模型的性能,并用它来进行预测。

准备好将理论转化为现实了吗?让我们开始吧!

4.1 PyTorch:优雅的深度学习利器

在我们开始编码之前,先简单了解一下为什么选择 PyTorch。

PyTorch 之所以备受学术界和工业界的青睐,主要有以下几个原因:

  • Pythonic: 它的设计哲学与 Python 高度契合,代码直观、易于上手,调试也相对简单。
  • 动态计算图: 与一些早期框架的静态图不同,PyTorch 的计算图是动态的。这意味着你可以在运行时改变网络结构,这为复杂的模型设计提供了极大的灵活性。
  • 强大的生态系统: 拥有丰富的库(如 torchvision 用于图像处理,torchaudio 用于音频处理)和活跃的社区支持。
  • 无缝的 CPU/GPU 切换: 可以非常方便地将计算任务在 CPU 和 GPU 之间切换。

PyTorch 的核心是 张量(Tensor),它是一种多维数组,与我们熟知的 NumPy ndarray 非常相似,但它有一个关键的超能力:可以在 GPU 上进行计算以加速运算。此外,PyTorch 的 自动求导机制(Autograd) 会自动为我们处理所有与梯度相关的计算,让我们从反向传播的复杂数学中解放出来。

环境搭建

现在,让我们来安装 PyTorch。官方推荐使用 condapip进行安装。最稳妥的方式是访问 PyTorch 官网的 “Get Started” 页面,根据你的操作系统(Windows/Mac/Linux)、包管理器(Conda/Pip)、计算平台(CPU/CUDA版本)来生成最适合你系统的安装命令。

对于大多数没有 NVIDIA GPU 的用户,一个典型的 CPU 版本 安装命令如下(使用 pip):

pip install torch torchvision torchaudio

强烈建议 您访问官网获取最准确的命令。

安装完成后,你可以在 Python 解释器或脚本中通过以下代码来验证安装是否成功:

import torch# 打印 PyTorch 版本
print(f"PyTorch Version: {torch.__version__}")# 创建一个张量
x = torch.rand(5, 3)
print("A random tensor:")
print(x)# 检查是否有可用的 GPU
is_cuda_available = torch.cuda.is_available()
print(f"CUDA (GPU) Available: {is_cuda_available}")if is_cuda_available:print(f"CUDA Device Name: {torch.cuda.get_device_name(0)}")

如果代码能够顺利运行并打印出版本号和张量,那么恭喜你,PyTorch 环境已经准备就绪!

在接下来的章节中,我们将使用 Jupyter Notebook 或类似的交互式环境进行编码,这非常适合数据科学和机器学习的探索性工作。

4.2 数据准备:加载与变换 MNIST

在机器学习中,数据是驱动一切的燃料。对于我们的第一个项目,我们将使用 MNIST 数据集,这是一个包含了 70,000 张 28x28 像素的手写数字灰度图像的集合(60,000 张用于训练,10,000 张用于测试),由美国国家标准与技术研究院整理。它是图像分类领域的"Hello, World!"。

幸运的是,torchvision 库让我们可以极其方便地获取和使用它。

1. 定义数据变换

在将图像送入模型之前,我们通常需要进行一些预处理。最常见的两个步骤是:

  • 转换为张量:将 PIL 图像或 NumPy ndarray 转换为 PyTorch 的 Tensor 格式。
  • 归一化 (Normalization):将张量的像素值从 [0, 255] 的范围缩放到一个更小的、以 0 为中心的范围,例如 [-1, 1]。这有助于加速模型收敛并提高性能。

我们可以使用 torchvision.transforms.Compose 将这些操作串联起来。

from torchvision import datasets, transforms# 定义一个转换流程
transform = transforms.Compose([transforms.ToTensor(),  # 将图片转换为张量,并将像素值从 [0, 255] 归一化到 [0.0, 1.0]transforms.Normalize((0.5,), (0.5,))  # 将 [0.0, 1.0] 的范围归一化到 [-1.0, 1.0]
])

注:对于 MNIST 这样的灰度图,其均值(Mean)和标准差(Standard Deviation)都接近 0.5,所以我们使用 (0.5,) 作为归一化参数。

2. 下载并加载数据集

现在我们可以使用 datasets.MNIST 来下载并创建我们的训练集和测试集了。

# 下载训练数据集
train_dataset = datasets.MNIST(root='./data',    # 数据存放的根目录train=True,       # 指定这是训练集download=True,    # 如果 `./data` 目录下没有数据,就自动下载transform=transform  # 应用我们刚刚定义的转换
)# 下载测试数据集
test_dataset = datasets.MNIST(root='./data',train=False,      # 指定这是测试集download=True,transform=transform
)

3. 创建数据加载器(DataLoader)

直接在完整的数据集上进行迭代效率很低。我们通常希望分批次(mini-batch)地、并且随机地给模型喂数据。torch.utils.data.DataLoader 正是为此而生。

DataLoader 是一个迭代器,它将数据集封装起来,为我们提供了批处理、数据打乱、并行加载等一系列功能。

from torch.utils.data import DataLoader# 定义批次大小
batch_size = 64# 创建训练数据加载器
train_loader = DataLoader(dataset=train_dataset,batch_size=batch_size,shuffle=True  # 打乱数据,这在训练时非常重要
)# 创建测试数据加载器
test_loader = DataLoader(dataset=test_dataset,batch_size=batch_size,shuffle=False # 测试时通常不需要打乱数据
)

4. 可视化我们的数据

为了更直观地感受我们正在处理的数据,让我们来看一下训练集中的一些图片。

import matplotlib.pyplot as plt
import numpy as np# 从训练数据加载器中获取一个批次的数据
dataiter = iter(train_loader)
images, labels = next(dataiter)# images.shape 会是 [64, 1, 28, 28],代表 (批次大小, 通道数, 高, 宽)# 创建一个 8x8 的网格来显示图片
fig, axes = plt.subplots(8, 8, figsize=(10, 10))
for i, ax in enumerate(axes.flat):# 显示图片# 我们需要将 Normalize 的效果反转回来以便正确显示img = images[i].numpy().squeeze() # 去掉通道维度ax.imshow(img, cmap='gray')# 显示标签ax.set_title(labels[i].item())ax.axis('off')plt.tight_layout()
plt.show()

运行这段代码,你应该能看到一个 8x8 的网格,里面是各种手写数字的图片及其对应的标签。

到此为止,我们已经成功地将数据准备就绪。下一步,我们将利用这些数据来构建和定义我们的第一个神经网络模型。

4.3 模型构建:定义你的神经网络

数据已经就位,现在是时候构建我们的大脑——神经网络模型了。在 PyTorch 中,任何自定义的模型都是通过创建一个继承自 torch.nn.Module 的类来实现的。这个基类为我们提供了模型追踪、参数管理等一系列底层功能。

我们的模型需要完成以下任务:

  1. 接收一个被"压平"的 28x28 像素图像(即一个长度为 784 的一维向量)作为输入。
  2. 通过几个全连接的线性层(nn.Linear)进行变换。
  3. 在层与层之间使用非线性激活函数(例如 ReLU)来增加模型的表达能力。
  4. 最终输出一个包含 10 个值的向量,每个值代表输入图像是 0 到 9 这 10 个数字中某一个的"得分"或"对数概率"(logits)。

1. 定义模型类

让我们来创建一个名为 SimpleMLP(简单多层感知机)的类。

import torch.nn as nn
import torch.nn.functional as Fclass SimpleMLP(nn.Module):def __init__(self):# 首先,调用父类的 __init__ 方法super(SimpleMLP, self).__init__()# 定义网络的层次结构# 输入层:784 个特征 (28*28)# 第一个隐藏层:128 个神经元self.fc1 = nn.Linear(28 * 28, 128)# 第二个隐藏层:64 个神经元self.fc2 = nn.Linear(128, 64)# 输出层:10 个神经元,对应 10 个类别self.fc3 = nn.Linear(64, 10)def forward(self, x):# 定义数据在前向传播中的流动方式# 1. 压平输入图像# x 的原始 shape: [batch_size, 1, 28, 28]# x.view(-1, 28 * 28) 会将其转换为 [batch_size, 784]x = x.view(-1, 28 * 28)# 2. 通过第一个隐藏层,并应用 ReLU 激活函数x = F.relu(self.fc1(x))# 3. 通过第二个隐藏层,并应用 ReLU 激活函数x = F.relu(self.fc2(x))# 4. 通过输出层# 这里我们不需要应用 softmax,因为 nn.CrossEntropyLoss 会为我们处理x = self.fc3(x)return x

在这个类中:

  • __init__ 方法负责"声明"模型中所有需要学习参数的层。我们定义了三个线性层 fc1, fc2, fc3 (fc = fully connected)。
  • forward 方法则像一张流程图,它接收输入张量 x,并精确地定义了 x 是如何一步步流过我们在 __init__ 中声明的各个部分的。

2. 实例化并查看模型

现在我们可以轻松地创建这个模型的一个实例,并打印它来查看其结构。

# 创建模型实例
model = SimpleMLP()# 打印模型结构
print(model)

运行后,你将看到一个清晰的、描述我们模型结构的输出:

SimpleMLP((fc1): Linear(in_features=784, out_features=128, bias=True)(fc2): Linear(in_features=128, out_features=64, bias=True)(fc3): Linear(in_features=64, out_features=10, bias=True)
)

这告诉我们模型由三个线性层组成,并清晰地标明了每一层的输入和输出特征数。PyTorch 已经自动为我们处理了每一层权重和偏置的初始化。

下面是我们刚刚定义的 SimpleMLP 模型的结构示意图:

在这里插入图片描述

图 4.1: 一个全连接神经网络的结构示意图。我们的模型与之类似,输入层接收压平的图像数据(784个节点),经过两个隐藏层(128和64个节点),最终由输出层(10个节点)得出分类结果。

现在,我们的模型、数据都已经准备就绪。在把它们投入训练的熔炉之前,我们还需最后两个关键组件:

  1. 损失函数(Loss Function / Criterion):定义了我们优化的"目标"。它会衡量模型输出与真实标签之间的差距。
  2. 优化器(Optimizer):定义了我们实现优化的"方法"。它会根据损失函数计算出的梯度,来更新模型的权重。

1. 损失函数

对于像 MNIST 这样的多分类问题,torch.nn.CrossEntropyLoss 是最理想的选择。它是一个非常强大的损失函数,其内部帮我们集成了两个步骤:

  1. nn.LogSoftmax():将模型的原始输出(logits)转换成对数概率。
  2. nn.NLLLoss()(Negative Log Likelihood Loss):计算这些对数概率与真实标签之间的负对数似然损失。

组合在一起,它就能非常有效地衡量我们的分类模型表现有多糟糕。我们的目标就是让这个损失值尽可能地小。

# 定义损失函数
criterion = nn.CrossEntropyLoss()

2. 优化器

优化器负责执行梯度下降算法。PyTorch 在 torch.optim 模块中提供了多种优化算法的实现,如 SGD, Adam, RMSprop 等。

我们将使用 Adam(Adaptive Moment Estimation),它是一种非常流行且通常表现优异的优化算法,它会为每个参数独立地计算自适应学习率。

在创建优化器时,我们需要告诉它两件事:

  1. 哪些参数需要被优化:我们可以通过调用 model.parameters() 来轻松获取模型中所有需要学习的参数。
  2. 学习率(Learning Rate):这是梯度下降中最重要的超参数之一,它控制了每次参数更新的步长。我们先从一个常用的值 0.001 开始。
from torch import optim# 定义优化器,并将模型参数传递给它
# lr = learning rate (学习率)
optimizer = optim.Adam(model.parameters(), lr=0.001)

至此,所有零件都已准备齐全:我们有了数据(DataLoader)、有了模型(SimpleMLP),有了衡量标准(CrossEntropyLoss),也有了更新方法(Adam)。

下一节,我们将把所有这些组件组装起来,构建最终的训练循环,真正开始训练我们的模型!

4.5 训练循环:让模型学习起来

终于,我们来到了最激动人心的部分。我们将把之前准备的所有组件——数据、模型、损失函数、优化器——全部投入到这个训练循环中,让模型真正地开始学习。

训练过程通常包含多个 轮次(Epochs)。一个 Epoch 指的是我们的模型完整地看过一遍训练集中的所有数据。我们会训练多个 Epochs,因为模型需要反复地从数据中学习,才能逐渐优化其内部的参数。

在每一个 Epoch 内部,我们会分批次(mini-batch)地将数据喂给模型,并执行我们烂熟于心的学习五部曲。

训练代码

下面是完整的训练循环代码。它看起来可能有点长,但其核心正是我们反复强调的五个步骤。

# 定义训练的轮次
epochs = 15# 记录训练过程中的损失
train_losses = []print("开始训练...")
for e in range(epochs):running_loss = 0# 内层循环:遍历训练数据加载器,获取每个批次的数据for images, labels in train_loader:# 步骤 1: 梯度清零# 这是非常重要的一步,因为PyTorch默认会累积梯度optimizer.zero_grad()# 步骤 2: 前向传播# 将一个批次的图像数据输入模型,得到预测输出(logits)output = model(images)# 步骤 3: 计算损失# 比较模型的预测输出和真实的标签loss = criterion(output, labels)# 步骤 4: 反向传播# 计算损失相对于模型所有参数的梯度loss.backward()# 步骤 5: 更新参数# 优化器根据梯度更新模型的权重optimizer.step()# 累加批次损失running_loss += loss.item()# 每个 Epoch结束后,打印一次平均损失epoch_loss = running_loss / len(train_loader)train_losses.append(epoch_loss)print(f"训练轮次 {e+1}/{epochs}.. "f"训练损失: {epoch_loss:.3f}")print("训练完成!")

当你运行这段代码时,你会看到损失值随着训练轮次的增加而稳步下降。这表明我们的模型正在从数据中学习,它对数字的预测正变得越来越准确!

例如,你可能会看到类似这样的输出:

开始训练...
训练轮次 1/15.. 训练损失: 0.383
训练轮次 2/15.. 训练损失: 0.160
训练轮次 3/15.. 训练损失: 0.116
...
训练轮次 15/15.. 训练损失: 0.026
训练完成!

这个不断下降的损失值,就是我们所有理论知识和代码工作的最好回报。

我们可以将记录下来的 train_losses 绘制成图表,来更直观地观察学习过程。

import matplotlib.pyplot as pltplt.plot(train_losses, label='Training loss')
plt.title('Loss over time')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.grid(True)
plt.show()

在这里插入图片描述

图 4.2: 训练损失随训练轮次(Epochs)变化的曲线。可以看到损失值迅速下降并逐渐趋于平稳,这表明模型正在有效地学习。

我们的模型已经学有所成。但它究竟学得怎么样?口说无凭,我们需要在它从未见过的数据(测试集)上检验它的真实能力。这就是我们最后一节要做的事情:模型评估与预测。

4.6 模型评估与预测:见证成果的时刻

模型训练完成,但它的表现如何?训练损失低并不完全代表模型泛化能力强。我们需要在独立的测试集上评估其性能,这才是衡量模型真实水平的黄金标准。

1. 模型评估

评估过程与训练过程非常相似,但有几个关键区别:

  1. 开启评估模式:我们需要调用 model.eval()。这会告诉模型中的特定层(如 Dropout, BatchNorm)它们现在处于评估模式,其行为应与训练时不同。对于我们这个简单模型,虽然没有这些层,但这始终是一个好习惯。
  2. 关闭梯度计算:在评估时,我们不需要计算梯度,这可以大大加快计算速度并节省内存。我们可以使用 with torch.no_grad(): 上下文管理器来包裹我们的评估代码。

我们将计算模型在整个测试集上的准确率(Accuracy)

# 准备评估
correct_count, all_count = 0, 0
print("开始评估...")
model.eval() # 切换到评估模式with torch.no_grad(): # 关闭梯度计算for images, labels in test_loader:# 对每个批次进行预测for i in range(len(labels)):img = images[i].view(1, 784)log_ps = model(img) # 获取 log-probabilities# 将 log-probabilities 转换为真实概率ps = torch.exp(log_ps)# 获取概率最高的类别作为预测结果probab = list(ps.numpy()[0])pred_label = probab.index(max(probab))# 与真实标签比较true_label = labels.numpy()[i]if(true_label == pred_label):correct_count += 1all_count += 1print(f"测试集图片总数: {all_count}")
print(f"模型准确率 = {(correct_count/all_count):.3f}")

运行后,你可能会看到一个非常喜人的结果,比如 模型准确率 = 0.975。这意味着我们的模型在它从未见过的 10,000 张图片中,有 97.5% 的概率能够正确识别出数字!对于一个如此简单的模型来说,这是一个非常出色的成绩。

除了计算总体准确率,我们还可以使用 混淆矩阵(Confusion Matrix) 来更深入地分析模型的性能。混淆矩阵可以清晰地展示出模型对于每个类别的分类情况,尤其是哪些类别之间容易被混淆。

from sklearn.metrics import confusion_matrix
import seaborn as sns# 重新获取所有预测和标签用于生成混淆矩阵
y_pred = []
y_true = []model.eval()
with torch.no_grad():for images, labels in test_loader:outputs = model(images)_, predicted = torch.max(outputs, 1)y_pred.extend(predicted.numpy())y_true.extend(labels.numpy())# 计算混淆矩阵
cm = confusion_matrix(y_true, y_pred)# 绘制混淆矩阵
plt.figure(figsize=(10, 8))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=range(10), yticklabels=range(10))
plt.xlabel('Predicted Label')
plt.ylabel('True Label')
plt.title('Confusion Matrix')
plt.show()

在这里插入图片描述

图 4.3: MNIST 测试集的混淆矩阵。对角线上的数字代表该类别被正确预测的数量,颜色越深表示数量越多。非对角线上的数字则代表模型犯错的情况(例如,将真实标签为’9’的图片错误地预测为了’4’)。

2. 单个图像预测与可视化

数字化的准确率固然重要,但亲眼看到模型的预测结果会更加震撼。让我们编写一小段代码,从测试集中随机抽取一些图片,让模型进行预测,并将结果可视化出来。

# 再次获取一批测试数据
dataiter = iter(test_loader)
images, labels = next(dataiter)# 进行预测
output = model(images)
# 将 log-probabilities 转换为概率
ps = torch.exp(output)# 获取预测的类别 (概率最高的那个)
_, top_class = ps.topk(1, dim=1)# 创建一个 8x8 的网格来显示图片和预测结果
fig, axes = plt.subplots(8, 8, figsize=(12, 12))
for i, ax in enumerate(axes.flat):ax.imshow(images[i].numpy().squeeze(), cmap='gray')# 设置标题,绿色为正确,红色为错误ax.set_title(f'Pred: {top_class[i].item()}\nTrue: {labels[i].item()}',color=("green" if top_class[i] == labels[i] else "red"))ax.axis('off')plt.tight_layout()
plt.show()

运行这段代码,你会看到一个图片网格。每张图片的标题都显示了模型的预测值(Pred)和真实值(True)。绝大多数情况下,它们都是绿色的(预测正确),偶尔出现一两个红色的(预测错误),让你能直观地感受到模型的强大,也能看到它犯错的样子。


祝贺你!

你已经成功地走完了从零开始构建一个神经网络的全过程。从抽象的理论到具体的代码实现,你亲手打造并训练了一个能够高精度识别手写数字的智能模型。这不仅仅是一个练习,你掌握的这套流程——数据准备、模型构建、定义损失与优化、训练、评估——是所有更复杂、更强大的深度学习项目的基础。

红色为错误
ax.set_title(
f’Pred: {top_class[i].item()}\nTrue: {labels[i].item()}',
color=(“green” if top_class[i] == labels[i] else “red”)
)
ax.axis(‘off’)

plt.tight_layout()
plt.show()

运行这段代码,你会看到一个图片网格。每张图片的标题都显示了模型的预测值(`Pred`)和真实值(`True`)。绝大多数情况下,它们都是绿色的(预测正确),偶尔出现一两个红色的(预测错误),让你能直观地感受到模型的强大,也能看到它犯错的样子。

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

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

相关文章

MST56XXB/MST5650B/MST5033B 是一款耐高压的LDO芯片,针对中控设备,给MCU供电,60V的耐压,150mA

MST56XXB系列是一款高输入电压(60V)低静态电流、高PSRR线性稳压器(LDO),能够提供150mA负载电流。LDO针对线电压瞬变和负载电流瞬变具有非常快速的响应特性,并确保LDO启动期间和短路恢复过程中不会出现过冲电压。该设备具有集成的短路和热关断保护。该设备…

Java基础(五):流程控制全解析——分支(if/switch)和循环(for/while)的深度指南

Java基础系列文章 Java基础(一):发展史、技术体系与JDK环境配置详解 Java基础(二):八种基本数据类型详解 Java基础(三):逻辑运算符详解 Java基础(四):位运算符详解 Java基础(五):if/switch与for/while - 深入理解…

面向对象概述

1 面向过程程序设计 面向过程是最为实际的一种思考方式,面向对象的方法也是含有面向过程的思想,面向过程是一种基础的方法。它考虑的是实际的实现,一般的面向过程是从上往下步步求精。面向过程最重要的是模块化的思想方法。对比面向对象&…

linux dts overlay

设备树 Overlay(Device Tree Overlays, DTO),它在嵌入式Linux系统(尤其是基于ARM的设备,比如树莓派、NanoPi等)中非常常见。它主要用于动态修改设备树,以适配硬件的变化或扩展外设支持。 1. 设备…

ArkUI-X的声明式语法转换过程

以下是ArkUI-X声明式语法转换过程的详细解析,结合其核心设计原理与实现机制: ‌一、基础语法转换规则 组件声明转换 传统命令式组件创建(如Android XMLJava)转换为ArkUI-X的Component结构: // 命令式(A…

Docker 入门教程(一):从概念到第一个容器

文章目录 🐳 Docker 入门教程(一):从概念到第一个容器1. Docker 是什么?2. Docker 的核心概念3. 安装 Docker4. 运行你的第一个 Docker 容器 🐳 Docker 入门教程(一):从概…

如何在 Vue 应用中嵌入 ONLYOFFICE 编辑器

以下是仅包含 纯前端集成 ONLYOFFICE 文档编辑器到 Vue.js 项目 的完整代码与说明,无需重新创建项目,可直接集成到现有 Vue 项目中: Vue.js 集成 ONLYOFFICE 文档编辑器(纯前端实现) 后端需要部署到服务器&#xff0c…

Cursor 1.0 炸裂功能:在后台运行多个Agent,释放双手

Cursor 1.0 版本更新了用于代码审查的 BugBot、对内存、一键式 MCP 设置、Jupyter 支持以及 Background Agent 的正式发布。 今天这篇文章主要介绍 Background Agent 的使用教程。 文章目录 1. Background Agent 的基本概念2. 后台 Agent 的使用方法3. 让后台 Agent 创造一个简…

MLX LM - 在Apple芯片上运行大语言模型的Python工具包

文章目录 一、关于MLX LM1、项目概览2、相关链接资源3、功能特性 二、安装配置三、使用指南1、快速开始2、Python API3、量化模型,上传HF4、流式生成采样 5、命令行6、长提示词与生成 四、支持模型大模型 一、关于MLX LM 1、项目概览 MLX LM是一个Python工具包&am…

【git学习】学习目标及课程安排

Git 是一款非常强大的版本控制工具,掌握它对编程和团队协作都有巨大帮助。 🎯学习目标(适合个人与团队使用) 理解 Git 和版本控制的基本概念 熟练使用 Git 进行代码提交、分支管理、合并与冲突解决 掌握远程仓库协作流程&#x…

HDFS(Hadoop分布式文件系统)总结

文章目录 一、HDFS概述1. 定义与定位2. 核心特点 二、HDFS架构核心组件1. NameNode(名称节点)2. DataNode(数据节点)3. Client(客户端)4. Secondary NameNode(辅助名称节点) 三、数据…

破局与融合:破解开发与供应链安全的业务场景难题

数字化转型下的安全新范式与融合挑战 在数字化浪潮的席卷下,企业正全面拥抱云计算、微服务、容器化和开源技术。这种转型在极大提升业务敏捷性的同时,也带来了全新的安全挑战:传统网络边界消融,攻击面急剧扩大,“开发安…

Centos7 安装部署Git、创建仓库

概述 Git版本控制,大家都不会陌生,实践诉求是从零部署本地Git服务,在执行推送远程分支代码时,用Git服务自带的hooks触发同步代码文件,从而做到自动同步代码,实现自动更新,操作环境centos7.9,Gi…

【Redis原理】Redis数据结构底层原理

目录 一、SDS 二、IntSet(整数集合) 三、双向链表 四、压缩列表 五、字典(哈希表) 七、跳表 八、QuickList 九、RedisObject 一、SDS Redis 是用 C语言实现的,但是它没有直接使用C 语言的 char* 字符数组来实现…

C#.NET HttpClient 使用教程

简介 HttpClient 是 .NET 中用于发送 HTTP 请求和接收 HTTP 响应的现代化 API,它取代了过时的 WebClient 和 HttpWebRequest 类。 HttpClient 是 .NET Framework 4.5 和 .NET Core/.NET 5 中提供的、基于消息处理管道(message handler pipeline&#…

Nginx常用安全配置指南

Nginx是一个轻量级的,高性能的Web服务器以及反向代理和邮箱代理服务器。它运行在UNIX、GNU、linux、BSD、Mac OS X、Solaris和Windows各种版本。根据调查统计数据显示,当前全球超过6%的网站使用Nginx Web服务器来管理Web网站应用。 为了保证基于Nginx的…

【UniApp 日期选择器实现与样式优化实践】

UniApp 日期选择器实现与样式优化实践 发布时间:2025/6/26 前言 在移动端应用开发中,日期选择器是一个常见且重要的交互组件。本文将分享我们在 UniApp 项目中实现自定义日期选择器的经验,特别是在样式优化过程中遇到的问题及解决方案。通过…

推荐系统的视频特征-视频关键帧特征提取与向量生成

📌 总体流程概览 视频文件 (.mp4)↓ 关键帧抽取(FFmpeg / SceneDetect)↓ 帧图像(.jpg)↓ 图像模型提取特征(CLIP / CNN / ViT)↓ 多帧聚合成视频向量(均值池化等)↓ 向…

Apache SeaTunnel Flink引擎执行流程源码分析

目录 1. 任务启动入口 2. 任务执行命令类:FlinkTaskExecuteCommand 3. FlinkExecution的创建与初始化 3.1 核心组件初始化 3.2 关键对象说明 4. 任务执行:FlinkExecution.execute() 5. Source处理流程 5.1 插件初始化 5.2 数据流生成 6. Transform处理流程 6.1 插…

Vue 3 + Element Plus 实现「动态表单组件」详解教程

✅ Vue 3 Element Plus 实现「动态表单组件」详解教程 📌 适用场景:表单字段根据配置动态生成,支持校验、提交、自定义组件、复杂布局等。 🧩 技术栈:Vue 3 TypeScript Element Plus 🔧 核心特性&#x…