BP算法
同时构造w和b
一种比较方便的方法是将偏置放在w的第一个.
def initialize_wb(lay_in, lay_out, epsilon=1.0):
# 其中wb[0]就是偏置,因此要有 1 + lay_in
wb = np.random.rand(lay_out, 1 + lay_in) * 2 * epsilon - epsilon
return wb
主要代码
def main():
x, y = get_data()
wb1 = initialize_wb(INPUT_LAYER_SIZE, HIDDEN_LAYER_SIZE)
wb2 = initialize_wb(HIDDEN_LAYER_SIZE, OUT_LABELS_SIZE)
f = tanh
f_gradient = tanh_gradient
loss = []
for epoch in range(epoch_time):
h_total = np.zeros((num_observations, 1)) # 所有样本的预测值, m*1, probability
# D_1 = np.zeros(wb1.shape) # Δ_1
# D_2 = np.zeros(wb2.shape) # Δ_2
for t in range(num_observations):
# 前向传播计算预测值
a_1 = np.vstack((np.array([[1]]), x[t:t + 1, :].T)) # 垂直堆叠成[[1],[当前x]]的列向量
z_2 = np.dot(wb1, a_1) # 2*1
a_2 = np.vstack((np.array([[1]]), f(z_2))) # 3*1
z_3 = np.dot(wb2, a_2) # 1*1
a_3 = f(z_3)
h = a_3 # 预测值h就等于a_3, 1*1
# 后向传播梯度下降计算误差
h_total[t, 0] = h
delta_3 = h - y[t:t + 1, :].T # 最后一层每一个单元的误差, δ_3, 1*1
delta_2 = np.multiply(np.dot(wb2[:, 1:].T, delta_3),
f_gradient(z_2)) # 第二层每一个单元的误差(不包括偏置单元), δ_2, 2*1
# D_2 = D_2 + np.dot(delta_3, a_2.T) # 第二层所有参数的误差, 1*3
# D_1 = D_1 + np.dot(delta_2, a_1.T) # 第一层所有参数的误差, 2*3
wb2 = wb2 - learning_rate * np.dot(delta_3, a_2.T)
wb1 = wb1 - learning_rate * np.dot(delta_2, a_1.T)
# 如果使用D_1和D_2的话是到最后再调整参数,注意要用均值
# wb1 = wb1 - learning_rate * (1.0 / num_observations) * D_1 # 第一层参数的偏导数,取所有样本中参数的均值,没有加正则项
# wb2 = wb2 - learning_rate * (1.0 / num_observations) * D_2
# 均方误差
J = (1.0 / num_observations) * np.sum((y - h_total) ** 2)
# 计算损失 交叉熵
# J = (1.0 / num_observations) * np.sum(-y * np.log(h_total) - (np.array([[1]]) - y) * np.log(1 - h_total))
loss.append(J)
if epoch % 100 == 0:
print("epoch: %s, lost: %s, " % (epoch, J))
# print(h_total.reshape(num_observations))
if epoch == epoch_time - 1:
plt.plot(x, h_total)
# plt.plot(range(epoch_time), loss)
# plt.plot(x, last_out)
plt.plot(x, y)
plt.show()
检查方法
首先检查输入与输入的维度是否正确
# x = np.linspace(0, 500, num_observations) 一维的
x = np.linspace(0, 0.5, num_observations).reshape((num_observations, 1)) # 通过reshap编程二维的,同时y也会编程二维的
# f(x)=sin6(5πx)
y = np.sin(5 * np.pi * x) ** 6
更换激活函数
比如此次如果使用sigmoid函数,是得不到结果的。
# sigmoid和tanh两个激活函数及其导数。
def sigmoid(x):
return 1.0 / (1.0 + np.exp(-x))
def sigmoid_gradient(z):
g = np.multiply(sigmoid(z), (1 - sigmoid(z)))
return g
def tanh(x):
return np.tanh(x)
def tanh_gradient(x):
# tanh函数的导数
return 1 - np.tanh(x) * np.tanh(x)
更改学习率
如果学习率保持一个比较低的水平不变,那么调大学习率。
可能的原因是陷入局部极小,调大学习率将会跳出局部极小的状态。