编码实现全连接神经网络

layers.py

首先实现神经网络中仿射层、ReLU层以及组合单层神经元。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#-*- 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

shallow_layer_net.py

实现浅层神经网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#-*- 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

fc_net.py

实现深层全连接网络。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#-*- 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


编码实现全连接神经网络
https://fulequn.github.io/2020/11/Article202011303/
作者
Fulequn
发布于
2020年11月30日
许可协议