Day 4: PyTorch基础
学习目标
- 理解PyTorch的核心概念和架构
- 掌握PyTorch张量的创建和操作方法
- 学习自动微分机制和计算图
- 了解如何构建和训练简单的神经网络
- 对比JAVA和PyTorch在深度学习开发方面的差异
1. PyTorch简介
1.1 什么是PyTorch
PyTorch是一个开源的深度学习框架,由Facebook的AI研究团队开发,提供了灵活的工具和库,用于构建和训练神经网络模型。
定义:PyTorch是一个基于Python的科学计算库,提供了两个高级功能:
- 具有强大GPU加速的张量计算(类似NumPy)
- 基于自动微分系统的深度神经网络构建和训练
核心特点:
- 命令式编程风格(Eager Execution)
- 动态计算图(Define-by-Run)
- Python优先设计
- 强大的GPU加速
- 丰富的生态系统和社区支持
1.2 PyTorch vs TensorFlow
PyTorch和TensorFlow是当前最流行的两个深度学习框架,它们有不同的设计理念和优势。
| 特性 | PyTorch | TensorFlow |
|---|---|---|
| 编程范式 | 命令式(Eager Execution) | 符号式(Graph Execution)+ Eager |
| 计算图 | 动态(Define-by-Run) | 静态(Define-and-Run)+ 动态 |
| 调试 | 简单直观(标准Python调试) | 相对复杂(特别是静态图模式) |
| 部署 | 相对复杂 | 成熟的部署工具(TF Serving, TFLite) |
| 社区和生态 | 学术研究和原型开发 | 工业应用和生产部署 |
| 学习曲线 | 对Python开发者友好 | 相对陡峭(特别是TF 1.x) |
为什么选择PyTorch:
- 更Pythonic的API和编程风格
- 动态计算图便于调试和实验
- 研究社区广泛采用
- 大模型开发中的主导地位
1.3 PyTorch与JAVA对比
| 特性 | JAVA | PyTorch |
|---|---|---|
| 语言特性 | 静态类型,编译执行 | 动态类型,解释执行 |
| 内存管理 | 自动垃圾回收 | 自动垃圾回收 + GPU内存管理 |
| 并行处理 | 多线程,并发API | GPU并行计算,分布式训练 |
| 深度学习支持 | 有限(DL4J等第三方库) | 原生支持,完整生态系统 |
| 开发效率 | 冗长但类型安全 | 简洁但可能有运行时错误 |
| 部署环境 | 广泛支持(服务器、移动设备等) | 主要用于研究和训练环境 |
JAVA深度学习示例:
// 使用DL4J创建简单神经网络
import org.deeplearning4j.nn.conf.MultiLayerConfiguration;
import org.deeplearning4j.nn.conf.NeuralNetConfiguration;
import org.deeplearning4j.nn.conf.layers.DenseLayer;
import org.deeplearning4j.nn.conf.layers.OutputLayer;
import org.deeplearning4j.nn.multilayer.MultiLayerNetwork;
import org.deeplearning4j.nn.weights.WeightInit;
import org.nd4j.linalg.activations.Activation;
import org.nd4j.linalg.learning.config.Adam;
import org.nd4j.linalg.lossfunctions.LossFunctions;
public class SimpleNN {
public static void main(String[] args) {
// 配置网络
MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()
.seed(123)
.updater(new Adam(0.001))
.weightInit(WeightInit.XAVIER)
.list()
.layer(0, new DenseLayer.Builder()
.nIn(784) // 输入特征数
.nOut(256)
.activation(Activation.RELU)
.build())
.layer(1, new DenseLayer.Builder()
.nOut(128)
.activation(Activation.RELU)
.build())
.layer(2, new OutputLayer.Builder()
.nOut(10) // 输出类别数
.activation(Activation.SOFTMAX)
.lossFunction(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
.build())
.build();
// 创建模型
MultiLayerNetwork model = new MultiLayerNetwork(conf);
model.init();
// 训练和评估代码...
}
}
PyTorch示例:
# 使用PyTorch创建简单神经网络
import torch
import torch.nn as nn
import torch.optim as optim
# 定义网络
class SimpleNN(nn.Module):
def __init__(self):
super(SimpleNN, self).__init__()
self.fc1 = nn.Linear(784, 256)
self.fc2 = nn.Linear(256, 128)
self.fc3 = nn.Linear(128, 10)
self.relu = nn.ReLU()
def forward(self, x):
x = self.relu(self.fc1(x))
x = self.relu(self.fc2(x))
x = self.fc3(x)
return x
# 创建模型
model = SimpleNN()
# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练和评估代码...
2. PyTorch基础:张量
2.1 张量简介
张量(Tensor)是PyTorch的核心数据结构,类似于NumPy的多维数组,但具有额外的功能,如自动微分和GPU加速。
张量的特点:
- 多维数组结构
- 支持GPU加速
- 支持自动微分
- 与NumPy数组兼容
- 丰富的操作API
2.2 创建张量
import torch
import numpy as np
# 从列表创建张量
tensor1 = torch.tensor([1, 2, 3, 4])
print(tensor1) # tensor([1, 2, 3, 4])
# 创建特定形状的张量
zeros = torch.zeros(2, 3) # 2x3的全0张量
print(zeros)
# tensor([[0., 0., 0.],
# [0., 0., 0.]])
ones = torch.ones(2, 3) # 2x3的全1张量
print(ones)
# tensor([[1., 1., 1.],
# [1., 1., 1.]])
# 创建随机张量
rand_tensor = torch.rand(2, 3) # 均匀分布随机数
print(rand_tensor)
randn_tensor = torch.randn(2, 3) # 标准正态分布随机数
print(randn_tensor)
# 指定数据类型
float_tensor = torch.tensor([1, 2, 3], dtype=torch.float32)
print(float_tensor) # tensor([1., 2., 3.])
# 从NumPy数组创建张量
numpy_array = np.array([1, 2, 3])
tensor_from_numpy = torch.from_numpy(numpy_array)
print(tensor_from_numpy) # tensor([1, 2, 3], dtype=torch.int64)
# 创建特定范围的张量
range_tensor = torch.arange(0, 10, step=2)
print(range_tensor) # tensor([0, 2, 4, 6, 8])
linspace_tensor = torch.linspace(0, 10, steps=5)
print(linspace_tensor) # tensor([ 0.0000, 2.5000, 5.0000, 7.5000, 10.0000])
# 创建单位矩阵
eye_tensor = torch.eye(3)
print(eye_tensor)
# tensor([[1., 0., 0.],
# [0., 1., 0.],
# [0., 0., 1.]])
2.3 张量属性和操作
# 创建示例张量
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
# 张量属性
print(tensor.shape) # torch.Size([2, 3])
print(tensor.dtype) # torch.int64
print(tensor.device) # cpu (或 cuda:0 如果在GPU上)
print(tensor.ndim) # 2 (维度数)
print(tensor.size()) # torch.Size([2, 3])
# 索引和切片(类似NumPy)
print(tensor[0]) # tensor([1, 2, 3])
print(tensor[0, 1]) # tensor(2)
print(tensor[:, 1]) # tensor([2, 5])
# 张量操作
# 算术运算
a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])
print(a + b) # tensor([5, 7, 9])
print(torch.add(a, b)) # tensor([5, 7, 9])
print(a - b) # tensor([-3, -3, -3])
print(torch.sub(a, b)) # tensor([-3, -3, -3])
print(a * b) # tensor([4, 10, 18])
print(torch.mul(a, b)) # tensor([4, 10, 18])
print(a / b) # tensor([0.2500, 0.4000, 0.5000])
print(torch.div(a, b)) # tensor([0.2500, 0.4000, 0.5000])
# 矩阵运算
x = torch.tensor([[1, 2], [3, 4]])
y = torch.tensor([[5, 6], [7, 8]])
print(torch.matmul(x, y)) # 矩阵乘法
# tensor([[19, 22],
# [43, 50]])
print(x @ y) # 矩阵乘法的另一种写法
# tensor([[19, 22],
# [43, 50]])
# 统计操作
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(tensor.sum()) # tensor(21)
print(tensor.mean()) # tensor(3.5000)
print(tensor.max()) # tensor(6)
print(tensor.min()) # tensor(1)
# 按维度操作
print(tensor.sum(dim=0)) # 按行求和,tensor([5, 7, 9])
print(tensor.sum(dim=1)) # 按列求和,tensor([6, 15])
# 形状操作
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
# 改变形状
reshaped = tensor.reshape(3, 2)
print(reshaped)
# tensor([[1, 2],
# [3, 4],
# [5, 6]])
# 转置
transposed = tensor.t()
print(transposed)
# tensor([[1, 4],
# [2, 5],
# [3, 6]])
# 添加维度
unsqueezed = tensor.unsqueeze(0) # 在第0维添加维度
print(unsqueezed.shape) # torch.Size([1, 2, 3])
# 移除维度
squeezed = unsqueezed.squeeze(0) # 移除第0维
print(squeezed.shape) # torch.Size([2, 3])
# 连接张量
tensor1 = torch.tensor([[1, 2], [3, 4]])
tensor2 = torch.tensor([[5, 6], [7, 8]])
# 按行连接
cat_row = torch.cat((tensor1, tensor2), dim=0)
print(cat_row)
# tensor([[1, 2],
# [3, 4],
# [5, 6],
# [7, 8]])
# 按列连接
cat_col = torch.cat((tensor1, tensor2), dim=1)
print(cat_col)
# tensor([[1, 2, 5, 6],
# [3, 4, 7, 8]])
# 堆叠张量
stacked = torch.stack((tensor1, tensor2))
print(stacked.shape) # torch.Size([2, 2, 2])
2.4 GPU加速
PyTorch可以利用GPU加速计算,特别是对于大型神经网络和大数据集。
# 检查GPU是否可用
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"Using device: {device}")
# 创建张量并移动到GPU
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
tensor_gpu = tensor.to(device)
print(tensor_gpu.device) # cuda:0 (如果GPU可用)
# 在GPU上创建张量
tensor_gpu = torch.tensor([[1, 2, 3], [4, 5, 6]], device=device)
# 将GPU张量移回CPU
tensor_cpu = tensor_gpu.to('cpu')
# 注意:只有在同一设备上的张量才能进行操作
a = torch.tensor([1, 2, 3], device=device)
b = torch.tensor([4, 5, 6], device=device)
c = a + b # 正确:两个张量都在同一设备上
# 错误示例
# a = torch.tensor([1, 2, 3], device='cuda')
# b = torch.tensor([4, 5, 6], device='cpu')
# c = a + b # 错误:张量在不同设备上
2.5 与NumPy的互操作
PyTorch张量可以与NumPy数组相互转换,便于与其他Python科学计算库集成。
import torch
import numpy as np
# NumPy数组转换为PyTorch张量
numpy_array = np.array([[1, 2, 3], [4, 5, 6]])
tensor = torch.from_numpy(numpy_array)
print(tensor) # tensor([[1, 2, 3], [4, 5, 6]])
# PyTorch张量转换为NumPy数组
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
numpy_array = tensor.numpy()
print(numpy_array)
# array([[1, 2, 3],
# [4, 5, 6]])
# 注意:共享内存
numpy_array = np.array([1, 2, 3])
tensor = torch.from_numpy(numpy_array)
numpy_array[0] = 5
print(tensor) # tensor([5, 2, 3]) - 张量也被修改
# 注意:GPU张量不能直接转换为NumPy数组
if torch.cuda.is_available():
tensor_gpu = torch.tensor([1, 2, 3], device='cuda')
# numpy_array = tensor_gpu.numpy() # 错误
numpy_array = tensor_gpu.cpu().numpy() # 正确:先移到CPU
3. 自动微分与计算图
3.1 自动微分简介
自动微分(Autograd)是PyTorch的核心功能之一,它能够自动计算神经网络中的梯度,这对于实现反向传播算法至关重要。
自动微分的工作原理:
- 记录操作历史,构建计算图
- 前向传播计算输出值
- 反向传播计算梯度
3.2 计算图
计算图是一种表示计算过程的有向图,其中节点表示操作,边表示数据流。
PyTorch的动态计算图:
- 在运行时构建(Define-by-Run)
- 每次前向传播可以构建不同的计算图
- 便于调试和实现复杂控制流
import torch
# 创建需要梯度的张量
x = torch.tensor(2.0, requires_grad=True)
y = torch.tensor(3.0, requires_grad=True)
# 构建计算图
z = x**2 + y**3
# 查看计算图
print(z.grad_fn) # AddBackward0
print(z.grad_fn.next_functions) # (PowBackward0, PowBackward0)
# 可视化计算图(需要安装graphviz)
from torchviz import make_dot
make_dot(z, {'x': x, 'y': y}).render("computation_graph", format="png")
3.3 梯度计算
import torch
# 创建需要梯度的张量
x = torch.tensor(2.0, requires_grad=True)
y = torch.tensor(3.0, requires_grad=True)
# 前向传播
z = x**2 + y**3 # z = 4 + 27 = 31
# 反向传播(计算梯度)
z.backward()
# 查看梯度
print(x.grad) # dz/dx = 2*x = 2*2 = 4
print(y.grad) # dz/dy = 3*y^2 = 3*3^2 = 27
# 多次反向传播
x = torch.tensor(2.0, requires_grad=True)
y = torch.tensor(3.0, requires_grad=True)
# 第一次前向和反向传播
z = x**2 + y**3
z.backward()
print(f"First pass - x.grad: {x.grad}, y.grad: {y.grad}")
# 梯度会累积,需要手动清零
x.grad.zero_()
y.grad.zero_()
# 第二次前向和反向传播
z = x**3 + y**2
z.backward()
print(f"Second pass - x.grad: {x.grad}, y.grad: {y.grad}")
3.4 梯度下降示例
import torch
import matplotlib.pyplot as plt
# 创建数据
x = torch.linspace(-10, 10, 1000, requires_grad=True)
y = x**2 # 简单的二次函数
# 可视化函数
plt.figure(figsize=(10, 6))
plt.plot(x.detach().numpy(), y.detach().numpy())
plt.title('f(x) = x^2')
plt.grid(True)
plt.show()
# 实现梯度下降
learning_rate = 0.1
x_val = torch.tensor([8.0], requires_grad=True) # 初始点
optimizer = torch.optim.SGD([x_val], lr=learning_rate)
steps = []
x_values = []
y_values = []
for i in range(20):
# 前向传播
y_val = x_val**2
# 记录当前值
steps.append(i)
x_values.append(x_val.item())
y_values.append(y_val.item())
print(f"Step {i}: x = {x_val.item():.4f}, y = {y_val.item():.4f}")
# 反向传播
optimizer.zero_grad()
y_val.backward()
# 更新参数
optimizer.step()
# 可视化优化过程
plt.figure(figsize=(10, 6))
plt.plot(x.detach().numpy(), y.detach().numpy(), 'b-')
plt.plot(x_values, y_values, 'ro-')
plt.title('Gradient Descent Optimization')
plt.grid(True)
plt.show()
3.5 禁用梯度计算
在某些情况下,我们需要禁用梯度计算以提高性能或防止不必要的计算。
import torch
# 方法1:使用torch.no_grad()上下文管理器
x = torch.tensor(2.0, requires_grad=True)
with torch.no_grad():
y = x**2
print(y.requires_grad) # False
# 方法2:使用detach()方法
x = torch.tensor(2.0, requires_grad=True)
y = x**2
z = y.detach() # 创建一个新的张量,与计算图分离
print(z.requires_grad) # False
# 方法3:设置requires_grad属性
x = torch.tensor(2.0, requires_grad=True)
x.requires_grad_(False)
y = x**2
print(y.requires_grad) # False
4. 神经网络基础
4.1 神经网络模块(nn.Module)
PyTorch的nn.Module是构建神经网络的基础类,提供了模型定义、参数管理和前向传播的框架。
import torch
import torch.nn as nn
# 定义一个简单的神经网络
class SimpleNN(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super(SimpleNN, self).__init__()
# 定义网络层
self.fc1 = nn.Linear(input_size, hidden_size) # 第一个全连接层
self.relu = nn.ReLU() # ReLU激活函数
self.fc2 = nn.Linear(hidden_size, output_size) # 第二个全连接层
def forward(self, x):
# 定义前向传播
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
return x
# 创建模型实例
model = SimpleNN(input_size=10, hidden_size=20, output_size=2)
print(model)
# 查看模型参数
for name, param in model.named_parameters():
print(f"Parameter {name}: {param.shape}")
# 前向传播
input_tensor = torch.randn(5, 10) # 批量大小为5,每个样本10个特征
output = model(input_tensor)
print(f"Input shape: {input_tensor.shape}, Output shape: {output.shape}")
4.2 常用神经网络层
PyTorch提供了丰富的神经网络层,用于构建各种类型的神经网络。
import torch
import torch.nn as nn
# 全连接层(Linear/Dense)
linear = nn.Linear(in_features=10, out_features=20)
input_tensor = torch.randn(5, 10) # 批量大小为5,每个样本10个特征
output = linear(input_tensor)
print(f"Linear output shape: {output.shape}") # torch.Size([5, 20])
# 卷积层(Convolutional)
conv2d = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1)
input_tensor = torch.randn(5, 3, 32, 32) # [batch_size, channels, height, width]
output = conv2d(input_tensor)
print(f"Conv2d output shape: {output.shape}") # torch.Size([5, 16, 32, 32])
# 池化层(Pooling)
maxpool = nn.MaxPool2d(kernel_size=2, stride=2)
output = maxpool(output)
print(f"MaxPool output shape: {output.shape}") # torch.Size([5, 16, 16, 16])
# 批归一化(Batch Normalization)
batch_norm = nn.BatchNorm2d(16)
output = batch_norm(output)
print(f"BatchNorm output shape: {output.shape}") # torch.Size([5, 16, 16, 16])
# 激活函数(Activation Functions)
relu = nn.ReLU()
output = relu(output)
print(f"ReLU output shape: {output.shape}") # torch.Size([5, 16, 16, 16])
# Dropout(防止过拟合)
dropout = nn.Dropout(p=0.5)
output = dropout(output)
print(f"Dropout output shape: {output.shape}") # torch.Size([5, 16, 16, 16])
# 循环层(Recurrent)
lstm = nn.LSTM(input_size=10, hidden_size=20, num_layers=2, batch_first=True)
input_tensor = torch.randn(5, 8, 10) # [batch_size, sequence_length, input_size]
output, (h_n, c_n) = lstm(input_tensor)
print(f"LSTM output shape: {output.shape}") # torch.Size([5, 8, 20])
print(f"LSTM hidden state shape: {h_n.shape}") # torch.Size([2, 5, 20])
# Embedding层(用于NLP)
embedding = nn.Embedding(num_embeddings=10000, embedding_dim=300)
input_tensor = torch.LongTensor([[1, 2, 3], [4, 5, 6]])
output = embedding(input_tensor)
print(f"Embedding output shape: {output.shape}") # torch.Size([2, 3, 300])
4.3 损失函数
损失函数用于衡量模型预测与真实值之间的差距,是模型训练的优化目标。
import torch
import torch.nn as nn
# 回归损失函数
# 均方误差损失(MSE)
mse_loss = nn.MSELoss()
predictions = torch.tensor([0.5, 1.5, 2.5])
targets = torch.tensor([1.0, 2.0, 3.0])
loss = mse_loss(predictions, targets)
print(f"MSE Loss: {loss.item()}") # 0.25
# L1损失(平均绝对误差)
l1_loss = nn.L1Loss()
loss = l1_loss(predictions, targets)
print(f"L1 Loss: {loss.item()}") # 0.5
# 平滑L1损失(Huber损失)
smooth_l1_loss = nn.SmoothL1Loss()
loss = smooth_l1_loss(predictions, targets)
print(f"Smooth L1 Loss: {loss.item()}")
# 分类损失函数
# 交叉熵损失
ce_loss = nn.CrossEntropyLoss()
predictions = torch.tensor([[0.1, 0.6, 0.3], [0.2, 0.3, 0.5]]) # [batch_size, num_classes]
targets = torch.tensor([1, 2]) # 类别索引
loss = ce_loss(predictions, targets)
print(f"Cross Entropy Loss: {loss.item()}")
# 二元交叉熵损失
bce_loss = nn.BCEWithLogitsLoss()
predictions = torch.tensor([0.7, -0.2, 0.9])
targets = torch.tensor([1.0, 0.0, 1.0])
loss = bce_loss(predictions, targets)
print(f"Binary Cross Entropy Loss: {loss.item()}")
4.4 优化器
优化器用于更新模型参数,以最小化损失函数。
import torch
import torch.nn as nn
import torch.optim as optim
# 定义一个简单模型
model = nn.Linear(10, 1)
# 随机梯度下降(SGD)
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
# Adam优化器
optimizer = optim.Adam(model.parameters(), lr=0.001, betas=(0.9, 0.999))
# RMSprop优化器
optimizer = optim.RMSprop(model.parameters(), lr=0.01, alpha=0.99)
# Adagrad优化器
optimizer = optim.Adagrad(model.parameters(), lr=0.01)
# 学习率调度器
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)
scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=10)
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=200)
# 优化器使用示例
model = nn.Linear(10, 1)
optimizer = optim.SGD(model.parameters(), lr=0.1)
criterion = nn.MSELoss()
# 模拟训练循环
for epoch in range(100):
# 前向传播
inputs = torch.randn(32, 10) # 批量大小为32,每个样本10个特征
targets = torch.randn(32, 1) # 对应的目标值
outputs = model(inputs)
loss = criterion(outputs, targets)
# 反向传播和优化
optimizer.zero_grad() # 清除之前的梯度
loss.backward() # 计算梯度
optimizer.step() # 更新参数
# 打印训练信息
if (epoch + 1) % 10 == 0:
print(f'Epoch [{epoch+1}/100], Loss: {loss.item():.4f}')
5. 构建和训练神经网络
5.1 完整的训练流程
下面是一个完整的神经网络训练流程示例,包括数据加载、模型定义、训练和评估。
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import matplotlib.pyplot as plt
import numpy as np
# 设置随机种子以便结果可复现
torch.manual_seed(42)
np.random.seed(42)
# 1. 准备数据
# 生成一些模拟数据
X = np.random.rand(1000, 10)
y = (X[:, 0] + X[:, 1])**2 + np.random.randn(1000) * 0.1 # 非线性关系加噪声
# 划分训练集和测试集
X_train, X_test = X[:800], X[800:]
y_train, y_test = y[:800], y[800:]
# 转换为PyTorch张量
X_train_tensor = torch.FloatTensor(X_train)
y_train_tensor = torch.FloatTensor(y_train).view(-1, 1)
X_test_tensor = torch.FloatTensor(X_test)
y_test_tensor = torch.FloatTensor(y_test).view(-1, 1)
# 创建数据集和数据加载器
train_dataset = TensorDataset(X_train_tensor, y_train_tensor)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
# 2. 定义模型
class RegressionModel(nn.Module):
def __init__(self):
super(RegressionModel, self).__init__()
self.fc1 = nn.Linear(10, 64)
self.relu1 = nn.ReLU()
self.fc2 = nn.Linear(64, 32)
self.relu2 = nn.ReLU()
self.fc3 = nn.Linear(32, 1)
def forward(self, x):
x = self.relu1(self.fc1(x))
x = self.relu2(self.fc2(x))
x = self.fc3(x)
return x
# 创建模型实例
model = RegressionModel()
# 3. 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 4. 训练模型
num_epochs = 100
train_losses = []
for epoch in range(num_epochs):
model.train() # 设置为训练模式
running_loss = 0.0
for inputs, targets in train_loader:
# 前向传播
outputs = model(inputs)
loss = criterion(outputs, targets)
# 反向传播和优化
optimizer.zero_grad()
loss.backward()
optimizer.step()
running_loss += loss.item()
# 计算平均损失
epoch_loss = running_loss / len(train_loader)
train_losses.append(epoch_loss)
# 打印训练信息
if (epoch + 1) % 10 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {epoch_loss:.4f}')
# 5. 评估模型
model.eval() # 设置为评估模式
with torch.no_grad():
predictions = model(X_test_tensor)
test_loss = criterion(predictions, y_test_tensor)
print(f'Test Loss: {test_loss.item():.4f}')
# 6. 可视化训练过程
plt.figure(figsize=(10, 6))
plt.plot(range(1, num_epochs + 1), train_losses, 'b-')
plt.title('Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.grid(True)
plt.show()
# 7. 可视化预测结果
plt.figure(figsize=(10, 6))
plt.scatter(y_test, predictions.numpy(), alpha=0.5)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--')
plt.title('Predictions vs Actual')
plt.xlabel('Actual')
plt.ylabel('Predictions')
plt.grid(True)
plt.show()
5.2 使用内置数据集
PyTorch提供了一些内置数据集,可以通过torchvision.datasets访问。
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
# 设置随机种子
torch.manual_seed(42)
# 1. 加载MNIST数据集
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
train_dataset = torchvision.datasets.MNIST(
root='./data',
train=True,
download=True,
transform=transform
)
test_dataset = torchvision.datasets.MNIST(
root='./data',
train=False,
download=True,
transform=transform
)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=1000, shuffle=False)
# 2. 定义CNN模型
class ConvNet(nn.Module):
def __init__(self):
super(ConvNet, self).__init__()
self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1)
self.relu1 = nn.ReLU()
self.pool1 = nn.MaxPool2d(kernel_size=2)
self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)
self.relu2 = nn.ReLU()
self.pool2 = nn.MaxPool2d(kernel_size=2)
self.fc1 = nn.Linear(64 * 7 * 7, 128)
self.relu3 = nn.ReLU()
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.pool1(self.relu1(self.conv1(x)))
x = self.pool2(self.relu2(self.conv2(x)))
x = x.view(-1, 64 * 7 * 7)
x = self.relu3(self.fc1(x))
x = self.fc2(x)
return x
# 创建模型实例
model = ConvNet()
# 3. 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 4. 训练模型
num_epochs = 5
for epoch in range(num_epochs):
model.train()
running_loss = 0.0
for i, (images, labels) in enumerate(train_loader):
# 前向传播
outputs = model(images)
loss = criterion(outputs, labels)
# 反向传播和优化
optimizer.zero_grad()
loss.backward()
optimizer.step()
running_loss += loss.item()
if (i + 1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(train_loader)}], Loss: {loss.item():.4f}')
# 评估模型
model.eval()
with torch.no_grad():
correct = 0
total = 0
for images, labels in test_loader:
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(f'Epoch [{epoch+1}/{num_epochs}], Accuracy: {100 * correct / total:.2f}%')
5.3 保存和加载模型
PyTorch提供了保存和加载模型的功能,便于模型的部署和复用。
import torch
import torch.nn as nn
# 定义一个简单模型
class SimpleModel(nn.Module):
def __init__(self):
super(SimpleModel, self).__init__()
self.fc = nn.Linear(10, 5)
def forward(self, x):
return self.fc(x)
# 创建模型实例
model = SimpleModel()
# 保存整个模型
torch.save(model, 'model_full.pth')
# 只保存模型参数(推荐方式)
torch.save(model.state_dict(), 'model_params.pth')
# 加载整个模型
loaded_model = torch.load('model_full.pth')
loaded_model.eval() # 设置为评估模式
# 加载模型参数(需要先创建模型实例)
new_model = SimpleModel()
new_model.load_state_dict(torch.load('model_params.pth'))
new_model.eval() # 设置为评估模式
# 保存和加载检查点(包含模型参数、优化器状态等)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
epoch = 10
loss = 0.1
# 保存检查点
checkpoint = {
'epoch': epoch,
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'loss': loss
}
torch.save(checkpoint, 'checkpoint.pth')
# 加载检查点
checkpoint = torch.load('checkpoint.pth')
model = SimpleModel()
model.load_state_dict(checkpoint['model_state_dict'])
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
epoch = checkpoint['epoch']
loss = checkpoint['loss']
6. 从JAVA开发者视角理解PyTorch
6.1 编程范式对比
JAVA面向对象编程:
- 类和对象是核心概念
- 静态类型系统
- 显式类型声明
- 编译时类型检查
- 继承和多态
PyTorch混合编程范式:
- 函数式和面向对象混合
- 动态类型系统
- 隐式类型推断
- 运行时类型检查
- 基于组合的模型构建
6.2 代码组织对比
JAVA项目结构:
src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ ├── model/
│ │ │ └── NeuralNetwork.java
│ │ ├── data/
│ │ │ └── DataLoader.java
│ │ ├── train/
│ │ │ └── Trainer.java
│ │ └── utils/
│ │ └── Evaluator.java
│ └── resources/
└── test/
└── java/
PyTorch项目结构:
project/
├── data/
│ └── dataset.py
├── models/
│ └── network.py
├── utils/
│ ├── train.py
│ └── evaluate.py
├── config.py
└── main.py
6.3 设计模式对比
JAVA常用设计模式:
- 工厂模式
- 单例模式
- 观察者模式
- 策略模式
- 装饰器模式
PyTorch常用设计模式:
- 组合模式(nn.Module)
- 工厂函数(如torch.nn.functional)
- 装饰器(如@property)
- 上下文管理器(如with torch.no_grad())
- 回调函数(如学习率调度器)
6.4 实现对比
JAVA实现简单神经网络:
// JAVA使用DL4J实现简单神经网络
import org.deeplearning4j.nn.conf.MultiLayerConfiguration;
import org.deeplearning4j.nn.conf.NeuralNetConfiguration;
import org.deeplearning4j.nn.conf.layers.DenseLayer;
import org.deeplearning4j.nn.conf.layers.OutputLayer;
import org.deeplearning4j.nn.multilayer.MultiLayerNetwork;
import org.deeplearning4j.nn.weights.WeightInit;
import org.deeplearning4j.optimize.listeners.ScoreIterationListener;
import org.nd4j.linalg.activations.Activation;
import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.dataset.DataSet;
import org.nd4j.linalg.factory.Nd4j;
import org.nd4j.linalg.learning.config.Adam;
import org.nd4j.linalg.lossfunctions.LossFunctions;
public class SimpleNeuralNetwork {
public static void main(String[] args) {
// 创建训练数据
INDArray input = Nd4j.rand(100, 10);
INDArray output = Nd4j.rand(100, 1);
DataSet trainingData = new DataSet(input, output);
// 配置网络
MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()
.seed(123)
.updater(new Adam(0.001))
.weightInit(WeightInit.XAVIER)
.list()
.layer(0, new DenseLayer.Builder()
.nIn(10)
.nOut(64)
.activation(Activation.RELU)
.build())
.layer(1, new DenseLayer.Builder()
.nOut(32)
.activation(Activation.RELU)
.build())
.layer(2, new OutputLayer.Builder()
.nOut(1)
.activation(Activation.IDENTITY)
.lossFunction(LossFunctions.LossFunction.MSE)
.build())
.build();
// 创建并初始化网络
MultiLayerNetwork model = new MultiLayerNetwork(conf);
model.init();
model.setListeners(new ScoreIterationListener(10));
// 训练网络
for (int i = 0; i < 100; i++) {
model.fit(trainingData);
}
// 使用网络进行预测
INDArray testInput = Nd4j.rand(10, 10);
INDArray predictions = model.output(testInput);
System.out.println("Predictions: " + predictions);
}
}
PyTorch实现简单神经网络:
# PyTorch实现简单神经网络
import torch
import torch.nn as nn
import torch.optim as optim
# 创建训练数据
X = torch.rand(100, 10)
y = torch.rand(100, 1)
# 定义网络
class SimpleNN(nn.Module):
def __init__(self):
super(SimpleNN, self).__init__()
self.fc1 = nn.Linear(10, 64)
self.relu1 = nn.ReLU()
self.fc2 = nn.Linear(64, 32)
self.relu2 = nn.ReLU()
self.fc3 = nn.Linear(32, 1)
def forward(self, x):
x = self.relu1(self.fc1(x))
x = self.relu2(self.fc2(x))
x = self.fc3(x)
return x
# 创建模型实例
model = SimpleNN()
# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练网络
for epoch in range(100):
# 前向传播
outputs = model(X)
loss = criterion(outputs, y)
# 反向传播和优化
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (epoch + 1) % 10 == 0:
print(f'Epoch [{epoch+1}/100], Loss: {loss.item():.4f}')
# 使用网络进行预测
test_input = torch.rand(10, 10)
with torch.no_grad():
predictions = model(test_input)
print("Predictions:", predictions)
7. 实践练习
练习1:张量操作
- 创建一个5x5的随机张量
- 计算张量的均值、标准差、最大值和最小值
- 对张量进行归一化(减去均值,除以标准差)
- 将张量转换为NumPy数组,然后再转回PyTorch张量
练习2:自动微分
- 创建一个需要梯度的标量张量x,初始值为2.0
- 计算函数f(x) = x^3 + 2x^2 + 3x + 1
- 计算f(x)关于x的梯度
- 使用梯度下降法找到函数的局部最小值
练习3:神经网络
- 创建一个三层神经网络,用于对MNIST数据集进行分类
- 训练网络10个epoch
- 评估网络在测试集上的准确率
- 保存模型参数,然后重新加载并验证
8. 总结与反思
- PyTorch是一个强大的深度学习框架,提供了灵活的工具和库,用于构建和训练神经网络模型
- PyTorch的核心是张量(Tensor)和自动微分(Autograd)系统,它们使得神经网络的构建和训练变得简单高效
- PyTorch的动态计算图(Define-by-Run)使得调试和实验更加方便,特别适合研究和原型开发
- 相比JAVA的深度学习库,PyTorch提供了更简洁的API和更丰富的功能,特别适合大模型开发
- JAVA开发者可以利用已有的面向对象编程经验,快速理解PyTorch的模块化设计和组合模式
- PyTorch的生态系统非常丰富,包括数据加载、模型构建、训练优化和模型部署等各个方面
9. 预习与延伸阅读
预习内容
- 大模型开发环境搭建(Conda、Jupyter等)
- GPU加速和分布式训练基础
- 云平台使用(如AWS、Azure、阿里云等)
延伸阅读
- PyTorch官方文档:pytorch.org/docs/stable…
- Eli Stevens等,《Deep Learning with PyTorch》
- Vishnu Subramanian,《Deep Learning with PyTorch: A practical approach to building neural network models using PyTorch》
- Sebastian Raschka等,《PyTorch Recipes: A Problem-Solution Approach》
- PyTorch官方教程:pytorch.org/tutorials/
10. 明日预告
明天我们将学习大模型开发环境的搭建,包括Conda环境管理、Jupyter Notebook使用、GPU加速配置以及云平台的使用。这些工具和技术将为后续的大模型开发提供必要的基础设施支持。