deeplearning课后作业(课程一第三周作业)

具有一个隐藏层的神经网络平面数据分类

本篇文章将通过python构建一个简单浅层神经网络(具有一个隐藏层)来分类数据。

1, 导入相关包

构建一个浅层神经网络,需要导入的相关python包,具体如下所示:

# Package imports
import numpy as np
import matplotlib.pyplot as plt

import sklearn
import sklearn.datasets
import sklearn.linear_model
%matplotlib inline
np.random.seed(1) 

导入相关库的代码如上所示,其中,一些库和代码的解释如下:
sklearn:提供了数据挖掘和数据分析的一些简单易用的工具。
np.random.seed(1)保证了每一次生成的随机数是一样的,保证了代码每次运行结果能保持不变。

2. 数据可视化

本次作业中,所加载的数据集是通过python生成的,加载数据并可视化之后,数据在直角坐标系上呈现出一个简单的花的形状,并且具有两种颜色。具体实现代码和效果,如下所示:

  • 生成数据的代码如下所示:
def load_planar_dataset():
    np.random.seed(1)  #固定随机种子
    m = 400 # 样本的个数
    N = int(m/2) # 某一类样本的个数
    D = 2 # 数据维数
    X = np.zeros((m,D)) #利用数据生成矩阵大小,其中,每一个行向量代表一个样本
    Y = np.zeros((m,1), dtype='uint8') # 输出标签向量,0代表红色,1代表蓝色
    a = 4 # 生成花的形状设置,四条对称花瓣
   """
    在二维平面上生成数据的坐标,可以看成一个极坐标,
    t代表的是角度,r代表由角度生成的半径,也就是玫瑰花瓣的长度。最后, 利用参数方程,将极坐标转换为直角坐标,
    以下两次循环中,第一次生成红色点的坐标,
    第二次生成蓝色点的坐标。
  """
    for j in range(2):
        ix = range(N*j,N*(j+1))
        t = np.linspace(j*3.12,(j+1)*3.12,N) + np.random.randn(N)*0.2 # theta
        r = a*np.sin(4*t) + np.random.randn(N)*0.2 #极坐标曲线
        X[ix] = np.c_[r*np.sin(t), r*np.cos(t)]  #参数方程标出每个点的坐标,并赋值给一个二维向量
        Y[ix] = j
        
    X = X.T
    Y = Y.T

    return X, Y

代码的详细解释如上注释所示,最后利用matplotlib数据可视化,如下所示:

X, Y = load_planar_dataset() 
plt.scatter(X[0,:], X[1,:], c=np.squeeze(Y), s=40, cmap=plt.cm.Spectral)

对于生成的数据,先查看数据的维数和形状,以确保后续的算法实现,具体实现代码如下所示:


shape_X = X.shape
shape_Y = Y.shape
m = shape_X[1]  

print ('The shape of X is: ' + str(shape_X)) #(2,400)
print ('The shape of Y is: ' + str(shape_Y)) #(1,400)
print ('I have m = %d training examples!' % (m)) #m=400

3. 简单的逻辑回归实现

在神经网络实现之前,作为对比,可以先采用机器学习中的逻辑回归算法对此问题做出解答,逻辑回归实现中,可以直接调用sklearn包实现,具体实现代码如下所示:

#直接调用sklearn包中的方法
clf = sklearn.linear_model.LogisticRegressionCV();
clf.fit(X.T, Y.T);

在逻辑回归算法实现之前,先绘出其决策边界,具体代码实现,可以如下所示:


def plot_decision_boundary(model, X, y):
    # 设置最大,最小值,并给其添加一些填充
   # 这里的x和y代表的是直接坐标,而不是输入变量与输出变量
    x_min, x_max = X[0, :].min() - 1, X[0, :].max() + 1 
    y_min, y_max = X[1, :].min() - 1, X[1, :].max() + 1
    h = 0.01
    #生成点网格,并将其距离设置为h
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
 """
将两个向量合成一个矩阵,x为其行向量,而y形成其列向量
其实就是生成了一个网络坐标矩阵,且xx和yy的形状一样
"""
    # 预测整个网格的函数值
    Z = model(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    # 绘制轮廓和训练样本
    plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)
    plt.ylabel('x2')
    plt.xlabel('x1')
    plt.scatter(X[0, :], X[1, :], c=y, cmap=plt.cm.Spectral)

利用python绘图并利用逻辑回归做出预测,其结果如下所示,可以看到,利用机器学习中的逻辑回归算法,数据集的分类精度只达到了47%,距理想结果相去甚远。

# 为逻辑回归绘制决策边界
plot_decision_boundary(lambda x: clf.predict(x), X, np.squeeze(Y))
plt.title("Logistic Regression")

#打印训练精确度
LR_predictions = clf.predict(X.T)
print ('Accuracy of logistic regression: %d ' % float((np.dot(Y,LR_predictions) + np.dot(1-Y,1-LR_predictions))/float(Y.size)*100) +
       '% ' + "(percentage of correctly labelled datapoints)")

由于数据集并不是线性分布的,所以线性分类中的逻辑回归效果并不理想,采用以下逻辑回归算法,效果可能更加理想。

4. 神经网络模型

在这一部分中,需要构建一个简单的神经网络分类器来实现数据分类,构建的神经网络结构如下图所示:


对于一个样本 x^{(i)}:
z^{[1] (i)} = W^{[1]} x^{(i)} + b^{[1] (i)}\tag{1}
a^{[1] (i)} = \tanh(z^{[1] (i)})\tag{2}
z^{[2] (i)} = W^{[2]} a^{[1] (i)} + b^{[2] (i)}\tag{3}
\hat{y}^{(i)} = a^{[2] (i)} = \sigma(z^{ [2] (i)})\tag{4}
y^{(i)}_{prediction} = \begin{cases} 1 & \mbox{if } a^{[2](i)} > 0.5 \\ 0 & \mbox{otherwise } \end{cases}\tag{5}

给定所有样本的预测值,可以用如下公式计算函数损失值:
J = - \frac{1}{m} \sum\limits_{i = 0}^{m} \large\left(\small y^{(i)}\log\left(a^{[2] (i)}\right) + (1-y^{(i)})\log\left(1- a^{[2] (i)}\right) \large \right) \small \tag{6}

构建一个神经网络的步骤可以如下表示“

  • 构建神经网络,输入单元,输出单元和隐藏单元等
  • 初始化模型参数
  • 循环:
    • 计算其前向传播
    • 计算损失
    • 利用反向传播算法计算梯度
    • 根据梯度下降算法更新参数
4.1 定义神经网络的结构

如上图所示的神经网络结构图,我们需要定义神经网络的输入层,隐藏层和输出层的神经单元数目,具体实现代码如下所示:

def layer_sizes(X, Y):
   
    n_x = 2   #输入层的单元数,输入变量的特征数
    n_h = 4  #隐藏层的单元数
    n_y = 1  #输出层的单元
    return (n_x, n_h, n_y)
4.2 初始化模型参数

初始化模型参数时,要根据神经网络的结构控制输出随机矩阵的形状,如模型结构图所示,权重参数w可以简单的理解为输入层到隐藏层的有向线段数,从图中可以看出,从输入层到隐藏层的有向线段数目是8条,特征x_1,x_2分别有4条有向线段指向隐藏层,所以权重从参数w_1的形状可以直接等效为(4,2),同理,从隐藏层到输出层的权重参数w_2的形状为(4,1)。最后,采用随机初始化的方法初始化权重矩阵,再给w_1和w_2分别乘上0.01,最后,整个代码实现方式如下所示;


def initialize_parameters(n_x, n_h, n_y):
  
    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((1,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

注意:

  • randn(D,H)表示从均值为0的单位标准高斯分布进行取样,因为设置了随机初始化种子,所以每次都会生成相同的随机值。
  • 权重参数乘以0.01是为了防止初始化参数过大,提前进入激活函数饱和区,从而使收敛速度变慢。
4.3 循环部分的实现
1) 前向传播算法的实现

根据公式(1) - 公式(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 = 1/(1+np.exp(-Z2))
    assert(A2.shape == (1, X.shape[1]))
    
    cache = {"Z1": Z1,
             "A1": A1,
             "Z2": Z2,
             "A2": A2}
    
    return A2, cache

2) 损失函数的计算

有了以上前向传播函数的计算,循环第二步中计算损失函数的过程根据公式(6)如下所示:

def compute_cost(A2, Y):

    m = Y.shape[1]  #样本数 m = 400
    logprobs = np.multiply(np.log(A2),Y)+np.multiply(np.log(1-A2),(1-Y))
    cost = -1/m *np.sum(logprobs)
    cost = np.squeeze(cost)   
    assert(isinstance(cost, float))
    
    return cost
3) 反向传播函数的计算

完成以上步骤之后,就可以实现一个反向传播算法了,反向传播算法的计算简单来说就是一个从后往前按照神经网络的结构和导数的链式计算法则一次求导的过程,其具体公式如下所示:

dZ^{[2]} = A^{[2]} -Y\tag{7}
dW^{[2]} = \frac{1}{m}dZ^{[2]}A^{[1]T}\tag{8}
db^{[2]} = \frac{1}{m}np.sum(dZ^{[2]},axis= 1,keepdims= True)\tag{9}
dZ^{[1]} = W^{[2]T}dZ^{[2]} * g[1]^{'}(Z^{[1]})\tag{10}
dW^{[1]} = \frac{1}{m}dZ^{[1]}x^T\tag{11}
db^{[1]} = \frac{1}{m}np.sum(dZ^{[1]},axis=1,keepdims = True)\tag{12}

根据以上公式,其算法实现的代码如下所示:


def backward_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 = 1/m*np.dot(dZ2,A1.T)
    db2 = 1/m*np.sum(dZ2,axis = 1,keepdims = True)
    dZ1 = np.dot(W2.T,dZ2)*(1-(np.tanh(np.dot(W1,X)))**2)
    dW1 = 1/m*np.dot(dZ1,X.T)
    db1 = 1/m*np.sum(dZ1,axis = 1,keepdims = True)
    grads = {"dW1": dW1,
             "db1": db1,
             "dW2": dW2,
             "db2": db2}
    
    return grads
4) 梯度更新

有了以上对于梯度的计算,现在可以根据相关公式,进行梯度更新了,具体公式和代码实现如下所示:

梯度更新的公式如下所示:
w1 := w1- \alpha*dw1\tag{13}
b1 := b1- \alpha*db1\tag{14}
w2 := w2- \alpha*dw2\tag{15}
b2 := b2- \alpha*db2\tag{16}

代码实现如下所示:

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

综上,将所有代码和公式结合在一起,利用多次迭代实现梯度下降算法的代码如下所示:

def nn_model(X, Y, n_h, num_iterations = 10000, print_cost=False):
 
    np.random.seed(2)
    n_x = layer_sizes(X, Y)[0]
    n_y = layer_sizes(X, Y)[2]
    

    parameters = initialize_parameters(n_x,n_h,n_y)
    W1 = parameters['W1']
    b1 = parameters['b1']
    W2 = parameters['W2']
    b2 = parameters['b2']
   costs = list()
    for i in range(0, num_iterations):
         
        A2, cache =  forward_propagation(X, parameters)

        cost = compute_cost(A2,Y)
        grads =  backward_propagation(parameters, cache, X, Y)
 
        parameters = update_parameters(parameters,grads)
        
        if print_cost and i % 1000 == 0:
            print ("Cost after iteration %i: %f" %(i, cost))

    return parameters

为以上代码,编写测试代码如下所示:


parameters,costs = nn_model(X, Y, 4, num_iterations=10000, print_cost=True)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))
x_f = np.linspace(0,1000,10000)

plt.plot(x_f,costs)
plt.show()

运行结果如下所示:

简单绘出梯度下降的图形,可以看出,通过一个浅层的神经网络,损失逐渐变小并收敛。

4.4 做出预测

根据构建神经网络并通过梯度下降算法得到了权重参数之后,根据公式(17)就可以做出预测了,具体实现代码如下所示:
predictions = y_{prediction} = \mathbb 1 \text{{activation > 0.5}} = \begin{cases} 1 & \text{if}\ activation > 0.5 \\ 0 & \text{otherwise} \end{cases}\tag{17}

def predict(parameters, X):
   
    A2, cache = forward_propagation(X, parameters)
   
    predictions = np.zeros((1,m))
    for i in range(m):
        if A2[0,i]>=0.5:
            predictions[0,i] = 1
        else:
            predictions[0,i] = 0
    return predictions

输出预测结果,如下所示:

parameters,costs = nn_model(X, Y, n_h = 4, num_iterations = 20000, print_cost=False)
predictions = predict(parameters, X)
print(predictions,predictions.shape)
print("predictions mean = " + str(np.mean(predictions)))

根据代码绘制决策边界,并输出精确度,如下所示,可以看出,分类精确度达到了90%,比逻辑回归算法的47%高出了很多。


parameters,costs= nn_model(X, Y, n_h = 4, num_iterations = 10000, print_cost=True)
print(parameters)

plot_decision_boundary(lambda x:predict(parameters,x.T),X,Y)
plt.title("Decision Boundary for hidden layer size " + str(4))
# Print accuracy
predictions = predict(parameters, X)
print ('Accuracy: %d' % float((np.dot(Y,predictions.T) + np.dot(1-Y,1-predictions.T))/float(Y.size)*100) + '%')
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容