• [问题求助] 【Atlas卡产品】有提供硬件加速的硬件解码单元吗?还是用的都是神经网络的芯片去处理?
    有提供硬件加速的硬件解码单元吗?还是用的都是神经网络的芯片去处理?与英伟达的对比?
  • [其他] 图神经网络的困境,用微分几何和代数拓扑解决
        本文的作者是 Twitter 首席科学家、DeepMind 人工智能教授 Michael Bronstein。    对称,无论从广义还是狭义的角度讲,都是人类一直以来试图理解和创造秩序与美的一种观念。——Hermann WeylHermann Weyl 这种诗意的描述强调了对称性在科学中的基石作用。Felix Klein 在 1872 年的「Erlangen Programme」中用对称群表征几何。这不仅是数学上的一个突破,即统一了「几何大家庭」,还推进了现代物理理论的发展,这些理论可以完全从对称性的第一原理推导出来。几何深度学习领域也出现了类似的原则,通过群不变性和等变性能够推导出大多数流行神经网络架构的通用蓝图。图神经网络可以被认为是几何深度学习蓝图的一个特例,其构建模块是具有对称群的域(在这种情况下是具有置换群的图)、域上的信号(节点特征)和此类信号的群等变函数(消息传递)。几何深度学习蓝图可以应用于不同的领域,例如 grid、mesh 或图 (graph)。然而,前两者具有明确的连续形式的类比对象,grid 可以被认为是欧几里得空间或更一般的均匀空间(如球体)的离散化,mesh 则是二维流形的常见离散化),图却没有直接的连续形式的类比。这种不可类比有点令人不安,因此我们决定仔细研究用于图学习的连续模型。图神经网络扩散。图神经网络 (GNN) 通过在图上执行某种形式的消息传递来学习。其中,特征通过边从一个节点传递到另一个节点。这种机制与图上的扩散过程有关,可以用称为「扩散方程」的偏微分方程 (PDE) 形式表示。我们最近在一篇论文中展示了这种具有非线性可学习扩散函数的 PDE 的离散化(称为 GRAND),泛化了一大类 GNN 架构,例如图注意力网络(GAT。PDE 的思维方式提供了多种优势,例如可以利用兼具稳定性和收敛性的高效数值求解器(例如隐式、多步、自适应和 multigrid 方案)。其中一些求解器在流行的 GNN 架构中没有直接的类比,可能会促成一些新型图神经网络设计。由于我们考虑的扩散 PDE 可以看作是一些相关能量的梯度流 ,因此这种架构可能比典型架构更易于解释。同时,虽然 GRAND 模型提供连续时间来代替传统 GNN 中的层,但方程的空间部分仍然是离散的,并且依赖于输入图。重要的是,在这个扩散模型中,域(图)是固定的,其上定义的某些属性会演化。微分几何中常用的一个不同概念是几何流(geometric flow),域本身的属性不断演化。我的博士生导师 Ron Kimmel 等研究者 20 世纪 90 年代在图像处理领域就采用了这个想法。他们将图像建模为嵌入在联合位置和颜色空间中的流形,并通过 PDE 对它们进行推导演化,以最小化嵌入的谐波能量。这样的偏微分方程称为贝尔特拉米流(Beltrami flow),具有各向同性非欧几里得扩散的形式,并产生保边图像去噪。我们将这种范式应用于「Beltrami 神经扩散(BLEND)」框架中的图。图的节点现在由位置坐标和特征坐标来表征,这两个坐标都是经过演化的,并且都决定了扩散性。在这种思维模式下,图本身就变成了一个辅助角色:它可以从位置坐标生成(例如作为 k - 最近邻图)并在整个演化过程中重新连接。下图说明了这种同时演化的过程。转发自:https://www.jiqizhixin.com/articles/2022-03-27-2
  • [其他] 人工智能的数学问题:损失函数
    这里还是先简单说一下人工智能的极简的历史。 1956年,美国计算机科学家马文·明斯基,约翰·麦卡锡以及信息论的奠基者香农等人,召开了达特茅斯会议。在这次会议上,**人们创造出了人工智能这个词**。从那次会议开始,人工智能也进入了大发展时代。 后来,明斯基和麦卡锡都因为在人工智能领域的贡献获得了图灵奖。而香农已经不需要图灵奖了,他的名字被命名到通信理论方面的诺贝尔奖上,那就是香农奖。 从此之后的几十年中,由于算法和算力的限制,人工智能**几经起落**。直到1997年,IBM的人工智能程序“深蓝”战胜了雄据国际象棋霸主12年的卡斯帕罗夫,人工智能迎来了第三次大发展。 从那之后的二十多年,在人工智能算法方面,涌现出许多**灵魂人物**,例如被誉为深度学习之父的多伦多大学的计算机学家杰弗里·辛顿,他将反向传播算法(BP)引入了人工智能领域。纽约大学计算机科学家杨立昆,他最著名的工作是卷积神经网络(CNN)。他们俩连同加拿大蒙特利尔大学计算机学家约书亚•本吉奥共同获得了2018年的图灵奖。 经过众多科学家的努力,在特定的领域——例如图像识别,人工智能的识别率已经超过了人类。在语音识别,智能翻译等领域,人工智能也有长足的应用。现在我们上网,遇到看不懂的外文,只要按一下翻译就能变成汉语了。去国外旅游,也可以双方用一个人工智能软件就能交流了。 好了,下面进入重点。 ## 损失函数 计算机到底是如何做到这一点的呢?说到底,这是一个数学问题。我们首先来举一个例子:如何预测房屋的成交价格? 也许我们每个人都有一种简单判断:大城市比小城市房子值钱,市区房子比郊区房子值钱,学区房比非学区房值钱…那么,你能用数学关系把它表示出来么? 比如:在最简单的模型下,我们考虑房屋的价格与面积有关。我们有了一些房屋的面积,以及它们的成交价格,把数据(xi,yi)画在一张图上,如下: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/29/1648524068215748430.png) 从图形上看,我们发现房价和面积接近于正相关,我们希望获得一个函数关系,使它尽量准确的表示出房价y与面积x的关系,最简单的关系就是直线y预=wx+b。其中w是直线的斜率,b是直线的截距。参数w和b的值不一样,这条直线就能在平面内改变位置。 我们希望每一个数据点都能在直线上,但是实际上,这往往是做不到的,通过函数关系预测的房价yi预和实际房价yi之间总有差别。我们用 损失函数 描述这个差别:把每一个数据点真实的价格y与输出的价格y预做差,再把这些差别做平方和。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/29/1648524168325524966.png) 如果损失函数特别小,就说明我们的函数最贴近实际的数据,这就是一个好的回归分析。我们的目的就是要寻找合适的参数w和b,使得误差函数J最小。在数学上,这叫做最小二乘法,在高斯和勒让德时代人们就找到了通过方程求解参数w和b的方法。 不过,如果参数特别多,高斯的方程算法就不是那么方便了,人们提出了一种逐步趋近的方法——梯度下降算法。通过一次次的逼近,找到小的损失函数和最优的参数。 具体来讲:损失函数J其实是参数w和b的函数。我们定性的画出损失函数随着参数的变化规律,它有可能存在一个最低点,我们希望寻找这个最低点。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/29/1648524362185229695.png) 大家看:在参数取适当值、损失函数最小的时候,损失函数是不随着参数而变化的,或者说损失函数对这个参数的梯度(导数)为零;如果参数取的不当,损失函数会随着参数的变化而变化,梯度(导数)不为零。而且,梯度(导数)越大,往往表示距离损失函数最低点越远。 于是,我们首先预设一对参数wi和bi,然后使用算法进行迭代: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/29/1648524398579446524.png) 这里的η表示步长,表示一次迭代我们希望多大程度的改变参数。利用这个迭代方程,我们可以一步步的寻找最优化的参数就是一步走多远。我们一点一点沿着误差函数寻找,就能慢慢找到那个误差函数的最小值点,此时的参数w和b就是最优解——它是表示房价与面积关系的最好直线。 当然,房屋的价格并不只取决于面积。我们可能有更多的输入数据。例如每一个房子都有面积参数x1、城市参数x2、房屋年龄x3、位置参数x4,那么我们可以假设价格是这四个参数的线性组合: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/29/1648524425240654189.png) 此时,我们就有了5个参数k1…k4和b,我们要做的就是不停的通过求解梯度来调整参数,找到最合适的那一组,使得预测的结果与已知数据之间的误差函数最小。只是刚才,我们是在二维平面上寻找损失函数的最小值,这时我们是在一个五维空间中寻找损失函数的最小值。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/29/1648524439475187918.png) 输入数据、进行计算、调整参数,这个过程就称之为机器学习或者训练。假如最后找到或者逼近了最优解,训练就结束了。如果还没有找到,就需要调整参数和模型。其实,这和人类的学习过程非常相似,皮亚杰关于认知发展理论中的同化和顺应过程,就是这样的一个过程,老话说吃一堑长一智,也是这个问题。只是现在我们用数学的方法把它表现出来了。 原作者:李永乐老师
  • [行业资讯] 没有数字技术,二氧化碳减排就不会发生
    物联网 (IoT) 和 5G 等数字技术有助于减少排放,这是一个最近在商业界越来越热门的话题。例如,英国的一项案例研究表明,通过在制造、运输和农业领域采用它们,每年可减少1740万吨二氧化碳排放,从而为英国政府实现到2035年减少78%排放量、到2050年实现零排放的目标发挥关键作用。这一结论来自沃达丰和WPI Economics关于“净零连接:通过数字技术应对气候危机”的研究。这项研究选择了这三个领域,因为它们可以对英国的减排做出重大贡献。正如人们预料的那样,人口密度更高的地区,如城市,在碳排放量中所占的份额更大。然而,在农村地区,人均排放量更高。这项研究表明,如果不使用数字技术,部分二氧化碳排放量的减少是不可能实现的。例如,就制造业而言,之前成功的减排举措近年来有所放缓,但 3D 打印、智能传感器和自动化等物联网和 5G 技术有可能每年从大气中减少 270 至 330 万吨碳。在英国另一个排放量高的领域——农业,应用于监测作物和肥料、饲料和水供应的互联数字技术可以显著减少资源浪费,从而有助于减少 240至 480 万吨二氧化碳的排放。对于英国最大的排放源、自1990年以来几乎没有变化的英国地面交通来说,数字解决方案可以帮助减少交通和拥堵,从而限制不必要的燃料使用。研究表明,交通领域的减排潜力最大——每年最多可减少 930 万吨二氧化碳。制造细节从 1990 年代初到 2000 年代中期,英国制造业在脱碳方面取得了长足的进步,但近年来进展缓慢。2019年,由于英国的制造活动,约有8200万吨二氧化碳被排放到大气中,占该国总量的15%。在未来几年中,政府为实现2050年零碳排放而采取的关键行动中,技术处于中心位置。这些行动包括旨在使用低碳燃料源和部署工业数字技术的行动,其中工业数字技术可分为五大类,主要由物联网和5G推动:人工智能、机器学习和数据分析增材制造(3D 打印)机器人和自动化虚拟现实和增强现实工业物联网 (IIoT) 和连接性除了已经提到的减排之外,这些技术结合起来还可以在生产力、质量和效率方面带来重大好处。此外,埃森哲的一项分析显示,采用工业数字技术能够使制造业每年增长1.5%至3%,保守估计净增175,000个工作岗位。农业细节2019 年,农业排放了 4630 万吨二氧化碳,占英国排放总量的 9%。农业活动与其他领域不同,主要有两个原因:农业活动的主要温室气体排放不是二氧化碳,而是乙烷和一氧化二氮。农业活动导致气候变化,但它们也是解决方案的一部分——在农民管理的土地上种植树木、草和其他非农业植物,可以从大气中吸收二氧化碳。数字技术可以通过物联网传感器使农业更加智能,这些传感器有助于更好地利用自然资源、减少化肥消耗产生的排放、促进再生农业和恢复生物多样性。交通细节交通目前是英国最大的排放源,自 1990 年以来,地面交通(与航空或航运无关)产生的份额几乎没有变化,2019年二氧化碳排放量达到1.13亿吨,占英国总排放量的22%。大约78%的地面运输排放来自汽车和轻型货车。事实是,尽管该行业实现了显著的效率节约,但需求也有所增加。当今存在的物联网可以通过帮助改变驾驶员行为,同时作为联网和自动驾驶汽车以及最终电动汽车的基础,在短期内减少排放方面发挥重要作用。(编译:iothome)
  • [其他] 目标检测新框架:大幅度提升检测精度
    目标检测中,点特征使用方便,但可能缺乏精确定位的明确边界信息。 论文地址:https://arxiv.org/pdf/2007.11056.pdf源代码地址:https://github.com/Megvii-BaseDetection/BorderDet1简述密集物体检测器依赖于滑动窗口范式,可以在规则的图像网格上预测物体。同时,采用网格点上的特征图来生成边界框预测。点特征使用方便,但可能缺乏精确定位的明确边界信息。 在今天分享中,有研究者提出了一种简单高效的算子,称为 Border-Align,从边界的极值点提取“边界特征”以增强点特征。基于BorderAlign,研究者设计了一种称为BorderDet的新型检测架构,它明确利用边界信息进行更强的分类和更准确的定位。2背景Sliding Window滑窗法作为一种经典的物体检测方法,个人认为不同大小的窗口在图像上进行滑动时候,进行卷积运算后的结果与已经训练好的分类器判别存在物体的概率。选择性搜索(Selective Search)是主要运用图像分割技术来进行物体检测。通过滑窗法流程图可以很清晰理解其主要思路:首先对输入图像进行不同窗口大小的滑窗进行从左往右、从上到下的滑动。每次滑动时候对当前窗口执行分类器(分类器是事先训练好的)。如果当前窗口得到较高的分类概率,则认为检测到了物体。对每个不同窗口大小的滑窗都进行检测后,会得到不同窗口检测到的物体标记,这些窗口大小会存在重复较高的部分,最后采用非极大值抑制(Non-Maximum Suppression, NMS)的方法进行筛选。最终,经过NMS筛选后获得检测到的物体。3新框架分析滑动窗口目标检测器通常在密集的、规则的特征图网格上生成边界框预测。如上图所示,网格每个点上的特征一般用于预测目标的类别和位置。这种基于点的特征表示很难包含有效的边界特征,并且可能会限制目标检测器的定位能力。对于两阶段目标检测器,目标由从整个边界框中提取的区域特征来描述,如上图(b)所示。这种基于区域的特征表示能够为目标分类和定位提供比基于点的特征表示更丰富的特征。在上表中,研究者对边界框的特征表示进行了更深入的分析。首先,采用一个简单的密集目标检测器(FCOS)作为新框架的基线来生成粗边界框预测。Border Align受R-FCN的启发,新框架的BorderAlign以具有(4 + 1)C通道的边界敏感特征图I作为输入。特征图的4C通道对应四个边界(左、上、右、下)。border-sensitive特征图可视化边界上的橙色圆圈表示极值点。'Single Point', 'Left Border', 'Top Border', 'Right Border' 和 'Bottom Border'的特征图是边界敏感特征图的每个 C 通道的最大特征值。新方法也可以作为典型的两级检测器的更好的候选生成器。 研究者将边界对齐模块添加到RPN并将新结构表示为BorderRPN。BorderRPN的架构如上图所示。保留RPN中的回归分支来预测粗边界框位置。RPN中的第一个3 × 3卷积被替换为3 × 3空洞卷积以增加有效感受野。4实验在NVIDIA 2080Ti GPU上的测试结果在某种程度上,可证明BorderAlign确实在提取边界极限的特征,且边界极限点的特征对物体的精准定位确实有一些帮助。转载自:AI算法与图像处理公众号
  • [论文解析] MindQuantum实现变分量子奇异值分解
    论文题目:Variational Quantum Singular Value Deposition(VQSVD)项目介绍复现过程案例1:分解随机生成的8*8复数矩阵先引入需要使用的包: import os os.environ['OMP_NUM_THREADS'] = '1' ​ import mindspore as ms from mindquantum import Simulator, MQAnsatzOnlyLayer, add_prefix from mindquantum import Hamiltonian, Circuit, RY, RZ, X ​ import numpy as np from scipy.sparse import csr_matrix from scipy.linalg import norm from matplotlib import pyplot import tqdm定义常数并设置权重: n_qubits = 3  # qbits number cir_depth = 20  # circuit depth N = 2**n_qubits rank = 8  # learning rank step = 3 ITR = 200  # iterations LR = 0.02  # learning rate ​ # Set equal learning weights if step == 0:     weight = ms.Tensor(np.ones(rank)) else:     weight = ms.Tensor(np.arange(rank * step, 0, -step))随机生成一个8*8的复数矩阵M # Define random seed np.random.seed(42) ​ ​ def mat_generator():     '''    Generate a random complex matrix    '''     matrix = np.random.randint(         10, size=(N, N)) + 1j * np.random.randint(10, size=(N, N))     return matrix ​ ​ # Generate matrix M which will be decomposed M = mat_generator() ​ # m_copy is generated to error analysis m_copy = np.copy(M) ​ # Print M print('Random matrix M is: ') print(M) ​ # Get SVD results U, D, v_dagger = np.linalg.svd(M, full_matrices=True) Random matrix M is:  [[6.+1.j 3.+9.j 7.+3.j 4.+7.j 6.+6.j 9.+8.j 2.+7.j 6.+4.j]  [7.+1.j 4.+4.j 3.+7.j 7.+9.j 7.+8.j 2.+8.j 5.+0.j 4.+8.j]  [1.+6.j 7.+8.j 5.+7.j 1.+0.j 4.+7.j 0.+7.j 9.+2.j 5.+0.j]  [8.+7.j 0.+2.j 9.+2.j 2.+0.j 6.+4.j 3.+9.j 8.+6.j 2.+9.j]  [4.+8.j 2.+6.j 6.+8.j 4.+7.j 8.+1.j 6.+0.j 1.+6.j 3.+6.j]  [8.+7.j 1.+4.j 9.+2.j 8.+7.j 9.+5.j 4.+2.j 1.+0.j 3.+2.j]  [6.+4.j 7.+2.j 2.+0.j 0.+4.j 3.+9.j 1.+6.j 7.+6.j 3.+8.j]  [1.+9.j 5.+9.j 5.+2.j 9.+6.j 3.+0.j 5.+3.j 1.+3.j 9.+4.j]]定义论文中使用的ansatz类: class Ansatz:     '''    Define ansatz    ''' ​     def __init__(self, n, depth):         self.circ = Circuit()         num = 0         for _ in range(depth): ​             for i in range(n):                 self.circ += RY('theta' + str(num)).on(i)                 num += 1 ​             for i in range(n):                 self.circ += RZ('theta' + str(num)).on(i)                 num += 1 ​             for i in range(n - 1):                 self.circ += X.on(i + 1, i) ​             self.circ += X.on(0, n - 1)定义画出优化过程中的学习曲线的函数: def loss_plot(loss):     '''    Plot loss over iteration    '''     pyplot.plot(list(range(1, len(loss) + 1)), loss)     pyplot.xlabel('iteration')     pyplot.ylabel('loss')     pyplot.title('Loss Over Iteration')     pyplot.suptitle('step = ' + str(step))     pyplot.show() def quantnet(qubits_num, hams, circ_right, circ_left=None, base=None):     '''    Generate quantum net using hams, circ_right and circ_left under given base    '''     sim = Simulator('projectq', qubits_num) ​     if base is None:         pass     else:         sim.set_qs(base)     grad_ops = sim.get_expectation_with_grad(hams, circ_right, circ_left) ​     ms.context.set_context(mode=ms.context.PYNATIVE_MODE, device_target="CPU") ​     quantumnet = MQAnsatzOnlyLayer(grad_ops, 'ones') ​     return quantumnet实例化要使用到的ansatz:U_ansatz和V_ansatz # Define ansatz u_ansatz = add_prefix(Ansatz(n_qubits, cir_depth).circ, 'u') v_ansatz = add_prefix(Ansatz(n_qubits, cir_depth).circ, 'v') v_ansatzq0: ──RY(v_theta0)────RZ(v_theta3)────●─────────X────RY(v_theta6)─────RZ(v_theta9)────●─────────X────RY(v_theta12)────RZ(v_theta15)────●─────────X────RY(v_theta18)────RZ(v_theta21)────●─────────X────RY(v_theta24)────RZ(v_theta27)────●─────────X────RY(v_theta30)────RZ(v_theta33)────●─────────X────RY(v_theta36)────RZ(v_theta39)────●─────────X────RY(v_theta42)────RZ(v_theta45)────●─────────X────RY(v_theta48)────RZ(v_theta51)────●─────────X────RY(v_theta54)────RZ(v_theta57)────●─────────X────RY(v_theta60)────RZ(v_theta63)────●─────────X────RY(v_theta66)────RZ(v_theta69)────●─────────X────RY(v_theta72)────RZ(v_theta75)────●─────────X────RY(v_theta78)────RZ(v_theta81)────●─────────X────RY(v_theta84)────RZ(v_theta87)────●─────────X────RY(v_theta90)────RZ(v_theta93)────●─────────X────RY(v_theta96)────RZ(v_theta99)─────●─────────X────RY(v_theta102)────RZ(v_theta105)────●─────────X────RY(v_theta108)────RZ(v_theta111)────●─────────X────RY(v_theta114)────RZ(v_theta117)────●─────────X── │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ q1: ──RY(v_theta1)────RZ(v_theta4)────X────●────┼────RY(v_theta7)────RZ(v_theta10)────X────●────┼────RY(v_theta13)────RZ(v_theta16)────X────●────┼────RY(v_theta19)────RZ(v_theta22)────X────●────┼────RY(v_theta25)────RZ(v_theta28)────X────●────┼────RY(v_theta31)────RZ(v_theta34)────X────●────┼────RY(v_theta37)────RZ(v_theta40)────X────●────┼────RY(v_theta43)────RZ(v_theta46)────X────●────┼────RY(v_theta49)────RZ(v_theta52)────X────●────┼────RY(v_theta55)────RZ(v_theta58)────X────●────┼────RY(v_theta61)────RZ(v_theta64)────X────●────┼────RY(v_theta67)────RZ(v_theta70)────X────●────┼────RY(v_theta73)────RZ(v_theta76)────X────●────┼────RY(v_theta79)────RZ(v_theta82)────X────●────┼────RY(v_theta85)────RZ(v_theta88)────X────●────┼────RY(v_theta91)────RZ(v_theta94)────X────●────┼────RY(v_theta97)────RZ(v_theta100)────X────●────┼────RY(v_theta103)────RZ(v_theta106)────X────●────┼────RY(v_theta109)────RZ(v_theta112)────X────●────┼────RY(v_theta115)────RZ(v_theta118)────X────●────┼── │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ q2: ──RY(v_theta2)────RZ(v_theta5)─────────X────●────RY(v_theta8)────RZ(v_theta11)─────────X────●────RY(v_theta14)────RZ(v_theta17)─────────X────●────RY(v_theta20)────RZ(v_theta23)─────────X────●────RY(v_theta26)────RZ(v_theta29)─────────X────●────RY(v_theta32)────RZ(v_theta35)─────────X────●────RY(v_theta38)────RZ(v_theta41)─────────X────●────RY(v_theta44)────RZ(v_theta47)─────────X────●────RY(v_theta50)────RZ(v_theta53)─────────X────●────RY(v_theta56)────RZ(v_theta59)─────────X────●────RY(v_theta62)────RZ(v_theta65)─────────X────●────RY(v_theta68)────RZ(v_theta71)─────────X────●────RY(v_theta74)────RZ(v_theta77)─────────X────●────RY(v_theta80)────RZ(v_theta83)─────────X────●────RY(v_theta86)────RZ(v_theta89)─────────X────●────RY(v_theta92)────RZ(v_theta95)─────────X────●────RY(v_theta98)────RZ(v_theta101)─────────X────●────RY(v_theta104)────RZ(v_theta107)─────────X────●────RY(v_theta110)────RZ(v_theta113)─────────X────●────RY(v_theta116)────RZ(v_theta119)─────────X────●── # Embed M matrix into Hamiltonian ham ham = Hamiltonian(csr_matrix(M)) ​ i_matrix = np.identity(N) quantum_models = dict() quantum_models['net_0'] = quantnet(n_qubits, ham, v_ansatz, u_ansatz,                                    i_matrix[0]) for s in range(1, rank):     quantum_models["net_" + str(s)] = quantnet(n_qubits, ham, v_ansatz,                                                u_ansatz, i_matrix[s])     quantum_models["net_" + str(s)].weight = quantum_models['net_0'].weight ​ class MyNet(ms.nn.Cell):     '''    define quantum-classic net    ''' ​     def __init__(self):         super(MyNet, self).__init__() ​         self.build_block = ms.nn.CellList()         for j in range(rank):             self.build_block.append(quantum_models["net_" + str(j)]) ​     def construct(self):         x = self.build_block[0]() * weight[0]         k = 1 ​         for layer in self.build_block[1:]:             x += layer() * weight[k]             k += 1 ​         return -x实例化量子经典混合网络并调用MindSpore的API开始训练: # Define network net = MyNet() ​ # Define optimizer opt = ms.nn.Adam(net.trainable_params(), learning_rate=LR) ​ # Simple gradient descent train_net = ms.nn.TrainOneStepCell(net, opt) ​ # Start to train net loss_list = list() for itr in tqdm.tqdm(range(ITR)):     res = train_net()     loss_list.append(res.asnumpy().tolist())100%|██████████| 200/200 [00:42<00:00,  4.72it/s]读取训练出的结果,即奇异值: # Get singular value results singular_value = list() ​ for _, qnet in quantum_models.items():     singular_value.append(qnet().asnumpy()[0])观察学习曲线以判断是否收敛: # Plot loss over iteration loss_plot(loss_list)通过学习曲线我们可以发现损失函数已收敛。接下来打印出学习到的奇异值结果,并与经典比较: print('Predicted singular values from large to small:', singular_value) print("True singular values from large to small:", D) Predicted singular values from large to small: [54.83174, 19.169168, 14.88653, 11.093878, 10.533753, 7.648352, 5.5560594, -0.3320913] True singular values from large to small: [54.83484985 19.18141073 14.98866247 11.61419557 10.15927045 7.60223249  5.81040539 3.30116001]直观上我们可以看到使用量子经典混合网络学习出的奇异值与真实奇异值相差不大。为了测量误差,我们首先取出网络中的参数值并赋给U_ansatz和V_ansatz,以获得学习出的奇异矩阵: # Get parameters value value = quantum_models['net_0'].weight.asnumpy() v_value = value[:120] u_value = value[120:] ​ # Calculate U and V u_learned = u_ansatz.matrix(u_value) v_learned = v_ansatz.matrix(v_value) ​ v_dagger_learned = np.conj(v_learned.T) d_learned = np.array(singular_value)我们将学习到的奇异值分解结果组装回去,与原矩阵计算 Frobenius-norm 误差以观察效果,并与经典奇异值分解的误差作比较: err_subfull, err_local, err_svd = [], [], [] U, D, v_dagger = np.linalg.svd(M, full_matrices=True) ​ # Calculate Frobenius-norm error for t in range(rank):     lowrank_mat = np.matrix(U[:, :t]) * np.diag(D[:t]) * np.matrix(         v_dagger[:t, :])     recons_mat = np.matrix(u_learned[:, :t]) * np.diag(         d_learned[:t]) * np.matrix(v_dagger_learned[:t, :])     err_local.append(norm(lowrank_mat - recons_mat))     err_subfull.append(norm(m_copy - recons_mat))     err_svd.append(norm(m_copy - lowrank_mat)) ​ # Plot SVD error and VQSVD error fig, ax = pyplot.subplots() ax.plot(list(range(1, rank + 1)),         err_subfull,         "o-.",         label='Reconstruction via VQSVD') ax.plot(list(range(1, rank + 1)),         err_svd,         "^--",         label='Reconstruction via SVD') # ax.plot(list(range(1, rank + 1)), err_local, "*--", label='SVD V/S QSVD') pyplot.xlabel('Singular Value Used (Rank)', fontsize=14) pyplot.ylabel('Norm Distance', fontsize=14) leg = pyplot.legend(frameon=True) leg.get_frame().set_edgecolor('k')可以看到,使用量子神经网络分解出的奇异值结果误差与经典奇异值分解的误差十分接近。案例2:图像压缩我们先引入图像处理的包,并导入提前准备好的图片。 from PIL import Image ​ # Open figure MNIST_32.jpg and get matrix form img = Image.open(r'.\\figure\\MNIST_32.png') imgmat = np.array(list(img.getdata(band=0)), float) imgmat.shape = (img.size[1], img.size[0]) imgmat = np.matrix(imgmat) / 255使用经典SVD压缩图像的效果我们分别取前5、10、15个奇异值作图像压缩,观察结果。 # Get SVD results and show it U, sigma, V = np.linalg.svd(imgmat) ​ for t in range(5, 16, 3):     reconstimg = np.matrix(U[:, :t]) * np.diag(sigma[:t]) * np.matrix(V[:t, :])     pyplot.imshow(-reconstimg, cmap='gray')     title = "n = %s" % t     pyplot.title(title)     pyplot.show()量子版本的分解效果:此处我们展示取前8个奇异值作图像压缩的过程及最终结果。 # Set super parameters n_qubits = 5  # qbits number cir_depth = 40  # circuit depth N = 2**n_qubits rank = 8  # learning rank step = 2 ITR = 200  # iterations LR = 0.02  # learning rate SEED = 14  # random seed ​ # Set equal learning weights if step == 0:     weight = ms.Tensor(np.ones(rank)) else:     weight = ms.Tensor(np.arange(rank * step, 0, -step)) ​ ​ def mat_generator(image):     '''    Generate matrix by input image    '''     img_matrix = np.array(list(image.getdata(band=0)), float)     img_matrix.shape = (image.size[1], image.size[0])     img_np = np.matrix(img_matrix)     return img_np ​ ​ # Generate matrix M which will be decomposed M = mat_generator(img) ​ # Get SVD results U, D, v_dagger = np.linalg.svd(M, full_matrices=True)定义论文中使用的ansatz类: class Ansatz:     '''    Define ansatz    ''' ​     def __init__(self, n, depth):         self.circ = Circuit()         num = 0         for _ in range(depth): ​             for i in range(n):                 self.circ += RY('theta' + str(num)).on(i)                 num += 1 ​             for i in range(n - 1):                 self.circ += X.on(i + 1, i) def quantnet(qubits_num, hams, circ_right, circ_left=None, base=None):     '''    Generate quantum net using hams, circ_right and circ_left under given base    '''     sim = Simulator('projectq', qubits_num) ​     if base is None:         pass     else:         sim.set_qs(base)     grad_ops = sim.get_expectation_with_grad(hams, circ_right, circ_left) ​     ms.context.set_context(mode=ms.context.PYNATIVE_MODE, device_target="CPU") ​     quantumnet = MQAnsatzOnlyLayer(grad_ops, 'ones') ​     return quantumnet # Define ansatz u_ansatz = add_prefix(Ansatz(n_qubits, cir_depth).circ, 'u') v_ansatz = add_prefix(Ansatz(n_qubits, cir_depth).circ, 'v') # Embed M matrix into Hamiltonian ham ham = Hamiltonian(csr_matrix(M)) ​ i_matrix = np.identity(N) quantum_models = dict() quantum_models['net_0'] = quantnet(n_qubits, ham, v_ansatz, u_ansatz,                                    i_matrix[0]) for s in range(1, rank):     quantum_models["net_" + str(s)] = quantnet(n_qubits, ham, v_ansatz,                                                u_ansatz, i_matrix[s])     quantum_models["net_" + str(s)].weight = quantum_models['net_0'].weight class MyNet(ms.nn.Cell):     '''    define quantum-classic net    ''' ​     def __init__(self):         super(MyNet, self).__init__() ​         self.build_block = ms.nn.CellList()         for j in range(rank):             self.build_block.append(quantum_models["net_" + str(j)]) ​     def construct(self):         x = self.build_block[0]() * weight[0]         k = 1 ​         for layer in self.build_block[1:]:             x += layer() * weight[k]             k += 1 ​         return -x # Define network net = MyNet() ​ # Define optimizer opt = ms.nn.Adam(net.trainable_params(), learning_rate=LR) ​ # Simple gradient descent train_net = ms.nn.TrainOneStepCell(net, opt) ​ # Start to train net loss_list = list() for itr in tqdm.tqdm(range(ITR)):     res = train_net()     loss_list.append(res.asnumpy().tolist())100%|██████████| 200/200 [00:18<00:00, 10.58it/s] # Get singular value results singular_value = list() ​ for _, qnet in quantum_models.items():     singular_value.append(qnet().asnumpy()[0]) # Get parameters value value = quantum_models['net_0'].weight.asnumpy() v_value = value[:200] u_value = value[200:] ​ # Calculate U and V u_learned = u_ansatz.matrix(u_value) v_learned = v_ansatz.matrix(v_value) ​ v_dagger_learned = np.conj(v_learned.T) d_learned = np.array(singular_value)将学习到的奇异值分解结果组装回去,并使用pyplot包的imshow直观展示,以观察图像压缩的结果: # Calculate recombined matrix mat mat = np.matrix(u_learned[:, :rank]) * np.diag(d_learned[:rank]) * np.matrix(     v_dagger_learned[:rank, :]) ​ # Show recombination result reconstimg = np.abs(mat) pyplot.imshow(-reconstimg, cmap='gray')可以看到,图像压缩只取前8个奇异值作近似得到的结果仍具有较好的辨识度。项目总结本次复现我完成了量子版本的奇异值分解变分算法,成功应用算法分解随机8*8的复数矩阵、压缩矩阵分析误差并与经典奇异值分解算法结果相对比。虽然论文里理论上是使用变分量子神经网络来近似优化,但在实际代码中,原作者使用的是将量子网络转换成经典张量网络优化,又转换回了经典计算机上,并未利用到量子计算机的特性。这种方法更符合量子计算的逻辑,更能发挥出量子ansatz的优势,从最终结果来看,这种方法的误差也要优于原作者转换成张量网络求解的方法。下面两图是量子奇异值分解(VQSVD)误差与经典奇异值分解误差(SVD)对比,其中第一图是使用新算法复现出的结果,第二图是使用原算法画出的图,二者对比可以发现新算法的误差优于原算法。 下面两图是在案例二:图像压缩中运行出的结果,其中第一图是使用新算法复现出的结果,第二图是使用原算法画出的图,二者对比可以发现使用新算法压缩出的图片效果要优于原算法。 本论文展示出了变分量子线路的强大,它只需几个比特就可以完成指数级矩阵的运算,未来我们可以考虑基于变分量子线路,通过搭建量子经典混合神经网络求解量子奇异值转换的多项式参数,从而解决算法的精度问题。参考文献[1] Wang, X., Song, Z., & Wang, Y. Variational Quantum Singular Value Decomposition. Quantum, 5, 483 (2021).
  • [技术干货] 轻量模块ECANet----通道注意力超强改进
    深度卷积神经网络(CNNs)在计算机视觉领域得到了广泛的应用,在图像分类、目标检测和语义分割等领域取得了很大的进展。从开创性的AlexNet开始,为了进一步提高深度cnn的性能,不断推出新的CNN模型。近年来,将通道注意力引入卷积块引起了人们的广泛关注,在性能改进方面显示出巨大潜力。其中代表性的方法是SENet,它可以学习每个卷积块的通道注意力,对各种深度CNN架构带来明显的性能增益。SENet主要是 squeeze 和 excitation 两大操作,最近,一些研究通过捕获更复杂的通道依赖或结合额外的空间注意来改进SE块。这些方法虽然取得了较高的精度,但往往带来较高的模型复杂度和较大的计算负担。与前面提到的以更高的模型复杂度为代价来获得更好性能的方法不同,本文转而关注一个问题:能否以更有效的方式学习有效的通道注意力? 为了回答这个问题,我们首先回顾一下SENet中的通道注意模块。具体来说,在给定输入特征的情况下,SE块首先对每个通道单独使用全局平均池化,然后使用两个具有非线性的完全连接(FC)层,然后使用一个Sigmoid函数来生成通道权值。两个FC层的设计是为了捕捉非线性的跨通道交互,其中包括降维来控制模型的复杂性。虽然该策略在后续的通道注意模块中得到了广泛的应用,但作者的实验研究表明,降维对通道注意预测带来了副作用,捕获所有通道之间的依赖是低效的,也是不必要的。 因此,本文提出了一种针对深度CNN的高效通道注意(ECA)模块,该模块避免了降维,有效捕获了跨通道交互的信息。如下图2所示: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/25/1648186327393509305.png) 论文链接:https://arxiv.org/abs/1910.03151 代码地址:https://github.com/BangguWu/ECANet 论文翻译:https://wanghao.blog.csdn.net/article/details/113073026 ECANet主要对SENet模块进行了一些改进,提出了一种不降维的局部跨信道交互策略(ECA模块)和自适应选择一维卷积核大小的方法,从而实现了性能上的提优。 在不降低维数的通道级全局平均池化之后,ECA通过考虑每个通道及其k个邻居来捕获局部跨通道交互信息。实践证明,该方法保证了模型效率和计算效果。需要注意的是,ECA可以通过大小为k的快速1D卷积来有效实现,其中卷积核大小为k代表了局部跨信道交互的覆盖率,即,该通道附近有多少邻居参与了这个信道的注意力预测,为了避免通过交叉验证对k进行手动调优,本文提出了一种方法来自适应地确定k,其中交互的覆盖率(即卷积核大小 k)与通道维数成正比。 ECANet的实现: class eca_layer(nn.Module): """Constructs a ECA module. Args: channel: Number of channels of the input feature map k_size: Adaptive selection of kernel size """ def __init__(self, channel, k_size=3): super(eca_layer, self).__init__() self.avg_pool = nn.AdaptiveAvgPool2d(1) self.conv = nn.Conv1d(1, 1, kernel_size=k_size, padding=(k_size - 1) // 2, bias=False) self.sigmoid = nn.Sigmoid() def forward(self, x): # x: input features with shape [b, c, h, w] b, c, h, w = x.size() # feature descriptor on the global spatial information y = self.avg_pool(x) # Two different branches of ECA module y = self.conv(y.squeeze(-1).transpose(-1, -2)).transpose(-1, -2).unsqueeze(-1) # Multi-scale information fusion y = self.sigmoid(y) return x * y.expand_as(x) ECANet在模型中的调用 channelNum=64 class CRBlock(nn.Module): def __init__(self): super(CRBlock, self).__init__() self.convban = nn.Sequential(OrderedDict([ ("conv3x3_bn", ConvBN(channelNum, channelNum, 3)), ])) self.path1 = Encoder_conv(channelNum, 2) self.path2 = nn.Sequential(OrderedDict([ ('conv1x5', ConvBN(channelNum, channelNum, [1, 3])), ('conv5x1', ConvBN(channelNum, channelNum, 3)), ('ac', ACBlock(channelNum, channelNum, kernel_size=3)), ('eca', eca_layer(channelNum, 3)), # ('ac', ACBlock(channelNum, channelNum, kernel_size=3)), ])) self.path2 = nn.Sequential(OrderedDict([ ('conv1x5', ConvBN(channelNum, channelNum, [1, 5])), ('conv5x1', ConvBN(channelNum, channelNum, [5, 1])), ("conv9x1_bn", ConvBN(channelNum, channelNum, 1)), ('eca', eca_layer(channelNum, 3)), ])) self.encoder_conv = Encoder_conv(channelNum * 4) self.encoder_conv1 = ConvBN(channelNum * 4, channelNum, 1) self.identity = nn.Identity() self.relu = Mish() self.ca1 = eca_layer(channelNum * 4, 3) # self.ca2 = eca_layer(channelNum*4, 1) def forward(self, x): identity = self.identity(x) x = self.convban(x) out1 = self.path1(x) out2 = self.path2(x) out3 = self.path2(x) out = torch.cat((out1, out2, out3, x), dim=1) out = self.relu(out) out = self.encoder_conv(out) out = self.ca1(out) out = self.encoder_conv1(out) out = self.relu(out + identity) return out
  • [干货汇总] 全卷积网络(FCN)实战:使用FCN实现语义分割
    本文分享自华为云社区《[全卷积网络(FCN)实战:使用FCN实现语义分割](https://bbs.huaweicloud.com/blogs/337081?utm_source=csdn&utm_medium=bbs-ex&utm_campaign=other&utm_content=content)》,作者: AI浩。 FCN对图像进行像素级的分类,从而解决了语义级别的图像分割(semantic segmentation)问题。与经典的CNN在卷积层之后使用全连接层得到固定长度的特征向量进行分类(全联接层+softmax输出)不同,FCN可以接受任意尺寸的输入图像,采用反卷积层对最后一个卷积层的feature map进行上采样, 使它恢复到输入图像相同的尺寸,从而可以对每个像素都产生了一个预测, 同时保留了原始输入图像中的空间信息, 最后在上采样的特征图上进行逐像素分类。 下图是语义分割所采用的全卷积网络(FCN)的结构示意图: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/25/1648176266080602657.png) # 传统的基于CNN的分割方法缺点? 传统的基于CNN的分割方法:为了对一个像素分类,使用该像素周围的一个图像块作为CNN的输入,用于训练与预测,这种方法主要有几个缺点: 1)存储开销大,例如,对每个像素使用15 * 15的图像块,然后不断滑动窗口,将图像块输入到CNN中进行类别判断,因此,需要的存储空间随滑动窗口的次数和大小急剧上升; 2)效率低下,相邻像素块基本上是重复的,针对每个像素块逐个计算卷积,这种计算有很大程度上的重复; 3)像素块的大小限制了感受区域的大小,通常像素块的大小比整幅图像的大小小很多,只能提取一些局部特征,从而导致分类性能受到限制。 而全卷积网络(FCN)则是从抽象的特征中恢复出每个像素所属的类别。即从图像级别的分类进一步延伸到像素级别的分类。 # FCN改变了什么? ​ 对于一般的分类CNN网络,如VGG和Resnet,都会在网络的最后加入一些全连接层,经过softmax后就可以获得类别概率信息。但是这个概率信息是1维的,即只能标识整个图片的类别,不能标识每个像素点的类别,所以这种全连接方法不适用于图像分割。 ​ 而FCN提出可以把后面几个全连接都换成卷积,这样就可以获得一张2维的feature map,后接softmax层获得每个像素点的分类信息,从而解决了分割问题,如图4。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/25/1648176304994189028.png) # FCN缺点 (1)得到的结果还是不够精细。进行8倍上采样虽然比32倍的效果好了很多,但是上采样的结果还是比较模糊和平滑,对图像中的细节不敏感。 (2)对各个像素进行分类,没有充分考虑像素与像素之间的关系。忽略了在通常的基于像素分类的分割方法中使用的空间规整(spatial regularization)步骤,缺乏空间一致性。 # 数据集 本例的数据集采用PASCAL VOC 2012 数据集,它有二十个类别: **Person:**person Animal: bird, cat, cow, dog, horse, sheep **Vehicle:**aeroplane, bicycle, boat, bus, car, motorbike, train Indoor: bottle, chair, dining table, potted plant, sofa, tv/monitor ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/25/1648176348604357113.png) 下载地址:The PASCAL Visual Object Classes Challenge 2012 (VOC2012) (ox.ac.uk)。 数据集的结构: VOCdevkit └── VOC2012 ├── Annotations 所有的图像标注信息(XML文件) ├── ImageSets │ ├── Action 人的行为动作图像信息 │ ├── Layout 人的各个部位图像信息 │ │ │ ├── Main 目标检测分类图像信息 │ │ ├── train.txt 训练集(5717) │ │ ├── val.txt 验证集(5823) │ │ └── trainval.txt 训练集+验证集(11540) │ │ │ └── Segmentation 目标分割图像信息 │ ├── train.txt 训练集(1464) │ ├── val.txt 验证集(1449) │ └── trainval.txt 训练集+验证集(2913) │ ├── JPEGImages 所有图像文件 ├── SegmentationClass 语义分割png图(基于类别) └── SegmentationObject 实例分割png图(基于目标) 数据集包含物体检测和语义分割,我们只需要语义分割的数据集,所以可以考虑把多余的图片删除,删除的思路: 1、获取所有图片的name。 2、获取所有语义分割mask的name。 3、求二者的差集,然后将差集的name删除。 代码如下: import glob import os image_all = glob.glob('data/VOCdevkit/VOC2012/JPEGImages/*.jpg') image_all_name = [image_file.replace('\\', '/').split('/')[-1].split('.')[0] for image_file in image_all] image_SegmentationClass = glob.glob('data/VOCdevkit/VOC2012/SegmentationClass/*.png') image_se_name= [image_file.replace('\\', '/').split('/')[-1].split('.')[0] for image_file in image_SegmentationClass] image_other=list(set(image_all_name) - set(image_se_name)) print(image_other) for image_name in image_other: os.remove('data/VOCdevkit/VOC2012/JPEGImages/{}.jpg'.format(image_name)) # 代码链接 本例选用的代码来自deep-learning-for-image-processing/pytorch_segmentation/fcn at master · WZMIAOMIAO/deep-learning-for-image-processing (github.com) 其他的代码也有很多,这篇比较好理解! 其实还有个比较好的图像分割库:GitHub - qubvel/segmentation_models.pytorch: Segmentation models with pretrained backbones. PyTorch. 这个图像分割集合由俄罗斯的程序员小哥Pavel Yakubovskiy一手打造。在后面的文章,我也会使用这个库演示。 # 项目结构 1. ├── src: 模型的backbone以及FCN的搭建 2. ├── train_utils: 训练、验证以及多GPU训练相关模块 3. ├── my_dataset.py: 自定义dataset用于读取VOC数据集 4. ├── train.py: 以fcn_resnet50(这里使用了Dilated/Atrous Convolution)进行训练 5. ├── predict.py: 简易的预测脚本,使用训练好的权重进行预测测试 6. ├── validation.py: 利用训练好的权重验证/测试数据的mIoU等指标,并生成record_mAP.txt文件 7. └── pascal_voc_classes.json: pascal_voc标签文件 由于代码很多不能一一讲解,所以,接下来对重要的代码做剖析。 # 自定义数据集读取 my_dataset.py自定义数据读取的方法,代码如下: import os import torch.utils.data as data from PIL import Image class VOCSegmentation(data.Dataset): def __init__(self, voc_root, year="2012", transforms=None, txt_name: str = "train.txt"): super(VOCSegmentation, self).__init__() assert year in ["2007", "2012"], "year must be in ['2007', '2012']" root = os.path.join(voc_root, "VOCdevkit", f"VOC{year}") root=root.replace('\\','/') assert os.path.exists(root), "path '{}' does not exist.".format(root) image_dir = os.path.join(root, 'JPEGImages') mask_dir = os.path.join(root, 'SegmentationClass') txt_path = os.path.join(root, "ImageSets", "Segmentation", txt_name) txt_path=txt_path.replace('\\','/') assert os.path.exists(txt_path), "file '{}' does not exist.".format(txt_path) with open(os.path.join(txt_path), "r") as f: file_names = [x.strip() for x in f.readlines() if len(x.strip()) > 0] self.images = [os.path.join(image_dir, x + ".jpg") for x in file_names] self.masks = [os.path.join(mask_dir, x + ".png") for x in file_names] assert (len(self.images) == len(self.masks)) self.transforms = transforms 导入需要的包。 定义VOC数据集读取类VOCSegmentation。在init方法中,核心是读取image列表和mask列表。 def __getitem__(self, index): img = Image.open(self.images[index]).convert('RGB') target = Image.open(self.masks[index]) if self.transforms is not None: img, target = self.transforms(img, target) return img, target __getitem__方法是获取单张图片和图片对应的mask,然后对其做数据增强。 def collate_fn(batch): images, targets = list(zip(*batch)) batched_imgs = cat_list(images, fill_value=0) batched_targets = cat_list(targets, fill_value=255) return batched_imgs, batched_targets collate_fn方法是对一个batch中数据调用cat_list做数据对齐。 在train.py中torch.utils.data.DataLoader调用 train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, num_workers=num_workers, shuffle=True, pin_memory=True, collate_fn=train_dataset.collate_fn) val_loader = torch.utils.data.DataLoader(val_dataset, batch_size=1, num_workers=num_workers, pin_memory=True, collate_fn=val_dataset.collate_fn) # 训练 # 重要参数 打开train.py,我们先认识一下重要的参数: def parse_args(): import argparse parser = argparse.ArgumentParser(description="pytorch fcn training") # 数据集的根目录(VOCdevkit)所在的文件夹 parser.add_argument("--data-path", default="data/", help="VOCdevkit root") parser.add_argument("--num-classes", default=20, type=int) parser.add_argument("--aux", default=True, type=bool, help="auxilier loss") parser.add_argument("--device", default="cuda", help="training device") parser.add_argument("-b", "--batch-size", default=32, type=int) parser.add_argument("--epochs", default=30, type=int, metavar="N", help="number of total epochs to train") parser.add_argument('--lr', default=0.0001, type=float, help='initial learning rate') parser.add_argument('--momentum', default=0.9, type=float, metavar='M', help='momentum') parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float, metavar='W', help='weight decay (default: 1e-4)', dest='weight_decay') parser.add_argument('--print-freq', default=10, type=int, help='print frequency') parser.add_argument('--resume', default='', help='resume from checkpoint') parser.add_argument('--start-epoch', default=0, type=int, metavar='N', help='start epoch') # 是否使用混合精度训练 parser.add_argument("--amp", default=False, type=bool, help="Use torch.cuda.amp for mixed precision training") args = parser.parse_args() return args data-path:定义数据集的根目录(VOCdevkit)所在的文件夹 num-classes:检测目标类别数(不包含背景)。 aux:是否使用aux_classifier。 device:使用cpu还是gpu训练,默认是cuda。 batch-size:BatchSize设置。 epochs:epoch的个数。 lr:学习率。 resume:继续训练时候,选择用的模型。 start-epoch:起始的epoch,针对再次训练时,可以不需要从0开始。 amp:是否使用torch的自动混合精度训练。 # 数据增强 增强调用transforms.py中的方法。 训练集的增强如下: class SegmentationPresetTrain: def __init__(self, base_size, crop_size, hflip_prob=0.5, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)): # 随机Resize的最小尺寸 min_size = int(0.5 * base_size) # 随机Resize的最大尺寸 max_size = int(2.0 * base_size) # 随机Resize增强。 trans = [T.RandomResize(min_size, max_size)] if hflip_prob > 0: #随机水平翻转 trans.append(T.RandomHorizontalFlip(hflip_prob)) trans.extend([ #随机裁剪 T.RandomCrop(crop_size), T.ToTensor(), T.Normalize(mean=mean, std=std), ]) self.transforms = T.Compose(trans) def __call__(self, img, target): return self.transforms(img, target) 训练集增强,包括随机Resize、随机水平翻转、随即裁剪。 验证集增强: class SegmentationPresetEval: def __init__(self, base_size, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)): self.transforms = T.Compose([ T.RandomResize(base_size, base_size), T.ToTensor(), T.Normalize(mean=mean, std=std), ]) def __call__(self, img, target): return self.transforms(img, target) 验证集的增强比较简单,只有随机Resize。 # Main方法 对Main方法,我做了一些修改,修改的代码如下: #定义模型,并加载预训练 model = fcn_resnet50(pretrained=True) # 默认classes是21,如果不是21,则要修改类别。 if num_classes != 21: model.classifier[4] = torch.nn.Conv2d(512, num_classes, kernel_size=(1, 1), stride=(1, 1)) model.aux_classifier[4] = torch.nn.Conv2d(256, num_classes, kernel_size=(1, 1), stride=(1, 1)) print(model) model.to(device) # 如果有多张显卡,则使用多张显卡 if torch.cuda.device_count() > 1: print("Let's use", torch.cuda.device_count(), "GPUs!") model = torch.nn.DataParallel(model) 模型,我改为pytorch官方的模型了,如果能使用官方的模型尽量使用官方的模型。 默认类别是21,如果不是21,则要修改类别。 检测系统中是否有多张卡,如果有多张卡则使用多张卡不能浪费资源。 如果不想使用所有的卡,而是指定其中的几张卡,可以使用: `os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'` 也可以在DataParallel方法中设定: `model = torch.nn.DataParallel(model,device_ids=[0,1])` 如果使用了多显卡,再使用模型的参数就需要改为model.module.xxx,例如: params = [p for p in model.module.aux_classifier.parameters() if p.requires_grad] params_to_optimize.append({"params": params, "lr": args.lr * 10}) 上面的都完成了就可以开始训练了,如下图: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/25/1648176590761509030.png) # 测试 在开始测试之前,我们还要获取到调色板,新建脚本get_palette.py,代码如下: import json import numpy as np from PIL import Image # 读取mask标签 target = Image.open("./2007_001288.png") # 获取调色板 palette = target.getpalette() palette = np.reshape(palette, (-1, 3)).tolist() print(palette) # 转换成字典子形式 pd = dict((i, color) for i, color in enumerate(palette)) json_str = json.dumps(pd) with open("palette.json", "w") as f: f.write(json_str) 选取一张mask,然后使用getpalette方法获取,然后将其转为字典的格式保存。 接下来,开始预测部分,新建predict.py,插入以下代码: import os import time import json import torch from torchvision import transforms import numpy as np from PIL import Image from torchvision.models.segmentation import fcn_resnet50 导入程序需要的包文件,然在mian方法中: def main(): aux = False # inference time not need aux_classifier classes = 20 weights_path = "./save_weights/model_5.pth" img_path = "./2007_000123.jpg" palette_path = "./palette.json" assert os.path.exists(weights_path), f"weights {weights_path} not found." assert os.path.exists(img_path), f"image {img_path} not found." assert os.path.exists(palette_path), f"palette {palette_path} not found." with open(palette_path, "rb") as f: pallette_dict = json.load(f) pallette = [] for v in pallette_dict.values(): pallette += v - 定义是否需要aux_classifier,预测不需要aux_classifier,所以设置为False。 - 设置类别为20,不包括背景。 - 定义权重的路径。 - 定义调色板的路径。 - 读去调色板。 接下来,是加载模型,单显卡训练出来的模型和多显卡训练出来的模型加载有区别,我们先看单显卡训练出来的模型如何加载。 model = fcn_resnet50(num_classes=classes+1) print(model) # 单显卡训练出来的模型,加载 # delete weights about aux_classifier weights_dict = torch.load(weights_path, map_location='cpu')['model'] for k in list(weights_dict.keys()): if "aux_classifier" in k: del weights_dict[k] # load weights model.load_state_dict(weights_dict) model.to(device) 定义模型fcn_resnet50,num_classes设置为类别+1(背景) 加载训练好的模型,并将aux_classifier删除。 然后加载权重。 再看多显卡的模型如何加载 # create model model = fcn_resnet50(num_classes=classes+1) model = torch.nn.DataParallel(model) # delete weights about aux_classifier weights_dict = torch.load(weights_path, map_location='cpu')['model'] print(weights_dict) for k in list(weights_dict.keys()): if "aux_classifier" in k: del weights_dict[k] # load weights model.load_state_dict(weights_dict) model=model.module model.to(device) 定义模型fcn_resnet50,num_classes设置为类别+1(背景),将模型放入DataParallel类中。 加载训练好的模型,并将aux_classifier删除。 加载权重。 执行torch.nn.DataParallel(model)时,model被放在了model.module,所以model.module才真正需要的模型。所以我们在这里将model.module赋值给model。 接下来是图像数据的处理 # load image original_img = Image.open(img_path) # from pil image to tensor and normalize data_transform = transforms.Compose([transforms.Resize(520), transforms.ToTensor(), transforms.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225))]) img = data_transform(original_img) # expand batch dimension img = torch.unsqueeze(img, dim=0) 加载图像。 对图像做Resize、标准化、归一化处理。 使用torch.unsqueeze增加一个维度。 完成图像的处理后,就可以开始预测了。 model.eval() # 进入验证模式 with torch.no_grad(): # init model img_height, img_width = img.shape[-2:] init_img = torch.zeros((1, 3, img_height, img_width), device=device) model(init_img) t_start = time_synchronized() output = model(img.to(device)) t_end = time_synchronized() print("inference+NMS time: {}".format(t_end - t_start)) prediction = output['out'].argmax(1).squeeze(0) prediction = prediction.to("cpu").numpy().astype(np.uint8) np.set_printoptions(threshold=sys.maxsize) print(prediction.shape) mask = Image.fromarray(prediction) mask.putpalette(pallette) mask.save("test_result.png") 将预测后的结果保存到test_result.png中。查看运行结果: 原图: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/25/1648176711259303424.png) 结果: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/25/1648176719819890027.png) 打印出来的数据: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/25/1648176755090525769.png) 类别列表: { "aeroplane": 1, "bicycle": 2, "bird": 3, "boat": 4, "bottle": 5, "bus": 6, "car": 7, "cat": 8, "chair": 9, "cow": 10, "diningtable": 11, "dog": 12, "horse": 13, "motorbike": 14, "person": 15, "pottedplant": 16, "sheep": 17, "sofa": 18, "train": 19, "tvmonitor": 20 } 从结果来看,已经预测出来图像上的类别是“train”。 # 总结 这篇文章的核心内容是讲解如何使用FCN实现图像的语义分割。 在文章的开始,我们讲了一些FCN的结构和优缺点。 然后,讲解了如何读取数据集。 接下来,告诉大家如何实现训练。 最后,是测试以及结果展示。 希望本文能给大家带来帮助。 完整代码: https://download.csdn.net/download/hhhhhhhhhhwwwwwwwwww/83778007
  • [论文解析] Mindquantum实现变分量子奇异值分解
    # 论文题目:Variational Quantum Singular Value Deposition(VQSVD) ## 项目介绍 ### SVD **奇异值分解**(Singular Value Decomposition,简称**SVD**)是线性代数中一种重要的矩阵分解,它作为特征分解在任意维数矩阵上的推广,在机器学习领域中被广泛应用,常用于矩阵压缩、推荐系统以及自然语言处理等。 **定义**:给定一个复数矩阵 $M \in \mathbb{C}^{m \times n}$ ,则定义矩阵 $M$ 的**SVD**为: $M = UDV^\dagger$ 。其中 $U$ 是 $m \times m$ 的矩阵, $V$ 是 $n \times n$ 的矩阵, $U, V$ 都是酉矩阵,即满足 $UU^\dagger = I, VV^\dagger = I$ 。$D$ 是 $m \times n$ 的对角阵,主对角线上的的元素从大到小排列,每个元素都称为矩阵 $M$ 的奇异值。 ### VQSVD **变分量子奇异值分解**(Variational Quantum Singular Value Decomposition,简称**VQSVD**)将SVD转换成优化问题,并通过变分量子线路求解。 论文将矩阵奇异值分解分成4个步骤求解: 1. 输入待分解的矩阵 $M$ ,想压缩到的阶数 $T$ ,权重 $Weights$ ,测量基 $\{ | \psi_1\rangle,\cdots |\psi_T\rangle\}$ ,参数化的酉矩阵 $U(\theta)$ 和 $V(\phi)$ (即ansatz); 2. 搭建量子神经网络估算奇异值 $m_j = \text{Re}\langle\psi_j|U(\theta)^{\dagger} M V(\phi)|\psi_j\rangle$ ,并最大化加权奇异值的和 $L(\theta,\phi) = \sum_{j=1}^T q_j\times \text{Re} \langle\psi_j|U(\theta)^{\dagger} M V(\phi)|\psi_j\rangle$ ,其中,加权是为了让计算出的奇异值从大到小排列; 3. 读出最大化时参数值 $\alpha^ \star$ 和 $\beta^\star$ ,计算出 $U(\alpha^\star)$ 和 $V(\beta^\star)$ 4. 输出结果:奇异值 ${m_1, \cdots, m_r}$ 和奇异矩阵 $U(\alpha^\star)$ 和 $V(\beta^\star)$ ![QSVD.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/21/1647839708061106499.png) ## 复现过程 ### 案例1:分解随机生成的8*8复数矩阵 先引入需要使用的包: ```python import os import mindspore as ms from mindquantum import Simulator, MQAnsatzOnlyLayer, add_prefix from mindquantum import Hamiltonian, Circuit, RY, RZ, X import numpy as np from scipy.sparse import csr_matrix from scipy.linalg import norm from matplotlib import pyplot import tqdm os.environ['OMP_NUM_THREADS'] = '1' ``` 定义常数并设置权重: ```python n_qubits = 3 # qbits number cir_depth = 20 # circuit depth N = 2**n_qubits rank = 8 # learning rank step = 3 ITR = 200 # iterations LR = 0.02 # learning rate # Set equal learning weights if step == 0: weight = ms.Tensor(np.ones(rank)) else: weight = ms.Tensor(np.arange(rank * step, 0, -step)) ``` 随机生成一个8*8的复数矩阵M ```python # Define random seed np.random.seed(42) def mat_generator(): ''' Generate a random complex matrix ''' matrix = np.random.randint( 10, size=(N, N)) + 1j * np.random.randint(10, size=(N, N)) return matrix # Generate matrix M which will be decomposed M = mat_generator() # m_copy is generated to error analysis m_copy = np.copy(M) # Print M print('Random matrix M is: ') print(M) # Get SVD results U, D, v_dagger = np.linalg.svd(M, full_matrices=True) ``` Random matrix M is: [[6.+1.j 3.+9.j 7.+3.j 4.+7.j 6.+6.j 9.+8.j 2.+7.j 6.+4.j] [7.+1.j 4.+4.j 3.+7.j 7.+9.j 7.+8.j 2.+8.j 5.+0.j 4.+8.j] [1.+6.j 7.+8.j 5.+7.j 1.+0.j 4.+7.j 0.+7.j 9.+2.j 5.+0.j] [8.+7.j 0.+2.j 9.+2.j 2.+0.j 6.+4.j 3.+9.j 8.+6.j 2.+9.j] [4.+8.j 2.+6.j 6.+8.j 4.+7.j 8.+1.j 6.+0.j 1.+6.j 3.+6.j] [8.+7.j 1.+4.j 9.+2.j 8.+7.j 9.+5.j 4.+2.j 1.+0.j 3.+2.j] [6.+4.j 7.+2.j 2.+0.j 0.+4.j 3.+9.j 1.+6.j 7.+6.j 3.+8.j] [1.+9.j 5.+9.j 5.+2.j 9.+6.j 3.+0.j 5.+3.j 1.+3.j 9.+4.j]] 定义论文中使用的ansatz类: ```python class Ansatz: ''' Define ansatz ''' def __init__(self, n, depth): self.circ = Circuit() num = 0 for _ in range(depth): for i in range(n): self.circ += RY('theta' + str(num)).on(i) num += 1 for i in range(n): self.circ += RZ('theta' + str(num)).on(i) num += 1 for i in range(n - 1): self.circ += X.on(i + 1, i) self.circ += X.on(0, n - 1) ``` 定义画出优化过程中的学习曲线的函数: ```python def loss_plot(loss): ''' Plot loss over iteration ''' pyplot.plot(list(range(1, len(loss) + 1)), loss) pyplot.xlabel('iteration') pyplot.ylabel('loss') pyplot.title('Loss Over Iteration') pyplot.suptitle('step = ' + str(step)) pyplot.show() ``` 定义不同测量基下的量子网络。 `get_expectation_with_grad` 方法可以用来计算如下表达式的值和线路中参数的梯度。 $$ E(\theta) = \langle\phi|U_l^{\dagger}(\theta) H U_r(\theta)|\psi\rangle $$ 将待分解矩阵M嵌入哈密顿量H中,将 $U_l(\theta)$ 设置成 $U(\theta)$ , $U_r(\theta)$ 设置成 $V(\phi)$ ,通过模拟器的`set_qs`设置模拟器的状态为给定的测量基,从而获取到该测量基下的测量结果,即对应位置的奇异值。再使用 `MQAnsatzOnlyLayer` 即搭建出基于给定测量基下的量子网络层,其输出为 $\text{Re}\langle\psi_j|U(\theta)^{\dagger} M V(\phi)|\psi_j\rangle$ 。 ```python def quantnet(qubits_num, hams, circ_right, circ_left=None, base=None): ''' Generate quantum net using hams, circ_right and circ_left under given base ''' sim = Simulator('projectq', qubits_num) if base is None: pass else: sim.set_qs(base) grad_ops = sim.get_expectation_with_grad(hams, circ_right, circ_left) ms.context.set_context(mode=ms.context.PYNATIVE_MODE, device_target="CPU") quantumnet = MQAnsatzOnlyLayer(grad_ops, 'ones') return quantumnet ``` 实例化要使用到的ansatz:U_ansatz和V_ansatz ```python # Define ansatz u_ansatz = add_prefix(Ansatz(n_qubits, cir_depth).circ, 'u') v_ansatz = add_prefix(Ansatz(n_qubits, cir_depth).circ, 'v') ``` 实例化量子网络层,将待分解的8*8矩阵M稀疏化后生成相应的哈密顿量H,使用计算基$\{ |000\rangle, |001\rangle,\cdots |111\rangle\}$利用量子神经网络计算奇异值 $m_j = \text{Re}\langle\psi_j|U(\theta)^{\dagger} M V(\phi)|\psi_j\rangle$ 。 ```python # Embed M matrix into Hamiltonian ham ham = Hamiltonian(csr_matrix(M)) i_matrix = np.identity(N) quantum_models = dict() quantum_models['net_0'] = quantnet(n_qubits, ham, v_ansatz, u_ansatz, i_matrix[0]) for s in range(1, rank): quantum_models["net_" + str(s)] = quantnet(n_qubits, ham, v_ansatz, u_ansatz, i_matrix[s]) quantum_models["net_" + str(s)].weight = quantum_models['net_0'].weight ``` 结合**MindSpore**提供的经典学习框架构建出量子经典混合网络,实现对量子网络层加权求和,计算出 $L(\theta,\phi) = \sum_{j=1}^T q_j\times \text{Re} \langle\psi_j|U(\theta)^{\dagger} M V(\phi)|\psi_j\rangle$ 。 ```python class MyNet(ms.nn.Cell): ''' define quantum-classic net ''' def __init__(self): super(MyNet, self).__init__() self.build_block = ms.nn.CellList() for j in range(rank): self.build_block.append(quantum_models["net_" + str(j)]) def construct(self): x = self.build_block[0]() * weight[0] k = 1 for layer in self.build_block[1:]: x += layer() * weight[k] k += 1 return -x ``` 实例化量子经典混合网络并调用**MindSpore**的API开始训练: ```python # Define network net = MyNet() # Define optimizer opt = ms.nn.Adam(net.trainable_params(), learning_rate=LR) # Simple gradient descent train_net = ms.nn.TrainOneStepCell(net, opt) # Start to train net loss_list = list() for itr in tqdm.tqdm(range(ITR)): res = train_net() loss_list.append(res.asnumpy().tolist()) ``` 100%|██████████| 200/200 [03:5700:00, 1.19s/it] 读取训练出的结果,即奇异值: ```python # Get singular value results singular_value = list() for _, qnet in quantum_models.items(): singular_value.append(qnet().asnumpy()[0]) ``` 观察学习曲线以判断是否收敛: ```python # Plot loss over iteration loss_plot(loss_list) ``` ![output_26_0.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/21/1647839883705154633.png) 通过学习曲线我们可以发现损失函数已收敛。 接下来打印出学习到的奇异值结果,并与经典比较: ```python print('Predicted singular values from large to small:', singular_value) print("True singular values from large to small:", D) ``` Predicted singular values from large to small: [54.83174, 19.169168, 14.88653, 11.093878, 10.533753, 7.648352, 5.5560594, -0.3320913] True singular values from large to small: [54.83484985 19.18141073 14.98866247 11.61419557 10.15927045 7.60223249 5.81040539 3.30116001] 直观上我们可以看到使用量子经典混合网络学习出的奇异值与真实奇异值相差不大。 接下来我们数值分析使用VQSVD进行矩阵压缩的误差。 我们使用前 $T$ 个奇异值和前 $T$ 个左右奇异向量组合出新的矩阵 $M_{re}^{(T)}$ 来近似表示矩阵 $M$ : $$ M_{re}^{(T)} = U^{(T)}D^{(T)}({V^{(T)}})^{ \dagger} $$ 这样近似的误差为: $error = ||M - M_{re}^{(T)}||$ 。 设矩阵 $M$ 的秩为 $r$ ,即奇异值个数为 $r$ ,则误差随着使用奇异值的数量变多会越来越小。经典的奇异值算法可以保证 $\lim_{T\rightarrow r} ||M - M_{re}^{(T)}||^2_F = 0$ 。 其中矩阵间的距离我们使用 Frobenius-norm 来度量: $||M||_F = \sqrt{\sum_{i,j} |M_{ij}|^2}$ 。 为了测量误差,我们首先取出网络中的参数值并赋给U_ansatz和V_ansatz,以获得学习出的奇异矩阵: ```python # Get parameters value value = quantum_models['net_0'].weight.asnumpy() v_value = value[:120] u_value = value[120:] # Calculate U and V u_learned = u_ansatz.matrix(u_value) v_learned = v_ansatz.matrix(v_value) v_dagger_learned = np.conj(v_learned.T) d_learned = np.array(singular_value) ``` 我们将学习到的奇异值分解结果组装回去,与原矩阵计算 Frobenius-norm 误差以观察效果,并与经典奇异值分解的误差作比较: ```python err_subfull, err_local, err_svd = [], [], [] U, D, v_dagger = np.linalg.svd(M, full_matrices=True) # Calculate Frobenius-norm error for t in range(rank): lowrank_mat = np.matrix(U[:, :t]) * np.diag(D[:t]) * np.matrix( v_dagger[:t, :]) recons_mat = np.matrix(u_learned[:, :t]) * np.diag( d_learned[:t]) * np.matrix(v_dagger_learned[:t, :]) err_local.append(norm(lowrank_mat - recons_mat)) err_subfull.append(norm(m_copy - recons_mat)) err_svd.append(norm(m_copy - lowrank_mat)) # Plot SVD error and VQSVD error fig, ax = pyplot.subplots() ax.plot(list(range(1, rank + 1)), err_subfull, "o-.", label='Reconstruction via VQSVD') ax.plot(list(range(1, rank + 1)), err_svd, "^--", label='Reconstruction via SVD') # ax.plot(list(range(1, rank + 1)), err_local, "*--", label='SVD V/S QSVD') pyplot.xlabel('Singular Value Used (Rank)', fontsize=14) pyplot.ylabel('Norm Distance', fontsize=14) leg = pyplot.legend(frameon=True) leg.get_frame().set_edgecolor('k') ``` ![output_33_0.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/21/1647839864049892127.png) 可以看到,使用量子神经网络分解出的奇异值结果误差与经典奇异值分解的误差十分接近。 --- ## 案例2:图像压缩 我们先引入图像处理的包,并导入提前准备好的图片。 ```python from PIL import Image # Open figure MNIST_32.jpg and get matrix form img = Image.open(r'.\\figure\\MNIST_32.png') imgmat = np.array(list(img.getdata(band=0)), float) imgmat.shape = (img.size[1], img.size[0]) imgmat = np.matrix(imgmat) / 255 ``` ### 使用经典SVD压缩图像的效果 我们分别取前5、10、15个奇异值作图像压缩,观察结果。 ```python # Get SVD results and show it U, sigma, V = np.linalg.svd(imgmat) for t in range(5, 16, 5): reconstimg = np.matrix(U[:, :t]) * np.diag(sigma[:t]) * np.matrix(V[:t, :]) pyplot.imshow(reconstimg, cmap='gray') title = "n = %s" % t pyplot.title(title) pyplot.show() ``` ![output_38_0.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/21/1647839901986634466.png) ![output_38_1.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/21/1647839909572558804.png) ![output_38_2.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/21/1647839918255172628.png) ### 量子版本的分解效果: 此处我们展示取前8个奇异值作图像压缩的过程及最终结果。 ```python # Set super parameters n_qubits = 5 # qbits number cir_depth = 40 # circuit depth N = 2**n_qubits rank = 8 # learning rank step = 2 ITR = 200 # iterations LR = 0.02 # learning rate SEED = 14 # random seed # Set equal learning weights if step == 0: weight = ms.Tensor(np.ones(rank)) else: weight = ms.Tensor(np.arange(rank * step, 0, -step)) def mat_generator(image): ''' Generate matrix by input image ''' img_matrix = np.array(list(image.getdata(band=0)), float) img_matrix.shape = (image.size[1], image.size[0]) img_np = np.matrix(img_matrix) return img_np # Generate matrix M which will be decomposed M = mat_generator(img) # Get SVD results U, D, v_dagger = np.linalg.svd(M, full_matrices=True) ``` 定义论文中使用的ansatz类: ```python class Ansatz: ''' Define ansatz ''' def __init__(self, n, depth): self.circ = Circuit() num = 0 for _ in range(depth): for i in range(n): self.circ += RY('theta' + str(num)).on(i) num += 1 for i in range(n - 1): self.circ += X.on(i + 1, i) ``` ```python def quantnet(qubits_num, hams, circ_right, circ_left=None, base=None): ''' Generate quantum net using hams, circ_right and circ_left under given base ''' sim = Simulator('projectq', qubits_num) if base is None: pass else: sim.set_qs(base) grad_ops = sim.get_expectation_with_grad(hams, circ_right, circ_left) ms.context.set_context(mode=ms.context.PYNATIVE_MODE, device_target="CPU") quantumnet = MQAnsatzOnlyLayer(grad_ops, 'ones') return quantumnet ``` ```python # Define ansatz u_ansatz = add_prefix(Ansatz(n_qubits, cir_depth).circ, 'u') v_ansatz = add_prefix(Ansatz(n_qubits, cir_depth).circ, 'v') ``` ```python # Embed M matrix into Hamiltonian ham ham = Hamiltonian(csr_matrix(M)) i_matrix = np.identity(N) quantum_models = dict() quantum_models['net_0'] = quantnet(n_qubits, ham, v_ansatz, u_ansatz, i_matrix[0]) for s in range(1, rank): quantum_models["net_" + str(s)] = quantnet(n_qubits, ham, v_ansatz, u_ansatz, i_matrix[s]) quantum_models["net_" + str(s)].weight = quantum_models['net_0'].weight ``` ```python class MyNet(ms.nn.Cell): ''' define quantum-classic net ''' def __init__(self): super(MyNet, self).__init__() self.build_block = ms.nn.CellList() for j in range(rank): self.build_block.append(quantum_models["net_" + str(j)]) def construct(self): x = self.build_block[0]() * weight[0] k = 1 for layer in self.build_block[1:]: x += layer() * weight[k] k += 1 return -x ``` ```python # Define network net = MyNet() # Define optimizer opt = ms.nn.Adam(net.trainable_params(), learning_rate=LR) # Simple gradient descent train_net = ms.nn.TrainOneStepCell(net, opt) # Start to train net loss_list = list() for itr in tqdm.tqdm(range(ITR)): res = train_net() loss_list.append(res.asnumpy().tolist()) ``` 100%|██████████| 200/200 [08:0600:00, 2.43s/it] ```python # Get singular value results singular_value = list() for _, qnet in quantum_models.items(): singular_value.append(qnet().asnumpy()[0]) ``` ```python # Get parameters value value = quantum_models['net_0'].weight.asnumpy() v_value = value[:200] u_value = value[200:] # Calculate U and V u_learned = u_ansatz.matrix(u_value) v_learned = v_ansatz.matrix(v_value) v_dagger_learned = np.conj(v_learned.T) d_learned = np.array(singular_value) ``` 将学习到的奇异值分解结果组装回去,并使用pyplot包的imshow直观展示,以观察图像压缩的结果: ```python # Calculate recombined matrix mat mat = np.matrix(u_learned[:, :rank]) * np.diag(d_learned[:rank]) * np.matrix( v_dagger_learned[:rank, :]) # Show recombination result reconstimg = np.abs(mat) pyplot.imshow(reconstimg, cmap='gray') ``` ![output_51_1.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/21/1647839950280854092.png) 可以看到,图像压缩只取前8个奇异值作近似得到的结果仍具有较好的辨识度。 ## 项目总结 本次复现我完成了量子版本的奇异值分解变分算法,成功应用算法分解随机8*8的复数矩阵、压缩矩阵分析误差并与经典奇异值分解算法结果相对比。 虽然论文里理论上是使用变分量子神经网络来近似优化,但在实际代码中,原作者使用的是将量子网络转换成经典张量网络优化,又转换回了经典计算机上,并未利用到量子计算机的特性。 考虑到**MindQuantum**是模拟真实物理上的量子计算机,基于测量给出结果,我学习了**MindQuantum**的用法后发现`get_expectation_with_grad` 方法可以用来计算如下表达式的值和线路中参数的梯度。 $$ E(\theta) = \langle\phi|U_l^{\dagger}(\theta) H U_r(\theta)|\psi\rangle $$ 我将待分解矩阵M嵌入哈密顿量H中,将 $U_l(\theta)$ 设置成 $U(\theta)$ , $U_r(\theta)$ 设置成 $V(\psi)$ ,通过模拟器的`set_qs`设置模拟器的状态为我所取的测量基,从而获取到给定测量基下的测量结果,即对应位置的奇异值。再利用**MindQuantum**的 `MQAnsatzOnlyLayer` 搭建出基于各测量基下的量子网络层,并结合**MindSpore**提供的经典学习框架构建出量子经典混合网络,实现对量子网络层加权求和,从而构建出损失函数并进行优化。 这种方法更符合量子计算的逻辑,更能发挥出量子ansatz的优势,从最终结果来看,这种方法的误差也要优于原作者转换成张量网络求解的方法。 下面两图是量子奇异值分解(VQSVD)误差与经典奇异值分解误差(SVD)对比,其中第一图是使用新算法复现出的结果,第二图是使用原算法画出的图,二者对比可以发现新算法的误差优于原算法。 ![error.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/21/1647839786964930415.png) ![error_bd.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/21/1647839775889388267.png) 下面两图是在案例二:图像压缩中运行出的结果,其中第一图是使用新算法复现出的结果,第二图是使用原算法画出的图,二者对比可以发现使用新算法压缩出的图片效果要优于原算法。 ![compress.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/21/1647839826556826594.png) ![compress_baidu.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/21/1647839831733271022.png) 本论文展示出了变分量子线路的强大,它只需几个比特就可以完成指数级矩阵的运算,未来我们可以考虑基于变分量子线路,通过搭建量子经典混合神经网络求解量子奇异值转换的多项式参数,从而解决算法的精度问题。 --- ## 参考文献 [1] Wang, X., Song, Z., & Wang, Y. Variational Quantum Singular Value Decomposition. [Quantum, 5, 483 (2021).](https://quantum-journal.org/papers/q-2021-06-29-483/)
  • [前沿快讯] CVPR 2022 | 图像也是德布罗意波!华为诺亚&amp;北大提出量子启发MLP,性能超越Swin Transfomer
    来自华为诺亚方舟实验室、北京大学、悉尼大学的研究者提出了一种受量子力学启发的视觉 MLP 新架构。近年来,计算机视觉领域的新型架构层出不穷,包括视觉 Transformer、MLP 等,它们在很多任务上都取得了超越 CNN 的性能,受到广泛关注。其中,视觉 MLP 具有极其简单的架构,它仅由多层感知器(MLP)堆叠而成。与 CNN 和 Transformer 相比,这些简洁的 MLP 架构引入了更少的归纳偏置,具有更强的泛化性能。 然而,现有视觉 MLP 架构的性能依然弱于 CNN 和 Transformer。来自华为诺亚方舟实验室、北京大学、悉尼大学的研究者提出了一种受量子力学启发的视觉 MLP 架构,在 ImageNet 分类、COCO 检测、ADE20K 分割等多个任务上取得了 SOTA 性能。 论文地址:https://arxiv.org/abs/2111.12294 PyTorch 代码:https://github.com/huawei-noah/CV-Backbones/tree/master/wavemlp_pytorch MindSpore 代码:https://gitee.com/mindspore/models/tree/master/research/cv/wave_mlp Wave-MLP 该研究受量子力学中波粒二象性的启发,将 MLP 中每个图像块 (Token) 表示成波函数的形式,从而提出了一个新型的视觉 MLP 架构——Wave-MLP,在性能上大幅超越了现有 MLP 架构以及 Transformer。量子力学是描述微观粒子运动规律的物理学分支,经典力学可被视为量子力学的特例。量子力学的一个基本属性是波粒二象性,即所有的个体(比如电子、光子、原子等)都可以同时使用粒子的术语和波的术语来描述。一个波通常包括幅值和相位两个属性,幅值表示一个波可能达到的最大强度,相位指示着当前处在一个周期的哪个位置。将一个经典意义上的粒子用波(比如,德布罗意波)的形式来表示,可以更完备地描述微观粒子的运动状态。那么,对于视觉 MLP 中的图像块,能不能也把它表示成波的形式呢?该研究用幅值表达每个 Token 所包含的实际信息,用相位来表示这个 Token 当前所处的状态。在**不同 Token 信息的时候,不同 Token 之间的相位差会调制它们之间的聚合过程(如图 3 示)。考虑到来自不同输入图像的 Token 包含不同的语义内容,该研究使用一个简单的全连接模块来动态估计每个 Token 的相位。对于同时带有幅度和相位信息的 Token,作者提出了一个相位感知 Token 混合模块(PATM,如下图 1 所示)来聚合它们的信息。交替堆叠 PATM 模块和 MLP 模块构成了整个 Wave-MLP 架构。相比现有的视觉 Transformer 和 MLP 架构,Wave-MLP 有着明显的性能优势(如下图 2 所示)。在 ImageNet,Wave-MLP-S 模型上以 4.5G FLOPs 实现了 82.6% 的 top-1 准确率,比相似计算代价的 Swin-T 高 1.3 个点。此外,Wave-MLP 也可以推广到目标检测和语义分割等下游任务,展现出强大的泛化性能。 用波表示 Token 在 Wave-MLP 中,Token 被表示为同时具有幅值和相位信息的波其中 i 是满足 i^2 = -1 的虚数单位,|·| 表示绝对值运算,⊙是逐元素乘法。幅值 |z_j| 是实值的特征,表示每个 Token 所包含的内容。θ_j 表示相位,即 Token 在一个波周期内的当前位置。 两个 Token 之间的相位差对它们的聚合过程有很大影响 (如下图 3 所示)。当两个 token 具有相同的相位时,它们会相互增强,得到幅值更大的波(图 3(b));当两个 token 相位相反时,他们合成的波将相互减弱。在其他情况下,它们之间的相互作用更加复杂,但仍取决于相位差(图 3(a))。经典方法中使用实值表示 token 的,这实际上是上式的一个特例。 图 3:两个具有不同相位的波的聚合过程。左侧表示两个波在复数域中的叠加,右侧表示它们在实轴上的投影随着相位的变化。虚线表示两个初始相位不同的波,实线是他们的叠加。 相位感知的 Token 聚合 公式(1)中包含幅值和相位两项,幅值 z_j 类似于实值特征,可以采用标准的 Channel-FC 生成: (5)在视觉 MLP 中,该研究构建了一个相位感知模块(PATM,图 1)来完成 Token 聚合的过程。交替堆叠 PATM 模块和 channel-mixing MLP 组建了整个 WaveMLP 架构。 实验结果 该研究在大规模的分类数据集 ImageNet, 目标检测数据集 COCO 和语义分割数据集 ADE20K 上都进行了大量实验。 ImageNet 上图像分类的结果如表 1,表 2 所示:相比于现有的 Vision MLP 架构和 Transformer 架构,WaveMLP 都取得了明显的性能优势。 在下游目标检测、语义分割等任务中,Wave-MLP 同样表现出更优的性能。
  • [技术干货] MindSpore快速入门
    置运行信息MindSpore通过context.set_context来配置运行需要的信息,譬如运行模式、后端信息、硬件等信息。导入context模块,配置运行需要的信息。import osimport argparsefrom mindspore import contextparser = argparse.ArgumentParser(description='MindSpore LeNet Example')parser.add_argument('--device_target', type=str, default="CPU", choices=['Ascend', 'GPU', 'CPU'])args = parser.parse_args()context.set_context(mode=context.GRAPH_MODE, device_target=args.device_target)在样例中我们配置样例运行使用图模式。根据实际情况配置硬件信息,譬如代码运行在Ascend AI处理器上,则--device_target选择Ascend,代码运行在CPU、GPU同理。详细参数说明,请参见context.set_context接口说明。数据处理数据集对于模型训练非常重要,好的数据集可以有效提高训练精度和效率。 MindSpore提供了用于数据处理的API mindspore.dataset ,用于存储样本和标签。在加载数据集前,我们通常会对数据集进行一些处理,dataset也集成了常见的数据处理方法。定义数据集及数据操作首先导入MindSpore中mindspore.dataset和其他相应的模块。import mindspore.dataset as dsimport mindspore.dataset.transforms.c_transforms as Cimport mindspore.dataset.vision.c_transforms as CVfrom mindspore.dataset.vision import Interfrom mindspore import dtype as mstype我们定义函数create_dataset来创建数据集:def create_dataset(data_path, batch_size=32, repeat_size=1,num_parallel_workers=1):# 定义数据集mnist_ds = ds.MnistDataset(data_path)在这个函数中,我们定义好需要进行的数据增强和处理操作,为之后进行map映射做准备。resize_height, resize_width = 32, 32rescale = 1.0 / 255.0shift = 0.0rescale_nml = 1 / 0.3081shift_nml = -1 * 0.1307 / 0.3081# 定义所需要的操作的map映射resize_op = CV.Resize((resize_height, resize_width), interpolation=Inter.LINEAR)rescale_nml_op = CV.Rescale(rescale_nml, shift_nml)rescale_op = CV.Rescale(rescale, shift)hwc2chw_op = CV.HWC2CHW()type_cast_op = C.TypeCast(mstype.int32)使用map映射函数,将数据操作应用到数据集。mnist_ds = mnist_ds.map(operations=type_cast_op, input_columns="label", num_parallel_workers=num_parallel_workers)mnist_ds = mnist_ds.map(operations=resize_op, input_columns="image", num_parallel_workers=num_parallel_workers)mnist_ds = mnist_ds.map(operations=rescale_op, input_columns="image", num_parallel_workers=num_parallel_workers)mnist_ds = mnist_ds.map(operations=rescale_nml_op, input_columns="image", num_parallel_workers=num_parallel_workers)mnist_ds = mnist_ds.map(operations=hwc2chw_op, input_columns="image", num_parallel_workers=num_parallel_workers)先进行shuffle、batch操作,再进行repeat操作,这样能保证1个epoch内数据不重复。buffer_size = 10000mnist_ds = mnist_ds.shuffle(buffer_size=buffer_size)mnist_ds = mnist_ds.batch(batch_size, drop_remainder=True)return mnist_ds其中,batch_size:每组包含的数据个数,现设置每组包含32个数据。MindSpore支持进行多种数据处理和增强的操作,具体可以参考 数据处理和 数据增强章节。创建模型使用MindSpore定义神经网络需要继承mindspore.nn.Cell。Cell是所有神经网络(如Conv2d-relu-softmax等)的基类。神经网络的各层需要预先在__init__方法中定义,然后通过定义construct方法来完成神经网络的前向构造。按照LeNet的网络结构,定义网络各层如下:import mindspore.nn as nnfrom mindspore.common.initializer import Normalclass LeNet5(nn.Cell):"""Lenet网络结构"""def __init__(self, num_class=10, num_channel=1):super(LeNet5, self).__init__()# 定义所需要的运算self.conv1 = nn.Conv2d(num_channel, 6, 5, pad_mode='valid')self.conv2 = nn.Conv2d(6, 16, 5, pad_mode='valid')self.fc1 = nn.Dense(16 * 5 * 5, 120, weight_init=Normal(0.02))self.fc2 = nn.Dense(120, 84, weight_init=Normal(0.02))self.fc3 = nn.Dense(84, num_class, weight_init=Normal(0.02))self.relu = nn.ReLU()self.max_pool2d = nn.MaxPool2d(kernel_size=2, stride=2)self.flatten = nn.Flatten()def construct(self, x):# 使用定义好的运算构建前向网络x = self.conv1(x)x = self.relu(x)x = self.max_pool2d(x)x = self.conv2(x)x = self.relu(x)x = self.max_pool2d(x)x = self.flatten(x)x = self.fc1(x)x = self.relu(x)x = self.fc2(x)x = self.relu(x)x = self.fc3(x)return x# 实例化网络net = LeNet5()阅读更多有关 在MindSpore中构建神经网络的信息。优化模型参数要训练神经网络模型,需要定义损失函数和优化器。MindSpore支持的损失函数有SoftmaxCrossEntropyWithLogits、L1Loss、MSELoss等。这里使用交叉熵损失函数SoftmaxCrossEntropyWithLogits。# 定义损失函数net_loss = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')阅读更多有关 在MindSpore中损失函数的信息。MindSpore支持的优化器有Adam、AdamWeightDecay、Momentum等。这里使用Momentum优化器为例。# 定义优化器net_opt = nn.Momentum(net.trainable_params(), learning_rate=0.01, momentum=0.9)阅读更多有关 在MindSpore中优化器的信息。训练及保存模型MindSpore提供了回调callback机制,可以在训练过程中执行自定义逻辑,这里使用框架提供的ModelCheckpoint为例。 ModelCheckpoint可以保存网络模型和参数,以便进行后续的fine-tuning(微调)操作。from mindspore.train.callback import ModelCheckpoint, CheckpointConfig# 设置模型保存参数config_ck = CheckpointConfig(save_checkpoint_steps=1875, keep_checkpoint_max=10)# 应用模型保存参数ckpoint = ModelCheckpoint(prefix="checkpoint_lenet", config=config_ck)通过MindSpore提供的model.train接口可以方便地进行网络的训练,LossMonitor可以监控训练过程中loss值的变化。# 导入模型训练需要的库from mindspore.nn.metric import Accuracyfrom mindspore.train.callback import LossMonitorfrom mindspore import modeldef train_net(args, model, epoch_size, data_path, repeat_size, ckpoint_cb, sink_mode):"""定义训练的方法"""# 加载训练数据集ds_train = create_dataset(os.path.join(data_path, "train"), 32, repeat_size)model.train(epoch_size, ds_train, callbacks=[ckpoint_cb, LossMonitor()], dataset_sink_mode=sink_mode)其中,dataset_sink_mode是用于控制数据是否下沉,数据下沉是指数据通过通道直接传送到Device上,可以加快训练速度,dataset_sink_mode为True表示数据下沉,否则为非下沉。通过模型运行测试数据集得到的结果,验证模型的泛化能力。使用model.eval接口读入测试数据集。使用保存后的模型参数进行推理。def test_net(network, model, data_path):"""定义验证的方法"""ds_eval = create_dataset(os.path.join(data_path, "test"))acc = model.eval(ds_eval, dataset_sink_mode=False)print("{}".format(acc))这里把train_epoch设置为1,对数据集进行1个迭代的训练。在train_net和 test_net方法中,我们加载了之前下载的训练数据集,mnist_path是MNIST数据集路径。train_epoch = 1mnist_path = "./MNIST_Data"dataset_size = 1model = Model(net, net_loss, net_opt, metrics={"Accuracy": Accuracy()})train_net(args, model, train_epoch, mnist_path, dataset_size, ckpoint, False)test_net(net, model, mnist_path)使用以下命令运行脚本:python lenet.py --device_target=CPU其中,lenet.py:为你根据教程编写的脚本文件。--device_target CPU:指定运行硬件平台,参数为CPU、GPU或者Ascend,根据你的实际运行硬件平台来指定。训练过程中会打印loss值,类似下图。loss值会波动,但总体来说loss值会逐步减小,精度逐步提高。每个人运行的loss值有一定随机性,不一定完全相同。 训练过程中loss打印示例如下:epoch: 1 step: 1, loss is 2.3025916epoch: 1 step: 2, loss is 2.302577...epoch: 1 step: 1871, loss is 0.048939988epoch: 1 step: 1872, loss is 0.028885357epoch: 1 step: 1873, loss is 0.09475248epoch: 1 step: 1874, loss is 0.046067055epoch: 1 step: 1875, loss is 0.12366105{'Accuracy': 0.9663477564102564}可以在打印信息中看出模型精度数据,示例中精度数据达到96.6%,模型质量良好。随着网络迭代次数train_epoch增加,模型精度会进一步提高。加载模型from mindspore.train.serialization import load_checkpoint, load_param_into_net# 加载已经保存的用于测试的模型param_dict = load_checkpoint("checkpoint_lenet-1_1875.ckpt")# 加载参数到网络中load_param_into_net(net, param_dict)阅读更多有关 MindSpore加载模型的信息的信息。验证模型classes = ["Zero","One","Two","Three","Four","Fives","Six","Seven","Eight","Nine",]x, y = test_data[0][0], test_data[0][1]pred = model(x)predicted, actual = classes[pred[0].argmax(0)], classes[y]print(f'Predicted: "{predicted}", Actual: "{actual}"')其中,lenet.py:为你根据教程编写的脚本文件。--device_target CPU:指定运行硬件平台,参数为CPU、GPU或者Ascend,根据你的实际运行硬件平台来指定。运行结果示例如下:Predicted: "Eight", Actual: "Eight"原文链接:https://blog.csdn.net/u011983997/article/details/123117072?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164717901716780274180858%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=164717901716780274180858&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-12-123117072.nonecase&utm_term=MindSpore&spm=1018.2226.3001.4450
  • [主题讨论] RNN系batch_first参数
    这个参数没什么用啊,我以为和pytorch一样,发现没什么用
  • [其他] YOLOv7非正式版开源
    YOLOv7非正式版开源,好的框架离不开发起人的参与和技术支持和众多开发者的努力“You Only Look Once”或“YOLO”是一个对象检测算法的名字,这是Redmon等人在2016年的一篇研究论文中命名的。YOLO实现了自动驾驶汽车等前沿技术中使用的实时对象检测。让我们看看是什么使该算法如此受欢迎,并概述其工作原理。当人们看到图像以后,可以立即识别其中的对象、它们的位置和相对位置。这使得我们能够在几乎无意识的情况下完成复杂的任务,比如开车。因此,对汽车进行自动驾驶训练需要类似水平的反应能力和准确性。在其最基本的形式中,这样的系统必须能够分析实时视频中的道路,并能够在继续确定路径之前检测各种类型的对象及其在现实世界中的位置,所有这些都必须是实时的。YOLO将对象检测重新定义为一个回归问题。它将单个卷积神经网络(CNN)应用于整个图像,将图像分成网格,并预测每个网格的类概率和边界框。例如,以一个100x100的图像为例。我们把它分成网格,比如7x7。每个边界框可以使用四个描述符进行描述:界框的中心,高度,宽度,值映射到对象所属的类Github:https://github.com/jinfagang/yolov7
  • [特性分析] 再改RNNs,试用While循环不展开特性
    RNNs的循环问题上次改RNNs已经是半年前的事了,虽然保证了正确性,但是还是遗留了一个问题。这里直接截取核心的一段: ...... def recurrent(self, x, h_0, w_ih, w_hh, b_ih, b_hh): time_step = x.shape[0] outputs = [] t = 0 h = h_0 while t < time_step: h = self.cell(x[t], h, w_ih, w_hh, b_ih, b_hh) if self.is_lstm: outputs.append(h[0]) else: outputs.append(h) t += 1 outputs = P.Stack()(outputs) return outputs, h ......核心问题在于RNN的循环特性,也就是每个time step的结果都要依赖上一个,一般RNN层都是以一个大算子的形式直接实现双向多层,然后Python层做个binding调用。用小算子堆叠+While循环,表现在计算图里就会是一个很深的调用栈。假设一个循环中执行的算子数为N,输入序列长度为T ,则引入一个单层单向的RNN,在While循环展开后需要N*T个算子,同样也是调用栈深度。而MindSpore默认设置的最大调用深度为1000,一旦序列长度过长,会直接报错:RuntimeError: mindspore/ccsrc/pipeline/jit/static_analysis/evaluator.cc:201 Eval] Exceed function call depth limit 1000, (function call depth: 1001, simulate call depth: 998). It's always happened with complex construction of code or infinite recursion or loop. Please check the code if it's has the infinite recursion or call 'context.set_context(max_call_depth=value)' to adjust this value. If max_call_depth is set larger, the system max stack depth should be set larger too to avoid stack overflow.此时可以适当调大max_call_depth参数,但是会引入几个问题:代码修改,哪怕只有一行编译时间过长max_call_depth的设置需要多次尝试总体来说,一般序列长度在200以下不会触发max_call_depth的限制,但是通常NLP长文本任务轻易就会到达500以上的长度,且编译时间随着文本长度的增加而线性增长,导致RNNs在长文本场景下几乎不可用。While循环不展开所幸1.5到1.6这段前端编译团队时间攻关了这个问题,提供了While循环不展开的写法,这里先放个链接。使用流程控制语句 - MindSpore master documentation昨天试用了一下,把RNNs的实现改成了While循环不展开的形态: def construct(self, x, h, seq_length, w_ih, w_hh, b_ih, b_hh): time_step = x.shape[0] outputs = P.Zeros()((time_step, h.shape[0], h.shape[1]), x.dtype) t = P.ScalarToTensor()(0, mstype.int64) while t < time_step: x_t = x[t] h = self.cell(x_t, h, w_ih, w_hh, b_ih, b_hh) outputs[t] = h t += 1 if seq_length is not None: h = get_hidden(outputs, seq_length) mask = sequence_mask(seq_length, time_step) outputs = select_by_mask(outputs, mask) return outputs, h将while循环条件变量由scalar改成Tensor,即可直接使用不展开编译,但是有以下的限制:当while的条件为变量时,while循环体不能展开,while循环体内的表达式都是在各个step运行时计算,因此循环体内部不能出现标量、List、Tuple等非Tensor类型的计算操作,这些类型计算操作需要在图编译时期完成,与while在执行期进行计算的机制是矛盾的。所以实现上不再用List保存然后Stack的方式,而是先实例化一个空的Tensor,然后逐个step的填充结果。简单测试了一下GPU环境下的编译时间和执行时间(batch size = 3, input size = 16, hidden size = 32):序列长度编译时间-展开(s)执行时间-展开(s)编译时间-不展开(s)执行时间-不展开(s)1002.2580.0490.3750.04850016.2210.1300.3800.149100054.5040.2070.3500.30210000--0.4202.686可以看到循环不展开的情况下,编译时间基本不受序列长度的影响,而执行时间上相对循环展开会有一定的损耗。但是在序列长度为500以上均需要调整max_call_depth, 当长度达到10000时,编译时长已经到了完全不可接受的程度(编译了30分钟还没有结束,手动结束进程)。小结作为一个NLPer,循环不展开特性是刚需中的刚需,一些有时序依赖的模型都可以用这个方式进行加速,在静态图下可以基本无视文本长度进行训练了。MindSpore想要从静态图出发逐步去支持Python语法集合来达到动静统一的目标也逐步开始有达成的痕迹,相较于Tensorflow的tf.while_loop接口设计,确实基本实现了Python语法一致。写此文之前咨询过开发的同事,对于List和Tuple等非Tensor的操作应该后续也会支持,在常用场景都覆盖的情况下,将While循环默认置为不展开,又是对易用性的一个显著提升。最后RNNs的坑基本上算是填完了,后面会把修改后的代码上到master分支,后续有好玩/有用的特性应该还会写写,顺带预告一下下一篇,讲讲CRF和怎么用MindSpore实现。未完待续。。。
  • [热门活动] 2021年度CANN训练营第二期课程回顾
    CANN训练营第二期包含:萌新-模型营、萌新-应用营、高玩-模型营、高玩-应用营、高玩算子营学习基础萌新-模型营:python基础使用、大致了解深度学习原理萌新-应用营:python基础使用、大致了解深度学习原理高玩-模型营:python基础使用、大致了解深度学习原理、基本了解分布式训练原理、一定NLP基础高玩-应用营:C++、Python基础知识,一定深度学习框架与昇腾应用开发经验。高玩-算子营:python基础使用,一定的数学基础 2021年6月28日CANN训练营第二期开营仪式https://www.bilibili.com/video/BV15M4y1g7sS萌新-模型营(共3讲)萌新-模型营第一课——当你成为一个卷积算子https://www.bilibili.com/video/BV18v411H7fK/萌新-模型营第二课——卷积:我裂开了https://www.bilibili.com/video/BV1df4y1L7TJ萌新-模型营第三课——卷积:我好像见鬼了https://www.bilibili.com/video/BV1TK4y1u7Bd萌新-应用营(共3讲)萌新-应用营第一课——MindX SDK快速开发AI应用https://education.huaweicloud.com/courses/course-v1:HuaweiX+CBUCNXA042+Self-paced/about?isAuth=0&cfrom=hwc萌新-应用营第二课——和X一起冲上云霄https://www.bilibili.com/video/BV1wh411a72c萌新-应用营第三课——与X终极一战https://www.bilibili.com/video/BV1t64y1z78B高玩-模型营高玩赛段-模型营第四课——啥是卷积啊?Attention is all you need.https://www.bilibili.com/video/BV1EU4y1H7j2高玩赛段-模型营赛题解析https://www.bilibili.com/video/BV1x44y1k7s4高玩-应用营视频课程:MindX应用使能组件介绍https://www.hiascend.com/zh/activities/cloud2021/live/8120实验课程:使用MindX SDK开发智能质检应用https://lab.huaweicloud.com/testdetail_531实验课程:MindX SDK + Pytorch yolov5 应用案例详解https://bbs.huaweicloud.com/forum/thread-118598-1-1.html高玩-算子营高玩赛段-算子营——TBE算子开发(初级)(CANN5.x版本)https://www.bilibili.com/video/BV1L44y127df该路径下的四个视频均为本次学习视频:2021年8月19日CANN训练营第二期结营仪式https://www.bilibili.com/video/BV1Rh411i7wB 往期回顾汇总:CANN训练营第一期课程回顾(点击查看)CANN训练营第二期课程回顾(点击查看)CANN训练营第三期课程回顾(点击查看)CANN训练营第四期课程回顾(点击查看)