从零开始的神经网络

news2024/9/23 3:25:13

先决条件

在本文中,我将解释如何通过实现前向和后向传递(反向传播)来构建基本的深度神经网络。这需要一些关于神经网络功能的具体知识。

了解线性代数的基础知识也很重要,这样才能理解我为什么要在本文中执行某些运算。我最好的建议是看 3Blue1Brown 的系列线性代数的本质.

NumPy

在本文中,我构建了一个包含 4 层的基本深度神经网络:1 个输入层、2 个隐藏层和 1 个输出层。所有层都是完全连接的。我正在尝试使用一个名为MNIST.该数据集由 70,000 张图像组成,每张图像的尺寸为 28 x 28 像素。数据集包含每个图像的一个标签,该标签指定我在每张图像中看到的数字。我说有 10 个类,因为我有 10 个标签。

MNIST data set

MNIST 数据集中的 10 个数字示例,放大 2 倍

为了训练神经网络,我使用随机梯度下降,这意味着我一次将一张图像通过神经网络。

让我们尝试以精确的方式定义图层。为了能够对数字进行分类,在运行神经网络后,您必须最终获得属于某个类别的图像的概率,因为这样您就可以量化神经网络的性能。

  1. 输入层:在此层中,我输入由 28x28 图像组成的数据集。我将这些图像展平成一个包含 28×28=78428×28=784 个元素的数组。这意味着输入层将有 784 个节点。

  2. 隐藏层 1:在此层中,我将输入层中的节点数从 784 个减少到 128 个节点。当你在神经网络中前进时,这会带来一个挑战(我稍后会解释这一点)。

  3. 隐藏层 2:在这一层中,我决定从第一个隐藏层的 128 个节点开始使用 64 个节点。这并不是什么新挑战,因为我已经减少了第一层的数量。

  4. 输出层:在这一层中,我将 64 个节点减少到总共 10 个节点,以便我可以根据标签评估节点。此标签以包含 10 个元素的数组的形式接收,其中一个元素为 1,其余元素为 0。

您可能意识到,每层中的节点数从 784 个节点减少到 128 个节点,从 64 个节点减少到 10 个节点。这是基于经验观察这会产生更好的结果,因为我们既没有过度拟合,也没有过度拟合,只是试图获得正确数量的节点。本文选择的具体节点数是随机选择的,但为了避免过度拟合,节点数量会减少。在大多数现实生活中,您可能希望通过蛮力或良好的猜测(通常是通过网格搜索或随机搜索)来优化这些参数,但这超出了本文的讨论范围。

Nodes diagram

导入和数据集

对于整个 NumPy 部分,我特别想分享使用的导入。请注意,我使用 NumPy 以外的库来更轻松地加载数据集,但它们不用于任何实际的神经网络。

from sklearn.datasets import fetch_openml
from keras.utils.np_utils import to_categorical
import numpy as np
from sklearn.model_selection import train_test_split
import time

现在,我必须加载数据集并对其进行预处理,以便可以在 NumPy 中使用它。我通过将所有图像除以 255 来进行归一化,并使所有图像的值都在 0 - 1 之间,因为这消除了以后激活函数的一些数值稳定性问题。我使用独热编码标签,因为我可以更轻松地从神经网络的输出中减去这些标签。我还选择将输入加载为 28 * 28 = 784 个元素的扁平化数组,因为这是输入层所需要的。

x, y = fetch_openml('mnist_784', version=1, return_X_y=True)
x = (x/255).astype('float32')
y = to_categorical(y)
​
x_train, x_val, y_train, y_val = train_test_split(x, y, test_size=0.15, random_state=42)

初始化

神经网络中权重的初始化有点难考虑。要真正理解以下方法的工作原理和原因,您需要掌握线性代数,特别是使用点积运算时的维数。

尝试实现前馈神经网络时出现的具体问题是,我们试图从 784 个节点转换为 10 个节点。在实例化类时,我传入一个大小数组,该数组定义了每一层的激活次数。DeepNeuralNetwork

dnn = DeepNeuralNetwork(sizes=[784, 128, 64, 10])

这将通过函数初始化类。

DeepNeuralNetwork``init

def __init__(self, sizes, epochs=10, l_rate=0.001):
    self.sizes = sizes
    self.epochs = epochs
    self.l_rate = l_rate
​
    # we save all parameters in the neural network in this dictionary
    self.params = self.initialization()
 

让我们看看在调用函数时大小如何影响神经网络的参数。我正在准备“可点”m x n 矩阵,以便我可以进行前向传递,同时随着层的增加减少激活次数。我只能对两个矩阵 M1 和 M2 使用点积运算,其中 M1 中的 m 等于 M2 中的 n,或者 M1 中的 n 等于 M2 中的 minitialization()

通过这个解释,您可以看到我用 m=128m=128 和 n=784n=784 初始化了第一组权重 W1,而接下来的权重 W2 是 m=64m=64 和 n=128n=128。如前所述,输入层 A0 中的激活次数等于 784,当我将激活 A0 点在 W1 上时,操作成功。

def initialization(self):
    # number of nodes in each layer
    input_layer=self.sizes[0]
    hidden_1=self.sizes[1]
    hidden_2=self.sizes[2]
    output_layer=self.sizes[3]
​
    params = {
        'W1':np.random.randn(hidden_1, input_layer) * np.sqrt(1. / hidden_1),
        'W2':np.random.randn(hidden_2, hidden_1) * np.sqrt(1. / hidden_2),
        'W3':np.random.randn(output_layer, hidden_2) * np.sqrt(1. / output_layer)
    }
​
    return params

前馈

前向传递由 NumPy 中的点运算组成,结果证明它只是矩阵乘法。我必须将权重乘以前一层的激活。然后,我必须将激活函数应用于结果。

为了完成每一层,我依次应用点运算,然后应用 sigmoid 激活函数。在最后一层中,我使用激活函数,因为我想拥有每个类的概率,以便我可以测量当前前向传递的性能。softmax

注意:我选择了该函数的数值稳定版本。您可以从斯坦福大学的课程中内容softmaxCS231n.

def initialization(self):
    # number of nodes in each layer
    input_layer=self.sizes[0]
    hidden_1=self.sizes[1]
    hidden_2=self.sizes[2]
    output_layer=self.sizes[3]
​
    params = {
        'W1':np.random.randn(hidden_1, input_layer) * np.sqrt(1. / hidden_1),
        'W2':np.random.randn(hidden_2, hidden_1) * np.sqrt(1. / hidden_2),
        'W3':np.random.randn(output_layer, hidden_2) * np.sqrt(1. / output_layer)
    }
​
    return params

以下代码显示了本文中使用的激活函数。可以看出,我提供了 sigmoid 的衍生版本,因为稍后通过神经网络反向传播时需要它。

def sigmoid(self, x, derivative=False):
    if derivative:
        return (np.exp(-x))/((np.exp(-x)+1)**2)
    return 1/(1 + np.exp(-x))
​
def softmax(self, x):
    # Numerically stable with large exponentials
    exps = np.exp(x - x.max())
    return exps / np.sum(exps, axis=0)

反向传播

向后传递很难正确,因为必须对齐的大小和操作才能使所有操作成功。这是向后传递的完整功能。我在下面介绍每个重量更新。

def backward_pass(self, y_train, output):
    '''
        This is the backpropagation algorithm, for calculating the updates
        of the neural network's parameters.
    '''
    params = self.params
    change_w = {}
​
    # Calculate W3 update
    error = output - y_train
    change_w['W3'] = np.dot(error, params['A3'])
​
    # Calculate W2 update
    error = np.multiply( np.dot(params['W3'].T, error), self.sigmoid(params['Z2'], derivative=True) )
    change_w['W2'] = np.dot(error, params['A2'])
​
    # Calculate W1 update
    error = np.multiply( np.dot(params['W2'].T, error), self.sigmoid(params['Z1'], derivative=True) )
    change_w['W1'] = np.dot(error, params['A1'])
​
    return change_w
W3 更新

的更新可以通过减去具有从称为 的前向传递的输出中调用的标签的真值数组来计算。此操作成功,因为是 10 并且也是 10。下面的代码可能是一个示例,其中 1 对应于 .W3``y_train``output``len(y_train)``len(output)``y_train``output

y_train = np.array([0, 0, 1, 0, 0, 0, 0, 0, 0, 0])

下面的代码显示了一个示例,其中数字是对应于 的类的概率。output``y_train

output = np.array([0.2, 0.2, 0.5, 0.3, 0.6, 0.4, 0.2, 0.1, 0.3, 0.7])

如果减去它们,则得到以下结果。

>>> output - y_train
array([ 0.2,  0.2, -0.5,  0.3,  0.6,  0.4,  0.2,  0.1,  0.3,  0.7])

下一个操作是点操作,它将错误(我刚刚计算的)与最后一层的激活点在一起。

error = output - y_train
change_w['W3'] = np.dot(error, params['A3'])
W2 更新

接下来是更新权重。为了成功,需要涉及更多的操作。首先,形状略有不匹配,因为具有形状和具有,即完全相同的尺寸。因此,我可以使用W2``W3``(10, 64)``error``(10, 64)转置操作这样数组的维度就会置换,并且形状现在会对齐以进行点操作。W3``.T

Transpose

转置操作的示例。左:原始矩阵。右:置换矩阵

W3`现在有 shape 和 has shape ,它们与点运算兼容。结果是`(64, 10)``error``(10, 64)`[逐个元素相乘](https://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html)(也称为 Hadamard 积)与 的 sigmoid 函数的导数的结果。最后,我将错误与上一层的激活点在一起。`Z2
error = np.multiply( np.dot(params['W3'].T, error), self.sigmoid(params['Z2'], derivative=True) )
change_w['W2'] = np.dot(error, params['A2'])
W1 更新

同样,用于更新的代码提前一步使用神经网络的参数。除其他参数外,代码等同于 W2 更新。W1

error = np.multiply( np.dot(params['W2'].T, error), self.sigmoid(params['Z1'], derivative=True) )
change_w['W1'] = np.dot(error, params['A1'])

训练(随机梯度下降)

我已经定义了向前和向后传递,但如何开始使用它们?我必须创建一个训练循环,并使用随机梯度下降 (SGD) 作为优化器来更新神经网络的参数。训练函数中有两个主要循环。一个循环表示 epoch 数,即我遍历整个数据集的次数,第二个循环用于逐个遍历每个观察值。

对于每个观测值,我都会使用 进行前向传递,这是数组中长度为 784 的一张图像,如前所述。前向传递的 与 一起使用,后者是后向传递中的独热编码标签(真实值)。这给了我一本关于神经网络中权重更新的字典。x``output``y

def train(self, x_train, y_train, x_val, y_val):
    start_time = time.time()
    for iteration in range(self.epochs):
        for x,y in zip(x_train, y_train):
            output = self.forward_pass(x)
            changes_to_w = self.backward_pass(y, output)
            self.update_network_parameters(changes_to_w)
​
        accuracy = self.compute_accuracy(x_val, y_val)
        print('Epoch: {0}, Time Spent: {1:.2f}s, Accuracy: {2}'.format(
            iteration+1, time.time() - start_time, accuracy
        ))

该函数具有 SGD 更新规则的代码,该规则只需要权重的梯度作为输入。需要明确的是,SGD 涉及使用来自后向传递的反向传播来计算梯度,而不仅仅是更新参数。它们似乎是分开的,应该分开考虑,因为这两种算法是不同的。update_network_parameters()

def update_network_parameters(self, changes_to_w):
    '''
        Update network parameters according to update rule from
        Stochastic Gradient Descent.
​
        θ = θ - η * ∇J(x, y),
            theta θ:            a network parameter (e.g. a weight w)
            eta η:              the learning rate
            gradient ∇J(x, y):  the gradient of the objective function,
                                i.e. the change for a specific theta θ
    '''
​
    for key, value in changes_to_w.items():
        for w_arr in self.params[key]:
            w_arr -= self.l_rate * value

在更新了神经网络的参数后,我可以测量我之前准备的验证集的准确性,以验证网络在整个数据集上的每次迭代后的性能。

以下代码使用一些与训练函数相同的部分。首先,它进行前向传递,然后找到网络的预测,并检查与标签是否相等。之后,我将预测结果相加并除以 100 以找到准确性。接下来,我平均每个类的准确性。

def compute_accuracy(self, x_val, y_val):
    '''
        This function does a forward pass of x, then checks if the indices
        of the maximum value in the output equals the indices in the label
        y. Then it sums over each prediction and calculates the accuracy.
    '''
    predictions = []
​
    for x, y in zip(x_val, y_val):
        output = self.forward_pass(x)
        pred = np.argmax(output)
        predictions.append(pred == y)
​
    summed = sum(pred for pred in predictions) / 100.0
    return np.average(summed)

最后,在知道会发生什么之后,我可以调用训练函数。我使用训练和验证数据作为训练函数的输入,然后等待。

dnn.train(x_train, y_train, x_val, y_val)

请注意,结果可能会有很大差异,具体取决于权重的初始化方式。我的结果准确率为0%-95%。

以下是概述所发生情况的完整代码。

from sklearn.datasets import fetch_openml
from keras.utils.np_utils import to_categorical
import numpy as np
from sklearn.model_selection import train_test_split
import time
​
x, y = fetch_openml('mnist_784', version=1, return_X_y=True)
x = (x/255).astype('float32')
y = to_categorical(y)
​
x_train, x_val, y_train, y_val = train_test_split(x, y, test_size=0.15, random_state=42)
​
class DeepNeuralNetwork():
    def __init__(self, sizes, epochs=10, l_rate=0.001):
        self.sizes = sizes
        self.epochs = epochs
        self.l_rate = l_rate
​
        # we save all parameters in the neural network in this dictionary
        self.params = self.initialization()
​
    def sigmoid(self, x, derivative=False):
        if derivative:
            return (np.exp(-x))/((np.exp(-x)+1)**2)
        return 1/(1 + np.exp(-x))
​
    def softmax(self, x):
        # Numerically stable with large exponentials
        exps = np.exp(x - x.max())
        return exps / np.sum(exps, axis=0)
​
    def initialization(self):
        # number of nodes in each layer
        input_layer=self.sizes[0]
        hidden_1=self.sizes[1]
        hidden_2=self.sizes[2]
        output_layer=self.sizes[3]
​
        params = {
            'W1':np.random.randn(hidden_1, input_layer) * np.sqrt(1. / hidden_1),
            'W2':np.random.randn(hidden_2, hidden_1) * np.sqrt(1. / hidden_2),
            'W3':np.random.randn(output_layer, hidden_2) * np.sqrt(1. / output_layer)
        }
​
        return params
​
    def forward_pass(self, x_train):
        params = self.params
​
        # input layer activations becomes sample
        params['A0'] = x_train
​
        # input layer to hidden layer 1
        params['Z1'] = np.dot(params["W1"], params['A0'])
        params['A1'] = self.sigmoid(params['Z1'])
​
        # hidden layer 1 to hidden layer 2
        params['Z2'] = np.dot(params["W2"], params['A1'])
        params['A2'] = self.sigmoid(params['Z2'])
​
        # hidden layer 2 to output layer
        params['Z3'] = np.dot(params["W3"], params['A2'])
        params['A3'] = self.softmax(params['Z3'])
​
        return params['A3']
​
    def backward_pass(self, y_train, output):
        '''
            This is the backpropagation algorithm, for calculating the updates
            of the neural network's parameters.
​
            Note: There is a stability issue that causes warnings. This is
                  caused  by the dot and multiply operations on the huge arrays.
​
                  RuntimeWarning: invalid value encountered in true_divide
                  RuntimeWarning: overflow encountered in exp
                  RuntimeWarning: overflow encountered in square
        '''
        params = self.params
        change_w = {}
​
        # Calculate W3 update
        error = output - y_train
        change_w['W3'] = np.dot(error, params['A3'])
​
        # Calculate W2 update
        error = np.multiply( np.dot(params['W3'].T, error), self.sigmoid(params['Z2'], derivative=True) )
        change_w['W2'] = np.dot(error, params['A2'])
​
        # Calculate W1 update
        error = np.multiply( np.dot(params['W2'].T, error), self.sigmoid(params['Z1'], derivative=True) )
        change_w['W1'] = np.dot(error, params['A1'])
​
        return change_w
​
    def update_network_parameters(self, changes_to_w):
        '''
            Update network parameters according to update rule from
            Stochastic Gradient Descent.
​
            θ = θ - η * ∇J(x, y),
                theta θ:            a network parameter (e.g. a weight w)
                eta η:              the learning rate
                gradient ∇J(x, y):  the gradient of the objective function,
                                    i.e. the change for a specific theta θ
        '''
​
        for key, value in changes_to_w.items():
            for w_arr in self.params[key]:
                w_arr -= self.l_rate * value
​
    def compute_accuracy(self, x_val, y_val):
        '''
            This function does a forward pass of x, then checks if the indices
            of the maximum value in the output equals the indices in the label
            y. Then it sums over each prediction and calculates the accuracy.
        '''
        predictions = []
​
        for x, y in zip(x_val, y_val):
            output = self.forward_pass(x)
            pred = np.argmax(output)
            predictions.append(pred == y)
​
        summed = sum(pred for pred in predictions) / 100.0
        return np.average(summed)
​
    def train(self, x_train, y_train, x_val, y_val):
        start_time = time.time()
        for iteration in range(self.epochs):
            for x,y in zip(x_train, y_train):
                output = self.forward_pass(x)
                changes_to_w = self.backward_pass(y, output)
                self.update_network_parameters(changes_to_w)
​
            accuracy = self.compute_accuracy(x_val, y_val)
            print('Epoch: {0}, Time Spent: {1:.2f}s, Accuracy: {2}'.format(
                iteration+1, time.time() - start_time, accuracy
            ))
​
dnn = DeepNeuralNetwork(sizes=[784, 128, 64, 10])
dnn.train(x_train, y_train, x_val, y_val)

NumPy 中的良好练习

您可能已经注意到,该代码的可读性很强,但它占用了大量空间,可以优化为循环运行。这是一个优化和改进它的机会。如果你是这个主题的新手,以下练习的难度很容易很难,最后一个练习是最难的。

  1. 简单:实现 ReLU 激活函数或任何其他激活函数。检查 sigmoid 函数的实现方式以供参考,并记住实现导数。使用 ReLU 激活函数代替 sigmoid 函数。

  2. 简单:初始化偏差并将它们添加到前向传递中的激活函数之前的 Z,并在后向传递中更新它们。当您尝试添加偏差时,请注意数组的维度。

  3. 中:优化前向和后向传递,使它们在每个函数中循环运行。这使得代码更易于修改,并且可能更易于维护。for

    • 优化为神经网络进行权重的初始化函数,以便您可以在不使神经网络失败的情况下修改参数。sizes=[]

  4. 中:实现小批量梯度下降,取代随机梯度下降。不要更新每个样品的参数,而是根据小批量中每个样品累积的梯度总和的平均值进行更新。小批量的大小通常在 64 以下。

  5. 困难:实现 Adam 优化器。这应该在训练功能中实现。

    1. 通过添加额外的术语来实现 Momentum

    2. 基于 AdaGrad 优化器实现自适应学习率

    3. 结合步骤 1 和 2 来实现 Adam

我的信念是,如果你完成这些练习,你就会有一个良好的基础。下一步是实现卷积、滤波器等,但这留待以后的文章使用。

结论

本文介绍了如何在没有框架帮助的情况下构建神经网络的基础知识,这些框架可能使其更易于使用。我构建了一个具有 4 层的基本深度神经网络,并解释了如何通过实现前向和后向传递(反向传播)来构建基本的深度神经网络。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1325095.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【FLV】文件解析源码分析:视频解析为可解码的nalu单元

https flv 拉到的数据是flv宏观看 : 每一部分都是 A+ Prev 的模式 A 可以是header :9个字节可以是TAG :大小可变而每个TAG 都有个固定的部分: TAG HEADER ,大小9个字节 ,里面是类型、大小、时间戳、扩展时间戳、流ID 因此,可以直接去掉9+4 个字节的第一部分:FLV HEADER…

凯斯西储大学轴承数据解读

文章目录 一、凯斯西储大学轴承数据基础知识?1.1 故障种类1.2 故障点尺寸(单点故障)1.3 载荷和转速 二、数据解读2.1 文件2.2 以12k Drive End Bearing Fault Data为例2.3 以(0.007,inner race)为例。 3 Normal Baseli…

vivado 关于时钟

关于时钟 在数字设计中,时钟代表了从寄存器可靠传输数据的时间基准注册。AMD Vivado™集成设计环境(IDE)计时引擎使用时钟计算时序路径要求并通过以下方式报告设计时序裕度的特性松弛计算的方法有关更多信息,请参阅Vivado Design…

杰发科技AC7840——在Eclipse环境下使用Jlink调试

序 杰发给的代码里面已经做代码相关配置,搭建好eclipse环境即可运行,搭建步骤还是比较简单的。 参考文章 如何使用Eclipse搭配JLink来调试HelloWold应用程序?-电子发烧友网 软件链接 杰发科技Eclipse的sample代码里面的doc文章&#xff…

C# .Net学习笔记—— Expression 表达式目录树

一、什么是表达式目录树 (1)Expression我们称为是表达式树,是一种数据结构体,用于存储需要计算,运算的一种结构,这种结构可以只是存储,而不进行运算。通常表达式目录树是配合Lambda一起来使用的…

关于“Python”的核心知识点整理大全32

目录 12.6.4 调整飞船的速度 settings.py ship.py alien_invasion.py 12.6.5 限制飞船的活动范围 ship.py 12.6.6 重构 check_events() game_functions.py 12.7 简单回顾 12.7.1 alien_invasion.py 12.7.2 settings.py 12.7.3 game_functions.py 12.7.4 ship.py …

Hive入门+部署

看黑马视频做的笔记 目录 概念 1.基本概述 2.基础架构 总架构 部署 1.安装MySQL 2.配置Hadoop 3.下载解压Hive 4.下载MySQL Driver包 注意! 5.配置Hive 6.初始化元数据库 7.启动Hive(使用Hadoop用户) 实例 查看HDFS上表中存…

网络基础【网线的制作、OSI七层模型、集线器、交换机介绍、路由器的配置】

目录 一.网线的制作 1.1.网线的标准 1.2.水晶头的做法 二.OSI七层模型、集线器、交换机介绍 集线器(Hub): 交换机(Switch): 三.路由器的配置 3.1.使用 3.2.常用的功能介绍 1、如何管理路由器 2、家…

CW32单片机在智能马桶的应用介绍

智能科技的迅速发展使得我们的日常生活变得更加便捷和舒适。智能马桶作为其中一种智能家居产品,通过单片机接受和处理来自传感器的数据,然后通过控制模块对智能马桶的各项功能进行控制,实现对智能马桶的全面控制和调节。本文将介绍CW32单片机…

苹果发布iOS 17.2.1版本更新

12月20日,苹果向iPhone用户推送了iOS 17.2.1更新。苹果公司在更新日志中称:“本更新包含了重要的错误修复,并解决了某些情况下电池电量较预期更快耗尽的问题。” 据报道,iOS 17系统在发布初期便出现了一系列问题,如发…

PSP - 结构生物学中的机器学习 (NIPS MLSB Workshop 2023.12)

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/135120094 Machine Learning in Structural Biology (机器学习在结构生物学中) 网址:https://www.mlsb.io/ Workshop at the 37th Co…

神经科学与计算神经科学的蓬勃发展与未来趋势

导言 神经科学和计算神经科学是当前科学研究领域中备受关注的方向。本文将深入研究这两个领域的发展历程、遇到的问题、解决过程,以及未来的可用范围。我们还将关注在各国的应用现状以及未来的研究趋势,探讨如何在竞争中取胜,以及在哪些方面发…

大数据处理与分析

掌握分布式并行编程框架MapReduce掌握基于内存的分布式计算框架Spark理解MapReduce的工作流程、Spark运行原理熟悉机器学习概念 一.MapReduce Hadoop MapReduce是一个软件框架,基于该框架能够容易地编写应用程序,这些应用程序能够运行在由上千个商用机器…

机器学习数据的清洗,转化,汇总及建模完整步骤(基于Titanic数据集)

目录 介绍: 一、数据 二、检查数据缺失 三、数据分析 四、数据清洗 五、数据类别转化 六、数据汇总和整理 七、建模 介绍: 线性回归是一种常用的机器学习方法,用于建立一个输入变量与输出变量之间线性关系的预测模型。线性回归的目标…

vs code创建工程,以koa框架为例

以下内容为本人的学习笔记,如需要转载,请声明原文链接 微信公众号「englyf」https://mp.weixin.qq.com/s/x2OXMTaLlxb_Os7NDHrKsg 这里以应用 koa 框架写一个 http 服务器为例,来说明怎么用 vs code 创建工程。 进入 vs code 后&#xff0c…

云服务器 nginx自启动、mysql自启动、pyhton后端自启动

nginx自启动 方法一: 1、建立启动文件 vim /usr/lib/systemd/system/nginx.service [Unit] Descriptionnginx - high performance web server Afternetwork.target remote-fs.target nss-lookup.target[Service] Typeforking ExecStart/usr/local/nginx/sbin/ng…

DMA实验3-外设到内存搬运

实验要求 使用 DMA 的方式将串口接收缓存寄存器的值搬运到内存中,同时闪烁 LED1 。 CubeMX 配置 DMA 配置: 串口中断配置 代码实现 如何判断串口接收是否完成?如何知道串口收到数据的长度? 使用串口空闲中断(IDL…

2023优秀开源项目获选榜名单(开放原子开源基金会)|JeecgBoot 成功入选

JeecgBoot 是一个开源的企业级低代码开发平台,它成功入选2023年度生态开源项目,这是对其十年坚持开源的认可。作为一个开源项目,JeecgBoot 在过去的十年里一直秉承着开放、共享、协作的理念,不断推动着开源社区的发展。 2023年开放…

华为配置IPv4静态路由与静态BFD联动示例

组网需求 如图1所示,SwitchA通过SwitchB和NMS跨网段相连。在SwitchA上通过静态路由与NMS进行正常通信。在SwitchA和SwitchB之间实现毫秒级故障感知,提高收敛速度 配置思路 采用如下思路配置IPv4静态路由与静态BFD联动: 在SwitchA和SwitchB上…

LVS+keepalived小白都看得懂也不来看?

1 高可用集群 1.1 一个合格的集群应该具备的特性 1.负载均衡 LVS Nginx HAProxy F5 2.健康检查(使得调度器检查节点状态是否可以正常运行,调度器(负载均衡器)也要做健康检查)for调度器/节点服务器 keeplived hearb…