
1、人工智能-机器学习-深度学习之间的关系
-
人工智能是模拟人类智能的广泛技术领域
-
机器学习(Machine Learning,ML):人工智能的一个子领域,专注于让计算机通过数据学习和改进性能,无需显式编程。
-
深度学习(DeepLearning,DL):机器学习的一个更具体的子集,基于人工神经网络,特别擅长处理复杂的非线性问题。
-
大语言模型:大语言模型(LLM)是一种基于大量数据训练的统计语言模型,可用于生成和翻译文本和其他内容,以及执行其他自然语言处理(NLP)任务。
-
生成式AI:生成式AI(生成式人工智能)是指使用AI来创作新内容,例如文本、图片、音乐、音频和视频。
-
特征工程:特征工程是机器学习流程中至关重要的环节,其核心目标是通过数据转换和特征构造提升模型性能。
-
sklearn:Scikit-leam(简称skleam)是Python中最流行的开源机器学习库之一,专注于提供简单高效的数据挖掘和数据分析工具。
-
Orange3是一个开源的 Python数据挖掘和机器学习工具箱,以其可视化编程界面和丰富的功能著称,适合从新手到专家的各类用户。
-
模型评估:机器学习流程中验证模型性能的关键环节,其核心目标是通过量化指标和可视化分析判断模型的泛化能力、稳定性和适用性。
2、机器学习的流程

3、机器学习的步骤

五大步骤:数据处理-->特征工程-->建立模型-->评估迭代-->上线应用-->持续优化
4、机器学习中的任务
4.1机器学习的任务类别

-
分类(classification):分类需要先找到数据样本点中的分界线,再根据分界线对新数据进行分类,分类数据是离散的值,比如图片识别、情感分析等领域会经常用到分类任务。比如把人分为好人和坏人之类的学习任务;分析图片是具体是哪种动物等等;
-
二分类(binary classification):只涉及两个类别的分类任务。
-
多分类(multi-class classification):涉及多个类别的分类。

-
-
回归(regression):回归是对已有的数据样本点进行拟合,再根据拟合出来的函数,对未来进行预测。回归数据是连续的值预测值是连续值,比如你的好人程度达到了0.9;房价预测、股价预测等等;

-
聚类:聚类是根据样本之间的相似度,将一批数据划分为N个组。比如用户分组、异常值检测等领域会用到聚类任务。

-
降维:减少数据的维度,对数据进行降噪、去冗余,方便计算和训练;如数据预处理,减少一些对模型准确率影响很小维度,可以提高计算效率。再如图表可视化,我们进行数据分析时,通常会将高维模型降为三维或二维图表,便于直观分析。

5、机器学习系统编程模型的演进

随着机器学习系统的诞生,如何设计易用且高性能的API接口就一直成为了系统设计者首要解决的问题。
机器学习系统编程模型的首要设计目标是:对开发者的整个工作流进行完整的编程支持。一个常见的机器学习任务一般包含数据处理、模型定义、优化器定义、训练、测试和调试等几大阶段。
-
数据处理:首先,用户需要数据处理API来支持将数据集从磁盘读入。
-
模型定义:用户需要模型定义API来定义机器学习模型。这些模型带有模型参数,可以对给定的数据进行推理。
-
优化器定义:模型输出需要和用户的标记进行对比,这个对比差异一般通过损失函数(Loss function)来进行评估。并根据损失来引入(Import)和定义各种优化算法(Optimisation algorithms)来计算梯度(Gradient),完成对模型参数的更新。
-
训练:给定一个数据集,模型,损失函数和优化器,用户需要训练API来定义一个循环(Loop)从而将数据集中的数据按照小批量(mini-batch)的方式读取出来,反复计算梯度来更新模型。
-
测试和调试:训练过程中,用户需要测试API来对当前模型的精度进行评估。当精度达到目标后,训练结束。
6、PyTorch
PyTorch是一种开源深度学习框架,以出色的灵活性和易用性著称。这在一定程度上是因为与机器学习开发者和数据科学家所青睐的热门 Python高级编程语言兼容。
PyTorch是一个由Facebook开源的深度学习框架,是目前市场上最流行的深度学习框架之一。PyTorch的应用范围非常广泛,包括图像和语音识别、自然语言处理、计算机视觉、推荐系统等领域。PyTorch具有易于使用、灵活性高和代码可读性好等特点,使得它成为深度学习研究和应用的首选框架之一。PyTorch使用Python编写,因此对于大多数机器学习开发者而言,学习和使用起来相对简单。PyTorch的独特之处在于,它完全支持GPU,并且使用反向模式自动微分技术,因此可以动态修改计算图形。这使其成为快速实验和原型设计的常用选择。
6.1、环境准备
我们需要配置一个环境来运行 Python、 Jupyter Notebook、相关库以及运行本书所需的代码,以快速入门并获得动手学习经验。
6.2、conda安装
Miniconda是一个轻量级的Anaconda发行版,专为需要灵活管理Python环境和包的用户设计。以下是关于Miniconda的详细信息:
Miniconda的特点
-
轻量级:仅包含Conda包管理器和Python的最小安装包,占用空间小。
-
灵活性:允许用户根据需要安装额外的库,避免不必要的依赖。
-
跨平台:支持Windows、macOS和Linux系统。
-
安装conda,Python工具大全,方便管理多个Python环境,必须选择跟自己环境配套的版本。
-
网速慢的,可以参考国内源,也可以去这里看看:
6.3、Pytorch安装
PyTorch的安装,结合自己想要安装的环境,动态选择即可。https://pytorch.org

环境初始化
~/miniconda3/bin/conda init
创建虚拟环境
conda create --name deeplearning python=3.9
激活环境
conda activate deeplearning
安装插件
pip install torch==1.12.0 torchvision==0.13.0 numpy==1.21.5 matplotlib==3.5.1 requests==2.25.1 pandas==1.2.4
python -m pip install -U pip
python -m pip install -U matplotlib
退出环境
conda deactivate
列出当前有哪些环境
conda env list
删除环境
conda env remove deeplearning
6.3、测试conda环境
python
import torch
print(f"PyTorch 版本: {torch.__version__}")
print(f"CUDA 是否可用: {torch.cuda.is_available()}")
print(f"可用的 GPU 数量: {torch.cuda.device_count()}")
if torch.cuda.is_available():
print(f"当前 GPU 名称: {torch.cuda.get_device_name(0)}")
print(f"PyTorch 内置的 CUDA 版本: {torch.version.cuda}")
# 测试GPU计算
import torch
import time
#创建一个时间戳,记录此刻的时间,精确到毫秒
start_time = time.time()
print(f"开始时间戳(毫秒): {start_time * 1000:.3f}")
# Test GPU computation
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"Using device: {device}")
# Create two large tensors and compute on GPU
a = torch.randn(10000, 10000, device=device) # Create directly on GPU
b = torch.randn(10000, 10000, device=device)
c = a @ b # Matrix multiplication
print(f"GPU matrix computation completed! Result shape: {c.shape}")
# View GPU memory information
print(f"\nGPU Memory Info:")
print(f" Device name: {torch.cuda.get_device_name(0)}")
print(f" Total memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.2f} GB")
print(f" Currently allocated: {torch.cuda.memory_allocated(0) / 1e9:.2f} GB")
print(f" Current cache: {torch.cuda.memory_reserved(0) / 1e9:.2f} GB")
print("一共用时:", time.time() - start_time, "秒")

6.4、数学基础
标量
-
定义:仅用单一数值即可完全描述的量,没有方向,只有大小(或"量级")。
-
数学表示:标量是零阶张量(0-dimensional),用单个实数或复数表示。
-
例子:温度(如25°C)、质量(如5kg)、时间(如10秒)
向量
-
定义:既有大小又有方向的量,可看作一维有序数组,是一阶张量。
-
特点:
-
可进行加减、标量乘法、点积、叉积等运算。
-
方向性体现在其分量随坐标系变化(如速度、力)。
-
机器学习中的特征向量(如用户画像的数值表示)
-
张量(Tensor)
-
定义:广义的多维数组,可表示任意阶数的数据。标量是0阶张量,向量是1阶张量,矩阵是2阶张量,更高维的数组为高阶张量。
-
数学表示:
-
阶数(秩)由索引数量决定。例如,3阶张量T可表示为 Tijk。
-
例子:
-
矩阵(2阶张量):图像灰度像素矩阵28×28。
-
3阶张量:RGB彩色图像(高度×宽度×3颜色通道)。
-
4阶张量:视频数据(时间×高度×宽度×颜色通道)。
-
-
小结
三者的核心区别
| 属性 | 标量 | 向量 | 张量 |
|---|---|---|---|
| 阶数(维度) | 0阶(零维) | 1阶(一维) | 任意阶 (n-维) |
| 数据结构 | 单个数值 | 一维数组 | 多维数组 |
| 方向性 | 无方向 | 有方向 | 可能包含多个方向 |
| 数学运算 | 普通算术(+ - × ÷) | 向量加减、点积、叉积 | 张量积、收缩、卷积等 |
| 物理意义 | 纯量(如能量) | 方向性量(如力) | 复杂关系(如应力) |
-
标量是单一数值,向量是带方向的数列,张量是广义的多维数据结构。
-
张量是标量和向量的高阶推广,能表示更复杂的物理量或数据关系。
-
在深度学习中,张量是核心数据结构,例如图像(3阶张量)和批量数据(4阶张量)
-
PyTorch和 TensorFlow中都是用 Tensor的概念,MXNet中使用ndarray的概念。
7、PyTorch中的张量
首先,我们导入torch。请注意,虽然它被称为PyTorch,但是代码中使用torch而不是pytorch。
PyTorch中张量Tensors定义
-
张量表示一个由数值组成的数组,这个数组可能有多个维度。
-
具有一个轴的张量对应数学上的向量(vector);
-
具有两个轴的张量对应数学上的矩阵(matrix);
-
具有两个轴以上的张量没有特殊的数学名称。
在 PyTorch中,标量、向量、张量与数学界的标识基本一致。为了存储方便,在计算机上存储时,通常有不同的存储表达。
8、机器学习中的关键术语及其含义
8.1、神经元及神经网络
机器学习中的神经网络是一种模仿生物神经网络的结构和功能的数学模型或计算模型。它是指按照一定的规则将多个神经元连接起来的网络。
神经网络是一种运算模型,由大量的节点(或称神经元)之间相互联接构成。每个节点代表一种特定的输出函数,称为激励函数。

偏置项的作用

8.2、多层感知机
多个神经元可以组合一起,形成多层感知机。多层感知器(Multi-Layer Perceptron,MLP):通过叠加多层全连接层来提升网络的表达能力。相比单层网络,多层感知器有很多中间层的输出并不暴露给最终输出,这些层被称为隐含层(Hidden Layers)。

下图描述了神经网络构建过程中的基本细节。
-
基类需要初始化训练参数、管理参数状态以及定义计算过程;神经网络模型需要实现对神经网络层和神经网络层参数管理的功能。
-
在机器学习编程库中,承担此功能有MindSpore的Cell、PyTorch的Module。Cell和Module是模型抽象方法也是所有网络的基类。现有模型抽象方案有两种,
-
一种是抽象出两个方法分别为Layer(负责单个神经网络层的参数构建和前向计算),Model(负责对神经网络层进行连接组合和神经网络层参数管理);
-
另一种是将Layer和Model抽象成一个方法,该方法既能表示单层神经网络层也能表示包含多个神经网络层堆叠的模型,Cell和Module就是这样实现的。

-
8.3、样本、目标函数、损失函数、特征及训练
-
样本(Sample)定义:样本是数据集中的单个实例或数据点,通常由一组特征(自变量)和一个标签(因变量)组成。举例:在房价预测中,一个样本可能包含房屋的面积、卧室数量、位置等特征,以及对应的房价标签。
-
标签(Label)定义:标签是与样本关联的目标值或类别,用于监督学习中指导模型学习。举例:在垃圾邮件分类中,标签可以是"垃圾邮件"或"非垃圾邮件"。
-
自变量(Independent Variable)定义:自变量是用于预测或解释目标变量的输入特征,也称为特征或预测变量。举例:在预测学生成绩时,自变量可以是学习时间、家庭收入等。
-
目标函数(TargetFunction)定义:目标函数是模型训练过程中优化的目标,通常由损失函数构成,用于衡量模型性能。举例:在线性回归中,目标函数是最小化均方误差(MSE)。
-
损失函数(LossFunction)定义:损失函数用于量化模型预测值与真实值之间的差异,是目标函数的核心组成部分。用来衡量单个样本中计算值与标签值的差异。举例:在分类问题中,常用的损失函数是交叉熵损失。
-
代价函数(CostFunction)定义:代价函数是损失函数在所有样本上的平均值,用于衡量模型在整个数据集上的性能。损失函数与代价函数的区别在于,损失函数只适用于单个训练样本,而代价函数是参数的总代价。举例:在逻辑回归中,代价函数是交叉熵损失的平均值。
-
特征(Feature)定义:特征是描述样本的属性或变量,用于模型的输入。举例:在图像分类中,特征可以是像素值或提取的边缘信息。
-
模型(Model)定义:模型是通过机器学习算法从数据中学习到的数学表示,用于对新数据进行预测。举例:决策树模型可以根据输入特征决定输出类别。
-
训练数据(TrainingData)定义:训练数据是用于训练机器学习模型的数据集,通常包含输入特征和对应的标签。举例:在预测房价的模型中,训练数据可能包括房屋特征及其相应的价格。
-
测试数据(TestingData)定义:测试数据是用于评估模型在未知数据上表现的数据集。举例:在训练垃圾邮件过滤器后,可以在以前从未见过的电子邮件上对其进行测试。
-
正则化(Regularization)定义:正则化是一种技术,用于防止模型过度拟合,通过在损失函数中添加惩罚项来限制模型的复杂度。举例:L2正则化通过在损失函数中添加权重平方和来限制权重的大小。
-
学习率(LeamingRate)定义:学习率是一个超参数,控制模型权重相对于损失梯度的更新程度。举例:在神经网络中,学习率决定了模型在训练期间从错误中学习的速度。
-
Epoch定义:一个epoch是指在模型训练过程中对整个训练数据集进行一次完整的遍历。举例:如果有1000个训练样本,1个epoch意味着模型已经看过所有1000个样本一次。
-
超参数(Hyperparameter)定义:超参数是在训练之前设置的参数,用于控制学习过程和模型结构。举例:学习率、批量大小(batch size)、神经网络的层数和每层的神经元数量等都是常见的超参数。
-
回归问题:回归问题是机器学习中的一种任务,其目标是预测一个连续值作为输出。
-
目标变量:回归问题中的目标变量是连续的,可以取任何实数值。
-
特征:用于预测目标变量的输入变量,可以是连续的或离散的。
-
-
线性回归问题:线性回归是一种回归模型,假设输入变量(特征)和输出变量(目标)之间存在线性关系。它通过找到一条最佳拟合直线来模拟这种关系。
-
逻辑回归问题:逻辑回归是一种用于分类问题的统计方法,尽管名字中包含"回归",但它实际上是一种分类算法。
- 定义:它通过逻辑函数(Sigmoid函数)将输入特征映射到0到1之间的概率值,表示属于某个类别的可能性。
-
训练:找到一组参数值Weight(面积)、Weight(age)、b,能够使得在给定的训练数据集合上,所产生的集体误差最小。
-
预测:当用户给定房屋面积、年龄后,能够给出对应的房价。这个房价越贴近于真实值,表示模型效果越好。
-
数据分布:训练的过程就是找到一个函数,能够匹配数据的分布的过程。

8.4、非线性激活函数
激活函数:人工神经网络中的一个关键组件,它负责将神经元的输入信号转换为输出信号,从而引入非线性特性。如果没有激活函数,神经网络的每一层都只是线性变换的叠加,整个网络将退化为一个简单的线性模型,无法拟合复杂的数据分布或解决非线性问题。
讨论1:为什么激活函数必须要有非线性的?

- 激活函数对模型至关重要:引入非线性、增强表达能力、控制输出范围、促进梯度传播、模拟生物神经元的激活机制。
8.5、常见的激活函数
-
常见激活函数:
-
Sigmoid函数:将输入映射到(0,1)之间,常用于二分类问题,但容易导致梯度消失问题。
-
Tanh函数:将输入映射到(-1,1)之间,解决了Sigmoid的零均值问题,但仍存在梯度消失问题。
-
ReLU函数:在输入为正时输出输入值,输入为负时输出0,计算简单且能有效缓解梯度消失问题,但可能导致"神经元死亡"问题。

-
Softmax函数:将输入映射为概率分布,常用于多分类问题的输出层。

8.6、反向传播机制
用一个实际的例子来理解机器学习和反向传播

8.7、学习率的影响
梯度下降法
步长太小,迭代次数多,收敛慢
步长 = n × df(x)/dx
n:学习率
步长太大,引起震荡,可能无法收敛

8.8、正向传播与反向传播的计算

8.9、由单个样本至多个样本的反向传播的计算
-
(单个样本)正向传播的计算过程: y∧是根据输入参数计算得到的结果,而 y是样本数据自带的实际结果。根据下面的公式即可得到代价,
-
(单个样本)反向传播的计算过程:

9、链式法则

参数权重矩阵和个别参数更新的关系:如上图是函数的变量是以矩阵的形式体现的,但在计算参数偏导时是具体到每个参数的,所以注意求偏导时函数要展开成针对参数矩阵里特定的参数。
-
反向传播的顺序;梯度是一步一步进行传播的。从计算可以看到,前面网络层的参数更新依赖后面网络层的参数,所以参数如果过小,经过连乘效应,前面的参数基本得不到更新,这就是所谓的参数消失。
-
网络保存着前向传播计算得到的值,包括隐藏层,所以在反向传播计算时候可以直接拿到这些值,不过这也需要存储空间。
10、正向传播和反向传播的代码实现

python
import numpy as np
# batch size是批量大小;dim_in是输入大小
# hidden_layer是隐层的大小;dim_out是输出大小
batch_size = 64
dim_in = 1000
hidden_layer = 100
dim_out = 10
# 随机产生输入与输出
x = np.random.randn(batch_size, dim_in)
y = np.random.randn(batch_size, dim_out)
# 随机初始化参数
w1 = np.random.randn(dim_in, hidden_layer)
w2 = np.random.randn(hidden_layer, dim_out)
learning_rate = 1e-6
for t in range(500):
# 前向计算y
h = x.dot(w1)
h_relu = np.maximum(h, 0)
y_pred = h_relu.dot(w2)
# 计算loss
loss = np.square(y_pred - y).sum()
print(t, loss)
# 反向计算梯度
grad_y_pred = 2.0 * (y_pred - y)
grad_w2 = h_relu.T.dot(grad_y_pred)
grad_h_relu = grad_y_pred.dot(w2.T)
grad_h = grad_h_relu.copy()
grad_h[h < 0] = 0
grad_w1 = x.T.dot(grad_h)
# 更新参数
w1 -= learning_rate * grad_w1
w2 -= learning_rate * grad_w2
11、浅层神经网络及代码实现
假设有一个二层神经网络,现在需要用代码来计算其计算过程。关于维数: Wi和 bi的维度,W的维度是(上一层神经元个数,下一层神经元个数),b的维度是(下一层神经元个数,1)
def initialize_parameters(n_x, n_h, n_y):
"""
参数:
n_x-输入层节点的数量
n_h-隐藏层节点的数量
n_y-输出层节点的数量
返回:
parameters-包含参数的字典:
W1-权重矩阵,维度为(n_h, n_x)
b1-偏向量,维度为(n_h,1)
W2-权重矩阵,维度为(n_y,n_h)
b2-偏向量,维度为(n_y,1)
"""
np.random.seed(2)
W1= np.random.randn(n_h, n_x)* 0.01
b1= np.zeros(shape=(n_h, 1))
W2= np.random.randn(n_y, n_h)* 0.01
b2= np.zeros(shape=(n_y, 1))
assert(W1.shape==(n_h, n_x))
assert(b1.shape==(n_h, 1))
assert(W2.shape==(n_y, n_h))
assert(b2.shape==(n_y, 1))
parameters={"W1": W1,
"b1": b1,
"W2": W2,
"b2":b2}
return parameters
def forward_propagation(X, parameters):
"""
参数:
X-维度为(n_x,m)的输入数据。
parameters-初始化函数(initialize_parameters)的输出
返回:
A2-使用sigmoid()函数计算的第二次激活后的数值
cache-包含"Z1","A1","Z2"和"A2"的字典类型变量
"""
W1= parameters["W1"]
b1= parameters["b1"]
W2= parameters["W2"]
b2= parameters["b2"]
#前向传播计算A2
Z1= np.dot(W1, X)+ b1
A1= np.tanh(Z1)
Z2= np.dot(W2, A1)+b2
A2= sigmoid(Z2)
#使用断言确保我的数据格式是正确的
assert(A2.shape==(1, X.shape[1]))
cache={"Z1": Z1,
"A1": A1,
"Z2": Z2,
"A2": A2}
return(A2, cache)
def backward_propagation(parameters,cache, X, Y):
"""
搭建反向传播函数
参数:
parameters-包含我们的参数的一个字典类型的变量。
cache-包含"Z1","A1","Z2"和"A2"的字典类型的变量。
X-输入数据,维度为(2,数量)
Y-"True"标签,维度为(1,数量)
返回:
grads-包含W和b的导数一个字典类型的变量。
"""
m= X.shape[1]
W1= parameters["W1"]
W2= parameters["W2"]
A1= cache["A1"]
A2= cache["A2"]
dZ2= A2- Y
dW2=(1/ m)* np.dot(dZ2, A1.T)
db2=(1/ m)* np.sum(dZ2, axis=1, keepdims=True)
dZ1= np.multiply(np.dot(W2.T,dZ2),1- np.power(A1,2))
dW1=(1/m)* np.dot(dZ1, X.T)
db1=(1/m)* np.sum(dZ1, axis=1, keepdims=True)
grads={"dW1": dW1,
"db1": db1,
"dW2": dW2,
"db2": db2}
return grads
def update_parameters(parameters, grads, learning_rate=1.2):
"""
更新参数
参数:
parameters-包含参数的字典类型的变量。
grads-包含导数值的字典类型的变量。
learning_rate-学习速率
返回:
parameters-包含更新参数的字典类型的变量。
"""
W1, W2= parameters["W1"], parameters["W2"]
b1, b2= parameters["b1"], parameters["b2"]
dW1, dW2= grads["dW1"], grads["dW2"]
db1, db2= grads["db1"], grads["db2"]
W1= W1- learning_rate* dW1
b1= b1- learning_rate* db1
W2= W2- learning_rate* dW2
b2= b2- learning_rate* db2
parameters={"W1": W1,
"b1": b1,
"W2": W2,
"b2": b2}
return parameters
12、自动求导
通过上面的实例,我们可以看出:人工求导太费劲,而且容易出错。那么有没有可以自动求导的方法?
常见的计算机程序求导的方法
自动微分(Automatic Differentiation, AD)是一种对计算机程序进行高效且准确求导的技术,在上个世纪六七十年代就已经被广泛应用于流体力学、天文学、数学金融等领域。时至今日,自动微分的实现及其理论仍然是一个活跃的研究领域。许多机器学习模型使用的优化算法都需要获取模型的导数,因此自动微分技术成为了一些热门的机器学习框架(例如TensorFlow和PyTorch)的核心特性。
常见的计算机程序求导的方法可以归纳为以下四种:
-
手工微分(Manual Differentiation)
-
数值微分(Numerical Differentiation)
-
符号微分(Symbolic Differentiation)
-
自动微分(Automatic Differentiation)
自动微分:自动微分的思想是将计算机程序中的运算操作分解为一个有限的基本操作集合,且集合中基本操作的求导规则均为已知,在完成每一个基本操作的求导后,使用链式法则将结果组合得到整体程序的求导结果。自动微分是一种介于数值微分和符号微分之间的求导方法,结合了数值微分和符号微分的思想。
示例:计算函数 y=2x2+3的导数
假设我们要计算函数 y=2x2+3在 x=2时的导数。我们可以使用PyTorch的自动求导功能来完成这个任务。
实例一:利用单层神经元网络来实现线性模型预测

import torch
import torch.nn as nn
import torch.optim as optim
#定义一个简单的线性回归模型。在LinearRegression模型中,这行代码定义了线性回归
#线性回归的目标是找到一个线性函数(y=wx+b)来拟合输入数据(x)和目标值(y),
#就是用来学习权重(w)和偏置(b)的。
class LinearRegression(nn.Module):
def __init__(self):
super(LinearRegression, self).__init__()
self.fc=nn.Linear(1,1) #输入1维,输出1维
def forward(self, x):
return self.fc(x)
#初始化模型、损失函数和优化器
model= LinearRegression()
criterion=nn.MSELoss()
optimizer= optim.SGD(model.parameters(),lr=0.01)
#训练数据
x=torch.tensor([[1.0],[2.0],[3.0]])
y_true=torch.tensor([[3.0],[5.0],[7.0]])
#训练循环
for epoch in range(100):
#前向传播
y_pred= model(x)
loss= criterion(y_pred,y_true)
#反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
if epoch% 10== 0:
print(f'Epoch {epoch}, Loss: {loss.item():.4f}')
#预测结果
print(f"预测结果:{model(torch.tensor([[4.0]])).item():.2f}")#应接近9.0
如何计算神经网络模型的参数量
随着层数的加深,网络参数的个数会越来越多,小的网络有成千上万个参数,大的可以达到千万个网络参数。那如何计算神经网络的参数数量?神经网络的参数量通常涉及到计算权重(weights)和偏差(biases)的数量,类似于传统算法中的空间复杂度。

实例二:利用线性模型来对数据进行二分类
- 05-线性模型及优化调优的讨论.ipynb
13、计算图
为什么需要计算图?
现代机器学习模型的拓扑结构日益复杂。复杂的模型结构需要机器学习框架能够对模型算子的执行依赖关系、梯度计算以及训练参数进行快速高效的分析,便于优化模型结构、制定调度执行策略以及实现自动化梯度计算,从而提高机器学习框架训练复杂模型的效率。因此,机器学习系统设计者需要一个通用的数据结构来理解、表达和执行机器学习模型。为了应对这个需求,基于计算图的机器学习框架应运而生,框架延续前端语言与后端语言分离的设计。从高层次来看,计算图实现了以下关键功能:
-
统一的计算过程表达。在编写机器学习模型程序的过程中,用户希望使用高层次编程语言(如Python、Julia和C++)。然而,硬件加速器等设备往往只提供了C和C++编程接口,因此机器学习系统的实现通常需要基于C和C++。用不同的高层次语言编写的程序因此需要被表达为一个统一的数据结构,从而被底层共享的C和C++系统模块执行。这个数据结构(即计算图)可以表述用户的输入数据、模型中的计算逻辑(通常称为算子)以及算子之间的执行顺序。
-
自动化计算梯度。用户的模型训练程序接收训练数据集的数据样本,通过神经网络前向计算,最终计算出损失值。根据损失值,机器学习系统为每个模型参数计算出梯度来更新模型参数。考虑到用户可以写出任意的模型拓扑和损失值计算方法,计算梯度的方法必须通用并且能实现自动运行。计算图可以辅助机器学习系统快速分析参数之间的梯度传递关系,实现自动化计算梯度的目标。
-
分析模型变量生命周期。在用户训练模型的过程中,系统会通过计算产生临时的中间变量,如前向计算中的激活值和反向计算中的梯度。前向计算的中间变量可能与梯度共同参与到模型的参数更新过程中。通过计算图,系统可以准确分析出中间变量的生命周期(一个中间变量生成以及销毁时机),从而帮助框架优化内存管理。
-
优化程序执行。用户给定的模型程序具备不同的网络拓扑结构。机器学习框架利用计算图来分析模型结构和算子执行依赖关系,并自动寻找算子并行计算的策略,从而提高模型的执行效率。
什么是计算图?
计算图由基本数据结构张量(Tensor)和基本运算单元算子构成。在计算图中通常使用节点来表示算子,节点间的有向边(Directed Edge)来表示张量状态,同时也描述了计算间的依赖关系。计算图是用于表示计算过程的有向无环图(DAG)。这里强调一定是有向无环图。因为有环图在计算上会导致死循环。
在机器学习框架中可以生成静态图和动态图两种计算图。
静态图的生成与执行原理:
采用先编译后执行的方式,该模式将计算图的定义和执行进行分离。
使用前端语言定义模型形成完整的程序表达后,机器学习框架首先对神经网络模型进行分析,获取网络层之间的连接拓扑关系以及参数变量设置、损失函数等信息。然后机器学习框架会将完整的模型描述编译为可被后端计算硬件调用执行的固定代码文本,这种固定代码文本通常被称为静态计算图。其核心是:先定义,再执行。其优势有:
-
当使用静态计算图进行模型训练或者推理过程时,无需编译前端语言模型。
-
静态计算图直接接收数据并通过相应硬件调度执行图中的算子来完成任务。
-
静态计算图可以通过优化策略转换成等价的更加高效的结构,提高后端硬件的计算效率。
动态图的生成
动态图:采用解析式的执行方式,其核心特点是编译与执行同时发生。
示例:当调用到下图中,张量W1的Matmul算子节点时,框架会执行两个操作:调用Matmul算子,计算关于输入X和W1的乘积结果,同时根据反向计算过程Grad_W1=Grad_Y*X,记录下需要参与反向计算的算子和张量X,机器学习框架依据收集的信息完成前向计算和反向图构建。
动态图采用前端语言自身的解释器对代码进行解析,利用机器学习框架本身的算子分发功能,算子会即刻执行并输出结果。
动态图模式采用用户友好的命令式编程范式,使用前端语言构建神经网络模型更加简洁。
from torch import relu
from numpy import matmul
#以 PyTorch为代表的动态图定义
def model(X, flag):
if flag>0:
Y= matmul(W1,X)
else:
Y= matmul(W2,X)
Y=Y+b
Y= relu(Y)
return Y
#基于 TensorFlow的静态图
import tensorflow as tf
import numpy as np
x=tf.placeholder(dtype=tf.float32,shape=(5,5))#数据占位符
w1=tf.Variable(tf.ones([5,5]),name='w1')
w2= tf.Variable(tf.zeros([5,5]),name='w2')
b=tf.Variable(tf.zeros([5,]),name='b')
def f1():
return tf.matmul(w1,x)
def f2():
return tf.matmul(w2,x)
y1= tf.cond(flag> 0, f1, f2)#图内条件控制算子
y2= tf.add(y1,b)
output= tf.relu(y2)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())#静态图变量初始化
random_array= np.random.rand(5,5)
sess.run(output, feed_dict={x:random_array, flag:[1.0]})#静态图执行
利用PyTorch高级API简洁实现模型
在过去的几年里,出于对深度学习强烈的兴趣,许多公司、学者和业余爱好者开发了各种成熟的开源框架。这些框架可以自动化基于梯度的学习算法中重复性的工作。前面的环节中我们只运用了:
-
通过张量来进行数据存储和线性代数;
-
通过自动微分来计算梯度。
实际上,由于数据迭代器、损失函数、优化器和神经网络层很常用,现代深度学习库也为我们实现了这些组件。
对于标准深度学习模型,我们可以使用框架的预定义好的层。这使我们只需关注使用哪些层来构造模型,而不必关注层的实现细节。
我们首先定义一个模型变量net,它是一个Sequential类的实例。Sequential类将多个层串联在一起。当给定输入数据时,Sequential实例将数据传入到第一层,然后将第一层的输出作为第二层的输入,以此类推。
在下面的例子中,我们的模型只包含一个层,因此实际上不需要Sequential。但是由于以后几乎所有的模型都是多层的,在这里使用Sequential会让你熟悉"标准的流水线"。
线性回归示例代码
#线性回归示例代码(不可实际运行)
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
import numpy as np
from torch import nn
#定义模型
net= nn.Sequential(nn.Linear(2, 1))
#在使用`net`之前,我们需要初始化模型参数
#如在线性回归模型中的权重和偏置。
#深度学习框架通常有预定义的方法来初始化参数。
#在这里,我们指定每个权重参数应该从均值为0、标准差为0.01的正态分布中随机采样,偏置初始化为0。
net[0].weight.data.normal_(0,0.01)
net[0].bias.data.fill_(0)
#计算均方误差使用的是`MSELoss`类,也称为平方L2范数。默认情况下,它返回所有样本损失的平均值。
loss=nn.MSELoss()
#小批量随机梯度下降算法是一种优化神经网络的标准工具, PyTorch在`optim`模块中实现了该算法的许多变种。
trainer= torch.optim.SGD(net.parameters(), lr=0.03)
#通过深度学习框架的高级API来实现我们的模型只需要相对较少的代码。
#我们不必单独分配参数、不必定义我们的损失函数,也不必手动实现小批量随机梯度下降。
#当我们需要更复杂的模型时,高级API的优势将大大增加。
num_epochs= 3
for epoch in range(num_epochs):
for X, y in data_iter:
l=loss(net(X),y)#通过调用`net(X)`生成预测并计算损失`l`(前向传播)。
trainer.zero_grad()
l.backward()#通过进行反向传播来计算梯度。
trainer.step()#通过调用优化器来更新模型参数。
l= loss(net(features), labels)
print(f'epoch{epoch+ 1}, loss{l:f}')
#预估结果
w= net[0].weight.data
print('w的估计误差:',true_w- w.reshape(true_w.shape))
b= net[0].bias.data
print('b的估计误差:',true_b-b)
多层感知机示例代码
做0-9的分、类任务
python
import torch
import torch.nn as nn
import torch.nn.functional as F
#定义MLP模型
class MLP(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim):
super(MLP, self).__init__()
#一个全连接层的隐含层
self.fc1= nn.Linear(input_dim, hidden_dim)
self.fc2=nn.Linear(hidden_dim, output_dim)
def forward(self, x):
x=F.relu(self.fc1(x))
x= self.fc2(x)
return x
#示例数据
x_train=torch.randn(100,784)#100个样本,每个样本784维
y_train=torch.randint(0,10,(100,))# 100个标签,每个标签 0-9
#实例化模型、损失函数和优化器
model= MLP(784,128,10)
criterion= nn.CrossEntropyLoss()
optimizer= torch.optim.Adam(model.parameters(),lr=0.001)
#训练模型
for epoch in range(10):
optimizer.zero_grad()
outputs= model(x_train)
loss= criterion(outputs,y_train)
loss.backward()
optimizer.step()
if(epoch+1)% 1== 0:
print(f'Epoch[{epoch+1}/10], Loss:{loss.item():.4f}')
