|
## 微小趋势判断
- 效率稍微高一些,但取的是极小值
- x = x 加/减 一个微小变化
- $x -= \Delta x$
- 重点是让x加上一个微小的值,这个微小的值可以变化,也可以是定值,在一定范围内的一个比较小的值就可以
```
import numpy as np
def fn(x):
return x ** 2
def dfn(x):
return 2 * x
# 随机选取一个出生点
x0 = np.random.uniform(low=-1000, high=1000)
print(x0)
print(fn(x0))
for _ in range(5000):
x0 -= 0.001 * dfn(x0)
print(fn(x0))
```
580.7493541721117
337269.8123713248
0.0006813815105911381
- 现多使用梯度下降法求函数极小值,来训练模型。 - 梯度,导数,更确切的说是偏导数/微分,描述了函数在某点的变化率和变化方向。 - 用更自然的语言来说,是变化趋势与变化的快慢(大小)。 - 一个代表方向,最重要 - 一个代表大小,在远离极小值的地方,函数值变化快,梯度大;在接近极小值的地方,函数值变化慢,梯度小。 - 这使得开始时变化幅度大,接近极小值时变化幅度小,比较合理。 - dl09-1.py.ipynb |
|
```
import numpy as np
x = np.linspace(start=-10,stop=10,num=50)
from matplotlib import pyplot as plt
plt.plot(x,x**2)
```
|
|
```
import numpy as np
x = np.random.uniform(low=-10, high=10,)
x # -8.248295885583863
```
```
"""
1,随机生成一个数组
2,求最小值
"""
import numpy as np
x = np.random.uniform(low=-10, high=10, size=100000)
def fn(x):
return x ** 2
# x0存放最小值点对应的x
x0 = x[0]
# y_min 存放最小值
y_min = fn(x[0])
for ele in x:
y = fn(ele)
if y < y_min:
y_min = y
x0 = ele
print(x0, y_min)
```
|
|
|
|
|
|
```
import torch
w1 = torch.tensor([1.0,1.0],requires_grad=True)
b = torch.tensor(1.0,requires_grad=True)
y = 2*w1 + b # tensor([2., 2.], grad_fn=
|
- 一个变量往往是由一组变量组合表示,即一个变量是与多个变量的组合呈线性关系
- 组合方式,即变量的系数,就是权重
- 每个变量都有一个梯度
|
|
- 兼容写法
```
USE_CUDA = torch.cuda.is_available()
device = torch.device("cuda" if USE_CUDA else "cpu")
device = "cuda:0" if torch.cuda.is_available() else "cpu"
X = X.to(device=device)
y = y.to(device=device)
```
### 两种device写法的区别
#### 写法1:torch.device对象
```python
device = torch.device("cuda" if USE_CUDA else "cpu")
X = X.to(device)
```
**特点:**
- 返回一个`torch.device`对象,类型明确
- 可以指定更详细的设备信息,如`torch.device("cuda:0")`
- 代码可读性更好,语义更清晰
- 适合在代码中多次传递device参数
#### 写法2:字符串
```python
device = "cuda:0" if torch.cuda.is_available() else "cpu"
X = X.to(device)
```
**特点:**
- 直接使用字符串,简洁但类型不够明确
- `"cuda:0"`明确指定使用第0号GPU
- 代码更紧凑,适合简单场景
- 字符串会被PyTorch自动转换为device对象
**推荐:** 在生产代码中使用`torch.device`对象,类型更安全,可维护性更好。
### 单GPU与多GPU
#### 单GPU使用
```python
# 默认使用cuda:0(第一块GPU)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = model.to(device)
data = data.to(device)
# 或者简写为
device = "cuda" # 默认使用cuda:0
```
#### 多GPU使用场景
##### 1. 查看可用GPU
```python
import torch
# 查看GPU数量
num_gpus = torch.cuda.device_count()
print(f"可用GPU数量: {num_gpus}")
# 查看每个GPU的名称
for i in range(num_gpus):
print(f"GPU {i}: {torch.cuda.get_device_name(i)}")
# 查看当前GPU
print(f"当前GPU: {torch.cuda.current_device()}")
```
##### 2. 指定特定GPU
```python
# 使用第1块GPU(索引从0开始)
device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")
model = model.to(device)
data = data.to(device)
```
##### 3. 数据并行(DataParallel)- 最常用
```python
import torch
import torch.nn as nn
# 定义模型
model = MyModel()
# 检查是否有多个GPU
if torch.cuda.device_count() > 1:
print(f"使用 {torch.cuda.device_count()} 个GPU")
# 将模型包装为DataParallel
model = nn.DataParallel(model)
# 将模型移至GPU(默认移至所有可用GPU)
model = model.cuda()
# 数据只需要移至默认GPU(cuda:0),DataParallel会自动分发
data, target = data.cuda(), target.cuda()
# 前向传播
output = model(data)
```
**DataParallel工作原理:**
- **模型复制:** 将完整的模型复制到每个GPU上(每个GPU都有完整的模型权重参数副本)
- **数据拆分:** 自动将输入数据的batch拆分到多个GPU
- **并行计算:** 每个GPU使用自己那份模型副本,独立计算前向传播
- **结果汇总:** 收集所有GPU的结果并汇总到主GPU(默认cuda:0)
- **梯度同步:** 反向传播时自动同步所有GPU的梯度,并更新模型参数
**核心概念:数据并行 vs 模型并行**
```
┌─────────────────────────────────────────────────────────────┐
│ 数据并行 (DataParallel) │
├─────────────────────────────────────────────────────────────┤
│ GPU 0: [模型完整副本] + [数据批次 1/3] → [输出1/3] │
│ GPU 1: [模型完整副本] + [数据批次 2/3] → [输出2/3] │
│ GPU 2: [模型完整副本] + [数据批次 3/3] → [输出3/3] │
│ │
│ 每个GPU都有完整的模型,处理不同的数据,最后汇总结果 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 模型并行 (Model Parallel) │
├─────────────────────────────────────────────────────────────┤
│ GPU 0: [模型第1层] → [中间结果] ──┐ │
│ GPU 1: [模型第2层] → [中间结果] ←──┤ │
│ GPU 2: [模型第3层] → [最终输出] ←──┘ │
│ │
│ 模型分散在多个GPU上,数据按顺序流经各个GPU │
└─────────────────────────────────────────────────────────────┘
```
**注意事项:**
- 数据batch size应该是GPU数量的整数倍
- 主GPU(cuda:0)需要额外存储汇总结果,显存占用更大
- 适合单机多卡场景
- **每个GPU都需要能够完整容纳模型**(这是关键限制)
##### 4. 分布式数据并行(DistributedDataParallel)- 推荐
```python
import torch
import torch.distributed as dist
import torch.multiprocessing as mp
from torch.nn.parallel import DistributedDataParallel as DDP
def setup(rank, world_size):
# 初始化进程组
dist.init_process_group(
backend='nccl', # NVIDIA GPU推荐使用nccl
init_method='tcp://localhost:12345',
rank=rank,
world_size=world_size
)
def cleanup():
dist.destroy_process_group()
def train(rank, world_size):
setup(rank, world_size)
# 创建模型并移至当前GPU
model = MyModel().to(rank)
# 包装为DDP模型
model = DDP(model, device_ids=[rank])
# 创建数据加载器(每个进程获取不同数据)
dataset = MyDataset()
sampler = torch.utils.data.distributed.DistributedSampler(
dataset,
num_replicas=world_size,
rank=rank
)
dataloader = DataLoader(dataset, batch_size=32, sampler=sampler)
# 训练循环
for data, target in dataloader:
data, target = data.to(rank), target.to(rank)
output = model(data)
# ... 训练逻辑
cleanup()
if __name__ == "__main__":
world_size = 2 # 使用2个GPU
mp.spawn(train, args=(world_size,), nprocs=world_size, join=True)
```
**DDP优势:**
- 每个GPU独立运行一个进程,性能更好
- 避免DataParallel的GIL限制
- 支持多机多卡训练
- 生产环境推荐方案
##### 5. 手动分配数据到不同GPU(模型并行)
```python
# 场景:模型的不同部分在不同GPU上计算
# 当模型太大无法放入单个GPU时使用
class MultiGPUModel(nn.Module):
def __init__(self):
super().__init__()
# 模型的不同部分放在不同GPU上
self.part1 = nn.Linear(1000, 500).to('cuda:0')
self.part2 = nn.Linear(500, 100).to('cuda:1')
def forward(self, x):
# 数据需要在GPU间传输
x = x.to('cuda:0')
x = self.part1(x)
x = x.to('cuda:1') # 中间结果传输到GPU 1
x = self.part2(x)
return x
model = MultiGPUModel()
data = data.to('cuda:0')
output = model(data)
```
**注意事项:**
- GPU间数据传输有性能开销(这是主要瓶颈)
- 仅适用于特殊场景(模型太大单个GPU放不下)
- 需要手动管理数据流,编程复杂度高
- **这是模型并行的实现方式,与DataParallel的数据并行不同**
#### 数据并行 vs 模型并行对比
| 特性 | 数据并行 (DataParallel) | 模型并行 (Model Parallel) |
|------|------------------------|---------------------------|
| **模型分布** | 每个GPU有完整模型副本 | 模型分散在多个GPU上 |
| **数据分布** | 数据batch拆分到各GPU | 所有GPU处理相同数据 |
| **适用场景** | 模型能放入单个GPU | 模型太大,单个GPU放不下 |
| **通信开销** | 梯度同步(相对较小) | GPU间数据传输(可能较大) |
| **实现复杂度** | 低(一行代码) | 高(需要手动设计) |
| **训练速度** | 接近线性加速 | 受限于GPU间传输速度 |
| **显存需求** | 每个GPU需容纳完整模型 | 每个GPU只需容纳模型一部分 |
**实际应用建议:**
- **绝大多数情况使用数据并行**(DataParallel/DDP)
- 只有当模型单个GPU放不下时才考虑模型并行
- 可以结合使用:先用模型并行将大模型拆分,再对每个部分用数据并行
```python
# 示例:结合数据并行和模型并行
class HybridParallelModel(nn.Module):
def __init__(self):
super().__init__()
# 第一层在GPU 0,1上数据并行
self.part1 = nn.DataParallel(nn.Linear(1000, 500).cuda(0))
# 第二层在GPU 2,3上数据并行
self.part2 = nn.DataParallel(nn.Linear(500, 100).cuda(2))
def forward(self, x):
x = self.part1(x)
x = x.cuda(2) # 在GPU组间传输
x = self.part2(x)
return x
```
### GPU内存管理
```python
# 查看GPU内存使用
print(torch.cuda.memory_summary(device=None, abbreviated=False))
# 查看当前GPU显存分配
print(f"已分配: {torch.cuda.memory_allocated()} / {torch.cuda.get_device_properties(0).total_memory}")
# 清空缓存
torch.cuda.empty_cache()
# 设置GPU内存增长策略
import os
os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'max_split_size_mb:128'
```
### 最佳实践
1. **单GPU场景:** 使用`device = torch.device("cuda")`即可
2. **多GPU训练:** 优先使用`DataParallel`(简单)或`DistributedDataParallel`(高效)
3. **指定GPU:** 使用环境变量`CUDA_VISIBLE_DEVICES=0,1`限制可见GPU
4. **batch size:** 设置为GPU数量的整数倍以保证负载均衡
```bash
# 在命令行指定使用哪些GPU
CUDA_VISIBLE_DEVICES=0,1,2,3 python train.py
```
- 模型从CPU迁移至GPU
```
import torch
use_gpu = torch.cuda.is_available()
if use_gpu:
model.cuda()
```
|
|
### 全局控制
#### set_num_threads
```
import torch
# 设置全局线程数
torch.set_num_threads(4)
```
- 注意,torch.set_num_threads 设置的是全局线程数,
- 它将影响所有的 PyTorch 操作,包括数据加载、模型前向传播等。
#### 环境变量
```
import os
设置环境变量以限制线程数
os.environ['OMP_NUM_THREADS'] = '4'
os.environ['MKL_NUM_THREADS'] = '4'
现在导入 PyTorch 和你的模型
import torch
import torch.nn as nn
你的模型定义和加载代码...
```
### 数据加载时
- 如果你的瓶颈在于数据加载而不是模型推理,
- 你可以通过调整 DataLoader 的 num_workers 参数来控制用于数据加载的线程数。
```
from torch.utils.data import DataLoader, Dataset
假设你有一个自定义的 Dataset 类
dataset = MyDataset()
创建 DataLoader 并设置 num_workers
dataloader = DataLoader(dataset, batch_size=32, num_workers=4)
```
|
|
|
|
|
|
|
|
|
|
- 一个变量往往是由一组变量的线性组合所表示的
- 这种组合方式就是线性变换的规则T
- $\Alpha = T(\Beta)$
|
|
平均绝对误差(Mean Absolute Error, MAE)的数学公式如下:
## MAE 公式
$$MAE = \frac{1}{n} \sum_{i=1}^{n} |y_i - \hat{y}_i|$$
## 公式说明
### 参数含义
- **$n$**:样本总数
- **$y_i$**:第 $i$ 个样本的真实值(实际值)
- **$\hat{y}_i$**:第 $i$ 个样本的预测值
- **$|y_i - \hat{y}_i|$**:第 $i$ 个样本的绝对误差
### 公式解读
1. 先计算每个样本的**绝对误差**:$|y_i - \hat{y}_i|$
2. 将所有样本的绝对误差**求和**:$\sum_{i=1}^{n} |y_i - \hat{y}_i|$
3. 最后**求平均**:除以样本总数 $n$
## 其他常用形式
### 加权平均绝对误差
当样本具有不同权重时:
$$WMAE = \frac{\sum_{i=1}^{n} w_i |y_i - \hat{y}_i|}{\sum_{i=1}^{n} w_i}$$
其中 $w_i$ 是第 $i$ 个样本的权重。
### 样本外MAE(预测用)
对于时间序列预测:
$$MAE_{out} = \frac{1}{T} \sum_{t=1}^{T} |y_t - \hat{y}_t|$$
其中 $T$ 是预测期的长度。
## 代码实现
### Python 实现
```python
import numpy as np
from sklearn.metrics import mean_absolute_error
# 方法1:手动计算
def mae_manual(y_true, y_pred):
"""
手动计算MAE
"""
n = len(y_true)
absolute_errors = np.abs(np.array(y_true) - np.array(y_pred))
mae = np.sum(absolute_errors) / n
return mae
# 方法2:使用sklearn
def mae_sklearn(y_true, y_pred):
"""
使用sklearn计算MAE
"""
return mean_absolute_error(y_true, y_pred)
# 示例
y_true = [3, -0.5, 2, 7]
y_pred = [2.5, 0.0, 2, 8]
print(f"手动计算 MAE: {mae_manual(y_true, y_pred):.4f}")
print(f"sklearn MAE: {mae_sklearn(y_true, y_pred):.4f}")
```
## MAE 的特点
### 优点
1. **直观易懂**:表示平均预测误差的绝对值
2. **单位一致**:与原始数据单位相同
3. **鲁棒性**:对异常值不如MSE敏感
4. **线性惩罚**:所有误差权重相同
### 缺点
1. **不可微分**:在0点不可导(优化困难)
2. **不放大误差**:无法突出大误差的影响
3. **缺乏方向信息**:无法知道误差是正偏还是负偏
## 与其他指标的对比
| 指标 | 公式 | 特点 |
|------|------|------|
| **MAE** | $\frac{1}{n}\sum\|y_i-\hat{y}_i\|$ | 平均绝对误差,线性惩罚 |
| **MSE** | $\frac{1}{n}\sum(y_i-\hat{y}_i)^2$ | 均方误差,放大误差 |
| **RMSE** | $\sqrt{\frac{1}{n}\sum(y_i-\hat{y}_i)^2}$ | 均方根误差,与原始单位一致 |
## 实际应用场景
MAE 常用于:
- **回归问题评估**:房价预测、销量预测
- **时间序列预测**:股票价格、气象数据
- **机器学习模型比较**:评估模型预测精度
- **异常检测**:识别预测偏差大的样本
### 解释示例
```python
# 实际应用示例
house_prices_true = [300, 350, 280, 400, 320] # 万元
house_prices_pred = [310, 340, 290, 380, 330] # 万元
mae = mean_absolute_error(house_prices_true, house_prices_pred)
print(f"房价预测 MAE: {mae:.2f} 万元")
print(f"解释:平均每个房子的预测价格与实际价格相差 {mae:.2f} 万元")
```
|
|
- 量化之后需要一个衡量的标准,之后才能优化
- 模型输出,与真实标签之间
- 如果使用想减再相加,那么会有正负中和的问题,
- 内部本有较大差异,但中和后可能整体差异不明显
- 这实际上涉及三步
- 差异计算 :行为,目标
- 衡量方法 :如何比较,比较的方法
- 微分优化 :一次优化一小步
|
|
|
|
|
|
|
|
|
|
|
|
---
## 📘 深度学习要点总结
### 📌 摘要
本总结涵盖了 PyTorch 中张量的基本概念与维度、深度学习编程中的常见问题与封装思想、PyTorch 模型定义的核心方法(`__init__` 与 `forward`),以及深度学习开发中需要注意的形状优先原则和模型保存习惯。
---
### 📊 要点表格
| 类别 | 要点 | 说明 |
|------|------|------|
| **张量维度** | 标量 (scalar) | 维度为 0,单个数值 |
| | 向量 (vector) | 维度为 1 或 2,一行或一列数值 |
| | 矩阵 (matrix) | 二维结构,成行成列 |
| | 张量 (tensor) | 高维数据容器,是深度学习的基本数据结构 |
| **编程问题与封装** | 数据批量处理 | 数据加载器确保数据以批次形式输入 |
| | 层封装 | 将变量定义与处理逻辑封装为“层” |
| | 损失函数封装 | 常用损失函数可直接调用,减少重复代码 |
| | 优化器封装 | 封装参数更新与梯度清零操作 |
| **PyTorch 编程结构** | `__init__` 函数 | 定义超参数和网络层 |
| | 类与对象 | 定义模型类,实例化为对象 |
| | `forward` 函数 | 定义前向传播逻辑,对象可被调用执行 |
| **开发注意事项** | 形状优先 | 张量的形状是调试和建模的第一步 |
| | 结果次之 | 模型输出结果是第二步关注点 |
| **模型保存习惯** | 结构单独保存 | 网络结构可保存为脚本或配置文件 |
| | 参数单独保存 | 模型权重参数通常保存为 `.pth` 文件 |
---
|
|
```
class Model:
def __init__(self,params):
"""机器学习框架"""
self.params = params
def fit(self,X,y):
pas
def predict(self,X):
pass
```
|
|
|
|
|
|
|
|
|
|
|
|
|