如何减小神经网络的“黑匣子”属性?

embedded/2024/12/2 19:35:26/

一、什么是“黑匣子”

        在学习人工智能知识的过程中,我们经常会听到有人说,神经网络就是个“黑盒”或者“黑匣子”,这是什么意思呢?实际上,神经网络的“黑匣子”属性(Black Box Property)是指神经网络作为一个整体系统在处理输入和输出时的不透明性。简单来说,就是我们并不知道给定输入之后,神经网络的输出为什么是abc而不是cba,这与决策树之类的白盒模型形成了鲜明的对比(在白盒模型中,我们能够非常准确地解释模型每一步决策的决定因素)。换句话来说,就是神经网络有着较差的可解释性,这个属性主要体现在以下几个方面:

  • 网络复杂性神经网络,尤其是如今的大模型,包含大量的层和参数(上亿量级的参数已经是家常便饭了),这使得我们很难弄清楚每个神经元和层是如何贡献于最终的输出的。

  • 数据驱动神经网络的训练过程高度依赖于数据,网络的行为和输出主要由训练数据决定(给什么学什么),而不是由明确的规则或逻辑决定。

  • 模型内部的不可见性: 在神经网络中,输入数据如何通过网络的层被转换为输出的过程是不可见的,我们无法直接观察到网络内部的状态。即便我们能够打印出每一层的参数、数据值,我们也无法解释清楚每个数值的取值原因。

        当然,实践证明这种“黑匣子”属性也使得神经网络变得比白盒模型要更为强大。毕竟很多非线性的、隐含的数据规律,单纯通过定义规则是很难发现的,而激活函数的非线性以及反向传播机制等却使得神经网络能够自主学习数据规律,即便在面对未见过的数据时也能做出合理的预测。

二、如何提高神经网络可解释性

        这里,我们介绍近些年来有些名堂的一个概念——物理信息神经网络(Physics-Informed Neural Network,PINNPINN在与物理规律相关的深度学习建模任务中发挥了重要作用,是提高神经网络可解释性、提高模型性能的一种有效手段。

        PINN通过将物理定律(一般是偏微分方程的形式)嵌入到神经网络的损失函数中,指导神经网络的学习过程朝着与底层物理原理更一致的解决方案发展。这么做的好处是,即便训练的数据量很少,或者数据质量不高,由于有物理规律的制约,神经网络的预测结果也能够符合特定规律,从而减少了输出的随机性和不合理性,提高了整个模型结构以及输出结果的可解释程度。这里,推荐阅读《Physics-informed neural networks: A deep learning framework for solving forward and inverse problems involving nonlinear partial differential equations》这篇论文,文章非常详细地介绍了如何构建一个PINN,且提出了正问题、反问题两种策略:正问题是基于已有的物理规律来控制神经网络的训练过程;而反问题则是基于目前的数据规律推导出潜在的物理关系。此外,作者也很慷慨地给出了可复现的论文源码(Github)。

        关于PINN的论文中,物理公式的推导或许很令人头疼,至少对于上一次学物理还是在高中的笔者来说是这样的,而当我们扒开源码来看就很清晰了。例如,作者在论文中用薛定谔方程式作为其中的一个例子。

        而相应的源码长这样:

class PhysicsInformedNN:# Initialize the classdef __init__(self, x0, u0, v0, tb, X_f, layers, lb, ub):X0 = np.concatenate((x0, 0*x0), 1) # (x0, 0)X_lb = np.concatenate((0*tb + lb[0], tb), 1) # (lb[0], tb)X_ub = np.concatenate((0*tb + ub[0], tb), 1) # (ub[0], tb)self.lb = lbself.ub = ubself.x0 = X0[:,0:1]self.t0 = X0[:,1:2]self.x_lb = X_lb[:,0:1]self.t_lb = X_lb[:,1:2]self.x_ub = X_ub[:,0:1]self.t_ub = X_ub[:,1:2]self.x_f = X_f[:,0:1]self.t_f = X_f[:,1:2]self.u0 = u0self.v0 = v0# Initialize NNsself.layers = layersself.weights, self.biases = self.initialize_NN(layers)# tf Placeholders        self.x0_tf = tf.placeholder(tf.float32, shape=[None, self.x0.shape[1]])self.t0_tf = tf.placeholder(tf.float32, shape=[None, self.t0.shape[1]])self.u0_tf = tf.placeholder(tf.float32, shape=[None, self.u0.shape[1]])self.v0_tf = tf.placeholder(tf.float32, shape=[None, self.v0.shape[1]])self.x_lb_tf = tf.placeholder(tf.float32, shape=[None, self.x_lb.shape[1]])self.t_lb_tf = tf.placeholder(tf.float32, shape=[None, self.t_lb.shape[1]])self.x_ub_tf = tf.placeholder(tf.float32, shape=[None, self.x_ub.shape[1]])self.t_ub_tf = tf.placeholder(tf.float32, shape=[None, self.t_ub.shape[1]])self.x_f_tf = tf.placeholder(tf.float32, shape=[None, self.x_f.shape[1]])self.t_f_tf = tf.placeholder(tf.float32, shape=[None, self.t_f.shape[1]])# tf Graphsself.u0_pred, self.v0_pred, _ , _ = self.net_uv(self.x0_tf, self.t0_tf)self.u_lb_pred, self.v_lb_pred, self.u_x_lb_pred, self.v_x_lb_pred = self.net_uv(self.x_lb_tf, self.t_lb_tf)self.u_ub_pred, self.v_ub_pred, self.u_x_ub_pred, self.v_x_ub_pred = self.net_uv(self.x_ub_tf, self.t_ub_tf)self.f_u_pred, self.f_v_pred = self.net_f_uv(self.x_f_tf, self.t_f_tf)# Lossself.loss = tf.reduce_mean(tf.square(self.u0_tf - self.u0_pred)) + \tf.reduce_mean(tf.square(self.v0_tf - self.v0_pred)) + \tf.reduce_mean(tf.square(self.u_lb_pred - self.u_ub_pred)) + \tf.reduce_mean(tf.square(self.v_lb_pred - self.v_ub_pred)) + \tf.reduce_mean(tf.square(self.u_x_lb_pred - self.u_x_ub_pred)) + \tf.reduce_mean(tf.square(self.v_x_lb_pred - self.v_x_ub_pred)) + \tf.reduce_mean(tf.square(self.f_u_pred)) + \tf.reduce_mean(tf.square(self.f_v_pred))# Optimizersself.optimizer = tf.contrib.opt.ScipyOptimizerInterface(self.loss, method = 'L-BFGS-B', options = {'maxiter': 50000,'maxfun': 50000,'maxcor': 50,'maxls': 50,'ftol' : 1.0 * np.finfo(float).eps})self.optimizer_Adam = tf.train.AdamOptimizer()self.train_op_Adam = self.optimizer_Adam.minimize(self.loss)# tf sessionself.sess = tf.Session(config=tf.ConfigProto(allow_soft_placement=True,log_device_placement=True))init = tf.global_variables_initializer()self.sess.run(init)def initialize_NN(self, layers):        weights = []biases = []num_layers = len(layers) for l in range(0,num_layers-1):W = self.xavier_init(size=[layers[l], layers[l+1]])b = tf.Variable(tf.zeros([1,layers[l+1]], dtype=tf.float32), dtype=tf.float32)weights.append(W)biases.append(b)        return weights, biasesdef xavier_init(self, size):in_dim = size[0]out_dim = size[1]        xavier_stddev = np.sqrt(2/(in_dim + out_dim))return tf.Variable(tf.truncated_normal([in_dim, out_dim], stddev=xavier_stddev), dtype=tf.float32)def neural_net(self, X, weights, biases):num_layers = len(weights) + 1H = 2.0*(X - self.lb)/(self.ub - self.lb) - 1.0for l in range(0,num_layers-2):W = weights[l]b = biases[l]H = tf.tanh(tf.add(tf.matmul(H, W), b))W = weights[-1]b = biases[-1]Y = tf.add(tf.matmul(H, W), b)return Ydef net_uv(self, x, t):X = tf.concat([x,t],1)uv = self.neural_net(X, self.weights, self.biases)u = uv[:,0:1]v = uv[:,1:2]u_x = tf.gradients(u, x)[0]v_x = tf.gradients(v, x)[0]return u, v, u_x, v_xdef net_f_uv(self, x, t):u, v, u_x, v_x = self.net_uv(x,t)u_t = tf.gradients(u, t)[0]u_xx = tf.gradients(u_x, x)[0]v_t = tf.gradients(v, t)[0]v_xx = tf.gradients(v_x, x)[0]f_u = u_t + 0.5*v_xx + (u**2 + v**2)*vf_v = v_t - 0.5*u_xx - (u**2 + v**2)*u   return f_u, f_vdef callback(self, loss):print('Loss:', loss)def train(self, nIter):tf_dict = {self.x0_tf: self.x0, self.t0_tf: self.t0,self.u0_tf: self.u0, self.v0_tf: self.v0,self.x_lb_tf: self.x_lb, self.t_lb_tf: self.t_lb,self.x_ub_tf: self.x_ub, self.t_ub_tf: self.t_ub,self.x_f_tf: self.x_f, self.t_f_tf: self.t_f}start_time = time.time()for it in range(nIter):self.sess.run(self.train_op_Adam, tf_dict)# Printif it % 10 == 0:elapsed = time.time() - start_timeloss_value = self.sess.run(self.loss, tf_dict)print('It: %d, Loss: %.3e, Time: %.2f' % (it, loss_value, elapsed))start_time = time.time()self.optimizer.minimize(self.sess, feed_dict = tf_dict,         fetches = [self.loss], loss_callback = self.callback)        def predict(self, X_star):tf_dict = {self.x0_tf: X_star[:,0:1], self.t0_tf: X_star[:,1:2]}u_star = self.sess.run(self.u0_pred, tf_dict)  v_star = self.sess.run(self.v0_pred, tf_dict)  tf_dict = {self.x_f_tf: X_star[:,0:1], self.t_f_tf: X_star[:,1:2]}f_u_star = self.sess.run(self.f_u_pred, tf_dict)f_v_star = self.sess.run(self.f_v_pred, tf_dict)return u_star, v_star, f_u_star, f_v_star

        关键就在于如何通过一步步求偏导最终得到下面倒数第二行和第三行的结果。其中,f_u代表薛定谔方程的实部表达式,而f_v则是薛定谔方程的虚部表达式:

def net_f_uv(self, x, t):u, v, u_x, v_x = self.net_uv(x,t)u_t = tf.gradients(u, t)[0]u_xx = tf.gradients(u_x, x)[0]v_t = tf.gradients(v, t)[0]v_xx = tf.gradients(v_x, x)[0]f_u = u_t + 0.5*v_xx + (u**2 + v**2)*vf_v = v_t - 0.5*u_xx - (u**2 + v**2)*u   return f_u, f_v

三、总结

        PINN神经网络可解释性做出了重要贡献,此外,也有不少研究致力于将神经网络内部的计算过程可视化出来,例如有学者可视化了CNN每一层的特征向量结果,从而一定程度上解释了CNN在处理图像的过程中是如何提取特征的。

        然而,我们在很多时候其实并不会特别关注模型的可解释性,只要神经网络足够稳定,具备强鲁棒性,那么不管黑猫白猫,能抓到老鼠就是好猫。PINN除了能够提高可解释性,更重要的是它约束了神经网络的输出要符合物理规律,从而提高模型的鲁棒性。在与物理学强相关的领域,PINN也是个很不错的选择。


http://www.ppmy.cn/embedded/142397.html

相关文章

网络安全概论——网络加密与密钥管理

一、网络加密的方式及实现 1、常见的加密算法 常见的密钥加密算法类型大体可以分为三类:对称加密、非对称加密、单向加密。 对称加密算法采用单密钥加密,在通信过程中,数据发送方将原始数据分割成固定大小的块,经过密钥和加密算法逐个加密…

芯盾时代的身份安全产品体系

芯盾时代具备全栈零信任身份安全产品和服务能力: 芯盾时代IAM能够适配大企业用户复杂的应用访问需求,提供云端、互联网端、企业内网全场景的身份访问安全接入能力; 芯盾时代IAM能够理解大企业用户的身份差异,为内部用户、合作方和…

Docker Stack简介及使用

Docker Stack简介及使用 1.简介 Docker Compose的缺点是不能在分布式多服务器上使用,Docker swarm的缺点是不能同时编排多个服务,所以才有了Docker Stack,可以在分布式多服务器上同时编排多个服务。 2.使用实例 1.准备docker-compose文件…

scxml editor插件在vscode中无法使用的问题

I had the same issue, I’m running it on Linux and looking at the Developer Tools console it said it could not find “./editorglue” and would fail to load the extension. “EditorGlue.js” indeed exists and I presume this works in Windows as it does not ca…

️ 如何优化爬虫程序以提高抓取效率?

在开发爬虫程序时,提高抓取效率是至关重要的。以下是一些常见的性能优化策略,可以帮助你优化Java爬虫程序: 多线程与分布式抓取: 利用多线程技术可以显著提高爬虫的抓取速度。例如,在WebMagic中,可以通过配…

springboot360志愿服务管理系统--论文(论文+源码)_kaic

毕 业 设 计(论 文) 摘 要 传统办法管理信息首先需要花费的时间比较多,其次数据出错率比较高,而且对错误的数据进行更改也比较困难,最后,检索数据费事费力。因此,在计算机上安装志愿服务管理系…

LWIP和FATFS 实现 FTP 服务端

目录 一、前言 二、LWIP 和 FTP 简介 1.LWIP 2.FTP 三、实现 FTP 服务端的主要步骤 1.初始化 LWIP 2.创建 FTP 服务器任务 3.处理客户端连接 4.实现 FTP 命令处理 5.文件系统操作 6.错误处理和日志记录 四、示例代码 1.创建FTP任务 2. FTP任务代码 3.处理交互数据…

介绍SSD硬盘

SSD硬盘(固态硬盘,Solid State Drive)是一种利用闪存技术存储数据的存储设备,与传统的机械硬盘(HDD)不同,SSD没有任何活动部件,因此其性能和耐用性较为优越。以下是SSD硬盘的一些主要…