1. 线性表示代码:从数学公式到Python实现
线性表示是机器学习中最基础也最重要的概念之一。简单来说,线性表示就是用一组线性方程来描述输入特征与输出结果之间的关系。在Python中,我们可以用NumPy库高效地实现这些数学运算。
1.1 线性代数基础回顾
线性表示的核心是矩阵运算。假设我们有一个简单的线性模型:y = wx + b,其中:
- w是权重(weight)
- x是输入特征(input feature)
- b是偏置项(bias)
- y是预测输出(predicted output)
在Python中,我们可以这样实现:
python复制import numpy as np
# 定义权重和偏置
w = np.array([2.5, -1.0]) # 两个特征的权重
b = 3.0
# 输入特征
x = np.array([1.0, 2.0])
# 线性计算
y = np.dot(w, x) + b
print(y) # 输出:2.5*1 + (-1.0)*2 + 3 = 3.5
1.2 批量数据处理
实际应用中,我们通常需要处理多个样本。这时矩阵运算的优势就体现出来了:
python复制# 多个样本的输入特征 (3个样本,每个样本2个特征)
X = np.array([[1.0, 2.0],
[0.5, 1.5],
[2.0, 0.5]])
# 批量计算预测值
Y = np.dot(X, w) + b
print(Y) # 输出:[3.5, 3.25, 7.0]
提示:使用NumPy的广播(broadcasting)机制可以高效实现矩阵运算,比循环计算快几个数量级。
1.3 线性表示的局限性
虽然线性模型简单高效,但它只能表示输入特征的线性组合。对于非线性关系,我们需要更复杂的模型,这就是神经网络出现的原因。
2. 神经网络训练原理详解
神经网络通过多层非线性变换,可以学习复杂的特征表示。训练神经网络的核心是反向传播算法,它通过计算损失函数对参数的梯度来更新权重。
2.1 前向传播过程
以一个简单的两层神经网络为例:
python复制def forward(X, W1, b1, W2, b2):
# 第一层计算
Z1 = np.dot(X, W1) + b1
A1 = np.maximum(0, Z1) # ReLU激活函数
# 第二层计算
Z2 = np.dot(A1, W2) + b2
return Z2, A1
2.2 反向传播算法
反向传播通过链式法则计算梯度:
python复制def backward(X, Y, Z2, A1, W2):
m = X.shape[0] # 样本数量
# 计算输出层梯度
dZ2 = (Z2 - Y) / m
dW2 = np.dot(A1.T, dZ2)
db2 = np.sum(dZ2, axis=0)
# 计算隐藏层梯度
dA1 = np.dot(dZ2, W2.T)
dZ1 = dA1 * (A1 > 0) # ReLU的导数
dW1 = np.dot(X.T, dZ1)
db1 = np.sum(dZ1, axis=0)
return dW1, db1, dW2, db2
2.3 参数更新
使用梯度下降更新参数:
python复制def update_parameters(W1, b1, W2, b2, dW1, db1, dW2, db2, learning_rate):
W1 -= learning_rate * dW1
b1 -= learning_rate * db1
W2 -= learning_rate * dW2
b2 -= learning_rate * db2
return W1, b1, W2, b2
注意:学习率(learning_rate)是一个关键超参数,太大可能导致震荡,太小则收敛缓慢。
3. Python实现完整神经网络训练流程
现在我们将上述组件整合成一个完整的训练流程。
3.1 数据准备
python复制from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
# 生成非线性可分数据
X, y = make_moons(n_samples=1000, noise=0.1, random_state=42)
y = y.reshape(-1, 1) # 转换为列向量
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
3.2 网络初始化
python复制def initialize_parameters(input_size, hidden_size, output_size):
# 使用He初始化,适合ReLU激活函数
W1 = np.random.randn(input_size, hidden_size) * np.sqrt(2. / input_size)
b1 = np.zeros((1, hidden_size))
W2 = np.random.randn(hidden_size, output_size) * np.sqrt(2. / hidden_size)
b2 = np.zeros((1, output_size))
return W1, b1, W2, b2
input_size = 2
hidden_size = 4
output_size = 1
W1, b1, W2, b2 = initialize_parameters(input_size, hidden_size, output_size)
3.3 训练循环
python复制def compute_loss(Y_pred, Y_true):
# 均方误差损失
return np.mean((Y_pred - Y_true)**2)
def train(X, Y, W1, b1, W2, b2, learning_rate=0.01, epochs=1000):
for epoch in range(epochs):
# 前向传播
Y_pred, A1 = forward(X, W1, b1, W2, b2)
# 计算损失
loss = compute_loss(Y_pred, Y)
# 反向传播
dW1, db1, dW2, db2 = backward(X, Y, Y_pred, A1, W2)
# 参数更新
W1, b1, W2, b2 = update_parameters(W1, b1, W2, b2, dW1, db1, dW2, db2, learning_rate)
if epoch % 100 == 0:
print(f"Epoch {epoch}, Loss: {loss:.4f}")
return W1, b1, W2, b2
# 训练模型
W1, b1, W2, b2 = train(X_train, y_train, W1, b1, W2, b2)
3.4 模型评估
python复制def predict(X, W1, b1, W2, b2):
Y_pred, _ = forward(X, W1, b1, W2, b2)
return (Y_pred > 0.5).astype(int)
# 测试集评估
y_pred = predict(X_test, W1, b1, W2, b2)
accuracy = np.mean(y_pred == y_test)
print(f"Test Accuracy: {accuracy*100:.2f}%")
4. 高级话题与优化技巧
4.1 激活函数选择
除了ReLU,常用的激活函数还有:
- Sigmoid: 1/(1 + e^-x),适合二分类输出层
- Tanh: (e^x - e^-x)/(e^x + e^-x),输出范围[-1,1]
- LeakyReLU: max(0.01x, x),解决ReLU的"死亡神经元"问题
python复制def sigmoid(x):
return 1 / (1 + np.exp(-x))
def tanh(x):
return np.tanh(x)
def leaky_relu(x, alpha=0.01):
return np.maximum(alpha * x, x)
4.2 优化算法进阶
除了标准梯度下降,还有更先进的优化算法:
- Momentum: 积累之前的梯度方向
- RMSprop: 自适应调整学习率
- Adam: 结合Momentum和RMSprop的优点
python复制# Adam优化器实现示例
def initialize_adam(parameters):
v = {}
s = {}
for key in parameters:
v["d"+key] = np.zeros_like(parameters[key])
s["d"+key] = np.zeros_like(parameters[key])
return v, s
def update_parameters_with_adam(parameters, grads, v, s, t, learning_rate=0.01,
beta1=0.9, beta2=0.999, epsilon=1e-8):
v_corrected = {}
s_corrected = {}
for key in parameters:
# 动量更新
v["d"+key] = beta1 * v["d"+key] + (1 - beta1) * grads["d"+key]
v_corrected["d"+key] = v["d"+key] / (1 - beta1**t)
# RMSprop更新
s["d"+key] = beta2 * s["d"+key] + (1 - beta2) * (grads["d"+key]**2)
s_corrected["d"+key] = s["d"+key] / (1 - beta2**t)
# 参数更新
parameters[key] -= learning_rate * v_corrected["d"+key] / (np.sqrt(s_corrected["d"+key]) + epsilon)
return parameters, v, s
4.3 正则化技术
防止过拟合的常用方法:
- L2正则化:在损失函数中添加权重平方和
- Dropout:训练时随机丢弃部分神经元
- 早停法:验证集性能不再提升时停止训练
python复制# L2正则化示例
def compute_loss_with_regularization(Y_pred, Y_true, parameters, lambda_=0.1):
mse_loss = compute_loss(Y_pred, Y_true)
L2_penalty = 0
for key in parameters:
L2_penalty += np.sum(parameters[key]**2)
return mse_loss + (lambda_/(2*Y_true.shape[0])) * L2_penalty
4.4 批归一化(Batch Normalization)
批归一化可以加速训练并提高模型性能:
python复制def batch_norm_forward(x, gamma, beta, eps=1e-5):
mu = np.mean(x, axis=0)
var = np.var(x, axis=0)
x_hat = (x - mu) / np.sqrt(var + eps)
out = gamma * x_hat + beta
cache = (x, x_hat, mu, var, gamma, beta, eps)
return out, cache
在实际项目中,我通常会先尝试简单的线性模型作为基线,然后逐步增加网络复杂度。调试神经网络时,可视化损失曲线和中间层激活值非常有帮助。例如,使用Matplotlib绘制训练过程中的损失变化:
python复制import matplotlib.pyplot as plt
def plot_loss_history(loss_history):
plt.plot(loss_history)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss History')
plt.show()
另一个实用技巧是在训练初期使用较小的学习率进行"热身"(warm-up),然后再逐渐增大学习率,这可以避免训练初期的不稳定。
