神经网络入门

前言

本文主要介绍最基础的神经网络,包括其结构,学习方法, C++ \texttt{C++} C++ 的实现代码。 Python \texttt{Python} Python 的代码可以搜索互联网得到。

前排提示:本人涉及一丁点数学知识。

神经网络的结构

神经网络包括多个层次(Layer)。一般来说神经网络包括三个部分:输入层隐藏层输出层 。输入层和输出层都只有一个,而隐藏层可以有多个。顺序为 输入层,隐藏层,输出层。

每一个层次会有若干的神经元。我们称第 L l L_l Ll 层为输出层,也就是最后一层。对于第 L L L 层(从 0 0 0 开始计数),我们用 n L n_L nL 表示其神经元的数量。

对于第 L L L 层的第 i i i 个神经元(从 0 0 0 开始计数),我们用 a i L a^L_i aiL 表示其输出到下一层神经元或者作为结果的值。对于生物体的神经元, a i L a_i^L aiL 的取值只有 0 , 1 0,1 0,1 ,但是对于本文而言,计算机的神经元有 a i L ∈ [ 0 , 1 ] a_i^L\in[0,1] aiL[0,1] 。同时,每个神经元还有一个偏移值 b i L b^L_i biL

除输入层外,每一个神经元都与上一层的所有神经元都有一个连接,对于第 L ( 1 < L ) L(1<L) L(1<L) 的第 i i i 个神经元,它与 L − 1 L-1 L1 层的所有神经元都有连接,其中它与第 j j j 个神经元的连接有权重 w i , j L w^L_{i,j} wi,jL

下图展示了一个简单的神经网络的构成:

图中有 n 0 = 3 , n 1 = n 2 = 2 n_0=3,n_1=n_2=2 n0=3,n1=n2=2 ,且第 0 0 0 层为输入层,第 2 2 2 层为输出层。

PS \texttt{PS} PS: 此图没有展示神经元的偏移值。

在这里插入图片描述

输出值的计算

对于输入层而言,其输出值 a i 0 ( i ∈ [ 0 , n 0 ) ∩ Z ) a^0_i (i\in[0,n_0)\cap \mathbb{Z}) ai0(i[0,n0)Z) ,其输出值与神经网络的输入相同。

对于隐藏层和输出层,其计算方式如下:
a i L = f ( z i L ) z i L = b i L + ∑ j = 0 n L − 1 w i , j L a j L − 1 \begin{aligned} a^L_i&=f\left(z^L_i\right)\\ z^L_i&=b^L_i + \sum_{j=0}^{n_{L-1}} w^L_{i,j}a^{L-1}_j \end{aligned} aiLziL=f(ziL)=biL+j=0nL1wi,jLajL1
其中, f ( x ) f(x) f(x) 函数一般为 S i g m o i d \mathrm{Sigmoid} Sigmoid 函数,常见于生物种群数量的自然增长。其定义和导数如下:
f ( x ) = 1 1 + e − x f ′ ( x ) = f ( x ) ( 1 − f ( x ) ) \begin{aligned} f(x)&=\frac{1}{1+e^{-x}}\\ f'(x)&=f(x)(1-f(x)) \end{aligned} f(x)f(x)=1+ex1=f(x)(1f(x))
这个函数可以让我们的输出值取值范围固定在 [ 0 , 1 ] [0,1] [0,1] 之间。

实际上,神经网络使用的 f ( x ) f(x) f(x) 不一定需要让输出值固定在 [ 0 , 1 ] [0,1] [0,1] 之间,比如卷积神经网络(CNN)中有一个 f ( x ) f(x) f(x) R e L U \mathrm{ReLU} ReLU 函数,其定义为:
f ( x ) = { x x > 0 0 o t h e r w i s e f(x)=\begin{cases} x & x>0\\ 0 & \mathrm{otherwise} \end{cases} f(x)={x0x>0otherwise

请留意 z i L z^L_i ziL 这个值,我们暂且将其称为成为中间值

神经网络的学习方法

整个神经网络的计算过程,可以认为是一个庞大的函数 F F F ,其自变量的个数是输入层神经元的个数,而因变量的个数是输出层神经元的个数。而我们需要的让这个函数去拟合一个标准函数 G G G

而神经网络的学习过程,实际上是一个用提供的若干个输入输出(我们称这些输入输出为训练集)不断调整权重 w w w 以及偏移值 b b b ,使得 F F F 拟合 G G G 的过程。

过拟合

实际上在神经网络的学习过程中,因为参数过多(或者其他原因),容易出现过拟合(overfitting)的现象。表现为在对于训练集中的数据,神经网络可以给出优秀的结果,但是对于训练集以外的数据,则出现很多错误。

一个形象的理解方法就是:当我们用给定的一些三次函数 G G G 上的点,用一个 6 6 6 次函数 F F F 去拟合这个 3 3 3 次函数 G G G 的时候,对于给定的点, F F F 都和 G G G 的输出一致,但是本应该是 0 0 0 4 , 5 , 6 4,5,6 4,5,6 次项不全是 0 0 0 ,导致 F F F 会出现一些弯曲,这时候就出现了过拟合。下图是一个过拟合的示意图:

在这里插入图片描述

其中绿色的点是训练集, G ( x ) G(x) G(x) 是我们需要的函数, F ( x ) F(x) F(x) 为神经网络拟合出的函数。

一般来说,我们会使用另一套输入输出(我们称之为测试集)来评判神经网络的学习效果。

在当前状态下,我们并不考虑过拟合问题。

反向传播方法(Backprop,简称BP)

反向传播方法是神经网络学习的核心算法。

对于训练集中的一个输入输出 k k k ,我们用 O i O_i Oi 表示理想状态下,神经网络的输出层的第 i i i 个神经元应该有的输出值。 这时候,我们可以计算出神经网络当前的函数 F F F G G G 的误差,这里我们用类似方差的表达式表示其误差:
C k = ∑ i = 0 N L l ( a i L l − O i ) 2 C_k=\sum_{i=0}^{N_{L_l}} (a^{L_l}_i-O_i)^2 Ck=i=0NLl(aiLlOi)2

实际上有其他表示误差的方式,本人不是很了解。

显然的,我们需要让 C k C_k Ck 越小越好,这时候,我们需要知道 a i L l a_i^{L_l} aiLl 应该如何变化才能使得 C k C_k Ck 变小,可以想到运用导数计算出 a a a 应该有的变化,也就是:
∂ C k ∂ a i L l = 2 ( a i L l − O i ) \frac{\partial C_k}{\partial a^{L_l}_i} =2\left(a^{L_l}_i - O_i\right) aiLlCk=2(aiLlOi)
想让 C k C_k Ck 变小,就需要让 a i L l a_i^{L_l} aiLl 向着 − ∂ C k a i L l -\frac{\partial C_k}{a^{L_l}_i} aiLlCk 方向减小,至于如何做到这一点,我们可以调整 b i L l , w i , ∗ L l , a ∗ L l − 1 b^{L_l}_i,w^{L_l}_{i,*}, a^{L_{l-1}}_* biLl,wi,Ll,aLl1。而调整这些参数,也可以使用求导的方式 ( 0 ≤ j < n l − 1 0\leq j<n_{l-1} 0j<nl1):
∂ C k ∂ b i L l = ∂ z i L l ∂ b i L l ∂ a i L l ∂ z i L l ∂ C k ∂ a i L l = 1 ⋅ f ′ ( z i L l ) ∂ C k ∂ a i L l ∂ C k ∂ w i , j L l = ∂ z i L l ∂ w i , j L l ∂ a i L l ∂ z i L l ∂ C k ∂ a i L l = a j L l − 1 f ′ ( z i L l ) ∂ C k ∂ a i L l ∂ C k ∂ a j L l − 1 = ∑ i = 0 n L l ∂ z i L l ∂ a j L l − 1 ∂ a i L l ∂ z i L l ∂ C k ∂ a i L l = ∑ i = 0 n L l − 1 w i , j L l f ′ ( z i L l ) ∂ C k ∂ a i L l \begin{aligned} \frac{\partial C_k}{\partial b^{L_l}_i} &= \frac{\partial z^{L_l}_i}{\partial b^{L_l}_i} \frac{\partial a^{L_l}_i}{\partial z^{L_l}_i} \frac{\partial C_k}{\partial a^{L_l}_i} = 1\cdot f'\left(z^{L_l}_i\right)\frac{\partial C_k}{\partial a^{L_l}_i}\\ \frac{\partial C_k}{\partial w^{L_l}_{i,j}} &= \frac{\partial z^{L_l}_i}{\partial w^{L_l}_{i,j}} \frac{\partial a^{L_l}_i}{\partial z^{L_l}_i} \frac{\partial C_k}{\partial a^{L_l}_i} = a^{L_{l}-1}_j f'\left(z^{L_l}_i\right)\frac{\partial C_k}{\partial a^{L_l}_i}\\ \frac{\partial C_k}{\partial a^{L_{l}-1}_j} &= \sum_{i=0}^{n_{L_l}}\frac{\partial z^{L_l}_i}{\partial a^{L_{l}-1}_j} \frac{\partial a^{L_l}_i}{\partial z^{L_l}_i} \frac{\partial C_k}{\partial a^{L_l}_i} = \sum_{i=0}^{n_{L_l}-1}w^{L_l}_{i,j}f'\left(z^{L_l}_i\right)\frac{\partial C_k}{\partial a^{L_l}_i} \end{aligned} biLlCkwi,jLlCkajLl1Ck=biLlziLlziLlaiLlaiLlCk=1f(ziLl)aiLlCk=wi,jLlziLlziLlaiLlaiLlCk=ajLl1f(ziLl)aiLlCk=i=0nLlajLl1ziLlziLlaiLlaiLlCk=i=0nLl1wi,jLlf(ziLl)aiLlCk
要调整这些参数,只需要向着导数的反方向增减这些变量即可,至于 a j L l − 1 a^{L_l-1}_j ajLl1 的调整,需要调整 b j L l − 1 b^{L_l-1}_j bjLl1 以及 w j , ∗ L l − 1 w^{L_l-1}_{j,*} wj,Ll1 ,其调整方式也是求导,且结构和上述的式子大致相同:
∀ L ∈ [ 1 , L l − 1 ] ∩ Z ∂ C k ∂ b i L = ∂ z i L ∂ b i L ∂ a i L ∂ z i L ∂ C k ∂ a i L = 1 ⋅ f ′ ( z i L ) ∂ C k ∂ a i L ∂ C k ∂ w i , j L = ∂ z i L ∂ w i , j L ∂ a i L ∂ z i L ∂ C k ∂ a i L = a j L − 1 f ′ ( z i L ) ∂ C k ∂ a i L ∂ C k ∂ a j L = ∑ i = 0 n L + 1 − 1 ∂ z i L + 1 ∂ a j L ∂ a i L + 1 ∂ z i L + 1 ∂ C k ∂ a i L + 1 = ∑ i = 0 n L + 1 − 1 w i , j L + 1 f ′ ( z i L + 1 ) ∂ C k ∂ a i L + 1 \begin{aligned} &\forall L\in [1,L_l-1] \cap \mathbb{Z}\\ &\frac{\partial C_k}{\partial b^{L}_i} = \frac{\partial z^{L}_i}{\partial b^{L}_i} \frac{\partial a^{L}_i}{\partial z^{L}_i} \frac{\partial C_k}{\partial a^{L}_i} = 1\cdot f'\left(z^{L}_i\right)\frac{\partial C_k}{\partial a^{L}_i}\\ &\frac{\partial C_k}{\partial w^{L}_{i,j}} = \frac{\partial z^{L}_i}{\partial w^{L}_{i,j}} \frac{\partial a^{L}_i}{\partial z^{L}_i} \frac{\partial C_k}{\partial a^{L}_i} = a^{L-1}_j f'\left(z^{L}_i\right)\frac{\partial C_k}{\partial a^{L}_i}\\ &\frac{\partial C_k}{\partial a^{L}_j} = \sum_{i=0}^{n_{L+1}-1}\frac{\partial z^{L+1}_i}{\partial a^{L}_j} \frac{\partial a^{L+1}_i}{\partial z^{L+1}_i} \frac{\partial C_k}{\partial a^{L+1}_i} = \sum_{i=0}^{n_{L+1}-1}w^{L+1}_{i,j}f'\left(z^{L+1}_i\right)\frac{\partial C_k}{\partial a^{L+1}_i} \end{aligned} L[1,Ll1]ZbiLCk=biLziLziLaiLaiLCk=1f(ziL)aiLCkwi,jLCk=wi,jLziLziLaiLaiLCk=ajL1f(ziL)aiLCkajLCk=i=0nL+11ajLziL+1ziL+1aiL+1aiL+1Ck=i=0nL+11wi,jL+1f(ziL+1)aiL+1Ck
至此,我们得到了为了拟合数据 k k k 而需要调整的数据的内容,以及调整之前的网络的信息,我们用两个数组(或者认为是矩阵,向量)将其整合一下:
∇ C k = [ ∂ C k ∂ b 0 0 ∂ C k ∂ b 1 0 ⋮ ∂ C k ∂ b n L l − 1 L l ∂ C k ∂ w 0 , 0 1 ∂ C k ∂ w 0 , 1 1 ⋮ ∂ C k ∂ w n L l − 1 , n L l − 1 − 1 L l ] , D = [ b 0 0 b 1 0 ⋮ b n L l − 1 L l w 0 , 0 1 w 0 , 1 1 ⋮ w n L l − 1 , n L l − 1 − 1 L l ] \nabla C_k=\begin{bmatrix} \frac{\partial C_k}{\partial b^{0}_0}\\ \frac{\partial C_k}{\partial b^{0}_1}\\ \vdots\\ \frac{\partial C_k}{\partial b^{L_l}_{n_{L_l}-1}}\\ \frac{\partial C_k}{\partial w^{1}_{0,0}}\\ \frac{\partial C_k}{\partial w^{1}_{0,1}}\\ \vdots\\ \frac{\partial C_k}{\partial w^{L_l}_{n_{L_l}-1,n_{L_l-1}-1}} \end{bmatrix} , D=\begin{bmatrix} b^{0}_0\\ b^{0}_1\\ \vdots\\ b^{L_l}_{n_{L_l}-1}\\ w^{1}_{0,0}\\ w^{1}_{0,1}\\ \vdots\\ w^{L_l}_{n_{L_l}-1,n_{L_l-1}-1} \end{bmatrix} Ck= b00Ckb10CkbnLl1LlCkw0,01Ckw0,11CkwnLl1,nLl11LlCk ,D= b00b10bnLl1Llw0,01w0,11wnLl1,nLl11Ll
当我们调整网络的时候,可以使用一个参数 η \eta η 调整这个数据对学习的影响,而调整方式,可以用 D ′ = D − η ⋅ ∇ C k D'=D-\eta \cdot \nabla C_k D=DηCk 表示对这个数据的学习。可想而知 η \eta η 越大,这个数据对网络的影响越强。而 η \eta η 的调节,是一个需要讨论的问题,但是本人不太懂,所以本文中的 η = 1 \eta =1 η=1

Mini-Batch

如果对于每个数据,都进行一次调整 ,那么效率会变得很低,为此,我们可以使用一个小技巧,也就是 Mini-Batch \texttt{Mini-Batch} Mini-Batch 。具体操作方式如下:

首先设定一个大小 S S S ,本文中,我们设其为 10 10 10 。接着,将数据集打乱,然后每 S S S 个数据组成一个 B a t c h \mathrm{Batch} Batch 。对于每一个 B a t c h \mathrm{Batch} Batch ,设其中的数据编号为 1 , 2 , 3 , … , S 1,2,3,\dots, S 1,2,3,,S ,对于每一个数据 i i i ,都用 BP \texttt{BP} BP 计算出一个 ∇ C i \nabla C_i Ci ,接着将 B a t c h \mathrm{Batch} Batch 中的调整信息整合,这里我们用平均值进行整合: ∇ C = 1 S ∑ i = 1 S ∇ C i \nabla C=\frac{1}{S}\sum_{i=1}^S \nabla C_i C=S1i=1SCi ,接着用这个 ∇ C \nabla C C 调整神经网络。

代码

NN . h \texttt{NN}.h NN.h

#pragma once#include <vector>
#include <cstdarg>
#include "gloconst.h"class Network {
private:double **a, **b, **da, **db, **z;double ***w, ***dw;std::vector<int> network_size;/// <summary>/// 进行逆向传播/// </summary>/// <param name="output">学习的输出</param>void Backprop(double *output);
public:Network(std::vector<int> &network_size);~Network();/// <summary>/// 计算一个数据/// </summary>/// <param name="input">输入</param>void Calculate(double *input);/// <summary>/// 对模型进行训练/// </summary>/// <param name="data">数据 data[i][0] 表示第i个输入,data[i][1] 表示第i个输出</param>/// <param name="size">数据的数量</param>/// <param name="batch_size">mini-batch的大小</param>void Train(double ***data, int size, int batch_size = 100);/// <summary>/// 得到输出值a最大的节点的id/// </summary>/// <returns></returns>int GetMaxOutputNode();
};

NN.cpp \texttt{NN.cpp} NN.cpp

#include "NN.h"
#include <algorithm>
#include <ctime>
#include <cassert>
#include <random>unsigned seed = time(NULL);
std::default_random_engine gen(seed);
std::normal_distribution<double> dis(0, 1);//生成正态分布的随机数 mu=0,sigma=1
inline double randn() {return dis(gen);
}
Network::Network(std::vector<int> &network_size) {this->network_size = network_size;a = new double *[network_size.size()];b = new double *[network_size.size()];da = new double *[network_size.size()];db = new double *[network_size.size()];z = new double *[network_size.size()];w = new double **[network_size.size() - 1];dw = new double **[network_size.size() - 1];for (int i = 0; i < network_size.size(); i++) { int size_i = network_size[i];b[i] = new double[size_i], a[i] = new double[size_i], da[i] = new double[size_i], db[i] = new double[size_i], z[i] = new double[size_i];for (int j = 0; j < size_i; j++) b[i][j] = randn();if (i) {w[i] = new double *[size_i];dw[i] = new double *[size_i];for (int j = 0; j < size_i; j++) {int size2 = network_size[i - 1];w[i][j] = new double[size2];dw[i][j] = new double[size2];for (int k = 0; k < size2; k++)w[i][j][k] = randn();}}}
}Network::~Network() {for (int i = 0; i < network_size.size(); i++) {int size_i = network_size[i];delete[] a[i], delete[] b[i], delete[] da[i], delete[] db[i], delete[] z[i];if (i) for (int j = 0; j < size_i; j++)delete[] w[i][j], delete[] dw[i][j];}
}void Network::Calculate(double *input) {memcpy(a[0], input, sizeof(double) * network_size[0]);for (int i = 1; i < network_size.size(); i++) {int sizei = network_size[i], size_lst = network_size[i - 1];for (int j = 0; j < sizei; j++) {z[i][j] = b[i][j];for (int k = 0; k < size_lst; k++) z[i][j] += w[i][j][k] * a[i - 1][k];a[i][j] = Sigmoid(z[i][j]);}}
}void Network::Backprop(double *output) {int lstid = network_size.size() - 1, sizei = network_size[network_size.size() - 1];for (int i = 0; i < sizei; i++) {da[lstid][i] = (a[lstid][i] - output[i]),db[lstid][i] = DSigmoid(z[lstid][i]) * da[lstid][i];for (int j = 0; j < network_size[lstid - 1]; j++)dw[lstid][i][j] = a[lstid - 1][j] * db[lstid][i];}for (int i = lstid - 1; i > 0; i--) {for (int j = 0; j < network_size[i]; j++) {da[i][j] = 0;for (int k = 0; k < network_size[i + 1]; k++)da[i][j] += w[i + 1][k][j] * db[i + 1][k];db[i][j] = DSigmoid(z[i][j]) * da[i][j];for (int k = 0; k < network_size[i - 1]; k++)dw[i][j][k] = a[i - 1][k] * db[i][j];}}
}void array_add(double *dst, double *src, int sz) {for (int i = 0; i < sz; i++) dst[i] += src[i];
}
void array_mul(double *dst, double x, int sz) {for (int i = 0; i < sz; i++) dst[i] *= x;
}void Network::Train(double ***data, int size, int batch_size) {batch_size = std::min(batch_size, size);std::random_shuffle(data, data + size);double **avr_db, ***avr_dw;avr_db = new double *[network_size.size()];avr_dw = new double **[network_size.size()];for (int i = 0; i < network_size.size(); i++) {avr_db[i] = new double[network_size[i]];avr_dw[i] = new double*[network_size[i]];if (i) for (int j = 0; j < network_size[i]; j++)avr_dw[i][j] = new double[network_size[i - 1]];}const double eta = 1;for (int st = 0; st < size; st += batch_size) {//clean the avr arrayfor (int i = 0; i < network_size.size(); i++) {memset(avr_db[i], 0, sizeof(double) * network_size[i]);if (i) for (int j = 0; j < network_size[i]; j++)memset(avr_dw[i][j], 0, sizeof(double) * network_size[i - 1]);}for (int x = st; x < st + batch_size; x++) {Calculate(data[x][0]);Backprop(data[x][1]);for (int i = 0; i < network_size.size(); i++) {array_add(avr_db[i], db[i], network_size[i]);if (i) for (int j = 0; j < network_size[i]; j++)array_add(avr_dw[i][j], dw[i][j], network_size[i - 1]);}}for (int i = 1; i < network_size.size(); i++) {array_mul(avr_db[i], -eta / batch_size, network_size[i]);array_add(b[i], avr_db[i], network_size[i]);if (i) for (int j = 0; j < network_size[i]; j++) {array_mul(avr_dw[i][j], -eta / batch_size, network_size[i - 1]),array_add(w[i][j], avr_dw[i][j], network_size[i - 1]);}}}for (int i = 0; i < network_size.size(); i++) {delete[] avr_db[i];if (i) {for (int j = 0; j < network_size[i]; j++) delete[] avr_dw[i][j];delete[] avr_dw[i];}}delete[] avr_db, delete[] avr_dw;
}int Network::GetMaxOutputNode() {int res = 0;double *lyr = a[network_size.size() - 1];int size = network_size[network_size.size() - 1];for (int i = 1; i < size; i++)res = (lyr[res] > lyr[i] ? res : i);return res;
}

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

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

相关文章

[Linux]进程概念

[Linux]进程概念 文章目录 [Linux]进程概念进程的定义进程和程序的关系Linux下查看进程Linux下通过系统调用获取进程标示符Linux下通过系统调用创建进程-fork函数使用 进程的定义 进程是程序的一个执行实例&#xff0c;是担当分配系统资源&#xff08;CPU时间&#xff0c;内存…

二、数学建模之整数规划篇

1.定义 2.例题 3.使用软件及解题 一、定义 1.整数规划&#xff08;Integer Programming&#xff0c;简称IP&#xff09;&#xff1a;是一种数学优化问题&#xff0c;它是线性规划&#xff08;Linear Programming&#xff0c;简称LP&#xff09;的一个扩展形式。在线性规划中&…

构造不包含字母和数字的webshell

文章目录 利用不含字母与数字进行绕过知识介绍题目方法一&#xff1a;异或操作绕过方法二&#xff1a;取反进行绕过 过滤不是很严格的情况进阶绕过利用php7特性直接绕过 利用不含字母与数字进行绕过 知识介绍 <?phpecho "A"^"";?>从运行结果为! …

深度学习|CNN卷积神经网络

CNN卷积神经网络 解决的问题人类的视觉原理原理卷积层——提取特征池化层——数据降维全连接层——输出结果 应用图像处理自然语言处理 解决的问题 在CNN没有出现前&#xff0c;图像对人工智能来说非常难处理。 主要原因&#xff1a; 图像要处理的数据量太大了。图像由像素组…

使用IDEA把Java程序打包成jar

点击左上角File,选择Project Structure 左侧选中Artifacts,点击右侧的号 选择JAR->From modules with dependencies 选择你要运行的main方法所在的类,选好了点击OK Artifacts添加完成后点击右下角OK 在工具栏中找到Build,选择Build Artifacts 刚才创建好的Artifacts,选择Bui…

【附安装包】Midas Civil2019安装教程

软件下载 软件&#xff1a;Midas Civil版本&#xff1a;2019语言&#xff1a;简体中文大小&#xff1a;868.36M安装环境&#xff1a;Win11/Win10/Win8/Win7硬件要求&#xff1a;CPU2.5GHz 内存4G(或更高&#xff09;下载通道①百度网盘丨64位下载链接&#xff1a;https://pan.…

Java设计模式-抽象工厂模式

简介 设计模式是软件设计中的一种常见方法&#xff0c;通过定义一系列通用的解决方案&#xff0c;来解决常见的软件设计问题。其中&#xff0c;抽象工厂模式是一种非常常见的设计模式&#xff0c;它可以帮助我们创建一组相关的对象&#xff0c;而不需要指定具体的实现方式。 …

大数据平台是什么意思?有什么用?一般包含哪些模块?

大数据时代&#xff0c;还有很多人不知道大数据平台是什么意思&#xff1f;有什么用&#xff1f;一般包含哪些模块&#xff1f;今天我们就一起来简单了解一下吧&#xff01;仅供参考哦&#xff01; 大数据平台是什么意思&#xff1f;有什么用&#xff1f;一般包含哪些模块&am…

Spring框架中JavaBean的生命周期及单例模式与多列模式

Spring框架中JavaBean的生命周期及单例模式与多列模式 1. Spring框架中JavaBean的管理过程1.1 #定义Bean1.2 Bean的实例化1.3 属性注入1.4 初始化方法1.5 Bean的使用和引用1.6 销毁方法 2. 单例模式与原型模式在JavaBean管理中的应用1.在Spring管理JavaBean的过程中&#xff0c…

Kafka 集群搭建过程

前言 跟着尚硅谷海哥文档搭建的Kafka集群环境&#xff0c;在此记录一下&#xff0c;侵删 注意&#xff1a;博主在服务器上搭建环境的时候使用的是一个服务器&#xff0c;所以这篇博客可能会出现一些xsync分发到其他服务器时候的错误&#xff0c;如果你在搭建的过程中出现了错…

一种pug与html相互转换的工具

有时候看pug很不方便&#xff0c;这个语言虽然简洁&#xff0c;但可读性与维护性较差&#xff0c;所以需要进行转换&#xff0c;这个是win工具&#xff0c;比较方便。 这个工具的下载地址如下&#xff1a; https://download.csdn.net/download/qq_40032778/88244980 解压后如下…

【Azure API 管理】APIM如何实现对部分固定IP进行访问次数限制呢?如60秒10次请求

问题描述 使用Azure API Management, 想对一些固定的IP地址进行访问次数的限制&#xff0c;如被限制的IP地址一分钟可以访问10次&#xff0c;而不被限制的IP地址则可以无限访问&#xff1f; ChatGPT 解答 最近ChatGPT爆火&#xff0c;所以也把这个问题让ChatGPT来解答&#x…

算法通关村十二关 | 字符串经典题目

字符串问题&#xff0c;大家记得模板思路即可&#xff0c;一个类型的题目有很多种。 1. 字符串反转的问题 1.1 反转字符串 题目&#xff1a;LeetCode344: 思路 还是我们常见的双指针问题&#xff0c; left字符数组头部指针&#xff0c;right字符数组尾部指针。当left < r…

Blazor 依赖注入妙用:巧设回调

文章目录 前言依赖注入特性需求解决方案示意图 前言 依赖注入我之前写过一篇文章&#xff0c;没看过的可以看看这个。 C# Blazor 学习笔记(10):依赖注入 依赖注入特性 只能Razor组件中注入所有Razor组件在作用域注入的都是同一个依赖。作用域可以看看我之前的文章。 需求 …

【实例分割】(一)Mask R-CNN详细介绍带python代码

目录 1.&#x1f340;&#x1f340;实例分割定义 2.&#x1f340;&#x1f340;Mask R-CNN 3.&#x1f340;&#x1f340;经典的实例分割算法 4.&#x1f340;&#x1f340;Mask R-CNN python代码 整理不易&#xff0c;欢迎一键三连&#xff01;&#xff01;&#xff01;…

C++ vector

前言&#xff1a; vector的部分源码&#xff1a; &#xff08;做过删除&#xff0c;留下关键信息&#xff09; vector的使用 构造函数&#xff1a; 1 无参构造 vector<int> v1; 2 构造并初始化n个val vector<int> v2(5,1);3 拷贝构造 vector<int> v3…

4-4 Representing text Exercise

本文所用资料下载 一. Representing text Let’s load Jane Austen’s Pride and Prejudice. We first split our text into a list of lines and pick an arbitrary line to focus on: with open(D:jane-austen/1342-0.txt, encodingutf8) as f:text f.read() lines text.…

继承(C++)

继承 一、初识继承概念“登场”语法格式 继承方式九种继承方式组合小结&#xff08;对九种组合解释&#xff09; 二、继承的特性赋值转换 一一 切片 / 切割作用域 一一 隐藏 / 重定义 三、派生类的默认成员函数派生类的默认成员函数1. 构造函数2. 拷贝构造3. 赋值运算符重载4. …

Centos7查看磁盘和CUP统计信息iostat命令

Centos7查看磁盘和CUP统计信息iostat命令 Centos7内存高|查看占用内存命令 docker实战(一):centos7 yum安装docker docker实战(二):基础命令篇 docker实战(三):docker网络模式(超详细) docker实战(四):docker架构原理 docker实战(五):docker镜像及仓库配置 docker实战(六…

Spring Clould 注册中心 - Eureka,Nacos

视频地址&#xff1a;微服务&#xff08;SpringCloudRabbitMQDockerRedis搜索分布式&#xff09; Eureka 微服务技术栈导学&#xff08;P1、P2&#xff09; 微服务涉及的的知识 认识微服务-服务架构演变&#xff08;P3、P4&#xff09; 总结&#xff1a; 认识微服务-微服务技…