-
目标检测中,点特征使用方便,但可能缺乏精确定位的明确边界信息。 论文地址: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算法与图像处理公众号
-
论文题目: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).
-
深度卷积神经网络(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实现语义分割](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
-
# 论文题目: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/)
-
来自华为诺亚方舟实验室、北京大学、悉尼大学的研究者提出了一种受量子力学启发的视觉 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通过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
-
这个参数没什么用啊,我以为和pytorch一样,发现没什么用
-
YOLOv7非正式版开源,好的框架离不开发起人的参与和技术支持和众多开发者的努力“You Only Look Once”或“YOLO”是一个对象检测算法的名字,这是Redmon等人在2016年的一篇研究论文中命名的。YOLO实现了自动驾驶汽车等前沿技术中使用的实时对象检测。让我们看看是什么使该算法如此受欢迎,并概述其工作原理。当人们看到图像以后,可以立即识别其中的对象、它们的位置和相对位置。这使得我们能够在几乎无意识的情况下完成复杂的任务,比如开车。因此,对汽车进行自动驾驶训练需要类似水平的反应能力和准确性。在其最基本的形式中,这样的系统必须能够分析实时视频中的道路,并能够在继续确定路径之前检测各种类型的对象及其在现实世界中的位置,所有这些都必须是实时的。YOLO将对象检测重新定义为一个回归问题。它将单个卷积神经网络(CNN)应用于整个图像,将图像分成网格,并预测每个网格的类概率和边界框。例如,以一个100x100的图像为例。我们把它分成网格,比如7x7。每个边界框可以使用四个描述符进行描述:界框的中心,高度,宽度,值映射到对象所属的类Github:https://github.com/jinfagang/yolov7
-
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实现。未完待续。。。
-
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训练营第四期课程回顾(点击查看)
-
CNN原理CNN,又称卷积神经网络,是深度学习中重要的一个分支。CNN在很多领域都表现优异,精度和速度比传统计算学习算法高很多。特别是在计算机视觉领域,CNN是解决图像分类、图像检索、物体检测和语义分割的主流模型。1. 卷积如图1所示,图中的X和O无论怎么旋转或者缩放,人眼其实还是很容易识别出X和O。图1但是计算机不同,它看到的其实是一个个的像素阵列,如图2。如何对像素的阵列进行特征的提取其实就是卷积神经网络要干的事情。图2再看图3,我们发现X即使进行了旋转,但是绿、橙、紫框标记的区域在两张图中还是一致的,某种程度上,这其实就是X的特征。图3因此可以将这三个特征的区间提取出来,就形成了三个卷积核,如图4所示。图4既然有了卷积核,那么卷积核是如何进行卷积操作的呢?其实很简单,可以看一下图5,卷积核其实就是拿着这个矩阵在图片的矩阵上一点点的平移,就像扫地一样。每扫到一处地方就可以进行卷积的运算,计算方法很简单,如图5所示,左上角的卷积核扫到绿色框的位置,则卷积核矩阵的数字就和扫到的位置的矩阵的数字一一对应相乘然后相加,最后取一个均值,该值就是卷积核提取的特征。图5卷积核提取的所有的特征组成了一个长和宽变小的矩阵,这个矩阵又称为feature map,如图6。使用不同的卷积核也就能提取出不同的feature map。所以可以想象的是,如果不断的进行卷积操作,那么图片的矩阵会逐步地长宽减少,厚度增加。图6可以看到卷积操作通过卷积核是可以分别提取到图片的特征的,但是如何提前知道卷积核呢?像上文的例子,很容易可以找到3个卷积核,但是假如是人脸识别这样成千上万个特征的图片,就没办法提前知道什么是合适的卷积核。其实也没必要知道,因为选择什么样的卷积核,完全可以通过训练不断优化。初始时只需要随机设置一些卷积核,通过训练,模型其实自己可以学习到合适的卷积核,这也是卷积神经网络模型强大的地方。
-
KNN算法之训练样本不平衡情况若正负样本处于不平衡状态,运用投票决策的KNN算法判断输入样本的所属类别:结果显示输入样本为绿色类 。原因是红色类的个数远远小于绿色样本,导致出现的分类错误 。(1)若分类决策选择限定半径最近邻法,即以输入样本为圆心,最大半径R的圆内选择出现次数最多的类做为输入样本的类 。如下图,黑色样本的分类结果正确。(2)投票法是默认每个样本的权重相等,我们假定权重与距离成反比,即距离越大,对结果的影响越小,那么该样本的权重也越小,反之,权重则越大,根据权重对输入样本进行分类 。这种思想与adaBoost算法相似,分类性能好的弱分类器给予一个大的权重 。分类过程:(1)、选择与输入样本距离X0最近的K个训练样本Xi(i = 1,2,...,K),d(X0,Xi)表示输入样本和训练样本的距离。 (2)、根据距离与样本成反比的性质将距离转化成权重Wi,Wi表示输入样本X0与训练样本Xi的权重。(3)、我们累加每一类的样本权重,并认为该权重占所有权重和的比例是该类的生成概率,概率最大的类就是输入样本的分类结果。假设目标是二分类{C1,C2},表达式:若,则分类结果为C1类,反之C2类。回归过程:(1)(2)步骤与分类过程一直,第(3)步使用如下表达式得到回归值:其中,y为输出结果,f(xi)为最近邻样本的值。若权重相同的话,则输出结果为K个训练样本的平均值。用权重思想重新对上例进行分类,可得输入样本为红色类。
-
KNN算法之暴力实现方法暴力搜索(brute-force search)是线性扫描输入实例与每一个训练实例的距离并选择前k个最近邻的样本来多数表决,算法简单,但是当训练集或特征维度很大时,计算非常耗时,故这种暴力实现原理是不可行的 。4. KNN算法之kd树实现方法kd树是一种对k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构,构造kd树相当于不断用垂直于坐标轴的超平面将k维空间进行划分,构成一系列的K维超矩形区域,kd树省去了对大部分数据的搜索,大大的较少了计算量。kd树的KNN算法实现包括三部分:kd树的构建,kd树的搜索和kd树的分类。1. 构建kd树kd树实质是二叉树,其划分思想与cart树一致,即切分使样本复杂度降低最多的特征。kd树认为特征方差越大,则该特征的复杂度亦越大,优先对该特征进行切分 ,切分点是所有实例在该特征的中位数。重复该切分步骤,直到切分后无样本则终止切分,终止时的样本为叶节点。【例】给定一个二维空间的数据集:构造kd树的步骤:(1)、数据集在维度和的方差分别为6.9和5.3,因此首先从维度进行切分。(2)、 数据集在维度的中位数是7,以平面=7将空间分为左右两个矩形。(3)、分别对左右两个矩形的样本在维度的中位数进行切分。(4)、重复步骤(2)(3),直到无样本,该节点为叶子节点。如下图,绿色为叶子节点 ,红色为节点和根节点。2. KD树搜索(1)、搜索路径从根节点到叶节点,在KD树里面找到包含目标点的叶子节点。(2)、搜索路径从叶节点到根节点,找到距离目标点最近的样本实例点。过程不再复述,具体方法请参考李航博士《统计学习方法》。3. KD树预测每一次搜寻与输入样本最近的样本节点,然后忽略该节点,重复同样步骤K次,找到与输入样本最近邻的K个样本 ,投票法确定输出结果。
-
KNN算法三要素K值的选择、距离度量和分类决策规则是K近邻算法的三个基本要素。当三个要素确定后,对于任何一个新的输入实例,它所属的Y值也确定了,本节介绍了三要素的含义。1. 分类决**策规则**KNN算法一般是用多数表决方法,即由输入实例的K个邻近的多数类决定输入实例的类。这种思想也是经验风险最小化的结果。训练样本为(xi , yi)。当输入实例为 x,标记为c,是输入实例x的k近邻训练样本集。我们定义训练误差率是K近邻训练样本标记与输入标记不一致的比例,误差率表示为:因此,要使误差率最小化即经验风险最小,就要使(2.1)式右端的最大,即K近邻的标记值尽可能的与输入标记一致,所以多数表决规则等价于经验风险最小化。2. K值的选择:K取值较小时,模型复杂度高,训练误差会减小,泛化能力减弱;K取值较大时,模型复杂度低,训练误差会增大,泛化能力有一定的提高。KNN模型的复杂度可以通过对噪声的容忍度来理解,若模型对噪声很敏感,则模型的复杂度高;反之,模型的复杂度低。为了更好理解模型复杂度的含义,我们取一个极端,分析K=1和K="样本数"的模型复杂度。由上图可知,K=1时,模型输出的结果受噪声的影响很大。由上图可知,样本数等于7,当K=7时,不管输入数据的噪声有多大,输出结果都是绿色类,模型对噪声极不敏感,但是模型太过简单,包含的信息太少,也是不可取的。通过上面两种极端的K选取结果可知,K值选择应适中,K值一般小于20,建议采用交叉验证的方法选取合适的K值。3. 距离度量KNN算法用距离来度量两个样本间的相似度,常用的距离表示方法:(1)、欧式距离(2)、曼哈顿距离(3)、闵可夫斯基距离可以看出,欧式距离是闵可夫斯基距离在p=2时的特例,而曼哈顿距离是p=1时的特例 。文章来源于机器学习算法那些事 ,作者石头
上滑加载中
推荐直播
-
全面解析华为云EI-API服务:理论基础与实践应用指南
2024/11/29 周五 18:20-20:20
Alex 华为云学堂技术讲师
本期直播给大家带来的是理论与实践结合的华为云EI-API的服务介绍。从“主要功能,应用场景,实践案例,调用流程”四个维度来深入解析“语音交互API,文字识别API,自然语言处理API,图像识别API及图像搜索API”五大场景下API服务,同时结合实验,来加深开发者对API服务理解。
回顾中 -
企业员工、应届毕业生、在读研究生共探项目实践
2024/12/02 周一 19:00-21:00
姚圣伟 在职软件工程师 昇腾社区优秀开发者 华为云云享专家 HCDG天津地区发起人
大神带你一键了解和掌握LeakyReLU自定义算子在ONNX网络中应用和优化技巧,在线分享如何入门,以及在工作中如何结合实际项目进行学习
即将直播 -
昇腾云服务ModelArts深度解析:理论基础与实践应用指南
2024/12/03 周二 14:30-16:30
Alex 华为云学堂技术讲师
如何快速创建和部署模型,管理全周期AI工作流呢?本期直播聚焦华为昇腾云服务ModelArts一站式AI开发平台功能介绍,同时结合基于ModelArts 的实践性实验,帮助开发者从理论到实验更好地理解和使用ModelArts。
去报名
热门标签