作业实战:实现浅层神经网络
1. 浅层神经网络的基本概念
1.1 什么是浅层神经网络
浅层神经网络(Shallow Neural Network)是指具有一个隐藏层的神经网络模型。相比于单神经元网络,浅层神经网络具有更强的表达能力,可以解决非线性可分的问题。
1.2 浅层神经网络的结构
浅层神经网络的结构包括:
- 输入层:接收输入特征
- 隐藏层:提取输入数据的特征表示
- 输出层:产生最终输出
- 权重和偏置:连接不同层的参数
- 激活函数:对线性组合结果进行非线性变换
1.3 浅层神经网络的数学表达式
对于一个具有一个隐藏层的浅层神经网络,数学表达式为:
# 隐藏层
z1 = W1*x + b1
a1 = f(z1)
# 输出层
z2 = W2*a1 + b2
y = g(z2)其中,W1和b1是输入层到隐藏层的权重和偏置,W2和b2是隐藏层到输出层的权重和偏置,f和g是激活函数。
2. 浅层神经网络的实现步骤
2.1 数据准备
首先,我们需要准备用于训练和测试的数据。
2.2 初始化参数
初始化网络的权重和偏置。
2.3 前向传播
计算网络的前向传播过程,得到预测输出。
2.4 计算损失
计算预测输出与真实标签之间的损失。
2.5 反向传播
计算损失函数对权重和偏置的梯度。
2.6 参数更新
使用梯度下降法更新权重和偏置。
2.7 模型评估
在测试集上评估模型的性能。
3. 实战案例:实现浅层神经网络解决非线性分类问题
3.1 问题描述
我们将实现一个浅层神经网络,用于解决非线性可分的二分类问题。具体来说,我们将创建一个非线性可分的数据集,然后训练浅层神经网络对数据进行分类。
3.2 数据准备
首先,我们创建一个非线性可分的二分类数据集:
import numpy as np
import matplotlib.pyplot as plt
# 设置随机种子,确保结果可复现
np.random.seed(42)
# 创建非线性可分的数据集
def create_nonlinear_dataset(n_samples=200):
# 创建两类数据
class0 = []
class1 = []
# 创建环形数据集
for i in range(n_samples):
# 为Class 0创建内部点
r = np.random.uniform(0, 1.5)
theta = np.random.uniform(0, 2*np.pi)
x = r * np.cos(theta)
y = r * np.sin(theta)
class0.append([x, y])
# 为Class 1创建外部点
r = np.random.uniform(2, 3.5)
theta = np.random.uniform(0, 2*np.pi)
x = r * np.cos(theta)
y = r * np.sin(theta)
class1.append([x, y])
# 转换为numpy数组
class0 = np.array(class0)
class1 = np.array(class1)
# 创建标签
labels0 = np.zeros((n_samples, 1))
labels1 = np.ones((n_samples, 1))
# 合并数据和标签
X = np.vstack((class0, class1))
y = np.vstack((labels0, labels1))
# 打乱数据
indices = np.random.permutation(2*n_samples)
X = X[indices]
y = y[indices]
return X, y
# 创建数据集
X, y = create_nonlinear_dataset()
# 可视化数据集
plt.scatter(X[y.flatten() == 0][:, 0], X[y.flatten() == 0][:, 1], color='blue', label='Class 0')
plt.scatter(X[y.flatten() == 1][:, 0], X[y.flatten() == 1][:, 1], color='red', label='Class 1')
plt.title('Nonlinear Classification Dataset')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
plt.show()3.3 浅层神经网络的实现
现在,我们实现一个浅层神经网络:
class ShallowNeuralNetwork:
def __init__(self, input_size, hidden_size, output_size):
"""
初始化浅层神经网络
参数:
input_size: 输入特征的维度
hidden_size: 隐藏层神经元的数量
output_size: 输出层神经元的数量
"""
# 初始化权重和偏置
self.W1 = np.random.randn(input_size, hidden_size) * 0.01
self.b1 = np.zeros((1, hidden_size))
self.W2 = np.random.randn(hidden_size, output_size) * 0.01
self.b2 = np.zeros((1, output_size))
def sigmoid(self, z):
"""
sigmoid激活函数
参数:
z: 线性组合结果
返回:
激活后的值
"""
return 1 / (1 + np.exp(-z))
def sigmoid_derivative(self, z):
"""
sigmoid激活函数的导数
参数:
z: 线性组合结果
返回:
sigmoid的导数
"""
return self.sigmoid(z) * (1 - self.sigmoid(z))
def relu(self, z):
"""
ReLU激活函数
参数:
z: 线性组合结果
返回:
激活后的值
"""
return np.maximum(0, z)
def relu_derivative(self, z):
"""
ReLU激活函数的导数
参数:
z: 线性组合结果
返回:
ReLU的导数
"""
return np.where(z > 0, 1, 0)
def forward(self, X, activation='relu'):
"""
前向传播
参数:
X: 输入数据,形状为(m, n),其中m是样本数,n是特征数
activation: 隐藏层的激活函数,可选值为'relu'或'sigmoid'
返回:
预测输出
"""
# 计算隐藏层的线性组合
self.Z1 = np.dot(X, self.W1) + self.b1
# 应用隐藏层的激活函数
if activation == 'relu':
self.A1 = self.relu(self.Z1)
elif activation == 'sigmoid':
self.A1 = self.sigmoid(self.Z1)
else:
raise ValueError("激活函数必须是'relu'或'sigmoid'")
# 计算输出层的线性组合
self.Z2 = np.dot(self.A1, self.W2) + self.b2
# 应用输出层的激活函数(sigmoid用于二分类)
self.A2 = self.sigmoid(self.Z2)
return self.A2
def compute_loss(self, y, a):
"""
计算损失
参数:
y: 真实标签
a: 预测输出
返回:
损失值
"""
m = y.shape[0]
# 计算交叉熵损失
loss = -np.mean(y * np.log(a) + (1 - y) * np.log(1 - a))
return loss
def backward(self, X, y, a, activation='relu'):
"""
反向传播
参数:
X: 输入数据
y: 真实标签
a: 预测输出
activation: 隐藏层的激活函数
返回:
权重和偏置的梯度
"""
m = X.shape[0]
# 计算输出层的梯度
dZ2 = a - y
# 计算输出层权重的梯度
dW2 = (1/m) * np.dot(self.A1.T, dZ2)
# 计算输出层偏置的梯度
db2 = (1/m) * np.sum(dZ2, axis=0, keepdims=True)
# 计算隐藏层的梯度
dA1 = np.dot(dZ2, self.W2.T)
if activation == 'relu':
dZ1 = dA1 * self.relu_derivative(self.Z1)
elif activation == 'sigmoid':
dZ1 = dA1 * self.sigmoid_derivative(self.Z1)
else:
raise ValueError("激活函数必须是'relu'或'sigmoid'")
# 计算隐藏层权重的梯度
dW1 = (1/m) * np.dot(X.T, dZ1)
# 计算隐藏层偏置的梯度
db1 = (1/m) * np.sum(dZ1, axis=0, keepdims=True)
return dW1, db1, dW2, db2
def update_parameters(self, dW1, db1, dW2, db2, learning_rate):
"""
更新参数
参数:
dW1: 隐藏层权重的梯度
db1: 隐藏层偏置的梯度
dW2: 输出层权重的梯度
db2: 输出层偏置的梯度
learning_rate: 学习率
"""
self.W1 -= learning_rate * dW1
self.b1 -= learning_rate * db1
self.W2 -= learning_rate * dW2
self.b2 -= learning_rate * db2
def train(self, X, y, epochs=10000, learning_rate=0.01, activation='relu', print_interval=1000):
"""
训练模型
参数:
X: 输入数据
y: 真实标签
epochs: 训练轮数
learning_rate: 学习率
activation: 隐藏层的激活函数
print_interval: 打印间隔
"""
losses = []
for epoch in range(epochs):
# 前向传播
a = self.forward(X, activation)
# 计算损失
loss = self.compute_loss(y, a)
losses.append(loss)
# 反向传播
dW1, db1, dW2, db2 = self.backward(X, y, a, activation)
# 更新参数
self.update_parameters(dW1, db1, dW2, db2, learning_rate)
# 打印损失
if epoch % print_interval == 0:
print(f'Epoch {epoch}, Loss: {loss:.4f}')
return losses
def predict(self, X, activation='relu', threshold=0.5):
"""
预测
参数:
X: 输入数据
activation: 隐藏层的激活函数
threshold: 阈值
返回:
预测标签
"""
a = self.forward(X, activation)
return (a > threshold).astype(int)
def evaluate(self, X, y, activation='relu'):
"""
评估模型
参数:
X: 输入数据
y: 真实标签
activation: 隐藏层的激活函数
返回:
准确率
"""
predictions = self.predict(X, activation)
accuracy = np.mean(predictions == y)
return accuracy3.4 训练浅层神经网络
现在,我们使用准备好的数据训练浅层神经网络:
# 创建浅层神经网络
nn = ShallowNeuralNetwork(input_size=2, hidden_size=4, output_size=1)
# 训练模型
losses = nn.train(X, y, epochs=20000, learning_rate=0.01, activation='relu', print_interval=2000)
# 可视化损失曲线
plt.plot(losses)
plt.title('Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show()
# 评估模型
accuracy = nn.evaluate(X, y, activation='relu')
print(f'Training Accuracy: {accuracy:.4f}')3.5 可视化决策边界
我们可以可视化浅层神经网络的决策边界:
# 可视化决策边界
def plot_decision_boundary(model, X, y, activation='relu'):
# 设置网格范围
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
# 创建网格
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 100),
np.linspace(y_min, y_max, 100))
# 预测网格点的标签
grid = np.c_[xx.ravel(), yy.ravel()]
predictions = model.predict(grid, activation)
predictions = predictions.reshape(xx.shape)
# 绘制决策边界
plt.contourf(xx, yy, predictions, alpha=0.8)
# 绘制数据点
plt.scatter(X[y.flatten() == 0][:, 0], X[y.flatten() == 0][:, 1], color='blue', label='Class 0')
plt.scatter(X[y.flatten() == 1][:, 0], X[y.flatten() == 1][:, 1], color='red', label='Class 1')
plt.title('Decision Boundary')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
plt.show()
# 绘制决策边界
plot_decision_boundary(nn, X, y, activation='relu')3.6 分析训练结果
通过训练浅层神经网络,我们可以:
- 观察损失函数的下降过程
- 了解模型的收敛情况
- 可视化决策边界,理解模型的学习能力
- 评估模型的准确率
- 体会浅层神经网络解决非线性问题的能力
4. 浅层神经网络的超参数调优
4.1 隐藏层神经元数量的选择
隐藏层神经元数量的选择会影响模型的性能:
- 过少的神经元:模型可能无法捕捉数据中的复杂模式
- 过多的神经元:模型可能会过拟合训练数据
4.2 学习率的选择
学习率的选择会影响模型的训练速度和收敛性:
- 过小的学习率:模型训练速度慢,可能陷入局部最优
- 过大的学习率:模型可能无法收敛
4.3 训练轮数的选择
训练轮数的选择会影响模型的性能:
- 过少的训练轮数:模型可能未充分学习数据中的模式
- 过多的训练轮数:模型可能会过拟合训练数据
4.4 超参数调优实验
我们可以通过实验来找到最佳的超参数组合:
# 超参数调优
def tune_hyperparameters(X, y):
# 定义超参数组合
hidden_sizes = [2, 4, 8, 16]
learning_rates = [0.001, 0.01, 0.1]
activations = ['relu', 'sigmoid']
best_accuracy = 0
best_params = {}
for hidden_size in hidden_sizes:
for learning_rate in learning_rates:
for activation in activations:
print(f'\n测试超参数: hidden_size={hidden_size}, learning_rate={learning_rate}, activation={activation}')
# 创建并训练模型
nn = ShallowNeuralNetwork(input_size=2, hidden_size=hidden_size, output_size=1)
nn.train(X, y, epochs=10000, learning_rate=learning_rate, activation=activation, print_interval=5000)
# 评估模型
accuracy = nn.evaluate(X, y, activation=activation)
print(f'准确率: {accuracy:.4f}')
# 更新最佳参数
if accuracy > best_accuracy:
best_accuracy = accuracy
best_params = {
'hidden_size': hidden_size,
'learning_rate': learning_rate,
'activation': activation
}
print(f'\n最佳超参数: {best_params}')
print(f'最佳准确率: {best_accuracy:.4f}')
return best_params
# 运行超参数调优
best_params = tune_hyperparameters(X, y)
# 使用最佳超参数训练模型
best_nn = ShallowNeuralNetwork(
input_size=2,
hidden_size=best_params['hidden_size'],
output_size=1
)
best_nn.train(
X, y,
epochs=20000,
learning_rate=best_params['learning_rate'],
activation=best_params['activation'],
print_interval=2000
)
# 评估模型
best_accuracy = best_nn.evaluate(X, y, activation=best_params['activation'])
print(f'最佳模型准确率: {best_accuracy:.4f}')
# 可视化决策边界
plot_decision_boundary(best_nn, X, y, activation=best_params['activation'])5. 扩展:使用不同的损失函数
5.1 常见的损失函数
除了交叉熵损失函数外,常见的损失函数还包括:
- 均方误差损失(MSE):适用于回归问题
- 平均绝对误差损失(MAE):适用于回归问题
- 铰链损失:适用于支持向量机
5.2 实现均方误差损失函数
def compute_mse_loss(self, y, a):
"""
计算均方误差损失
参数:
y: 真实标签
a: 预测输出
返回:
损失值
"""
m = y.shape[0]
loss = (1/(2*m)) * np.sum((a - y)**2)
return loss
# 添加compute_mse_loss方法到ShallowNeuralNetwork类
ShallowNeuralNetwork.compute_mse_loss = compute_mse_loss
# 修改train方法,支持不同的损失函数
def train_with_loss(self, X, y, epochs=10000, learning_rate=0.01, activation='relu', loss_function='cross_entropy', print_interval=1000):
"""
训练模型(支持不同的损失函数)
参数:
X: 输入数据
y: 真实标签
epochs: 训练轮数
learning_rate: 学习率
activation: 隐藏层的激活函数
loss_function: 损失函数,可选值为'cross_entropy'或'mse'
print_interval: 打印间隔
"""
losses = []
for epoch in range(epochs):
# 前向传播
a = self.forward(X, activation)
# 计算损失
if loss_function == 'cross_entropy':
loss = self.compute_loss(y, a)
elif loss_function == 'mse':
loss = self.compute_mse_loss(y, a)
else:
raise ValueError("损失函数必须是'cross_entropy'或'mse'")
losses.append(loss)
# 反向传播
dW1, db1, dW2, db2 = self.backward(X, y, a, activation)
# 更新参数
self.update_parameters(dW1, db1, dW2, db2, learning_rate)
# 打印损失
if epoch % print_interval == 0:
print(f'Epoch {epoch}, Loss: {loss:.4f}')
return losses
# 添加train_with_loss方法到ShallowNeuralNetwork类
ShallowNeuralNetwork.train_with_loss = train_with_loss
# 使用均方误差损失函数训练模型
nn_mse = ShallowNeuralNetwork(input_size=2, hidden_size=4, output_size=1)
losses_mse = nn_mse.train_with_loss(X, y, epochs=20000, learning_rate=0.01, activation='relu', loss_function='mse', print_interval=2000)
# 可视化损失曲线
plt.plot(losses, label='Cross Entropy')
plt.plot(losses_mse, label='MSE')
plt.title('Training Loss Comparison')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()
# 评估模型
accuracy_mse = nn_mse.evaluate(X, y, activation='relu')
print(f'Training Accuracy (MSE): {accuracy_mse:.4f}')
# 可视化决策边界
plot_decision_boundary(nn_mse, X, y, activation='relu')6. 扩展:使用不同的优化算法
6.1 小批量梯度下降法
小批量梯度下降法是一种常用的优化算法,它每次使用一小批样本进行更新,可以提高训练速度和稳定性。
def create_minibatches(X, y, batch_size):
"""
创建小批量数据
参数:
X: 输入数据
y: 真实标签
batch_size: 批量大小
返回:
小批量数据的列表
"""
m = X.shape[0]
minibatches = []
# 打乱数据
permutation = np.random.permutation(m)
X_shuffled = X[permutation]
y_shuffled = y[permutation]
# 创建小批量
for i in range(0, m, batch_size):
end = i + batch_size
if end > m:
end = m
minibatch_X = X_shuffled[i:end]
minibatch_y = y_shuffled[i:end]
minibatches.append((minibatch_X, minibatch_y))
return minibatches
# 实现小批量梯度下降法
def train_minibatch(self, X, y, epochs=10000, batch_size=32, learning_rate=0.01, activation='relu', print_interval=1000):
"""
使用小批量梯度下降法训练模型
参数:
X: 输入数据
y: 真实标签
epochs: 训练轮数
batch_size: 批量大小
learning_rate: 学习率
activation: 隐藏层的激活函数
print_interval: 打印间隔
"""
losses = []
m = X.shape[0]
for epoch in range(epochs):
epoch_loss = 0
# 创建小批量
minibatches = create_minibatches(X, y, batch_size)
for minibatch_X, minibatch_y in minibatches:
# 前向传播
a = self.forward(minibatch_X, activation)
# 计算损失
loss = self.compute_loss(minibatch_y, a)
epoch_loss += loss * len(minibatch_X) / m
# 反向传播
dW1, db1, dW2, db2 = self.backward(minibatch_X, minibatch_y, a, activation)
# 更新参数
self.update_parameters(dW1, db1, dW2, db2, learning_rate)
losses.append(epoch_loss)
# 打印损失
if epoch % print_interval == 0:
print(f'Epoch {epoch}, Loss: {epoch_loss:.4f}')
return losses
# 添加train_minibatch方法到ShallowNeuralNetwork类
ShallowNeuralNetwork.train_minibatch = train_minibatch
# 使用小批量梯度下降法训练模型
nn_minibatch = ShallowNeuralNetwork(input_size=2, hidden_size=4, output_size=1)
losses_minibatch = nn_minibatch.train_minibatch(X, y, epochs=5000, batch_size=32, learning_rate=0.01, activation='relu', print_interval=500)
# 可视化损失曲线
plt.plot(losses, label='Full Batch')
plt.plot(losses_minibatch, label='Mini-batch')
plt.title('Training Loss Comparison')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()
# 评估模型
accuracy_minibatch = nn_minibatch.evaluate(X, y, activation='relu')
print(f'Training Accuracy (Mini-batch): {accuracy_minibatch:.4f}')
# 可视化决策边界
plot_decision_boundary(nn_minibatch, X, y, activation='relu')7. 总结与展望
7.1 主要内容总结
本教程介绍了如何实现一个浅层神经网络,包括:
- 浅层神经网络的基本概念和结构
- 浅层神经网络的实现步骤
- 实战案例:实现浅层神经网络解决非线性分类问题
- 浅层神经网络的超参数调优
- 扩展:使用不同的损失函数
- 扩展:使用不同的优化算法
7.2 未来学习方向
通过本教程的学习,读者可以:
- 掌握浅层神经网络的基本实现方法
- 理解前向传播和反向传播的原理
- 了解不同激活函数、损失函数和优化算法的效果
- 学习超参数调优的方法
- 为实现更复杂的神经网络模型打下基础
7.3 实践建议
在实践中,读者可以:
- 尝试使用不同的数据集训练浅层神经网络
- 调整网络结构,如增加隐藏层的数量,构建深层神经网络
- 尝试实现其他类型的激活函数和损失函数
- 探索更高级的优化算法,如动量梯度下降法和Adam优化算法
- 学习如何使用深度学习框架(如TensorFlow和PyTorch)实现神经网络
通过不断的实践和探索,读者将能够更深入地理解神经网络的工作原理,为后续的深度学习学习打下坚实的基础。