COMP7250的编程赋值欢迎参加COMP7250的课业!本课业由两部分组成:
第1部分:用神经网络进行简单分类
第2部分:神经网络的对抗性例子。通过尝试此任务,您将对以下内容有一个简要的了解:如何处理数据;如何建立一个简单的网络;向前和向后传播的机制;如何使用FGSM和PGD方法生成对抗性示例。注意:尽管今天我们有强大的PyTorch来构建深度学习管道,但在这个任务中,您需要使用python和一些基本包,如numpy来实现以下功能并了解其工作原理。任务1。请实现一个util函数:one_hot_labels。在实现交叉熵损失时,如果数字标签是转化为一个热门标签。例如,数字标签5->一个热标签[0,0,0,0,0,1,0,0,0]。首先,MNIST数据集是从以下文件加载的:请注意,原始训练图像和标签被分离为一组50k数据用于训练以及用于验证的10k数据。导入随机
将numpy导入为np
将matplotlib.pyplot导入为plt
从tqdm导入tqdm
定义one_hot_labels(标签):
one_hot_labels=np.零((labels.size,10))###您的代码在这里
###结束代码
返回one_hot_labels
images_train.npy:具有归一化特征的60k个图像,图像的维度特征是784。
images_test.npy
labels_train.npy:对应的数字标签(0-9)。labels_test.npy
任务2。无序播放数据
众所周知,为了避免一些潜在因素影响的学习过程
模型、混洗数据是优选的。因此,请通过添加混洗操作来完成数据。任务3。请实现softmax函数以及sigmoid函数:softmax(x)和乙状结肠(x)。
softmax的第-个元素通过以下方式计算:
最后一个等式成立,因为添加常数不会改变softmax结果。请注意,您可以softmax计算指数时遇到溢出,因此请使用“max”技巧以避免这个问题。
乙状结肠的计算公式为:
对于数值稳定性,请使用第一个方程作为正输入,使用第二个方程作为负输入。
#从文件中读取数据和标签的功能
def readData(images_file,label_file):x=np.load(images_file)
y=np.load(label_file)
返回x,y
def prepare_data():
trainData,trainLabels=readData('images_train.npy','labels_train.npy')trainLabels=one_hot_labels(trainLabels)###您的代码在这里
###结束代码
valData=trainData[0:10000,:]valLabels=trainLabels[0:10000,:]trainData=trainData[10000:,:]trainLabels=trainLabels[10000:,:]testData,testLabels=readData('images_test.npy','labels_test.npy')testLabels=one_hot_labels(testLabels)return trainData,trainLabels,valData,valLabels,testData,testLabels#用于训练、验证和测试的负载数据。
trainData,trainLabels,devData,devLabels,testData,testLabels=prepare_data()def softmax(x):
"""
x的形状为:batch_size*#class
"""
任务4。请尝试根据以下说明建立一个学习模型。
1.完成正向传播功能。现在是时候实现两层神经网络的前向传播了并且softmax被用作链接功能。正式地
隐藏层:
输出层:丧失其中,是来自隐藏层和输出层的输出(在激活函数之前);和是可学习的参数。具体来说,和表示重量矩阵和网络隐藏层的偏置向量。类似地,和是输出层的权重和偏差。请注意,在计算损失值时,我们应该使用np.log(y+1e-16)而不是np.log(y),以避免案例log(0)中的NaN(非数字)错误。2.完成坡度的计算。基于forward_pass函数,您可以为backward实现相应的函数传播为了帮助您更好地了解反向传播过程,请首先手工计算梯度并完成代码。(梯度包括梯度属于在下面的单元格中显示计算过程降价。梯度计算单元:请在此单元格中显示计算过程。
###您的代码在这里
###结束代码
返回s
def sigmoid(x):
"""
x的形状为:batch_size*dim_hidden"""
###您的代码在这里
###结束代码
返回s
类模型(对象):
def __init__(self,input_dim,num_hidden,output_dim,batchsize,learning_rate,reg_coef):
'''
Args:
input_dim:int。输入特征的维度;
num_hidden:int。隐藏单位的维度;
output_dim:int。输出特征的维度;
任务5。请完成培训程序:nn_train(trainData,trainLabels,devData,devLabels,**argv)。
batchsize:int。数据的batchsize;learning_rate:浮动。模型的学习率;
reg_coef:浮点。正则化的系数。
'''
自己W1=np随机标准_正常((input_dim,num_hidden))self-b1=np.零((1,num_hidden),dtype=浮点)自己W2=np随机标准_正常((num_hidden,output_dim))self-b2=np.零((1,output_dim))self.reg_coeffect=reg_coefself.bsz=批量
self.lr=学习率
def forward_pass(自身、数据、标签):"""
返回隐藏层、输出(softmax)层和损失
数据的形状为:batch_size*dim_x
标签的形状为:batch_size*#class
"""
#您的代码在这里
#结束代码
返回h,y_hat,损失
def optimize_params(self、h、y_hat、data、labels):#您的代码在这里
#结束代码
如果self.reg_coeffect>0:
grad_W2+=self.reg_coef*self。W2grad_W1+=self.reg_coef*self。W1grad_W2/=self.bsz
grad_W1/=self.bsz
自己W1-=自.lr*grad_W1
self-b1-=self-lr*grad_b1
自己W2-=自.lr*坡度_W2
self-b2-=self-lr*grad_b2
def calc_accurcy(y,标签):
"""
返回y给定(真)标签的准确性。
"""
pred=np.zeros-like(y)
pred[np.arange(y.shape[0]),np.argmax(y,轴=1)]=1res=np.abs((pred-labels)).sum(轴=1)acc=res[res=].shape[0]/res.shape[0]返回帐户
按照惯例,参数应该用标准高斯随机初始化
变量和参数应该由0初始化。可以使用不同的值运行nn_train的超参数reg_strength来验证正则化对网络的影响性能(例如reg_strength=0.5)。
训练后,我们可以绘制训练和验证损失/准确性曲线,以评估模型和学习过程。
def nn_train(trainData,trainLabels,devData,devLabels,num_hidden=300,learning_rate=5,batch_size=1000,num_epochs=30,reg_strength=0):
(m,n)=trainData.shape
params={}
N=m
D=n
K=列车标签.形状[1]
H=数字_隐藏
B=批次_大小
model=模型(input_dim=n,num_hidden=H,output_dim=K,batchsize=B、learning_rate=learning_rate,reg_coef=reg_strength)
num_iter=int(N/B)
tr_less,tr_metric,dev_loss,dev_metric=[],[],[]对于tqdm中的i(范围(num_epochs)):对于范围(num_iter)中的j:
batch_data=列车数据[j*B:(j+1)*B]batch_labels=列车标签[j*B:(j+1)*B]###您的代码在这里
###结束代码
_,_y,_cost=model.forward_pass(trainData,trainLabels)tr_loss.append(_cost)
tr_metric.append(calc_accuracy(_y,trainLabels))_,_y,_cost=model.forward_pass(devData,devLabels)dev_loss.append(_cost)
dev_metric.append(calc_accuracy(_y,devLabels))返回模型、tr_loss、tr_metric、dev_loss和dev_metricnum_epochs=30
模型,tr_loss,tr_metric,dev_loss、dev_metric=nn_train(trainData,trainLabels,devData,devLabels,num_epochs=num_epochs)xs=np.arange(num_epochs)
图,轴=plt.subplots(1,2,sharex=True,sharey=False,figsize=(12,4))ax0,ax1=轴.行程()
ax0.plot(xs,tr_loss,label='降雨损失')ax0.plot(xs,dev_loss,label='dev-loss')最后,我们应该根据测试数据评估模型的性能。
第2部分:神经网络的对抗性示例
已经发现,包括神经网络在内的许多分类器都非常容易受到被称为对抗性例子——输入的小扰动导致分类器
分类错误,但人类无法察觉。例如,对的图像进行小的更改停车标志可能导致自动驾驶车辆中的物体检测器将其分类为让行标志,这可能导致事故。
在这一部分中,我们将看到如何为神经网络构建对抗性示例为此,给出了在MNIST数据集上训练的3隐藏层感知器。由于我们感兴趣的是构造反样本而不是原始分类
任务,我们不需要太担心神经网络和
数据的处理(已经给出)。可以加载感知器的参数
从fc*.weight、npy和fc*.bias.npy。测试数据集可以从X_test.npy和Y_test.npy。MNIST的每个图像大小为28×28像素,通常表示为平面784个数字的矢量。它还包括每个示例的标签,一个数字表示实际该图像中手写的数字(0-9)享受练习生成对抗性示例的乐趣!首先,我们需要为以后的计算定义一些函数。
任务1。请实现以下功能:
relu:relu函数计算如下:
ax0.legend()
ax0.set_xlabel('#epoch')
ax0.set_ylabel('CE损失')
ax1.plot(xs,tr_metric,label='train-acc')ax1.plot(xs,dev_metric,label='dev-acc')ax1.legend()
ax1.set_xlabel('#epoch')
ax1.set_ylabel(“准确性”)
plt.show()
def nn_test(数据、标签):
h、 output,cost=model.forward_pass(数据,标签)精度=精度=(np.argmax(输出,轴=1)==np.argmax(标签,轴=1)).sum()*1./标签.形状[0]返回精度
准确性=nn_test(testData,testLabels)打印(“测试精度:{0}”。格式(精度))
从__future_import print_function将numpy导入为np
将matplotlib.pyplot导入为plt
relu_grad:relu_grad用于计算relu函数的梯度,如下所示:接下来,我们定义了多层感知器的结构和一些实用函数。神经网络是一个具有三个隐藏层的完全连接的多层感知器。隐藏的层分别包含2048、512和512个隐藏节点。我们使用ReLU作为激活函数。最后一个中间层的输出通过softmax
函数,并且损失被测量为结果概率向量之间的交叉熵
以及真正的标签。
def-relu(x):
'''
输入
x: ndarray格式的矢量
输出
relux:ndarray格式的矢量,
表示x的ReLu激活。
'''
###您的代码在这里
###结束代码
返回relux
def-relu_grad(x):
'''
输入
x: ndarray格式的矢量
输出
relugrad_x:ndarray格式的矢量,
代表ReLu活化的梯度。
'''
###您的代码在这里
###结束代码
返回relugrad_x
def cross_entropy(y,y_hat):'''
输入
y: 表示类标签的int
y_hat:ndarray格式的矢量,显示预测的
每个类别的概率。
输出
交叉熵损失。
'''
etp=one_hot_labels(y)*np.log(y_hat+1e-16)损失=-np.sum(etp)
回波损耗
任务2。请实现正向传播和梯度计算:
正向传播规则如下。
坡度计算规则如下。
设L表示图像标签对(x,y)的交叉熵损失。我们对坡度感兴趣L w.r.t.x,并在梯度的(符号)方向上移动x以增加L。如果L变为很大程度上,新图像x_adv将可能被错误分类。
我们使用链式规则进行梯度计算。再次,设h0为x的别名。我们有:中间项可以计算如下。
任务3。请根据梯度生成对抗性示例(&D)。
我们从推导一种简单的方法开始,围绕输入(x,y)构建对抗性示例。假设我们用函数f:X{0,…,9}来表示我们的神经网络。假设我们想找到x的一个小扰动,使得神经网络f分配一个标签不同于y到x+。为了找到这样一个,我们想增加的交叉熵损失网络f在(x,y);换句话说,我们想迈出一小步,沿着这一小步,交叉熵损失增加,从而导致错误分类。我们可以将其写成梯度上升更新,并且为了确保我们只迈出一小步,我们可以使用的每个坐标的符号坡度最后的算法是这样的:
x: 尺寸为1x784的输入图像矢量。
y: x的真类标签。
zi:激活前第i个中间层的值,带尺寸
1x2048、1x512、1x512和1x10,对于i=1、2、3、4。hi:激活后第i个中间层的值,带尺寸
1x2048、1x512和1x512用于i=1、2、3。p: softmax函数之后的预测类概率向量,其中尺寸1x10。
Wi:第(i-1)个和第i个中间层之间的权重。对于简单地说,我们使用h0作为x的别名。每个Wi都有维度li_1 x li,其中li是第i层中的节点数。例如,W1的尺寸为784x2048。bi:第(i-1)个和第i个中间层之间的偏置。这个尺寸为1 x li。
其中L是交叉熵损失,它被称为快速梯度符号法(FGSM)。从图像处理的角度来看,数字图像通常每像素只使用8位,因此它们会丢弃动态范围以下的所有信息。由于曲率的精度是有限的,
如果
扰动的每个元素都小于特征的精度。因此,这是意料之中的事如果扰动
饱腹感。在实际中,应用约束
像
请对算法应用此约束。
如上所示,FGSM是一个单步方案。接下来,我们将介绍一个多步骤方案,指定梯度下降(PGD)。
与FGSM不同,PGD采取了几个步骤来获得更强大的对抗性示例:具体来说,首先用x初始化。
然后,对于每个步骤,执行以下操作:
计算损失;
计算输入的梯度:;
使用-Norm对上面的梯度进行归一化;
生成中间对抗:
使用扰动的-范数对进行归一化:
获取对抗性示例:
对对手施加约束。
类多层感知器():
'''
这个类定义了我们将要使用的多层感知器
作为攻击目标。
'''
def __init__(self):
通过
def load_params(self,params):'''
此方法加载已训练模型的权重和偏差。
'''
自己W1=参数[“fc1.weight”]
self.b1=参数[“fc1.bias”]
自己W2=参数[“fc2.weight”]
self.b2=参数[“fc2.bias”]
自己W3=参数[“fc3.weight”]
self.b3=参数[“fc3.bias”]
自己W4=参数[“fc4.weight”]
self.b4=参数[“fc4.bias”]
def set_attack_budget(self,eps):'''
此方法设置对抗性的最大L_infty范数
扰动。
'''
self.eps=eps
def转发(self,x):
'''
该方法找到输入的预测概率向量
图像x。
输入
x: ndarray格式的单个图像矢量
Ouput
self.p:ndarray格式的矢量,表示预测类x的概率。
中间结果存储为类属性。
您可能需要它们来进行梯度计算。
'''
W1,W2,W3,W4=自身。W1,自我。W2,自我。W3,自我。W4b1,b2,b3,b4=self-b1,self-b2,self-b3,self-b4self.z1=np.matmul(x,W1)+b1##########################################您的代码在这里
###结束代码
#######################################返回self-p
def预测(self,x):
'''
此方法获取单个图像向量x,并返回
预测了它的类标签。
'''
res=自身向前(x)
返回np.argmax(res)
def渐变(self,x,y):
'''
该方法求出了交叉熵损失的梯度
图像标签对(x,y)相对于图像x的w.r.t。
输入
x: ndarray格式的输入图像矢量
y: x的真标号
输出
grad:ndarray格式的矢量,表示
(x,y)的交叉熵损失的梯度
w.r.t.图像x。
'''
##########################################您的代码在这里
###结束代码
#######################################回归梯度
def攻击(self,x,y,attak_mode,epsilon,alpha=2./255.):'''
此方法生成的对抗性示例
图像标签对(x,y)。
输入
x: ndarray格式的图像矢量,表示
要损坏的图像。
y: 图像x的真实标签。
attak_mode:str.攻击模式的选择。模式可以是选自['no_controtion','L-inf','L2'];epsilon:浮动。用于产生扰动的超参数;
pgd_steps:int。运行pgd算法的步骤数;alpha:浮动。生成对抗性示例的超参数
PGD的每个步骤;
输出
x_adv:ndarray格式的矢量,表示
根据图像x创建的对抗性示例。
'''
#######################################如果attach_mode=='L-inf':
###您的代码在这里
###结束代码
elif attach_mode=='L2':
###您的代码在这里
###结束代码
elif attak_mode==“no_constration”:###您的代码在这里
###结束代码
其他的
raise ValueError(“无法识别的攻击模式,请从['Lf','L2']中选择。”)#######################################返回x_adv
现在,让我们加载测试数据和预训练的模型。
检查图像数据是否正确加载。让我们可视化数据集中的第一个图像。检查模型是否正确加载。测试准确率应为97.6%
任务4。请生成对抗性示例并检查图像。
生成具有(无约束)的FGSM对抗性示例。
生成一个带有(带约束)的PGD对抗性示例。
任务5。尝试对抗性攻击,并测试使用PGD对抗性示例的准确性。X_test=np.load(“./X_test.npy”)Y_test=np.load(“./Y_test.npy”)params={}
param_names=[“fc1.weight”,“fc1.bias”,“fc2.weight”,“fc2.bias”,
“fc3.weight”,“fc3.bias”,
“fc4.weight”,“fc4.bias”]
对于param_names中的名称:
params[name]=np.load(“./”+名称+'.npy')clf=多层感知器()
clf.load_params(params)
x、 y=x_test[0],y_test[0]
print(“这是数字的图像”,y)
像素=x.整形((28,28))
plt.imshow(像素,cmap=“灰色”)
nTest=1000
Y_pred=np.零(nTest)
对于tqdm中的i(范围(nTest)):
x、 y=x_test[i][np.newaxis,:],y_test[i]Y_pred[i]=clf.predict(x)
acc=np.sum(Y_pred==Y_test[:nTest])*1.0/nTest打印(“测试精度为”,acc)
###输出像素:一个FGSM对抗性示例,eps=0.1(无约束)。