• [其他] 浅学人工神经网络(Artificial Neural Network,ANN )
    人工神经网络(Artificial Neural Network,即ANN ),是20世纪80 年代以来人工智能领域兴起的研究热点。它从信息处理角度对人脑神经元网络进行抽象, 建立某种简单模型,按不同的连接方式组成不同的网络。在工程与学术界也常直接简称为神经网络或类神经网络。神经网络是一种运算模型,由大量的节点(或称神经元)之间相互联接构成。每个节点代表一种特定的输出函数,称为激励函数(activation function)。每两个节点间的连接都代表一个对于通过该连接信号的加权值,称之为权重,这相当于人工神经网络的记忆。网络的输出则依网络的连接方式,权重值和激励函数的不同而不同。而网络自身通常都是对自然界某种算法或者函数的逼近,也可能是对一种逻辑策略的表达。人工神经网络是由大量处理单元互联组成的非线性、自适应信息处理系统。它是在现代神经科学研究成果的基础上提出的,试图通过模拟大脑神经网络处理、记忆信息的方式进行信息处理。人工神经网络具有四个基本特征:(1)非线性 非线性关系是自然界的普遍特性。大脑的智慧就是一种非线性现象。人工神经元处于激活或抑制二种不同的状态,这种行为在数学上表现为一种非线性关系。具有阈值的神经元构成的网络具有更好的性能,可以提高容错性和存储容量。(2)非局限性 一个神经网络通常由多个神经元广泛连接而成。一个系统的整体行为不仅取决于单个神经元的特征,而且可能主要由单元之间的相互作用、相互连接所决定。通过单元之间的大量连接模拟大脑的非局限性。联想记忆是非局限性的典型例子。(3)非常定性 人工神经网络具有自适应、自组织、自学习能力。神经网络不但处理的信息可以有各种变化,而且在处理信息的同时,非线性动力系统本身也在不断变化。经常采用迭代过程描写动力系统的演化过程。(4)非凸性 一个系统的演化方向,在一定条件下将取决于某个特定的状态函数。例如能量函数,它的极值相应于系统比较稳定的状态。非凸性是指这种函数有多个极值,故系统具有多个较稳定的平衡态,这将导致系统演化的多样性。人工神经网络中,神经元处理单元可表示不同的对象,例如特征、字母、概念,或者一些有意义的抽象模式。网络中处理单元的类型分为三类:输入单元、输出单元和隐单元。输入单元接受外部世界的信号与数据;输出单元实现系统处理结果的输出;隐单元是处在输入和输出单元之间,不能由系统外部观察的单元。神经元间的连接权值反映了单元间的连接强度,信息的表示和处理体现在网络处理单元的连接关系中。人工神经网络是一种非程序化、适应性、大脑风格的信息处理 ,其本质是通过网络的变换和动力学行为得到一种并行分布式的信息处理功能,并在不同程度和层次上模仿人脑神经系统的信息处理功能。它是涉及神经科学、思维科学、人工智能、计算机科学等多个领域的交叉学科。人工神经网络是并行分布式系统,采用了与传统人工智能和信息处理技术完全不同的机理,克服了传统的基于逻辑符号的人工智能在处理直觉、非结构化信息方面的缺陷,具有自适应、自组织和实时学习的特点。人工神经网络模型主要考虑网络连接的拓扑结构、神经元的特征、学习规则等。目前,已有近40种神经网络模型,其中有反传网络、感知器、自组织映射、Hopfield网络、波耳兹曼机、适应谐振理论等。根据连接的拓扑结构,神经网络模型可以分为:前向网络网络中各个神经元接受前一级的输入,并输出到下一级,网络中没有反馈,可以用一个有向无环路图表示。这种网络实现信号从输入空间到输出空间的变换,它的信息处理能力来自于简单非线性函数的多次复合。网络结构简单,易于实现。反传网络是一种典型的前向网络。 反馈网络网络内神经元间有反馈,可以用一个无向的完备图表示。这种神经网络的信息处理是状态的变换,可以用动力学系统理论处理。系统的稳定性与联想记忆功能有密切关系。Hopfield网络、波耳兹曼机均属于这种类型。
  • [前沿分享] 【课程作业经验】【NeRF】基于Mindspore的NeRF实现
    ## 一、NeRF介绍 ### 1. 背景 传统计算机图形学技术经过几十年发展,主要技术路线已经相对稳定。随着深度学习技术的发展,新兴的神经渲染技术给计算机图形学带来了新的机遇,受到了学界和工业界的广泛关注。**神经渲染**是深度网络合成图像的各类方法的总称,各类神经渲染的目标是实现图形渲染中建模和渲染的全部或部分的功能,基于**神经辐射场**(Neural Radiance Field, **NeRF**)的场景三维重建是近期神经渲染的热点方向,目标是使用神经网络实现新视角下的2D图像生成,在20和21年的CVPR、NeuIPS等各大AI顶会上,我们可以看到几十、上百篇相关的高水平论文。 ### 2. 网络结构 NeRF使用多层感知机(Multilayer Perceptron,**MLP**)来重建三维场景,也就是去拟合空间点的颜色分布和光密度分布的函数。NeRF的网络结构如图1所示,网络的输入是采样点对应的空间坐标和视角,输出是采样点对应的密度和RGB值。 由于颜色$\boldsymbol{c}$和光密度$\sigma$在空间中并不是平滑的,变化是比较剧烈的,这意味着函数存在很多高频的部分,让模型去表示这种函数比较困难,所以NeRF通过**positional encoding**,对输入的$\boldsymbol{r},\boldsymbol{d}$进行编码、升维,从而能够让模型更好地学出场景的一些细节部分,具体映射方式如下所示,该映射将标量$p$映射成一个$2L+1$维的向量: $$ \boldsymbol{\gamma}(p)=\left[p,\sin(2^0\pi p),\cos(2^0\pi p),\cdots,\sin(2^{L-1}\pi p), \cos(2^{L-1}\pi p)\right] $$ NeRF采用的MLP完整架构如下图所示: ![MLP](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/28/1656399813915919437.png) ### 3. 光线步进法 NeRF使用MLP隐式重建三维场景时的输入是采样点的位姿,NeRF的目标是实现2D新视角图像生成,那么要怎么得到采样点的位姿,又怎么使用重建得到的3D密度和颜色呢,得到新视角下的2D图像? 为了处理这两个问题,NeRF使用了**光线步进**(Ray Marching)这一经典方法,设$\boldsymbol{o}$代表相机原点$O_c$在世界坐标系中的位置,$\boldsymbol{d}$代表射线的单位方向矢量,$t$代表从$O_c$出发,沿射线方向行进的距离。使用光线步进法时,2D图像的每个像素对应于一条射线,每条射线上的任意位置可以表示为$\boldsymbol{r}(t)=\boldsymbol{o}+t\boldsymbol{d}$,对这些射线进行采样(具体使用下文介绍的**随机采样**和**重要性采样**)即可得到采样点的位姿,对这些射线上的采样点进行积分(具体使用下文介绍的**体绘制方法**)即可得到2D像素的RGB值。 ![rays](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/28/1656399831300545626.png) ### 4. 随机采样和重要性采样 NeRF使用随机采样和重要性采样结合的方式在光线步进法生成的射线上进行采样,这是因为空间中的物体分布是稀疏的,一条射线上可能只有很小的一段区域是对最终渲染起作用的,如果用均匀采样会浪费很多采样点,网络也难以学到整个连续空间中的分布,所以采用**coarse to fine**的思想,构建粗采样网络和细采样网络可以更好的对空间进行采样。 **随机采样**:将射线从近场到远场的范围$[t_n,t_f]$均匀划分成$N_c$个区间,在每段区间内随机取一个点,将其空间坐标$\boldsymbol{r}$和空间视角$\boldsymbol{d}$输入**粗采样网络**,得到粗采样网络预测的该空间点的$RGB\sigma$。 **重要性采样**:粗采样网络的输出中包括一条射线上所有点的**权重**$w_i$(具体将在下一节解释),将其归一化后作为采样区间的**概率密度函数**PDF,按照概率密度函数随机采样$N_f$个点,与前面分段均匀采样的$N_c$个点合并后输入**细采样网络**。 NeRF将粗采样网络和细采样网络的渲染结果(2D像素点的RGB值),分别与ground truth计算均方误差,将两者之和作为总的loss,来同时训练两个网络。 ![sampling](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/28/1656399851221930914.png) ### 5. 体绘制原理 得到采样点的密度和RGB值之后,NeRF使用计算机图形学中经典的体绘制技术(Voulume rendering)将3D采样值渲染至2D平面,具体原理如下。 光在介质中的衰减满足以下微分方程: $$ dL=-L\cdot \sigma \cdot dt $$ 其中$L$为光强,$\sigma$为衰减系数,其解为: $$ L=L_0\cdot \exp(-\int{\sigma}dt) $$ 假设:1) 空间点的颜色$\boldsymbol{c}=[R,G,B]^T$和视线方向$\boldsymbol{d}$有关;2) 空间点的光密度$\sigma$和视线方向$\boldsymbol{d}$无关。因为观察到的物体颜色会受到观察视角的影响(比如金属反射面),而光密度是由物体材质所决定。这一点假设也体现在了NeRF的网络结构当中。 根据光在介质中的衰减方程和体绘制原理,对于一个像素,其渲染颜色为 $$ \boldsymbol{C}=\int_{t_{n}}^{t_{f}} T(t) \sigma(\boldsymbol{r}(t)) c(\boldsymbol{r}(t), \boldsymbol{d}) dt $$ 其中,$T(t)=\exp(-\int_{t_n}^{t}{\sigma(\boldsymbol{r}(s))}ds)$代表$[t_n,t]$内的**累积透光率**,$\sigma(\boldsymbol{r}(t))dt$代表距离微元$dt$内的光强衰减率,等效为$\boldsymbol{r}(t)$处的**反射率**。 再对积分式进行离散化处理,使之适于计算机处理:沿着射线取$N$个点,在每个点$i$所代表的区间长度$\delta_i$内,$\sigma, \mathbf{c}$视作常数,得到渲染得到的2D像素RGB值表达式为 $$ \hat{C}(\mathbf{r})=\sum_{i=1}^{N}{T_i(1-\exp(-\sigma_i \delta_i))\mathbf{c_i}} $$ 其中,**累计透光率**为$T_i=\exp(-\sum_{j=1}^{i-1}\sigma_j \delta_j)$ 用体绘制处理得到的2D像素RGB值,可用于计算loss,也可用于生成最终的2D新视角图像。同时,也可以得到如下两个量: - $\alpha_i=1-\exp(-\sigma_i \delta_i)$,为第$i$个区间的**不透明度**。因此,$T_i=\prod_{j=1}^{i-1}{(1-\alpha_i)}$,为前$i-1$个区间的累积透明度; - $w_i=T_i\cdot \alpha_i$,为第$i$个点的颜色对渲染颜色的贡献度,也就是**权重**,可用于计算粗采样网络得到的PDF。 ### 6. NeRF的总体流程 综上所述,NeRF实现场景新视角合成的工作流程如下图所示。 1. 输入多视角图片(包括像素坐标、像素颜色),以及相机内参、位姿等数据; 2. 根据光线步进法产生射线,用随机采样和重要性采样得到空间采样点的坐标; 3. 将空间采样点的坐标以及射线的视角输入含有位置编码的NeRF,得到网络对于空间点$RGB\sigma$值的预测。 4. 根据空间点的$RGB\sigma$值,用体绘制原理,渲染出射线对应的二维像素点的$RGB$。 5. 将预测、渲染得到的像素点的$RGB$,与ground truth做MSE loss,训练神经网络。 ![workflow](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/28/1656399878480381442.png) ## 二、代码流程介绍 项目地址:等算子完善后发布 程序实现环境:mindspore1.6.1+CUDA11.1。 整体实验流程: ![steps](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/28/1656404684981651912.png) ### 1. 数据准备 NeRF的训练,除了对同一场景拍摄的多视角照片外,还需要相机的内参和每张照片的位姿。后者是无法通过直接测量得到的,因此需要使用一定的算法来获取。 COLMAP是一款通用的运动恢复结构 (SfM) 和多视图立体 (MVS) 软件,可用于点云重建。我们用COLMAP获取相机的内参和每张照片拍摄时的位姿,并根据3D特征点坐标,估计相机成像的深度范围,从而确定远、近平面,也就是边界。 操作步骤如下: 1. 安装COLMAP,官方地址:https://demuc.de/colmap/ 2. 打开软件。点击File---New project,导入照片文件夹images,并在其所在目录下创建一个`database`文件。 ![import](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/28/1656404657829157505.png) 3. 点击`Processing---Feature extraction`,提取照片中的特征点。 4. 点击`Processing---Feature matching`,完成特征点匹配。 5. 点击`Reconstruction---Start reconstruction`,完成稀疏重建。 6. 点击`Reconstruction---Dense reconstruction`,点击`Undistortion`,输出稀疏重建的结果,包括**相机内参、照片位姿、三维特征点信息**的二进制文件。 ![bins](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/28/1656404708727511052.png) COLMAP生成的数据为二进制文件,需要将数据重新保存成`npy`格式的文件`poses_bounds.npy`,便于后续数据的加载。操作步骤如下: 1. 运行`img2pose.py`文件,运行前需要配置形参为`dense`文件夹所在的目录。 ![gen_pose](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/28/1656404724944682770.png) 2. 运行后,在相应文件夹下生成`pose_bound.npy`文件,数据预处理任务完成。`pose_bound.npy`文件内容:包含相机的内参、每幅图像的位姿(从相机坐标系到世界坐标系的变换矩阵),以及根据照片中的三维特征点信息计算出的每幅照片的成像深度范围。 此外,原始的拍摄照片像素数太多,无法进行计算,需要事先对图片进行下采样,减少计算量,此处采用8倍降采样。 ### 2. 模型搭建 #### 2.1 主干网络模块 首先是主干模型的搭建。**模型输入**:空间三维坐标在位置编码后的高维向量;**模型输出**:相应三维坐标点处的 的预测值。模型的具体架构参考原理部分。 这里需要**注意模型权重的初始化**,它对训练时收敛的快慢影响很大。这里,采用权重和偏置都在$(-\sqrt{k},\sqrt{k})$内均匀分布,其中$k=1/\text{in-channels}$,实测这种初始化方式对于NeRF的收敛是有帮助的。 ```python class LinearReLU(nn.Cell): def __init__(self, in_channels, out_channels): super().__init__() self.linear_relu = nn.SequentialCell([ nn.Dense(in_channels, out_channels, weight_init=Uniform(-math.sqrt(1. / in_channels)), bias_init=Uniform(-math.sqrt(1. / in_channels))), nn.ReLU() ]) def construct(self, x): return self.linear_relu(x) class NeRF(nn.Cell): def __init__(self, D=8, W=256, input_ch=3, input_ch_views=3, skips=[4], use_viewdirs=False): super(NeRF, self).__init__() self.D = D self.W = W self.input_ch = input_ch self.input_ch_views = input_ch_views self.skips = skips self.use_viewdirs = use_viewdirs self.pts_layers = nn.SequentialCell( [LinearReLU(input_ch, W)] + [LinearReLU(W, W) if i not in self.skips else LinearReLU(W + input_ch, W) for i in range(D - 1)] ) self.feature_layer = LinearReLU(W, W) if use_viewdirs: self.views_layer = LinearReLU(input_ch_views + W, W // 2) else: self.output_layer = LinearReLU(W, W // 2) self.sigma_layer = nn.SequentialCell([ nn.Dense(W, 1, weight_init=Uniform(-math.sqrt(1. / W)), bias_init=Uniform(-math.sqrt(1. / W))) if use_viewdirs else nn.Dense(W // 2, 1, weight_init=Uniform(-math.sqrt(1. / (W // 2))), bias_init=Uniform(-math.sqrt(1. / (W // 2)))), ]) self.rgb_layer = nn.SequentialCell( nn.Dense(W // 2, 3, weight_init=Uniform(-math.sqrt(1. / (W // 2))), bias_init=Uniform(-math.sqrt(1. / (W // 2)))), nn.Sigmoid() ) def construct(self, x): pts, views = mnp.split(x, [self.input_ch], axis=-1) h = pts for i, l in enumerate(self.pts_layers): h = self.pts_layers<i>(h) if i in self.skips: h = mnp.concatenate([pts, h], -1) if self.use_viewdirs: sigma = self.sigma_layer(h) feature = self.feature_layer(h) h = mnp.concatenate([feature, views], -1) h = self.views_layer(h) rgb = self.rgb_layer(h) else: h = self.feature_layer(h) h = self.output_layer(h) sigma = self.sigma_layer(h) rgb = self.rgb_layer(h) outputs = mnp.concatenate([rgb, sigma], -1) return outputs ``` #### 2.2 位置编码模块 位置编码需要将输入的三维坐标映射到高维,因此需要构建一个由$\sin,\cos$函数构成的列表。对于每个输入,进行编码函数的遍历,将输出结果拼接成高维。 ```python class Embedder: def __init__(self, **kwargs): self.kwargs = kwargs self.create_embedding_fn() def create_embedding_fn(self): embed_fns = [] d = self.kwargs['input_dims'] out_dim = 0 if self.kwargs['include_input']: embed_fns.append(lambda x: x) out_dim += d max_freq = self.kwargs['max_freq_log2'] N_freqs = self.kwargs['num_freqs'] if self.kwargs['log_sampling']: pow = ops.Pow() freq_bands = pow(2., mnp.linspace(0., max_freq, N_freqs)) else: freq_bands = mnp.linspace(2. ** 0., 2. ** max_freq, N_freqs) for freq in freq_bands: for p_fn in self.kwargs['periodic_fns']: embed_fns.append(lambda x, p_fn=p_fn, freq=freq: p_fn(x * freq)) out_dim += d self.embed_fns = embed_fns self.out_dim = out_dim def embed(self, inputs): return mnp.concatenate([fn(inputs) for fn in self.embed_fns], -1) def get_embedder(L): embed_kwargs = { 'include_input': True, 'input_dims': 3, 'max_freq_log2': L - 1, 'num_freqs': L, 'log_sampling': True, 'periodic_fns': [mnp.sin, mnp.cos], } embedder_obj = Embedder(**embed_kwargs) embed = lambda x, eo=embedder_obj: eo.embed(x) return embed, embedder_obj.out_dim ``` #### 2.3 损失网络 NeRF涉及到两个网络loss的相加后作为总的loss,来优化两个网络。因此,需要用`NeRFWithLossCell`类,将前向网络与损失函数连接起来。其中,前向计算过程比较复杂,涉及**空间采样、位置编码、神经网络预测、体渲染**等步骤,而这些步骤在测试阶段同样需要用到,因此将其封装成一个函数`sample_and_render()`,只需输入射线信息`rays`、网络信息`net_kwargs`、训练/测试的参数设置`train_kwargs/test_kwargs`,就能输出射线所对应像素点的RGB的预测值。 类的属性包括优化器、网络信息,还有打印信息所需`psnr`。`psnr`也是一个损失函数,但它不是优化的目标,因此不能作为`construct`的输出,只能用属性的方式进行记录。 ```python class NeRFWithLossCell(nn.Cell): def __init__(self, optimizer, net_coarse, net_fine, embed_fn_pts, embed_fn_views): super(NeRFWithLossCell, self).__init__() self.optimizer = optimizer self.net_coarse = net_coarse self.net_fine = net_fine self.embed_fn_pts = embed_fn_pts self.embed_fn_views = embed_fn_views self.net_kwargs = { 'net_coarse': self.net_coarse, 'net_fine': self.net_fine, 'embed_fn_pts': self.embed_fn_pts, 'embed_fn_views': self.embed_fn_views } self.psnr = None def construct(self, H, W, K, rays_batch, rgb, chunk=1024 * 32, c2w=None, ndc=True, near=0., far=1., use_viewdirs=False, **kwargs): # 数据准备,获取射线的位置、方向、远近场、视角 rays = get_rays_info(H, W, K, rays_batch, c2w, ndc, near, far, use_viewdirs=True) # 采样+渲染 rets_coarse, rets_fine = sample_and_render(rays, **self.net_kwargs, **kwargs) # 计算loss loss_coarse = img2mse(rets_coarse['rgb_map'], rgb) loss_fine = img2mse(rets_fine['rgb_map'], rgb) loss = loss_coarse + loss_fine self.psnr = mse2psnr(loss_coarse) return loss ``` #### 2.4 训练网络 这个类是封装损失网络和优化器,用优化器单步更新网络参数。 ```python class NeRFTrainOneStepCell(nn.TrainOneStepCell): def __init__(self, network, optimizer): super(NeRFTrainOneStepCell, self).__init__(network, optimizer) self.grad = ops.GradOperation(get_by_list=True) self.optimizer = optimizer def construct(self, H, W, K, rays_batch, rgb, **kwargs): weights = self.weights loss = self.network(H, W, K, rays_batch, rgb, **kwargs) grads = self.grad(self.network, weights)(H, W, K, rays_batch, rgb, **kwargs) return F.depend(loss, self.optimizer(grads)) ``` ### 3. 采样 #### 3.1 粗采样 粗采样就是沿着射线进行分段均匀分布的随机采样。输入为射线和采样点数,以及其他参数配置,返回值为采样点的三维坐标`pts`、采样点在-z方向距离值`z_vals`,-z方向分段点`z_splits`。 ```python def sample_coarse(rays, N_samples, perturb=1., lindisp=False, pytest=False): N_rays = rays.shape[0] rays_o, rays_d = rays[:, 0:3], rays[:, 3:6] near, far = rays[..., 6:7], rays[..., 7:8] t_vals = mnp.linspace(0, 1, N_samples + 1) if not lindisp: z_splits = near * (1. - t_vals) + far * t_vals else: z_splits = 1. / (1. / near * (1. - t_vals) + 1. / far * t_vals) z_splits = mnp.broadcast_to(z_splits, (N_rays, N_samples + 1)) if perturb > 0.: upper = z_splits[..., 1:] lower = z_splits[..., :-1] t_rand = np.random.rand(*list(upper.shape)) if pytest: np.random.seed(0) t_rand = np.random.rand(*list(z_splits.shape)) t_rand = Tensor(t_rand, dtype=ms.float32) z_vals = lower + (upper - lower) * t_rand else: z_vals = .5 * (z_splits[..., 1:] + z_splits[..., :-1]) pts = rays_o[..., None, :] + rays_d[..., None, :] * z_vals[..., :, None] return pts, z_vals, z_splits ``` #### 3.2 细采样 细采样根据粗采样的得到的分段权重,将其作为概率密度函数进行采样,然后将采样结果和粗采样结果拼接,得到细采样输出。这里用`sample_pdf()`按照概率密度函数进行采样,其程序实现的主要步骤为: 1. 根据PDF,计算累积分布函数CDF,它将是一个分段线性函数。 2. 在[0, 1]内,对CDF值用均匀分布进行采样。 3. 将采样到的CDF值映射回坐标值。 其中,第3步需要用高维的`searchsorted`算子去寻找坐标值的索引,然而,目前MindSpore的`searchsorted`只支持1维输入,无法完成这一任务,暂时用pytorch的算子代替。 ```python def sample_pdf(bins, weights, N_samples, det=False, pytest=False): weights = weights + 1e-5 pdf = weights / mnp.sum(weights, -1, keepdims=True) cdf = mnp.cumsum(pdf, -1) cdf = mnp.concatenate([mnp.zeros_like(cdf[..., :1]), cdf], -1) if det: u = mnp.linspace(0., 1., N_samples) u = mnp.broadcast_to(u, tuple(cdf.shape[:-1]) + (N_samples,)) else: u = np.random.randn(*(list(cdf.shape[:-1]) + [N_samples])) u = Tensor(u, dtype=ms.float32) if pytest: np.random.seed(0) new_shape = list(cdf.shape[:-1]) + [N_samples] if det: u = np.linspace(0., 1., N_samples) u = np.broadcast_to(u, new_shape) else: u = np.random.rand(*new_shape) u = Tensor(u, dtype=ms.float32) cdf_tmp, u_tmp = torch.Tensor(cdf.asnumpy()), torch.Tensor(u.asnumpy()) inds = Tensor(torch.searchsorted(cdf_tmp, u_tmp, right=True).numpy()) below = ops.Cast()(mnp.stack([mnp.zeros_like(inds - 1), inds - 1], -1), ms.float32) below = ops.Cast()(below.max(axis=-1), ms.int32) above = ops.Cast()(mnp.stack([(cdf.shape[-1] - 1) * mnp.ones_like(inds), inds], -1), ms.float32) above = ops.Cast()(above.min(axis=-1), ms.int32) inds_g = mnp.stack([below, above], -1) matched_shape = (inds_g.shape[0], inds_g.shape[1], cdf.shape[-1]) cdf_g = ops.GatherD()(mnp.broadcast_to(cdf.expand_dims(1), matched_shape), 2, inds_g) bins_g = ops.GatherD()(mnp.broadcast_to(bins.expand_dims(1), matched_shape), 2, inds_g) denom = (cdf_g[..., 1] - cdf_g[..., 0]) denom = mnp.where(denom < 1e-5, mnp.ones_like(denom), denom) t = (u - cdf_g[..., 0]) / denom samples = bins_g[..., 0] + t * (bins_g[..., 1] - bins_g[..., 0]) return samples ``` ### 4. 射线获取 `get_rays()`完成的是根据照片尺寸$[H,W]$、相机内参矩阵$K$,相机位姿$c2w$,计算在世界坐标系下每个像素所对应的位置射线`rays_o`和方向射线`rays_d`。这里统一采用openGL坐标系,即$x$为向右、$y$为向上、$z$为向外。其中,`rays_d`统一按照-z方向,也就是拍摄照片时相机的朝向,进行归一化。因为在进行粗采样和细采样时,对于所有不同方向的射线,都是以-z方向上的距离为准进行采样的。 `get_rays_info()`是将射线的所有信息拼接成`rays`张量,便于后续调用,射线信息包括: 1. 位置向量rays_o; 2. 方向向量rays_d; 3. 近场near(体渲染时的积分下限); 4. 远场far(体渲染时的积分上限); 5. 单位化后的视角向量view_dir; ```python def get_rays(H, W, K, c2w): i, j = mnp.meshgrid(mnp.linspace(0, W - 1, W), mnp.linspace(0, H - 1, H), indexing='xy') dirs = mnp.stack([(i - K[0][2]) / K[0][0], -(j - K[1][2]) / K[1][1], -mnp.ones_like(i)], -1) c2w = Tensor(c2w) rays_d = mnp.sum(dirs[..., None, :] * c2w[:3, :3], -1) rays_o = ops.BroadcastTo(rays_d.shape)(c2w[:3, -1]) return rays_o, rays_d def get_rays_info(H, W, K, rays_batch=None, c2w=None, ndc=True, near=0, far=1, use_viewdirs=True): if c2w is not None: rays_o, rays_d = get_rays(H, W, K, c2w) else: rays_o, rays_d = rays_batch if use_viewdirs: viewdirs = rays_d viewdirs = viewdirs / mnp.norm(viewdirs, axis=-1, keepdims=True) viewdirs = mnp.reshape(viewdirs, [-1, 3]) if ndc: rays_o, rays_d = ndc_rays(H, W, K[0][0], 1., rays_o, rays_d) rays_o = mnp.reshape(rays_o, [-1, 3]) rays_d = mnp.reshape(rays_d, [-1, 3]) near, far = near * mnp.ones_like(rays_d[..., :1]), far * mnp.ones_like(rays_d[..., :1]) rays = mnp.concatenate([rays_o, rays_d, near, far], -1) if use_viewdirs: rays = mnp.concatenate([rays, viewdirs], -1) return rays ``` ### 5. 渲染 根据网络输出的空间采样点的$RGB\sigma$,用体渲染公式计算出射线对应二维像素点的RGB值。因此,本函数是NeRF前向计算的主体框架。其主要过程为: 1. 根据射线数据,进行粗采样。 2. 将粗采样得到的空间采样点坐标`pts_coarse`,和射线单位方向向量`views_coarse`拼接,输入粗采样网络。经过位置编码和神经网络前向计算,得到空间采样点$RGB\sigma$预测值`raw_coarse`。其中,`views_coarse`是`rays_d`的单位向量,消除了不同向量模长的影响。 3. 将`raw_coarse`输入体绘制函数`render()`,得到渲染后的返回值`ret_coarse`。它是一个字典,包括了渲染后的所有结果,其中包括粗采样点的权重`rets_coarse['weights']`。 4. 根据粗采样网络采样点的权重,以及采样分段区间,进行细采样。 5. 将细采样得到的空间采样点坐标`pts_fine`,和射线单位方向向量`views_fine`拼接,输入细采样网络。经过位置编码和神经网络前向计算,得到空间采样点$RGB\sigma$预测值`raw_fine`。 6. 将`raw_fine`输入体绘制函数`render()`,得到渲染后的返回值`ret_fine`,它同样包括了渲染后的所有结果。 7. 返回粗采样网络和细采样网络渲染后的返回值`rets_coarse`,`rets_fine`。 ```python def sample_and_render(rays, net_coarse=None, net_fine=None, embed_fn_pts=None, embed_fn_views=None, N_coarse=64, N_fine=64, perturb=1., lindisp=False, pytest=False, raw_noise_std=0., white_bkgd=False): # 数据准备 rays_d = rays[..., 3: 6] views = rays[:, -3:] if rays.shape[-1] > 8 else None # 粗采样 pts_coarse, z_coarse, z_splits = sample_coarse(rays, N_coarse, perturb, lindisp, pytest) views_coarse = mnp.broadcast_to(views[..., None, :], pts_coarse.shape) sh = pts_coarse.shape # 粗采样网络的positional encoding pts_embeded_coarse = embed_fn_pts(pts_coarse.reshape([-1, 3])) views_coarse_embeded = embed_fn_views(views_coarse.reshape([-1, 3])) # 输入粗采样网络 + 渲染 得到输出 net_coarse_input = mnp.concatenate([pts_embeded_coarse, views_coarse_embeded], -1) raw_coarse = net_coarse(net_coarse_input).reshape(list(sh[:-1]) + [4]) rets_coarse = render(raw_coarse, z_coarse, rays_d, raw_noise_std, white_bkgd, pytest) # 细采样 weights = rets_coarse['weights'] pts_fine, z_fine = sample_fine(rays, z_coarse, z_splits, weights, N_fine, perturb, pytest) views_fine = mnp.broadcast_to(views[..., None, :], pts_fine.shape) sh = pts_fine.shape # 细采样网络的positional encoding pts_embeded_fine = embed_fn_pts(pts_fine.reshape([-1, 3])) views_embeded_fine = embed_fn_views(views_fine.reshape([-1, 3])) # 输入细采样网络 + 渲染 得到输出 net_fine_input = mnp.concatenate([pts_embeded_fine, views_embeded_fine], -1) raw_fine = net_fine(net_fine_input).reshape(list(sh[:-1]) + [4]) rets_fine = render(raw_fine, z_fine, rays_d, raw_noise_std, white_bkgd, pytest) return rets_coarse, rets_fine ``` ### 6. 主函数 main函数的任务如下: 1. 加载图片和位姿、内参等数据,将其分成一个一个batch并打乱; 2. 用`create_nerf()`,创建一个NeRF模型,返回值包括:各网络实例构成的字典`net_kwargs`,训练参数`train_kwargs`,测试参数`test_kwargs`,优化器`optimizer`。 3. 构建损失网络`net_with_loss`,并加载之前保存的训练参数。 4. 构建训练网络`train_net`。 5. 迭代优化`train_net`。 6. 保存训练时参数,保存渲染视频。 训练时调用训练网络: ```python train_net(H, W, K, batch_rays, target_s, **train_kwargs) ``` ## 三、参考资料 [1] Mildenhall, B., Srinivasan, P.P., Tancik, M., Barron, J.T., Ramamoorthi, R., Ng, R. (2020). NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis. In: Vedaldi, A., Bischof, H., Brox, T., Frahm, JM. (eds) Computer Vision – ECCV 2020. ECCV 2020. [2] NeRF论文的官方实现: https://github.com/bmild/nerf [3] NeRF的Pytorch实现:https://github.com/yenchenlin/nerf-pytorch [4] MindSpore 1.6 API:https://www.mindspore.cn/docs/api/zh-CN/r1.6/index.html
  • [AI家园] 边缘人工智能的梦想与挑战
    人工智能正在进一步进入我们的日常生活,但重点主要集中在“大型机器”上,例如自动驾驶汽车。 也许现在也是“小机器”人工智能革命的时候了。在本文中,我们主要探讨两个主要问题,即在“小型机器”中实现人工智能的理由,以及开发人工智能小型机器将面临哪些挑战?未来,在人工智能方面,我们应该有飞行汽车和机器人管家。甚至还可能遇到有感知能力的机器人决定起来反抗我们。虽然我们还没有发展到这种程度,但显然人工智能(AI)技术已经进入了我们的世界。每次当我们让智能语音助手做一件事是,机器学习技术就会先搞明白你说了什么,并试图对你想让它做什么做出最好的决定。例如,每次视频网站或电商平台向你推荐“你可能喜欢的电影”或“你可能需要的商品”时,它都是基于复杂的机器学习算法,尽可能地向你提供具有说服力的建议,这显然比过去的促销活动更有吸引力。虽然我们可能不是所有人都有自动驾驶汽车,但我们敏锐地意识到这一领域的发展以及自动导航提供的潜力。人工智能技术承载着一个伟大的希望——机器可以根据周围的世界做出决定,像人类一样处理信息,甚至以一种优于人类的方式。但如果我们考虑一下上面的例子,就会发现只有“大型机器”才能实现人工智能的承诺,这些设备往往没有功率、尺寸或成本的限制。或者换句话说,它们会发热,有线路供电,体积很大,而且很昂贵。例如,Alexa 和 Netflix 这些全球领先的IT巨头企业依靠云中的大型耗电服务器(数据中心)来推断用户的意图。虽然自动驾驶汽车很可能依赖电池,但考虑到这些电池必须转动车轮和转向,它们的能量容量是巨大的。与最昂贵的人工智能决策相比,它们是巨大的能源支出。因此,尽管人工智能前景广阔,但“小机器”却被抛在了后面 。由较小电池供电或具有成本和尺寸限制的设备无法参与机器可以看到和听到的想法。今天,这些小机器只能利用简单的人工智能技术,也许是听一个关键词,或者从心率分析低维信号,如光电体积描记术 (PPG)。如果小机器能看能听会怎样?但是,小型机器能够看到和听到是否有价值?可能很多人很难想象像门铃摄像头这样利用自动驾驶或自然语言处理等技术的小设备。尽管如此,诸如词汇识别、语音识别和图像分析之类的不太复杂、处理密集程度较低的 AI 计算仍然存在机会:门铃摄像头和消费级安全摄像头通常会触发一些无趣的事件,例如风引起的植物运动、云引起的剧烈光线变化,甚至是狗或猫在镜头前面动等事件。这可能导致错误警报触发,导致房主开始忽略掉一些重要事件。因为,房主可能在世界不同的地方旅行,也可能正在睡觉,而他们的安全摄像机却对日出、云和日落引起的照明变化频繁发出警报。而更智能的摄像机则可以更加精准是识别物体变化,如人体的轮廓,进而避免误报干扰。门锁或其他接入点可以使用面部识别甚至语音识别来验证人员访问权限,在很多情况下无需钥匙或IC卡。许多摄像头希望在某些事件上触发:例如,跟踪摄像头可能希望在画面中出现某一种动物时触发,安全摄像头可能希望在画面中出现人或开门或脚步声等噪音时触发,并且有些摄像机可能想要通过语音命令触发等等。大词汇量命令在许多应用中都很有用。虽然有很多类似 “Hey Alexa”、“Hey Siri” 解决方案,但如果开始考虑 20 个或更多单词的词汇,则可以在工业设备、家庭自动化、烹饪用具和许多其他设备中找到用于简化人机交互的用途。这些例子只是表面上的。让小型机器看到、听到和解决以前需要人工干预的问题的想法是一个强大的想法,我们每天都在继续寻找创造性的新用例。让小型机器能看和听的挑战是什么?那么,如果人工智能对小型机器如此有价值,为什么我们还没有广泛应用呢?答案是计算能力。人工智能推理是神经网络模型计算的结果。把神经网络模型想象成你的大脑如何处理图片或声音的一个粗略的近似,把它分解成非常小的片段,然后当这些小片段组合在一起时识别出模式。现代视觉问题的主力模型是卷积神经网络 (CNN)。这些模型在图像分析方面非常出色,在音频分析中也非常有用。挑战在于此类模型需要数百万或数十亿次数学计算。传统上,这些应用很难选择实施:使用廉价且低功耗的微控制器解决方案。虽然平均功耗可能很低,但 CNN 可能需要几秒钟的时间来计算,这意味着 AI 推理不是实时的,因此会消耗大量电池电量。购买可以在所需延迟内完成这些数学运算的昂贵且高性能的处理器。这些处理器通常很大,需要大量外部组件,包括散热器或类似的冷却组件。但是,它们执行 AI 推理的速度非常快。无法实施。低功耗微控制器解决方案将太慢而无法使用,而高性能处理器方法将打破成本、尺寸和功率预算。我们需要的是一种嵌入式的人工智能解决方案,从头开始构建,以最大限度地减少CNN计算的能源消耗。与传统的微控制器或处理器解决方案相比,AI推断需要在一个数量级上执行,并且不需要内存等外部组件的帮助,这些外部组件会消耗能量、体积和成本。如果人工智能推理解决方案可以消除机器视觉的能量损失,那么即使是最小的设备也可以看到并识别周围世界发生的事情。幸运的是,我们正处于这场“小机器”革命的开端。现在的产品几乎可以消除人工智能推断的能源成本,并实现电池驱动的机器视觉。例如,一个微控制器可用于执行 AI 推理,同时仅消耗微焦耳的能量。责任编辑:庞桂玉    来源: 千家网
  • [其他] 华为诺亚实验室的研究员发现图神经网络(GNN)
    华为诺亚实验室的研究员发现图神经网络(GNN)也能做视觉骨干网络。将图像表示为图结构,通过简洁高效的适配,提出一种新型视觉网络架构 ViG,表现优于传统的卷积网络和 Transformer。在 ImageNet 图像识别任务,ViG 在相似计算量情况下 Top-1 正确率达 82.1%,高于 ResNet 和 Swin Transformer。论文链接:https://arxiv.org/abs/2206.00272PyTorch 代码:https://github.com/huawei-noah/CV-BackbonesMindSpore 代码:https://gitee.com/mindspore/models/tree/master/research/cv/ViG在计算机视觉领域,骨干网络一直是特征提取的重要部件。从 AlexNet 到 ResNet,卷积网络 CNN 在很长一段时间内一直是视觉任务的标配。近年来,基于注意力机制的 Transformer 和以全连接层为主的 MLP 网络也开始在计算机视觉领域崭露头角。与现有主流 CNN 模型相比,基于 Transformer 或 MLP 的模型在视觉任务上也显示出了良好的性能。直到现在,关于谁是更好的视觉骨干网络还是一个仍在探索和颇具争议的课题。 传统的卷积网络将图像视作一个矩阵或网格,通过滑动窗口对邻域像素点或特征点进行聚合;视觉 Transformer 或 MLP 则是输入图片切分为若干个图像块,形成一个序列,用注意力机制或全连接层处理序列关系,如图 1 所示。网格或序列表示的方法,对于图像来说显得不够灵活。比如,一个人往往由头部、四肢和躯干构成,这些部位之间有一定连接关系,是一种非规则化的会变化的模式。 图 1:图像的 3 种表示方法。 为了更好地对图像进行表示,本文提出用图结构(Graph)来对图像进行解析。将图像切分成若干图像块,每个图像块视作一个节点来构建图结构。进而提出用图神经网络进行图像识别等任务,首次构建了视觉 GNN,简称 ViG,如下图 2 所示。 图 2:视觉图神经网络 ViG 架构。 直接使用原始 GNN 在图像任务会有过平滑的问题,也就是随着网络的加深,节点特征之间会越来越相似。为了缓解这个问题,ViG 引入前馈神经网络 FFN 模块来增强特征变换能力和特征多样性。通过基础的图卷积模块和 FFN 模块,作者构建了 isotropic 式和金字塔式的 ViG 网络架构。在 ImageNet 基准测试和下游任务上的实验表明了该方法在视觉任务方面的优越性。例如, Pyramid ViG-S 仅用 4.5G FLOPs 就达到了 82.1% 的 ImageNet top-1 正确率,这比计算量相近的 Swin Transformer 和 ResNet 都要高出不少。 转发自:https://www.jiqizhixin.com/articles/2022-06-26
  • [其他] 神经网络中重要的算法:反向传播BP(Back Propagation)
    在很久以前,人工神经网络面临一个重大的挑战:增加神经网络的层数虽然可为其提供更大的灵活性,让网络能有更强的表征能力,也就是说,能解决的问题更多,但随之而来的庞大网络参数训练,一直是制约多层神经网络发展的一个重要瓶颈。 说到BP算法,我们通常强调的是反向传播。其实在本质上,它是一个双向算法: (1)正向传播输入信息,实现分类功能(所有的有监督学习,在本质都可以归属于分类); (2)反向传播误差,调整网络权值。 神经网络包含了前馈网络和反向传播两个主要过程。前者是多个函数的复合,如果你画出前馈神经网络的图结构,你会发现这个图结构形象地描述了函数的复合方式,最经典的是“链式结构”。而反向传播是目前用来训练神经网络的最常用并且也是最有效的方法。梯度下降因为基于反向传播的思想方法,而得以在神经网络训练过程中大施拳脚。 简化的说,反向传播是从错误出发层层究责的过程,究什么责?究那个部分导致最终训练结果误差的权重的责。究责是为了改正过失,这就是神经网络学习的本质——找到网络中神经元之间的最佳连接权值。换句话说,反向传播就是反向计算偏微分,将信息逆向从神经网络的高层向前层传播。 前向传播完毕后,我们得出了预测值,而这个输出结果与实际结果必然存在着误差。误差越大,说明模型越差,但是这种差可以改善。我们通过将误差转化规范成损失函数,通过训练模型将损失函数不断减小。怎么训练呢?你是否还记得梯度下降这个概念, 反向传播通过对链式函数构成的误差函数的梯度下降,在将这个误差从输出层向隐藏层传播的过程中,对每个权重进行更新,直至传播到输入层。 各个权重在不断迭代的反向传播过程中得以调整,直至收敛。 损失函数的最小化过程采取了对链式结构函数的梯度下降的求解,虽然计算深层的链式结构的梯度直接易懂,但是计算量很大,而反向传播有效地减小了梯度下降算法的计算量,我们也可以说反向传播是计算梯度的算法。
  • [其他] 联结主义人工智能的基本观点
    联结主义人工智能的基本观点是:人工智能必须能够模仿大脑的功能,更具体地强调机器应具有学习能力和认知能力,通过学习来解决问题。 联结主义人工能特指感知机、神经网络发展的机器学习,广泛意义上也可以包括强化学习以及其他机器学习方法。联结主义人工智能主要解决模式识别、聚类、分类、联想等非结构化同题。早在20世纪40年代,McCulloch和Walter开创了集成认知神经学、科学和数理逻辑计算的计算神经学,提出了最早的神经网络模型,给出了神经元的形式化数学描述和网络结构、证明了单个神经元能执行逻辑功能。 1958年,Frank将感知器建立在一个非线性神经元上,得到了神经元的Pitts模型。这是在计算机有限运算能力的条件下,基于硬件结构建立的第一个可进行模式识别的单层神经网络模型。但在此后的一段时间内,神经网络模型仍然无法测试,而且连比较基本的异或(XOR)的逻辑运算都不能实现,即便多层神经网络也不具有低复杂度,相关研究进人了长达20年的停滞状态。 1974年,哈佛大学的Paul首次提出误差反向传播算法,不仅解决了异或运算问题,而且大大提升了神经网络迭代计算效率。直到20世纪80年代,神经网络才再度迎来研究高潮。1982年,John提出了可用作联想存储器的互连网络模型,该模型是一种从输出到输人有反愦连接的循环神经网络。1986年,David等人在神经网络上应用了反向传播算法。1989年,Yann等人受日本学者福岛邦彦提出的仿生视觉皮层神经网络模型等的启发,构建了应用于计算机视觉问题的包含2个卷积层和2个全连接层的神经网络,正式提出了卷积神经网络,并于1998年构建更完备的卷积神经网络LeNet-5,成功用于解决手写识别问题。 2006年,Hinton等人发表文章,提出将预训练、自编码器与深度神经网络结合,开启了深度学习(深度神经网络)在学术界和工业界的研究与应用浪潮,相继在语音识别、图像识别等领域开展应用,显著降低了错误识别率。2011年后,深度学习开始将卷积层与最大池化层合并,并将其输出传递给几个全连接层,再传递给输出层。2013年4月,美国麻省理了学院(MIT)的《麻省理工科技评论》将深度学习列为2013年十大突破性技术之一。2014年,当时还在蒙特利尔大学读博上的Ian等人提出生成对抗网络溉念,使得神经网络系统不仅能够更好地生成图像与声音,还能识别它们,从而奠定了无监督自主学习算法框架的基础。2015年,谷歌DeepMind提出正式的Deep Q Networks系统。2016年,融合了深度学习、强化学习和蒙特卡罗树搜索方法的AlphaGo战胜了世界围棋高手李世石。这些研究让联结主义大放异彩,也是当前人工智能高潮形成的主因。 其他机器学习方向也在不断取得成果。早在1952年,IBM科学家Arthur开发了一个跳棋程序。该程序能够通过观察当前位置,并学习一个隐含的模型,为后续动作提供更好的指导。从1984年开始,Leo Breiman等人相继提出了适用于分类和连续输人(特征)/输出(预测)变量回归预测建模问题的决策树算法和多决策树组合的随机森林算法,后者属于集成学习范畴。1995年,由Corinna等人提出了支持向量机的溉念,通过Kernel的技巧将低维空间线性不可分或难分的样本数据点映射至高维空间使其线性可分,在解决小样本、非线性以及高维模式识别问题方面具有明显优势。
  • [其他] DepthShrinker:一种新的压缩范式,用于提高紧凑神经网络的实际硬件效率
    配备紧凑算子(如深度卷积)的高效深度神经网络(DNN)模型在降低DNN的理论复杂性(如权重/操作的总数)同时保持良好的模型精度方面显示出了巨大的潜力。然而,现有的高效DNN在提高实际硬件效率方面仍然有限,原因是其通常采用的紧凑算子的硬件利用率较低。在这项工作中,我们为开发真正硬件高效的DNN开辟了一种新的压缩范式,从而在保持模型准确性的同时提高硬件效率。有趣的是,我们观察到,虽然一些DNN层的激活函数有助于DNN的训练优化和达到精度,但训练后可以适当地去除它们,而不影响模型的精度。受此启发,提出了一种深度收缩框架DepthShrinker,通过将现有高效且具有不规则计算模式的深度神经网络的基本模块收缩为密集的基本模块,开发硬件友好的紧凑网络,大大提高了硬件利用率,从而提高了硬件效率。令人兴奋的是,我们的DepthShrinker框架提供了硬件友好的紧凑网络,性能优于最先进的高效DNN和压缩技术,例如,在特斯拉V100上比SOTA通道修剪方法MetaPruning更高3.06%的精度和1.53×吞吐量。我们的代码可以在https://github.com/RICEEIC/DepthShrinker上找到。
  • [其他] 图神经网络异常检测的再思考
    图神经网络(GNNs)在图异常检测中有着广泛的应用。由于谱滤波器的选择是GNN设计的关键之一,因此通过图谱分析异常是GNN设计的第一步。我们的关键观察是,异常的存在将导致“右移”现象,即频谱能量分布较少集中在低频,较多集中在高频。这一事实促使我们提出了Beta小波图神经网络(BWGNN)。事实上,BWGNN具有光谱和空间本地化带通滤波器,以更好地处理异常中的“右移”现象。在4个大规模异常检测数据集上验证了BWGNN的有效性。我们的代码和数据发布在https://github.com/squareRoot3/ Rethinking-Anomaly-Detection。
  • [其他] 深度神经网络中的特征学习与信号传播
    Baratin等人最近的工作(2021)揭示了深度神经网络训练期间发生的一个有趣的模式:与其他层相比,一些层与数据的对齐更多(其中对齐被定义为切线特征矩阵和数据标签矩阵的欧氏乘积)。对齐曲线作为层指数的函数(通常)表现出一种上升-下降模式,在某些隐藏层达到最大值。在这项工作中,我们为这一现象提供了第一种解释。我们引入平衡假设,将这种对齐模式与深度神经网络中的信号传播联系起来。我们的实验证明与理论预测非常吻合。https://www.zhuanzhi.ai/paper/eb536aebcbd07e082fa029bfa966c533
  • [其他] 深入探讨置换敏感图神经网络
    邻接矩阵排列的不变性,即图同构,是图神经网络(GNNs)的首要要求。通常,聚合消息时,节点排列上的不变操作可以满足这个前提条件。但是,这种不变性可能会忽略相邻节点之间的关系,从而影响GNN的表达能力。在这项工作中,我们通过排列组设计了一种高效的排列敏感聚合机制,捕获相邻节点之间的成对关联。我们证明了我们的方法严格地比二维Weisfeiler-Lehman (2-WL)图同构检验更强大,且不低于3-WL检验。此外,我们证明了我们的方法实现了线性抽样复杂度。在多个合成数据集和真实数据集上的综合实验证明了我们的模型的优越性。https://www.zhuanzhi.ai/paper/da818de2d710f7cb9087582587f6240f
  • [其他] 浅谈常见聚类算法
    K-means 聚类算法K-means 聚类算法 可能是大家最为熟悉的聚类算法。它在许多的工业级数据科学和机器学习课程中都有被讲解。并且容易理解和实现相应功能的代码 。    我们先确定要聚类的数量,并随机初始化它们各自的中心点。为了确定要聚类的数量,最好快速查看数据并尝试识别任何不同的分组。中心点是与每个数据点向量长度相同的向量,是上图中的“x”。通过计算当前点与每个组中心之间的距离,对每个数据点进行分类,然后归到与距离最近的中心的组中。基于迭代后的结果,计算每一类内,所有点的平均值,作为新簇中心。迭代重复这些步骤,或者直到组中心在迭代之间变化不大。您还可以选择随机初始化组中心几次,然后选择看起来提供最佳结果。Mean-Shift 聚类Mean-shift 聚类是一个基于滑窗的算法,尝试找到数据点密集的区域。它是一个基于质心的算法,也就是说他的目标是通过更新中心点候选者定位每个组或类的中心点,将中心点候选者更新为滑窗内点的均值。这些候选滑窗之后会在后处理阶段被过滤,来减少临近的重复点,最后形成了中心点的集合和他们对应的组。DBSCAN 笑脸聚类    DBSCAN 从一个任意的还没有被访问过的启动数据点开始。用一个距离 epsilon ε 将这个点的邻域提取出来(所有再距离 ε 内的点都视为邻居点)。如果在邻域内有足够数量的点(根据 minPoints) ,那么聚类过程开始,并且当前数据点变成新集群中的第一个点。否则,该点将被标记为噪声(之后这个噪声点可能会变成集群中的一部分)。在这两种情况中的点都被标记为”已访问“。对于这个新集群中的第一个点,在它 ε 距离邻域内的点已将变成相同集群中的一部分。这个让所有在 ε 邻域内的点都属于相同集群的过程在之后会一直被重复做,直到所有新点都被加进集群分组中。第 2,3 步的过程会一直重复直到集群内所有点都被确定,即所有在 ε 邻域内的点都被访问且被打上标签。我们在当前集群做完这些,一个新的未被访问的点会被提取并处理,从而会接着发现下一个集群或噪声。这个过程反复进行直到所有的点都被编辑为已访问。既然在最后所有的点都被访问,那么每个点都被标记为属于一个集群或者是噪声。基于高斯混合模型(GMM)的期望最大化(EM)聚类k-means的一个主要缺点是它简单地使用了集群中心的平均值。通过下面的图片,我们可以看到为什么这不是最好的方式。在左手边,人眼可以很明显地看到,有两个半径不同的圆形星团以相同的平均值为中心。k-means不能处理这个问题,因为不同簇的平均值非常接近。当簇不是圆形时,k均值也会失效,这也是将均值用作簇中心的后果。使用GMMs的EM聚类我们首先设定聚类簇的数量(如k-means),然后随机初始化每个集群的高斯分布参数。我们也可以通过快速查看数据来为初始参数提供一个很好的猜测。正如上图所示,这不是100%必要的,因为高斯操作开始时候是非常差的,但很快优化。给定每个簇的高斯分布,计算每个数据点属于特定簇的概率。一个点越靠近高斯中心,它就越可能属于该簇。这应该是直观的,因为对于高斯分布,我们假设大多数数据都靠近集群的中心。基于这些概率,我们为高斯分布计算了一组新的参数,这样我们就可以最大化群集中数据点的概率。我们使用数据点位置的加权和计算这些新参数,其中权重是属于特定集群的数据点的概率。为了以可视化的方式解释这一点,我们可以查看上面的图形,特别是以黄色集群为例。在第一次迭代中,分布是随机开始的,但是我们可以看到大多数黄点都在分布的右边。当我们计算一个由概率加权的和时,即使在中心附近有一些点,但大多数都在右边。因此,分布的平均值很自然地移近这些点集。我们还可以看到,大多数点是“从右上到左下”。因此,标准偏差会发生变化,以创建一个更适合这些点的椭圆,以便最大化概率加权的和。第2步和第3步重复进行,直到收敛,也就是在收敛过程中,迭代变化不大。凝聚层次聚类凝聚层次聚类算法实际上分为 2 类:自上而下或自下而上。自下而上算法在一开始将每个数据点当作一个单个集群对待,然后逐步的合并(或凝聚)成对的集群,直到所有的集群被合并到一个集群中,这个集群包含所有的点。自下而上层次聚类因此被叫做层次凝聚的聚类或者 HAC。这个聚类的层次被表示为一棵树(或者树状图)。树根是唯一的集群,他聚集了所有的样本,叶子是只有一个样本的集群。
  • [技术干货] MindSpore前馈神经网络运用
    MindSpore前馈神经网络运用实验经典介绍本实验主要介绍使用MindSpore开发前馈神经网络,并使用Fashion-MNIST数据集训练和测试模型。实验目的掌握如何使用MindSpore进行简单前馈神经网络的开发。了解如何使用MindSpore进行简单图片分类任务的训练。了解如何使用MindSpore进行简单图片分类任务的测试和预测。预备知识熟练使用Python。具备一定的深度学习理论知识,如感知机、前馈神经网络、损失函数、优化器,训练策略等。了解华为云的基本使用方法,包括OBS(对象存储)、ModelArts(AI开发平台)、训练作业等功能。华为云官网:https://www.huaweicloud.com了解并熟悉MindSpore AI计算框架,MindSpore官网:https://www.mindspore.cn/实验环境MindSpore 1.0.0(MindSpore版本会定期更新,本指导也会定期刷新,与版本配套);华为云ModelArts(控制台左上角选择“华北-北京四”):ModelArts是华为云提供的面向开发者的一站式AI开发平台,集成了昇腾AI处理器资源池,用户可以在该平台下体验MindSpore。实验准备已经对ModelArts云环境很熟悉的玩家可以直接跳到实验步骤。数据集准备Fashion-MNIST是一个替代MNIST手写数字集的图像数据集。 它是由Zalando(一家德国的时尚科技公司)旗下的研究部门提供。其涵盖了来自10种类别的共7万个不同商品的正面图片。Fashion-MNIST的大小、格式和训练集/测试集划分与原始的MNIST完全一致。60000/10000的训练测试数据划分,28x28x1的灰度图片。这里介绍一下经典的MNIST(手写字母)数据集。经典的MNIST数据集包含了大量的手写数字。十几年来,来自机器学习、机器视觉、人工智能、深度学习领域的研究员们把这个数据集作为衡量算法的基准之一。实际上,MNIST数据集已经成为算法作者的必测的数据集之一,但是MNIST数据集太简单了。很多深度学习算法在测试集上的准确率已经达到99.6%。从Fashion-MNIST GitHub仓库下载如下4个文件到本地并解压:train-images-idx3-ubyte     training set images(47,042,560 bytes)   train-labels-idx1-ubyte     training set labels(61,440 bytes)  t10k-images-idx3-ubyte      test set images (7,843,840 bytes)  t10k-labels-idx1-ubyte      test set labels (12,288 bytes) 1234脚本准备从课程gitee仓库上下载本实验相关脚本。将脚本和数据集组织为如下形式:feedforward  ├── Fashion-MNIST  │   ├── test  │   │   ├── t10k-images-idx3-ubyte  │   │   └── t10k-labels-idx1-ubyte  │   └── train  │       ├── train-images-idx3-ubyte  │       └── train-labels-idx1-ubyte   └── main.py  创建OBS桶本实验需要使用华为云OBS存储脚本和数据集,可以参考快速通过OBS控制台上传下载文件了解使用OBS创建桶、上传文件、下载文件的使用方法(下文给出了操作步骤)。提示: 华为云新用户使用OBS时通常需要创建和配置“访问密钥”,可以在使用OBS时根据提示完成创建和配置。也可以参考获取访问密钥并完成ModelArts全局配置获取并配置访问密钥。打开OBS控制台,点击右上角的“创建桶”按钮进入桶配置页面,创建OBS桶的参考配置如下:区域:华北-北京四数据冗余存储策略:单AZ存储桶名称:全局唯一的字符串存储类别:标准存储桶策略:公共读归档数据直读:关闭企业项目、标签等配置:免上传文件点击新建的OBS桶名,再打开“对象”标签页,通过“上传对象”、“新建文件夹”等功能,将脚本和数据集上传到OBS桶中。上传文件后,查看页面底部的“任务管理”状态栏(正在运行、已完成、失败),确保文件均上传完成。若失败请:参考上传对象大小限制/切换上传方式,参考上传对象失败常见原因。若无法解决请新建工单,产品类为“对象存储服务”,问题类型为“桶和对象相关”,会有技术人员协助解决。实验步骤推荐使用ModelArts训练作业进行实验,适合大规模并发使用。若使用ModelArts Notebook,请参考LeNet5及Checkpoint实验案例,了解Notebook的使用方法和注意事项。代码梳理导入MindSpore模块和辅助模块用到的框架主要包括:mindspore,用于神经网络的搭建numpy,用于处理一些数据matplotlib,用于画图、图像展示struct,用于处理二进制文件import osimport structimport sysfrom easydict import EasyDict as edictimport matplotlib.pyplot as pltimport numpy as npimport mindsporeimport mindspore.dataset as dsimport mindspore.nn as nnfrom mindspore import contextfrom mindspore.nn.metrics import Accuracyfrom mindspore.train import Modelfrom mindspore.train.callback import ModelCheckpoint, CheckpointConfig, LossMonitor, TimeMonitorfrom mindspore import Tensorcontext.set_context(mode=context.GRAPH_MODE, device_target='Ascend')变量定义cfg = edict({    'train_size': 60000,  # 训练集大小    'test_size': 10000,  # 测试集大小    'channel': 1,  # 图片通道数    'image_height': 28,  # 图片高度    'image_width': 28,  # 图片宽度    'batch_size': 60,    'num_classes': 10,  # 分类类别    'lr': 0.001,  # 学习率    'epoch_size': 20,  # 训练次数    'data_dir_train': os.path.join('Fashion-MNIST', 'train'),    'data_dir_test': os.path.join('Fashion-MNIST', 'test'),    'save_checkpoint_steps': 1,  # 多少步保存一次模型    'keep_checkpoint_max': 3,  # 最多保存多少个模型    'output_directory': './model_fashion',  # 保存模型路径    'output_prefix': "checkpoint_fashion_forward"  # 保存模型文件名字})读取并处理数据读取数据def read_image(file_name):    '''    :param file_name: 文件路径    :return:  训练或者测试数据    如下是训练的图片的二进制格式    [offset] [type]          [value]          [description]    0000     32 bit integer  0x00000803(2051) magic number    0004     32 bit integer  60000            number of images    0008     32 bit integer  28               number of rows    0012     32 bit integer  28               number of columns    0016     unsigned byte   ??               pixel    0017     unsigned byte   ??               pixel    ........    xxxx     unsigned byte   ??               pixel    '''    file_handle = open(file_name, "rb")  # 以二进制打开文档    file_content = file_handle.read()  # 读取到缓冲区中    head = struct.unpack_from('>IIII', file_content, 0)  # 取前4个整数,返回一个元组    offset = struct.calcsize('>IIII')    imgNum = head[1]  # 图片数    width = head[2]  # 宽度    height = head[3]  # 高度    bits = imgNum * width * height  # data一共有60000*28*28个像素值    bitsString = '>' + str(bits) + 'B'  # fmt格式:'>47040000B'    imgs = struct.unpack_from(bitsString, file_content, offset)  # 取data数据,返回一个元组    imgs_array = np.array(imgs).reshape((imgNum, width * height))  # 最后将读取的数据reshape成 【图片数,图片像素】二维数组    return imgs_arraydef read_label(file_name):    '''    :param file_name:    :return:    标签的格式如下:    [offset] [type]          [value]          [description]    0000     32 bit integer  0x00000801(2049) magic number (MSB first)    0004     32 bit integer  60000            number of items    0008     unsigned byte   ??               label    0009     unsigned byte   ??               label    ........    xxxx     unsigned byte   ??               label    The labels values are 0 to 9.    '''    file_handle = open(file_name, "rb")  # 以二进制打开文档    file_content = file_handle.read()  # 读取到缓冲区中    head = struct.unpack_from('>II', file_content, 0)  # 取前2个整数,返回一个元组    offset = struct.calcsize('>II')    labelNum = head[1]  # label数    bitsString = '>' + str(labelNum) + 'B'  # fmt格式:'>47040000B'    label = struct.unpack_from(bitsString, file_content, offset)  # 取data数据,返回一个元组    return np.array(label)def get_data():    # 文件获取    train_image = os.path.join(cfg.data_dir_train, 'train-images-idx3-ubyte')    test_image = os.path.join(cfg.data_dir_test, "t10k-images-idx3-ubyte")    train_label = os.path.join(cfg.data_dir_train, "train-labels-idx1-ubyte")    test_label = os.path.join(cfg.data_dir_test, "t10k-labels-idx1-ubyte")    # 读取数据    train_x = read_image(train_image)    test_x = read_image(test_image)    train_y = read_label(train_label)    test_y = read_label(test_label)    return train_x, train_y, test_x, test_y数据预处理和处理结果图片展示train_x, train_y, test_x, test_y = get_data()train_x = train_x.reshape(-1, 1, cfg.image_height, cfg.image_width)test_x = test_x.reshape(-1, 1, cfg.image_height, cfg.image_width)train_x = train_x / 255.0test_x = test_x / 255.0train_x = train_x.astype('Float32')test_x = test_x.astype('Float32')train_y = train_y.astype('int32')test_y = test_y.astype('int32')print('训练数据集样本数:', train_x.shape[0])print('测试数据集样本数:', test_y.shape[0])print('通道数/图像长/宽:', train_x.shape[1:])print('一张图像的标签样式:', train_y[0])  # 一共10类,用0-9的数字表达类别。plt.figure()plt.imshow(train_x[0,0,...])plt.colorbar()plt.grid(False)plt.show()训练数据集数量: 60000测试数据集数量: 10000通道数/图像长/宽: (1, 28, 28)一张图像的标签样式: 9使用MindSpore GeneratorDataset接口将numpy.ndarray类型的数据转换为Dataset# 转换数据类型为DatasetXY_train = list(zip(train_x, train_y))ds_train = ds.GeneratorDataset(XY_train, ['x', 'y'])ds_train = ds_train.shuffle(buffer_size=cfg.train_size).batch(cfg.batch_size, drop_remainder=True)XY_test = list(zip(test_x, test_y))ds_test = ds.GeneratorDataset(XY_test, ['x', 'y'])ds_test = ds_test.shuffle(buffer_size=cfg.test_size).batch(cfg.batch_size, drop_remainder=True)定义前馈神经网络前馈神经网络是一种最简单的神经网络,各神经元分层排列(其中每一层包含若干个神经元)。每个神经元只与前一层的神经元相连,接收前一层的输出,并输出给下一层,各层间没有反馈。是目前应用最广泛、发展最迅速的人工神经网络之一。第0层叫输入层,最后一层叫输出层,其他中间层叫做隐含层(或隐藏层、隐层)。隐层可以是一层,也可以是多层,是由全连接层堆叠而成。# 定义前馈神经网络class Forward_fashion(nn.Cell):    def __init__(self, num_class=10):  # 一共分十类,图片通道数是1        super(Forward_fashion, self).__init__()        self.num_class = num_class        self.flatten = nn.Flatten()        self.fc1 = nn.Dense(cfg.channel * cfg.image_height * cfg.image_width, 128)        self.relu = nn.ReLU()        self.fc2 = nn.Dense(128, self.num_class)    def construct(self, x):        x = self.flatten(x)        x = self.fc1(x)        x = self.relu(x)        x = self.fc2(x)        return x训练使用Fashion-MNIST数据集对上述定义的前馈神经网络模型进行训练。训练策略如下表所示,可以调整训练策略并查看训练效果。batch size    number of epochs    learning rate    input shape    optimizer60    20    0.001    (1,28,28)    Adam# 构建网络network = Forward_fashion(cfg.num_classes)# 定义模型的损失函数,优化器net_loss = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction="mean")net_opt = nn.Adam(network.trainable_params(), cfg.lr)# 训练模型model = Model(network, loss_fn=net_loss, optimizer=net_opt, metrics={"acc"})loss_cb = LossMonitor(per_print_times=int(cfg.train_size / cfg.batch_size))config_ck = CheckpointConfig(save_checkpoint_steps=cfg.save_checkpoint_steps,                             keep_checkpoint_max=cfg.keep_checkpoint_max)ckpoint_cb = ModelCheckpoint(prefix=cfg.output_prefix, directory=cfg.output_directory, config=config_ck)print("============== Starting Training ==============")model.train(cfg.epoch_size, ds_train, callbacks=[ckpoint_cb, loss_cb], dataset_sink_mode=False)============== Starting Training ==============epoch: 1 step: 1000, loss is 0.6812696epoch: 2 step: 1000, loss is 0.39710096epoch: 3 step: 1000, loss is 0.43427807epoch: 4 step: 1000, loss is 0.3170758epoch: 5 step: 1000, loss is 0.24550956epoch: 6 step: 1000, loss is 0.4204946epoch: 7 step: 1000, loss is 0.35653585epoch: 8 step: 1000, loss is 0.31376493epoch: 9 step: 1000, loss is 0.27455378epoch: 10 step: 1000, loss is 0.18871705epoch: 11 step: 1000, loss is 0.20512795epoch: 12 step: 1000, loss is 0.2589024epoch: 13 step: 1000, loss is 0.31454447epoch: 14 step: 1000, loss is 0.24145015epoch: 15 step: 1000, loss is 0.32082427epoch: 16 step: 1000, loss is 0.27023837epoch: 17 step: 1000, loss is 0.34484679epoch: 18 step: 1000, loss is 0.41191268epoch: 19 step: 1000, loss is 0.07990202epoch: 20 step: 1000, loss is 0.26586318评估测试# 使用测试集评估模型,打印总体准确率metric = model.eval(ds_test, dataset_sink_mode=False)print(metric)预测class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']#从测试集中取出一组样本,输入模型进行预测test_ = ds_test.create_dict_iterator()._get_next()#利用key值选出样本test = Tensor(test_['x'], mindspore.float32)predictions = model.predict(test)softmax = nn.Softmax()predictions = softmax(predictions)predictions = predictions.asnumpy()true_label = test_['y'].asnumpy()true_image = test_['x'].asnumpy()for i in range(15):    p_np = predictions[i, :]    pre_label = np.argmax(p_np)    print('第' + str(i) + '个sample预测结果:', class_names[pre_label], '   真实结果:', class_names[true_label[i]])对预测结果可视化# -------------------定义可视化函数--------------------------------# 输入预测结果序列,真实标签序列,以及图片序列# 目标是根据预测值对错,让其标签显示为红色或者蓝色。对:标签为蓝色;错:标签为红色def plot_image(predicted_label, true_label, img):    plt.grid(False)    plt.xticks([])    plt.yticks([])    # 显示对应图片    plt.imshow(img, cmap=plt.cm.binary)    # 显示预测结果的颜色,如果对上了是蓝色,否则为红色    if predicted_label == true_label:        color = 'blue'    else:        color = 'red'    # 显示对应标签的格式,样式    plt.xlabel('{},({})'.format(class_names[predicted_label],                                    class_names[true_label]), color=color)# 将预测的结果以柱状图形状显示蓝对红错def plot_value_array(predicted_label, true_label,predicted_array):    plt.grid(False)    plt.xticks([])    plt.yticks([])    this_plot = plt.bar(range(10), predicted_array, color='#777777')    plt.ylim([0, 1])    this_plot[predicted_label].set_color('red')    this_plot[true_label].set_color('blue')# 预测15个图像与标签,并展现出来num_rows = 5num_cols = 3num_images = num_rows * num_colsplt.figure(figsize=(2 * 2 * num_cols, 2 * num_rows))for i in range(num_images):    plt.subplot(num_rows, 2 * num_cols, 2 * i + 1)    pred_np_ = predictions[i, :]    predicted_label = np.argmax(pred_np_)    image_single = true_image[i, 0, ...]    plot_image(predicted_label, true_label[i], image_single)    plt.subplot(num_rows, 2 * num_cols, 2 * i + 2)    plot_value_array(predicted_label, true_label[i], pred_np_)plt.show()适配训练作业(可跳过)创建训练作业时,运行参数会通过脚本传参的方式输入给脚本代码,脚本必须解析传参才能在代码中使用相应参数。如data_url和train_url,分别对应数据存储路径(OBS路径)和训练输出路径(OBS路径)。脚本对传参进行解析后赋值到args变量里,在后续代码里可以使用。import argparseparser = argparse.ArgumentParser()parser.add_argument('--data_url', required=True, default=None, help='Location of data.')parser.add_argument('--train_url', required=True, default=None, help='Location of training outputs.')args, unknown = parser.parse_known_args()MindSpore暂时没有提供直接访问OBS数据的接口,需要通过ModelArts自带的moxing框架与OBS交互。将OBS桶中的数据拷贝至执行容器中,供MindSpore使用:import moxing# src_url形如's3://OBS/PATH',为OBS桶中数据集的路径,dst_url为执行容器中的路径moxing.file.copy_parallel(src_url=args.data_url, dst_url='Fashion-MNIST/')如需将训练输出(如模型Checkpoint)从执行容器拷贝至OBS,请参考:import moxing# src_url为执行容器中的路径,dst_url形如's3://OBS/PATH',目录若不存在则会新建moxing.file.copy_parallel(src_url='model_fashion', dst_url=args.train_url)   创建训练作业可以参考使用常用框架训练模型来创建并启动训练作业。打开ModelArts控制台-训练管理-训练作业,点击“创建”按钮进入训练作业配置页面,创建训练作业的参考配置:算法来源:常用框架->Ascend-Powered-Engine->MindSpore;代码目录:选择上述新建的OBS桶中的feedforward目录;启动文件:选择上述新建的OBS桶中的feedforward目录下的main.py;数据来源:数据存储位置->选择上述新建的OBS桶中的feedforward目录下的Fashion-MNIST目录;训练输出位置:选择上述新建的OBS桶中的feedforward目录并在其中创建model_fashion目录;作业日志路径:同训练输出位置;规格:Ascend:1*Ascend 910;其他均为默认;启动并查看训练过程:点击提交以开始训练;在训练作业列表里可以看到刚创建的训练作业,在训练作业页面可以看到版本管理;点击运行中的训练作业,在展开的窗口中可以查看作业配置信息,以及训练过程中的日志,日志会不断刷新,等训练作业完成后也可以下载日志到本地进行查看;参考上述代码梳理,在日志中找到对应的打印信息,检查实验是否成功;运行成功。实验小结本实验展示了如何使用MindSpore进行Fashion-MNIST数据集分类。首先训练前馈神经网络,然后使用训练后的前馈神经网络模型对Fashion-MNIST测试数据进行分类,从结果上分析准确率大于80%,即前馈神经网络学习到了Fashion-MNIST数据集分类。————————————————原文链接:https://blog.csdn.net/weixin_54227557/article/details/123533208
  • [基础知识] 【MindSpore易点通】深度学习系列-经典卷积神经网络
    上周小伙伴说我们卷积神经网络讲的太简单了,基础嘛,当然要先打好()。这不,更加复杂的卷积神经网络来了~经典CNN之LeNet手写字体识别模型LeNet5诞生于1994年,是最早的卷积神经网络之一。LeNet5利用卷积、参数共享、池化等操作提取特征,避免了大量的计算成本,最后使用全连接神经网络进行分类识别。 LeNet5的网络结构示意图LeNet5由7层CNN(不包含输入层)组成,图中输入的原始图像大小是32×32像素,卷积层:Ci;子采样层(pooling,池化):Si;全连接层:Fi。C1层(卷积层):该层使用了6个卷积核,每个卷积核的大小为5×5,可以得到6个特征图(feature map)。(1)特征图大小每个卷积核(5×5)与原始的输入图像(32×32)进行卷积,这样得到的特征图大小为(32-5+1)×(32-5+1)= 28×28这里有个小知识点:卷积核与输入图像按卷积核大小逐个区域进行匹配计算,匹配后原始输入图像的尺寸将变小,因为边缘部分卷积核无法越出界,只能匹配一次,匹配计算后的尺寸变为Cr×Cc=(Ir-Kr+1)×(Ic-Kc+1),其中Cr、Cc,Ir、Ic,Kr、Kc分别表示卷积后结果图像、输入图像以及卷积核的行列大小。(2)参数个数由于参数(权值)共享,对于同个卷积核每个神经元均使用相同的参数,因此,参数个数为(5×5+1)×6= 156,其中5×5为卷积核参数,1为偏置参数。(3)连接数卷积后的图像大小为28×28,因此每个特征图有28×28个神经元,每个卷积核参数为(5×5+1)×6,因此,该层的连接数为(5×5+1)×6×28×28=1223042、S2层(下采样层,也称池化层):(1)特征图大小这一层主要是做池化或者特征映射(特征降维),池化单元为2×2,因此,6个特征图的大小经池化后即变为14×14。由于池化单元之间没有重叠,在池化区域内进行聚合统计后得到新的特征值,因此经2×2池化后,每两行两列重新算出一个特征值出来,相当于图像大小减半,因此卷积后的28×28图像经2×2池化后就变为14×14。这一层的计算过程是:2×2 单元里的值相加,然后再乘以训练参数w,再加上一个偏置参数b(每一个特征图共享相同的w和b),然后取sigmoid值(S函数:0-1区间),作为对应的该单元的值。卷积操作与池化的示意图(2)参数个数S2层由于每个特征图都共享相同的w和b这两个参数,因此需要2×6=12个参数(3)连接数下采样之后的图像大小为14×14,因此S2层的每个特征图有14×14个神经元,每个池化单元连接数为2×2+1(1为偏置量),因此,该层的连接数为(2×2+1)×14×14×6 = 58803、C3层(卷积层):C3层有16个卷积核,卷积模板大小为5×5。(1)特征图大小与C1层的分析类似,C3层的特征图大小为(14-5+1)×(14-5+1)= 10×10(2)参数个数需要注意的是,C3与S2并不是全连接而是部分连接,有些是C3连接到S2三层、有些四层、甚至达到6层,通过这种方式提取更多特征,连接的规则如下表所示:例如第一列表示C3层的第0个特征图(feature map)只跟S2层的第0、1和2这三个feature maps相连接,计算过程为:用3个卷积模板分别与S2层的3个feature maps进行卷积,然后将卷积的结果相加求和,再加上一个偏置,再取sigmoid得出卷积后对应的feature map了。其它列也是类似(有些是3个卷积模板,有些是4个,有些是6个)。因此,C3层的参数数目为(5×5×3+1)×6 +(5×5×4+1)×9 +5×5×6+1 = 1516(3)连接数卷积后的特征图大小为10×10,参数数量为1516,因此连接数为1516×10×10= 151600S4(下采样层,也称池化层):(1)特征图大小与S2的分析类似,池化单元大小为2×2,因此,该层与C3一样共有16个特征图,每个特征图的大小为5×5。(2)参数数量与S2的计算类似,所需要参数个数为16×2 = 32(3)连接数连接数为(2×2+1)×5×5×16 = 2000C5层(卷积层):(1)特征图大小该层有120个卷积核,每个卷积核的大小仍为5×5,因此有120个特征图。由于S4层的大小为5×5,而该层的卷积核大小也是5×5,因此特征图大小为(5-5+1)×(5-5+1)= 1×1。这样该层就刚好变成了全连接,当然这里真的只是coincidence,如果原始输入的图像比较大,则该层就不是全连接了。(2)参数个数本层的参数数目为120×(5×5×16+1) = 48120(3)连接数由于该层的特征图大小刚好为1×1,因此连接数为48120×1×1=481206、F6层(全连接层):(1)特征图大小F6层有84个单元,由于输出层的对应的是一个7×12的比特图,如下图所示,-1表示白色,1表示黑色,这样每个符号的比特图的黑白色就对应于一个编码。该层有84个特征图,特征图大小与C5一样都是1×1,与C5层全连接。(2)参数个数由于是全连接,参数数量为(120+1)×84=10164。跟经典神经网络一样,F6层计算输入向量和权重向量之间的点积,再加上一个偏置,然后将其传递给sigmoid函数得出结果。(3)连接数由于是全连接,连接数与参数数量一样,也是10164。7、OUTPUT层(输出层):Output层也是全连接层,共有10个节点,分别代表数字0到9。如果第i个节点的值为0,则表示网络识别的结果是数字i。(1)特征图大小该层采用径向基函数(RBF)的网络连接方式,假设x是上一层的输入,y是RBF的输出,则RBF输出的计算方式是:上式中的Wij的值由i的比特图编码确定,i从0到9,j取值从0到7×12-1。RBF输出的值越接近于0,表示当前网络输入的识别结果与字符i越接近。(2)参数个数由于是全连接,参数个数为84×10=840(3)连接数由于是全连接,连接数与参数个数一样,也是840LeNet卷积层用来识别图像⾥的空间模式,例如线条和物体局部,池化层则⽤来降低卷积层对位置的敏感性,在交替使用卷积层和最大池化层后接全连接层来进⾏图像分类,展示了通过梯度下降训练卷积神经网络可以达到手写数字识别在当时最先进的结果。经典CNN之AlexNet第一个典型的CNN是LeNet5网络结构,但是第一个引起大家注意的网络却是AlexNet。AlexNet网络结构网络总共的层数为8层,5层卷积,3层全连接层。1、第一层:卷积层C1,输入为224×224×3的图像,卷积核的数量为96,卷积核的大小为11×11×3,步长stride 为4,pad = 0,表示不扩充边缘;卷积后的图形大小:wide = (224 + 2 * padding - kernel_size) / stride + 1 = 54height = (224 + 2 * padding - kernel_size) / stride + 1 = 54dimention = 96然后进行 (Local Response Normalized), 后面跟着池化pool_size = (3, 3), stride = 2, pad = 0,最终获得第一层卷积的feature map。2、第二层:卷积层C2, 输入为上一层卷积的feature map,卷积的个数为256个,卷积核的大小为:5×5×48,pad = 2,stride = 1,然后做 LRN,最后 max_pooling, pool_size = (3, 3), stride = 2。3、第三层:卷积层C3, 输入为第二层的输出,卷积核个数为384, kernel_size = (3 ×3×256),padding = 1,第三层没有做LRN和Pool。4、第四层:卷积层C4, 输入为第三层的输出,卷积核个数为384, kernel_size = (3×3), padding = 1, 和第三层一样,没有LRN和Pool。5、第五层:卷积层C5, 输入为第四层的输出,卷积核个数为256,kernel_size = (3×3×3), padding = 1。然后直接进行max_pooling, pool_size = (3, 3), stride = 2;6、第6,7,8层是全连接层,每一层的神经元的个数为4096,最终输出softmax为1000,然后全连接层中使用了RELU和Dropout。AlexNet将LeNet的思想发扬光大,把CNN的基本原理应用到了很深很宽的网络中。首先成功使用ReLU作为CNN的激活函数,并验证其效果在较深的网络超过了Sigmoid,成功解决了Sigmoid在网络较深时的梯度弥散问题。Relu函数:然后选择采用覆盖的池化操作。常规的池化层由于没有重叠,所以pool_size 和 stride一般是相等的,例如8×8的一个图像,如果池化层的尺寸是2×2,那么经过池化后的操作得到的图像是4×4,这种设置叫做不覆盖的池化操作。而如果 stride < pool_size, 那么就会产生覆盖的池化操作,这种有点类似于convolutional化的操作,在训练模型过程中,覆盖的池化层更不容易过拟合。同时,神经网络的一个比较严重的问题就是过拟合问题,AlexNet采用的数据扩充和Dropout的方法处理过拟合问题。对于某一层神经元,通过定义的概率来随机删除一些神经元,同时保持输入层与输出层神经元的个数不变,然后按照神经网络的学习方法进行参数更新,下一次迭代中,重新随机删除一些神经元,直至训练结束。总结AlexNet和LeNet的设计理念非常相似,但也存在显著差异。首先,AlexNet比相对较小的LeNet5要深得多。AlexNet由八层组成:五个卷积层、两个全连接隐藏层和一个全连接输出层。其次,AlexNet使用ReLU而不是sigmoid作为其激活函数。AlexNet的更高层建立在底层表示的基础上,以表示更大的特征,如眼睛、鼻子、草叶等等。而更高的层可以检测整个物体,如人、飞机、狗或飞盘。最终的隐藏神经元可以学习图像的综合表示,从而使属于不同类别的数据易于区分。AlexNet首次证明了学习到的特征可以超越手工设计的特征,AlexNet在结果上要优于LeNet很多,特别是其在处理大规模数据方便的优势更是明显。AlexNet的问世也开启了深度学习在计算机视觉领域的大规模应用。一般我们可以将其看做浅层神经网络和深层神经网络的分界线。当然啦,经典的CNN还是有很多其他的网络的,比如VGG、GoogLeNet、ResNet等等,欢迎大伙儿一起学习使用呀!
  • [技术干货] 【论文分享】基于SqueezeNet卷积神经网络的车辆检测
    基于SqueezeNet卷积神经网络的车辆检测魏泽发1, 崔华21 长安大学教育技术与网络中心,陕西 西安 7100642 长安大学信息工程学院,陕西 西安 710064摘要在智能交通系统中,针对车辆目标检测算法可移植性不高、检测速度较慢等问题,提出了一种基于SqueezeNet卷积神经网络的车辆检测方法。通过融合SqueezeNet与SSD(single shot multibox detector)算法的车辆检测方法,在UA-DETRAC数据集上进行训练,实现了车辆目标的快速检测,提升了模型的可移植性,缩短了单帧检测时间。实验结果表明,所提模型在保证准确率的同时,模型单帧检测时间可达22.3 ms,模型大小为16.8 MB,相较于原SSD算法,模型大小减少了约8/9。关键词: 智能交通 ; 卷积神经网络 ; SqueezeNet ; 车辆检测1 引言随着智能交通系统的不断发展和完善,实现车辆的精准、快速检测成为智能交通系统中的关键。车辆检测是自动驾驶的重要组成部分,在减少甚至避免交通事故方面具有重要意义。同时,车辆检测可以为交通管理部门提供准确的数据支撑,在交通管制、拥堵检测和信号配时等方面有广泛应用。传统的车辆检测算法需要对图像进行预处理,然后对整幅图像进行滑动窗口遍历操作,通过初步判断车辆目标可能会出现的位置,人工设计车辆目标的某种特征,如常见的方向梯度直方图(HOG,histogram of oriented gradient)特征[1]、尺度不变特征变换(SIFT,scale-invariant feature transform)特征[2]、哈尔特征(Haar-link feature)[3]和加速稳健特征(SURF,speeded up robust feature)[4]等,最后将特征送入支持向量机(SVM,support vector machine)[5]或 Adaboost[6]分类器进行分类,完成检测任务。但是人工设计特征存在很大弊端,在设计过程中会过于依赖以往经验,且算法在陌生场景下的表现效果不好,检测算法的稳健性不强,极大地阻碍了检测算法的应用。随着深度学习在各个领域取得了较大突破,目标检测将面临新的发展机遇。深度学习目标检测算法在检测准确率上有巨大的提升,主要得益于卷积神经网络强大的特征提取能力。卷积神经网络可以自动提取目标中的关键特征信息,不需要人工设计和经验,只需要向网络中传递足够多的图像数据即可,因此,该算法具有更高的稳健性,更适应不同场景,在实际应用中具有较大优势。基于深度学习的目标检测算法主要包括两类,即one-stage方法和two-stage方法。其中,two-stage方法首先需要使用生成算法产生一系列候选框,然后在候选框上进行回归和分类操作,该方法的特点是检测准确率高、检测速度较慢。2014年,Girshick等[7]提出了R-CNN(region-CNN)目标检测算法,利用选择性搜索(selective search)[8]方法生成候选框,然后将候选框送入卷积神经网络提取特征,最后将特征输入 SVM 分类器进行回归运算。为了改善 two-stage 方法中存在的问题,研究人员提出了one-stage 方法,其主要思路是摒弃候选框生成过程,直接利用卷积神经网络在图像数据上进行卷积操作,然后在后续特征图上不断提取信息,最后从提取出的信息中取部分特征层完成最终的检测任务,该方法的检测速度较快,但是检测准确率较低。2016年,YOLO(you only look once)目标检测系列算法[9,10,11]很好地解决了算法的实时性问题,通过将检测和分类两个过程整合为一个过程,在每个特征单元上预测检测框的位置和类别,然后结合图像中的背景信息在整个图像特征上进行预测,虽然检测准确率下降了,但该算法为未来的研究工作提供了很好的思路和方向。SSD检测算法[12]很好地结合了R-CNN目标检测算法和YOLO目标检测系列算法的优势,继承了 R-CNN 算法的 anchor 机制和YOLO算法的回归思想,通过在多个尺度的特征图上生成不同长宽比的候选框,实现对各种尺寸目标的检测。相比于R-CNN目标检测算法和YOLO目标检测系列算法,SSD算法在各个领域中的应用更广泛。本文提出一种基于 SqueezeNet[13]与 SSD 算法融合的车辆目标检测算法,利用轻量级卷积神经网络SqueezeNet的特性,在保证同等检测准确率的同时,大幅度减少模型的参数量,且缩短了模型的单帧检测时间。这为将算法移植到如现场可编程逻辑门阵列(FPGA,field programmable gate array)这类移动开发板上提供了可能,也意味着服务器在同等时间内可以处理更多数据,大幅度提升了服务器的利用效率。2 结束语本文提出了一种基于 SqueezeNet 卷积神经网络的车辆目标检测算法,解决了在智能交通系统中关于车辆目标检测算法可移植性不高、检测速度较慢等问题,该算法通过将SqueezeNet与SSD算法融合,同时根据融合后的模型表现采取针对性改进方式,在保证模型检测准确率的同时,降低网络参数量,缩短模型的单帧检测时间,实现了对车辆目标的精准、快速检测,为模型的移植、开发提供了可能。因此,利用该技术的车辆目标检测算法可以应用于交通摄像头、车载相机等设备拍摄的场景,对自动驾驶以及交通管理部门工作效率的提升具有重要意义。The authors have declared that no competing interests exist.作者已声明无竞争性利益关系。3 原文链接http://www.infocomm-journal.com/wlw/article/2020/2096-3750/2096-3750-4-3-00120.shtml
  • [行业资讯] 特斯联取得智能物联网技术最新突破 多项研究成果入选国际顶刊
    新华财经北京5月12日电(记者吴丛司)记者12日获悉,特斯联近期在智能物联网技术(AIoT)领域取得最新科研成果,主要包括:优化智能物联网的能耗、延时与交互,算力网络的资源管理和任务调度,以及多智体反馈神经网络框架和应用。以上技术突破由特斯联前不久任命的首席科学家杨旸博士带队完成,该科研成果已被IEEE国际学术期刊和国际顶级会议收录。智能物联网的能耗、时延与交互由于蜂窝移动通信系统基础设施的封闭性,以及缺乏有效的现场测量工具,NB-IoT网络的许多重要指标一直以来都没有被深入地研究,例如:无线接入性能和能耗等。基于实际应用场景中完成的扎实研究工作,团队进一步给出了提升智能物联网技术规范和芯片设计方向的优化建议。在工业物联网的应用场景中,无线传感器设备无需铺设专门的有线网络,具有灵活性和可扩展等优势。但是,工业生产环境中复杂时变的无线衰落信道会导致无法预测的随机服务时延和时延抖动,严重降低了工业生产闭环反馈控制系统的性能和稳定性,这是无线传感器设备和无线通信技术应用于高精度、高可靠的自动化工业生产系统的最主要技术挑战。针对工业物联网应用场景中的严苛时延要求,杨旸博士及团队提出了在无线多径衰落信道中对时延分布进行塑形的新方法,通过对原始最优化函数的解耦分析,设计了“双层闭式反馈控制算法(TACAN)”,实现了时延分布方差的最小化,从而显著提高了工业物联网系统的可靠性和稳定性。算力网络的资源管理和任务调度在智能物联网的应用场景中,边缘计算资源的广泛部署可以及时有效地满足终端用户的低时延、强计算、快响应等服务需求。针对复杂多变的无线信道环境和多层次的移动通信网络架构,杨旸博士与团队提出了基于大规模多天线中继节点辅助的多层次算力系统,来增强复杂用户任务的计算能力和效率。多智体反馈神经网络框架和应用杨旸博士及团队提出了“多智体反馈神经网络”(MAFENN)框架,包含三个充分合作的智能体,其中的反馈智能体模拟了灵长类动物大脑中的信号反馈和错误纠正机制,有效提升了神经网络训练过程中的反馈学习能力、特征提取能力、噪声和干扰消除能力。
总条数:945 到第
上滑加载中