首先实现神经网络中仿射层、ReLU层以及组合单层神经元。
#-*- coding: utf-8 -*-
import numpy as np
def affine_forward(x, w, b):
"""
计算神经网络当前层的前馈传播,该方法计算在全连接情况下的得分函数。
注:如果不理解affine仿射变换,简单的理解为在全连接情况下的得分函数即可。
输入数据x的形状为(N, d_1, ..., d_k),其中N表示数据量,(d_1, ..., d_k)表示
每一通道的数据维度,如果是图片数据就为(长,宽,色道)。数据的总维度为
D = d_1 * ... * d_k,因此我们需要将数据重塑成形状为(N,D)的数组再进行仿射变换。
Inputs:
- x: 输入数据,其形状为(N, d_1, ..., d_k)的numpy数组。
- w: 权重矩阵,其形状为(D,M)的numpy数组,D表示输入数据维度,M表示输出数据维度
可以将D看成输入的神经元个数,M看成输出神经元个数。
- b: 偏置向量,其形状为(M,)的numpy数组。
Returns 元组:
- out: 形状为(N, M)的输出结果。
- cache: 将输入进行缓存(x, w, b)。
"""
out = None
#############################################################################
# 任务: 实现全连接前向传播 #
# 注:首先你需要将输入数据重塑成行。 #
#############################################################################
# 获取样本数
N = x.shape[0]
# 整合数据
x_new = x.reshape(N, -1)
# 计算仿射的得分
out = np.dot(x_new, w)+b
#############################################################################
# 结束编码 #
#############################################################################
cache = (x, w, b)
return out, cache
def affine_backward(dout, cache):
"""
计算仿射层的反向传播.
Inputs:
- dout: 形状为(N, M)的上层梯度
- cache: 元组:
- x: (N, d_1, ... d_k)的输入数据
- w: 形状为(D, M)的权重矩阵
Returns 元组:
- dx: 输入数据x的梯度,其形状为(N, d1, ..., d_k)
- dw: 权重矩阵w的梯度,其形状为(D,M)
- db: 偏置项b的梯度,其形状为(M,)
"""
x, w, b = cache
dx, dw, db = None, None, None
#############################################################################
# 任务: 实现仿射层反向传播 #
# 注意:你需要将x重塑成(N,D)后才能计算各梯度, #
# 求完梯度后你需要将dx的形状与x重塑成一样 #
#############################################################################
# 计算b的梯度
db = np.sum(dout, axis=0)
# 将x重塑成(N, D)
xx = x.reshape(x.shape[0], -1)
dw = np.dot(xx.T, dout)
dx = np.dot(dout, w.T)
# 将dx的形状与x重塑成一样
dx = np.reshape(dx, x.shape)
#############################################################################
# 结束编码 #
#############################################################################
return dx, dw, db
def relu_forward(x):
"""
计算rectified linear units (ReLUs)激活函数的前向传播,并保存相应缓存
Input:
- x: 输入数据
Returns 元组:
- out: 和输入数据x形状相同
- cache: x
"""
out = None
#############################################################################
# 任务: 实现ReLU 的前向传播. #
# 注意:你只需要1行代码即可完成 #
#############################################################################
out = np.maximum(0, x)
#############################################################################
# 结束编码 #
#############################################################################
cache = x
return out, cache
def relu_backward(dout, cache):
"""
计算 rectified linear units (ReLUs)激活函数的反向传播.
Input:
- dout: 上层误差梯度
- cache: 输入 x,其形状应该和dout相同
Returns:
- dx: x的梯度
"""
dx, x = None, cache
#############################################################################
# 任务: 实现 ReLU 反向传播. #
#############################################################################
dx = dout
dx[x<=0] = 0
#############################################################################
# 结束编码 #
#############################################################################
return dx
def affine_relu_forward(x, w, b):
"""
ReLU神经元前向传播
Inputs:
- x: 输入到 affine层的数据
- w, b: affine层的权重矩阵和偏置向量
Returns 元组:
- out: ReLU的输出结果
- cache: 前向传播的缓存
"""
######################################################################
# 任务: 实现 ReLU神经元前向传播. #
# 注意:你需要调用affine_forward以及relu_forward函数, #
# 并将各自的缓存保存在cache中 #
######################################################################
a, fc_cache = affine_forward(x, w, b)
out, relu_cache = relu_forward(a)
cache = (fc_cache, relu_cache)
######################################################################
# 结束编码 #
######################################################################
return out, cache
def affine_relu_backward(dout, cache):
"""
ReLU神经元的反向传播
Input:
- dout: 上层误差梯度
- cache: affine缓存,以及relu缓存
Returns:
- dx: 输入数据x的梯度
- dw: 权重矩阵w的梯度
- db: 偏置向量b的梯度
"""
#############################################################################
# 任务: 实现 ReLU神经元反向传播. #
#############################################################################
fc_cache, relu_cache = cache
da = relu_backward(dout, relu_cache)
dx, dw, db = affine_backward(da, fc_cache)
#############################################################################
# 结束编码 #
#############################################################################
return dx, dw, db
def softmax_loss(x, y):
'''
计算Softmax损失函数
Parameters
----------
x : numpy数组
表示训练数据.
y : numpy数组
表示数据类标。
Returns
-------
loss : TYPE
数据损失值
dx : TYPE
梯度
'''
probs = np.exp(x - np.max(x, axis=1, keepdims=True))
probs /= np.sum(probs, axis=1, keepdims=True)
# 数据量
N = x.shape[0]
loss = -np.sum(np.log(probs[np.arange(N), y])) / N
dx = probs.copy()
dx[np.arange(N), y] -= 1
dx /= N
return loss, dx
实现浅层神经网络
#-*- coding: utf-8 -*-
import numpy as np
from .layers import *
class ShallowLayerNet(object):
"""
浅层全连接神经网络,其中隐藏层使用ReLU作为激活函数,输出层使用softmax作为分类器
该网络结构应该为 affine - relu -affine - softmax
"""
def __init__(self, input_dim=3*32*32, hidden_dim=100, num_classes=10,
weight_scale=1e-3, reg=0.0):
"""
初始化网络.
Inputs:
- input_dim: 输入数据维度
- hidden_dim: 隐藏层维度
- num_classes: 分类数量
- weight_scale: 权重范围,给予初始化权重的标准差
- reg: L2正则化的权重衰减系数.
"""
self.params = {
}
self.reg = reg
############################################################################
# 任务:初始化权重以及偏置项 #
# 权重应该服从标准差为weight_scale的高斯分布,偏置项应该初始化为0, #
# 所有权重矩阵和偏置向量应该存放在self.params字典中。 #
# 第一层的权重和偏置使用键值 'W1'以及'b1',第二层使用'W2'以及'b2' #
############################################################################
self.params['W1'] = weight_scale * np.random.randn(input_dim, hidden_dim)
self.params['b1'] = np.zeros(hidden_dim)
self.params['W2'] = weight_scale * np.random.randn(hidden_dim, num_classes)
self.params['b2'] = np.zeros(num_classes)
############################################################################
# 结束编码 #
############################################################################
def loss(self, X, y=None):
"""
计算数据X的损失值以及梯度.
Inputs:
- X: 输入数据,形状为(N, d_1, ..., d_k)的numpy数组。
- y: 数据类标,形状为(N,)的numpy数组。
Returns:
如果y为 None, 表明网络处于测试阶段直接返回输出层的得分即可:
- scores:形状为 (N, C),其中scores[i, c] 是数据 X[i] 在第c类上的得分.
如果y为 not None, 表明网络处于训练阶段,返回一个元组:
- loss:数据的损失值
- grads: 与参数字典相同的梯度字典,键值和参数字典的键值要相同
"""
scores = None
############################################################################
# 任务: 实现浅层网络的前向传播过程, #
# 计算各数据的分类得分 #
############################################################################
# 第1层是affine和relu的组合层
out1, cache1 = affine_relu_forward(X, self.params['W1'], self.params['b1'])
# 第2层是affine层
scores, cache2 = affine_forward(out1, self.params['W2'], self.params['b2'])
############################################################################
# 结束编码 #
############################################################################
# 如果y为 None 直接返回得分
if y is None:
return scores
loss, grads = 0, {
}
############################################################################
# 任务:实现浅层网络的反向传播过程, #
# 将损失值存储在loss中,将各层梯度储存在grads字典中。 #
# 注意:别忘了还要计算权重衰减哟。 #
############################################################################
# 计算最后的分类
loss, dy = softmax_loss(scores, y)
loss += 0.5*self.reg*(np.sum(self.params['W1']*self.params['W1'])
+np.sum(self.params['W2']*self.params['W2']))
dx2, dw2, grads['b2'] = affine_backward(dy, cache2)
grads['W2'] = dw2 + self.reg*self.params['W2']
dx, dw1, grads['b1'] = affine_relu_backward(dx2, cache1)
grads['W1'] = dw1 + self.reg*self.params['W1']
############################################################################
# 结束编码 #
############################################################################
return loss, grads
def train(self, X, y, X_val, y_val,
learning_rate=1e-3, learning_rate_decay=0.95,
reg=1e-5, num_iters=100,
batch_size=200, verbose=False):
"""
使用随机梯度下贱训练神经网络
Inputs:
- X: 训练数据
- y: 训练类标.
- X_val: 验证数据.
- y_val:验证类标.
- learning_rate: 学习率.
- learning_rate_decay: 学习率衰减系数
- reg: 权重衰减系数.
- num_iters: 迭代次数.
- batch_size: 批量大小.
- verbose:是否在训练过程中打印结果.
"""
num_train = X.shape[0]
self.reg =reg
#打印以及学习率衰减周期
iterations_per_epoch = max(num_train / batch_size, 1)
# 存储训练中的数据
loss_history = [] #loss值
train_acc_history = [] #训练精度
val_acc_history = [] #记录精度
best_val=-1 #记录最佳精度
for it in range(num_iters):
X_batch = None
y_batch = None
sample_index = np.random.choice(num_train, batch_size, replace=True)
X_batch = X[sample_index, :] # (batch_size,D)
y_batch = y[sample_index] # (1,batch_size)
#计算损失及梯度
loss, grads = self.loss(X_batch, y=y_batch)
loss_history.append(loss)
#修改权重
self.params['W1'] += -learning_rate*grads['W1']
self.params['W2'] += -learning_rate*grads['W2']
self.params['b1'] += -learning_rate*grads['b1']
self.params['b2'] += -learning_rate*grads['b2']
if verbose and it % 100 == 0:
print('迭代次数 %d / %d: 损失值 %f' % (it, num_iters, loss))
if it % iterations_per_epoch == 0:
# 测试精度
train_acc = (self.predict(X_batch) == y_batch).mean()
val_acc = (self.predict(X_val) == y_val).mean()
# 记录数据
train_acc_history.append(train_acc)
val_acc_history.append(val_acc)
if (best_val < val_acc):
best_val = val_acc
# 学习率衰减
learning_rate *= learning_rate_decay
# 以字典的形式返回结果
return {
'loss_history': loss_history,
'train_acc_history': train_acc_history,
'val_acc_history': val_acc_history,
'best_val_acc':best_val
}
def predict(self, X):
"""
Inputs:
- X: 输入数据
Returns:
- y_pred: 预测类别
"""
y_pred = None
# 使用神经网络计算各类的可能性结果
out1, cache1 = affine_relu_forward(X,self.params['W1'],self.params['b1'])
scores, cache2 = affine_forward(out1,self.params['W2'],self.params['b2'])
# 选择可能性最大的类别作为该样本的分类
y_pred = np.argmax(scores, axis=1)
return y_pred
实现深层全连接网络。
#-*- coding: utf-8 -*-
import numpy as np
from .layers import *
class FullyConnectedNet(object):
"""
深层全连接神经网络,其中隐藏层使用ReLU作为激活函数,输出层使用softmax作为分类器
该网络结构应该为 {affine - relu}x(L -1) -affine - softmax
"""
def __init__(self, input_dim=3*32*32, hidden_dims=[50,50], num_classes=10,
reg=0.0, weight_scale=1e-3):
"""
初始化网络.
Inputs:
- input_dim: 输入数据维度
- hidden_dim: 隐藏层各层维度
- num_classes: 分类数量
- weight_scale: 权重范围,给予初始化权重的标准差
- reg: L2正则化的权重衰减系数.
"""
self.reg = reg
self.num_layers = 1 + len(hidden_dims)
self.params = {
}
# 这里存储的是每层的神经元数量。
layers_dims = [input_dim] + hidden_dims + [num_classes]
############################################################################
# 任务:初始化任意多层权重以及偏置项 #
# 权重应该服从标准差为weight_scale的高斯分布,偏置项应该初始化为0, #
# 所有权重矩阵和偏置向量应该存放在self.params字典中。 #
# 第一层的权重和偏置使用键值 'W1'以及'b1',第n层使用'Wn'以及'bn' #
############################################################################
for i in range(self.num_layers):
self.params['W'+str(i+1)] = weight_scale*np.random.randn(
layers_dims[i], layers_dims[i+1])
self.params['b'+str(i+1)] = np.zeros((1, layers_dims[i+1]))
############################################################################
# 结束编码 #
############################################################################
def loss(self, X, y=None):
"""
计算数据X的损失值以及梯度.
Inputs:
- X: 输入数据,形状为(N, d_1, ..., d_k)的numpy数组。
- y: 数据类标,形状为(N,)的numpy数组。
Returns:
如果y为 None, 表明网络处于测试阶段直接返回输出层的得分即可:
- scores:形状为 (N, C),其中scores[i, c] 是数据 X[i] 在第c类上的得分.
如果y为 not None, 表明网络处于训练阶段,返回一个元组:
- loss:数据的损失值
- grads: 与参数字典相同的梯度字典,键值和参数字典的键值要相同
"""
scores = None
############################################################################
# 任务: 实现深层网络的前向传播过程, #
# 计算各数据的分类得分 #
############################################################################
cache_relu, outs, cache_out = {
}, {
}, {
}
outs[0] = X
num_h = self.num_layers-1
for i in range(num_h):
outs[i+1], cache_relu[i+1] = affine_relu_forward(
outs[i], self.params['W'+str(i+1)], self.params['b'+str(i+1)])
scores, cache_out = affine_forward(outs[num_h],
self.params['W'+str(num_h+1)],
self.params['b'+str(num_h+1)])
############################################################################
# 结束编码 #
############################################################################
loss, grads = 0.0, {
}
############################################################################
# 任务:实现深层网络的反向传播过程, #
# 将损失值存储在loss中,将各层梯度储存在grads字典中。 #
# 注意:别忘了还要计算权重衰减哟。 #
############################################################################
dout, daffine = {
}, {
}
loss, dy = softmax_loss(scores, y)
# 隐藏层数
h = self.num_layers-1
# 最后一层是affine层,前面的均是组合层
for i in range(self.num_layers):
loss += 0.5*self.reg*(np.sum(self.params['W'+str(i+1)]*self.params['W'+str(i+1)]))
dout[h], grads['W'+str(h+1)], grads['b'+str(h+1)] = affine_backward(dy, cache_out)
grads['W'+str(h+1)] += self.reg*self.params['W'+str(h+1)]
for i in range(h):
dout[h-i-1], grads['W'+str(h-i)], grads['b'+str(h-i)] = affine_relu_backward(
dout[h-i], cache_relu[h-i])
grads['W'+str(h-i)] += self.reg*self.params['W'+str(h-i)]
############################################################################
# 结束编码 #
############################################################################
return loss, grads
def train(self, X, y, X_val,
y_val,learning_rate=1e-3, learning_rate_decay=0.95,
num_iters=100,batch_size=200, verbose=False):
"""
使用随机梯度下降训练神经网络
Inputs:
- X: 训练数据
- y: 训练类标.
- X_val: 验证数据.
- y_val:验证类标.
- learning_rate: 学习率.
- learning_rate_decay: 学习率衰减系数
- reg: 权重衰减系数.
- num_iters: 迭代次数.
- batch_size: 批量大小.
- verbose:是否在训练过程中打印结果.
"""
num_train = X.shape[0]
iterations_per_epoch = max(num_train / batch_size, 1)
loss_history = []
train_acc_history = []
val_acc_history = []
best_val=-1
for it in range(num_iters):
X_batch = None
y_batch = None
sample_index = np.random.choice(num_train, batch_size, replace=True)
X_batch = X[sample_index, :] # (batch_size,D)
y_batch = y[sample_index] # (1,batch_size)
#计算损失以及梯度
loss, grads = self.loss(X_batch, y=y_batch)
loss_history.append(loss)
#修改权重
############################################################################
# 任务:修改深层网络的权重 #
############################################################################
for i,j in self.params.items():
self.params[i] += -learning_rate*grads[i]
############################################################################
# 结束编码 #
############################################################################
if verbose and it % 100 == 0:
print('iteration %d / %d: loss %f' % (it, num_iters, loss))
if it % iterations_per_epoch == 0:
# 检验精度
train_acc = (self.predict(X) == y).mean()
val_acc = (self.predict(X_val) == y_val).mean()
# 记录数据
train_acc_history.append(train_acc)
val_acc_history.append(val_acc)
if (best_val < val_acc):
best_val = val_acc
# 学习率衰减
learning_rate *= learning_rate_decay
# 以字典的形式返回结果
return {
'loss_history': loss_history,
'train_acc_history': train_acc_history,
'val_acc_history': val_acc_history,
'best_val_acc':best_val
}
def predict(self, X):
"""
Inputs:
- X: 输入数据
Returns:
- y_pred: 预测类别
"""
y_pred = None
###########################################################################
# 任务: 执行深层网络的前向传播, #
# 然后使用输出层得分函数预测数据类标 #
###########################################################################
outs = {
}
outs[0] = X
num_h = self.num_layers-1
for i in range(num_h):
outs[i+1], _ =affine_relu_forward(outs[i], self.params['W'+str(i+1)],
self.params['b'+str(i+1)])
scores, _ = affine_forward(outs[num_h], self.params['W'+str(num_h+1)],
self.params['b'+str(num_h+1)])
y_pred = np.argmax(scores, axis=1)
###########################################################################
# 结束编码 #
###########################################################################
return y_pred