1. 认识 Caspian
Caspian 是一个纯 Python 实现的轻量级深度学习研究库,其最大特色是完全基于 NumPy 构建,零外部框架依赖。这意味着你无需安装 TensorFlow、PyTorch 等庞然大物,仅凭熟悉的 NumPy 就能轻松搭建、训练并理解神经网络的每一个底层细节。
Caspian 的设计哲学深受 PyTorch 和 TensorFlow 的启发,它把神经网络中每个核心组件都抽象成独立的基类,你可以像搭积木一样自由继承和组合,构建出完全属于自己的模型。如果你希望深入理解深度学习框架的内部运作,或者需要一个极致轻量的实验环境,Caspian 会是理想的起点。
2. 设计哲学与架构解析
为什么需要 Caspian?
主流框架虽然功能强大,但内部封装复杂,代码庞大。Caspian 反其道而行之,将所有复杂操作回归到 NumPy 数组计算,让你能清晰地看到数据在前向传播、反向传播以及参数更新过程中的每一步变化。
六大核心基类
Caspian 的所有组件都继承自以下六种基类之一,它们共同构成了一个完整的深度学习生态系统:
cspn.Layer:一切模型的基础,负责数据的处理和特征学习。
cspn.Activation:非线性函数(如 ReLU、Sigmoid),赋予模型非线性表达能力。
cspn.PoolFunc:与池化层配合使用,对数据进行降维。
cspn.Loss:量化模型预测与真实标签之间误差的损失函数。
cspn.Optimizer:负责根据梯度更新层权重的优化器。
cspn.Scheduler:动态调整优化器学习率的调度器。
组件协作关系
在 Caspian 中,各组件并非独立存在,而是按一种清晰的结构组织在一起。下面的流程图直观地描绘了这种关系:
可以看出,Layer 是核心节点,可以按需装载 Activation/PoolFunc 或 Optimizer,而 Optimizer 又能进一步包含 Scheduler。这种设计让我们能灵活控制每一层的计算行为。
核心工作流程:训练一环
一个标准的训练迭代由四个步骤串联完成:
前向传播(forward):数据逐层流动,最终输出预测结果。
计算损失(Loss):通过损失函数度量预测值与真实值的差距。
反向传播(backward):将损失的梯度从输出层逐层传回输入层。
参数更新(step):优化器利用梯度信息更新该层权重。
3. 实践指南:用代码构建一切
构建你自己的模型
下面的 NeuralNet 演示了如何将一个完整的网络封装成单一的 Layer。这种做法类似于 PyTorch 的 nn.Sequential,能让代码结构非常清晰。
from caspian.layers import Layer, Densefrom caspian.activations import Activation, Softmaxfrom caspian.optimizers import Optimizerimport numpy as npclass NeuralNet(Layer): def __init__(self, inputs: int, hiddens: int, outputs: int, activation: Activation, opt: Optimizer): in_size = (inputs,) out_size = (outputs,) super().__init__(in_size, out_size) # 两个全连接层,使用独立的优化器副本 self.x_1 = Dense(activation, inputs, hiddens, optimizer=opt.deepcopy()) self.x_2 = Dense(activation, hiddens, outputs, optimizer=opt.deepcopy()) self.softmax = Softmax() def forward(self, data: np.ndarray, training: bool = False) -> np.ndarray: self.training = training step_1 = self.x_1(data, training) step_2 = self.x_2(step_1, training) return self.softmax(step_2) def backward(self, dx: np.ndarray) -> np.ndarray: assert self.training is True d_sm = self.softmax.backward(dx) d_2 = self.x_2.backward(d_sm) d_1 = self.x_1.backward(d_2) return d_1 def step(self) -> None: self.x_1.step() self.x_2.step()
自定义激活函数
继承 cspn.Activation 并实现 forward 和 backward 方法,就能轻松创建专属的激活函数。下面以 ReLU 为例:
from caspian.activations import Activationimport numpy as npclass ReLU(Activation): def forward(self, data: np.ndarray) -> np.ndarray: return np.maximum(0, data) def backward(self, data: np.ndarray) -> np.ndarray: return (data >= 0) * 1
自定义池化函数
池化函数的自定义流程与激活函数类似,但它返回的数组尺寸会相应减小:
from caspian.pooling import PoolFuncimport numpy as npclass Average(PoolFunc): def forward(self, partition: np.ndarray) -> np.ndarray: return np.average(partition) def backward(self, partition: np.ndarray) -> np.ndarray: return partition * (1.0 / partition.shape[self.axis])
自定义损失函数
损失函数不附属任何层,因此可以直接设计成静态方法:
from caspian.losses import Lossimport numpy as npclass CrossEntropy(Loss): @staticmethod def forward(actual: np.ndarray, prediction: np.ndarray) -> float: clip_pred = np.clip(prediction, 1e-10, 1 - 1e-10) return -np.sum(actual * np.log(clip_pred)) @staticmethod def backward(actual: np.ndarray, prediction: np.ndarray) -> np.ndarray: return prediction - actual
自定义优化器与调度器
优化器负责梯度处理,调度器控制学习率。下面是动量优化器和常量学习率调度器的实现:
# 优化器 examplefrom caspian.optimizers import Optimizerfrom caspian.schedulers import Schedulerimport numpy as npclass Momentum(Optimizer): def __init__(self, momentum: float = 0.9, learn_rate: float = 0.01, sched: Scheduler) -> None: super().__init__(learn_rate, sched) self.momentum = momentum self.previous = 0.0 def process_grad(self, grad: np.ndarray) -> np.ndarray: learn_rate = self.scheduler(self.learn_rate) velocity_grad = self.momentum * self.previous - learn_rate * grad self.previous = velocity_grad return velocity_grad def step(self) -> None: self.scheduler.step() def reset_grad(self) -> None: self.previous = 0.0 self.scheduler.reset() def deepcopy(self) -> 'Momentum': return Momentum(self.momentum, self.learn_rate, self.scheduler.deepcopy())# 调度器 exampleclass ConstantLR(Scheduler): def __init__(self, steps: int, const: float = 0.1) -> None: self.steps = steps self.const = const self.epoch = 0 def __call__(self, init_rate: float) -> float: return init_rate * self.const if self.epoch < self.steps else init_rate def step(self) -> None: self.epoch += 1 def reset(self) -> None: self.epoch = 0 def deepcopy(self) -> 'ConstantLR': return ConstantLR(self.steps, self.const)
4. 进阶体验:上手 MNIST 项目
下面这个完整的例子,展示了如何使用 Caspian 的卷积、全连接等工具,在 MNIST 数据集上训练一个数字识别网络:
import numpy as npfrom caspian.layers import Conv2D, Pooling2D, Reshape, Dense, Container, Sequencefrom caspian.activations import Sigmoid, ReLU, Softmaxfrom caspian.pooling import Maximumfrom caspian.losses import BinCrossEntropyfrom caspian.optimizers import StandardGDfrom keras.datasets import mnist# 1. 数据准备(xtrain, ytrain), (xtest, ytest) = mnist.load_data()x_train = np.array(xtrain).reshape(xtrain.shape[0], 784)x_test = np.array(xtest).reshape(xtest.shape[0], 784)y_train = np.zeros((ytrain.shape[0], ytrain.max()+1), dtype=np.float32)for i in range(len(y_train)): y_train[i][int(ytrain[i])] = 1xt = x_train.reshape(-1, 60, 1, 28, 28) / 255.0yt = y_train.reshape(-1, 60, 10)# 2. 构建模型optim = StandardGD(0.001)d1 = Conv2D(Sigmoid(), 32, 3, (1, 28, 28))d2 = Pooling2D(Maximum(), 2, (32, 26, 26), 2)d3 = Conv2D(Sigmoid(), 12, 3, (32, 13, 13))d4 = Pooling2D(Maximum(), 2, (12, 11, 11), 2)d5 = Reshape((-1, 12, 5, 5), (-1, 12*5*5))d6 = Dense(ReLU(), 12*5*5, 100)d7 = Dense(Sigmoid(), 100, 10)d8 = Container(Softmax())Seq1 = Sequence([d1, d2, d3, d4, d5, d6, d7])Seq1.set_optimizer(optim)ent = BinCrossEntropy()# 3. 训练循环losses = 0.0for ep in range(50): for x, y in zip(xt, yt): x_r = Seq1.forward(x, True) err_grad = ent.backward(y, x_r) loss = ent.forward(y, x_r) Seq1.backward(err_grad) Seq1.step() losses += loss print(f"Epoch {ep+1} - {losses / xt.shape[0]}") losses = 0.0
5. 总结与展望
Caspian 是一款追求纯粹和透明的深度学习库,非常适合教学、研究和轻量级实验。它通过清晰的基类分层和灵活的组件组合,让开发者能轻松触及神经网络的底层细节。未来它计划添加 Transformer 层、更多激活函数和优化器,潜力值得期待。
如果想要一个既能快速上手,又能深入理解原理的深度学习环境,不妨试试 Caspian,享受“知其然更知其所以然”的探索乐趣。
编辑:余文彬
审校:余洁洁