Browse Source

latest DLG-MindSpore

pull/311/head
小豆子 Gitee 3 years ago
parent
commit
82c4968f7d
No known key found for this signature in database GPG Key ID: 173E9B9CA92EEF8F
1 changed files with 312 additions and 0 deletions
  1. +312
    -0
      deep_leakage by xiaodouzi/DLG-FOR-Mindspore/DLG-1.py

+ 312
- 0
deep_leakage by xiaodouzi/DLG-FOR-Mindspore/DLG-1.py View File

@@ -0,0 +1,312 @@
import time # 时间模块, 用于记录每次迭代的时间打印到控制台
import os # 操作系统模块, 用于创建文件夹,以及路径的一些操作
import numpy as np
import matplotlib.pyplot as plt
import mindspore # 导入mindspore核心模块
import mindspore.nn as nn # nn模块是mindspore和神经网络相关的模块
import mindspore.dataset as ds # 从mindspore.dataset模块获取数据集的操作的模块
from mindspore.dataset import GeneratorDataset
from mindspore.dataset.vision import c_transforms
import mindspore.dataset.vision.c_transforms as c_vision #引入c_vision模块
import mindspore.dataset.vision.py_transforms as py_vision
from mindspore.dataset.transforms.c_transforms import Compose #引入Compose模块
from mindspore.common import set_seed
# datasets是加载数据,transforms是将数据进行处理,
# 比如将PILImage图像转换成Tensor,或者将Tensor转换成PILImage图像
import pickle # pickle模块主要是用来将对象序列化存储到硬盘
import PIL.Image as Image # 处理图像数据的模块
import argparse # 参数命令行交互模块
os.environ['KMP_DUPLICATE_LIB_OK']='True'
'''
argparse实际上就是与命令行交互的一个模块。使用命令行选择一些args执行。
如: python DLG.py --index 0 选择index为0的下标
通过命令行选择可调节参数
argparse 模块可以让人轻松编写用户友好的命令行接口。
程序定义它需要的参数,然后 argparse 将弄清如何从 sys.argv 解析出那些参数。
argparse 模块还会自动生成帮助和使用手册,并在用户给程序传入无效参数时报出错误信息。
preference: https://docs.python.org/zh-cn/3/library/argparse.html
'''
parser = argparse.ArgumentParser(description='Deep Leakage from Gradients.') # 使用 argparse 的第一步是创建一个 ArgumentParser 对象:
'''
给一个 ArgumentParser 添加程序参数信息是通过调用 add_argument() 方法完成的。
通常,这些调用指定 ArgumentParser 如何获取命令行字符串并将其转换为对象。这些信息在 parse_args() 调用时被存储和使用。
要想使用参考preference
'''
parser.add_argument('--index', type=int, default="10", # 这个参数是指明数据集下标,选择图片的下标
help='the index for leaking images on Dataset.')
parser.add_argument('--image', type=str, default="", # 这个参数是加载自己电脑的图片文件,输入在本地的图片地址即可
help='the path to customized image.')
parser.add_argument('--dataset', type=str, default="CIFAR10",
help='choose your dataset')
args = parser.parse_args()
class Dataset_from_Image(GeneratorDataset):
def __init__(self, imgs, labs, transform=None):
self.imgs = imgs # img paths
self.labs = labs # labs is ndarray
self.transform = transform
del imgs, labs
def __len__(self):
return self.labs.shape[0]
def __getitem__(self, idx):
lab = self.labs[idx]
img = Image.open(self.imgs[idx])
if img.mode != 'RGB':
img = img.convert('RGB')
img = self.transform(img)
return img, lab
def lfw_dataset(lfw_path, shape_img):
images_all = []
labels_all = []
folders = os.listdir(lfw_path)
for foldidx, fold in enumerate(folders):
files = os.listdir(os.path.join(lfw_path, fold))
for f in files:
if len(f) > 4 and f[-4:] == '.jpg':
images_all.append(os.path.join(lfw_path, fold, f))
labels_all.append(foldidx)
transform = c_transforms.Compose([c_vision.Resize(size=shape_img)])
dst = Dataset_from_Image(images_all, np.asarray(labels_all, dtype=int), transform=transform)
return dst
'''
自定义LeNet网络
'''
class LeNet(nn.Cell): # nn.Cell, 定义神经网络必须继承的模块, Mindspore框架规定的形式
def __init__(self, channel=3, hidden=768, num_classes=10): # 假设输入cifar10数据集, 默认3通道, 隐层维度为768, 分类为10
super(LeNet, self).__init__() # 继承mindspore神经网络工具箱中的模块
act = nn.Sigmoid # 激活函数为Sigmoid
# nn.Sequential: 顺序容器。 模块将按照在构造函数中传递的顺序添加到模块中。 或者,也可以传递模块的有序字典
self.body = nn.SequentialCell( # 设计神经网络结构,对于nn.nn.SequentialCell : https://mindspore.cn/docs/api/zh-CN/r1.5/api_python/nn/mindspore.nn.SequentialCell.html#mindspore.nn.SequentialCell
# 设计输入通道为channel,输出通道为12, 5x5卷积核尺寸,填充为5 // 2是整除。故填充为2, 步长为2的卷积层
nn.Conv2d(channel, 12, kernel_size=5, padding=5 // 2, stride=2),
# 经过卷积后, 使用Sigmoid激活函数激活
act(),
# 设计输入通道为12,输出通道为12, 5x5卷积核尺寸,填充为5 // 2是整除。故填充为2, 步长为2的卷积层
nn.Conv2d(12, 12, kernel_size=5, padding=5 // 2, stride=2),
# 经过卷积后, 使用Sigmoid激活函数激活
act(),
# 设计输入通道为12,输出通道为12, 5x5卷积核尺寸,填充为5 // 2是整除。故填充为2, 步长为1的卷积层
nn.Conv2d(12, 12, kernel_size=5, padding=5 // 2, stride=1),
# 经过卷积后, 使用Sigmoid激活函数激活
act()
)
# 设计一个全连接映射层, 将hidden隐藏层映射到十个分类标签
self.fc = nn.SequentialCell(
nn.Dense(hidden, num_classes)
)
print("-----猫子说一切正常-----")
# 设计前向传播算法
def forward(self, x):
out = self.body(x) # 先经过nn.SequentialCell的顺序层得到一个输出
out = out.view(out.size(0), -1) # 将输出转换对应的维度
out = self.fc(out) # 最后将输出映射到一个十分类的一个列向量
return out
print("-----猫子说一切正常-----")
'''
init weights
'''
def weights_init(m):
try:
if hasattr(m, "weight"): # hasattr:函数用于判断对象是否包含对应的属性。
m.weight.data.uniform_(-0.5, 0.5) # 对m.weight.data进行均值初始化。m.weights.data指的是网络中的卷积核的权重
except Exception:
print('warning: failed in weights_init for %s.weight' % m._get_name())
try:
if hasattr(m, "bias"): # 对偏置进行初始化
m.bias.data.uniform_(-0.5, 0.5)
except Exception:
print('warning: failed in weights_init for %s.bias' % m._get_name())
def main():
seed = 1234 # 经过专家的实验, 随机种子数为1234结果会较好
np.random.seed(seed)
dataset = args.dataset # 获得命令行输入的dataset
root_path = '.'
data_path = os.path.join(root_path, './data').replace('\\', '/') # 指定数据存放的路径地址, replace是进行转义
save_path = os.path.join(root_path, 'results/DLG_%s' % dataset).replace('\\', '/') # 图片保存的路径
lr = 1.0 # 学习率
num_dummy = 1 # 一次输入还原的图片数量
iteration = 300 # 一张图片迭代的次数
num_exp = 10 # 实验次数也就是神经网络训练的epoch
device = 'cuda' # 设置 device是cuda
tt = Compose([py_vision.ToTensor()]) # 将图像类型数据(PILImage)转换成Tensor张量
tp = Compose([py_vision.ToPIL()]) # 将Tensor张量转换成图像类型数据
print('--------已成功调用---------')
'''
打印路径
'''
print(ds, 'root_path:', root_path)
print(ds, 'data_path:', data_path)
print(ds, 'save_path:', save_path)
if not os.path.exists('results'): # 判断是否存在results文件夹,没有就创建,Linux中mkdir创建文件夹
os.mkdir('results')
if not os.path.exists(save_path): # 是否存在路径, 不存在则创建保存图片的路径
os.mkdir(save_path)
print('--------已成功调用---------')
'''
加载数据
'''
if ds == 'MNIST' or ds == 'mnist': # 判断是什么数据集
image_shape = (28, 28) # mnist数据集图片尺寸是28x28
num_classes = 10 # mnist数据分类为十分类: 0 ~ 9
channel = 1 # mnist数据集是灰度图像所以是单通道
hidden = 588 # hidden是神经网络最后一层全连接层的维度
dst = ds.MnistDataset(data_path, download=True)
print('--------已成功调用11111---------')
elif ds == 'cifar10' or ds == 'CIFAR10':
image_shape = (32, 32) # cifar10数据集图片尺寸是32x32
num_classes = 10 # cifar10数据分类为十分类:卡车、 飞机等
channel = 3 # cifar10数据集是RGB图像所以是三通道
hidden = 768 # hidden是神经网络最后一层全连接层的维度
dst = ds.Cifar10Dataset(data_path, download=True)
print('--------猫子已成功调用---------')
elif ds == 'cifar100' or ds == 'CIFAR100':
image_shape = (32, 32) # cifar100数据集图片尺寸是32x32
num_classes = 100 # cifar100数据分类为一百个分类
channel = 3 # cifar100数据集是灰度图像所以是单通道
hidden = 768 # hidden是神经网络最后一层全连接层的维度
dst = ds.Cifar100Dataset(data_path, download=True)
elif ds == 'lfw':
shape_img = (32, 32)
num_classes = 5749
channel = 3
hidden = 768
lfw_path = os.path.join(root_path, './data/lfw')
dst = ds.lfw_dataset(lfw_path, shape_img)
else:
exit('unkown dataset') # 未定义的数据集
for idx_net in range(num_exp):
net = LeNet(channel=channel, hidden=hidden, num_classes=num_classes) # 初始化LeNet模型
net.apply(weights_init) # 初始化模型中的卷积核的权重
print('running %d|%d experiment' % (idx_net, num_exp))
net = net.to(device)
print('%s, Try to generate %d images' % ('DLG', num_dummy))
criterion = nn.CrossEntropyLoss().to(device) # 设置损失函数为交叉熵函数
imidx_list = [] # 用于记录当前还原图片的下标
for imidx in range(num_dummy):
idx = args.index # 从命令行获取还原图片的index
imidx_list.append(idx) # 将index加入到列表中
tmp_datum = tt(dst[idx][0]).float().to(device) # 将数据集中index对应的图片数据拿出来转换成Tensor张量
tmp_datum = tmp_datum.view(1, *tmp_datum.size()) # 将tmp_datum数据重构形状, 可以用shape打印出来看看
tmp_label = mindspore.Tensor([dst[idx][1]]).long().to(device) # 将数据集中index对应的图片的标签拿出来转换成Tensor张量
tmp_label = tmp_label.view(1, ) # 将标签重塑为列向量形式
if imidx == 0: # 如果imidx为0, 代表只处理一张图片
gt_data = tmp_datum # gt_data表示真实图片数据
gt_label = tmp_label # gt_label 表示真实图片的标签
else:
gt_data = mindspore.ops.Concat((gt_data, tmp_datum), dim=0) # 如果是多张图片就要将数据cat拼接起来
gt_label = mindspore.ops.Concat((gt_label, tmp_label), dim=0)
# compute original gradient
out = net(gt_data) # 将真实图片数据丢入到net网络中获得一个预测的输出
y = criterion(out, gt_label) # 使用交叉熵误差函数计算真实数据的预测输出和真实标签的误差
dy_dx = mindspore.ops.GradOperation(y, net.parameters()) # 通过自动求微分得到真实梯度
# 这一步是一个列表推导式,先从dy_dx这个Tensor中一步一步取元素出来,对原有的tensor进行克隆, 放在一个list中
# https://blog.csdn.net/Answer3664/article/details/104417013
original_dy_dx = list((_.detach().clone() for _ in dy_dx))
# generate dummy data and label。 生成假的数据和标签
dummy_data = mindspore.ops.StandardNormal(gt_data.size()).to(device).requires_grad_(True)
dummy_label = mindspore.ops.StandardNormal((gt_data.shape[0], num_classes)).to(device).requires_grad_(True)
optimizer = mindspore.nn.Adam([dummy_data, dummy_label], lr=lr) #设置优化器为Adam法
history = [] # 记录全部的假的数据(这里假的数据指的是随机产生的假图像)
history_iters = [] # 记录画图使用的迭代次数
grad_difference = [] # 记录真实梯度和虚假梯度的差
data_difference = [] # 记录真实图片和虚假图片的差
train_iters = [] #
print('lr =', lr)
for iters in range(iteration): # 开始训练迭代
def closure(): # 闭包函数
optimizer.zero_grad() # 每次都将梯度清零
pred = net(dummy_data) # 将假的图片数据丢给神经网络求出预测的标签
# 将假的预测进行softmax归一化,转换为概率
dummy_loss = -mindspore.ops.ReduceMean(
mindspore.ops.ReduceSum(mindspore.nn.softmax(dummy_label, -1) * mindspore.ops.Log(mindspore.nn.softmax(pred, -1)), dim=-1))
# dummy_loss = criterion(pred, gt_label)
# 对假的数据进行自动微分, 求出假的梯度
dummy_dy_dx = mindspore.ops.GradOperation(dummy_loss, net.parameters(), create_graph=True)
grad_diff = 0 # 定义真实梯度和假梯度的差值
for gx, gy in zip(dummy_dy_dx, original_dy_dx): # 对应论文中的假的梯度减掉真的梯度平方的式子
grad_diff += ((gx - gy) ** 2).sum()
grad_diff.backward() # 对||dw假 - dw真|| 进行反向传播进行更新
return grad_diff
optimizer.step(closure) # 优化器更新梯度
current_loss = closure().item() # .item()方法是将Tensor中的元素转为值。item是得到一个元素张量里面的元素值
train_iters.append(iters) # 将每次迭代次数append到列表中
grad_difference.append(current_loss) # 将梯度差记录到losses列表中
data_difference.append(mindspore.ops.ReduceMean((dummy_data - gt_data) ** 2).item()) # 记录数据差
if iters % int(iteration / 30) == 0: # 这一行是代表多少个iters画一张图
current_time = str(time.strftime("[%Y-%m-%d %H:%M:%S]", time.localtime())) # 每次迭代打印出来时间
print(current_time, iters, '梯度差 = %.8f, 数据差 = %.8f' % (current_loss, data_difference[-1])) # 打印出梯度差和数据差
history.append([tp(dummy_data[imidx].cpu()) for imidx in range(num_dummy)]) # history 记录的是假的图片数据
history_iters.append(iters) # 记录迭代次数用于画图使用
for imidx in range(num_dummy): # 这个循环是迭代有多少张图片输入
plt.figure(figsize=(12, 8)) # plt.figure(figsize=())让图画在画布上, 并且使用figsize指定画布的大小(传入参数为元组)
plt.subplot(3, 10, 1) # 在figure画布上画子图的意思
# plt.imshow(tp(gt_data[imidx].cpu())) # 这一行是显示真实图片的意思, 如果是mnist数据集,将这一行改为如下
plt.imshow(tp(gt_data[imidx].cpu()), cmap='gray') # 灰度图像
for i in range(min(len(history), 29)): # 这一行是迭代画出子图的意思
plt.subplot(3, 10, i + 2)
# plt.imshow(history[i][imidx]) # 在figure显示history存储假的图片数据
plt.imshow(history[i][imidx], cmap='gray') # 显示灰度图像
plt.title('iter=%d' % (history_iters[i])) # 第几次迭代
plt.axis('off')
plt.savefig('%s/DLG_on_%s_%05d.png' % (save_path, imidx_list, imidx_list[imidx])) # 保存图片地址
plt.close()
if current_loss < 0.000001: # converge
break
loss_DLG = grad_difference # 梯度差
label_DLG = mindspore.ops.Argmax(dummy_label, dim=-1).detach().item() # 求虚假数据产生的标签, 方便和真实图片产生的标签进行比较
mse_DLG = data_difference # 数据差
print('imidx_list 图片的index :', imidx_list)
print('梯度差 :', loss_DLG[-1])
print('数据差 :', mse_DLG[-1])
print('gt_label 真实标签 :', gt_label.detach().cpu().data.numpy(), '虚假的还原数据标签: ', label_DLG)
print('----------------------\n\n')
if __name__ == '__main__':
main()

Loading…
Cancel
Save