第二章:Python与PyTorch基础回顾

说实话,每次带新人入门图神经网络,我最怕的不是他们数学不好,而是连Python和PyTorch的基本功都没打牢。你想想看,模型还没搭起来,光是在张量维度上就卡住了,这多憋屈。

这一章,咱们就把地基夯实。我会带着你快速过一遍Python核心语法、NumPy和Pandas的数据处理,然后重点攻克PyTorch的张量操作和自动求导机制。最后,咱们亲手搭一个最简单的神经网络——别小看它,这可是你走向GNN的第一步。

本章核心目标:让你在30分钟内,从「能写Python」进化到「能用PyTorch搭网络」。

2.1 Python核心语法速览

Python这东西,上手容易,但坑也不少。我见过太多人把列表推导式写得跟天书似的,其实没必要。咱们只捡最常用的说。

2.1.1 数据结构选型

我个人习惯,处理固定长度的数据用元组,需要频繁增删用列表,查重用集合,键值对用字典。别问我为什么——这都是血泪教训换来的。

# 列表:最常用,可变
features = [0.1, 0.2, 0.3, 0.4]
features.append(0.5)

# 元组:不可变,适合做特征组合
node = (1, 'user', 0.85)  # (id, type, score)

# 字典:图结构的基础
graph = {
    'nodes': [1, 2, 3],
    'edges': [(1,2), (2,3)]
}

# 集合:去重利器
unique_labels = {0, 1, 2, 1, 0}  # 结果:{0, 1, 2}

我的小技巧:处理图数据时,我习惯用字典存储邻接表。比如 adj[node] = [neighbor1, neighbor2],比邻接矩阵省内存多了。

2.1.2 函数与Lambda

Python的函数定义很简单,但有个细节要注意——默认参数别用可变对象。我曾经因为这个bug排查了整整一个下午。

# 正确的默认参数写法
def normalize(features, eps=1e-8):
    return [f / (sum(features) + eps) for f in features]

# Lambda表达式:图卷积里经常用
edge_weight = lambda x, y: 1.0 / (1.0 + abs(x - y))

2.2 NumPy与Pandas数据处理

做图神经网络,NumPy是绕不开的。你想想看,邻接矩阵、特征矩阵、标签向量,哪个不是多维数组?

2.2.1 NumPy核心操作

我记得刚接触图数据时,最头疼的就是矩阵运算。其实NumPy把一切都简化了。

import numpy as np

# 创建邻接矩阵(3个节点)
adj = np.array([
    [0, 1, 1],
    [1, 0, 0],
    [1, 0, 0]
])

# 特征矩阵(3个节点,每个4维特征)
features = np.random.randn(3, 4)

# 矩阵乘法:图卷积的核心
degree = np.diag(np.sum(adj, axis=1))
laplacian = degree - adj  # 拉普拉斯矩阵

print(f"邻接矩阵形状: {adj.shape}")
print(f"特征矩阵形状: {features.shape}")
NumPy函数 用途 图神经网络中的应用
np.dot() 矩阵乘法 特征传播
np.sum() 求和 计算节点度
np.diag() 创建对角矩阵 度矩阵
np.random.randn() 随机初始化 特征初始化

2.2.2 Pandas数据清洗

实际项目中,数据很少是干净的。我曾经处理过一个金融交易图,光清洗数据就花了两天。Pandas在这方面是神器。

import pandas as pd

# 模拟交易数据
transactions = pd.DataFrame({
    'from_id': [1, 1, 2, 3, 2],
    'to_id': [2, 3, 1, 2, 3],
    'amount': [100, 200, 150, None, 300],
    'time': ['2024-01-01', '2024-01-02', '2024-01-01', '2024-01-03', '2024-01-02']
})

# 处理缺失值
transactions['amount'].fillna(transactions['amount'].mean(), inplace=True)

# 按时间排序
transactions.sort_values('time', inplace=True)

print(transactions.head())

避坑指南:我曾经在构建图数据时,直接用Pandas的DataFrame做循环,结果数据量一大就卡死了。记住:能用向量化操作就别用循环,能用NumPy就别用纯Python。

2.3 PyTorch张量操作与自动求导

好了,重头戏来了。PyTorch的张量(Tensor)和NumPy的数组很像,但多了两个杀手锏:GPU加速和自动求导。

2.3.1 张量创建与操作

说白了,张量就是多维数组的升级版。我建议你从一开始就养成用PyTorch的习惯,别在NumPy和Tensor之间来回倒腾。

import torch

# 创建张量
x = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32)
y = torch.randn(2, 2)

# 张量运算
z = torch.mm(x, y)  # 矩阵乘法
print(f"矩阵乘法结果:\n{z}")

# 形状变换
x_flat = x.view(-1)  # 展平
print(f"展平后: {x_flat}")

# GPU支持(如果有)
if torch.cuda.is_available():
    x = x.cuda()
    print("数据已迁移到GPU")

2.3.2 自动求导机制

这是PyTorch最让我爱不释手的地方。你想想看,以前算梯度得手动推导公式,现在只要设置requires_grad=True,PyTorch自动帮你算好。

# 创建需要梯度的张量
w = torch.tensor([2.0], requires_grad=True)
b = torch.tensor([1.0], requires_grad=True)

# 前向传播
x = torch.tensor([3.0])
y_pred = w * x + b
loss = (y_pred - 10) ** 2  # 假设真实值是10

# 反向传播
loss.backward()

print(f"w的梯度: {w.grad}")
print(f"b的梯度: {b.grad}")

关键理解:自动求导的本质是计算图。每次前向传播都会构建一个动态计算图,反向传播时沿着图计算梯度。这就是为什么PyTorch被称为「动态图」框架。

2.4 搭建第一个简单神经网络

理论说再多,不如动手搭一个。咱们用PyTorch实现一个最简单的全连接网络,用来做二分类。

2.4.1 网络结构定义

我习惯把网络定义成一个类,继承nn.Module。这样代码清晰,也方便扩展。

import torch.nn as nn
import torch.nn.functional as F

class SimpleNN(nn.Module):
    def __init__(self, input_dim, hidden_dim, output_dim):
        super(SimpleNN, 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 = torch.sigmoid(self.fc2(x))
        return x

# 实例化网络
model = SimpleNN(input_dim=10, hidden_dim=5, output_dim=1)
print(model)

2.4.2 训练循环

训练循环的套路其实很固定:前向传播 -> 计算损失 -> 反向传播 -> 更新参数。我刚开始写的时候总忘记清零梯度,后来养成了习惯,每次反向传播前都调用optimizer.zero_grad()

import torch.optim as optim

# 生成模拟数据
X = torch.randn(100, 10)
y = torch.randint(0, 2, (100, 1)).float()

# 定义损失函数和优化器
criterion = nn.BCELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 训练
epochs = 100
for epoch in range(epochs):
    # 前向传播
    outputs = model(X)
    loss = criterion(outputs, y)
    
    # 反向传播
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    
    if (epoch + 1) % 20 == 0:
        print(f'Epoch [{epoch+1}/{epochs}], Loss: {loss.item():.4f}')

我的经验:刚开始训练时,如果loss不下降,先检查数据是否归一化。我曾经在一个项目里,因为特征值范围差了三个数量级,导致模型怎么都学不动。后来做了标准化,问题立刻解决。

2.4.3 知识体系总览

下面这张图,是我为你梳理的本章知识脉络。从Python基础到PyTorch实战,每一步都是为图神经网络打基础。

第二章:Python与PyTorch基础回顾 - 知识体系 Python核心语法 NumPy & Pandas PyTorch张量 数据结构 列表/元组/字典/集合 函数与Lambda 列表推导式 数据处理 矩阵运算 数据清洗 缺失值处理 张量操作 创建/变换/运算 GPU加速 自动求导 搭建第一个简单神经网络 为图神经网络打下基础

看到这张图,你应该能感受到:这一章的内容环环相扣。Python基础是工具,NumPy/Pandas是数据处理的利器,PyTorch是模型构建的核心,而那个简单的神经网络,就是你踏入图神经网络世界的第一块敲门砖。

本章小结:我们快速回顾了Python核心语法,掌握了NumPy和Pandas的数据处理技巧,深入理解了PyTorch的张量操作和自动求导机制,最后亲手搭建了一个完整的神经网络。这些知识,在后续的图神经网络课程中会反复用到。

专注资料整理