1 WGAN-div 简介

W散度的损失函数GAN-dv模型使用了W散度来替换W距离的计算方式,将原有的真假样本采样操作换为基于分布层面的计算。

2 代码实现

在WGAN-gp的基础上稍加改动来实现,重写损失函数的实现。

2.1 代码实战:引入模块并载入样本----WGAN_div_241.py(第1部分)

import torch
import torchvision
from torchvision import transforms
from torch.utils.data import DataLoader
from torch import nn
import torch.autograd as autograd
import matplotlib.pyplot as plt
import numpy as np
import matplotlib
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"# 1.1 引入模块并载入样本:定义基本函数,加载FashionMNIST数据集
def to_img(x):x = 0.5 * (x+1)x = x.clamp(0,1)x = x.view(x.size(0),1,28,28)return xdef imshow(img,filename = None):npimg = img.numpy()plt.axis('off')array = np.transpose(npimg,(1,2,0))if filename != None:matplotlib.image.imsave(filename,array)else:plt.imshow(array)# plt.savefig(filename) # 保存图片 注释掉,因为会报错,暂时不知道什么原因 2022.3.26 15:20plt.show()img_transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize(mean=[0.5],std=[0.5])]
)data_dir = './fashion_mnist'train_dataset = torchvision.datasets.FashionMNIST(data_dir,train=True,transform=img_transform,download=True)
train_loader = DataLoader(train_dataset,batch_size=1024,shuffle=True)
# 测试数据集
val_dataset = torchvision.datasets.FashionMNIST(data_dir,train=False,transform=img_transform)
test_loader = DataLoader(val_dataset,batch_size=10,shuffle=False)
# 指定设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)

2.2 代码实战:实现生成器和判别器----WGAN_div_241.py(第2部分)

# 1.2 实现生成器和判别器 :因为复杂部分都放在loss值的计算方面了,所以生成器和判别器就会简单一些。
# 生成器和判别器各自有两个卷积和两个全连接层。生成器最终输出与输入图片相同维度的数据作为模拟样本。
# 判别器的输出不需要有激活函数,并且输出维度为1的数值用来表示结果。
# 在GAN模型中,因判别器的输入则是具体的样本数据,要区分每个数据的分布特征,所以判别器使用实例归一化,
class WGAN_D(nn.Module): # 定义判别器类D :有两个卷积和两个全连接层def __init__(self,inputch=1):super(WGAN_D, self).__init__()self.conv1 = nn.Sequential(nn.Conv2d(inputch,64,4,2,1), # 输出形状为[batch,64,28,28]nn.LeakyReLU(0.2,True),nn.InstanceNorm2d(64,affine=True))self.conv2 = nn.Sequential(nn.Conv2d(64,128,4,2,1),# 输出形状为[batch,64,14,14]nn.LeakyReLU(0.2,True),nn.InstanceNorm2d(128,affine=True))self.fc = nn.Sequential(nn.Linear(128*7*7,1024),nn.LeakyReLU(0.2,True))self.fc2 = nn.Sequential(nn.InstanceNorm1d(1,affine=True),nn.Flatten(),nn.Linear(1024,1))def forward(self,x,*arg): # 正向传播x = self.conv1(x)x = self.conv2(x)x = x.view(x.size(0),-1)x = self.fc(x)x = x.reshape(x.size(0),1,-1)x = self.fc2(x)return x.view(-1,1).squeeze(1)# 在GAN模型中,因生成器的初始输入是随机值,所以生成器使用批量归一化。
class WGAN_G(nn.Module): # 定义生成器类G:有两个卷积和两个全连接层def __init__(self,input_size,input_n=1):super(WGAN_G, self).__init__()self.fc1 = nn.Sequential(nn.Linear(input_size * input_n,1024),nn.ReLU(True),nn.BatchNorm1d(1024))self.fc2 = nn.Sequential(nn.Linear(1024,7*7*128),nn.ReLU(True),nn.BatchNorm1d(7*7*128))self.upsample1 = nn.Sequential(nn.ConvTranspose2d(128,64,4,2,padding=1,bias=False), # 输出形状为[batch,64,14,14]nn.ReLU(True),nn.BatchNorm2d(64))self.upsample2 = nn.Sequential(nn.ConvTranspose2d(64,1,4,2,padding=1,bias=False), # 输出形状为[batch,64,28,28]nn.Tanh())def forward(self,x,*arg): # 正向传播x = self.fc1(x)x = self.fc2(x)x = x.view(x.size(0),128,7,7)x = self.upsample1(x)img = self.upsample2(x)return img

2.3 代码实战:计算w散度(WGAN-gp基础新增)----WGAN_div_241.py(第3部分)

# 1.3 计算w散度:返回值充当WGAN-gp中的惩罚项,用于计算判别器的损失
def compute_w_div(real_samples,real_out,fake_samples,fake_out):# 定义参数k = 2p = 6# 计算真实空间的梯度weight = torch.full((real_samples.size(0),),1,device=device)real_grad = autograd.grad(outputs=real_out,inputs=real_samples,grad_outputs=weight,create_graph=True,retain_graph=True,only_inputs=True)[0]# L2范数real_grad_norm = real_grad.view(real_grad.size(0),-1).pow(2).sum(1)# 计算模拟空间的梯度fake_grad = autograd.grad(outputs=fake_out,inputs=fake_samples,grad_outputs=weight,create_graph=True,retain_graph=True,only_inputs=True)[0]# L2范数fake_grad_norm = fake_grad.view(fake_grad.size(0),-1).pow(2).sum(1)# 计算W散度距离div_gp = torch.mean(real_grad_norm **(p/2)+fake_grad_norm**(p/2))*k/2return div_gp

2.4 代码实战:定义模型的训练函数(WGAN-gp基础修改)----WGAN_div_241.py(第4部分)

## 1.4 定义模型的训练函数
# 定义函数train(),实现模型的训练过程。
# 在函数train()中,按照对抗神经网络专题(一)中的式(8-24)实现模型的损失函数。
# 判别器的loss为D(fake_samples)-D(real_samples)再加上联合分布样本的梯度惩罚项gradient_penalties,其中fake_samples为生成的模拟数据,real_Samples为真实数据,
# 生成器的loss为-D(fake_samples)。
def train(D,G,outdir,z_dimension,num_epochs=30):d_optimizer = torch.optim.Adam(D.parameters(),lr=0.001) # 定义优化器g_optimizer = torch.optim.Adam(G.parameters(),lr=0.001)os.makedirs(outdir,exist_ok=True) # 创建输出文件夹# 在函数train()中,判别器和生成器是分开训练的。让判别器学习的次数多一些,判别器每训练5次,生成器优化1次。# WGAN_gp不会因为判别器准确率太高而引起生成器梯度消失的问题,所以好的判别器会让生成器有更好的模拟效果。for epoch in range(num_epochs):for i,(img,lab) in enumerate(train_loader):num_img = img.size(0)# 训练判别器real_img = img.to(device)y_one_hot = torch.zeros(lab.shape[0],10).scatter_(1,lab.view(lab.shape[0],1),1).to(device)for ii in range(5): # 循环训练5次d_optimizer.zero_grad() # 梯度清零real_img = real_img.requires_grad_(True) # 在WGAN-gp基础上新增,将输入参数real_img设置为可导# 对real_img进行判别real_out = D(real_img, y_one_hot)# 生成随机值z = torch.randn(num_img, z_dimension).to(device)fake_img = G(z, y_one_hot)  # 生成fake_imgfake_out = D(fake_img, y_one_hot)  # 对fake_img进行判别# 计算梯度惩罚项gradient_penalty_div = compute_w_div(real_img, real_out, fake_img, fake_out) # 使用gradient_penalty_div()求梯度# 计算判别器的lossd_loss = -torch.mean(real_out) + torch.mean(fake_out) + gradient_penalty_divd_loss.backward()d_optimizer.step()# 训练生成器for ii in range(1):g_optimizer.zero_grad()  # 梯度清0z = torch.randn(num_img, z_dimension).to(device)fake_img = G(z, y_one_hot)fake_out = D(fake_img, y_one_hot)g_loss = -torch.mean(fake_out)g_loss.backward()g_optimizer.step()# 输出可视化结果fake_images = to_img(fake_img.cpu().data)real_images = to_img(real_img.cpu().data)rel = torch.cat([to_img(real_images[:10]), fake_images[:10]], axis=0)imshow(torchvision.utils.make_grid(rel, nrow=10), os.path.join(outdir, 'fake_images-{}.png'.format(epoch + 1)))# 输出训练结果print('Epoch [{}/{}], d_loss: {:.6f}, g_loss: {:.6f} ''D real: {:.6f}, D fake: {:.6f}'.format(epoch, num_epochs,d_loss.data,g_loss.data,real_out.data.mean(),fake_out.data.mean()))# 保存训练模型torch.save(G.state_dict(), os.path.join(outdir, 'div-generator.pth'))torch.save(D.state_dict(), os.path.join(outdir, 'div-discriminator.pth'))

2.5 代码实战:实现可视化模型结果----WGAN_div_241.py(第5部分)

# 1.5 定义函数,实现可视化模型结果:获取一部分测试数据,显示由模型生成的模拟数据。
def displayAndTest(D,G,z_dimension):    # 可视化结果sample = iter(test_loader)images, labels = sample.next()y_one_hot = torch.zeros(labels.shape[0], 10).scatter_(1,labels.view(labels.shape[0], 1), 1).to(device)num_img = images.size(0) # 获取样本个数with torch.no_grad():z = torch.randn(num_img, z_dimension).to(device) # 生成随机数fake_img = G(z, y_one_hot)fake_images = to_img(fake_img.cpu().data) # 生成模拟样本rel = torch.cat([to_img(images[:10]), fake_images[:10]], axis=0)imshow(torchvision.utils.make_grid(rel, nrow=10))print(labels[:10])

2.6 代码实战:调用函数并训练模型----WGAN_div_241.py(第6部分)

# 1.6 调用函数并训练模型:实例化判别器和生成器模型,并调用函数进行训练
if __name__ == '__main__':z_dimension = 40  # 设置输入随机数的维度D = WGAN_D().to(device)  # 实例化判别器G = WGAN_G(z_dimension).to(device)  # 实例化生成器train(D, G, './w_img', z_dimension) # 训练模型displayAndTest(D, G, z_dimension) # 输出可视化

结果:

WGAN-dⅳ模型也会输出非常清晰的模拟样本。在有关WGAN-div的论文中,曾拿WGAN-div模型与WGAN-gp模型进行比较,发现WGAN-diⅳ模型的FID分数更高一些(FID是评价GAN生成图片质量的一种指标)。

3 代码总览(WGAN_div_241.py)

import torch
import torchvision
from torchvision import transforms
from torch.utils.data import DataLoader
from torch import nn
import torch.autograd as autograd
import matplotlib.pyplot as plt
import numpy as np
import matplotlib
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"# 1.1 引入模块并载入样本:定义基本函数,加载FashionMNIST数据集
def to_img(x):x = 0.5 * (x+1)x = x.clamp(0,1)x = x.view(x.size(0),1,28,28)return xdef imshow(img,filename = None):npimg = img.numpy()plt.axis('off')array = np.transpose(npimg,(1,2,0))if filename != None:matplotlib.image.imsave(filename,array)else:plt.imshow(array)# plt.savefig(filename) # 保存图片 注释掉,因为会报错,暂时不知道什么原因 2022.3.26 15:20plt.show()img_transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize(mean=[0.5],std=[0.5])]
)data_dir = './fashion_mnist'train_dataset = torchvision.datasets.FashionMNIST(data_dir,train=True,transform=img_transform,download=True)
train_loader = DataLoader(train_dataset,batch_size=1024,shuffle=True)
# 测试数据集
val_dataset = torchvision.datasets.FashionMNIST(data_dir,train=False,transform=img_transform)
test_loader = DataLoader(val_dataset,batch_size=10,shuffle=False)
# 指定设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)# 1.2 实现生成器和判别器 :因为复杂部分都放在loss值的计算方面了,所以生成器和判别器就会简单一些。
# 生成器和判别器各自有两个卷积和两个全连接层。生成器最终输出与输入图片相同维度的数据作为模拟样本。
# 判别器的输出不需要有激活函数,并且输出维度为1的数值用来表示结果。
# 在GAN模型中,因判别器的输入则是具体的样本数据,要区分每个数据的分布特征,所以判别器使用实例归一化,
class WGAN_D(nn.Module): # 定义判别器类D :有两个卷积和两个全连接层def __init__(self,inputch=1):super(WGAN_D, self).__init__()self.conv1 = nn.Sequential(nn.Conv2d(inputch,64,4,2,1), # 输出形状为[batch,64,28,28]nn.LeakyReLU(0.2,True),nn.InstanceNorm2d(64,affine=True))self.conv2 = nn.Sequential(nn.Conv2d(64,128,4,2,1),# 输出形状为[batch,64,14,14]nn.LeakyReLU(0.2,True),nn.InstanceNorm2d(128,affine=True))self.fc = nn.Sequential(nn.Linear(128*7*7,1024),nn.LeakyReLU(0.2,True))self.fc2 = nn.Sequential(nn.InstanceNorm1d(1,affine=True),nn.Flatten(),nn.Linear(1024,1))def forward(self,x,*arg): # 正向传播x = self.conv1(x)x = self.conv2(x)x = x.view(x.size(0),-1)x = self.fc(x)x = x.reshape(x.size(0),1,-1)x = self.fc2(x)return x.view(-1,1).squeeze(1)# 在GAN模型中,因生成器的初始输入是随机值,所以生成器使用批量归一化。
class WGAN_G(nn.Module): # 定义生成器类G:有两个卷积和两个全连接层def __init__(self,input_size,input_n=1):super(WGAN_G, self).__init__()self.fc1 = nn.Sequential(nn.Linear(input_size * input_n,1024),nn.ReLU(True),nn.BatchNorm1d(1024))self.fc2 = nn.Sequential(nn.Linear(1024,7*7*128),nn.ReLU(True),nn.BatchNorm1d(7*7*128))self.upsample1 = nn.Sequential(nn.ConvTranspose2d(128,64,4,2,padding=1,bias=False), # 输出形状为[batch,64,14,14]nn.ReLU(True),nn.BatchNorm2d(64))self.upsample2 = nn.Sequential(nn.ConvTranspose2d(64,1,4,2,padding=1,bias=False), # 输出形状为[batch,64,28,28]nn.Tanh())def forward(self,x,*arg): # 正向传播x = self.fc1(x)x = self.fc2(x)x = x.view(x.size(0),128,7,7)x = self.upsample1(x)img = self.upsample2(x)return img# 1.3 计算w散度:返回值充当WGAN-gp中的惩罚项,用于计算判别器的损失
def compute_w_div(real_samples,real_out,fake_samples,fake_out):# 定义参数k = 2p = 6# 计算真实空间的梯度weight = torch.full((real_samples.size(0),),1,device=device)real_grad = autograd.grad(outputs=real_out,inputs=real_samples,grad_outputs=weight,create_graph=True,retain_graph=True,only_inputs=True)[0]# L2范数real_grad_norm = real_grad.view(real_grad.size(0),-1).pow(2).sum(1)# 计算模拟空间的梯度fake_grad = autograd.grad(outputs=fake_out,inputs=fake_samples,grad_outputs=weight,create_graph=True,retain_graph=True,only_inputs=True)[0]# L2范数fake_grad_norm = fake_grad.view(fake_grad.size(0),-1).pow(2).sum(1)# 计算W散度距离div_gp = torch.mean(real_grad_norm **(p/2)+fake_grad_norm**(p/2))*k/2return div_gp## 1.4 定义模型的训练函数
# 定义函数train(),实现模型的训练过程。
# 在函数train()中,按照对抗神经网络专题(一)中的式(8-24)实现模型的损失函数。
# 判别器的loss为D(fake_samples)-D(real_samples)再加上联合分布样本的梯度惩罚项gradient_penalties,其中fake_samples为生成的模拟数据,real_Samples为真实数据,
# 生成器的loss为-D(fake_samples)。
def train(D,G,outdir,z_dimension,num_epochs=30):d_optimizer = torch.optim.Adam(D.parameters(),lr=0.001) # 定义优化器g_optimizer = torch.optim.Adam(G.parameters(),lr=0.001)os.makedirs(outdir,exist_ok=True) # 创建输出文件夹# 在函数train()中,判别器和生成器是分开训练的。让判别器学习的次数多一些,判别器每训练5次,生成器优化1次。# WGAN_gp不会因为判别器准确率太高而引起生成器梯度消失的问题,所以好的判别器会让生成器有更好的模拟效果。for epoch in range(num_epochs):for i,(img,lab) in enumerate(train_loader):num_img = img.size(0)# 训练判别器real_img = img.to(device)y_one_hot = torch.zeros(lab.shape[0],10).scatter_(1,lab.view(lab.shape[0],1),1).to(device)for ii in range(5): # 循环训练5次d_optimizer.zero_grad() # 梯度清零real_img = real_img.requires_grad_(True) # 在WGAN-gp基础上新增,将输入参数real_img设置为可导# 对real_img进行判别real_out = D(real_img, y_one_hot)# 生成随机值z = torch.randn(num_img, z_dimension).to(device)fake_img = G(z, y_one_hot)  # 生成fake_imgfake_out = D(fake_img, y_one_hot)  # 对fake_img进行判别# 计算梯度惩罚项gradient_penalty_div = compute_w_div(real_img, real_out, fake_img, fake_out) # 求梯度# 计算判别器的lossd_loss = -torch.mean(real_out) + torch.mean(fake_out) + gradient_penalty_divd_loss.backward()d_optimizer.step()# 训练生成器for ii in range(1):g_optimizer.zero_grad()  # 梯度清0z = torch.randn(num_img, z_dimension).to(device)fake_img = G(z, y_one_hot)fake_out = D(fake_img, y_one_hot)g_loss = -torch.mean(fake_out)g_loss.backward()g_optimizer.step()# 输出可视化结果fake_images = to_img(fake_img.cpu().data)real_images = to_img(real_img.cpu().data)rel = torch.cat([to_img(real_images[:10]), fake_images[:10]], axis=0)imshow(torchvision.utils.make_grid(rel, nrow=10), os.path.join(outdir, 'fake_images-{}.png'.format(epoch + 1)))# 输出训练结果print('Epoch [{}/{}], d_loss: {:.6f}, g_loss: {:.6f} ''D real: {:.6f}, D fake: {:.6f}'.format(epoch, num_epochs,d_loss.data,g_loss.data,real_out.data.mean(),fake_out.data.mean()))# 保存训练模型torch.save(G.state_dict(), os.path.join(outdir, 'div-generator.pth'))torch.save(D.state_dict(), os.path.join(outdir, 'div-discriminator.pth'))# 1.5 定义函数,实现可视化模型结果:获取一部分测试数据,显示由模型生成的模拟数据。
def displayAndTest(D,G,z_dimension):    # 可视化结果sample = iter(test_loader)images, labels = sample.next()y_one_hot = torch.zeros(labels.shape[0], 10).scatter_(1,labels.view(labels.shape[0], 1), 1).to(device)num_img = images.size(0) # 获取样本个数with torch.no_grad():z = torch.randn(num_img, z_dimension).to(device) # 生成随机数fake_img = G(z, y_one_hot)fake_images = to_img(fake_img.cpu().data) # 生成模拟样本rel = torch.cat([to_img(images[:10]), fake_images[:10]], axis=0)imshow(torchvision.utils.make_grid(rel, nrow=10))print(labels[:10])# 1.6 调用函数并训练模型:实例化判别器和生成器模型,并调用函数进行训练
if __name__ == '__main__':z_dimension = 40  # 设置输入随机数的维度D = WGAN_D().to(device)  # 实例化判别器G = WGAN_G(z_dimension).to(device)  # 实例化生成器train(D, G, './w_img', z_dimension) # 训练模型displayAndTest(D, G, z_dimension) # 输出可视化

【Pytorch神经网络实战案例】17 带W散度的WGAN-div模型生成Fashon-MNST模拟数据相关推荐

  1. 【Pytorch神经网络实战案例】13 构建变分自编码神经网络模型生成Fashon-MNST模拟数据

    1 变分自编码神经网络生成模拟数据案例说明 变分自编码里面真正的公式只有一个KL散度. 1.1 变分自编码神经网络模型介绍 主要由以下三个部分构成: 1.1.1 编码器 由两层全连接神经网络组成,第一 ...

  2. 【Pytorch神经网络实战案例】29 【代码汇总】GitSet模型进行步态与身份识别(CASIA-B数据集)

    1 GaitSet_DataLoader.py import numpy as np # 引入基础库 import os import torch.utils.data as tordata from ...

  3. 【Pytorch神经网络实战案例】09 使用卷积提取图片的轮廓信息(手动模拟Sobel算子)

    1 载入图片并显示 import matplotlib.pyplot as plt import matplotlib.image as mpimg import torch import torch ...

  4. 【Pytorch神经网络实战案例】21 基于Cora数据集实现Multi_Sample Dropout图卷积网络模型的论文分类

    Multi-sample Dropout是Dropout的一个变种方法,该方法比普通Dropout的泛化能力更好,同时又可以缩短模型的训练时间.XMuli-sampleDropout还可以降低训练集和 ...

  5. 【Pytorch神经网络实战案例】25 (带数据增强)基于迁移学习识别多种鸟类(CUB-200数据集)

    1 数据增强 在目前分类效果最好的EficientNet系列模型中,EfficientNet-B7版本的模型就是使用随机数据增强方法训练而成的. RandAugment方法也是目前主流的数据增强方法, ...

  6. 【Pytorch神经网络实战案例】24 基于迁移学习识别多种鸟类(CUB-200数据集)

    1 迁移学习 在实际开发中,常会使用迁移学习将预训练模型中的特征提取能力转移到自己的模型中. 1.1 迁移学习定义 迁移学习指将在一个任务上训练完成的模型进行简单的修改,再用另一个任务的数据继续训练, ...

  7. 【Pytorch神经网络实战案例】18 最大化深度互信信息模型DIM实现搜索最相关与最不相关的图片

    图片搜索器分为图片的特征提取和匹配两部分,其中图片的特征提取是关键.将使用一种基于无监督模型的提取特征的方法实现特征提取,即最大化深度互信息(DeepInfoMax,DIM)方法. 1 最大深度互信信 ...

  8. 【Pytorch神经网络实战案例】28 GitSet模型进行步态与身份识别(CASIA-B数据集)

    1 CASIA-B数据集 本例使用的是预处理后的CASIA-B数据集, 数据集下载网址如下. http://www.cbsr.ia.ac.cn/china/Gait%20Databases%20cH. ...

  9. 【Pytorch神经网络实战案例】15 WGAN-gp模型生成Fashon-MNST模拟数据

    1 WGAN-gp模型生成模拟数据案例说明 使用WGAN-gp模型模拟Fashion-MNIST数据的生成,会使用到WGAN-gp模型.深度卷积GAN(DeepConvolutional GAN,DC ...

最新文章

  1. Android热修复升级探索——代码修复冷启动方案
  2. PHP原生处理select结果集的函数介绍
  3. multipart/form-data ajax 提交问题(未解决)
  4. 负载均衡会话保持技术、原理、产品(以F5为例)
  5. linux内核启用64位除法,关于内核中的乘法和除法。
  6. 求方阵的鞍点(即在行最小列最大的那个点)
  7. kubernetes视频教程笔记 (32)-安全-准入控制Admission Control
  8. activity启动模式之standard
  9. Matlab-Simulink文件类型总结
  10. ansys模型导入matlab,CAD三维模型导入ANSYS的万能方法
  11. 怎么在html中把3个单元格合并成2个,Excel表格怎么将一个单元格拆分成2个?将多个单元合并成一个的方法...
  12. 举个栗子!Tableau 技巧(105):用 四象限图 对数据进行分类分析
  13. Material Design-Surface平面第二篇
  14. 什么是5G承载网?【转载自微信公众号网络技术联盟站】
  15. 复习单片机:点亮LED(内含实物图+硬件设计+软件编程+原始代码)
  16. laravel-admin Model does not exists添加模型报错
  17. R 语言中添加辅助线(ggplot2)
  18. 关于采购订单、销售订单、工作单行上的库存详细信息说明
  19. 阿里云服务器ECS怎么重装系统?
  20. Js微信公众号引JS-SDK调起微信支付

热门文章

  1. php 去掉实体,用PHP删除除5个预定义HTML实体之外的所有实体的最佳方法-用于XHTML5输出...
  2. android生命周期_Android开发 View的生命周期结合代码详解
  3. Flask 第三方组件之 login
  4. 编程随想 关系图_IT什么岗位比较好找工作?一张金字塔图就能明白
  5. matlab水力学工具箱,新浪潮水工设计软件
  6. android string数组转json_移动端开发基础【20】pages.json的配置项pages
  7. LeetCode:验证回文串【125】
  8. node进阶| 解决表单enctype=multipart/form-data 时获取不到Input值的问题
  9. java GZIP压缩和解压
  10. Hibernate写hql语句与不写hql语句的区别?