机器学习---构建和训练一个简单的二分类神经网络模型并对结果进行可视化

1. 导包

import numpy as np
from sklearn import datasets, linear_model
import matplotlib.pyplot as plt

2. 配置参数

class Config:
    nn_input_dim = 2  
    nn_output_dim = 2  
    epsilon = 0.01  
    reg_lambda = 0.01  

Config 类定义了神经网络的一些配置参数。以下是每个参数的解释:

nn_input_dim: 这个参数表示神经网络的输入层的维度。

nn_output_dim: 这个参数表示神经网络的输出层的维度。

epsilon: 梯度下降的学习率。

reg_lambda: 正则化强度。正则化是一种防止过拟合的技术,它通过在损失函数中添加一个额外的

项来惩罚过于复杂的模型。正则化强度决定了这个惩罚项的权重。较大的正则化强度意味着模型更

倾向于简单,较小的正则化强度意味着模型可能更复杂。选择合适的正则化强度同样非常重要。

3. 生成数据

def generate_data():
    np.random.seed(0)
    X, y = datasets.make_moons(200, noise=0.20)
    return X, y

     函数首先设置随机数生成器的种子为0,确保每次运行时生成的数据集都相同。接下来,使用

datasets.make_moons方法生成一个具有两个半月形状(moons)的数据集。这个方法可以生成一

个具有两个半月形状分布的非线性可分的数据集,通常用于测试分类器的性能。

   np.random.seed(0): 设置随机数生成器的种子为0。这样可以确保每次生成的数据集相同,有

助于实验的可重复性。

   datasets.make_moons(200, noise=0.20): 使用make_moons方法生成一个包含200个数据点的

数据集。其中,noise参数设置为0.20,表示在原始的半月形状上添加一定程度的噪声。这会使数

据集的边界更加复杂。

        其中X是一个形状为(200, 2)的数组,代表200个数据点的坐标;y是一个形状为(200,)的数组,

代表每个数据点对应的类别标签(0或1)。

4. 可视化

def visualize(X, y, model):
#     plt.scatter(X[:, 0], X[:, 1], s=40, c=y, cmap=plt.cm.Spectral)
#     plt.show()
    plot_decision_boundary(lambda x:predict(model,x), X, y)

def plot_decision_boundary(pred_func, X, y):
    x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
    y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
    h = 0.01
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
    Z = pred_func(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Spectral)
    plt.title("Neural Network")
    plt.show()

         x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5 和 y_min, y_max =

X[:,1].min() - .5, X[:, 1].max() + .5: 这两行代码确定了x坐标和y坐标的最小值和最大值,并

在这个范围外额外添加了0.5的边距。这样做是为了在绘制决策边界时,可以超出数据点的最小和

最大坐标,使图像更加美观。

    h = 0.01: 这行代码设置了生成网格点的间隔为0.01。

    np.meshgrid 函数生成成了一个在指定范围内、间隔为h的网格点。        

    pred_func 使用预测函数pred_func对网格点进行预测。

    np.c_[xx.ravel(), yy.ravel()]将网格点从网格形式转换为[x_coordinate, 

y_coordinate]的列表形式,然后应用预测函数。

    Z.reshape(xx.shape): 这行代码将预测结果从列表形式转换回网格形式,以便于后面的

绘制操作。

    plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral): 这行代码绘制了决策边界。

plt.contourf是一个用于绘制等高线图的函数,这里用它来表示预测结果的变化趋势,由此可以观

察模型的决策边界。

    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Spectral): 这行代码绘制了数据点。

与之前的visualize函数中相同,X[:, 0]X[:, 1]表示数据点的x和y坐标,c=y表示点的颜色由类

别标签y确定,cmap=plt.cm.Spectral表示使用Spectral颜色映射。

5. 计算损失

def calculate_loss(model, X, y):
    num_examples = len(X)  # training set size
    W1, b1, W2, b2 = model['W1'], model['b1'], model['W2'], model['b2']
    # Forward propagation to calculate our predictions
    z1 = X.dot(W1) + b1
    a1 = np.tanh(z1)
    z2 = a1.dot(W2) + b2
    exp_scores = np.exp(z2)
    probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
    # Calculating the loss
    corect_logprobs = -np.log(probs[range(num_examples), y])
    data_loss = np.sum(corect_logprobs)
    # Add regulatization term to loss (optional)
    data_loss += Config.reg_lambda / 2 * (np.sum(np.square(W1)) + np.sum(np.square(W2)))
    return 1. / num_examples * data_loss

        num_examples = len(X): 计算训练集的大小(数据点的数量)。

   W1, b1, W2, b2 = model['W1'], model['b1'], model['W2'], model['b2']: 从输入的模型

参数中提取权重矩阵W1W2和偏置项b1b2

   z1 = X.dot(W1) + b1 和 a1 = np.tanh(z1): 执行前向传播的第一层。X.dot(W1)执行输入层

到隐藏层的加权求和,然后加上偏置项b1。将结果z1传递给激活函数tanh,得到隐藏层的激活值

a1

   z2 = a1.dot(W2) + b2 和 exp_scores = np.exp(z2): 前向传播的第二层。a1.dot(W2)执行隐

藏层到输出层的加权求和,然后加上偏置项b2。将结果z2传递给softmax激活函数之前,先计算z2

中每个元素的指数值。

   probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True): 将上一步得到的指数

值归一化为概率值。这实际上是应用softmax激活函数的最后一步,使得probs中的每一行和为1。

   corect_logprobs = -np.log(probs[range(num_examples), y]) 和 data_loss =

np.sum(corect_logprobs): 计算模型在每个数据点上产生的交叉熵损失。首先计算每个数据点对应

正确类别的概率的负对数(即,交叉熵损失),然后将这些损失相加,得到整个数据集的总损失。

    data_loss += Config.reg_lambda / 2 * (np.sum(np.square(W1)) + np.sum(np.square(W2))): 添加了一个正则化项。正则化项是可选的,用于防止模型过拟合。这里

采用的是L2正则化,将权重矩阵W1W2的平方和乘以正则化系数Config.reg_lambda,再除以2,

然后加到data_loss中。

          最后,计算平均损失并返回。将总损失除以训练集的大小,得到每个数据点上的平均损失。

6. 预测

def predict(model, x):
    W1, b1, W2, b2 = model['W1'], model['b1'], model['W2'], model['b2']
    # Forward propagation
    z1 = x.dot(W1) + b1
    a1 = np.tanh(z1)
    z2 = a1.dot(W2) + b2
    exp_scores = np.exp(z2)
    probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
    return np.argmax(probs, axis=1)

         从输入的模型参数中提取权重矩阵W1W2和偏置项b1b2

   z1 = x.dot(W1) + b1 和 a1 = np.tanh(z1): 执行前向传播的第一层。x.dot(W1)执行输入层

到隐藏层的加权求和,然后加上偏置项b1。将结果z1传递给激活函数tanh,得到隐藏层激活值a1

   z2 = a1.dot(W2) + b2 和 exp_scores = np.exp(z2): 执行前向传播的第二层。a1.dot(W2)

行隐藏层到输出层的加权求和,然后加上偏置项b2。将结果z2传递给softmax激活函数之前,先计

z2中每个元素的指数值。

   probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True): 这行代码将上一步得

到的指数值归一化为概率值。这实际上是应用softmax激活函数的最后一步,使得probs中的每一行

的和为1。

        最后,这行代码返回每一行概率值中最大值的索引,即预测的类别标签。np.argmax函数在指

定轴(这里是axis=1,即横轴)上找到最大值的索引。

7. 训练模型

 

def build_model(X, y, nn_hdim, num_passes=20000, print_loss=False):
    # Initialize the parameters to random values. We need to learn these.
    num_examples = len(X)
    np.random.seed(0)
    W1 = np.random.randn(Config.nn_input_dim, nn_hdim) / np.sqrt(Config.nn_input_dim)
    b1 = np.zeros((1, nn_hdim))
    W2 = np.random.randn(nn_hdim, Config.nn_output_dim) / np.sqrt(nn_hdim)
    b2 = np.zeros((1, Config.nn_output_dim))

    # This is what we return at the end
    model = {}

    # Gradient descent. For each batch...
    for i in range(0, num_passes):

        # Forward propagation
        z1 = X.dot(W1) + b1
        a1 = np.tanh(z1)
        z2 = a1.dot(W2) + b2
        exp_scores = np.exp(z2)
        probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)

        # Backpropagation  will be teached in the next lession
        delta3 = probs
        delta3[range(num_examples), y] -= 1
        dW2 = (a1.T).dot(delta3)
        db2 = np.sum(delta3, axis=0, keepdims=True)
        delta2 = delta3.dot(W2.T) * (1 - np.power(a1, 2))
        dW1 = np.dot(X.T, delta2)
        db1 = np.sum(delta2, axis=0)

        # Add regularization terms (b1 and b2 don't have regularization terms)
        dW2 += Config.reg_lambda * W2
        dW1 += Config.reg_lambda * W1

        # Gradient descent parameter update
        W1 += -Config.epsilon * dW1
        b1 += -Config.epsilon * db1
        W2 += -Config.epsilon * dW2
        b2 += -Config.epsilon * db2

        # Assign new parameters to the model
        model = {'W1': W1, 'b1': b1, 'W2': W2, 'b2': b2}

        # Optionally print the loss.
        # This is expensive because it uses the whole dataset, so we don't want to do it too often.
        if print_loss and i % 1000 == 0:
            print("Loss after iteration %i: %f" % (i, calculate_loss(model, X, y)))

    return model

        函数开始时,初始化权重矩阵W1W2和偏置项b1b2。权重矩阵采用随机值初始化,除以输

入维度和隐藏层节点数的平方根,以减小初始值规模。偏置项初始化为零。

       创建模型字典:创建一个空字典model,用于存储模型参数。在梯度下降过程中,我们会不断

更新这些参数。

梯度下降:通过num_passes次迭代执行梯度下降算法。在每次迭代中,我们进行以下步骤:

        前向传播:计算输入层到隐藏层和隐藏层到输出层的加权求和,应用激活函数,并计算概

率。反向传播:计算梯度(偏导数),以便更新权重矩阵和偏置项。这里的反向传播算法用于计算

损失函数关于模型参数的梯度。

        添加正则化项:将权重矩阵W1W2的正则化项添加到梯度中(偏置项没有正则化项)。

        梯度下降参数更新:使用梯度更新权重矩阵和偏置项。乘以学习率(Config.epsilon),然

后从当前参数值中减去梯度。将更新后的参数赋值给模型字典。

        若print_loss参数为True,则每1000次迭代打印一次损失值。这个操作相对昂贵,因为它需

要计算整个数据集上的损失值。返回训练好的模型。

8. 实验结果

def main():
    X, y = generate_data()
    model = build_model(X, y, 3, print_loss=True)
    visualize(X, y, model)

if __name__ == "__main__":
    main()

机器学习---构建和训练一个简单的二分类神经网络模型并对结果进行可视化_第1张图片

机器学习---构建和训练一个简单的二分类神经网络模型并对结果进行可视化_第2张图片 

 

你可能感兴趣的:(机器学习,机器学习,人工智能)