参考并翻译教程:https://d2l.ai/chapter_generative-adversarial-networks/gan.html,加入笔者的理解和心得

1。生成对抗网络原理

在Colab中打开笔记本

在某种形式上,我们使用了深度神经网络学习的从数据示例到标签的映射。这种学习称为判别学习,例如,我们希望能够区分照片中的猫和狗中的照片。分类器和回归器都是歧视性学习的例子。通过反向传播训练的神经网络颠覆了我们认为关于大型复杂数据集的判别式学习的所有知识。在短短5至6年间,高分辨率图像的分类精度已从无用提高到了人类水平。我们将为您提供其他所有关于深度神经网络效果惊人的其他判别任务的帮助。

但是,机器学习不仅仅是解决区分性任务。例如,给定一个大型数据集,而没有任何标签,我们可能想要学习一个可以准确捕获此数据特征的模型。给定这样一个模型,我们可以对类似于训练数据分布的综合数据示例进行采样。例如,给定大量的面部照片,我们可能希望能够生成新的真实感图像,看起来好像它可能来自同一数据集。这种学习称为生成建模。

直到最近,我们还没有能够合成新颖的逼真的图像的方法。但是,深度神经网络在判别式学习中的成功开辟了新的可能性。在过去三年中,一大趋势是应用区分性深网来克服我们通常不认为是监督学习问题的问题中的挑战。递归神经网络语言模型是使用判别网络(经过训练可预测下一个字符)的一个示例,该判别网络一旦受过训练就可以充当生成模型。

2014年,一篇突破性的论文介绍了生成对抗网络(GAN)[Goodfellow et al。,2014],这是一种利用判别模型的力量来获得良好生成模型的聪明新方法。GAN的核心思想是,如果我们不能分辨真实数据之外的虚假数据,那么数据生成器就很好。在统计中,这称为两次抽样检验-回答数据集是否存在的问题的检验X={x1,…,xn}和 X′={x′1,…,x′n}是从相同的分布中得出的。大多数统计文件与GAN之间的主要区别在于,后者以建设性的方式使用了这一思想。换句话说,他们不只是训练模型说“嘿,这两个数据集看起来好像不是来自同一分布”,而是使用两次样本检验为生成的模型提供训练信号。这使我们能够改进数据生成器,直到它生成类似于真实数据的内容为止。至少,它需要愚弄分类器。即使我们的分类器是最先进的深度神经网络。

图1.1生成对抗网络

GAN架构图1。如您所见,GAN架构中有两个部分-首先,我们需要一个设备(例如,深层网络,但实际上可能是任何东西,例如游戏渲染引擎),它可能能够生成看起来很漂亮的数据。就像真实的东西一样。如果要处理图像,则需要生成图像。如果要处理语音,则需要生成音频序列,依此类推。我们称其为生成器网络。第二部分是鉴别器网络。它试图将伪造数据与真实数据区分开。这两个网络相互竞争。生成器网络尝试欺骗鉴别器网络。在这一点上,鉴别器网络适应了新的伪造数据。该信息继而用于改善生成器网络,等等。

鉴别器是一个二进制分类器,用于区分输入是否 x是真实的(来自真实数据)还是伪造的(来自生成器)。通常,鉴别器输出标量预测 o∈R 用于输入 x,例如使用隐藏大小为1的密集层,然后应用S形函数来获得预测的概率 D(x)=1/(1+e−o)。假设标签y 因为真正的数据是 1 和 0虚假数据。我们培养鉴别,以尽量减少交叉熵损失,

(1.1) minD{−ylogD(x)−(1−y)log(1−D(x))},

对于生成器,它首先绘制一些参数 z∈Rd来自随机源,例如正态分布z∼N(0,1)。我们经常称z为隐变量。然后应用一个函数来生成x′=G(z)。生成器的目的是欺骗鉴别器进行分类 x′=G(z)作为真实数据,我们想要 D(G(z))≈1。换句话说,对于给定的歧视者D,我们更新生成器的参数 G 最大化交叉熵损失 y=0,

(1.2) maxG{−(1−y)log(1−D(G(z)))}=maxG{−log(1−D(G(z)))}.

如果生成器做得很好,那么 D(x′)≈1 因此上述损失接近于0,这导致梯度太小而无法使鉴别器取得良好的进展。因此,通常我们将以下损失降到最低:

(1.3) minG{−ylog(D(G(z)))}=minG{−log(D(G(z)))},

这只是送 x′=G(z) 进入鉴别器,但给标签 y=1.

总结, D 和 G正在玩具有综合目标功能的“ minimax”游戏:

(1.4)minDmaxG{−Ex∼DatalogD(x)−Ez∼Noiselog(1−D(G(z)))}.

许多GAN应用程序都在图像的上下文中。作为演示目的,我们将首先满足于简化演示版。我们将说明如果使用GAN为高斯数据建立参数估计器,将会发生什么情况。让我们开始吧。

%matplotlib inline
import torch
from torch import nn
from d2l import torch as d2l

1.1。生成一些“真实”数据

由于这将是世界上最繁琐的示例,因此我们只需要从高斯生成数据即可。

X = torch.normal(0.0, 1, (1000, 2))
A = torch.tensor([[1, 2], [-0.1, 0.5]])
b = torch.tensor([1, 2])
data = torch.matmul(X, A) + b

让我们看看我们得到了什么。这应该是以某种相当随意的方式用均值平移的高斯b和协方差矩阵 ATA.

d2l.set_figsize()
d2l.plt.scatter(data[:100, (0)].detach().numpy(), data[:100,(1)].detach().numpy())
print(f'The covariance matrix is\n{torch.matmul(A.T, A)}')

The covariance matrix is
tensor([[1.0100, 1.9500],[1.9500, 4.2500]])

batch_size = 8
data_iter = d2l.load_array((data,), batch_size)

1.2。生成器

我们的生成器网络将是最简单的网络-单层线性模型。这是因为我们将使用高斯数据生成器来驱动线性网络。因此,它实际上只需要学习参数就可以完美地伪造事物。

net_G = nn.Sequential(nn.Linear(2, 2))

1.3。判别器

对于区分器,我们将更具区分性:我们将使用具有3层的MLP,使事情变得更加有趣。

net_D = nn.Sequential(nn.Linear(2, 5), nn.Tanh(), nn.Linear(5, 3), nn.Tanh(),nn.Linear(3, 1))

1.4。训练

首先,我们定义一个函数来更新鉴别器。

#@save
def update_D(X, Z, net_D, net_G, loss, trainer_D):"""Update discriminator."""batch_size = X.shape[0]ones = torch.ones((batch_size,), device=X.device)zeros = torch.zeros((batch_size,), device=X.device)trainer_D.zero_grad()real_Y = net_D(X)fake_X = net_G(Z)# Do not need to compute gradient for `net_G`, detach it from# computing gradients.fake_Y = net_D(fake_X.detach())loss_D = (loss(real_Y, ones.reshape(real_Y.shape)) +loss(fake_Y, zeros.reshape(fake_Y.shape))) / 2loss_D.backward()trainer_D.step()return loss_D

生成器的更新类似。在这里,我们复用了交叉熵损失,但是标签从0变为了1.

#@save
def update_G(Z, net_D, net_G, loss, trainer_G):"""Update generator."""batch_size = Z.shape[0]ones = torch.ones((batch_size,), device=Z.device)trainer_G.zero_grad()# We could reuse `fake_X` from `update_D` to save computationfake_X = net_G(Z)# Recomputing `fake_Y` is needed since `net_D` is changedfake_Y = net_D(fake_X)loss_G = loss(fake_Y, ones.reshape(fake_Y.shape))loss_G.backward()trainer_G.step()return loss_G

鉴别器和生成器都执行具有交叉熵损失的二进制逻辑回归。我们使用封装来简化培训过程。在每次迭代中,我们首先更新鉴别器,然后更新生成器。我们将损失和生成的示例可视化。

def train(net_D, net_G, data_iter, num_epochs, lr_D, lr_G, latent_dim, data):loss = nn.BCEWithLogitsLoss(reduction='sum')for w in net_D.parameters():nn.init.normal_(w, 0, 0.02)for w in net_G.parameters():nn.init.normal_(w, 0, 0.02)trainer_D = torch.optim.Adam(net_D.parameters(), lr=lr_D)trainer_G = torch.optim.Adam(net_G.parameters(), lr=lr_G)animator = d2l.Animator(xlabel='epoch', ylabel='loss',xlim=[1, num_epochs], nrows=2, figsize=(5, 5),legend=['discriminator', 'generator'])animator.fig.subplots_adjust(hspace=0.3)for epoch in range(num_epochs):# Train one epochtimer = d2l.Timer()metric = d2l.Accumulator(3)  # loss_D, loss_G, num_examplesfor (X,) in data_iter:batch_size = X.shape[0]Z = torch.normal(0, 1, size=(batch_size, latent_dim))metric.add(update_D(X, Z, net_D, net_G, loss, trainer_D),update_G(Z, net_D, net_G, loss, trainer_G), batch_size)# Visualize generated examplesZ = torch.normal(0, 1, size=(100, latent_dim))fake_X = net_G(Z).detach().numpy()animator.axes[1].cla()animator.axes[1].scatter(data[:, 0], data[:, 1])animator.axes[1].scatter(fake_X[:, 0], fake_X[:, 1])animator.axes[1].legend(['real', 'generated'])# Show the lossesloss_D, loss_G = metric[0] / metric[2], metric[1] / metric[2]animator.add(epoch + 1, (loss_D, loss_G))print(f'loss_D {loss_D:.3f}, loss_G {loss_G:.3f}, 'f'{metric[2] / timer.stop():.1f} examples/sec')

现在,我们指定超参数以拟合高斯分布。

lr_D, lr_G, latent_dim, num_epochs = 0.05, 0.005, 2, 20
train(net_D, net_G, data_iter, num_epochs, lr_D, lr_G, latent_dim,data[:100].detach().numpy())

loss_D 0.693, loss_G 0.693, 1519.0 examples/sec

1.5。概括

  • 生成对抗网络(GAN)由两个深层网络(生成器和鉴别器)组成。

  • 生成器通过最大化交叉熵损失(即)来生成尽可能接近真实图像的图像,以欺骗鉴别器。maxlog(D(x′)).

  • 鉴别器尝试从真实图像区分所生成的图像,通过最小化交叉熵损失,, min−ylogD(x)−(1−y)log(1−D(x)).

1.6。练习题

  • 在生成器获胜的地方是否存在均衡,鉴别器最终无法区分有限样本上的两个分布?

2。深度卷积生成对抗网络

Open the notebook in Colab

在1节中,我们介绍了GAN的工作原理。我们证明了他们可以从一些简单,易于采样的分布(如均匀分布或正态分布)中抽取样本,并将它们转换为看起来与某些数据集的分布相匹配的样本。尽管我们提出的匹配2D高斯分布的例子很明确,但这并不是特别令人兴奋。

在本节中,我们将演示如何使用GAN生成逼真的图像。我们将基于[Radford et al。,2015]中引入的深度卷积GAN(DCGAN)建立模型 。我们将借鉴已证明在区分计算机视觉问题上非常成功的卷积架构,并展示如何通过GAN来利用它们来生成逼真的图像。

import warnings
import torch
import torchvision
from torch import nn
from d2l import torch as d2l

2.1。宠物小精灵数据集

我们将使用的数据集是从pokemondb获得的Pokemon精灵的集合 。首先下载,提取并加载此数据集。

#@save
d2l.DATA_HUB['pokemon'] = (d2l.DATA_URL + 'pokemon.zip','c065c0e2593b8b161a2d7873e42418bf6a21106c')data_dir = d2l.download_extract('pokemon')
pokemon = torchvision.datasets.ImageFolder(data_dir)

Downloading ../data/pokemon.zip from http://d2l-data.s3-accelerate.amazonaws.com/pokemon.zip...

我们将每个图像调整为 64×64。该ToTensor 改造项目将像素值成[0,1],而我们的生成器将使用tanh函数来获取 [−1,1]。因此我们用0.5 意思是和 0.5 标准偏差以匹配值范围。

batch_size = 256
transformer = torchvision.transforms.Compose([torchvision.transforms.Resize((64, 64)),torchvision.transforms.ToTensor(),torchvision.transforms.Normalize(0.5, 0.5)])
pokemon.transform = transformer
data_iter = torch.utils.data.DataLoader(pokemon, batch_size=batch_size, shuffle=True,num_workers=d2l.get_dataloader_workers())

让我们可视化前20张图像。

warnings.filterwarnings('ignore')
d2l.set_figsize((4, 4))
for X, y in data_iter:imgs = X[0:20, :, :, :].permute(0, 2, 3, 1) / 2 + 0.5d2l.show_images(imgs, num_rows=4, num_cols=5)break

2.2。生成器

生成器需要映射噪声变量 z∈Rd,长度-d 向量,将RGB图像的宽度和高度设置为 64×64。使用转置卷积层来扩大输入大小的完全卷积网络 。生成器的基本块包含一个转置的卷积层,然后进行批量归一化和ReLU激活。

class G_block(nn.Module):def __init__(self, out_channels, in_channels=3, kernel_size=4, strides=2,padding=1, **kwargs):super(G_block, self).__init__(**kwargs)self.conv2d_trans = nn.ConvTranspose2d(in_channels, out_channels,kernel_size, strides, padding,bias=False)self.batch_norm = nn.BatchNorm2d(out_channels)self.activation = nn.ReLU()def forward(self, X):return self.activation(self.batch_norm(self.conv2d_trans(X)))

默认情况下,转置的卷积层使用 kh=kw=4  内核,一个 sh=sw=2  大步向前, ph=pw=1 填充。输入形状为 n′h×n′w=16×16 ,生成器块将输入的宽度和高度加倍。

x = torch.zeros((2, 3, 16, 16))
g_blk = G_block(20)
g_blk(x).shape
torch.Size([2, 20, 32, 32])

如果将转置的卷积层更改为 4×4 核心, 1×1大步前进和零填充。输入大小为 1×1,输出的宽度和高度将分别增加3。

x = torch.zeros((2, 3, 1, 1))
g_blk = G_block(20, strides=1, padding=0)
g_blk(x).shape

torch.Size([2, 20, 4, 4])

生成器由四个基本块组成,这些块将输入的宽度和高度从1增加到32。同时,它首先将隐变量投影到 64×8频道,然后每次将频道减半。最后,转置的卷积层用于生成输出。它进一步将宽度和高度加倍以匹配所需的64×64 形状,并将通道尺寸减小到 3。tanh激活功能适用于将输出值投影到(−1,1) 范围。

n_G = 64
net_G = nn.Sequential(G_block(in_channels=100, out_channels=n_G * 8, strides=1,padding=0),  # Output: (64 * 8, 4, 4)G_block(in_channels=n_G * 8,out_channels=n_G * 4),  # Output: (64 * 4, 8, 8)G_block(in_channels=n_G * 4,out_channels=n_G * 2),  # Output: (64 * 2, 16, 16)G_block(in_channels=n_G * 2, out_channels=n_G),  # Output: (64, 32, 32)nn.ConvTranspose2d(in_channels=n_G, out_channels=3,kernel_size=4, stride=2, padding=1, bias=False),nn.Tanh())  # Output: (3, 64, 64)

生成一个100维的潜在变量,以验证生成器的输出形状。

x = torch.zeros((1, 100, 1, 1))
net_G(x).shape

torch.Size([1, 3, 64, 64])

2.3。判别器

鉴别器是普通的卷积网络,只是它使用泄漏的ReLU作为其激活功能。给定 α∈[0,1],其定义是

可以看出,如果 α=0,以及一个身份函数(如果 α=1。为了α∈(0,1),泄漏ReLU是一个非线性函数,为负输入提供非零输出。它旨在解决“失活的ReLU”问题,因为神经元可能总是输出负值,因此由于ReLU的梯度为0,因此无法得到更新。

alphas = [0, .2, .4, .6, .8, 1]
x = torch.arange(-2, 1, 0.1)
Y = [nn.LeakyReLU(alpha)(x).detach().numpy() for alpha in alphas]
d2l.plot(x.detach().numpy(), Y, 'x', 'y', alphas)

鉴别器的基本模块是卷积层,然后是批处理归一化层和泄漏的ReLU激活。卷积层的超参数类似于生成器块中的转置卷积层。

class D_block(nn.Module):def __init__(self, out_channels, in_channels=3, kernel_size=4, strides=2,padding=1, alpha=0.2, **kwargs):super(D_block, self).__init__(**kwargs)self.conv2d = nn.Conv2d(in_channels, out_channels, kernel_size,strides, padding, bias=False)self.batch_norm = nn.BatchNorm2d(out_channels)self.activation = nn.LeakyReLU(alpha, inplace=True)def forward(self, X):return self.activation(self.batch_norm(self.conv2d(X)))

将具有默认设置的基本块将使输入的宽度和高度减半。例如,给定输入形状nh=nw=16,具有内核形状 kh=kw=4,一个大步的形状 sh=sw=2以及填充形状 ph=pw=1,输出形状将为:

x = torch.zeros((2, 3, 16, 16))
d_blk = D_block(20)
d_blk(x).shape

torch.Size([2, 20, 8, 8])

鉴别器与生成器对应的。

n_D = 64
net_D = nn.Sequential(D_block(n_D),  # Output: (64, 32, 32)D_block(in_channels=n_D,out_channels=n_D * 2),  # Output: (64 * 2, 16, 16)D_block(in_channels=n_D * 2,out_channels=n_D * 4),  # Output: (64 * 4, 8, 8)D_block(in_channels=n_D * 4,out_channels=n_D * 8),  # Output: (64 * 8, 4, 4)nn.Conv2d(in_channels=n_D * 8, out_channels=1, kernel_size=4,bias=False))  # Output: (1, 1, 1)

它使用带输出通道的卷积层 11 作为获取单个预测值的最后一层。

x = torch.zeros((1, 3, 64, 64))
net_D(x).shape

torch.Size([1, 1, 1, 1])

2.4。训练

与1节中的基本GAN相比,我们对生成器和鉴别器使用相同的学习率,因为它们彼此相似。另外,改变β1中0.9 至 0.5。它会降低动量的平滑度(过去的梯度的指数加权移动平均值),以照顾快速变化的梯度,因为生成器和鉴别器会相互竞争。此外,随机产生的噪声Z为4-D张量,我们使用GPU来加速计算。

def train(net_D, net_G, data_iter, num_epochs, lr, latent_dim,device=d2l.try_gpu()):loss = nn.BCEWithLogitsLoss(reduction='sum')for w in net_D.parameters():nn.init.normal_(w, 0, 0.02)for w in net_G.parameters():nn.init.normal_(w, 0, 0.02)net_D, net_G = net_D.to(device), net_G.to(device)trainer_hp = {'lr': lr, 'betas': [0.5, 0.999]}trainer_D = torch.optim.Adam(net_D.parameters(), **trainer_hp)trainer_G = torch.optim.Adam(net_G.parameters(), **trainer_hp)animator = d2l.Animator(xlabel='epoch', ylabel='loss',xlim=[1, num_epochs], nrows=2, figsize=(5, 5),legend=['discriminator', 'generator'])animator.fig.subplots_adjust(hspace=0.3)for epoch in range(1, num_epochs + 1):# Train one epochtimer = d2l.Timer()metric = d2l.Accumulator(3)  # loss_D, loss_G, num_examplesfor X, _ in data_iter:batch_size = X.shape[0]Z = torch.normal(0, 1, size=(batch_size, latent_dim, 1, 1))X, Z = X.to(device), Z.to(device)metric.add(d2l.update_D(X, Z, net_D, net_G, loss, trainer_D),d2l.update_G(Z, net_D, net_G, loss, trainer_G),batch_size)# Show generated examplesZ = torch.normal(0, 1, size=(21, latent_dim, 1, 1), device=device)# Normalize the synthetic data to N(0, 1)fake_x = net_G(Z).permute(0, 2, 3, 1) / 2 + 0.5imgs = torch.cat([torch.cat([fake_x[i * 7 + j].cpu().detach()for j in range(7)], dim=1)for i in range(len(fake_x) // 7)], dim=0)animator.axes[1].cla()animator.axes[1].imshow(imgs)# Show the lossesloss_D, loss_G = metric[0] / metric[2], metric[1] / metric[2]animator.add(epoch, (loss_D, loss_G))print(f'loss_D {loss_D:.3f}, loss_G {loss_G:.3f}, 'f'{metric[2] / timer.stop():.1f} examples/sec on {str(device)}')

我们仅以少数几个时期来训练模型,仅用于演示。为了获得更好的性能,num_epochs可以将变量设置为更大的数字。

latent_dim, lr, num_epochs = 100, 0.005, 20
train(net_D, net_G, data_iter, num_epochs, lr, latent_dim)
loss_D 0.020, loss_G 8.420, 1082.3 examples/sec on cuda:0

 

2.5。概括

  • DCGAN体系结构具有四个用于鉴别器的卷积层和四个用于生成器的“小跨度”卷积层。

  • 鉴别器是具有批归一化(输入层除外)和泄漏ReLU激活的4层跨卷积。

  • 泄漏的ReLU是一个非线性函数,为负输入提供非零输出。它旨在解决“垂死的ReLU”问题,并帮助渐变在整个体系结构中更轻松地流动。

2.6。练习题

  1. 如果我们使用标准的ReLU激活而不是泄漏的ReLU,将会发生什么?

  2. 将DCGAN应用于Fashion-MNIST,看看哪个类别有效,哪个类别无效。

『一起学AI』生成对抗网络(GAN)原理学习及实战开发相关推荐

  1. 【通知】《生成对抗网络GAN原理与实践》代码开源,勘误汇总!

    有三上个月出版了新书<生成对抗网络GAN:原理与实践>,Generative Adversarial Networks(中文名生成对抗网络,简称GAN)自从被提出来后,其发展就非常迅猛,几 ...

  2. 生成对抗网络gan原理_中国首个“芯片大学”即将落地;生成对抗网络(GAN)的数学原理全解...

    开发者社区技术周刊又和大家见面了,萌妹子主播为您带来第三期"开发者技术联播".让我们一起听听,过去一周有哪些值得我们开发者关注的重要新闻吧. 中国首个芯片大学,南京集成电路大学即将 ...

  3. 生成对抗网络gan原理_必读!TOP10生成对抗网络GAN论文(附链接)

    来源:新智元 本文约2200字,建议阅读7分钟. 本文所选论文提供了一个易读的对GAN的介绍,帮助你理解GAN技术的基础. [ 导读 ]生成对抗网络 (GAN) 是深度学习中最有趣.最受欢迎的应用之一 ...

  4. 从零开始学keras之生成对抗网络GAN

    生成对抗网络主要分为生成器网络和判别器网络. 生成器网络:他以一个随机向量(潜在空间的一个随机点)作为输入,并将其解码成一张合成图像. 判别器网络:以一张图像(真实的或合成的均可)作为输入,并预测该图 ...

  5. 图解 生成对抗网络GAN 原理 超详解

    生成对抗网络 一.背景 一般而言,深度学习模型可以分为判别式模型与生成式模型.由于反向传播(Back propagation, BP).Dropout等算法的发明,判别式模型得到了迅速发展.然而,由于 ...

  6. 条件生成对抗神经网络,生成对抗网络gan原理

    关于GAN生成式对抗网络中判别器的输出的问题 . ...摘要生成式对抗网络GAN(Generativeadversarialnetworks)目前已经成为人工智能学界一个热门的研究方向.GAN的基本思 ...

  7. 通俗易懂生成对抗网络GAN原理(一)

    生成对抗网络(Generative Adversarial Network, GAN) 学习李宏毅机器学习课程总结. 前言 生成式网络有什么特别的地方呢? 之前我们学到的神经网络就是一个函数,输入一个 ...

  8. 生成对抗网络gan原理_生成对抗网络(GAN)的半监督学习

    前言 如果您曾经听说过或研究过深度学习,那么您可能就知道MNIST, SVHN, ImageNet, PascalVoc或者其他数据集.这些数据集都有一个共同点: 它们由成千上万个有标签的数据组成. ...

  9. 生成对抗网络(GAN)原理和实现

    个人博客:http://www.chenjianqu.com/ 原文链接:http://www.chenjianqu.com/show-54.html 生成式对抗网络(GAN, Generative ...

最新文章

  1. 使用shell统计字符串出现的次数,并从大到小进行排序显示
  2. 戴尔存储副总裁谈戴尔-EqualLogic一周年庆
  3. django中的缓存以及跨域
  4. controller层没反应_埋地管道防腐层探测检漏仪FJ-10地下管线探测仪的说明及应用...
  5. Android Kotlin Coroutines ktx扩展
  6. 北大阿里中科院提出细粒度人体姿态迁移方法,提升外观细节逼真度
  7. #内存不够,swap来凑# Linux上创建SWAP文件/分区
  8. 联想万全t260磁盘阵列_联想万全T260G3服务器安装windows2008R2系统
  9. 记录一次 Win10 通过 VirtualBox安装CentOS7 的辛酸史
  10. python 文件读写 newline_「Python」:文件读写
  11. *第七周*数据结构实践项目三【负数把整数赶出队列】
  12. 4、第4次课 CSS代码第三节课20150923
  13. F - Good Words
  14. php 26进制转10进制,PHP 10进制转62进制
  15. 科来网络分析工具基本操作与案例分析
  16. python html 补全标签_补充:HTML标签和CSS
  17. 程序员的中年危机及路在何方?
  18. 同字母异序词 python_Python初学者必学的20个重要技巧
  19. 如果有一天我不更新博客了
  20. win7计算机文件夹折叠,win7系统折叠组窗口设置不折叠的操作方法

热门文章

  1. JAVA字符输入输出流
  2. UNL系列图——交互图
  3. 股指的趋势持续研究(Hurst指数)
  4. Umeng App监管执法合规自查通知
  5. Mac 安卓投屏Scrcpy使用
  6. 30行Python代码实现蚂蚁森林自动偷能量
  7. Google用AI技术为Allo增加表情符号建议按钮
  8. 奇安信行业安全研究中心
  9. 谷歌文件系统GFS理解
  10. 导出多个excel,打包成zip压缩包进行下载~~~~~~~~~