第二阶段_技术栈过渡-Day 4: PyTorch基础

75 阅读18分钟

Day 4: PyTorch基础

学习目标

  • 理解PyTorch的核心概念和架构
  • 掌握PyTorch张量的创建和操作方法
  • 学习自动微分机制和计算图
  • 了解如何构建和训练简单的神经网络
  • 对比JAVA和PyTorch在深度学习开发方面的差异

1. PyTorch简介

1.1 什么是PyTorch

PyTorch是一个开源的深度学习框架,由Facebook的AI研究团队开发,提供了灵活的工具和库,用于构建和训练神经网络模型。

定义:PyTorch是一个基于Python的科学计算库,提供了两个高级功能:

  1. 具有强大GPU加速的张量计算(类似NumPy)
  2. 基于自动微分系统的深度神经网络构建和训练

核心特点

  • 命令式编程风格(Eager Execution)
  • 动态计算图(Define-by-Run)
  • Python优先设计
  • 强大的GPU加速
  • 丰富的生态系统和社区支持

1.2 PyTorch vs TensorFlow

PyTorch和TensorFlow是当前最流行的两个深度学习框架,它们有不同的设计理念和优势。

特性PyTorchTensorFlow
编程范式命令式(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对比

特性JAVAPyTorch
语言特性静态类型,编译执行动态类型,解释执行
内存管理自动垃圾回收自动垃圾回收 + GPU内存管理
并行处理多线程,并发APIGPU并行计算,分布式训练
深度学习支持有限(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:张量操作

  1. 创建一个5x5的随机张量
  2. 计算张量的均值、标准差、最大值和最小值
  3. 对张量进行归一化(减去均值,除以标准差)
  4. 将张量转换为NumPy数组,然后再转回PyTorch张量

练习2:自动微分

  1. 创建一个需要梯度的标量张量x,初始值为2.0
  2. 计算函数f(x) = x^3 + 2x^2 + 3x + 1
  3. 计算f(x)关于x的梯度
  4. 使用梯度下降法找到函数的局部最小值

练习3:神经网络

  1. 创建一个三层神经网络,用于对MNIST数据集进行分类
  2. 训练网络10个epoch
  3. 评估网络在测试集上的准确率
  4. 保存模型参数,然后重新加载并验证

8. 总结与反思

  • PyTorch是一个强大的深度学习框架,提供了灵活的工具和库,用于构建和训练神经网络模型
  • PyTorch的核心是张量(Tensor)和自动微分(Autograd)系统,它们使得神经网络的构建和训练变得简单高效
  • PyTorch的动态计算图(Define-by-Run)使得调试和实验更加方便,特别适合研究和原型开发
  • 相比JAVA的深度学习库,PyTorch提供了更简洁的API和更丰富的功能,特别适合大模型开发
  • JAVA开发者可以利用已有的面向对象编程经验,快速理解PyTorch的模块化设计和组合模式
  • PyTorch的生态系统非常丰富,包括数据加载、模型构建、训练优化和模型部署等各个方面

9. 预习与延伸阅读

预习内容

  • 大模型开发环境搭建(Conda、Jupyter等)
  • GPU加速和分布式训练基础
  • 云平台使用(如AWS、Azure、阿里云等)

延伸阅读

  1. PyTorch官方文档:pytorch.org/docs/stable…
  2. Eli Stevens等,《Deep Learning with PyTorch》
  3. Vishnu Subramanian,《Deep Learning with PyTorch: A practical approach to building neural network models using PyTorch》
  4. Sebastian Raschka等,《PyTorch Recipes: A Problem-Solution Approach》
  5. PyTorch官方教程:pytorch.org/tutorials/

10. 明日预告

明天我们将学习大模型开发环境的搭建,包括Conda环境管理、Jupyter Notebook使用、GPU加速配置以及云平台的使用。这些工具和技术将为后续的大模型开发提供必要的基础设施支持。