PaddlePaddle 深度学习实战(第三部分)

PaddlePaddle 深度学习实战(第一部分)

PaddlePaddle 深度学习实战(第二部分)

PaddlePaddle 深度学习实战(第三部分)

PaddlePaddle 深度学习实战(第四部分)

PaddlePaddle 深度学习实战(第五部分)


 

浅层神经网络、BP算法(反向传播)


浅层神经网络的结构、前向传播、反向传播(BP算法)、梯度下降、激活函数(非线性函数/非线性映射)

import random
import numpy as np

def load_data_sets():
    """
    加载数据
    Return:
        train_x:训练集的X
        train_y:训练集的label
        X:测试集的X
        Y:测试集的label
    """
    N = 200  	# 每个类型的点数量 number of points per class
    D = 2  	# 维度大小 dimensionality
    K = 2  	# 类型数量 number of classes
    number = N * K
    X = np.zeros((N * K, D))  		# 矩阵中每行是一个样本数据 data matrix (each row = single example)
    y = np.zeros(N * K, dtype='uint8')  	# 类型标签 class labels

    #实质自动往X原数据集和y标签集数据填充数据
    for j in range(K):
        ix = range(N * j, N * (j + 1))
        r = np.linspace(0.0, 1, N)  	# 半径长度/半径范围 radius
        t = np.linspace(j * 4, (j + 1) * 4, N) + np.random.randn(N) * 0.2  # theta
        X[ix] = np.c_[r * np.sin(t), r * np.cos(t)]
        y[ix] = j

    X = X.T	#(2,400) 即(特征列数,样本数)
    Y = y.reshape(1, number) #(1,400) 即(特征列数,标签数)

    # 训练集 随机从1~400中选取320个数字作为样本数据的索引
    train_num = random.sample(range(number), 320)  # 共400组数据,训练集取其中80%,即320组
    train_x = X[:, train_num] #train_x.shape为(2,320)
    train_y = Y[:, train_num] #train_y.shape为(1,320)

    result = [train_x, train_y, X, Y]
    return result

"""
    使用python及numpy库来实现浅层神经网络识别花型图案,关键步骤如下:
    1.载入数据和预处理:load_planar_dataset
    2.初始化模型参数(Parameters)
    3.循环:
    a)	计算成本(Cost)
    b)	计算梯度(Gradient)
    c)	更新参数(Gradient Descent)
    4.搭建双层神经网络,利用模型进行预测
    5.分析预测结果
    6.定义model函数来按顺序将上述步骤合并
"""
import matplotlib.pyplot as plt
import numpy as np
import utils

def layer_sizes(X, Y):
    """
    设置网络结构
    Args:
        X: 输入的数据
        Y: 输出值
    Return:
        n_x: 输入层节点数
        n_h: 隐藏层节点数
        n_y: 输出层节点数
    """ 
    n_x = X.shape[0]  # 输入层大小(节点数),X.shape为(特征列数,样本数)
    n_h = 4
    n_y = Y.shape[0]  # 输出层大小(节点数),Y.shape为(特征列数,标签数)
    return (n_x, n_h, n_y)
 
def initialize_parameters(n_x, n_h, n_y):
    """
    初始化参数
    Args:
        n_x: 输入层大小
        n_h: 隐藏层大小
        n_y: 输出层大小
    Return:
        parameters: 一个包含所有参数的python字典,参数如下
            W1 -- 隐藏层权重,维度是 (n_h, n_x),即 (隐藏层节点数,输入层节点数)
            b1 -- 隐藏层偏移量,维度是 (n_h, 1),即 (隐藏层节点数,1)
            W2 -- 输出层权重,维度是 (n_y, n_h),即 (输出层节点数,隐藏层节点数)
            b2 -- 输出层偏移量,维度是 (n_y, 1),即 (输出层节点数,1)
    """
    np.random.seed(2)  # 设置随机种子

    # 随机初始化参数,偏移量初始化为0
    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

def forward_propagation(X, parameters):
    """
    前向传播
    Args:
        X: 输入值
        parameters: 一个python字典,包含权值W1,W2和bias b1,b2
    Return:
        A2: 模型输出值
        cache: 一个字典,包含 "Z1", "A1", "Z2" and "A2"
    """
    W1 = parameters["W1"] #第一层隐藏层权重
    b1 = parameters["b1"] #第一层隐藏层偏置
    W2 = parameters["W2"] #第二层输出层权重
    b2 = parameters["b2"] #第二层输出层偏置

    # 计算隐藏层:线性输出Z1、非线性输出A1 
    Z1 = np.dot(W1, X) + b1 	#线性函数wx+b
    A1 = np.tanh(Z1)	 	#非线性函数/非线性映射tanh
    # 计算输出层:线性输出Z2、非线性输出A2(模型预测值) 
    Z2 = np.dot(W2, A1) + b2   	#线性函数wx+b
    A2 = 1 / (1 + np.exp(-Z2)) 	#非线性函数/非线性映射sigmoid

    assert A2.shape == (1, X.shape[1]) #预测值A2的形状为(1,样本数),X.shape为(特征列数,样本数)

    cache = {"Z1": Z1,
             "A1": A1,
             "Z2": Z2,
             "A2": A2}

    return A2, cache

def calculate_cost(A2, Y, parameters):
    """
    根据第四章给出的公式计算成本
    Args:
        A2: 模型输出值
        Y: 真实值
        parameters: 一个python字典包含参数 W1, b1, W2和b2
    Return:
        cost: 成本函数
    """
    m = Y.shape[1]  # 样本个数,Y.shape为(特征列数,标签数),标签数即等于样本个数

    # 计算成本,使用交叉熵损失函数multi_binary_label_cross_entropy_cost
    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)  # 确保维度的正确性。squeeze可以对矩阵和list进行压缩/降维。
    assert isinstance(cost, float)

    return cost

def backward_propagation(parameters, cache, X, Y):
    """
    后向传播
    Args:
        parameters: 一个python字典,包含所有参数
        cache: 一个python字典包含"Z1", "A1", "Z2"和"A2".
        X: 输入值
        Y: 真实值
    Return:
        grads: 一个pyth字典包含所有参数的梯度dW1,db1,dW2,db2
    """
    m = X.shape[1] #m为样本数,X.shape为(特征列数,样本数)

    # 首先从"parameters"获取W1、W2,即 每层的权重W
    W1 = parameters["W1"] #第一层隐藏层权重 
    W2 = parameters["W2"] #第二层输出层权重

    # 从"cache"中获取A1、A2,即每层的输出A
    A1 = cache["A1"] #第一层隐藏层的非线性输出A1 
    A2 = cache["A2"] #第二层输出层的非线性输出A2(模型预测值) 

    """
    反向传播:计算参数W的梯度dW1、dW2和参数b的梯度db1、db2,根据链式法则+梯度下降的向量化实现理论得出如下
	1.第二层输出层:
		1.dZ2=A2-Y。模型预测值A2 减去 真实值Y
		2.dW2=dot(dZ2,A1.T)/m。dot为点乘运算。
		3.db2=sum(dZ2)/m。dZ2的总和除以样本数m
	2.第一层隐藏层:
		1.dZ1=dot(W2.T,dZ2)*(1-A1的平方)。dot为点乘运算。
		2.dW1=dot(dZ1.T,X.T)/m。
		3.db1=sum(dZ1)/m。dZ1的总和除以样本数m
    """
    # 后向传播: 计算参数W的梯度dW1、dW2和参数b的梯度db1、db2
    dZ2 = A2 - Y #dZ2=A2-Y
    dW2 = 1. / m * np.dot(dZ2, A1.T) #dW2=dot(dZ2,A1.T)/m 
    db2 = 1. / m * np.sum(dZ2, axis=1, keepdims=True) #db2=sum(dZ2)/m

    dZ1 = np.dot(W2.T, dZ2) * (1 - np.power(A1, 2)) #dZ1=dot(W2.T,dZ2)*(1-A1的平方) 
    dW1 = 1. / m * np.dot(dZ1, X.T) #dW1=dot(dZ1.T,X.T)/m
    db1 = 1. / m * np.sum(dZ1, axis=1, keepdims=True) #db1=sum(dZ1)/m

    grads = {"dW1": dW1,
             "db1": db1,
             "dW2": dW2,
             "db2": db2}

    return grads


def update_parameters(parameters, grads, learning_rate=1.2):
    """
    使用梯度更新参数
    Args:
        parameters: 包含所有参数的python字典
        grads: 包含所有参数梯度的python字典
        learning_rate: 学习步长
    Return:
        parameters: 包含更新后参数的python
    """
    # 从"parameters"中读取全部参数
    W1 = parameters["W1"]#第一层隐藏层权重
    b1 = parameters["b1"]#第一层隐藏层偏置
    W2 = parameters["W2"]#第二层输出层权重
    b2 = parameters["b2"]#第二层输出层偏置
 
    # 从"grads"中读取全部梯度
    dW1 = grads["dW1"]#第一层隐藏层权重的梯度值
    db1 = grads["db1"]#第一层隐藏层偏置的梯度值
    dW2 = grads["dW2"]#第二层输出层权重的梯度值
    db2 = grads["db2"]#第二层输出层偏置的梯度值

    # 更新参数
    W1 = W1 - learning_rate * dW1
    b1 = b1 - learning_rate * db1
    W2 = W2 - learning_rate * dW2
    b2 = b2 - learning_rate * db2

    parameters = {"W1": W1,
                  "b1": b1,
                  "W2": W2,
                  "b2": b2}

    return parameters

def train(X, Y, n_h, num_iterations=10000, print_cost=False):
    """
    定义神经网络模型,把之前的操作合并到一起
    Args:
        X: 输入值
        Y: 真实值
        n_h: 隐藏层大小/节点数
        num_iterations: 训练次数
        print_cost: 设置为True,则每1000次训练打印一次成本函数值
    Return:
        parameters: 模型训练所得参数,用于预测
    """

    np.random.seed(3) # 设置随机种子
    
    n_x = layer_sizes(X, Y)[0] #n_x输入层节点数
    n_y = layer_sizes(X, Y)[2] #n_y输出层节点数

    #n_x输入层节点数, n_h隐藏层节点数, n_y输出层节点数
    # 根据n_x, n_h, n_y初始化参数,并取出W1,b1,W2,b2
    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):
        # 前向传播, 输入: "X, parameters". 输出: "A2模型预测值, cache".
        A2, cache = forward_propagation(X, parameters)

        # 成本计算. 输入: "A2模型预测值, Y真实值, parameters". 输出: "cost".
        cost = calculate_cost(A2, Y, parameters)

        # 后向传播, 输入: "parameters, cache, X, Y". 输出: "grads".
        grads = backward_propagation(parameters, cache, X, Y)

        # 参数更新. 输入: "parameters, grads". 输出: "parameters".
        parameters = update_parameters(parameters, grads)

        # 每1000次训练打印一次成本函数值
        if print_cost and i % 1000 == 0:
            print "Cost after iteration %i: %f" % (i, cost)

    #返回训练好的参数
    return parameters

def predict(parameters, X):
    """
    使用训练所得参数,对每个训练样本进行预测
    Args:
        parameters: 存有参数的python字典
        X: 输入值
    Return:
        predictions: 模型预测值向量(红色: 0 / 蓝色: 1)
    """

    # 使用训练好所得的参数进行前向传播计算,并将模型输出值转化为预测值(大于0.5视作1,即True)
    A2, cache = forward_propagation(X, parameters)
    predictions = A2 > 0.5 #多个预测值中,有预测值大于0.5视作1,即True
    return predictions

import matplotlib.pyplot as plt
import numpy as np

def plot_decision_boundary(model, X, Y):
    """
    把数据和分界展示在图上
    Args:
        model: 模型
        X: 数据集的X
        Y: 数据集的标签
    Return:
    """
    # Set min and max values and give it some padding
    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
    # Generate a grid of points with distance h between them
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
    # Predict the function value for the whole grid
    Z = model(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    # Plot the contour and training examples
    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)
 
def main():
    """
    程序入口
    """
    # 加载数据:train_X训练集数据(2,320),train_Y训练标签集数据(1,320),test_X测试集数据(2,320),test_Y测试标签集数据(1,400)
    train_X, train_Y, test_X, test_Y = utils.load_data_sets()

    print "1. show the data set"
    shape_X = train_X.shape
    shape_Y = train_Y.shape
    print ('The shape of X is: ' + str(shape_X))
    print ('The shape of Y is: ' + str(shape_Y))

    plt.scatter(test_X.T[:, 0], test_X.T[:, 1], c=test_Y, s=40, cmap=plt.cm.Spectral)
    plt.title("show the data set")
    plt.show()

    print "2. begin to training"
    # 训练模型
    parameters = train(train_X, train_Y, n_h=10, num_iterations=10000, print_cost=True)
    # 预测训练集
    predictions = predict(parameters, train_X)
    # 输出准确率
    print('Train Accuracy: %d' % float((np.dot(train_Y, predictions.T) + np.dot(1 - train_Y, 1 - predictions.T)) / float(train_Y.size) * 100) + '%')
    # 预测测试集
    predictions = predict(parameters, test_X)
    print('Test Accuracy: %d' % float((np.dot(test_Y, predictions.T) + np.dot(1 - test_Y, 1 - predictions.T)) / float(test_Y.size) * 100) + '%')

    #把数据和分界展示在图上
    print "3. output the division"
    utils.plot_decision_boundary(lambda x: predict(parameters, x.T), train_X, train_Y)
    plt.title("Decision Boundary for hidden layer size " + str(4))
    plt.show()
 
if __name__ == '__main__':
    main()

"""
    使用paddle框架实现浅层神经网络识别“花,型图案,关键步骤如下:
    1.载入数据和预处理:load_data()
    2.定义train()和test()用于读取训练数据和测试数据,分别返回一个reader
    3.初始化
    4.配置网络结构和设置参数:
        - 定义成本函数cost
        - 创建parameters
        - 定义优化器optimizer
    5.定义event_handler
    6.定义trainer
    7.开始训练
    8.预测infer()并输出准确率train_accuracy和test_accuracy
    9.展示学习曲线plot_costs()
"""

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
import paddle.v2 as paddle
import utils

TRAINING_SET = None #包含训练集和标签集
TEST_SET = None	#包含测试集和标签集
DATA_DIM = None   #一个样本的特征数(列数),即一个样本的特征维度
 
def load_data():
    """
    载入数据,数据项包括:
        train_set_x:原始训练数据集
        train_set_y:原始训练数据标签
        test_set_x:原始测试数据集
        test_set_y:原始测试数据标签
    """
    global TRAINING_SET, TEST_SET, DATA_DIM

    train_set_x, train_set_y, test_set_x, test_set_y = utils.load_data_sets()

    # 定义纬度,即一个样本的特征数(列数),即一个样本的特征维度
    DATA_DIM = 2

    # 使用numpy.hstack实现numpy数组的横向合并。
    # np.hstack把两个矩阵进行堆叠组合在一起,默认在asix=0的列上进行堆叠,即增加一列,相当于把标签集作为一列增加到训练集中的最后一列的后面。
    TRAINING_SET = np.hstack((train_set_x.T, train_set_y.T))
    TEST_SET = np.hstack((test_set_x.T, test_set_y.T))


def read_data(data_set):
    """
    读取训练数据或测试数据,服务于train()和test()
    Args:
        data_set: 要获取的数据集
    Return:
        reader: 用于获取训练数据集及其标签的生成器generator
    """

    def reader():
        """
        一个reader
        Args:
        Return:
            data[:-1], data[-1:] -- 使用yield返回生成器(generator),
            data[:-1]表示前n-1个元素,也就是训练数据,data[-1:]表示最后一个元素,也就是对应的标签
        """
        for data in data_set:
            yield data[:-1], data[-1:]
    return reader


def train():
    """
    定义一个reader来获取训练数据集及其标签
    Args:
    Return:
        read_data: 用于获取训练数据集及其标签的reader
    """
    global TRAINING_SET
    return read_data(TRAINING_SET)


def test():
    """
    定义一个reader来获取测试数据集及其标签
    Return:
        read_data: 用于获取测试数据集及其标签的reader
    """
    global TEST_SET
    return read_data(TEST_SET)

def get_data(data_creator):
    """
    使用参数data_creator来获取测试数据
    Args:
        data_creator: 数据来源,可以是train()或者test()
    Return:
        result: 包含测试数据(image)和标签(label)的python字典
    """
    data_creator = data_creator
    data_image = []
    data_label = []

    for item in data_creator():
        data_image.append((item[0],))
        data_label.append(item[1])

    result = {
        "image": data_image,
        "label": data_label
    }

    return result

def network_config():
    """
    配置网络结构和设置参数
    Return:
        image: 输入层,DATADIM维稠密向量
        y_predict: 输出层,Sigmoid作为激活函数
        y_label: 标签数据,1维稠密向量
        cost: 损失函数
        parameters: 模型参数
        optimizer: 优化器
        feeding: 数据映射,python字典
    """
    # 输入层,paddle.layer.data表示数据层,name=’image’:名称为image,
    # type=paddle.data_type.dense_vector(DATA_DIM):数据类型为DATA_DIM维稠密向量,每个样本的特征维度,即(2,1)中的2行均为特征值
    image = paddle.layer.data(name='image', type=paddle.data_type.dense_vector(DATA_DIM))

    # 隐藏层,paddle.layer.fc表示全连接层,input=image: 该层输入数据为image
    # size=4:神经元个数,act=paddle.activation.Tanh():激活函数为Tanh()
    hidden_layer_1 = paddle.layer.fc(input=image, size=4, act=paddle.activation.Tanh())

    # 输出层,paddle.layer.fc表示全连接层,input=h1: 该层输入数据为h1
    # size=1:神经元个数,act=paddle.activation.Sigmoid():激活函数为Sigmoid()
    y_predict = paddle.layer.fc(input=hidden_layer_1, size=1, act=paddle.activation.Sigmoid())

    # 标签数据,paddle.layer.data表示数据层,name=’label’:名称为label
    # type=paddle.data_type.dense_vector(1):数据类型为1维稠密向量,每个样本的标签维度,即(1,1)中的1行为标签值
    y_label = paddle.layer.data(name='label', type=paddle.data_type.dense_vector(1))

    # 定义成本函数为交叉熵损失函数multi_binary_label_cross_entropy_cost
    cost = paddle.layer.multi_binary_label_cross_entropy_cost(input=y_predict, label=y_label)

    # 利用cost创建parameters
    parameters = paddle.parameters.create(cost)

    # 创建optimizer,并初始化momentum和learning_rate
    # 加入动量项Momentum,可以加速更新过程,比如当接近局部最小时,通过震荡作用,跳出局部最小继续下降到全局最小。
    # 训练过程在更新权重时采用动量优化器 Momentum ,比如momentum=0.9 代表动量优化每次保持前一次速度的0.9倍。	
    optimizer = paddle.optimizer.Momentum(momentum=0, learning_rate=0.0005)

    # 数据层和数组索引映射,用于trainer训练时喂数据
    feeding = {
        'image': 0,
        'label': 1}

    result = [image, y_predict, y_label, cost, parameters, optimizer, feeding]
    return result
def main():
    """
    定义神经网络结构,训练、预测、检验准确率并打印学习曲线
    """
    global DATA_DIM

    # 初始化,设置是否使用gpu,trainer数量表示仅使用一个线程进行训练
    paddle.init(use_gpu=False, trainer_count=1)

    # 载入数据
    load_data()

    # 配置网络结构和设置参数
    image, y_predict, y_label, cost, parameters, optimizer, feeding = network_config()

    # 记录成本cost
    costs = []

    # 处理事件
    def event_handler(event):
        """
        事件处理器,可以根据训练过程的信息作相应操作
        Args: event: 事件对象,包含event.pass_id, event.batch_id, event.cost等信息
        """
        if isinstance(event, paddle.event.EndIteration):
            if event.pass_id % 100 == 0:
                print("Pass %d, Batch %d, Cost %f" %
                      (event.pass_id, event.batch_id, event.cost))
                costs.append(event.cost)
                # with open('params_pass_%d.tar' % event.pass_id, 'w') as f:
                #     parameters.to_tar(f)

    # 构造trainer,SGD定义一个随机梯度下降,配置三个参数cost、parameters、update_equation,它们分别表示成本函数、参数和更新公式。
    trainer = paddle.trainer.SGD(cost=cost, parameters=parameters, update_equation=optimizer)

    """
    模型训练
    paddle.reader.shuffle(train(), buf_size=5000):表示trainer从train()这个reader中读取了buf_size=5000大小的数据并打乱顺序
    paddle.batch(reader(), batch_size=256):表示从打乱的数据中再取出batch_size=256大小的数据进行一次迭代训练
    feeding:用到了之前定义的feeding索引,将数据层image和label输入trainer
    event_handler:事件管理机制,可以自定义event_handler,根据事件信息作相应的操作
    num_passes:定义训练的迭代次数
    """
    trainer.train(
        reader=paddle.batch(
            paddle.reader.shuffle(train(), buf_size=5000),
            batch_size=256),
        feeding=feeding,
        event_handler=event_handler,
        num_passes=10000)

    # 预测
    infer(y_predict, parameters)

    # 展示学习曲线
    plot_costs(costs)
 
if __name__ == '__main__':
    main()

def calc_accuracy(probs, data):
    """
    根据数据集来计算准确度accuracy
    Args:
        probs: 数据集的预测结果,调用paddle.infer()来获取
        data: 数据集
    Return:
        calc_accuracy: 训练准确度
    """
    right = 0
    total = len(data['label'])
    for i in range(len(probs)):
        if float(probs[i][0]) > 0.5 and data['label'][i] == 1:
            right += 1
        elif float(probs[i][0]) < 0.5 and data['label'][i] == 0:
            right += 1
    accuracy = (float(right) / float(total)) * 100
    return accuracy

def infer(y_predict, parameters):
    """
    预测并输出模型准确率
    Args:
        y_predict: 输出层,DATADIM维稠密向量
        parameters: 训练完成的模型参数
    """
    # 获取测试数据和训练数据,用来验证模型准确度
    train_data = get_data(train())
    test_data = get_data(test())

    # 根据train_data和test_data预测结果,output_layer表示输出层,parameters表示模型参数,input表示输入的测试数据
    probs_train = paddle.infer(
        output_layer=y_predict,
        parameters=parameters,
        input=train_data['image']
    )
    probs_test = paddle.infer(
        output_layer=y_predict,
        parameters=parameters,
        input=test_data['image']
    )

    # 计算train_accuracy和test_accuracy
    print "train_accuracy: {} %".format(
        calc_accuracy(probs_train, train_data))
    print "test_accuracy: {} %".format(
        calc_accuracy(probs_test, test_data))
def plot_costs(costs):
    """
    利用costs展示模型的训练曲线
    Args: costs: 记录了训练过程的cost变化的list,每一百次迭代记录一次
    """
    costs = np.squeeze(costs)
    plt.plot(costs)
    plt.ylabel('cost')
    plt.xlabel('iterations (per hundreds)')
    plt.title("Learning rate = 0.0005")
    plt.show()
    plt.savefig('costs.png')


Jupyter文件中的内容

Numpy实现浅层神经网络
	实践部分将搭建神经网络,包含一个隐藏层,实验将会展现出与Logistic回归的不同之处。
	实验将使用两层神经网络实现对“花”型图案的分类,如图所示,图中的点包含红点(y=0)和蓝点(y=1)还有点的坐标信息,实验将通过以下步骤完成对两种点的分类,
	使用Numpy实现。
		输入样本;
		搭建神经网络;
		初始化参数;
		训练,包括前向传播与后向传播(即BP算法);
		得出训练后的参数;
		根据训练所得参数,绘制两类点边界曲线。
	该实验将使用Python原生库实现两层神经网络的搭建,完成分类。

"""
用于载入数据
load_planar_dataset()函数返回训练集和测试集
"""
import matplotlib.pyplot as plt
import numpy as np
import random
import sklearn
import sklearn.datasets
import sklearn.linear_model

#绘制分类结果边界
def plot_decision_boundary(model, X, y):
    """
    绘制分类边界
    model: 模型
    X: 输入值
    y: 真实值
    """
    # Set min and max values and give it some padding
    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
    # Generate a grid of points with distance h between them
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
    # Predict the function value for the whole grid
    Z = model(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    # Plot the contour and training examples
    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)
    
#定义Sigmoid函数
def sigmoid(x):
    """
    Compute the sigmoid of x
    Arguments:
    x -- A scalar or numpy array of any size.
    Return:
    s -- sigmoid(x)
    """
    # Sigmoid计算
    s = 1 / (1 + np.exp(-x))
    return s

#加载数据
def load_planar_dataset():
    """
    加载数据
    返回值:
        train_x:训练集的输入
        train_y:训练集的真实值
        X:测试集的输入
        Y:测试集的真实值
    """
    np.random.seed(1) #设置随机种子
    m = 400 # number of examples
    N = int(m / 2) # number of points per class
    D = 2 # dimensionality
    X = np.zeros((m, D)) # data matrix where each row is a single example
    Y = np.zeros((m, 1), dtype='uint8') # labels vector (0 for red, 1 for blue)
    a = 4 # maximum ray of the flower

    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 # radius
        X[ix] = np.c_[r * np.sin(t), r * np.cos(t)]
        Y[ix] = j

    #测试集,取全部数据
    X = X.T
    Y = Y.T

    #训练集
    train_num = random.sample(range(400),320)#共400组数据,训练集取其中80%,即320组
    train_x = X[: , train_num]
    train_y = Y[: , train_num]

    dataset = [train_x, train_y, X, Y]
    return dataset


#加载其他数据
def load_extra_datasets():
    """
    加载其它数据
    return:
        result:包含5种不同类型数据的集合
    """
    N = 200
    noisy_circles = sklearn.datasets.make_circles(n_samples=N, factor=.5, noise=.3)
    noisy_moons = sklearn.datasets.make_moons(n_samples=N, noise=.2)
    blobs = sklearn.datasets.make_blobs(n_samples=N, random_state=5, n_features=2, centers=6)
    gaussian_quantiles = sklearn.datasets.make_gaussian_quantiles(mean=None, cov=0.5, n_samples=N, n_features=2, n_classes=2, shuffle=True, random_state=None)
    no_structure = np.random.rand(N, 2), np.random.rand(N, 2)
    result = [noisy_circles, noisy_moons, blobs, gaussian_quantiles, no_structure]
    return result
1.引用库
	首先,载入几个需要用到的库,它们分别是:
		numpy:一个python的基本库,用于科学计算
		planar_utils:定义了一些工具函数
		matplotlib.pyplot:用于生成图,在验证模型准确率和展示成本变化趋势时会使用到
		sklearn:用于数据挖掘和数据分析
 
		import numpy as np
		import sklearn
		from planar_utils import plot_decision_boundary, sigmoid, load_planar_dataset, load_extra_datasets
		import matplotlib.pyplot as plt
		%matplotlib inline
		np.random.seed(1) 

2.载入数据并观察纬度
	载入数据后,输出维度
		#载入数据
		train_x, train_y, test_x, test_y = load_planar_dataset()
		#输出维度
		shape_X = train_x.shape
		shape_Y = train_y.shape
		print ('The shape of X is: ' + str(shape_X))
		print ('The shape of Y is: ' + str(shape_Y))
		#The shape of X is: (2, 320)
		#The shape of Y is: (1, 320)
		由输出可知每组输入坐标X包含两个值,Y包含一个值,共320组数据(测试集在训练集基础上增加80组数据,共400组)。

3.简单逻辑回归实验
	使用逻辑回归处理该数据,观察分类结果
		#训练逻辑回归分类器
		clf = sklearn.linear_model.LogisticRegressionCV();
		clf.fit(train_x.T, train_y.T);
		#绘制逻辑回归分类边界
		plot_decision_boundary(lambda x: clf.predict(x), train_x, train_y)
		plt.title("Logistic Regression")
		#输出训练集的预测值
		LR_predictions = clf.predict(train_x.T)
		#输出准确率
		print ('Accuracy of logistic regression:%d ' % float((np.dot(train_y, LR_predictions) + 
			   np.dot(1-train_y, 1-LR_predictions)) / float(train_y.size)*100) +
			   '% ' + "(percentage of correctly labelled datapoints)")
		#Accuracy of logistic regression:57 % (percentage of correctly labelled datapoints)

	可以看出逻辑回归效果并不好,这是因为逻辑回归网络结构只包含输入层和输出层,无法拟合更为复杂的模型,下面尝试神经网络模型。

4.神经网络模型
	下面开始搭建神经网络模型,我们采用两层神经网络实验,隐藏层包含4个节点,使用tanh激活函数;输出层包含一个节点,使用Sigmoid激活函数,
	结果小于0.5即认为是0,否则认为是1。

	1.神经网络结构
		下面用代码实现神经网络结构,首先确定神经网络的结构,即获取相关数据维度,并设置隐藏层节点个数(本实验设置4个隐藏层节点),用以初始化参数。

			#定义各层规模函数
			def layer_sizes(X, Y):
				"""
				参数含义:
					X -- 输入的数据
					Y -- 输出值
				返回值:
					n_x -- 输入层节点数
					n_h -- 隐藏层节点数
					n_y -- 输出层节点数
				"""
				n_x = X.shape[0] #输入层大小(节点数)
				n_h = 4
				n_y = Y.shape[0] #输出层大小(节点数)
				return (n_x, n_h, n_y)

	2.初始化模型参数
		获取相关维度信息后,开始初始化参数,定义相关函数。

			# 定义函数:初始化参数
			def initialize_parameters(n_x, n_h, n_y):
				"""
				参数:
					n_x -- 输入层大小
					n_h -- 隐藏层大小
					n_y -- 输出层大小
				返回值:
					params -- 一个包含所有参数的python字典:
						W1 -- (隐藏层)权重,维度是 (n_h, n_x)
						b1 -- (隐藏层)偏移量,维度是 (n_h, 1)
						W2 -- (输出层)权重,维度是 (n_y, n_h)
						b2 -- (输出层)偏移量,维度是 (n_y, 1)
				"""
				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.前向传播与后向传播
		获取输入数据,参数初始化完成后,可以开始前向传播的计算。
			# 定义函数:前向传播
			def forward_propagation(X, parameters):
				"""
				参数:
					X -- 输入值 
					parameters -- 一个python字典,包含计算所需全部参数(是initialize_parameters函数的输出)    
				返回值:
					A2 -- 模型输出值
					cache -- 一个字典,包含 "Z1", "A1", "Z2" and "A2"
				"""
				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

	4.前向传播最后可得出模型输出值(即代码中的A2),即可计算成本函数cost。
			# 定义函数:成本函数
			def compute_cost(A2, Y, parameters):
				"""
			   	根据第三章给出的公式计算成本
				参数:
					A2 -- 模型输出值    
					Y -- 真实值
					parameters -- 一个python字典包含参数 W1, b1, W2和b2
				返回值: cost -- 成本函数
				"""
				m = Y.shape[1] #样本个数
				#计算成本
				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

	5.计算了成本函数,可以开始后向传播的计算。
			# 定义函数:后向传播
			def backward_propagation(parameters, cache, X, Y):
				"""
				参数:
					parameters -- 一个python字典,包含所有参数 
					cache -- 一个python字典包含"Z1", "A1", "Z2"和"A2".
					X -- 输入值
					Y -- 真实值
				返回值: grads -- 一个python字典包含所有参数的梯度
				"""
				m = X.shape[1]
			  
				#首先从"parameters" 获取 W1,W2
				W1 = parameters["W1"]
				W2 = parameters["W2"]
					
				# 从"cache"中获取A1,A2
				A1 = cache["A1"]
				A2 = cache["A2"]
				
				#后向传播: 计算dW1, db1, dW2, db2. 
				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.power(A1, 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

	6.通过后向传播获取梯度后,可以根据梯度下降公式更新参数。
		def update_parameters(parameters, grads, learning_rate = 1.2):
			"""
			使用梯度更新参数
			参数:
				parameters -- 包含所有参数的python字典 
				grads -- 包含所有参数梯度的python字典 
			返回值:
				parameters -- 包含更新后参数的python 
			"""
			#从"parameters"中读取全部参数
			W1 = parameters["W1"]
			b1 = parameters["b1"]
			W2 = parameters["W2"]
			b2 = parameters["b2"]
			
			# 从"grads"中读取全部梯度
			dW1 = grads["dW1"]
			db1 = grads["db1"]
			dW2 = grads["dW2"]
			db2 = grads["db2"]
			
			#更新参数
			W1 = W1 - learning_rate * dW1
			b1 = b1 - learning_rate * db1
			W2 = W2 - learning_rate * dW2
			b2 = b2 - learning_rate * db2
			
			parameters = {"W1": W1,  "b1": b1, "W2": W2, "b2": b2}
			return parameters
	
	7.神经网络模型
		前向传播、成本函数计算和后向传播构成一个完整的神经网络,将上述函数组合,构建一个神经网络模型。
			#定义函数:神经网络模型
			def nn_model(X, Y, n_h, num_iterations = 10000, print_cost=False):
				"""
				参数:
					X -- 输入值
					Y -- 真实值
					n_h -- 隐藏层大小/节点数
					num_iterations -- 训练次数
					print_cost -- 设置为True,则每1000次训练打印一次成本函数值
				返回值: parameters -- 训练结束,更新后的参数值    
				"""
				np.random.seed(3)
				n_x = layer_sizes(X, Y)[0]
				n_y = layer_sizes(X, Y)[2]
				
				#根据n_x, n_h, n_y初始化参数,并取出W1,b1,W2,b2   
				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):      
					#前向传播, 输入: "X, parameters". 输出: "A2, cache".
					A2, cache = forward_propagation(X, parameters)
					
					#成本计算. 输入: "A2, Y, parameters". 输出: "cost".
					cost = compute_cost(A2, Y, parameters)
			 
					#后向传播, 输入: "parameters, cache, X, Y". 输出: "grads".
					grads = backward_propagation(parameters, cache, X, Y)
			 
					#参数更新. 输入: "parameters, grads". 输出: "parameters".
					parameters = update_parameters(parameters, grads)
					
					#每1000次训练打印一次成本函数值
					if print_cost and i % 1000 == 0:
						print ("Cost after iteration %i: %f" %(i, cost))

				return parameters

	8.预测
		通过上述模型可以训练得出最后的参数,此时需检测其准确率,用训练后的参数预测训练的输出,大于0.5的值视作1,否则视作0。
			#定义函数:预测
			def predict(parameters, X):
				"""
				使用训练所得参数,对每个训练样本进行预测
				参数:
					parameters -- 保安所有参数的python字典 
					X -- 输入值
				返回值:
					predictions -- 模型预测值向量(红色: 0 / 蓝色: 1)
				"""
				#使用训练所得参数进行前向传播计算,并将模型输出值转化为预测值(大于0.5视作1,即True)
				A2, cache = forward_propagation(X, parameters)
				predictions = A2 > 0.5
				return predictions

	9.下面对获取的数据进行训练,并输出准确率
		# 建立神经网络模型
		parameters = nn_model(train_x, train_y, n_h = 4, num_iterations = 10000, print_cost=True)
		# 绘制分类边界
		plot_decision_boundary(lambda x: predict(parameters, x.T), train_x, train_y)
		plt.title("Decision Boundary for hidden layer size " + str(4))
		# 预测训练集
		predictions = predict(parameters, train_x)
		print('Train Accuracy: %d' % float((np.dot(train_y, predictions.T) + np.dot(1 - train_y, 1 - predictions.T)) / float(train_y.size) * 100) + '%')
		# 预测测试集
		predictions = predict(parameters, test_x)
		print('Test Accuracy: %d' % float((np.dot(test_y, predictions.T) + np.dot(1 - test_y, 1 - predictions.T)) / float(test_y.size) * 100) + '%')

		对比逻辑回归47%的准确率和分类结果图,神经网络分类的结果提高了不少,这是因为神经网络增加的隐藏层,
		为模型训练提供了更多选择,使得神经网络能拟合更加复杂的模型,对于更加复杂的图案分类更加准确。

		#Cost after iteration 0: 0.693049
		#Cost after iteration 1000: 0.272304
		#Cost after iteration 2000: 0.261430
		#Cost after iteration 3000: 0.260627
		#Cost after iteration 4000: 0.257842
		#Cost after iteration 5000: 0.255809
		#Cost after iteration 6000: 0.254249
		#Cost after iteration 7000: 0.253019
		#Cost after iteration 8000: 0.252028
		#Cost after iteration 9000: 0.251208
		#Train Accuracy: 90%
		#Test Accuracy: 89%

Paddlepaddle实现浅层神经网络
	实践部分将搭建神经网络,包含一个隐藏层,实验将会展现出与Logistic回归的不同之处。
	实验将使用两层神经网络实现对“花”型图案的分类,如图所示,图中的点包含红点(y=0)和蓝点(y=1)还有点的坐标信息,实验将通过以下步骤完成对两种点的分类,
	使用PaddlePaddle实现。
		输入样本;
		搭建神经网络;
		初始化参数;
		训练,包括前向传播与后向传播(即BP算法);
		得出训练后的参数;
		根据训练所得参数,绘制两类点边界曲线。

import numpy as np
import h5py
 
def load_dataset():
    train_dataset = h5py.File('datasets/train_catvnoncat.h5', "r")
    train_set_x_orig = np.array(train_dataset["train_set_x"][:]) # your train set features
    train_set_y_orig = np.array(train_dataset["train_set_y"][:]) # your train set labels

    test_dataset = h5py.File('datasets/test_catvnoncat.h5', "r")
    test_set_x_orig = np.array(test_dataset["test_set_x"][:]) # your test set features
    test_set_y_orig = np.array(test_dataset["test_set_y"][:]) # your test set labels

    classes = np.array(test_dataset["list_classes"][:]) # the list of classes
    
    train_set_y_orig = train_set_y_orig.reshape((1, train_set_y_orig.shape[0]))
    test_set_y_orig = test_set_y_orig.reshape((1, test_set_y_orig.shape[0]))
    
    return train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes
 
"""
用于载入数据
load_planar_dataset()函数返回训练集和测试集
"""
import matplotlib.pyplot as plt
import numpy as np
import random
import sklearn
import sklearn.datasets
import sklearn.linear_model

#绘制分类结果边界
def plot_decision_boundary(model, X, y):
    """
    绘制分类边界
    model: 模型
    X: 输入值
    y: 真实值
    """
    # Set min and max values and give it some padding
    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
    # Generate a grid of points with distance h between them
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
    # Predict the function value for the whole grid
    Z = model(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    # Plot the contour and training examples
    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)
    
#定义Sigmoid函数
def sigmoid(x):
    """
    Compute the sigmoid of x
    Arguments:
    x -- A scalar or numpy array of any size.
    Return:
    s -- sigmoid(x)
    """
    # Sigmoid计算
    s = 1 / (1 + np.exp(-x))
    return s

#加载数据
def load_planar_dataset():
    """
    加载数据
    返回值:
        train_x:训练集的输入
        train_y:训练集的真实值
        X:测试集的输入
        Y:测试集的真实值
    """
    np.random.seed(1) #设置随机种子
    m = 400 # number of examples
    N = int(m / 2) # number of points per class
    D = 2 # dimensionality
    X = np.zeros((m, D)) # data matrix where each row is a single example
    Y = np.zeros((m, 1), dtype='uint8') # labels vector (0 for red, 1 for blue)
    a = 4 # maximum ray of the flower

    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 # radius
        X[ix] = np.c_[r * np.sin(t), r * np.cos(t)]
        Y[ix] = j

    #测试集,取全部数据
    X = X.T
    Y = Y.T

    #训练集
    train_num = random.sample(range(400),320)#共400组数据,训练集取其中80%,即320组
    train_x = X[: , train_num]
    train_y = Y[: , train_num]

    dataset = [train_x, train_y, X, Y]
    return dataset


#加载其他数据
def load_extra_datasets():
    """
    加载其它数据
    return:
        result:包含5种不同类型数据的集合
    """
    N = 200
    noisy_circles = sklearn.datasets.make_circles(n_samples=N, factor=.5, noise=.3)
    noisy_moons = sklearn.datasets.make_moons(n_samples=N, noise=.2)
    blobs = sklearn.datasets.make_blobs(n_samples=N, random_state=5, n_features=2, centers=6)
    gaussian_quantiles = sklearn.datasets.make_gaussian_quantiles(mean=None, cov=0.5, n_samples=N, n_features=2, n_classes=2, shuffle=True, random_state=None)
    no_structure = np.random.rand(N, 2), np.random.rand(N, 2)
    result = [noisy_circles, noisy_moons, blobs, gaussian_quantiles, no_structure]
    return result
在该实验中,我们将使用PaddlePaddle实现浅层神经网络,解决识别猫的问题,使用的数据与“Paddlepaddle实现Logistic回归”中一致。
本章节代码与“PaddlePaddle实现Logistic回归”基本一致,重复内容不再赘述,区别在于增加了一层隐藏层。

1.引用库
	首先,载入几个需要用到的库,它们分别是:
		numpy:一个python的基本库,用于科学计算
		planar_utils:定义了一些工具函数
		paddle.v2:paddle深度学习平台
		matplotlib.pyplot:用于生成图,在验证模型准确率和展示成本变化趋势时会使用到

			import matplotlib
			import numpy as np
			import paddle.v2 as paddle
			import matplotlib.pyplot as plt
			import planar_utils

			TRAINING_SET = None
			TEST_SET = None
			DATADIM = None

2. 数据预处理
	定义load_data()用于载入数据
		# 载入数据
		def load_data():
			global TRAINING_SET, TEST_SET, DATADIM
			train_set_x, train_set_y, test_set_x, test_set_y = planar_utils.load_planar_dataset()
			# 定义纬度,即一个样本的特征维度
			DATADIM = 2
			TRAINING_SET = np.hstack((train_set_x.T, train_set_y.T))
			TEST_SET = np.hstack((test_set_x.T, test_set_y.T))

3.构造reader
	构造reader()函数来读取训练数据集TRAINING_SET和测试数据集TEST_SET,需要注意的是,yield关键字的作用类似return关键字,
	但不同指出在于yield关键字让reader()变成一个生成器(Generator),生成器不会创建完整的数据集列表,而是在每次循环时计算下一个值,
	这样不仅节省内存空间,而且符合reader的定义,也即一个真正的读取器。

		# 读取训练数据或测试数据,服务于train()和test()
		def read_data(data_set):
			"""
				一个reader
				Args: data_set -- 要获取的数据集
				Return: reader -- 用于获取训练数据集及其标签的生成器generator
			"""
			def reader():
				"""
				一个reader
				Return: data[:-1], data[-1:] -- 使用yield返回生成器(generator),
				        data[:-1]表示前n-1个元素,也就是训练数据,data[-1:]表示最后一个元素,也就是对应的标签
				"""
				for data in data_set:
					yield data[:-1], data[-1:]
			return reader


		# 获取训练数据集
		def train():
			"""
			定义一个reader来获取训练数据集及其标签
			Return: read_data -- 用于获取训练数据集及其标签的reader
			"""
			global TRAINING_SET
			return read_data(TRAINING_SET)


		# 获取测试数据集
		def test():
			"""
			定义一个reader来获取测试数据集及其标签
			Return: read_data -- 用于获取测试数据集及其标签的reader
			"""
			global TEST_SET
			return read_data(TEST_SET)

4.配置网络结构和设置参数
	开始配置网络结构,这是本章与Logistic回归的不同之处,本章节实现双层神经网络,增加了一层隐藏层,隐藏层设置7个节点,激活函数使用Relu,其余不变。
	1.损失函数
		在这里使用PaddlePaddle提供的交叉熵损失函数,cost = paddle.layer.multi_binary_label_cross_entropy_cost(input=y_predict, label=y_label) 
		定义了成本函数,并使用y_predict与label计算成本。定义了成本函数之后,使用PaddlePaddle提供的简单接口parameters=paddle.parameters.create(cost)
		来创建和初始化参数。
	2.optimizer
		参数创建完成后,定义参数优化器optimizer= paddle.optimizer.Momentum(momentum=0, learning_rate=0.00002),使用Momentum作为优化器,
		并设置动量momentum为零,学习率为0.00002。注意,读者暂时无需了解Momentum的含义,只需要学会使用即可。
	3.其它配置 
		feeding={‘image’:0, ‘label’:1}是数据层名称和数组索引的映射,用于在训练时输入数据。

			# 配置网络结构和设置参数
			def netconfig():
				"""
				配置网络结构和设置参数
				Return:
					image -- 输入层,DATADIM维稠密向量,每个样本的特征维度
					y_predict -- 输出层,Sigmoid作为激活函数
					y_label -- 标签数据,1维稠密向量
					cost -- 损失函数
					parameters -- 模型参数
					optimizer -- 优化器
					feeding -- 数据映射,python字典
				"""
				# 输入层,paddle.layer.data表示数据层,name=’image’:名称为image,
				# type=paddle.data_type.dense_vector(DATADIM):数据类型为DATADIM维稠密向量,每个样本的特征维度
				image = paddle.layer.data(name='image', type=paddle.data_type.dense_vector(DATADIM))

				# 隐藏层,paddle.layer.fc表示全连接层,input=image: 该层输入数据为image
				# size=4:神经元个数,act=paddle.activation.Tanh():激活函数为Tanh()
				h1 = paddle.layer.fc(input=image, size=4, act=paddle.activation.Tanh())

				# 输出层,paddle.layer.fc表示全连接层,input=h1: 该层输入数据为h1
				# size=1:神经元个数,act=paddle.activation.Sigmoid():激活函数为Sigmoid()
				y_predict = paddle.layer.fc(input=h1, size=1, act=paddle.activation.Sigmoid())

				# 标签数据,paddle.layer.data表示数据层,name=’label’:名称为label
				# type=paddle.data_type.dense_vector(1):数据类型为1维稠密向量,每个样本的标签维度
				y_label = paddle.layer.data(name='label', type=paddle.data_type.dense_vector(1))

				# 定义成本函数为 交叉熵损失函数multi_binary_label_cross_entropy_cost
				cost = paddle.layer.multi_binary_label_cross_entropy_cost(input=y_predict, label=y_label)

				# 利用cost创建parameters
				parameters = paddle.parameters.create(cost)

				# 创建optimizer,并初始化momentum和learning_rate
				# 加入动量项Momentum,可以加速更新过程,比如当接近局部最小时,通过震荡作用,跳出局部最小继续下降到全局最小。
				# 训练过程在更新权重时采用动量优化器 Momentum ,比如momentum=0.9 代表动量优化每次保持前一次速度的0.9倍。
				optimizer = paddle.optimizer.Momentum(momentum=0, learning_rate=0.0075)

				# 数据层和数组索引映射,用于trainer训练时喂数据
				feeding = {
					'image': 0,
					'label': 1}
				data = [image, y_predict, y_label, cost, parameters, optimizer, feeding]
				return data

5.训练过程
	接下来进入训练过程。
	1.初始化
		首先进行最基本的初始化操作,paddle.init(use_gpu=False, trainer_count=1)表示不使用gpu进行训练并且仅使用一个trainer进行训练,
		load_data()用于获取并预处理数据。
			# 初始化
			paddle.init(use_gpu=False, trainer_count=1)
			# 获取数据并预处理
			load_data()
			# 配置网络结构和设置参数
			image, y_predict, y_label, cost, parameters, optimizer, feeding = netconfig()
			# 记录成本cost
			costs = []

	2.模型训练
		上述内容进行了初始化并配置了网络结构,接下来利用上述配置进行模型训练。
		首先定义一个随机梯度下降trainer,配置三个参数cost、parameters、update_equation,它们分别表示成本函数、参数和更新公式。
		再利用trainer.train()即可开始真正的模型训练:
			paddle.reader.shuffle(train(), buf_size=5000)表示trainer从train()这个reader中读取了buf_size=5000大小的数据并打乱顺序
			paddle.batch(reader(), batch_size=256)表示从打乱的数据中再取出batch_size=256大小的数据进行一次迭代训练
			参数feeding用到了之前定义的feeding索引,将数据层image和label输入trainer,也就是训练数据的来源。
			参数event_handler是事件管理机制,读者可以自定义event_handler,根据事件信息作相应的操作。
			参数num_passes=5000表示迭代训练5000次后停止训练。

			def event_handler(event):
					"""
					事件处理器,可以根据训练过程的信息作相应操作
					Args: event -- 事件对象,包含event.pass_id, event.batch_id, event.cost等信息
					"""
				if isinstance(event, paddle.event.EndIteration):
					if event.pass_id % 100 == 0:
						print("Pass %d, Batch %d, Cost %f" % (event.pass_id, event.batch_id, event.cost))
						costs.append(event.cost)
				         	#with open('params_pass_%d.tar' % event.pass_id, 'w') as f:
				         	#	parameters.to_tar(f)

				# 构造trainer,SGD定义随机梯度下降,配置三个参数cost、parameters、update_equation,它们分别表示成本函数、参数和更新公式。
				trainer = paddle.trainer.SGD(cost=cost, parameters=parameters, update_equation=optimizer)

				"""
				模型训练
				paddle.reader.shuffle(train(), buf_size=5000):表示trainer从train()这个reader中读取了buf_size=5000大小的数据并打乱顺序
				paddle.batch(reader(), batch_size=256):表示从打乱的数据中再取出batch_size=256大小的数据进行一次迭代训练
				feeding:用到了之前定义的feeding索引,将数据层image和label输入trainer
				event_handler:事件管理机制,可以自定义event_handler,根据事件信息作相应的操作
				num_passes:定义训练的迭代次数
				"""
				trainer.train(
					reader=paddle.batch(
						paddle.reader.shuffle(train(), buf_size=5000),
						batch_size=256),
					feeding=feeding,
					event_handler=event_handler,
					num_passes=10000)

				Pass 0, Batch 0, Cost 0.772777
				Pass 0, Batch 1, Cost 0.684858
				Pass 100, Batch 0, Cost 0.299152
				Pass 100, Batch 1, Cost 0.335288
				Pass 200, Batch 0, Cost 0.320332
				Pass 200, Batch 1, Cost 0.187563
				Pass 300, Batch 0, Cost 0.306633
 				。。。。。。。
				Pass 9900, Batch 0, Cost 0.212364
				Pass 9900, Batch 1, Cost 0.202101

	3.模型检验
		1.模型训练完成后,接下来检验模型的准确率。
		  首先定义get_data()函数来帮助我们读取训练数据和测试数据。
			# 获取data
			def get_data(data_creator):
				"""
				使用参数data_creator来获取测试数据
				Args: data_creator -- 数据来源,可以是train()或者test()
				Return: result -- 包含测试数据(image)和标签(label)的python字典
				"""
				data_creator = data_creator
				data_image = []
				data_label = []

				for item in data_creator():
					data_image.append((item[0],))
					data_label.append(item[1])

				result = {
					"image": data_image,
					"label": data_label
				}
				return result

		2.获得数据之后,我们就可以开始利用paddle.infer()来进行预测,参数output_layer表示输出层,参数parameters表示模型参数,参数input表示输入的测试数据。
			# 获取测试数据和训练数据,用来验证模型准确度
			train_data = get_data(train())
			test_data = get_data(test())
				
			# 根据train_data和test_data预测结果,output_layer表示输出层,parameters表示模型参数,input表示输入的测试数据
			probs_train = paddle.infer(output_layer=y_predict, parameters=parameters, input=train_data['image'])
			probs_test = paddle.infer(output_layer=y_predict, parameters=parameters, input=test_data['image'])

		3.获得检测结果probs_train和probs_test之后,我们将结果转化为二分类结果并计算预测正确的结果数量,定义calc_accuracy()分别计算训练准确度和测试准确度。
			# 计算准确度
			def calc_accuracy(probs, data):
				"""
				根据数据集来计算准确度accuracy
				Args:
					probs -- 数据集的预测结果,调用paddle.infer()来获取
					data -- 数据集
				Return: calc_accuracy -- 训练准确度
				"""
				right = 0
				total = len(data['label'])
				for i in range(len(probs)):
					if float(probs[i][0]) > 0.5 and data['label'][i] == 1:
						right += 1
					elif float(probs[i][0]) < 0.5 and data['label'][i] == 0:
						right += 1
				accuracy = (float(right) / float(total)) * 100
				return accuracy

		4.调用上述两个函数并输出
			# 计算train_accuracy和test_accuracy
			print("train_accuracy: {} %".format(calc_accuracy(probs_train, train_data)))
			print("test_accuracy: {} %".format(calc_accuracy(probs_test, test_data)))
			#train_accuracy: 90.625 %
			#test_accuracy: 90.75 %

		5.学习曲线
			可以输出成本的变化情况,利用学习曲线对模型进行分析。
			读者可以看到图中成本在刚开始收敛较快,随着迭代次数变多,收敛速度变慢,最终收敛到一个较小值。
				costs = np.squeeze(costs)
				plt.plot(costs)
				plt.ylabel('cost')
				plt.xlabel('iterations (per hundreds)')
				plt.title("Learning rate = 0.0075")
				plt.show()

发布了225 篇原创文章 · 获赞 111 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/zimiao552147572/article/details/104084574