CS231n实践项目:图像分类作业深度解析
本文深度解析了CS231n课程中的三个核心实践项目:Assignment 1涵盖了kNN、SVM和神经网络的实现,重点介绍了向量化编程和超参数调优;Assignment 2深入探讨了卷积网络、批归一化和Dropout等现代深度学习技术;Assignment 3则扩展到RNN、Transformer和GAN在视觉语言任务和生成模型中的应用。
Assignment 1:kNN、SVM和神经网络实现
CS231n课程的第一个实践项目Assignment 1是深度学习和计算机视觉的入门基石,它系统性地引导学习者从最简单的k近邻算法逐步过渡到支持向量机和神经网络。这个作业不仅教授算法原理,更重要的是培养向量化编程思维和超参数调优能力。
k近邻分类器实现
k近邻(k-Nearest Neighbor, kNN)算法是Assignment 1的第一个任务,它采用基于实例的学习方法,通过计算测试样本与训练样本之间的距离来进行分类。
距离度量函数实现:
def compute_distances_two_loops(X, X_train):
"""
使用双重循环计算L2距离(欧几里得距离)
"""
num_test = X.shape[0]
num_train = X_train.shape[0]
dists = np.zeros((num_test, num_train))
for i in range(num_test):
for j in range(num_train):
# 计算L2距离的平方
dists[i, j] = np.sqrt(np.sum((X[i] - X_train[j]) ** 2))
return dists
def compute_distances_one_loop(X, X_train):
"""
使用单循环向量化实现
"""
num_test = X.shape[0]
num_train = X_train.shape[0]
dists = np.zeros((num_test, num_train))
for i in range(num_test):
# 向量化计算第i个测试样本与所有训练样本的距离
dists[i, :] = np.sqrt(np.sum((X[i] - X_train) ** 2, axis=1))
return dists
def compute_distances_no_loops(X, X_train):
"""
完全向量化的实现,使用广播机制
"""
# 使用公式: (a - b)^2 = a^2 + b^2 - 2ab
test_sum = np.sum(X**2, axis=1, keepdims=True)
train_sum = np.sum(X_train**2, axis=1)
inner_product = np.dot(X, X_train.T)
dists = np.sqrt(test_sum + train_sum - 2 * inner_product)
return dists
kNN分类器核心算法:
class KNearestNeighbor:
def __init__(self):
self.X_train = None
self.y_train = None
def train(self, X, y):
self.X_train = X
self.y_train = y
def predict(self, X, k=1, num_loops=0):
if num_loops == 0:
dists = self.compute_distances_no_loops(X)
elif num_loops == 1:
dists = self.compute_distances_one_loop(X)
else:
dists = self.compute_distances_two_loops(X)
return self.predict_labels(dists, k=k)
def predict_labels(self, dists, k=1):
num_test = dists.shape[0]
y_pred = np.zeros(num_test)
for i in range(num_test):
# 获取最近的k个邻居的索引
closest_y = self.y_train[np.argsort(dists[i])[:k]]
# 投票决定类别
y_pred[i] = np.argmax(np.bincount(closest_y))
return y_pred
超参数调优流程:
支持向量机实现
支持向量机(Support Vector Machine, SVM)是Assignment 1的第二个核心任务,它引入了参数化模型和损失函数的概念。
多类SVM损失函数:
SVM的铰链损失(Hinge Loss)函数数学表达式为:
$$ L_i = \sum_{j\neq y_i} \max(0, s_j - s_{y_i} + \Delta) $$
其中 $\Delta$ 是边界参数,通常设置为1.0。
def svm_loss_naive(W, X, y, reg):
"""
非向量化的SVM损失函数实现
"""
dW = np.zeros(W.shape) # 梯度初始化
num_classes = W.shape[0]
num_train = X.shape[1]
loss = 0.0
for i in range(num_train):
scores = W.dot(X[:, i])
correct_class_score = scores[y[i]]
for j in range(num_classes):
if j == y[i]:
continue
margin = scores[j] - correct_class_score + 1 # delta = 1
if margin > 0:
loss += margin
dW[j, :] += X[:, i] # 错误类别的梯度
dW[y[i], :] -= X[:, i] # 正确类别的梯度
# 平均损失
loss /= num_train
dW /= num_train
# 添加L2正则化
loss += 0.5 * reg * np.sum(W * W)
dW += reg * W
return loss, dW
def svm_loss_vectorized(W, X, y, reg):
"""
向量化的SVM损失函数实现
"""
num_train = X.shape[1]
scores = W.dot(X)
# 获取正确类别的分数
correct_class_scores = scores[y, np.arange(num_train)]
# 计算边界
margins = np.maximum(0, scores - correct_class_scores + 1)
margins[y, np.arange(num_train)] = 0 # 忽略正确类别
loss = np.sum(margins) / num_train
loss += 0.5 * reg * np.sum(W * W)
# 计算梯度
binary = margins
binary[margins > 0] = 1
col_sum = np.sum(binary, axis=0)
binary[y, np.arange(num_train)] = -col_sum
dW = binary.dot(X.T) / num_train
dW += reg * W
return loss, dW
SVM训练优化过程:
两层神经网络实现
Assignment 1的第四个任务实现了完整的双层神经网络,这是深度学习的基础架构。
神经网络前向传播:
class TwoLayerNet:
def __init__(self, input_size, hidden_size, output_size, std=1e-4):
self.params = {}
self.params['W1'] = std * np.random.randn(input_size, hidden_size)
self.params['b1'] = np.zeros(hidden_size)
self.params['W2'] = std * np.random.randn(hidden_size, output_size)
self.params['b2'] = np.zeros(output_size)
def loss(self, X, y=None, reg=0.0):
# 前向传播
W1, b1 = self.params['W1'], self.params['b1']
W2, b2 = self.params['W2'], self.params['b2']
N, D = X.shape
# 第一层: ReLU激活
hidden_layer = np.maximum(0, np.dot(X, W1) + b1)
scores = np.dot(hidden_layer, W2) + b2
if y is None:
return scores
# 计算损失
scores_shift = scores - np.max(scores, axis=1, keepdims=True)
exp_scores = np.exp(scores_shift)
probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
correct_logprobs = -np.log(probs[np.arange(N), y])
data_loss = np.sum(correct_logprobs) / N
reg_loss = 0.5 * reg * (np.sum(W1*W1) + np.sum(W2*W2))
loss = data_loss + reg_loss
# 反向传播
grads = {}
dscores = probs.copy()
dscores[np.arange(N), y] -= 1
dscores /= N
grads['W2'] = np.dot(hidden_layer.T, dscores) + reg * W2
grads['b2'] = np.sum(dscores, axis=0)
dhidden = np.dot(dscores, W2.T)
dhidden[hidden_layer <= 0] = 0 # ReLU梯度
grads['W1'] = np.dot(X.T, dhidden) + reg * W1
grads['b1'] = np.sum(dhidden, axis=0)
return loss, grads
神经网络训练超参数优化:
| 超参数 | 典型值范围 | 影响说明 |
|---|---|---|
| 隐藏层大小 | 50-500 | 模型容量和表达能力 |
| 学习率 | 1e-4 to 1e-1 | 梯度下降步长 |
| 正则化强度 | 1e-5 to 1e-1 | 控制过拟合程度 |
| 训练迭代次数 | 1000-10000 | 收敛性和训练时间 |
神经网络架构可视化:
性能对比与分析
三种算法在CIFAR-10数据集上的性能表现对比如下:
| 算法 | 验证集准确率 | 测试集准确率 | 训练时间 | 预测速度 |
|---|---|---|---|---|
| kNN | 27-34% | 25-32% | 快 | 慢 |
| 线性SVM | 35-39% | 33-37% | 中等 | 快 |
| 两层神经网络 | 48-52% | 46-50% | 慢 | 快 |
关键学习要点:
- 向量化编程:从双重循环到完全向量化的实现,性能提升数百倍
- 超参数调优:通过交叉验证寻找最佳超参数组合
- 正则化技术:L2正则化有效防止过拟合
- 梯度检查:确保反向传播实现的正确性
- 学习率调度:动态调整学习率加速收敛
通过Assignment 1的实践,学习者不仅掌握了三种基础分类算法的实现,更重要的是建立了深度学习系统开发的完整思维框架,为后续的卷积神经网络和现代深度学习架构奠定了坚实基础。
Assignment 2:卷积网络与批归一化实践
在CS231n的第二个作业中,我们将深入探索现代深度学习的核心组件:卷积神经网络(CNN)和批归一化技术。这个作业不仅要求你理解这些概念的理论基础,更重要的是通过实践编码来掌握它们的实现细节。
卷积神经网络架构解析
卷积神经网络是专门为处理图像数据而设计的深度学习架构。与传统的全连接网络不同,CNN通过局部连接和参数共享机制大幅减少了模型参数数量,同时保持了强大的特征提取能力。
卷积层核心实现
卷积层的数学本质是在输入数据上滑动滤波器进行局部特征提取。每个滤波器在整个输入空间上共享参数,这种设计既减少了参数量,又增强了模型的平移不变性。
import numpy as np
def conv_forward_naive(x, w, b, conv_param):
"""
卷积层前向传播的简单实现
"""
N, C, H, W = x.shape
F, _, HH, WW = w.shape
stride, pad = conv_param['stride'], conv_param['pad']
# 计算输出尺寸
H_out = 1 + (H + 2 * pad - HH) // stride
W_out = 1 + (W + 2 * pad - WW) // stride
out = np.zeros((N, F, H_out, W_out))
# 对输入进行零填充
x_padded = np.pad(x, ((0,0), (0,0), (pad,pad), (pad,pad)), mode='constant')
for n in range(N): # 遍历每个样本
for f in range(F): # 遍历每个滤波器
for i in range(H_out):
for j in range(W_out):
# 计算感受野区域
h_start = i * stride
h_end = h_start + HH
w_start = j * stride
w_end = w_start + WW
# 执行卷积操作
region = x_padded[n, :, h_start:h_end, w_start:w_end]
out[n, f, i, j] = np.sum(region * w[f]) + b[f]
cache = (x, w, b, conv_param)
return out, cache
卷积参数计算表
| 参数 | 描述 | 典型值 | 计算公式 |
|---|---|---|---|
| 输入尺寸 (W₁ × H₁ × D₁) | 输入特征图的维度 | 32×32×3 | - |
| 滤波器数量 (K) | 输出特征图的深度 | 16, 32, 64 | 超参数 |
| 滤波器尺寸 (F) | 感受野大小 | 3×3, 5×5 | 超参数 |
| 步长 (S) | 滑动步幅 | 1, 2 | 超参数 |
| 填充 (P) | 边界零填充 | 1, 2 | 超参数 |
| 输出尺寸 (W₂ × H₂ × D₂) | 输出特征图维度 | 计算得出 | W₂ = (W₁ - F + 2P)/S + 1 |
批归一化技术深度实践
批归一化(Batch Normalization)是深度学习中最重要的技术创新之一,它通过标准化每层的输入分布来加速训练过程并提高模型稳定性。
批归一化算法原理
批归一化在训练时对每个mini-batch进行标准化,在测试时使用运行平均值:
def batchnorm_forward(x, gamma, beta, bn_param):
"""
批归一化前向传播
"""
mode = bn_param['mode']
eps = bn_param.get('eps', 1e-5)
momentum = bn_param.get('momentum', 0.9)
N, D = x.shape
running_mean = bn_param.get('running_mean', np.zeros(D, dtype=x.dtype))
running_var = bn_param.get('running_var', np.zeros(D, dtype=x.dtype))
if mode == 'train':
# 计算当前batch的均值和方差
sample_mean = np.mean(x, axis=0)
sample_var = np.var(x, axis=0)
# 更新运行平均值
running_mean = momentum * running_mean + (1 - momentum) * sample_mean
running_var = momentum * running_var + (1 - momentum) * sample_var
# 标准化
x_hat = (x - sample_mean) / np.sqrt(sample_var + eps)
out = gamma * x_hat + beta
cache = (x, gamma, beta, x_hat, sample_mean, sample_var, eps)
elif mode == 'test':
# 测试时使用运行平均值
x_hat = (x - running_mean) / np.sqrt(running_var + eps)
out = gamma * x_hat + beta
cache = None
else:
raise ValueError('Invalid forward batchnorm mode "%s"' % mode)
bn_param['running_mean'] = running_mean
bn_param['running_var'] = running_var
return out, cache
批归一化反向传播
def batchnorm_backward(dout, cache):
"""
批归一化反向传播
"""
x, gamma, beta, x_hat, mu, var, eps = cache
N, D = dout.shape
# 计算梯度
dbeta = np.sum(dout, axis=0)
dgamma = np.sum(dout * x_hat, axis=0)
dx_hat = dout * gamma
dvar = np.sum(dx_hat * (x - mu) * -0.5 * (var + eps
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



