使用Theano实现多层感知机(MLP)的完整指南
前言
多层感知机(Multilayer Perceptron, MLP)是最基础的前馈神经网络模型之一,广泛应用于各类机器学习任务。本文将基于Theano框架,详细讲解如何从零开始构建一个完整的MLP模型,并以MNIST手写数字分类任务为例展示其实现过程。
多层感知机基础
MLP由多个全连接层组成,每层包含以下组件:
- 权重矩阵W:连接当前层与下一层
- 偏置向量b:为下一层提供偏置
- 激活函数:引入非线性变换(如tanh、sigmoid等)
顶层通常使用softmax层进行分类输出。MLP通过反向传播算法进行训练,使用梯度下降优化参数。
Theano框架简介
Theano是一个强大的数值计算库,特别适合实现和优化涉及多维数组的数学表达式。它的主要特点包括:
- 自动求导功能
- GPU加速支持
- 符号表达式优化
- 与NumPy紧密集成
实现细节
1. 隐藏层实现
隐藏层是MLP的核心组件,负责非线性特征变换。我们定义一个HiddenLayer
类:
class HiddenLayer(object):
def __init__(self, rng, input, n_in, n_out, W=None, b=None,
activation=tensor.tanh):
self.input = input
# 权重初始化采用Glorot方法
if W is None:
W_values = numpy.asarray(
rng.uniform(
low=-numpy.sqrt(6. / (n_in + n_out)),
high=numpy.sqrt(6. / (n_in + n_out)),
size=(n_in, n_out)
),
dtype=theano.config.floatX
)
if activation == tensor.nnet.sigmoid:
W_values *= 4 # sigmoid需要更大的初始化范围
W = theano.shared(value=W_values, name='W', borrow=True)
if b is None:
b_values = numpy.zeros((n_out,), dtype=theano.config.floatX)
b = theano.shared(value=b_values, name='b', borrow=True)
self.W = W
self.b = b
# 线性变换+激活函数
lin_output = tensor.dot(input, self.W) + self.b
self.output = lin_output if activation is None else activation(lin_output)
self.params = [self.W, self.b] # 存储可训练参数
关键点说明:
- 采用Glorot初始化方法,根据激活函数类型调整初始化范围
- 支持自定义权重和偏置,便于模型加载
- 输出计算使用符号表达式,便于Theano优化
2. Softmax输出层实现
输出层使用softmax激活函数,实现多分类:
class LogisticRegression(object):
def __init__(self, input, target, n_in, n_out):
self.input = input
self.target = target
self.y = target.flatten() # 展平目标向量
# 初始化参数
self.W = theano.shared(
value=numpy.zeros((n_in, n_out), dtype=theano.config.floatX),
name='W', borrow=True)
self.b = theano.shared(
value=numpy.zeros((n_out,), dtype=theano.config.floatX),
name='b', borrow=True)
# 计算类别概率
self.p_y_given_x = tensor.nnet.softmax(tensor.dot(input, self.W) + self.b)
self.y_pred = tensor.argmax(self.p_y_given_x, axis=1) # 预测类别
self.params = [self.W, self.b]
def negative_log_likelihood(self):
# 计算负对数似然损失
log_prob = tensor.log(self.p_y_given_x)
log_likelihood = log_prob[tensor.arange(self.y.shape[0]), self.y]
return -log_likelihood.mean()
def errors(self):
# 计算错误率
misclass_nb = tensor.neq(self.y_pred, self.y)
return misclass_nb.mean()
3. 完整MLP模型
整合隐藏层和输出层,构建完整MLP:
class MLP(object):
def __init__(self, rng, input, target, n_in, n_hidden, n_out, activation=tensor.tanh):
self.input = input
self.target = target
self.y = target.flatten()
# 构建隐藏层
self.hidden_layers = []
layer_input = input
layer_n_in = n_in
for nh in n_hidden:
hidden_layer = HiddenLayer(
rng=rng, input=layer_input,
n_in=layer_n_in, n_out=nh,
activation=activation)
self.hidden_layers.append(hidden_layer)
layer_input = hidden_layer.output
layer_n_in = nh
# 添加输出层
self.log_reg_layer = LogisticRegression(
input=layer_input,
target=target,
n_in=layer_n_in,
n_out=n_out)
# 收集所有参数
self.params = []
self.weights = []
for i, hl in enumerate(self.hidden_layers):
self.params.extend(hl.params)
self.weights.append(hl.W)
# 为参数命名以便保存
for p in hl.params:
p.name = f'layer{i}.{p.name}'
self.params.extend(self.log_reg_layer.params)
self.weights.append(self.log_reg_layer.W)
# 正则化项
self.L1 = sum(abs(W).sum() for W in self.weights)
self.L2_sqr = sum((W ** 2).sum() for W in self.weights)
def negative_log_likelihood(self):
return self.log_reg_layer.negative_log_likelihood()
def errors(self):
return self.log_reg_layer.errors()
训练过程
1. 梯度计算与参数更新
实现SGD优化器:
def sgd_updates(params_and_grads, learning_rate):
return [(param, param - learning_rate * grad)
for param, grad in params_and_grads]
def get_training_fn(mlp_model, learning_rate, L1_reg=0., L2_reg=0.):
# 计算损失(含正则化)
loss = (mlp_model.negative_log_likelihood() +
L1_reg * mlp_model.L1 +
L2_reg * mlp_model.L2_sqr)
# 计算梯度
grads = theano.grad(loss, wrt=mlp_model.params)
updates = sgd_updates(zip(mlp_model.params, grads), learning_rate)
return theano.function(
inputs=[mlp_model.input, mlp_model.target],
outputs=[],
updates=updates)
2. 测试函数
def get_test_fn(mlp_model):
return theano.function(
[mlp_model.input, mlp_model.target],
mlp_model.errors())
模型训练与评估
训练参数设置
# 超参数
learning_rate = 0.01
L1_reg = 0.00
L2_reg = 0.0001
n_epochs = 1000
batch_size = 600
patience = 10000 # 早停耐心值
# 网络结构
n_hidden = [500] # 单隐藏层,500个神经元
训练循环
# 初始化模型
rng = numpy.random.RandomState(1234)
mlp = MLP(rng=rng, input=x, target=y,
n_in=28*28, n_hidden=n_hidden, n_out=10)
# 编译训练和测试函数
train_model = get_training_fn(mlp, learning_rate, L1_reg, L2_reg)
test_model = get_test_fn(mlp)
# 训练循环
best_validation_loss = numpy.inf
for epoch in range(n_epochs):
# 训练一个epoch
for batch in iterate_minibatches(train_set_x, train_set_y, batch_size):
train_model(batch[0], batch[1])
# 验证集评估
validation_loss = test_model(valid_set_x, valid_set_y)
# 早停判断
if validation_loss < best_validation_loss:
best_validation_loss = validation_loss
patience_counter = 0
else:
patience_counter += 1
if patience_counter >= patience:
break
进阶技巧
-
参数初始化:不同激活函数需要不同的初始化策略
- tanh: 使用Glorot初始化
- ReLU: 使用He初始化
- sigmoid: 需要更大的初始化范围
-
正则化方法:
- L1正则化促进稀疏解
- L2正则化防止过拟合
- Dropout随机失活部分神经元
-
优化算法改进:
- 动量法(Momentum)加速收敛
- 自适应学习率方法(Adam, RMSProp)
- 学习率衰减策略
-
批归一化:加速训练并提高模型稳定性
总结
本文详细介绍了如何使用Theano框架实现一个完整的MLP模型,包括:
- 隐藏层和输出层的设计与实现
- 参数初始化策略
- 损失函数和正则化项的计算
- 训练过程的实现与优化
- 模型评估与早停策略
通过这个实现,读者可以深入理解神经网络的基本原理和实现细节,为进一步研究更复杂的深度学习模型奠定基础。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考