本文是基于吴恩达老师的《深度学习》第一课第三周课后题所做,目的是使用浅层神经网络对给定的平面数据进行分类。该算法的实现共分:模型搭建,模型测试,参数调整,算法对比和更换数据五个步骤,详述如下:
一、模型搭建
在该题目中,吴恩达老师事先已贴心的给出了平面数据集,数据文件链接:https://pan.baidu.com/s/1NH_UQfNk4W-dSuYtGwWQHQ 密码:cmer,我们可通过调用planar_utils.py文件中的load_planar_dataset函数生成平面数据集。
X,Y = load_planar_dataset()
plt.scatter(X[0,:],X[1,:],c=np.ravel(Y),s=40,cmap=plt.cm.Spectral)
plt.show()
平面数据集如下图所示(随机生成红、蓝两色数据点,由于本文着重于学习算法,数据集的生成不过多叙述):
按照浅层神经网络构建的思路,我们可将算法划分成七个步骤来实现:
1. 确定各层神经元数量。
各层神经元数量决定了模型的深度,浅层神经网络采用三层结构:输入层、隐藏层、输出层。
def layer_size(X,Y):
n_x = X.shape[0]
n_h = 4
n_y = Y.shape[0]
return (n_x, n_h, n_y)
为了方便返回值的调用,此处使用元组返回各层神经元数量。
2.初始化权重参数W1,W2和偏置b1,b2。
为保证随机生成的初始值在各次迭代中能够保持一致性,需使用np.random.seed()函数进行约束。在初始权重参数时不可以采用零矩阵,这一点吴恩达老师在课程中重点讲解过,不再赘述。
def initialize_parameters(n_x, n_h, n_y):
np.random.seed(2)
W1 = np.random.randn(n_h,n_x) * 0.01
b1 = np.zeros((n_h,1))
W2 = np.random.randn(n_y,n_h) * 0.01
b2 = np.zeros((n_y,1))
assert(W1.shape == (n_h,n_x))
assert(b1.shape == (n_h,1))
assert(W2.shape == (n_y,n_h))
assert(b2.shape == (n_y,1))
parameters = {"W1" : W1,
"b1" : b1,
"W2" : W2,
"b2" : b2
}
return parameters
3.前向传播。
计算出A1,Z1和A2,Z2,隐藏层的激励函数采用tanh函数,输出层的激励函数采用sigmoid函数。神经网络前向传播过程中有4个重要公式,需要牢记:
def forward_propagation(X,parameters):
W1 = parameters["W1"]
b1 = parameters["b1"]
W2 = parameters["W2"]
b2 = parameters["b2"]
Z1 = np.dot(W1,X) + b1
A1 = np.tanh(Z1)
Z2 = np.dot(W2,A1) + b2
A2 = sigmoid(Z2)
assert(A2.shape == (1,X.shape[1]))
cache = {"Z1" : Z1,
"A1" : A1,
"Z2" : Z2,
"A2" : A2
}
return A2, cache
4.计算代价函数Cost/Loss。
代价函数公式为:
def compute_cost(A2,Y,parameters):
m = Y.shape[1]
logprobs = np.multiply(np.log(A2),Y) + np.multiply(np.log(1-A2),1-Y)
cost = - np.sum(logprobs) / m
cost = np.squeeze(cost)
assert(isinstance(cost, float))
return cost
5.后向传播。
后向传播通常采用梯度下降法,是该算法中的重中之重,此步骤有六个公式需要牢记(摘引吴恩达老师ppt):
def back_propagation(parameters,cache,X,Y):
m = X.shape[1]
W1 = parameters["W1"]
W2 = parameters["W2"]
A1 = cache["A1"]
A2 = cache["A2"]
dZ2 = A2 - Y
dW2 = np.dot(dZ2,A1.T) / m
db2 = np.sum(dZ2,axis=1,keepdims=True) / m
dZ1 = np.dot(W2.T,dZ2) * (1 - np.power(A1,2))
dW1 = np.dot(dZ1,X.T) / m
db1 = np.sum(dZ1,axis=1,keepdims=True) / m
grads = {"dW1" : dW1,
"db1" : db1,
"dW2" : dW2,
"db2" : db2
}
return grads
6.参数更新。
使用梯度下降法计算出的偏导数更新参数,学习率α的选择直接影响梯度下降的速度。
def update_parameters(parameters,grads,learning_rate = 1.2):
W1 = parameters["W1"]
b1 = parameters["b1"]
W2 = parameters["W2"]
b2 = parameters["b2"]
dW1 = grads["dW1"]
db1 = grads["db1"]
dW2 = grads["dW2"]
db2 = grads["db2"]
W1 -= learning_rate * dW1
b1 -= learning_rate * db1
W2 -= learning_rate * dW2
b2 -= learning_rate * db2
parameters = {"W1" : W1,
"b1" : b1,
"W2" : W2,
"b2" : b2
}
return parameters
7.构建神经网络模型。
将上述函数进行整合,并通过反复迭代步骤3-6,最终生成适用于神经网络模型的参数。
def nn_model(X,Y,n_h,num_iterations = 10000,print_cost = False):
np.random.seed(3)
n_x = layer_size(X,Y)[0]
n_y = layer_size(X,Y)[2]
parameters = initialize_parameters(n_x, n_h, n_y)
W1 = parameters["W1"]
b1 = parameters["b1"]
W2 = parameters["W2"]
b2 = parameters["b2"]
for i in range(0,num_iterations):
A2, cache = forward_propagation(X,parameters)
cost = compute_cost(A2,Y,parameters)
grads = back_propagation(parameters,cache,X,Y)
parameters = update_parameters(parameters,grads,learning_rate = 1.2)
if print_cost and i % 100 == 0:
print("Cost after iteration %i:%f" %(i,cost))
return parameters
二、模型测试
使用步骤一中训练好的神经网络模型,对新的样本进行预测。
def predict(parameters,X):
A2, cache = forward_propagation(X,parameters)
predictions = (A2 > 0.5)
return predictions
我们可以使用吴恩达老师给出的testCases_v2.py文件中的predict_test_case()函数进行验证测试。
def predict_test_case():
np.random.seed(1)
X_assess = np.random.randn(2, 3)
parameters = {'W1': np.array([[-0.00615039, 0.0169021 ],
[-0.02311792, 0.03137121],
[-0.0169217 , -0.01752545],
[ 0.00935436, -0.05018221]]),
'W2': np.array([[-0.0104319 , -0.04019007, 0.01607211, 0.04440255]]),
'b1': np.array([[ -8.97523455e-07],
[ 8.15562092e-06],
[ 6.04810633e-07],
[ -2.54560700e-06]]),
'b2': np.array([[ 9.14954378e-05]])}
return parameters, X_assess
parameters, X_assess = predict_test_case()
predictions = predict(parameters, X_assess)
print("predictions mean = " + str(np.mean(predictions)))
预测值为:
predictions mean = 0.6666666666666666
三、算法对比
使用搭建好的浅层神经网络模型对文首给出的平面数据集进行分类:
X,Y = load_planar_dataset()
n_h=4
parameters = nn_model(X,Y,n_h,num_iterations = 5000,print_cost = True)
plot_decision_boundary(lambda x:predict(parameters,x.T), X, Y)
plt.title("decision_boundary for hidden layer size" + str(4))
plt.show()
predictions = predict(parameters,X)
accuray = float((np.dot(Y,predictions.T)+np.dot(1-Y,1-predictions.T))/float(Y.size)*100)
print("Accuracy for {} hidden units:{}%".format(n_h,accuray) )
结果如下:
虽然是浅层神经网络但预测精度已经可以达到90.75%,相比逻辑回归算法精度大大提升。下面我们用逻辑回归算法对平面数据集进行分类处理。
clf = sklearn.linear_model.LogisticRegressionCV()
clf.fit(X.T,Y.T)
plot_decision_boundary(lambda x : clf.predict(x),X,Y)
plt.title("Logistic Regression")
LR_predictions = clf.predict(X.T)
print('Accuray of lr: %d'%float((np.dot(Y,LR_predictions)+
np.dot(1-Y,1-LR_predictions))/float(Y.size)*100)+
'%'+"(percentage of correctly labelled datapoints)")
plt.show()
使用逻辑回归分类的精度只有 47%。
四、参数调整
在建立模型时,隐藏层神经元个数我们选择为4个,这个参数的选择有很大的主观性,我们可在模型建立好之后通过试凑法来进行优化。
plt.figure(figsize=(16,32))
hidden_layer_sizes = [1,2,3,4,5,20,50]
for i, n_h in enumerate(hidden_layer_sizes):
plt.subplot(5,2,i+1)
plt.title('Hidden Layer of size %d'%n_h)
parameters = nn_model(X,Y,n_h,num_iterations = 5000)
plot_decision_boundary(lambda x:predict(parameters,x.T), X, Y)
#plt.title("decision_boundary for hidden layer size" + str(4))
#plt.show()
predictions = predict(parameters,X)
accuray = float((np.dot(Y,predictions.T)+np.dot(1-Y,1-predictions.T))/float(Y.size)*100)
print("Accuracy for {} hidden units:{}%".format(n_h,accuray) )
plt.show()
结果如下:
测试精度分别为:
Accuracy for 1 hidden units:61.5%
Accuracy for 2 hidden units:70.5%
Accuracy for 3 hidden units:66.25%
Accuracy for 4 hidden units:90.75%
Accuracy for 5 hidden units:90.5%
Accuracy for 20 hidden units:92.0%
Accuracy for 50 hidden units:90.75%
可见对于本文中采用数据,隐藏层神经元的个数设置在20左右,测试精度最优。
五、更换数据
最后,我们可以通过改变数据集来验证一下模型的泛化性,防止模型出现过拟合。
新的数据集如下图所示:
noisy_circles, noisy_moons, blobs, gaussian_quantiles, no_structure = load_extra_datasets()
datasets = {"noisy_circles" : noisy_circles,
"noisy_moons" : noisy_moons,
"blobs" : blobs,
"gaussian_quantiles" : gaussian_quantiles
}
dataset = "noisy_circles"
X,Y = datasets[dataset]
X,Y = X.T,Y.reshape(1,Y.shape[0])
if dataset == "blobs":
Y = Y % 2
plt.scatter(X[0,:],X[1,:],c=np.ravel(Y),s=40,cmap=plt.cm.Spectral)
plt.show()
预测结果为:
预测精度为85.5%小于原始数据集的90.75%,说明本模型的泛化能力还是比较弱,我们可以通过增加样本数量、调整参数及优化模型等方法进一步改善。