GAN 数学原理简单介绍以及代码实践
1. GAN 数学原理
1.1 GAN 概述
GAN(Generative Adversarial Network) 是一种深度生成神经网络,它包括 生成模型 与 判别模型 两个部分。其中,生成模型 的任务是生成 “假” 样本,而 判别模型 的任务是甄别出 “假” 样本。
对抗 的概念体现在两个模型之间的互动过程,生成模型 希望能成功欺骗 判别模型,达到 “以假乱真” 的目的;判别模型 需要擦亮自己的眼睛,尽量不让 生成模型 “欺骗”。
而 GAN 是最终大 Boss ,静静地看着两个小弟如虞我诈,最后欣慰地看着它们成为人才,为自己所用(大骗子员工+高级质检员工)。
1.2 总体结构
版权声明:以下图片以及伪代码均摘录于 《深度学习轻松学——核心算法于视觉实践》冯超 著 电子工业出版社
def GAN(G,D,X) :# 表示生成模型# 表示判别模型# 表示训练数据for it er in range (MAX_ITER) :for step in range(K)x = data_ sample (X)z = noise_sample ()optimize_D(G, D, x, z)z = noise_sample()optimize_G(G, D, z)
结合GAN的基本结构图以及伪代码进行理解,其中
- K 表示判别模型 D 的迭代次数;
- MAX_ITER 表示生成模型 G 的迭代次数,并且每次迭代中,执行 K 次迭代优化 判别模型。
1.3 数学公式推导
1.3.1 总体目标(目标函数)
目标函数如下:
m i n G m a x D V ( D , G ) = E x [ log D ( x ) ] + E z [ log ( 1 − D ( G ( z ) ) ) ] ( 1 ) min_G \ max_D V(D,G) = E_x[\log\ D(x)]+E_z[\log(1-D(G(z)))] \ \ \ \ \ \ \ \ \ \ \ (1) minG maxDV(D,G)=Ex[log D(x)]+Ez[log(1−D(G(z)))] (1)
对于判别模型 D 而言,它的任务是检测出真实样本, 它的输入时掺杂着 G 生成的样本数据与真实数据,输出的是它认为是真实数据的比例。比如说输入5份真实数据,5份假数据,那么因此,总体目标需要 最大化 判别模型的输出。
对于生成模型而言,它的目标是 最大化 生成样本与真实样本的相似程度,以达到欺骗 D 的目的。换句话说,它需要 最小化 判别模型的输出。
所以可以得到上面公式中的 min 与 max 的目标函数。
而等号右边的内容可以理解为判别模型对真实数据与假样本的识别能力:
- E x [ log D ( x ) ] E_x[\log\ D(x)] Ex[log D(x)] 输入的是真实数据,表示判别模型对真实样本的判别能力(概率值表示)
- E z [ log ( 1 − D ( G ( z ) ) ) ] E_z[\log(1-D(G(z)))] Ez[log(1−D(G(z)))] 中,先观察 G ( z ) G(z) G(z) 部分,这个是 G 生成假样本的意思,然后判别模型 D 对假样本进行识别,输出这些数据中真实数据的比例。换句话说,因为输入的是假样本,所以输出的是 D 的 误判率,明明全部都是假的,D 如果输出 4% 是真的,那么 D 的误判率就是 4%,所以 1 − D ( G ( z ) ) 1-D(G(z)) 1−D(G(z)) 的意思就是 D 的正确判断率。
所以公式1 等号右边求解的是判别模型D对假样本与真样本的识别能力。
1.3.2 当生成模型固定时
我们不可能两头一起动,就像上面的伪代码一下,内部循环提高判别模型的能力,再外部循环提高生成模型的能力。这里的公式推导也是如此,我们假设生成模型固定,此时需要最大化判别模型的输出,即
m a x D V ( D , G ) = E x [ log D ( x ) ] + E z [ log ( 1 − D ( G ( z ) ) ) ] = ∫ x p r ( x ) log D ( x ) d x + ∫ z p g ( z ) log ( 1 − D ( G ( z ) ) ) d z ( 2 ) max_D V(D,G) = E_x[\log\ D(x)]+E_z[\log(1-D(G(z)))] \\ =\int_x p_r(x)\log D(x)\ dx + \int_zp_g(z)\log (1-D(G(z)))\ dz \ \ \ \ \ \ \ \ \ \ (2) maxDV(D,G)=Ex[log D(x)]+Ez[log(1−D(G(z)))]=∫xpr(x)logD(x) dx+∫zpg(z)log(1−D(G(z))) dz (2)
由于组成式子的两部分积分的区域不同,会对后面的计算造成困难,我们首先将两个积分区域统一。我们将生成数据 G ( z ) G(z) G(z) 的分布与真实数据 x x x 的分布做一个投射,只要判别式能够在真实数据出现的地方保证判别正确最大化即可。可以得到
m a x D V ( D , G ) = ∫ x p r ( x ) log D ( x ) d x + ∫ x p g ( x ) log ( 1 − D ( x ) ) d x = ∫ x [ p r ( x ) log D ( x ) + p g ( x ) log ( 1 − D ( x ) ) ] d x ( 3 ) max_D V(D,G) = \int_x p_r(x)\log D(x)\ dx + \int_xp_g(x)\log (1-D(x))\ dx \\ =\int_x [p_r(x)\log D(x) +p_g(x)\log (1-D(x))] \ dx \ \ \ \ \ \ \ \ \ \ \ \ (3) maxDV(D,G)=∫xpr(x)logD(x) dx+∫xpg(x)log(1−D(x)) dx=∫x[pr(x)logD(x)+pg(x)log(1−D(x))] dx (3)
其中 p r ( x ) p_r(x) pr(x) 是真实数据概率分布(真样本),而 p g ( x ) p_g(x) pg(x) 是生成数据概率分布(假样本)。
换句话而言,当生成模型 G 固定时,或者说已经训练好时, G ( z ) G(z) G(z) 生成数据与真实数据 x x x 具有相同的地位以及性质,但是注意 p r ( x ) p_r(x) pr(x) 与 p g ( x ) p_g(x) pg(x) 是具有差异的。
1.3.3 何时取得最优解
当生成模型固定时,在何种情况下整个式子能够取得最优解?现在需要基于公式3进行下一步推导。
首先可以确定,公式3 中积分部分最大化时,整个式子就可以实现最大化。因而,需要求解下列式子何时取得最大值。
f ( D ( x ) ) = p r ( x ) log D ( x ) + p g ( x ) log ( 1 − D ( x ) ) ( 4 ) f(D(x)) = p_r(x)\log D(x) +p_g(x)\log (1-D(x)) \ \ \ \ \ \ \ \ \ \ (4) f(D(x))=pr(x)logD(x)+pg(x)log(1−D(x)) (4)
注意,这个时候已经固定了生成模型,因此优化过程中变动的是判别模型,换句话说,整个判别模型是一个变量,为了简洁,我们使用 t t t 来表示,即 令 t = D ( x ) t = D(x) t=D(x),并且 t ∈ ( 0 , 1 ) t\in(0,1) t∈(0,1)。
则达到的表达式为:
h ( t ) = p r ( x ) log t + p g ( x ) log ( 1 − t ) ( 5 ) h(t)=p_r(x)\log t +p_g(x)\log (1-t) \ \ \ \ \ \ \ \ \ \ (5) h(t)=pr(x)logt+pg(x)log(1−t) (5)
因此需要求解函数 h ( t ) h(t) h(t) 的最大值问题,其中 p r ( x ) p_r(x) pr(x) 与 p g ( x ) p_g(x) pg(x) 都只是大于0的参数。我们对公式5 进行求导,得
h ′ ( t ) = p r ( x ) 1 t − p g ( x ) 1 1 − t ( 6 ) h'(t) = p_r(x){{1}\over{t}}-p_g(x){{1}\over{1-t}} \ \ \ \ \ \ \ \ \ \ \ (6) h′(t)=pr(x)t1−pg(x)1−t1 (6)
可以求解得到,当 h ′ ( t ) = 0 h'(t)=0 h′(t)=0时,
t = p r ( x ) p r ( x ) + p g ( x ) ( 7 ) t={{ p_r(x)}\over{ p_r(x)+ p_g(x)}} \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (7) t=pr(x)+pg(x)pr(x) (7)
并且可以证明:
- 当 t < p r ( x ) p r ( x ) + p g ( x ) t < {{ p_r(x)}\over{ p_r(x)+ p_g(x)}} t<pr(x)+pg(x)pr(x) 时, h ′ ( t ) > 0 h'(t)>0 h′(t)>0, h ( t ) h(t) h(t) 单调递增;
- 当 t > p r ( x ) p r ( x ) + p g ( x ) t > {{ p_r(x)}\over{ p_r(x)+ p_g(x)}} t>pr(x)+pg(x)pr(x) 时, h ′ ( t ) < 0 h'(t)<0 h′(t)<0, h ( t ) h(t) h(t) 单调递减。
因此,当且仅当 t = p r ( x ) p r ( x ) + p g ( x ) t={{ p_r(x)}\over{ p_r(x)+ p_g(x)}} t=pr(x)+pg(x)pr(x) 时, h ( t ) h(t) h(t) 取得最大值。也就是说,当且仅当 D ( x ) = p r ( x ) p r ( x ) + p g ( x ) D(x)={{ p_r(x)}\over{ p_r(x)+ p_g(x)}} D(x)=pr(x)+pg(x)pr(x)时,整个式子取得最大值。
1.3.4 最大值表达式
现在已经确定整个式子何时取得最大值,现在需要计算,最大值的表达式。将公式7代入式子3中(还原 t = D ( x ) t=D(x) t=D(x))。可得:
m a x D V ( D , G ) = ∫ x p r ( x ) log p r ( x ) p r ( x ) + p g ( x ) d x + ∫ x p g ( x ) log ( 1 − p r ( x ) p r ( x ) + p g ( x ) ) d x = ∫ x p r ( x ) log p r ( x ) p r ( x ) + p g ( x ) d x + ∫ x p g ( x ) log ( p g ( x ) p r ( x ) + p g ( x ) ) d x = ∫ x p r ( x ) log p r ( x ) p r ( x ) + p g ( x ) 2 ∗ 1 2 d x + ∫ x p g ( x ) log ( p g ( x ) p r ( x ) + p g ( x ) 2 ) ∗ 1 2 d x = ∫ x p r ( x ) [ log p r ( x ) p r ( x ) + p g ( x ) 2 + log 1 2 ] d x + ∫ x p g ( x ) [ log ( p g ( x ) p r ( x ) + p g ( x ) 2 ) + log 1 2 ] d x = − 2 log 2 + ∫ x p r ( x ) log p r ( x ) p r ( x ) + p g ( x ) 2 d x + ∫ x p g ( x ) log ( p g ( x ) p r ( x ) + p g ( x ) 2 ) d x ( 8 ) max_D V(D,G) = \int_x p_r(x)\log {{ p_r(x)}\over{ p_r(x)+ p_g(x)}}\ dx + \int_xp_g(x)\log (1-{{ p_r(x)}\over{ p_r(x)+ p_g(x)}})\ dx \\ =\int_x p_r(x)\log {{ p_r(x)}\over{ p_r(x)+ p_g(x)}}\ dx + \int_xp_g(x)\log ({{ p_g(x)}\over{ p_r(x)+ p_g(x)}})\ dx \\ =\int_x p_r(x)\log {{ p_r(x)}\over{ {p_r(x)+ p_g(x)}\over{2}}}*{{1}\over{2}}\ dx + \int_xp_g(x)\log ({{ p_g(x)}\over{ {p_r(x)+ p_g(x)}\over{2}}})*{{1}\over{2}}\ dx \\ =\int_x p_r(x)[\log {{ p_r(x)}\over{ {p_r(x)+ p_g(x)}\over{2}}}+\log{{1}\over{2}}]\ dx + \int_xp_g(x)[\log ({{ p_g(x)}\over{ {p_r(x)+ p_g(x)}\over{2}}})+\log{{1}\over{2}}]\ dx\\ =-2\log2+\int_x p_r(x)\log {{ p_r(x)}\over{ {p_r(x)+ p_g(x)}\over{2}}}\ dx + \int_xp_g(x)\log ({{ p_g(x)}\over{ {p_r(x)+ p_g(x)}\over{2}}})\ dx \\ (8) maxDV(D,G)=∫xpr(x)logpr(x)+pg(x)pr(x) dx+∫xpg(x)log(1−pr(x)+pg(x)pr(x)) dx=∫xpr(x)logpr(x)+pg(x)pr(x) dx+∫xpg(x)log(pr(x)+pg(x)pg(x)) dx=∫xpr(x)log2pr(x)+pg(x)pr(x)∗21 dx+∫xpg(x)log(2pr(x)+pg(x)pg(x))∗21 dx=∫xpr(x)[log2pr(x)+pg(x)pr(x)+log21] dx+∫xpg(x)[log(2pr(x)+pg(x)pg(x))+log21] dx=−2log2+∫xpr(x)log2pr(x)+pg(x)pr(x) dx+∫xpg(x)log(2pr(x)+pg(x)pg(x)) dx(8)
为了方便对比,这里列出 K L KL KL 公式:
K L ( p ∣ ∣ q ) = ∫ p ( x ) log p ( x ) q ( x ) d x KL(p||q)=\int p(x)\log {{p(x)}\over{q(x)}}\ dx KL(p∣∣q)=∫p(x)logq(x)p(x) dx
公式8 可以转换为:
m a x D V ( D , G ) = − 2 l o g 2 + K L ( p r ( x ) ∣ ∣ p r ( x ) + p g ( x ) 2 ) + K L ( p g ( x ) ∣ ∣ p r ( x ) + p g ( x ) 2 ) ( 9 ) max_D V(D,G) = -2log2 + KL(p_r(x)\ ||\ {{p_r(x)+p_g(x)}\over{2}})+KL(p_g(x) \ ||\ {{p_r(x)+p_g(x)}\over{2}}) \ \ \ \ \ \ \ \ \ \ (9) maxDV(D,G)=−2log2+KL(pr(x) ∣∣ 2pr(x)+pg(x))+KL(pg(x) ∣∣ 2pr(x)+pg(x)) (9)
为了方便对比,这里列出 J S JS JS 公式:
J S ( P ∣ ∣ Q ) = 1 2 K L ( P ∣ ∣ P + Q 2 ) + 1 2 K L ( Q ∣ ∣ P + Q 2 ) JS(P \ ||\ Q ) = {{1}\over{2}}KL(P\ ||\ {{P+Q}\over{2}})+{{1}\over{2}}KL(Q \ ||\ {{P+Q}\over{2}}) JS(P ∣∣ Q)=21KL(P ∣∣ 2P+Q)+21KL(Q ∣∣ 2P+Q)
可以将公式9 转换为:
m a x D V ( D , G ) = − 2 l o g 2 + 2 J S ( p r ( x ) ∣ ∣ p g ( x ) ) ( 10 ) max_D V(D,G) = -2log2 + 2JS(\ p_r(x)\ || \ p_g(x)) \ \ \ \ \ \ \ \ \ \ (10) maxDV(D,G)=−2log2+2JS( pr(x) ∣∣ pg(x)) (10)
公式10表示当生成模型固定时,判别模型的最优输出。公式推导结束。
1.4 其他思考
- 固定判别模型,优化生成模型是否可行?
- 什么情况下, m a x D V ( D , G ) max_DV(D,G) maxDV(D,G) 取得最小值?
- 推导出公式10 有什么意义?
这些问题留给我们一起讨论,欢迎 在后面留言发表自己的观点。如果表述清楚将会摘录在博客中。
2. 代码相关
2.1 概述 DC-GAN(Deep Convolution GAN)
本部分参考基于keras 的 GAN 实现,源代码来自于 https://github.com/jacobgil/keras-dcgan/blob/master/dcgan.py 。
- 基于 Keras 的 GAN 的实现;
- 使用 MNIST 数据集;
- 放弃使用全连接层;
- 含卷积层;
- 将卷积层的非线性部分换成 ReLU。
2.2 源码详解
首先明确一下目标:我们需要训练得到一个很厉害的生成模型,这个生成模型可以较好的生成 0-9 的数字。注意这个地方并不是 Encode 和 Decode 过程,而是使用对抗训练的方式得到一个生成模型,从而产生很好的假的样本。
接下来的部分完全是结合 https://github.com/jacobgil/keras-dcgan/blob/master/dcgan.py 代码进行分析。
2.2.1 概述各个函数
generator_model():生成模型网络结构。
discriminator_model():判别模型网络结构。
generator_containing_discriminator(generator, discriminator) :对生成模型的效果进行判定。
combine_images(generated_images):组合图片,排版方便显示。
train(BATCH_SIZE) :训练过程。
generate(BATCH_SIZE, nice=False) :生成图片。
get_args() :运行main函数时读取附带的参数。
2.2.2 train 函数
这个部分直接贴所有代码:
def train(BATCH_SIZE):# 读取数据并且进行处理,使得所有像素点的值的范围在 [-1,1)(X_train, y_train), (X_test, y_test) = mnist.load_data()X_train = (X_train.astype(np.float32) - 127.5)/127.5X_train = X_train.reshape((X_train.shape[0], 1) + X_train.shape[1:])# 得到判别模型与生成模型discriminator = discriminator_model()generator = generator_model()# 对生成模型的效果进行判定discriminator_on_generator = \generator_containing_discriminator(generator, discriminator)# 优化器d_optim = SGD(lr=0.0005, momentum=0.9, nesterov=True)g_optim = SGD(lr=0.0005, momentum=0.9, nesterov=True)generator.compile(loss='binary_crossentropy', optimizer="SGD")discriminator_on_generator.compile(loss='binary_crossentropy', optimizer=g_optim)discriminator.trainable = Truediscriminator.compile(loss='binary_crossentropy', optimizer=d_optim)# 注意如果希望早点结束,把下面两行 100 改小noise = np.zeros((BATCH_SIZE, 100))for epoch in range(100):print("Epoch is", epoch)print("Number of batches", int(X_train.shape[0]/BATCH_SIZE))for index in range(int(X_train.shape[0]/BATCH_SIZE)):for i in range(BATCH_SIZE):noise[i, :] = np.random.uniform(-1, 1, 100)image_batch = X_train[index*BATCH_SIZE:(index+1)*BATCH_SIZE]generated_images = generator.predict(noise, verbose=0)# 保存图片if index % 20 == 0:image = combine_images(generated_images)image = image*127.5+127.5Image.fromarray(image.astype(np.uint8)).save(str(epoch)+"_"+str(index)+".png")# 拼接一下真样本与假样本X = np.concatenate((image_batch, generated_images))# 假样本设置标签为 0y = [1] * BATCH_SIZE + [0] * BATCH_SIZE# 判别模型的 loss d_loss = discriminator.train_on_batch(X, y)print("batch %d d_loss : %f" % (index, d_loss))noise = np.random.uniform(-1, 1, (BATCH_SIZE, 100))d.trainable = False# 生成模型的 loss (根据判别模型的输出)g_loss = d_on_g.train_on_batch(noise, [1] * BATCH_SIZE)d.trainable = Trueprint("batch %d g_loss : %f" % (index, g_loss))if index % 10 == 9:g.save_weights('generator', True)d.save_weights('discriminator', True)
具体内容请前去 https://github.com/jacobgil/keras-dcgan/blob/master/dcgan.py 复制代码运行查看效果。
2.2.3 运行效果
我的真实环境是:2核8G的云服务器
,anaconda notebook
/ keras
/ tensorflow 1.5
,需要运行很长时间。所以一定别忘了把默认的 100
改小查看效果,比如先改成 10
,体验一下需要多长时间。当然,这个与真实环境有关,高低配置之间差距还是挺大的。另外可以考虑把保存图片的周期调长,避免保存太多太多图片(比如说每 10 个 epoch 保存一次)。
因此如果希望能取得更好地运行效果,推荐使用免费的 Colab(请自行查阅),由谷歌提供的运行平台,非常方便切换为 GPU 模式运行。当然,如果不缺金,推荐使用 阿里 提供的 GPU 云服务器,相对来说限制更加少,更加方便。
具体运行过程是执行所有代码后,在 notebook 中输入:train(128)
进行训练,再输入 generate(BATCH_SIZE=128)
运行输出内容大致包括:
WARNING:tensorflow:From /root/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/ops/nn_impl.py:183: where (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where
Epoch is 0
Number of batches 468
WARNING:tensorflow:From /root/anaconda3/lib/python3.7/site-packages/keras/backend/tensorflow_backend.py:422: The name tf.global_variables is deprecated. Please use tf.compat.v1.global_variables instead.batch 0 d_loss : 0.642198
batch 0 g_loss : 0.746685
batch 1 d_loss : 0.637740
batch 1 g_loss : 0.737049
batch 2 d_loss : 0.632032
batch 2 g_loss : 0.733727
batch 3 d_loss : 0.616466
batch 3 g_loss : 0.724211
batch 4 d_loss : 0.609789
batch 4 g_loss : 0.721907
batch 5 d_loss : 0.585964
...
batch 15 d_loss : 0.470644
batch 15 g_loss : 0.620105
batch 16 d_loss : 0.467800
batch 16 g_loss : 0.608375
batch 17 d_loss : 0.467602
batch 17 g_loss : 0.608196
batch 18 d_loss : 0.456245
...
epoch is 1
...
并且保存了一堆图片,此处选择若干张展示如下(epoch分别等于0,30,60,90):
0 | 30
60 | 90
花了好长好长时间训练完成以后,运行 generate 函数生成图片:
generate(128)
保存生成的图片 generated_image.png
。
图片如下:
然后带 nice 参数运行,生成更加好的图片。
generate(128,True)
2.2.4 使用 colab 注意事项
推荐使用免费的 colab (需要访问谷歌),使用时需要注意以下问题:
- 使用 tensorflow 1.x 。为了避免报错,在运行前第一行输入以下魔法命令,切换为 tensorflow 1.x
%tensorflow_version 1.x
- 避免保存太多图片,如果太多图片 colab 可能出现无法加载的情况。找到源码保存图片的那个地方,
if index % 20 == 0:....
修改为:
if epoch % 10 == 0 and index == 0:...
2.2.5 补充问题
- 前面花了不少篇幅的公式推导内容去哪里了?
- GAN 是一种无监督模型,那么代码中却存存在的 label 是用来做什么的?
3. GAN 的发展
为了避免篇幅太长,这里只是极其简单地提出 几个常见的 GAN 模型。
3.1 Conditional GAN (cGAN)
cGAN:更加适合于解决带标签数据的生成问题。
简单理解:在生成数据时,输入数据除了 z z z (请结合 1.2 中的结构图) ,还添加了生成目标数据附加的条件。
一般情况下,cGAN 生成数据效果优于标准的 GAN。
3.2 Info-GAN
Info-GAN:在 GAN 的基础上,Info-GAN 解决了隐变量可解释性问题。
简单理解:在 GAN 的基础上,增加了对隐变量的关注(通过修改损失函数)。
3.3 Wasserstein GAN (WGAN)
Wassertein GAN:基于 Wasserstein 距离的 GAN 模型。
简单理解:将原来的 JS 距离 替换为 Wasserstein 距离,具体内容推荐参考这篇 博客。
4. 总结
花了大量的时间跑代码,分别在自己的云服务器与谷歌的 colab 上运行,并且得到了一系列的生成图片。这只是一个简单的实验,初步体验一下 GAN 的效果。
关于 GAN 的理论推导比较简单,中间用到了求导,求极大值的过程。
当然关于 GAN 的内容太多太多,这里只是介绍其中简单通用的一部分。
感谢 您的 阅读、收藏、点赞 与 关注 感谢!
如果任何疑问,欢迎 留言
参考文献:
[1] 《深度学习轻松学·核心算法与视觉实践》 冯超 著 电子工业出版社
[2] 《生成对抗网络入门指南》史丹青 著 机械工业出版社
[3] keras-dcgan 源码地址
[4] KL散度、交叉熵与JS散度数学公式以及代码例子
[5] 谷歌提供的 colab 平台
[6] 阿里云提供的 GPU 服务器 购买地址
[7] 关于 WGAN 博客
编写不易,拒绝白piao。。。
感谢 您的 阅读、点赞、收藏 和 评论 ,别忘了 还可以 关注 一下哈,感谢 您的支持!
Smileyan
2021.4.9 19:49
GAN 数学原理简单介绍以及代码实践相关推荐
- BP神经网络原理简单介绍以及公式推导(矩阵形式和分量形式)
BP神经网络原理简单介绍以及公式推导 标签(空格分隔): 神经网络 \def\net(#1){net^{(#1)}} \def\Y(#1){Y^{(#1)}} \def\part(#1){\parti ...
- LDAP服务器的概念和原理简单介绍
仅用于个人学习,侵删. 本文转自:LDAP服务器的概念和原理简单介绍 [http://seanlook.com/2015/01/15/openldap_introduction/] 1. 目录服务 目 ...
- KeyBert关键词提取 :原理、方法介绍、代码实践
@创建于:20210708 @修改于:20210708, 20221104 文章目录 1 概述 2 方法介绍 2.1 安装 2.2 KeyBERT(model) 2.3 extract_keyword ...
- CAS单点登录原理简单介绍
1. SSO简介 1.1 单点登录定义 单点登录(Single sign on),英文名称缩写SSO,SSO的意思就是在多系统的环境中,登录单方系统,就可以在不用再次登录的情况下访问相关受信任的系统. ...
- Android通过辅助功能实现抢微信红包原理简单介绍
简书文章:https://www.jianshu.com/p/e1099a94b979 附抢红包开源项目地址,代码已全改为Kotlin了,已适配到最新微信7.0.5版本,如果对你有所帮助赏个star吧 ...
- 最小二乘法的数学原理推导及python代码
1. 什么是最小二乘法? 最小二乘法(Ordinary Least Squares)是一种常用的数据拟合方法,它通过最小误差的平方和来找到一组数据的最佳函数匹配. 很多软件中都包含最小二乘法功能的模块 ...
- dubbo学习过程、使用经验分享及实现原理简单介绍
一.前言 部门去年年中开始各种改造,第一步是模块服务化,这边初选dubbo试用在一些非重要模块上,慢慢引入到一些稍微重要的功能上,半年时间,学习过程及线上使用遇到的些问题在此总结下. 整理这篇文章差不 ...
- dubbo学习过程、使用经验分享及实现原理简单介绍,dubbo经验分享
原文出处http://blog.csdn.net/hzzhoushaoyu/article/details/43273099 一.前言 部门去年年中开始各种改造,第一步是模块服务化,这边初选dubbo ...
- ResNet简单介绍+Pytroch代码实现
文章目录 一.背景介绍 二.ResNet网络结构 1.ResNet34结构示意图 2.不同层数的ResNet采用的Block结构. 3.不同层数的ResNet网络结构示意图 4.实验结果 三.Pytr ...
最新文章
- 二分查找(递归和非递归)
- NSkyKit 项目实践-Dagger2
- pygame加载中文名mp3文件出现error
- Java设计模式-Proxy代理模式
- linux c ecb 加密解密,OpenSSL对数组加密解密的完整实现代码
- Ubuntu 12.04(所有ubuntu发行版都适用)sudo免输入密码
- 用HTML做一份个人简历
- 自己为 GridView 写分页 如: [首页][上一页][下一页][末页]
- python except exception_Python 获取异常(Exception)信息的几种方法
- 2017 ACM - ICPC Asia Ho Chi Minh City Regional Contest
- Android 解决帧动画卡顿问题
- JMeter——》调整界面比例、字体大小
- HDU 5952 Counting Cliques(2016ACM/ICPC亚洲区沈阳站-重现赛)
- 【报告分享】万物互联时代的操作系统报告(附下载)
- 软件测试硬件培训,软件测试和硬件测试的技巧
- 阿里云生成支付二维码并支付前端实现
- Vue3时间轴(Timeline)
- Python图像处理1:导入图像
- 拉青电站调速器用比例阀实验出现问题
- 使用JabRef在WORD中自动引用参考文献的方法