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


    1. 导包

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

    2. 配置参数

    1. class Config:
    2. nn_input_dim = 2
    3. nn_output_dim = 2
    4. epsilon = 0.01
    5. reg_lambda = 0.01

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

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

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

    epsilon: 梯度下降的学习率。

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

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

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

    3. 生成数据

    1. def generate_data():
    2. np.random.seed(0)
    3. X, y = datasets.make_moons(200, noise=0.20)
    4. 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. 可视化

    1. def visualize(X, y, model):
    2. # plt.scatter(X[:, 0], X[:, 1], s=40, c=y, cmap=plt.cm.Spectral)
    3. # plt.show()
    4. plot_decision_boundary(lambda x:predict(model,x), X, y)
    5. def plot_decision_boundary(pred_func, X, y):
    6. x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
    7. y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
    8. h = 0.01
    9. xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
    10. Z = pred_func(np.c_[xx.ravel(), yy.ravel()])
    11. Z = Z.reshape(xx.shape)
    12. plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)
    13. plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Spectral)
    14. plt.title("Neural Network")
    15. 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. 计算损失

    1. def calculate_loss(model, X, y):
    2. num_examples = len(X) # training set size
    3. W1, b1, W2, b2 = model['W1'], model['b1'], model['W2'], model['b2']
    4. # Forward propagation to calculate our predictions
    5. z1 = X.dot(W1) + b1
    6. a1 = np.tanh(z1)
    7. z2 = a1.dot(W2) + b2
    8. exp_scores = np.exp(z2)
    9. probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
    10. # Calculating the loss
    11. corect_logprobs = -np.log(probs[range(num_examples), y])
    12. data_loss = np.sum(corect_logprobs)
    13. # Add regulatization term to loss (optional)
    14. data_loss += Config.reg_lambda / 2 * (np.sum(np.square(W1)) + np.sum(np.square(W2)))
    15. 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. 预测

    1. def predict(model, x):
    2. W1, b1, W2, b2 = model['W1'], model['b1'], model['W2'], model['b2']
    3. # Forward propagation
    4. z1 = x.dot(W1) + b1
    5. a1 = np.tanh(z1)
    6. z2 = a1.dot(W2) + b2
    7. exp_scores = np.exp(z2)
    8. probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
    9. 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. 训练模型

     

    1. def build_model(X, y, nn_hdim, num_passes=20000, print_loss=False):
    2. # Initialize the parameters to random values. We need to learn these.
    3. num_examples = len(X)
    4. np.random.seed(0)
    5. W1 = np.random.randn(Config.nn_input_dim, nn_hdim) / np.sqrt(Config.nn_input_dim)
    6. b1 = np.zeros((1, nn_hdim))
    7. W2 = np.random.randn(nn_hdim, Config.nn_output_dim) / np.sqrt(nn_hdim)
    8. b2 = np.zeros((1, Config.nn_output_dim))
    9. # This is what we return at the end
    10. model = {}
    11. # Gradient descent. For each batch...
    12. for i in range(0, num_passes):
    13. # Forward propagation
    14. z1 = X.dot(W1) + b1
    15. a1 = np.tanh(z1)
    16. z2 = a1.dot(W2) + b2
    17. exp_scores = np.exp(z2)
    18. probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
    19. # Backpropagation will be teached in the next lession
    20. delta3 = probs
    21. delta3[range(num_examples), y] -= 1
    22. dW2 = (a1.T).dot(delta3)
    23. db2 = np.sum(delta3, axis=0, keepdims=True)
    24. delta2 = delta3.dot(W2.T) * (1 - np.power(a1, 2))
    25. dW1 = np.dot(X.T, delta2)
    26. db1 = np.sum(delta2, axis=0)
    27. # Add regularization terms (b1 and b2 don't have regularization terms)
    28. dW2 += Config.reg_lambda * W2
    29. dW1 += Config.reg_lambda * W1
    30. # Gradient descent parameter update
    31. W1 += -Config.epsilon * dW1
    32. b1 += -Config.epsilon * db1
    33. W2 += -Config.epsilon * dW2
    34. b2 += -Config.epsilon * db2
    35. # Assign new parameters to the model
    36. model = {'W1': W1, 'b1': b1, 'W2': W2, 'b2': b2}
    37. # Optionally print the loss.
    38. # This is expensive because it uses the whole dataset, so we don't want to do it too often.
    39. if print_loss and i % 1000 == 0:
    40. print("Loss after iteration %i: %f" % (i, calculate_loss(model, X, y)))
    41. return model

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

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

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

    更新这些参数。

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

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

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

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

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

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

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

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

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

    8. 实验结果

    1. def main():
    2. X, y = generate_data()
    3. model = build_model(X, y, 3, print_loss=True)
    4. visualize(X, y, model)
    5. if __name__ == "__main__":
    6. main()

     

     

  • 相关阅读:
    SpringBoot整合Neo4j
    算法通关村第十六关:黄金挑战:滑动窗口与堆结合
    java的io流详解
    python学习思路
    基于微信小程序的火锅店点餐订餐系统设计与实现(源码+lw+部署文档+讲解等)
    数据结构与算法-----指针与结构体
    力扣(LeetCode)32. 最长有效括号(C++)
    快排&超详细,Leetcode排序数组题目带你升华掌握
    14、Elasticsearch开发搜索功能
    openresty 性能优化
  • 原文地址:https://blog.csdn.net/weixin_43961909/article/details/133812258